篮之新喜 发表于 2022-8-21 13:47:54

通过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,
}注:这里会在证书中特别添加了 DNS 和 IP (这个不是必须的),这个选项的增加代表的我们的证书可以支持多域名
为该证书创建私钥和公钥:
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{}
                errerror
        )
        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, timeStampingkeyUsage : 密钥用法,防否认(nonRepudiation)、数字签名(digitalSignature)、密钥加密(keyEncipherment)。
keyUsage = nonRepudiation, digitalSignature, keyEnciphermentReference
golang ca and signed cert go
package x509
出处:http://lc161616.cnblogs.com/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 阿里云优惠:点击力享低价墨墨学英语:帮忙点一下
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 通过Go语言创建CA与签发证书