当前位置:网站首页>通过Go语言创建CA与签发证书
通过Go语言创建CA与签发证书
2022-07-04 22:17:00 【Hermokrates】
本篇文章中,将描述如何使用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{
}
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
边栏推荐
- Why should garment enterprises talk about informatization?
- 《命令行上的数据科学第二版》校对活动重新启动
- LOGO special training camp section I identification logo and Logo Design Ideas
- 制作条形码的手机App推荐
- 30余家机构联合发起数字藏品行业倡议,未来会如何前进?
- Tiktok actual combat ~ the number of comments is updated synchronously
- 抖音实战~评论数量同步更新
- Practice and principle of PostgreSQL join
- Logo special training camp section II collocation relationship between words and graphics
- More than 30 institutions jointly launched the digital collection industry initiative. How will it move forward in the future?
猜你喜欢

Convolutional neural network model -- lenet network structure and code implementation

【OpenGL】笔记二十九、抗锯齿(MSAA)

都说软件测试很简单有手就行,但为何仍有这么多劝退的?

2022-07-04:以下go语言代码输出什么?A:true;B:false;C:编译错误。 package main import “fmt“ func main() { fmt.Pri

B站大量虚拟主播被集体强制退款:收入蒸发,还倒欠B站;乔布斯被追授美国总统自由勋章;Grafana 9 发布|极客头条

Play with grpc - go deep into concepts and principles

Locust performance test - environment construction and use
![[acwing] solution of the 58th weekly match](/img/e3/fd2c0ffbc9c7ca8a71875882d6c71b.png)
[acwing] solution of the 58th weekly match

Concurrent network modular reading notes transfer

LOGO特訓營 第一節 鑒別Logo與Logo設計思路
随机推荐
面试必备 LeetCode 链表算法题汇总,全程干货!
30余家机构联合发起数字藏品行业倡议,未来会如何前进?
Deveco device tool 3.0 release brings five capability upgrades to make intelligent device development more efficient
Why is Dameng data called the "first share" of domestic databases?
质量体系建设之路的分分合合
好用app推荐:扫描二维码、扫描条形码并查看历史
LOGO特訓營 第一節 鑒別Logo與Logo設計思路
Short video system source code, click the blank space of the screen, the keyboard does not automatically stow
业务太忙,真的是没时间搞自动化理由吗?
La prospérité est épuisée, les choses sont bonnes et mauvaises: Où puis - je aller pour un chef de station personnel?
Scala download and configuration
达梦数据凭什么被称为国产数据库“第一股”?
LOGO特训营 第四节 字体设计的重要性
Logo Camp d'entraînement section 3 techniques créatives initiales
《命令行上的数据科学第二版》校对活动重新启动
Why should garment enterprises talk about informatization?
[cooking record] - stir fried 1000 pieces of green pepper
You don't have to run away to delete the library! Detailed MySQL data recovery
SPSS安装激活教程(包含网盘链接)
Mysql root 账号如何重置密码