crypto/x509: add RawSignatureAlgorithm

Fixes #76133

Change-Id: I7889aeff2ed197ee15f2ce7689b99b936a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/778660
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
This commit is contained in:
Filippo Valsorda 2026-05-15 15:23:59 -04:00 committed by Gopher Robot
parent e62d3e6e89
commit 6997bcd820
5 changed files with 121 additions and 11 deletions

3
api/next/76133.txt Normal file
View file

@ -0,0 +1,3 @@
pkg crypto/x509, type Certificate struct, RawSignatureAlgorithm []uint8 #76133
pkg crypto/x509, type CertificateRequest struct, RawSignatureAlgorithm []uint8 #76133
pkg crypto/x509, type RevocationList struct, RawSignatureAlgorithm []uint8 #76133

View file

@ -0,0 +1,4 @@
The new [Certificate.RawSignatureAlgorithm], [CertificateRequest.RawSignatureAlgorithm],
and [RevocationList.RawSignatureAlgorithm] fields expose the DER-encoded
AlgorithmIdentifier of the signature algorithm, including when the
SignatureAlgorithm field is [UnknownSignatureAlgorithm].

View file

@ -936,7 +936,11 @@ func parseCertificate(der []byte) (*Certificate, error) {
cert.SerialNumber = serial
var sigAISeq cryptobyte.String
if !tbs.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
if !tbs.ReadASN1Element(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("x509: malformed signature algorithm identifier")
}
cert.RawSignatureAlgorithm = sigAISeq
if !sigAISeq.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("x509: malformed signature algorithm identifier")
}
// Before parsing the inner algorithm identifier, extract
@ -1143,7 +1147,11 @@ func ParseRevocationList(der []byte) (*RevocationList, error) {
}
var sigAISeq cryptobyte.String
if !tbs.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
if !tbs.ReadASN1Element(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("x509: malformed signature algorithm identifier")
}
rl.RawSignatureAlgorithm = sigAISeq
if !sigAISeq.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("x509: malformed signature algorithm identifier")
}
// Before parsing the inner algorithm identifier, extract

View file

@ -707,6 +707,7 @@ type Certificate struct {
RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo.
RawSubject []byte // DER encoded Subject
RawIssuer []byte // DER encoded Issuer
RawSignatureAlgorithm []byte // DER encoded AlgorithmIdentifier
Signature []byte
SignatureAlgorithm SignatureAlgorithm
@ -1914,6 +1915,7 @@ type CertificateRequest struct {
RawTBSCertificateRequest []byte // Certificate request info part of raw ASN.1 DER content.
RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo.
RawSubject []byte // DER encoded Subject.
RawSignatureAlgorithm []byte // DER encoded AlgorithmIdentifier.
Version int
Signature []byte
@ -1966,8 +1968,12 @@ type tbsCertificateRequest struct {
type certificateRequest struct {
Raw asn1.RawContent
TBSCSR tbsCertificateRequest
SignatureAlgorithm pkix.AlgorithmIdentifier
SignatureValue asn1.BitString
SignatureAlgorithm struct {
Raw asn1.RawContent
Algorithm asn1.ObjectIdentifier
Parameters asn1.RawValue `asn1:"optional"`
}
SignatureValue asn1.BitString
}
// oidExtensionRequest is a PKCS #9 OBJECT IDENTIFIER that indicates requested
@ -2202,11 +2208,12 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv
return nil, err
}
return asn1.Marshal(certificateRequest{
TBSCSR: tbsCSR,
SignatureAlgorithm: algorithmIdentifier,
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
})
cr := certificateRequest{}
cr.TBSCSR = tbsCSR
cr.SignatureAlgorithm.Algorithm = algorithmIdentifier.Algorithm
cr.SignatureAlgorithm.Parameters = algorithmIdentifier.Parameters
cr.SignatureValue = asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}
return asn1.Marshal(cr)
}
// ParseCertificateRequest parses a single certificate request from the
@ -2230,9 +2237,13 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
RawTBSCertificateRequest: in.TBSCSR.Raw,
RawSubjectPublicKeyInfo: in.TBSCSR.PublicKey.Raw,
RawSubject: in.TBSCSR.Subject.FullBytes,
RawSignatureAlgorithm: in.SignatureAlgorithm.Raw,
Signature: in.SignatureValue.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromAI(in.SignatureAlgorithm),
Signature: in.SignatureValue.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromAI(pkix.AlgorithmIdentifier{
Algorithm: in.SignatureAlgorithm.Algorithm,
Parameters: in.SignatureAlgorithm.Parameters,
}),
PublicKeyAlgorithm: getPublicKeyAlgorithmFromOID(in.TBSCSR.PublicKey.Algorithm.Algorithm),
@ -2327,6 +2338,9 @@ type RevocationList struct {
RawTBSRevocationList []byte
// RawIssuer contains the DER encoded Issuer.
RawIssuer []byte
// RawSignatureAlgorithm contains the DER encoded signature algorithm as a
// PKIX AlgorithmIdentifier.
RawSignatureAlgorithm []byte
// Issuer contains the DN of the issuing certificate.
Issuer pkix.Name

View file

@ -3506,6 +3506,87 @@ Qc4=
}
}
func TestRawSignatureAlgorithm(t *testing.T) {
// checkAI verifies that raw is a DER-encoded AlgorithmIdentifier with the
// expected OID.
checkAI := func(t *testing.T, raw []byte, wantOID asn1.ObjectIdentifier) {
t.Helper()
if len(raw) == 0 {
t.Fatal("RawSignatureAlgorithm is empty")
}
var ai pkix.AlgorithmIdentifier
rest, err := asn1.Unmarshal(raw, &ai)
if err != nil {
t.Fatalf("failed to unmarshal RawSignatureAlgorithm: %s", err)
}
if len(rest) != 0 {
t.Fatalf("trailing data after RawSignatureAlgorithm: %x", rest)
}
if !ai.Algorithm.Equal(wantOID) {
t.Fatalf("unexpected OID: got %v, want %v", ai.Algorithm, wantOID)
}
}
t.Run("Certificate", func(t *testing.T) {
p, _ := pem.Decode([]byte(pemCertificate))
cert, err := ParseCertificate(p.Bytes)
if err != nil {
t.Fatalf("failed to parse certificate: %s", err)
}
checkAI(t, cert.RawSignatureAlgorithm, oidSignatureSHA256WithRSA)
})
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("failed to generate key: %s", err)
}
t.Run("CertificateRequest", func(t *testing.T) {
csrDER, err := CreateCertificateRequest(rand.Reader, &CertificateRequest{}, priv)
if err != nil {
t.Fatalf("failed to create CSR: %s", err)
}
csr, err := ParseCertificateRequest(csrDER)
if err != nil {
t.Fatalf("failed to parse CSR: %s", err)
}
checkAI(t, csr.RawSignatureAlgorithm, oidSignatureECDSAWithSHA256)
})
t.Run("RevocationList", func(t *testing.T) {
issuerTmpl := &Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "issuer"},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour),
KeyUsage: KeyUsageCRLSign,
IsCA: true,
BasicConstraintsValid: true,
}
issuerDER, err := CreateCertificate(rand.Reader, issuerTmpl, issuerTmpl, priv.Public(), priv)
if err != nil {
t.Fatalf("failed to create issuer: %s", err)
}
issuer, err := ParseCertificate(issuerDER)
if err != nil {
t.Fatalf("failed to parse issuer: %s", err)
}
crlDER, err := CreateRevocationList(rand.Reader, &RevocationList{
Number: big.NewInt(1),
ThisUpdate: time.Now(),
NextUpdate: time.Now().Add(time.Hour),
}, issuer, priv)
if err != nil {
t.Fatalf("failed to create CRL: %s", err)
}
crl, err := ParseRevocationList(crlDER)
if err != nil {
t.Fatalf("failed to parse CRL: %s", err)
}
checkAI(t, crl.RawSignatureAlgorithm, oidSignatureECDSAWithSHA256)
})
}
func TestParseCertificateRawEquals(t *testing.T) {
p, _ := pem.Decode([]byte(pemCertificate))
cert, err := ParseCertificate(p.Bytes)