我正在try 使用OpenSSL和GO代码生成客户端证书.我有一个生成带有所需扩展的证书的OpenSSL脚本,我希望使用GO代码实现相同的结果.

使用OpenSSL

Options.ext

OpenSSL使用的Options.ext文件包含以下扩展名:

basicConstraints=CA:FALSE
authorityKeyIdentifier=keyid,issuer
subjectKeyIdentifier=hash
keyUsage=digitalSignature
extendedKeyUsage=clientAuth

Generate-client-cert.sh

我目前拥有的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

生成的证书具有以下 struct :

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            xx:xx:xx:xx:xx:xx:xx:xx
    Signature 算法rithm: 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 算法rithm: 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 算法rithm: sha256WithRSAEncryption

它应该如下所示:

X509v3 Authority Key Identifier: 
    DirName:CN=xxx
    serial:xx:xx:xx:xx:xx:xx:xx:xx

GO代码

在我的GO代码中,我使用x509包来生成证书.然而,我不确定如何设置x509v3授权密钥标识符扩展.以下是我的围棋代码的相关部分:

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授权密钥标识符中?

相关

当我试着这样做时:

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定义了 struct authKeyId,并使用该 struct 生成值:

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 decoder:

enter image description here


下面的C#演示给出了相同的结果:

using System.Security.Cryptography.X509Certificates;
using System.Text;

internal class Program
{
    private static void Main(string[] args)
    {
        var certBytes = Encoding.ASCII.GetBytes(@"-----BEGIN CERTIFICATE-----
MIIBoTCCAUegAwIBAgIQGoCjDJN1Y6rGWEbXW8V8MDAKBggqhkjOPQQDAjAmMQ8w
DQYDVQQKEwZNeSBPcmcxEzARBgNVBAMTCk15IFJvb3QgQ0EwHhcNMjMwNTE2MTQy
NTUwWhcNMjMwNTE3MTUyNTUwWjAmMQ8wDQYDVQQKEwZNeSBPcmcxEzARBgNVBAMT
Ck15IFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARZQz2Ka7Fi6w9/
32SJHTAjrkE+VqYx7hFNmtX1INPBAJNfvONF2SIlh5nQmS50JpNVGIvEhTbFL0A0
dcuruFHno1cwVTAOBgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5Y48DJ96LQWVh3S/aNJ/6SGy/j4w
CgYIKoZIzj0EAwIDSAAwRQIhANQDh6SGZ014wVFdH0ZHbEGhdb2TqXZUJxA7YMo3
80UnAiApZp4wlzqlB+J4fIPnep+Txru01JgFaKsml2yHv3mEWg==
-----END CERTIFICATE-----");

        using var issuer = new X509Certificate2(certBytes);
        var e = X509AuthorityKeyIdentifierExtension.CreateFromCertificate(issuer, true, true);
        Console.WriteLine(ByteArrayToHex(e.RawData));
    }

    private static string ByteArrayToHex(byte[] bytes)
    {
        var builder = new StringBuilder(bytes.Length * 2);

        for (int i = 0; i < bytes.Length; i++)
        {
            builder.Append($"{bytes[i]:x2}");
        }

        return builder.ToString();
    }
}

Update:

以下是gen的更新版本,其中包括邮箱地址:

func gen(issuer *x509.Certificate) ([]byte, error) {
    rdnSequence := issuer.Issuer.ToRDNSequence()
    if len(issuer.EmailAddresses) > 0 {
        oidEmail := asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}
        emails := make([]pkix.AttributeTypeAndValue, len(issuer.EmailAddresses))
        for i, value := range issuer.EmailAddresses {
            emails[i].Type = oidEmail
            emails[i].Value = value
        }
        rdnSequence = append(rdnSequence, emails)
    }

    return asn1.Marshal(authKeyId{
        KeyIdentifier:             issuer.SubjectKeyId,
        AuthorityCertIssuer:       generalNames{Name: []pkix.RDNSequence{rdnSequence}},
        AuthorityCertSerialNumber: issuer.SerialNumber,
    })
}

请注意,OID已弃用(请参见http://oid-info.com/get/1.2.840.113549.1.9.1).而.NET也不包括它.

Go相关问答推荐

"k8s.io/apimachinery/pkg/runtime.对象(缺少方法DeepCopyBody)

GitHub发布Golang子模块

验证访问令牌(密钥罩)

未对GoFr中的所有请求进行跟踪

如何在Golang中获取mp3文件的持续时间?

Go:为什么我不能比较 net.Addr

Go Template if 条件

如何在 `hashicorp / terraform-exec` 中将 `ApplyConfig` 传递给 `tf.Apply()`?

当填充通道的函数调用未嵌入 goroutine 时,为什么我会遇到死锁?

如何从 Asterisk Manager Interface Event 获取活动呼叫数

Golang crypto/rand 线程安全吗?

无法将 graphql-ws 连接到 gqlgen

如何确定作为函数参数传递的指针是否正在被修改或副本是否正在被修改?

Go Colly 如何找到请求的元素?

CBC Decrypter 解密加密文本,但部分文本被随机字符替换

如何模仿联合类型

如何在自定义验证函数中获取 struct 名称

Go AST:获取所有 struct

是否可以使用按位运算在随机 unicode 字符串中找到重复字符?

什么是无效字符实体 &ccb