Skip to content

Commit 5f541c3

Browse files
Support parsing of x509 certificates that have large policy-identifier sub-oid values
1 parent 2663d81 commit 5f541c3

File tree

2 files changed

+190
-4
lines changed

2 files changed

+190
-4
lines changed

src/crypto/x509/x509.go

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,16 @@ type Certificate struct {
760760
// CRL Distribution Points
761761
CRLDistributionPoints []string
762762

763+
// Policy identifiers with sub-oid values less than or equal to math.MaxInt32.
764+
// When parsing a certificate, the certificate policy identifiers are unmarshaled into
765+
// PolicyIdentifiers if all sub-oids are less than or equal to math.MaxInt32.
766+
// If at least one sub-oid is greater than math.MaxInt32, policy identifiers are
767+
// unmarshaled into PolicyIdentifiersExt.
768+
// When generating a certificate, set either PolicyIdentifiers or PolicyIdentifiersExt
769+
// but not both.
763770
PolicyIdentifiers []asn1.ObjectIdentifier
771+
// Policy identifiers with sub-oid values greater than math.MaxInt32.
772+
PolicyIdentifiersExt []asn1.ObjectIdentifierExt
764773
}
765774

766775
// ErrUnsupportedAlgorithm results from attempting to perform an operation that
@@ -943,7 +952,7 @@ type basicConstraints struct {
943952

944953
// RFC 5280 4.2.1.4
945954
type policyInformation struct {
946-
Policy asn1.ObjectIdentifier
955+
Policy asn1.ObjectIdentifierExt
947956
// policyQualifiers omitted
948957
}
949958

@@ -1517,9 +1526,25 @@ func parseCertificate(in *certificate) (*Certificate, error) {
15171526
} else if len(rest) != 0 {
15181527
return nil, errors.New("x509: trailing data after X.509 certificate policies")
15191528
}
1520-
out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies))
1521-
for i, policy := range policies {
1522-
out.PolicyIdentifiers[i] = policy.Policy
1529+
s := make([]asn1.ObjectIdentifier, 0, len(policies))
1530+
largeoids := false
1531+
for _, policy := range policies {
1532+
if oid, err1 := policy.Policy.GetObjectIdentifier(); err1 == nil {
1533+
// Add to PolicyIdentifiers if sub-oids are less than or equal to math.MaxInt32.
1534+
s = append(s, oid)
1535+
} else {
1536+
largeoids = true
1537+
}
1538+
}
1539+
if largeoids {
1540+
// The certificate contains at least one policy identifier that has a sub-oid
1541+
// value greater than math.MaxInt32.
1542+
out.PolicyIdentifiersExt = make([]asn1.ObjectIdentifierExt, len(policies))
1543+
for i, policy := range policies {
1544+
out.PolicyIdentifiersExt[i] = policy.Policy
1545+
}
1546+
} else {
1547+
out.PolicyIdentifiers = s
15231548
}
15241549

15251550
default:
@@ -1811,11 +1836,30 @@ func buildExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId
18111836
n++
18121837
}
18131838

1839+
if len(template.PolicyIdentifiers) > 0 && len(template.PolicyIdentifiersExt) > 0 {
1840+
err = errors.New("x509: invalid template, cannot specify both PolicyIdentifiers and PolicyIdentifiersExt")
1841+
return
1842+
}
1843+
18141844
if len(template.PolicyIdentifiers) > 0 &&
18151845
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
18161846
ret[n].Id = oidExtensionCertificatePolicies
18171847
policies := make([]policyInformation, len(template.PolicyIdentifiers))
18181848
for i, policy := range template.PolicyIdentifiers {
1849+
policies[i].Policy = asn1.NewObjectIdentifierExt(policy)
1850+
}
1851+
ret[n].Value, err = asn1.Marshal(policies)
1852+
if err != nil {
1853+
return
1854+
}
1855+
n++
1856+
}
1857+
1858+
if len(template.PolicyIdentifiersExt) > 0 &&
1859+
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
1860+
ret[n].Id = oidExtensionCertificatePolicies
1861+
policies := make([]policyInformation, len(template.PolicyIdentifiersExt))
1862+
for i, policy := range template.PolicyIdentifiersExt {
18191863
policies[i].Policy = policy
18201864
}
18211865
ret[n].Value, err = asn1.Marshal(policies)
@@ -2067,6 +2111,7 @@ var emptyASN1Subject = []byte{0x30, 0}
20672111
// - PermittedIPRanges
20682112
// - PermittedURIDomains
20692113
// - PolicyIdentifiers
2114+
// - PolicyIdentifiersExt
20702115
// - SerialNumber
20712116
// - SignatureAlgorithm
20722117
// - Subject

src/crypto/x509/x509_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"encoding/pem"
2323
"fmt"
2424
"internal/testenv"
25+
"math"
2526
"math/big"
2627
"net"
2728
"net/url"
@@ -686,6 +687,10 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
686687
t.Errorf("%s: failed to parse policy identifiers: got:%#v want:%#v", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers)
687688
}
688689

690+
if len(cert.PolicyIdentifiersExt) > 0 {
691+
t.Errorf("%s: unexpected PolicyIdentifiersExt value:%#v", test.name, template.PolicyIdentifiersExt)
692+
}
693+
689694
if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
690695
t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
691696
}
@@ -806,6 +811,82 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
806811
}
807812
}
808813

814+
func TestCreateSelfSignedCertificatePolicyIdentifier(t *testing.T) {
815+
random := rand.Reader
816+
817+
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
818+
if err != nil {
819+
t.Fatalf("Failed to generate ECDSA key: %s", err)
820+
}
821+
{
822+
// Cannot have a certificate template with both PolicyIdentifiers and PolicyIdentifiersExt.
823+
template := Certificate{
824+
SerialNumber: big.NewInt(-1),
825+
SignatureAlgorithm: ECDSAWithSHA256,
826+
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
827+
PolicyIdentifiersExt: []asn1.ObjectIdentifierExt{asn1.ObjectIdentifierExt{1, 2, 3, 4}},
828+
}
829+
_, err := CreateCertificate(random, &template, &template, &ecdsaPriv.PublicKey, ecdsaPriv)
830+
if err == nil {
831+
t.Error("specifying PolicyIdentifiers and PolicyIdentifiersExt should fail")
832+
}
833+
}
834+
{
835+
// Certificate template with policy identifier that has sub-oids less than math.MaxInt32.
836+
template := Certificate{
837+
SerialNumber: big.NewInt(-1),
838+
SignatureAlgorithm: ECDSAWithSHA256,
839+
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
840+
}
841+
derBytes, err := CreateCertificate(random, &template, &template, &ecdsaPriv.PublicKey, ecdsaPriv)
842+
if err != nil {
843+
t.Errorf("failed to create certificate: %s", err)
844+
}
845+
cert, err := ParseCertificate(derBytes)
846+
if err != nil {
847+
t.Errorf("failed to parse certificate: %s", err)
848+
}
849+
if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
850+
t.Errorf("failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers)
851+
}
852+
if len(cert.PolicyIdentifiersExt) > 0 {
853+
t.Errorf("unexpected PolicyIdentifiersExt value:%#v", template.PolicyIdentifiersExt)
854+
}
855+
}
856+
{
857+
// Certificate template with policy identifier that has sub-oids greater than math.MaxInt32.
858+
template := Certificate{
859+
SerialNumber: big.NewInt(-1),
860+
SignatureAlgorithm: ECDSAWithSHA256,
861+
PolicyIdentifiersExt: []asn1.ObjectIdentifierExt{
862+
asn1.ObjectIdentifierExt{1, 2, math.MaxInt32},
863+
asn1.ObjectIdentifierExt{1, 2, 1 << 60},
864+
asn1.ObjectIdentifierExt{1, 2, new(big.Int).Lsh(big.NewInt(1), 80)},
865+
asn1.ObjectIdentifierExt{1, 2, new(big.Int).Lsh(big.NewInt(1), 80), 1 << 60},
866+
},
867+
}
868+
derBytes, err := CreateCertificate(random, &template, &template, &ecdsaPriv.PublicKey, ecdsaPriv)
869+
if err != nil {
870+
t.Errorf("failed to create certificate: %s", err)
871+
}
872+
cert, err := ParseCertificate(derBytes)
873+
if err != nil {
874+
t.Errorf("failed to parse certificate: %s", err)
875+
}
876+
if len(cert.PolicyIdentifiers) > 0 {
877+
t.Errorf("unexpected PolicyIdentifiers value:%#v", template.PolicyIdentifiers)
878+
}
879+
if len(cert.PolicyIdentifiersExt) == 0 || len(cert.PolicyIdentifiersExt) != len(template.PolicyIdentifiersExt) {
880+
t.Errorf("failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiersExt, template.PolicyIdentifiersExt)
881+
}
882+
for i, pi := range cert.PolicyIdentifiersExt {
883+
if !pi.Equal(template.PolicyIdentifiersExt[i]) {
884+
t.Errorf("failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiersExt, template.PolicyIdentifiersExt)
885+
}
886+
}
887+
}
888+
}
889+
809890
// Self-signed certificate using ECDSA with SHA1 & secp256r1
810891
var ecdsaSHA1CertPem = `
811892
-----BEGIN CERTIFICATE-----
@@ -2189,6 +2270,66 @@ func TestCriticalNameConstraintWithUnknownType(t *testing.T) {
21892270
}
21902271
}
21912272

2273+
const certWithLargeSubOidPEM = `
2274+
-----BEGIN CERTIFICATE-----
2275+
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
2304+
-----END CERTIFICATE-----`
2305+
2306+
// X.509 certificates may contain policy identifiers that have
2307+
// sub-oid values greater than math.MaxInt32. ParseCertificate
2308+
// must not return an error when parsing such certificates.
2309+
func TestCertificateWithLargeSubOidInPolicyIdentifiers(t *testing.T) {
2310+
block, _ := pem.Decode([]byte(certWithLargeSubOidPEM))
2311+
c, err := ParseCertificate(block.Bytes)
2312+
if err != nil {
2313+
t.Errorf("Failed to parse certificate: %v", err.Error())
2314+
}
2315+
if len(c.PolicyIdentifiersExt) != 1 {
2316+
// The Certificate policy identifier is 1.2.36.67006527840.666.66.1.1
2317+
// Because it does not fit in the PolicyIdentifiers field, it is
2318+
// unmarshaled in the PolicyIdentifiersExt field.
2319+
t.Errorf("PolicyIdentifiersExt expected 1 but got %d", len(c.PolicyIdentifiersExt))
2320+
}
2321+
expected := "1.2.36.67006527840.666.66.1.1"
2322+
if c.PolicyIdentifiersExt[0].String() != expected {
2323+
t.Errorf("PolicyIdentifiersExt expected %s but got %s", expected, c.PolicyIdentifiersExt[0].String())
2324+
}
2325+
if len(c.PolicyIdentifiers) > 0 {
2326+
// The Certificate policy identifier for this particular x509 certificate
2327+
// does not fit in the PolicyIdentifiers field, because suboid 67006527840
2328+
// is more than 2^31
2329+
t.Errorf("PolicyIdentifiers expected zero length but got %d", len(c.PolicyIdentifiers))
2330+
}
2331+
}
2332+
21922333
const badIPMaskPEM = `
21932334
-----BEGIN CERTIFICATE-----
21942335
MIICzzCCAbegAwIBAgICEjQwDQYJKoZIhvcNAQELBQAwHTEbMBkGA1UEAxMSQmFk

0 commit comments

Comments
 (0)