通过Go语言创建CA与签发证书

打印 上一主题 下一主题

主题 780|帖子 780|积分 2340

本篇文章中,将描述如何使用go创建CA,并使用CA签署证书。在使用openssl创建证书时,遵循的步骤是 创建秘钥 > 创建CA > 生成要颁发证书的秘钥 > 使用CA签发证书。这种步骤,那么我们现在就来尝试下。
创建证书的颁发机构

首先,会从将从创建 CA 开始。CA 会被用来签署其他证书
  1. // 对证书进行签名
  2. ca := &x509.Certificate{
  3.         SerialNumber: big.NewInt(2019),
  4.         Subject: pkix.Name{
  5.         CommonName:    "domain name",
  6.                 Organization:  []string{"Company, INC."},
  7.                 Country:       []string{"US"},
  8.                 Province:      []string{""},
  9.                 Locality:      []string{"San Francisco"},
  10.                 StreetAddress: []string{"Golden Gate Bridge"},
  11.                 PostalCode:    []string{"94016"},
  12.         },
  13.         NotBefore:             time.Now(),  // 生效时间
  14.         NotAfter:              time.Now().AddDate(10, 0, 0), // 过期时间 年月日
  15.         IsCA:                  true, // 表示用于CA
  16.     // openssl 中的 extendedKeyUsage = clientAuth, serverAuth 字段
  17.         ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
  18.     // openssl 中的 keyUsage 字段
  19.         KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  20.         BasicConstraintsValid: true,
  21. }
复制代码
接下来需要对证书生成公钥和私钥
  1. caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
  2. if err != nil {
  3.         return err
  4. }
复制代码
然后生成证书:
  1. caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
  2. if err != nil {
  3.         return err
  4. }
复制代码
我们看到的证书内容是PEM编码后的,现在caBytes我们有了生成的证书,我们将其进行 PEM 编码以供以后使用:
  1. caPEM := new(bytes.Buffer)
  2. pem.Encode(caPEM, &pem.Block{
  3.         Type:  "CERTIFICATE",
  4.         Bytes: caBytes,
  5. })
  6. caPrivKeyPEM := new(bytes.Buffer)
  7. pem.Encode(caPrivKeyPEM, &pem.Block{
  8.         Type:  "RSA PRIVATE KEY",
  9.         Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
  10. })
复制代码
创建证书

证书的 x509.Certificate 与CA的 x509.Certificate 属性有稍微不同,需要进行一些修改
  1. cert := &x509.Certificate{
  2.         SerialNumber: big.NewInt(1658),
  3.         Subject: pkix.Name{
  4.         CommonName:    "domain name",
  5.                 Organization:  []string{"Company, INC."},
  6.                 Country:       []string{"US"},
  7.                 Province:      []string{""},
  8.                 Locality:      []string{"San Francisco"},
  9.                 StreetAddress: []string{"Golden Gate Bridge"},
  10.                 PostalCode:    []string{"94016"},
  11.         },
  12.     IPAddresses:  []net.IP{}, // 这里就是openssl配置文件中 subjectAltName 里的 IP:/IP=
  13.     DNSNames:     []string{}, // 这里就是openssl配置文件中 subjectAltName 里的 DNS:/DNS=
  14.         NotBefore:    time.Now(),
  15.         NotAfter:     time.Now().AddDate(10, 0, 0),
  16.         SubjectKeyId: []byte{1, 2, 3, 4, 6},
  17.     // 这里就是openssl中的extendedKeyUsage
  18.         ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
  19.         KeyUsage:     x509.KeyUsageDigitalSignature,
  20. }
复制代码
注:这里会在证书中特别添加了 DNS 和 IP (这个不是必须的),这个选项的增加代表的我们的证书可以支持多域名
为该证书创建私钥和公钥:
  1. certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
  2. if err != nil {
  3.         return err
  4. }
复制代码
使用CA签署证书

有了上述的内容后,可以创建证书并用CA进行签名
  1. certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey)
  2. if err != nil {
  3.         return err
  4. }
复制代码
要保存成证书格式需要做PEM编码
  1. certPEM := new(bytes.Buffer)
  2. pem.Encode(certPEM, &pem.Block{
  3.         Type:  "CERTIFICATE",
  4.         Bytes: certBytes,
  5. })
  6. certPrivKeyPEM := new(bytes.Buffer)
  7. pem.Encode(certPrivKeyPEM, &pem.Block{
  8.         Type:  "RSA PRIVATE KEY",
  9.         Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
  10. })
复制代码
把上面内容融合为一起

创建一个 ca.go 里面是创建ca和颁发证书的逻辑
  1. package main
  2. import (
  3.         "bytes"
  4.         cr "crypto/rand"
  5.         "crypto/rsa"
  6.         "crypto/x509"
  7.         "crypto/x509/pkix"
  8.         "encoding/pem"
  9.         "math/big"
  10.         "math/rand"
  11.         "net"
  12.         "os"
  13.         "time"
  14. )
  15. type CERT struct {
  16.         CERT       []byte
  17.         CERTKEY    *rsa.PrivateKey
  18.         CERTPEM    *bytes.Buffer
  19.         CERTKEYPEM *bytes.Buffer
  20.         CSR        *x509.Certificate
  21. }
  22. func CreateCA(sub *pkix.Name, expire int) (*CERT, error) {
  23.         var (
  24.                 ca  = new(CERT)
  25.                 err error
  26.         )
  27.         if expire < 1 {
  28.                 expire = 1
  29.         }
  30.         // 为ca生成私钥
  31.         ca.CERTKEY, err = rsa.GenerateKey(cr.Reader, 4096)
  32.         if err != nil {
  33.                 return nil, err
  34.         }
  35.         // 对证书进行签名
  36.         ca.CSR = &x509.Certificate{
  37.                 SerialNumber: big.NewInt(rand.Int63n(2000)),
  38.                 Subject:      *sub,
  39.                 NotBefore:    time.Now(),                       // 生效时间
  40.                 NotAfter:     time.Now().AddDate(expire, 0, 0), // 过期时间
  41.                 IsCA:         true,                             // 表示用于CA
  42.                 // openssl 中的 extendedKeyUsage = clientAuth, serverAuth 字段
  43.                 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
  44.                 // openssl 中的 keyUsage 字段
  45.                 KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  46.                 BasicConstraintsValid: true,
  47.         }
  48.         // 创建证书
  49.         // caBytes 就是生成的证书
  50.         ca.CERT, err = x509.CreateCertificate(cr.Reader, ca.CSR, ca.CSR, &ca.CERTKEY.PublicKey, ca.CERTKEY)
  51.         if err != nil {
  52.                 return nil, err
  53.         }
  54.         ca.CERTPEM = new(bytes.Buffer)
  55.         pem.Encode(ca.CERTPEM, &pem.Block{
  56.                 Type:  "CERTIFICATE",
  57.                 Bytes: ca.CERT,
  58.         })
  59.         ca.CERTKEYPEM = new(bytes.Buffer)
  60.         pem.Encode(ca.CERTKEYPEM, &pem.Block{
  61.                 Type:  "RSA PRIVATE KEY",
  62.                 Bytes: x509.MarshalPKCS1PrivateKey(ca.CERTKEY),
  63.         })
  64.         // 进行PEM编码,编码就是直接cat证书里面内容显示的东西
  65.         return ca, nil
  66. }
  67. func Req(ca *x509.Certificate, sub *pkix.Name, expire int, dns []string, ip []net.IP) (*CERT, error) {
  68.         var (
  69.                 cert = &CERT{}
  70.                 err  error
  71.         )
  72.         cert.CERTKEY, err = rsa.GenerateKey(cr.Reader, 4096)
  73.         if err != nil {
  74.                 return nil, err
  75.         }
  76.         if expire < 1 {
  77.                 expire = 1
  78.         }
  79.         cert.CSR = &x509.Certificate{
  80.                 SerialNumber: big.NewInt(rand.Int63n(2000)),
  81.                 Subject:      *sub,
  82.                 IPAddresses:  ip,
  83.                 DNSNames:     dns,
  84.                 NotBefore:    time.Now(),
  85.                 NotAfter:     time.Now().AddDate(expire, 0, 0),
  86.                 SubjectKeyId: []byte{1, 2, 3, 4, 6},
  87.                 ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
  88.                 KeyUsage:     x509.KeyUsageDigitalSignature,
  89.         }
  90.         cert.CERT, err = x509.CreateCertificate(cr.Reader, cert.CSR, ca, &cert.CERTKEY.PublicKey, cert.CERTKEY)
  91.         if err != nil {
  92.                 return nil, err
  93.         }
  94.         cert.CERTPEM = new(bytes.Buffer)
  95.         pem.Encode(cert.CERTPEM, &pem.Block{
  96.                 Type:  "CERTIFICATE",
  97.                 Bytes: cert.CERT,
  98.         })
  99.         cert.CERTKEYPEM = new(bytes.Buffer)
  100.         pem.Encode(cert.CERTKEYPEM, &pem.Block{
  101.                 Type:  "RSA PRIVATE KEY",
  102.                 Bytes: x509.MarshalPKCS1PrivateKey(cert.CERTKEY),
  103.         })
  104.         return cert, nil
  105. }
  106. func Write(cert *CERT, file string) error {
  107.         keyFileName := file + ".key"
  108.         certFIleName := file + ".crt"
  109.         kf, err := os.Create(keyFileName)
  110.         if err != nil {
  111.                 return err
  112.         }
  113.         defer kf.Close()
  114.         if _, err := kf.Write(cert.CERTKEYPEM.Bytes()); err != nil {
  115.                 return err
  116.         }
  117.         cf, err := os.Create(certFIleName)
  118.         if err != nil {
  119.                 return err
  120.         }
  121.         if _, err := cf.Write(cert.CERTPEM.Bytes()); err != nil {
  122.                 return err
  123.         }
  124.         return nil
  125. }
复制代码
如果需要使用的话,可以引用这些函数
  1. package main
  2. import (
  3.         "crypto/x509/pkix"
  4.         "log"
  5.         "net"
  6. )
  7. func main() {
  8.         subj := &pkix.Name{
  9.                 CommonName:    "chinamobile.com",
  10.                 Organization:  []string{"Company, INC."},
  11.                 Country:       []string{"US"},
  12.                 Province:      []string{""},
  13.                 Locality:      []string{"San Francisco"},
  14.                 StreetAddress: []string{"Golden Gate Bridge"},
  15.                 PostalCode:    []string{"94016"},
  16.         }
  17.         ca, err := CreateCA(subj, 10)
  18.         if err != nil {
  19.                 log.Panic(err)
  20.         }
  21.         Write(ca, "./ca")
  22.         crt, err := Req(ca.CSR, subj, 10, []string{"test.default.svc", "test"}, []net.IP{})
  23.         if err != nil {
  24.                 log.Panic(err)
  25.         }
  26.         Write(crt, "./tls")
  27. }
复制代码
遇到的问题

panic: x509: unsupported public key type: rsa.PublicKey
这里是因为 x509.CreateCertificate 的参数 privatekey 需要传入引用变量,而传入的是一个普通变量
注:x509: only RSA and ECDSA public keys supported
一些参数的意思

extendedKeyUsage :增强型密钥用法(参见"new_oids"字段):服务器身份验证、客户端身份验证、时间戳。
  1. extendedKeyUsage = critical,serverAuth, clientAuth, timeStamping
复制代码
keyUsage : 密钥用法,防否认(nonRepudiation)、数字签名(digitalSignature)、密钥加密(keyEncipherment)。
  1. keyUsage = nonRepudiation, digitalSignature, keyEncipherment
复制代码
Reference
golang ca and signed cert go
package x509
出处:http://lc161616.cnblogs.com/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 阿里云优惠:点击力享低价墨墨学英语:帮忙点一下
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

圆咕噜咕噜

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表