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


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

    创建证书的颁发机构#

    首先,会从将从创建 CA 开始。CA 会被用来签署其他证书

    // 对证书进行签名
    ca := &x509.Certificate{
    	SerialNumber: big.NewInt(2019),
    	Subject: pkix.Name{
            CommonName:    "domain name",
    		Organization:  []string{"Company, INC."},
    		Country:       []string{"US"},
    		Province:      []string{""},
    		Locality:      []string{"San Francisco"},
    		StreetAddress: []string{"Golden Gate Bridge"},
    		PostalCode:    []string{"94016"},
    	},
    	NotBefore:             time.Now(),  // 生效时间
    	NotAfter:              time.Now().AddDate(10, 0, 0), // 过期时间 年月日
    	IsCA:                  true, // 表示用于CA
        // openssl 中的 extendedKeyUsage = clientAuth, serverAuth 字段
    	ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
        // openssl 中的 keyUsage 字段
    	KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    	BasicConstraintsValid: true,
    }
    

    接下来需要对证书生成公钥和私钥

    caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
    if err != nil {
    	return err
    }
    

    然后生成证书:

    caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
    if err != nil {
    	return err
    }
    

    我们看到的证书内容是PEM编码后的,现在caBytes我们有了生成的证书,我们将其进行 PEM 编码以供以后使用:

    caPEM := new(bytes.Buffer)
    pem.Encode(caPEM, &pem.Block{
    	Type:  "CERTIFICATE",
    	Bytes: caBytes,
    })
    
    caPrivKeyPEM := new(bytes.Buffer)
    pem.Encode(caPrivKeyPEM, &pem.Block{
    	Type:  "RSA PRIVATE KEY",
    	Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
    })
    

    创建证书#

    证书的 x509.Certificate 与CA的 x509.Certificate 属性有稍微不同,需要进行一些修改

    cert := &x509.Certificate{
    	SerialNumber: big.NewInt(1658),
    	Subject: pkix.Name{
            CommonName:    "domain name",
    		Organization:  []string{"Company, INC."},
    		Country:       []string{"US"},
    		Province:      []string{""},
    		Locality:      []string{"San Francisco"},
    		StreetAddress: []string{"Golden Gate Bridge"},
    		PostalCode:    []string{"94016"},
    	},
        IPAddresses:  []net.IP{}, // 这里就是openssl配置文件中 subjectAltName 里的 IP:/IP=
        DNSNames:     []string{}, // 这里就是openssl配置文件中 subjectAltName 里的 DNS:/DNS=
    	NotBefore:    time.Now(),
    	NotAfter:     time.Now().AddDate(10, 0, 0),
    	SubjectKeyId: []byte{1, 2, 3, 4, 6},
        // 这里就是openssl中的extendedKeyUsage 
    	ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
    	KeyUsage:     x509.KeyUsageDigitalSignature,
    }
    

    注:这里会在证书中特别添加了 DNSIP (这个不是必须的),这个选项的增加代表的我们的证书可以支持多域名

    为该证书创建私钥和公钥:

    certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
    if err != nil {
    	return err
    }
    

    使用CA签署证书#

    有了上述的内容后,可以创建证书并用CA进行签名

    certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey)
    if err != nil {
    	return err
    }
    

    要保存成证书格式需要做PEM编码

    certPEM := new(bytes.Buffer)
    pem.Encode(certPEM, &pem.Block{
    	Type:  "CERTIFICATE",
    	Bytes: certBytes,
    })
    
    certPrivKeyPEM := new(bytes.Buffer)
    pem.Encode(certPrivKeyPEM, &pem.Block{
    	Type:  "RSA PRIVATE KEY",
    	Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
    })
    

    把上面内容融合为一起#

    创建一个 ca.go 里面是创建ca和颁发证书的逻辑

    package main
    
    import (
    	"bytes"
    	cr "crypto/rand"
    	"crypto/rsa"
    	"crypto/x509"
    	"crypto/x509/pkix"
    	"encoding/pem"
    	"math/big"
    	"math/rand"
    	"net"
    	"os"
    	"time"
    )
    
    type CERT struct {
    	CERT       []byte
    	CERTKEY    *rsa.PrivateKey
    	CERTPEM    *bytes.Buffer
    	CERTKEYPEM *bytes.Buffer
    	CSR        *x509.Certificate
    }
    
    func CreateCA(sub *pkix.Name, expire int) (*CERT, error) {
    	var (
    		ca  = new(CERT)
    		err error
    	)
    
    	if expire < 1 {
    		expire = 1
    	}
    	// 为ca生成私钥
    	ca.CERTKEY, err = rsa.GenerateKey(cr.Reader, 4096)
    	if err != nil {
    		return nil, err
    	}
    
    	// 对证书进行签名
    	ca.CSR = &x509.Certificate{
    		SerialNumber: big.NewInt(rand.Int63n(2000)),
    		Subject:      *sub,
    		NotBefore:    time.Now(),                       // 生效时间
    		NotAfter:     time.Now().AddDate(expire, 0, 0), // 过期时间
    		IsCA:         true,                             // 表示用于CA
    		// openssl 中的 extendedKeyUsage = clientAuth, serverAuth 字段
    		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
    		// openssl 中的 keyUsage 字段
    		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    		BasicConstraintsValid: true,
    	}
    	// 创建证书
    	// caBytes 就是生成的证书
    	ca.CERT, err = x509.CreateCertificate(cr.Reader, ca.CSR, ca.CSR, &ca.CERTKEY.PublicKey, ca.CERTKEY)
    	if err != nil {
    		return nil, err
    	}
    	ca.CERTPEM = new(bytes.Buffer)
    	pem.Encode(ca.CERTPEM, &pem.Block{
    		Type:  "CERTIFICATE",
    		Bytes: ca.CERT,
    	})
    	ca.CERTKEYPEM = new(bytes.Buffer)
    	pem.Encode(ca.CERTKEYPEM, &pem.Block{
    		Type:  "RSA PRIVATE KEY",
    		Bytes: x509.MarshalPKCS1PrivateKey(ca.CERTKEY),
    	})
    
    	// 进行PEM编码,编码就是直接cat证书里面内容显示的东西
    	return ca, nil
    }
    
    func Req(ca *x509.Certificate, sub *pkix.Name, expire int, dns []string, ip []net.IP) (*CERT, error) {
    	var (
    		cert = &CERT{}
    		err  error
    	)
    	cert.CERTKEY, err = rsa.GenerateKey(cr.Reader, 4096)
    	if err != nil {
    		return nil, err
    	}
    	if expire < 1 {
    		expire = 1
    	}
    	cert.CSR = &x509.Certificate{
    		SerialNumber: big.NewInt(rand.Int63n(2000)),
    		Subject:      *sub,
    		IPAddresses:  ip,
    		DNSNames:     dns,
    		NotBefore:    time.Now(),
    		NotAfter:     time.Now().AddDate(expire, 0, 0),
    		SubjectKeyId: []byte{1, 2, 3, 4, 6},
    		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
    		KeyUsage:     x509.KeyUsageDigitalSignature,
    	}
    
    	cert.CERT, err = x509.CreateCertificate(cr.Reader, cert.CSR, ca, &cert.CERTKEY.PublicKey, cert.CERTKEY)
    	if err != nil {
    		return nil, err
    	}
    
    	cert.CERTPEM = new(bytes.Buffer)
    	pem.Encode(cert.CERTPEM, &pem.Block{
    		Type:  "CERTIFICATE",
    		Bytes: cert.CERT,
    	})
    	cert.CERTKEYPEM = new(bytes.Buffer)
    	pem.Encode(cert.CERTKEYPEM, &pem.Block{
    		Type:  "RSA PRIVATE KEY",
    		Bytes: x509.MarshalPKCS1PrivateKey(cert.CERTKEY),
    	})
    	return cert, nil
    }
    
    func Write(cert *CERT, file string) error {
    	keyFileName := file + ".key"
    	certFIleName := file + ".crt"
    	kf, err := os.Create(keyFileName)
    	if err != nil {
    		return err
    	}
    	defer kf.Close()
    
    	if _, err := kf.Write(cert.CERTKEYPEM.Bytes()); err != nil {
    		return err
    	}
    
    	cf, err := os.Create(certFIleName)
    	if err != nil {
    		return err
    	}
    	if _, err := cf.Write(cert.CERTPEM.Bytes()); err != nil {
    		return err
    	}
    	return nil
    }
    
    折叠

    如果需要使用的话,可以引用这些函数

    package main
    
    import (
    	"crypto/x509/pkix"
    	"log"
    	"net"
    )
    
    func main() {
    	subj := &pkix.Name{
    		CommonName:    "chinamobile.com",
    		Organization:  []string{"Company, INC."},
    		Country:       []string{"US"},
    		Province:      []string{""},
    		Locality:      []string{"San Francisco"},
    		StreetAddress: []string{"Golden Gate Bridge"},
    		PostalCode:    []string{"94016"},
    	}
    	ca, err := CreateCA(subj, 10)
    	if err != nil {
    		log.Panic(err)
    	}
    
    	Write(ca, "./ca")
    
    	crt, err := Req(ca.CSR, subj, 10, []string{"test.default.svc", "test"}, []net.IP{})
    
    	if err != nil {
    		log.Panic(err)
    	}
    
    	Write(crt, "./tls")
    }
    
    折叠

    遇到的问题#

    panic: x509: unsupported public key type: rsa.PublicKey

    这里是因为 x509.CreateCertificate 的参数 privatekey 需要传入引用变量,而传入的是一个普通变量

    注:x509: only RSA and ECDSA public keys supported

    一些参数的意思#

    extendedKeyUsage :增强型密钥用法(参见"new_oids"字段):服务器身份验证、客户端身份验证、时间戳。

    extendedKeyUsage = critical,serverAuth, clientAuth, timeStamping
    

    keyUsage : 密钥用法,防否认(nonRepudiation)、数字签名(digitalSignature)、密钥加密(keyEncipherment)。

    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    

    Reference

    golang ca and signed cert go

    package x509

  • 相关阅读:
    【POI】word读取常见问题(涉及格式:doc、docx、rtf)
    欧拉道路/回路总结
    2.2 发布与订阅python
    windows 远程连接 ubuntu桌面xrdp
    前端er须知的Nginx技巧
    游戏复用列表实现思路
    LeetCode 2483. 商店的最少代价【字符串,前后缀分解】1494
    pytorch中nn.DataParallel多次使用
    python获取时间字符串的时间差
    计算机网络原理 谢希仁(第8版)第四章习题答案
  • 原文地址:https://www.cnblogs.com/Cylon/p/16436126.html