我正在尝试使用 OpenSSL 和 Go 代码生成客户端证书。我有一个 OpenSSL 脚本,可以生成具有所需扩展名的证书,我想使用 Go 代码实现相同的结果。
OpenSSL 使用的 options.ext 文件包含以下扩展名:
basicConstraints=CA:FALSE
authorityKeyIdentifier=keyid,issuer
subjectKeyIdentifier=hash
keyUsage=digitalSignature
extendedKeyUsage=clientAuth
我目前拥有的OpenSSL脚本如下:
openssl req \
-newkey rsa:2048 \
-keyout cert.crt \
-out cert.csr \
-nodes \
-sha256
openssl x509 \
-req \
-CA ca.crt \
-CAkey ca.key \
-in cert.csr \
-out cert.crt \
-days 365 \
-CAcreateserial \
-extfile options.ext \
-sha256
生成证书后,我可以使用以下命令查看其详细信息:
openssl x509 -in cert.crt -text -noout
生成的证书具有以下结构:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
xx:xx:xx:xx:xx:xx:xx:xx
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=xxx
Validity
Not Before: Jan 1 00:00:00 2023 GMT
Not After : Jan 1 00:00:00 2024 GMT
Subject: CN=xxx
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
...
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Authority Key Identifier:
DirName:CN=xxx
serial:xx:xx:xx:xx:xx:xx:xx:xx
X509v3 Subject Key Identifier:
...
X509v3 Key Usage:
Digital Signature
X509v3 Extended Key Usage:
TLS Web Client Authentication
Signature Algorithm: sha256WithRSAEncryption
它应该是这样的:
X509v3 Authority Key Identifier:
DirName:CN=xxx
serial:xx:xx:xx:xx:xx:xx:xx:xx
在我的 Go 代码中,我使用 x509 包生成证书。但是,我不确定如何设置 X509v3 Authority Key Identifier 扩展。这是我的 Go 代码的相关部分:
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"os"
"time"
)
...
var caCertificate *x509.Certificate
var caPrivateKey *rsa.PrivateKey
var authorityKeyIdentifierValue []byte // how to write this?
template := &x509.Certificate{
Subject: pkix.Name{
CommonName: "xxx",
},
ExtraExtensions: []pkix.Extension{
{
Id: asn1.ObjectIdentifier{2, 5, 29, 35},
Value: authorityKeyIdentifierValue,
},
},
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, 365),
IsCA: false,
BasicConstraintsValid: true,
}
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
// err
}
certificateBytes, err := x509.CreateCertificate(rand.Reader, template, caCertificate, &privateKey.PublicKey, caPrivateKey)
if err != nil {
// err
}
// out
如何将 DirName 和 serial 添加到 X509v3 Authority Key Identifier?
当我尝试这个时:
var caPublicKeyBytes []byte
publicKeyHash := (sha1.Sum(caPublicKeyBytes))[:]
var dirName string
authorityKeyIdentifierValue := []byte{0x30, len(publicKeyHash)}
authorityKeyIdentifierValue = append(authorityKeyIdentifierValue, publicKeyHash...)
authorityKeyIdentifierValue = append(authorityKeyIdentifierValue, 0x80, len(dirName))
authorityKeyIdentifierValue = append(authorityKeyIdentifierValue, []byte(dirName)...)
...
结果是:
X509v3 Authority Key Identifier:
0....0...<....).!.r[..F.....".hCN=xxx.....$...D
authorityKeyIdentifierValue
可以用asn1.Marshal生成。下面的演示根据 RFC 5280定义结构
authKeyId
并使用此结构生成值:
package main
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"fmt"
"math/big"
)
// RFC 5280, A.2. Implicitly Tagged Module, 1988 Syntax
//
// AuthorityKeyIdentifier ::= SEQUENCE {
// keyIdentifier [0] KeyIdentifier OPTIONAL,
// authorityCertIssuer [1] GeneralNames OPTIONAL,
// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
// -- authorityCertIssuer and authorityCertSerialNumber MUST both
// -- be present or both be absent
type authKeyId struct {
KeyIdentifier []byte `asn1:"optional,tag:0"`
AuthorityCertIssuer generalNames `asn1:"optional,tag:1"`
AuthorityCertSerialNumber *big.Int `asn1:"optional,tag:2"`
}
// RFC 5280, A.2. Implicitly Tagged Module, 1988 Syntax
//
// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
//
// GeneralName ::= CHOICE {
// otherName [0] AnotherName,
// rfc822Name [1] IA5String,
// dNSName [2] IA5String,
// x400Address [3] ORAddress,
// directoryName [4] Name,
// ediPartyName [5] EDIPartyName,
// uniformResourceIdentifier [6] IA5String,
// iPAddress [7] OCTET STRING,
// registeredID [8] OBJECT IDENTIFIER }
type generalNames struct {
Name []pkix.RDNSequence `asn1:"tag:4"`
}
func gen(issuer *x509.Certificate) ([]byte, error) {
return asn1.Marshal(authKeyId{
KeyIdentifier: issuer.SubjectKeyId,
AuthorityCertIssuer: generalNames{Name: []pkix.RDNSequence{issuer.Issuer.ToRDNSequence()}},
AuthorityCertSerialNumber: issuer.SerialNumber,
})
}
func main() {
caCert := `-----BEGIN CERTIFICATE-----
MIIBoTCCAUegAwIBAgIQGoCjDJN1Y6rGWEbXW8V8MDAKBggqhkjOPQQDAjAmMQ8w
DQYDVQQKEwZNeSBPcmcxEzARBgNVBAMTCk15IFJvb3QgQ0EwHhcNMjMwNTE2MTQy
NTUwWhcNMjMwNTE3MTUyNTUwWjAmMQ8wDQYDVQQKEwZNeSBPcmcxEzARBgNVBAMT
Ck15IFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARZQz2Ka7Fi6w9/
32SJHTAjrkE+VqYx7hFNmtX1INPBAJNfvONF2SIlh5nQmS50JpNVGIvEhTbFL0A0
dcuruFHno1cwVTAOBgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5Y48DJ96LQWVh3S/aNJ/6SGy/j4w
CgYIKoZIzj0EAwIDSAAwRQIhANQDh6SGZ014wVFdH0ZHbEGhdb2TqXZUJxA7YMo3
80UnAiApZp4wlzqlB+J4fIPnep+Txru01JgFaKsml2yHv3mEWg==
-----END CERTIFICATE-----`
b, _ := pem.Decode([]byte(caCert))
if b == nil {
panic("couldn't decode test certificate")
}
issuer, err := x509.ParseCertificate(b.Bytes)
if err != nil {
panic(err)
}
authorityKeyIdentifierValue, err := gen(issuer)
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(authorityKeyIdentifierValue))
}
十六进制编码的值为:
30548014e58e3c0c9f7a2d05958774bf68d27fe921b2fe3ea12aa4283026310f300d060355040a13064d79204f7267311330110603550403130a4d7920526f6f7420434182101a80a30c937563aac65846d75bc57c30
十六进制字符串可以用ASN.1 JavaScript解码器等工具解码: