クライアント証明書はオプションだったのか

GoでHTTPSサーバをシュッと書いて、curl --cacertで動作確認したら普通に動いたんだが、そういえばcurlはクライアント証明書送ってないんじゃないかって疑問が出てきた。

結論から言うと、サーバからクライアントへの証明書要求はオプションであって、ListenAndServeTLSではデフォルトで要求しないになってるからクライアントの証明書は不要だった。RFCで確認した。

https://tools.ietf.org/html/rfc5246#section-7.4.4

ちなみに、Goでサーバからクライアントへの証明書要求を行う場合、tlsパッケージのClientAuthTypeをサーバに設定することで要求できる。

最後に、クライアント証明書なしで自己署名した証明書を使ったシンプルなHTTPSクライアント・サーバをGoで書いた。

server.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/index", index)

    err := http.ListenAndServeTLS(":8443", "server.crt", "server.key", nil)
    if err != nil {
        log.Fatalln(err)
    }
}

func index(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Hello, https!")
    w.Write([]byte("Hello, https!"))
}

client.go

package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    caCert, err := ioutil.ReadFile("../server/server.crt")
    if err != nil {
        panic(err)
    }
    certPool := x509.NewCertPool()
    certPool.AppendCertsFromPEM(caCert)
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs: certPool,
        },
    }
    client := &http.Client{
        Transport: tr,
    }
    res, err := client.Get("https://localhost:8443/index")
    if err != nil {
        panic(err)
    }
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(body))
}