Как добавить имя каталога и серийный номер к идентификатору авторизационного ключа X509v3

Я пытаюсь создать сертификат клиента, используя код OpenSSL и Go. У меня есть сценарий OpenSSL, который генерирует сертификат с необходимыми расширениями, и я хочу добиться того же результата, используя код Go.

С OpenSSL

options.ext

Файл options.ext, используемый OpenSSL, содержит следующие расширения:

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

генерировать-клиент-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

Полученный сертификат имеет следующую структуру:

      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. Вот соответствующая часть моего кода 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 и серийный номер к идентификатору авторизационного ключа 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

1 ответ

The authorityKeyIdentifierValueможет быть сгенерирован с помощью asn1.Marshal . Демо ниже определяет структуруauthKeyIdсогласно RFC 5280 и генерирует значение, используя эту структуру:

      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

Шестнадцатеричную строку можно декодировать с помощью таких инструментов, как декодер JavaScript ASN.1 :


Следующая демонстрация 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();
    }
}

Обновлять :

Вот обновленная версия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 тоже не включает его.

Другие вопросы по тегам