Source file src/crypto/x509/parser_test.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package x509
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/ecdsa"
    10  	"crypto/elliptic"
    11  	"crypto/rand"
    12  	"crypto/x509/pkix"
    13  	"encoding/asn1"
    14  	"encoding/pem"
    15  	"os"
    16  	"reflect"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  
    21  	cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
    22  )
    23  
    24  func TestParseASN1String(t *testing.T) {
    25  	tests := []struct {
    26  		name        string
    27  		tag         cryptobyte_asn1.Tag
    28  		value       []byte
    29  		expected    string
    30  		expectedErr string
    31  	}{
    32  		{
    33  			name:     "T61String",
    34  			tag:      cryptobyte_asn1.T61String,
    35  			value:    []byte{0xbf, 0x61, 0x3f},
    36  			expected: string("¿a?"),
    37  		},
    38  		{
    39  			name:     "PrintableString",
    40  			tag:      cryptobyte_asn1.PrintableString,
    41  			value:    []byte{80, 81, 82},
    42  			expected: string("PQR"),
    43  		},
    44  		{
    45  			name:        "PrintableString (invalid)",
    46  			tag:         cryptobyte_asn1.PrintableString,
    47  			value:       []byte{1, 2, 3},
    48  			expectedErr: "invalid PrintableString",
    49  		},
    50  		{
    51  			name:     "UTF8String",
    52  			tag:      cryptobyte_asn1.UTF8String,
    53  			value:    []byte{80, 81, 82},
    54  			expected: string("PQR"),
    55  		},
    56  		{
    57  			name:        "UTF8String (invalid)",
    58  			tag:         cryptobyte_asn1.UTF8String,
    59  			value:       []byte{255},
    60  			expectedErr: "invalid UTF-8 string",
    61  		},
    62  		{
    63  			name:     "BMPString",
    64  			tag:      cryptobyte_asn1.Tag(asn1.TagBMPString),
    65  			value:    []byte{80, 81},
    66  			expected: string("偑"),
    67  		},
    68  		{
    69  			name:        "BMPString (invalid length)",
    70  			tag:         cryptobyte_asn1.Tag(asn1.TagBMPString),
    71  			value:       []byte{255},
    72  			expectedErr: "invalid BMPString",
    73  		},
    74  		{
    75  			name:        "BMPString (invalid surrogate)",
    76  			tag:         cryptobyte_asn1.Tag(asn1.TagBMPString),
    77  			value:       []byte{80, 81, 216, 1},
    78  			expectedErr: "invalid BMPString",
    79  		},
    80  		{
    81  			name:        "BMPString (invalid noncharacter 0xfdd1)",
    82  			tag:         cryptobyte_asn1.Tag(asn1.TagBMPString),
    83  			value:       []byte{80, 81, 253, 209},
    84  			expectedErr: "invalid BMPString",
    85  		},
    86  		{
    87  			name:        "BMPString (invalid noncharacter 0xffff)",
    88  			tag:         cryptobyte_asn1.Tag(asn1.TagBMPString),
    89  			value:       []byte{80, 81, 255, 255},
    90  			expectedErr: "invalid BMPString",
    91  		},
    92  		{
    93  			name:        "BMPString (invalid noncharacter 0xfffe)",
    94  			tag:         cryptobyte_asn1.Tag(asn1.TagBMPString),
    95  			value:       []byte{80, 81, 255, 254},
    96  			expectedErr: "invalid BMPString",
    97  		},
    98  		{
    99  			name:     "IA5String",
   100  			tag:      cryptobyte_asn1.IA5String,
   101  			value:    []byte{80, 81},
   102  			expected: string("PQ"),
   103  		},
   104  		{
   105  			name:        "IA5String (invalid)",
   106  			tag:         cryptobyte_asn1.IA5String,
   107  			value:       []byte{255},
   108  			expectedErr: "invalid IA5String",
   109  		},
   110  		{
   111  			name:     "NumericString",
   112  			tag:      cryptobyte_asn1.Tag(asn1.TagNumericString),
   113  			value:    []byte{49, 50},
   114  			expected: string("12"),
   115  		},
   116  		{
   117  			name:        "NumericString (invalid)",
   118  			tag:         cryptobyte_asn1.Tag(asn1.TagNumericString),
   119  			value:       []byte{80},
   120  			expectedErr: "invalid NumericString",
   121  		},
   122  	}
   123  
   124  	for _, tc := range tests {
   125  		t.Run(tc.name, func(t *testing.T) {
   126  			out, err := parseASN1String(tc.tag, tc.value)
   127  			if err != nil && err.Error() != tc.expectedErr {
   128  				t.Fatalf("parseASN1String returned unexpected error: got %q, want %q", err, tc.expectedErr)
   129  			} else if err == nil && tc.expectedErr != "" {
   130  				t.Fatalf("parseASN1String didn't fail, expected: %s", tc.expectedErr)
   131  			}
   132  			if out != tc.expected {
   133  				t.Fatalf("parseASN1String returned unexpected value: got %q, want %q", out, tc.expected)
   134  			}
   135  		})
   136  	}
   137  }
   138  
   139  const policyPEM = `-----BEGIN CERTIFICATE-----
   140  MIIGeDCCBWCgAwIBAgIUED9KQBi0ScBDoufB2mgAJ63G5uIwDQYJKoZIhvcNAQEL
   141  BQAwVTELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDENMAsG
   142  A1UECxMERlBLSTEdMBsGA1UEAxMURmVkZXJhbCBCcmlkZ2UgQ0EgRzQwHhcNMjAx
   143  MDIyMTcwNDE5WhcNMjMxMDIyMTcwNDE5WjCBgTELMAkGA1UEBhMCVVMxHTAbBgNV
   144  BAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVz
   145  dCBOZXR3b3JrMTIwMAYDVQQDEylTeW1hbnRlYyBDbGFzcyAzIFNTUCBJbnRlcm1l
   146  ZGlhdGUgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2p
   147  75cMpx86sS2aH4r+0o8r+m/KTrPrknWP0RA9Kp6sewAzkNa7BVwg0jOhyamiv1iP
   148  Cns10usoH93nxYbXLWF54vOLRdYU/53KEPNmgkj2ipMaTLuaReBghNibikWSnAmy
   149  S8RItaDMs8tdF2goKPI4xWiamNwqe92VC+pic2tq0Nva3Y4kvMDJjtyje3uduTtL
   150  oyoaaHkrX7i7gE67psnMKj1THUtre1JV1ohl9+oOuyot4p3eSxVlrMWiiwb11bnk
   151  CakecOz/mP2DHMGg6pZ/BeJ+ThaLUylAXECARIqHc9UwRPKC9BfLaCX4edIoeYiB
   152  loRs4KdqLdg/I9eTwKkCAwEAAaOCAxEwggMNMB0GA1UdDgQWBBQ1Jn1QleGhwb0F
   153  1cOdd0LHDBOWjDAfBgNVHSMEGDAWgBR58ABJ6393wl1BAmU0ipAjmx4HbzAOBgNV
   154  HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zCBiAYDVR0gBIGAMH4wDAYKYIZI
   155  AWUDAgEDAzAMBgpghkgBZQMCAQMMMAwGCmCGSAFlAwIBAw4wDAYKYIZIAWUDAgED
   156  DzAMBgpghkgBZQMCAQMSMAwGCmCGSAFlAwIBAxMwDAYKYIZIAWUDAgEDFDAMBgpg
   157  hkgBZQMCAQMlMAwGCmCGSAFlAwIBAyYwggESBgNVHSEEggEJMIIBBTAbBgpghkgB
   158  ZQMCAQMDBg1ghkgBhvhFAQcXAwEGMBsGCmCGSAFlAwIBAwwGDWCGSAGG+EUBBxcD
   159  AQcwGwYKYIZIAWUDAgEDDgYNYIZIAYb4RQEHFwMBDjAbBgpghkgBZQMCAQMPBg1g
   160  hkgBhvhFAQcXAwEPMBsGCmCGSAFlAwIBAxIGDWCGSAGG+EUBBxcDARIwGwYKYIZI
   161  AWUDAgEDEwYNYIZIAYb4RQEHFwMBETAbBgpghkgBZQMCAQMUBg1ghkgBhvhFAQcX
   162  AwEUMBsGCmCGSAFlAwIBAyUGDWCGSAGG+EUBBxcDAQgwGwYKYIZIAWUDAgEDJgYN
   163  YIZIAYb4RQEHFwMBJDBgBggrBgEFBQcBCwRUMFIwUAYIKwYBBQUHMAWGRGh0dHA6
   164  Ly9zc3Atc2lhLnN5bWF1dGguY29tL1NUTlNTUC9DZXJ0c19Jc3N1ZWRfYnlfQ2xh
   165  c3MzU1NQQ0EtRzMucDdjMA8GA1UdJAQIMAaAAQCBAQAwCgYDVR02BAMCAQAwUQYI
   166  KwYBBQUHAQEERTBDMEEGCCsGAQUFBzAChjVodHRwOi8vcmVwby5mcGtpLmdvdi9i
   167  cmlkZ2UvY2FDZXJ0c0lzc3VlZFRvZmJjYWc0LnA3YzA3BgNVHR8EMDAuMCygKqAo
   168  hiZodHRwOi8vcmVwby5mcGtpLmdvdi9icmlkZ2UvZmJjYWc0LmNybDANBgkqhkiG
   169  9w0BAQsFAAOCAQEAA751TycC1f/WTkHmedF9ZWxP58Jstmwvkyo8bKueJ0eF7LTG
   170  BgQlzE2B9vke4sFhd4V+BdgOPGE1dsGzllYKCWg0BhkCBs5kIJ7F6Ay6G1TBuGU1
   171  Ie8247GL+P9pcC5TVvXHC/62R2w3DuD/vAPLbYEbSQjobXlsqt8Kmtd6yK/jVuDV
   172  BTZMdZmvoNtjemqmgcBXHsf0ctVm0m6tH5uYqyVxu8tfyUis6Cf303PHj+spWP1k
   173  gc5PYnVF0ot7qAmNFENIpbKg3BdusBkF9rGxLaDSUBvSc7+s9iQz9d/iRuAebrYu
   174  +eqUlJ2lsjS1U8qyPmlH+spfPNbAEQEsuP32Aw==
   175  -----END CERTIFICATE-----
   176  `
   177  
   178  func TestPolicyParse(t *testing.T) {
   179  	b, _ := pem.Decode([]byte(policyPEM))
   180  	c, err := ParseCertificate(b.Bytes)
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  	if len(c.Policies) != 9 {
   185  		t.Errorf("unexpected number of policies: got %d, want %d", len(c.Policies), 9)
   186  	}
   187  	if len(c.PolicyMappings) != 9 {
   188  		t.Errorf("unexpected number of policy mappings: got %d, want %d", len(c.PolicyMappings), 9)
   189  	}
   190  	if !c.RequireExplicitPolicyZero {
   191  		t.Error("expected RequireExplicitPolicyZero to be set")
   192  	}
   193  	if !c.InhibitPolicyMappingZero {
   194  		t.Error("expected InhibitPolicyMappingZero to be set")
   195  	}
   196  	if !c.InhibitAnyPolicyZero {
   197  		t.Error("expected InhibitAnyPolicyZero to be set")
   198  	}
   199  }
   200  
   201  func TestParsePolicies(t *testing.T) {
   202  	for _, tc := range []string{
   203  		"testdata/policy_leaf_duplicate.pem",
   204  		"testdata/policy_leaf_invalid.pem",
   205  	} {
   206  		t.Run(tc, func(t *testing.T) {
   207  			b, err := os.ReadFile(tc)
   208  			if err != nil {
   209  				t.Fatal(err)
   210  			}
   211  			p, _ := pem.Decode(b)
   212  			_, err = ParseCertificate(p.Bytes)
   213  			if err == nil {
   214  				t.Error("parsing should've failed")
   215  			}
   216  		})
   217  	}
   218  }
   219  
   220  func TestParseCertificateNegativeMaxPathLength(t *testing.T) {
   221  	certs := []string{
   222  		// Certificate with MaxPathLen set to -1.
   223  		`
   224  -----BEGIN CERTIFICATE-----
   225  MIIByTCCATKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRURVNU
   226  MB4XDTcwMDEwMTAwMTY0MFoXDTcwMDEwMjAzNDY0MFowDzENMAsGA1UEAxMEVEVT
   227  VDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsaHglFuSicTT8TKfipgsSi3N
   228  Wb/TcvuAhanFF1VGB+vS95kO7yFqyfRgX3GgOwT0KlJVsVjPjghEGR9RGTSLqkTD
   229  UFbiBgm8+VEPMOrUtIHIHXhl+ye44AkOEStxfz7gjN/EAS2h8ffPKhvDTHOlShKw
   230  Y3LQlxR0LdeJXq3eSqUCAwEAAaM1MDMwEgYDVR0TAQH/BAgwBgEB/wIB/zAdBgNV
   231  HQ4EFgQUrbrk0tqQAEsce8uYifP0BIVhuFAwDQYJKoZIhvcNAQELBQADgYEAIkhV
   232  ZBj1ThT+eyh50XsoU570NUysTg3Nj/3lbkEolzdcE+wu0CPXvgxLRM6Y62u1ey82
   233  8d5VQHstzF4dXgc3W+O9UySa+CKdcHx/q7o7seOGXdysT0IJtAY3w66mFkuF7PIn
   234  y9b7M5t6pmWjb7N0QqGuWeNqi4ZvS8gLKmVEgGY=
   235  -----END CERTIFICATE-----
   236  `,
   237  		// Certificate with MaxPathLen set to -2.
   238  		`
   239  -----BEGIN CERTIFICATE-----
   240  MIIByTCCATKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRURVNU
   241  MB4XDTcwMDEwMTAwMTY0MFoXDTcwMDEwMjAzNDY0MFowDzENMAsGA1UEAxMEVEVT
   242  VDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsaHglFuSicTT8TKfipgsSi3N
   243  Wb/TcvuAhanFF1VGB+vS95kO7yFqyfRgX3GgOwT0KlJVsVjPjghEGR9RGTSLqkTD
   244  UFbiBgm8+VEPMOrUtIHIHXhl+ye44AkOEStxfz7gjN/EAS2h8ffPKhvDTHOlShKw
   245  Y3LQlxR0LdeJXq3eSqUCAwEAAaM1MDMwEgYDVR0TAQH/BAgwBgEB/wIB/jAdBgNV
   246  HQ4EFgQUrbrk0tqQAEsce8uYifP0BIVhuFAwDQYJKoZIhvcNAQELBQADgYEAGjIr
   247  YGQc7Ods+BuKck7p+vpAMONM8SLEuUtKorCP3ecsO51MoA4/niLbgMHaOGNHwzMp
   248  ajg0zLbY0Dj6Ml0VZ+lS3rjgTEhYXc626eZkoQqgUzL1jhe3S0ZbSxxmHMBKjJFl
   249  d5l1tRhScKu2NBgm74nYmJxJYgvuTA38wGhRrGU=
   250  -----END CERTIFICATE-----
   251  `,
   252  	}
   253  
   254  	for _, cert := range certs {
   255  		b, _ := pem.Decode([]byte(cert))
   256  		_, err := ParseCertificate(b.Bytes)
   257  		if err == nil || err.Error() != "x509: invalid basic constraints" {
   258  			t.Errorf(`ParseCertificate() = %v; want = "x509: invalid basic constraints"`, err)
   259  		}
   260  	}
   261  }
   262  
   263  func TestDomainNameValid(t *testing.T) {
   264  	for _, tc := range []struct {
   265  		name       string
   266  		dnsName    string
   267  		constraint bool
   268  		valid      bool
   269  	}{
   270  		// TODO(#75835): these tests are for stricter name validation, which we
   271  		// had to disable. Once we reenable these strict checks, behind a
   272  		// GODEBUG, we should add them back in.
   273  		// {"empty name, name", "", false, false},
   274  		// {"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, false},
   275  		// {"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, false},
   276  		// {"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, false},
   277  		// {"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, false},
   278  		// {"64 char single label, name", strings.Repeat("a", 64), false, false},
   279  		// {"64 char single label, constraint", strings.Repeat("a", 64), true, false},
   280  		// {"64 char label, name", "a." + strings.Repeat("a", 64), false, false},
   281  		// {"64 char label, constraint", "a." + strings.Repeat("a", 64), true, false},
   282  
   283  		// TODO(#75835): these are the inverse of the tests above, they should be removed
   284  		// once the strict checking is enabled.
   285  		{"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, true},
   286  		{"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, true},
   287  		{"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, true},
   288  		{"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, true},
   289  		{"64 char single label, name", strings.Repeat("a", 64), false, true},
   290  		{"64 char single label, constraint", strings.Repeat("a", 64), true, true},
   291  		{"64 char label, name", "a." + strings.Repeat("a", 64), false, true},
   292  		{"64 char label, constraint", "a." + strings.Repeat("a", 64), true, true},
   293  
   294  		// Check we properly enforce properties of domain names.
   295  		{"empty name, constraint", "", true, true},
   296  		{"empty label, name", "a..a", false, false},
   297  		{"empty label, constraint", "a..a", true, false},
   298  		{"period, name", ".", false, false},
   299  		{"period, constraint", ".", true, false}, // TODO(roland): not entirely clear if this is a valid constraint (require at least one label?)
   300  		{"valid, name", "a.b.c", false, true},
   301  		{"valid, constraint", "a.b.c", true, true},
   302  		{"leading period, name", ".a.b.c", false, false},
   303  		{"leading period, constraint", ".a.b.c", true, true},
   304  		{"trailing period, name", "a.", false, false},
   305  		{"trailing period, constraint", "a.", true, false},
   306  		{"bare label, name", "a", false, true},
   307  		{"bare label, constraint", "a", true, true},
   308  		{"63 char single label, name", strings.Repeat("a", 63), false, true},
   309  		{"63 char single label, constraint", strings.Repeat("a", 63), true, true},
   310  		{"63 char label, name", "a." + strings.Repeat("a", 63), false, true},
   311  		{"63 char label, constraint", "a." + strings.Repeat("a", 63), true, true},
   312  	} {
   313  		t.Run(tc.name, func(t *testing.T) {
   314  			valid := domainNameValid(tc.dnsName, tc.constraint)
   315  			if tc.valid != valid {
   316  				t.Errorf("domainNameValid(%q, %t) = %v; want %v", tc.dnsName, tc.constraint, !tc.valid, tc.valid)
   317  			}
   318  			// Also check that we enforce the same properties as domainToReverseLabels
   319  			trimmedName := tc.dnsName
   320  			if tc.constraint && len(trimmedName) > 1 && trimmedName[0] == '.' {
   321  				trimmedName = trimmedName[1:]
   322  			}
   323  			_, revValid := domainToReverseLabels(trimmedName)
   324  			if valid != revValid {
   325  				t.Errorf("domainNameValid(%q, %t) = %t != domainToReverseLabels(%q) = %t", tc.dnsName, tc.constraint, valid, trimmedName, revValid)
   326  			}
   327  		})
   328  	}
   329  }
   330  
   331  func TestRoundtripWeirdSANs(t *testing.T) {
   332  	// TODO(#75835): check that certificates we create with CreateCertificate that have malformed SAN values
   333  	// can be parsed by ParseCertificate. We should eventually restrict this, but for now we have to maintain
   334  	// this property as people have been relying on it.
   335  	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   336  	if err != nil {
   337  		t.Fatal(err)
   338  	}
   339  	badNames := []string{
   340  		"baredomain",
   341  		"baredomain.",
   342  		strings.Repeat("a", 255),
   343  		strings.Repeat("a", 65) + ".com",
   344  	}
   345  	tmpl := &Certificate{
   346  		EmailAddresses: badNames,
   347  		DNSNames:       badNames,
   348  	}
   349  	b, err := CreateCertificate(rand.Reader, tmpl, tmpl, &k.PublicKey, k)
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	_, err = ParseCertificate(b)
   354  	if err != nil {
   355  		t.Fatalf("Couldn't roundtrip certificate: %v", err)
   356  	}
   357  }
   358  
   359  func FuzzDomainNameValid(f *testing.F) {
   360  	f.Fuzz(func(t *testing.T, data string) {
   361  		domainNameValid(data, false)
   362  		domainNameValid(data, true)
   363  	})
   364  }
   365  
   366  func TestParseNameTypes(t *testing.T) {
   367  	block, _ := pem.Decode([]byte(`
   368  -----BEGIN CERTIFICATE-----
   369  MIICGzCCAcGgAwIBAgIJAIZft7jy3RcpMAoGCCqGSM49BAMCMA8xDTALBgNVBAMM
   370  BFRlc3QwHhcNMjUwOTAyMTg0MzE3WhcNMjUxMDAyMTg0MzE3WjCCARUxIjAgBg0q
   371  hkiG9xIEAYS3CQIBDA91dGY4LXN0cmluZ/CfpooxHzAdBg0qhkiG9xIEAYS3CQIC
   372  BAxvY3RldC1zdHJpbmcxGDAWBg0qhkiG9xIEAYS3CQIDDQUBAgMEBTETMBEGDSqG
   373  SIb3EgQBhLcJAgQFADEVMBMGDSqGSIb3EgQBhLcJAgUwAgECMRQwEgYNKoZIhvcS
   374  BAGEtwkCBgIBKjEgMB4GDSqGSIb3EgQBhLcJAgcGDSqGSIb3EgQBhLcJAgcxGDAW
   375  Bg0qhkiG9xIEAYS3CQIIAwUAqrvM3TEgMB4GDSqGSIb3EgQBhLcJAgkXDTI1MDkw
   376  MjE4NDMxN1oxFDASBg0qhkiG9xIEAYS3CQIKAQH/MFkwEwYHKoZIzj0CAQYIKoZI
   377  zj0DAQcDQgAE7M4zoqQtXbvGsudKaM5gd8emxyk68AFTjIYU4PO1AtiYX3wyL89k
   378  wbHjxgvmh/9aBg1LOj6kfsJIxULUmUpdzTAKBggqhkjOPQQDAgNIADBFAiBvKz3o
   379  ALhCqKrRFLUbax6+tI1s1B14IPVk2ZHbBEou5gIhAOpvJRNj5qluPXKLXmZvIK8u
   380  OjUhiZoowYvborSS1EBK
   381  -----END CERTIFICATE-----
   382  	`))
   383  	expected := map[string]any{
   384  		"1.2.840.113554.4.1.72585.2.1": "utf8-string🦊",
   385  		"1.2.840.113554.4.1.72585.2.2": []byte("octet-string"),
   386  		"1.2.840.113554.4.1.72585.2.3": asn1.RawValue{Tag: 13,
   387  			Bytes: []byte{1, 2, 3, 4, 5}, FullBytes: []byte{13, 5, 1, 2, 3, 4, 5}},
   388  		"1.2.840.113554.4.1.72585.2.4": nil,
   389  		"1.2.840.113554.4.1.72585.2.5": asn1.RawValue{Tag: asn1.TagSequence, IsCompound: true,
   390  			Bytes: []byte{1, 2}, FullBytes: []byte{0x30, 2, 1, 2}},
   391  		"1.2.840.113554.4.1.72585.2.6": int64(42),
   392  		"1.2.840.113554.4.1.72585.2.7": asn1.ObjectIdentifier{1, 2, 840, 113554, 4, 1, 72585, 2, 7},
   393  		"1.2.840.113554.4.1.72585.2.8": asn1.BitString{BitLength: 32,
   394  			Bytes: []byte{0xaa, 0xbb, 0xcc, 0xdd}},
   395  		"1.2.840.113554.4.1.72585.2.9":  time.Date(2025, 9, 2, 18, 43, 17, 0, time.UTC),
   396  		"1.2.840.113554.4.1.72585.2.10": true,
   397  	}
   398  	cert, err := ParseCertificate(block.Bytes)
   399  	if err != nil {
   400  		t.Fatalf("ParseCertificate failed: %v", err)
   401  	}
   402  	for _, attr := range cert.Subject.Names {
   403  		if val, ok := expected[attr.Type.String()]; ok {
   404  			if !reflect.DeepEqual(attr.Value, val) {
   405  				t.Errorf("unexpected value for %s: got %v (%T), want %v (%T)", attr.Type, attr.Value, attr.Value, val, val)
   406  			}
   407  		} else {
   408  			t.Errorf("unexpected attribute type: %s", attr.Type)
   409  		}
   410  	}
   411  	extra := pkix.Name{ExtraNames: cert.Subject.Names}
   412  	// asn1.Marshal does not encode NULL.
   413  	for i := range extra.ExtraNames {
   414  		if extra.ExtraNames[i].Value == nil {
   415  			extra.ExtraNames[i].Value = asn1.NullRawValue
   416  		}
   417  	}
   418  	got, err := asn1.Marshal(extra.ToRDNSequence())
   419  	if err != nil {
   420  		t.Fatalf("asn1.Marshal failed: %v", err)
   421  	}
   422  	if !bytes.Equal(got, cert.RawSubject) {
   423  		t.Errorf("unexpected marshaled RDNSequence: got %x, want %x", got, cert.RawSubject)
   424  	}
   425  }
   426  

View as plain text