本篇文章中,將描述如何使用go創(chuàng)建CA,并使用CA簽署證書。在使用openssl創(chuàng)建證書時(shí),遵循的步驟是 創(chuàng)建秘鑰 > 創(chuàng)建CA > 生成要頒發(fā)證書的秘鑰 > 使用CA簽發(fā)證書。這種步驟,那么我們現(xiàn)在就來(lái)嘗試下。
創(chuàng)新互聯(lián)建站專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站建設(shè)、網(wǎng)站制作、云陽(yáng)網(wǎng)絡(luò)推廣、微信小程序、云陽(yáng)網(wǎng)絡(luò)營(yíng)銷、云陽(yáng)企業(yè)策劃、云陽(yáng)品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)建站為所有大學(xué)生創(chuàng)業(yè)者提供云陽(yáng)建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:www.cdcxhl.com
首先,會(huì)從將從創(chuàng)建 CA 開始。CA 會(huì)被用來(lái)簽署其他證書
// 對(duì)證書進(jìn)行簽名
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{""},
},
NotBefore: time.Now(), // 生效時(shí)間
NotAfter: time.Now().AddDate(10, 0, 0), // 過(guò)期時(shí)間 年月日
IsCA: true, // 表示用于CA
// openssl 中的 extendedKeyUsage = clientAuth, serverAuth 字段
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
// openssl 中的 keyUsage 字段
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
接下來(lái)需要對(duì)證書生成公鑰和私鑰
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
}
我們看到的證書內(nèi)容是PEM編碼后的,現(xiàn)在caBytes
我們有了生成的證書,我們將其進(jìn)行 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
屬性有稍微不同,需要進(jìn)行一些修改
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{""},
},
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,
}
注:這里會(huì)在證書中特別添加了
DNS
和IP
(這個(gè)不是必須的),這個(gè)選項(xiàng)的增加代表的我們的證書可以支持多域名
為該證書創(chuàng)建私鑰和公鑰:
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return err
}
有了上述的內(nèi)容后,可以創(chuàng)建證書并用CA進(jìn)行簽名
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),
})
創(chuàng)建一個(gè) ca.go
里面是創(chuàng)建ca和頒發(fā)證書的邏輯
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
}
// 對(duì)證書進(jìn)行簽名
ca.CSR = &x509.Certificate{
SerialNumber: big.NewInt(rand.Int63n(2000)),
Subject: *sub,
NotBefore: time.Now(), // 生效時(shí)間
NotAfter: time.Now().AddDate(expire, 0, 0), // 過(guò)期時(shí)間
IsCA: true, // 表示用于CA
// openssl 中的 extendedKeyUsage = clientAuth, serverAuth 字段
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
// openssl 中的 keyUsage 字段
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
// 創(chuàng)建證書
// 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),
})
// 進(jìn)行PEM編碼,編碼就是直接cat證書里面內(nèi)容顯示的東西
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
}
如果需要使用的話,可以引用這些函數(shù)
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{""},
}
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
這里是因?yàn)?x509.CreateCertificate
的參數(shù) privatekey
需要傳入引用變量,而傳入的是一個(gè)普通變量
注:x509: only RSA and ECDSA public keys supported
extendedKeyUsage
:增強(qiáng)型密鑰用法(參見"new_oids"字段):服務(wù)器身份驗(yàn)證、客戶端身份驗(yàn)證、時(shí)間戳。
extendedKeyUsage = critical,serverAuth, clientAuth, timeStamping
keyUsage
: 密鑰用法,防否認(rèn)(nonRepudiation)、數(shù)字簽名(digitalSignature)、密鑰加密(keyEncipherment)。
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
Reference
golang ca and signed cert go
package x509