数字证书是一个经证书受权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。证书签发涉及到了非对称加密方面的知识,这里介绍使用golang中的x509标准库进行证书自签发,还有证书签发后如何使用golang进行双向认证.git
根证书是CA认证中心给本身颁发的证书,是信任链的起始点.这里咱们本身作CA使用openssl命令来生成根证书.golang
首先生成私钥tcp
openssl genrsa -out key.pem 2048
而后根据私钥提取公钥ui
openssl rsa -in key.pem -pubout -out key.pub
开始生成X509格式的自签名证书,会要求输入区别名DN的各项信息(国家,城市,组织,姓名,email等.编码
penssl req -x509 -new -days 365 -key rsakey.pem -out cert.crt
到这里根证书就制做好了,下面开始使用golang根据根证书签发下一级证书.加密
关于自签发证书的流程,这里作个简单介绍.golang的x509标准库下有个Certificate结构,这个结构就是证书解析后对应的实体.新证书须要先生成秘钥对,而后使用根证书的私钥进行签名.证书和私钥以及公钥这里使用的是pem编码方式.code
首先读取根证书的证书和私钥ip
//解析根证书 caFile, err := ioutil.ReadFile(rootCa) if err != nil { return } caBlock, _ := pem.Decode(caFile) cert, err := x509.ParseCertificate(caBlock.Bytes) if err != nil { return } //解析私钥 keyFile, err := ioutil.ReadFile(rootKey) if err != nil { return } keyBlock, _ := pem.Decode(keyFile) praKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) if err != nil { return }
而后须要生成新证书的模板,里面的字段根据本身需求填写,ssl
cer := &x509.Certificate{ SerialNumber: big.NewInt(rd.Int63()), //证书序列号 Subject: pkix.Name{ Country: []string{"CN"}, Organization: []string{"Easy"}, OrganizationalUnit: []string{"Easy"}, Province: []string{"ShenZhen"}, CommonName: equi.Code, Locality: []string{"ShenZhen"}, }, NotBefore: time.Now(), //证书有效期开始时间 NotAfter: time.Now().AddDate(1, 0, 0), //证书有效期结束时间 BasicConstraintsValid: true, //基本的有效性约束 IsCA: false, //是不是根证书 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, //证书用途(客户端认证,数据加密) KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment, EmailAddresses: []string{"test@test.com"}, IPAddresses: []net.IP{net.ParseIP("192.168.1.59")}, }
当获取到这种信息后就能够签发证书了,key和ca就是签发好的证书和私钥.ci
//生成公钥私钥对 priKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return } ca, err = x509.CreateCertificate(rand.Reader, equiCer, rootCa, &priKey.PublicKey, rootKey) if err != nil { return } //编码证书文件和私钥文件 caPem := &pem.Block{ Type: "CERTIFICATE", Bytes: ca, } ca = pem.EncodeToMemory(caPem) buf := x509.MarshalPKCS1PrivateKey(priKey) keyPem := &pem.Block{ Type: "PRIVATE KEY", Bytes: buf, } key = pem.EncodeToMemory(keyPem)
客户端
//加载客户端证书 //这里加载的是服务端签发的 cert, err := tls.LoadX509KeyPair("client_cert.pem", "client_key.pem") if err != nil { log.Fatalln(err) } config := &tls.Config{ //这里先不验证服务端证书,是本身签发的呀 InsecureSkipVerify: true, Certificates: []tls.Certificate{cert}, } raddr, err := net.ResolveTCPAddr("tcp", "192.168.1.59:6001") if err != nil { log.Fatalln(err) } conn, err := net.DialTCP("tcp", nil, raddr) if err != nil { log.Fatalln(err) } tlsConn := tls.Client(conn, config)
tlsConn就和net.Conn同样了,当调用Wirte时就会进行握手,若是服务端证书不符合要求,就会返回错误.
服务端
//这里读取的是根证书 buf, err := ioutil.ReadFile(d.conf.Tls.CA) if err != nil { return } pool := x509.NewCertPool() pool.AppendCertsFromPEM(buf) //加载服务端证书 cert, err := tls.LoadX509KeyPair(d.conf.Tls.Cert, d.conf.Tls.Key) if err != nil { return } tlsConfig := &tls.Config Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: pool, } //accept到conn后 tlsConn := tls.Server(conn, tlsConfig)
这个tlsConn和客户端的同样,也能够手动调用Handshake进行握手.
这里只是进行了简单介绍,关于证书的知识还不止这些.好比x509标准库还能够生成签发证书请求,解析证书吊销列表等.多加练习才能对证书方面知识理解更深.