Как добавить имя каталога и серийный номер к идентификатору авторизационного ключа 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?
Связанный
- http://oid-info.com/get/2.5.29.35
- https://github.com/golang/go/issues/47096 (исходный вопрос)
Когда я попробовал это:
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 тоже не включает его.