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

博客 分享
0 150
张三
张三 2022-07-01 23:01:16
悬赏:0 积分 收藏

通过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 mainimport (	"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 mainimport (	"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

posted @ 2022-07-01 22:15 Cylon 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员