用golang生成密钥和签名证书

https://github.com/timest/blog/issues/14

go version:

  1. $ go version
  2. go version go1.10 darwin/amd64

注意每个代码片段里的变量,在全文里是共用的。比如第一步里生成的私钥,在后面签名将直接拿来使用。

生成ECDSA密钥

  1. type ecdsaGen struct {
  2. curve elliptic.Curve
  3. }
  4. func (e *ecdsaGen) KeyGen() (key *ecdsa.PrivateKey, err error) {
  5. privKey, err := ecdsa.GenerateKey(e.curve, rand.Reader)
  6. if err != nil {
  7. return nil, err
  8. }
  9. return privKey, nil
  10. }
  11. func checkError(err error) {
  12. if err != nil {
  13. log.Panic(err)
  14. }
  15. }
  16. func main() {
  17. // 生成ecdsa
  18. e := &ecdsaGen{ curve: elliptic.P256()}
  19. priKey, err := e.KeyGen()
  20. checkError(err)
  21. }

priKey是我们生成的密钥,有时候我们想保存为一个pem格式的密钥文件:

  1. priKeyEncode, err := x509.MarshalECPrivateKey(priKey)
  2. checkError(err)
  3. f, err := os.Create("ec.pem")
  4. checkError(err)
  5. pem.Encode(f, &pem.Block{Type: "EC PRIVATE KEY", Bytes: priKeyEncode})
  6. f.close()

经过x509.MarshalECPrivateKey处理过后返回的是[]byte类型的密钥,然后通过pem.Encode写入到文件里。

然后我们可以用:$ openssl ec -text -in ec.pem -noout 命令来查看生成的密钥文件是否正确,后面同样有可以用openssl来检查生成的证书是否正确。
更多openssl命令可以查看openssl常用命令之创建、检查密钥和证书

证书

这里举两个例子,一个是用自己的密钥进行为证书签名,简称自签。还有一种现实中经常遇到的情况,是使用用户提供的密钥生成证书

自签

  1. // 根据ecdsa密钥生成特征标识码
  2. func priKeyHash(priKey *ecdsa.PrivateKey) []byte{
  3. hash := sha256.New()
  4. hash.Write(elliptic.Marshal(priKey.Curve, priKey.PublicKey.X, priKey.PublicKey.Y))
  5. return hash.Sum(nil)
  6. }
  7. func main() {
  8. template := x509.Certificate{
  9. SerialNumber: serialNumber,
  10. NotBefore: notBefore,
  11. NotAfter: notBefore.Add(expiry).UTC(),
  12. BasicConstraintsValid: true,
  13. IsCA: true,
  14. KeyUsage: x509.KeyUsageDigitalSignature |
  15. x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign |
  16. x509.KeyUsageCRLSign,
  17. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
  18. Subject: pkix.Name{
  19. Country: []string{"CN"},
  20. Locality: []string{"zhongguancun"},
  21. Province: []string{"Beijing"},
  22. OrganizationalUnit: []string{"tect"},
  23. Organization: []string{"paradise"},
  24. StreetAddress: []string{"street", "address", "demo"},
  25. PostalCode: []string{"310000"},
  26. CommonName: "demo.example.com",
  27. },
  28. }
  29. template.SubjectKeyId = priKeyHash(priKey)
  30. x509certEncode, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey, priKey)
  31. checkError(err)
  32. crt, err := os.Create("cert.crt")
  33. checkError(err)
  34. pem.Encode(crt, &pem.Block{Type: "CERTIFICATE", Bytes: x509certEncode})
  35. crt.Close()
  36. }

先生成x509的证书模板,然后通过x509.CreateCertificate创建证书,看下这个函数的定义:

  1. func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv interface{}) (cert []byte, err error) {
  2. ...
  3. }

当参数templateparent一样时,即为自签,代码最后将证书保存为cert.crt
如果想用openssl命令自签请参考:创建证书(自签名)

使用用户提供的密钥生成证书

我们假设有个bob,想让我们为他的密钥提供证书签名:

  1. func main() {
  2. // 使用bob的密钥进行证书签名
  3. bobPriKey, _ := e.KeyGen()
  4. bobPriKeyEncode, err := x509.MarshalECPrivateKey(bobPriKey)
  5. checkError(err)
  6. bobf, err := os.Create("bob.pem")
  7. checkError(err)
  8. pem.Encode(bobf, &pem.Block{Type: "EC PRIVATE KEY", Bytes: bobPriKeyEncode})
  9. bobf.Close()
  10. bobPubKey := bobPriKey.Public()
  11. bobSerialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
  12. notBefore = time.Now().Add(-5 * time.Minute).UTC()
  13. bobTemplate := x509.Certificate{
  14. SerialNumber: bobSerialNumber,
  15. NotBefore: notBefore,
  16. NotAfter: notBefore.Add(expiry).UTC(),
  17. BasicConstraintsValid: true,
  18. IsCA: false,
  19. KeyUsage: x509.KeyUsageDigitalSignature |
  20. x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign |
  21. x509.KeyUsageCRLSign,
  22. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
  23. Subject: pkix.Name{
  24. Country: []string{"CN"},
  25. Locality: []string{"Locality"},
  26. Province: []string{"Beijing"},
  27. OrganizationalUnit: []string{"tect"},
  28. Organization: []string{"paradise"},
  29. StreetAddress: []string{"street", "address", "demo"},
  30. PostalCode: []string{"310000"},
  31. CommonName: "demo.example.com",
  32. },
  33. }
  34. bobTemplate.SubjectKeyId = priKeyHash(bobPriKey)
  35. parent, err := x509.ParseCertificate(x509certEncode)
  36. checkError(err)
  37. // 这里的第三个参数是我们上面自签的证书,公钥是bob的公钥
  38. bobCertEncode, err := x509.CreateCertificate(rand.Reader, &bobTemplate, parent, bobPubKey, priKey)
  39. checkError(err)
  40. bcrt, _ := os.Create("bob.crt")
  41. pem.Encode(bcrt, &pem.Block{Type: "CERTIFICATE", Bytes: bobCertEncode})
  42. bcrt.close()
  43. }

代码和自签差不多,要注意的是这一句代码:

  1. // 这里的第三个参数是我们上面自签的证书,公钥是bob的公钥
  2. bobCertEncode, err := x509.CreateCertificate(rand.Reader, &bobTemplate, parent, bobPubKey, priKey)

最后附上全文代码:Gist !

ft_authoradmin  ft_create_time2020-11-09 16:01
 ft_update_time2020-11-09 16:01