使用golang进行证书签发和双向认证

https://segmentfault.com/a/1190000009666888

前言

数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。证书签发涉及到了非对称加密方面的知识,这里介绍使用golang中的x509标准库进行证书自签发,还有证书签发后如何使用golang进行双向认证.

自签发证书

生成根证书

根证书是CA认证中心给自己颁发的证书,是信任链的起始点.这里我们自己做CA使用openssl命令来生成根证书.

首先生成私钥

  1. openssl genrsa -out key.pem 2048

然后根据私钥提取公钥

  1. openssl rsa -in key.pem -pubout -out key.pub

开始生成X509格式的自签名证书,会要求输入区别名DN的各项信息(国家,城市,组织,姓名,email等.

  1. penssl req -x509 -new -days 365 -key rsakey.pem -out cert.crt

到这里根证书就制作好了,下面开始使用golang根据根证书签发下一级证书.

使用golang自签发证书

关于自签发证书的流程,这里做个简单介绍.golang的x509标准库下有个Certificate结构,这个结构就是证书解析后对应的实体.新证书需要先生成秘钥对,然后使用根证书的私钥进行签名.证书和私钥以及公钥这里使用的是pem编码方式.

首先读取根证书的证书和私钥

  1. //解析根证书
  2. caFile, err := ioutil.ReadFile(rootCa)
  3. if err != nil {
  4. return
  5. }
  6. caBlock, _ := pem.Decode(caFile)
  7. cert, err := x509.ParseCertificate(caBlock.Bytes)
  8. if err != nil {
  9. return
  10. }
  11. //解析私钥
  12. keyFile, err := ioutil.ReadFile(rootKey)
  13. if err != nil {
  14. return
  15. }
  16. keyBlock, _ := pem.Decode(keyFile)
  17. praKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
  18. if err != nil {
  19. return
  20. }

然后需要生成新证书的模板,里面的字段根据自己需求填写,

  1. cer := &x509.Certificate{
  2. SerialNumber: big.NewInt(rd.Int63()), //证书序列号
  3. Subject: pkix.Name{
  4. Country: []string{"CN"},
  5. Organization: []string{"Easy"},
  6. OrganizationalUnit: []string{"Easy"},
  7. Province: []string{"ShenZhen"},
  8. CommonName: equi.Code,
  9. Locality: []string{"ShenZhen"},
  10. },
  11. NotBefore: time.Now(), //证书有效期开始时间
  12. NotAfter: time.Now().AddDate(1, 0, 0), //证书有效期结束时间
  13. BasicConstraintsValid: true, //基本的有效性约束
  14. IsCA: false, //是否是根证书
  15. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, //证书用途(客户端认证,数据加密)
  16. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment,
  17. EmailAddresses: []string{"test@test.com"},
  18. IPAddresses: []net.IP{net.ParseIP("192.168.1.59")},
  19. }

当获取到这种信息后就可以签发证书了,key和ca就是签发好的证书和私钥.

  1. //生成公钥私钥对
  2. priKey, err := rsa.GenerateKey(rand.Reader, 2048)
  3. if err != nil {
  4. return
  5. }
  6. ca, err = x509.CreateCertificate(rand.Reader, equiCer, rootCa, &priKey.PublicKey, rootKey)
  7. if err != nil {
  8. return
  9. }
  10. //编码证书文件和私钥文件
  11. caPem := &pem.Block{
  12. Type: "CERTIFICATE",
  13. Bytes: ca,
  14. }
  15. ca = pem.EncodeToMemory(caPem)
  16. buf := x509.MarshalPKCS1PrivateKey(priKey)
  17. keyPem := &pem.Block{
  18. Type: "PRIVATE KEY",
  19. Bytes: buf,
  20. }
  21. key = pem.EncodeToMemory(keyPem)

使用golang进行双向认证

客户端

  1. //加载客户端证书
  2. //这里加载的是服务端签发的
  3. cert, err := tls.LoadX509KeyPair("client_cert.pem", "client_key.pem")
  4. if err != nil {
  5. log.Fatalln(err)
  6. }
  7. config := &tls.Config{
  8. //这里先不验证服务端证书,是自己签发的呀
  9. InsecureSkipVerify: true,
  10. Certificates: []tls.Certificate{cert},
  11. }
  12. raddr, err := net.ResolveTCPAddr("tcp", "192.168.1.59:6001")
  13. if err != nil {
  14. log.Fatalln(err)
  15. }
  16. conn, err := net.DialTCP("tcp", nil, raddr)
  17. if err != nil {
  18. log.Fatalln(err)
  19. }
  20. tlsConn := tls.Client(conn, config)

tlsConn就和net.Conn一样了,当调用Wirte时就会进行握手,如果服务端证书不符合要求,就会返回错误.

服务端

  1. //这里读取的是根证书
  2. buf, err := ioutil.ReadFile(d.conf.Tls.CA)
  3. if err != nil {
  4. return
  5. }
  6. pool := x509.NewCertPool()
  7. pool.AppendCertsFromPEM(buf)
  8. //加载服务端证书
  9. cert, err := tls.LoadX509KeyPair(d.conf.Tls.Cert, d.conf.Tls.Key)
  10. if err != nil {
  11. return
  12. }
  13. tlsConfig := &tls.Config
  14. Certificates: []tls.Certificate{cert},
  15. ClientAuth: tls.RequireAndVerifyClientCert,
  16. ClientCAs: pool,
  17. }
  18. //accept到conn后
  19. tlsConn := tls.Server(conn, tlsConfig)

这个tlsConn和客户端的一样,也可以手动调用Handshake进行握手.

结语

这里只是进行了简单介绍,关于证书的知识还不止这些.比如x509标准库还可以生成签发证书请求,解析证书吊销列表等.多加练习才能对证书方面知识理解更深.


pinecone 348 2017-06-05 发布 linux golang 5k 次阅读 · 读完需要 13 分钟