1. Golang中证书相关包
- crypto/tls实现tls1.2和tls1.3。
type Config struct { ...... // Certificates contains one or more certificate chains to present to the // other side of the connection. The first certificate compatible with the // peer's requirements is selected automatically. // // Server configurations must set one of Certificates, GetCertificate or // GetConfigForClient. Clients doing client-authentication may set either // Certificates or GetClientCertificate. // // Note: if there are multiple Certificates, and they don't have the // optional field Leaf set, certificate selection will incur a significant // per-handshake performance cost. Certificates []Certificate // RootCAs defines the set of root certificate authorities // that clients use when verifying server certificates. // If RootCAs is nil, TLS uses the host's root CA set. RootCAs *x509.CertPool // ServerName is used to verify the hostname on the returned // certificates unless InsecureSkipVerify is given. It is also included // in the client's handshake to support virtual hosting unless it is // an IP address. ServerName string // ClientAuth determines the server's policy for // TLS Client Authentication. The default is NoClientCert. ClientAuth ClientAuthType // ClientCAs defines the set of root certificate authorities // that servers use if required to verify a client certificate // by the policy in ClientAuth. ClientCAs *x509.CertPool // InsecureSkipVerify controls whether a client verifies the // server's certificate chain and host name. // If InsecureSkipVerify is true, TLS accepts any certificate // presented by the server and any host name in that certificate. // In this mode, TLS is susceptible to man-in-the-middle attacks. // This should be used only for testing. InsecureSkipVerify bool ...... }<style></style>
LoadX509KeyPair reads and parses a public/private key pair from a pair of files.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error)<style></style>
-
crypot/x509 解析X.509格式的密钥和证书。
type CertPool struct { // contains filtered or unexported fields }<style></style>
CertPool is a set of certificates.
func NewCertPool() *CertPool<style></style>
NewCertPool返回一个空的CertPool。
func SystemCertPool() (*CertPool, error)<style></style>
SystemCertPool returns a copy of the system cert pool.
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool)<style></style>
AppendCertsFromPEM attempts to parse a series of PEM encoded certificates. It appends any certificates found to s and reports whether any certificates were successfully parsed.
type Certificate struct { Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content. RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. RawSubject []byte // DER encoded Subject RawIssuer []byte // DER encoded Issuer Signature []byte SignatureAlgorithm SignatureAlgorithm PublicKeyAlgorithm PublicKeyAlgorithm PublicKey interface{} Version int SerialNumber *big.Int Issuer pkix.Name Subject pkix.Name NotBefore, NotAfter time.Time // Validity bounds. KeyUsage KeyUsage // Extensions contains raw X.509 extensions. When parsing certificates, // this can be used to extract non-critical extensions that are not // parsed by this package. When marshaling certificates, the Extensions // field is ignored, see ExtraExtensions. Extensions []pkix.Extension // Go 1.2 // ExtraExtensions contains extensions to be copied, raw, into any // marshaled certificates. Values override any extensions that would // otherwise be produced based on the other fields. The ExtraExtensions // field is not populated when parsing certificates, see Extensions. ExtraExtensions []pkix.Extension // Go 1.2 // UnhandledCriticalExtensions contains a list of extension IDs that // were not (fully) processed when parsing. Verify will fail if this // slice is non-empty, unless verification is delegated to an OS // library which understands all the critical extensions. // // Users can access these extensions using Extensions and can remove // elements from this slice if they believe that they have been // handled. UnhandledCriticalExtensions []asn1.ObjectIdentifier // Go 1.5 ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package. // BasicConstraintsValid indicates whether IsCA, MaxPathLen, // and MaxPathLenZero are valid. BasicConstraintsValid bool IsCA bool // MaxPathLen and MaxPathLenZero indicate the presence and // value of the BasicConstraints' "pathLenConstraint". // // When parsing a certificate, a positive non-zero MaxPathLen // means that the field was specified, -1 means it was unset, // and MaxPathLenZero being true mean that the field was // explicitly set to zero. The case of MaxPathLen==0 with MaxPathLenZero==false // should be treated equivalent to -1 (unset). // // When generating a certificate, an unset pathLenConstraint // can be requested with either MaxPathLen == -1 or using the // zero value for both MaxPathLen and MaxPathLenZero. MaxPathLen int // MaxPathLenZero indicates that BasicConstraintsValid==true // and MaxPathLen==0 should be interpreted as an actual // maximum path length of zero. Otherwise, that combination is // interpreted as MaxPathLen not being set. MaxPathLenZero bool // Go 1.4 SubjectKeyId []byte AuthorityKeyId []byte // RFC 5280, 4.2.2.1 (Authority Information Access) OCSPServer []string // Go 1.2 IssuingCertificateURL []string // Go 1.2 // Subject Alternate Name values. (Note that these values may not be valid // if invalid values were contained within a parsed certificate. For // example, an element of DNSNames may not be a valid DNS domain name.) DNSNames []string EmailAddresses []string IPAddresses []net.IP // Go 1.1 URIs []*url.URL // Go 1.10 // Name constraints PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. PermittedDNSDomains []string ExcludedDNSDomains []string // Go 1.9 PermittedIPRanges []*net.IPNet // Go 1.10 ExcludedIPRanges []*net.IPNet // Go 1.10 PermittedEmailAddresses []string // Go 1.10 ExcludedEmailAddresses []string // Go 1.10 PermittedURIDomains []string // Go 1.10 ExcludedURIDomains []string // Go 1.10 // CRL Distribution Points CRLDistributionPoints []string // Go 1.2 PolicyIdentifiers []asn1.ObjectIdentifier }<style></style>
示例:
caCertPath := "cert/ca.crt" caCrt, err := ioutil.ReadFile(caCertPath) if err != nil { fmt.Println("ReadFile err:", err) return } pool.AppendCertsFromPEM(caCrt)<style></style>
-
net/http提供HTTP的client和server实现。
客户端定制:For control over HTTP client headers, redirect policy等:
client := &http.Client{ CheckRedirect: redirectPolicyFunc, Transport: tr, }<style></style>
Transport定制:For control over proxies, TLS configuration, keep-alives, compression等:
tr := &http.Transport{ MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, DisableCompression: true, TLSClientConfig: &tls.Config{ RootCAs: pool, Certificates: [] tls.Certificate{clicrt} } } client := &http.Client{Transport: tr} resp, err := client.Get("https://example.com")<style></style>
服务端定制:
s := &http.Server{ Addr: ":8080", Handler: myHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ ClientCAs: pool, ClientAuth: tls.RequireAndVerifyClientCert, } } // log.Fatal(s.ListenAndServe()) log.Fatal(s.ListenAndServe("server.crt", "server.key"))<style></style>
HTTPS监听
Files containing a certificate and matching private key for the server must be provided.
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error<style></style>
ListenAndServeTLS listens on the TCP network address srv.Addr and then calls ServeTLS to handle requests on incoming TLS connections. Accepted connections are configured to enable TCP keep-alives.
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error<style></style>
ListenAndServeTLS always returns a non-nil error. After Shutdown or Close, the returned error is ErrServerClosed. Server需要定制时使用。
<style></style>2. 认证过程
单向认证过程:
客户点包含ca.crt,服务端包含server.key和server.crt。
客户端:客户端生成一个随机数random-client,传到服务器端;
服务端:服务器端接收消息之后,生成一个随机数random-server和包含公钥的证书,一起回馈给客户端;
客户端:客户端收到的东西原封不动,加上premaster secret(通过random-client、random-server 经过一定算法生成的数据),再一次送给服务器端,这次传过去的东西是经过服务端的公钥进行加密后数据;
服务端:服务端经过私钥(server.key),进行解密,获取 premaster secret(协商密钥过程);
此时客户端和服务器端都拥有了三个要素:random-client、random-server和premaster secret,安全通道已经建立,以后的交流都会校检上面的三个要素通过算法算出的session key。
双向认证过程相当于客户端和服务端反过来再执行认证、加解密、协商一遍。
3. 单向认证
单向认证只需要服务器端有证书即可。
CA证书
openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -key ca.key -subj "/CN=wang.com" -days 365 -out ca.crt<style></style>
Server证书
用CA证书签发server证书。
openssl genrsa -out server.key 2048 openssl req -new -key server.key -subj "/CN=server" -out server.csr openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt -days 365<style></style>
Server.go
服务器监听在:https://server:8088,域名是server证书申请的CN。
单向认证时服务端只需要使用http.ListenAndServeTLS()或srv.ListenAndServeTLS()导入证书即可。一般情况下,不需要配置Server,直接采用默认的http.ListenAndServeTLS()。
package main import ( "fmt" "net/http" "os" ) var Addr string = ":8088" func handler(w http.ResponseWriter, r *http.Request){ w.Write([]byte("Hello")) } func main(){ http.HandleFunc("/", handler) _, err := os.Open("cert/server.crt") if err != nil { fmt.Println("Can't open server.crt") panic(err) } fmt.Printf("listen...[%s]\n", Addr) err = http.ListenAndServeTLS(Addr, "cert/server.crt", "cert/server.key", nil) if err != nil { fmt.Println(err) } }<style></style>
Client.go
需要提前将server添加到/etc/hosts中以便本地测试。
单向认证时client端需导入CA根证书,需要定制http.Transport。
Golang默认支持HTTP/2协议,只要使用TLS则默认启动HTTP/2特性,但对http Client做一些定制化配置后,会覆盖掉http库的默认行为,导致开启HTTP/1.1。
package main import ( "fmt" "crypto/tls" "crypto/x509" "flag" "io/ioutil" "log" "net/http" "golang.org/x/net/http2" ) var addr = flag.String("addr", "https://server:8088?numa=4&numb=6", "connect to") var httpVer = flag.Int("httpVer", 2, "HTTP version") func main(){ flag.Parse() client := &http.Client{} caCert, err := ioutil.ReadFile("cert/ca.crt") if err != nil { log.Fatalf("Reading server certificate: %s", err) } pool := x509.NewCertPool() pool.AppendCertsFromPEM(caCert) tlsConfig := &tls.Config{ RootCAs: pool, } switch *httpVer { case 1: client.Transport = &http.Transport { TLSClientConfig: tlsConfig, } case 2: client.Transport = &http2.Transport { TLSClientConfig: tlsConfig, } } resp, err := client.Get(*addr) if err != nil { log.Fatalf("Failed get: %s", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatalf("Failed reading response body: %s", err) } fmt.Printf("Response %d: %s\nbody: %s\n", resp.StatusCode, resp.Proto, string(body)) }<style></style>
Curl的-k参数可忽略证书验证:
$ curl --cacert "cert/ca.crt" https://server:8088 Hi, This is an example of https service in golang! $ curl -k https://server:8088 Hi, This is an example of https service in golang! $ curl -v https://server:8088 * Rebuilt URL to: https://server:8088/ * Trying 127.0.0.1... * Connected to server (127.0.0.1) port 8088 (#0) * found 133 certificates in /etc/ssl/certs/ca-certificates.crt * found 403 certificates in /etc/ssl/certs * ALPN, offering http/1.1 * SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256 * server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none * Closing connection 0<style></style>
如果/CN使用IP地址,就会报如下类似错误:
Get https://10.183.47.206:8081: x509: cannot validate certificate for 10.183.47.206 because it doesn't contain any IP SANs<style></style>
Client测试
$ go run client.go Response 200: HTTP/2.0 body: Hello $ go run client.go -httpVer=1 Response 200: HTTP/1.1 body: Hello<style></style> <style></style>
HTTP/1.1非加密(10数据帧)
HTTP/1.1单向认证(17数据帧)
<style></style>HTTP/2单向认证(22数据帧)
4. 双向认证
双向认证要求客户端和服务端都要有证书,且都用CA证书验证对端证书。
Client证书
用CA证书签发client证书,而非server证书签发。
注意生成client端证书的时候,要多添加一个字段,golang的server端认证程序会对这个字段进行认证:
openssl genrsa -out client.key 2048 openssl req -new -key client.key -subj "/CN=client" -out client.csr echo extendedKeyUsage=clientAuth > extfile.cnf openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -set_serial 02 -extfile extfile.cnf -out client.crt -days 365<style></style>
Server.go
双向认证时需定制http.Server,增加CA证书等。
定制的http.Server的Handler是一个interface,需要实现ServeHTTP()接口函数;
加载服务端的公钥和私钥用于解密客户端发送过来的随机字符;
加载CA证书是为了验证客户端的证书是否合格;
package main import ( "fmt" "net/http" "io/ioutil" "crypto/tls" "crypto/x509" ) type myhandler struct{ } func (h *myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, " Hi, This is an example of https service in golang!\n") } func main(){ pool := x509.NewCertPool() caCertPath := "cert/ca.crt" caCrt, err := ioutil.ReadFile(caCertPath) if err != nil { fmt.Println("ReadFile err: ", err) return } pool.AppendCertsFromPEM(caCrt) s := &http.Server{ Addr: ":8088", Handler: &myhandler{}, TLSConfig: &tls.Config{ ClientCAs: pool, ClientAuth: tls.RequireAndVerifyClientCert, }, } fmt.Println("listen...") err = s.ListenAndServeTLS("cert/server.crt", "cert/server.key") if err != nil { fmt.Println(err) } }<style></style>
Client.go
Client需要定制http.Transport以加载客户端证书和CA证书。
Client的证书需要tls.LoadX509KeyPair()导入。
package main import ( "fmt" "io/ioutil" "net/http" "crypto/tls" "crypto/x509" "golang.org/x/net/http2" ) func main(){ // x509.Certificate pool := x509.NewCertPool() caCertPath := "cert/ca.crt" caCrt, err := ioutil.ReadFile(caCertPath) if err != nil { fmt.Println("ReadFile err:", err) return } pool.AppendCertsFromPEM(caCrt) cliCrt, err := tls.LoadX509KeyPair("cert/client.crt", "cert/client.key") if err != nil { fmt.Println("LoadX509keypair err: ", err) return } // tr := &http2.Transport{ // http2协议 tr := &http.Transport{ // http1.1协议 TLSClientConfig: &tls.Config{ RootCAs: pool, Certificates: []tls.Certificate{cliCrt}, }, } client := &http.Client{Transport: tr} //resp, err := client.Get("https://localhost:8088") resp, err := client.Get("https://server:8088") if err != nil { fmt.Println("http get error: ", err) panic(err) } body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) fmt.Println(resp.Status) }<style></style>
Curl验证
$ curl --cacert cert/ca.crt --cert cert/client.crt --key cert/client.key https://server:8088 Hi, This is an example of https service in golang!<style></style>
Client测试
$ go run dul_client.go Hi, This is an example of https service in golang<style></style>
200 OK
HTTP/1.1双向认证(19数据帧)
<style></style>HTTP/2双向认证(24数据帧)
<style></style>
参考:
1. Golang之双向认证 简书