diff --git a/v3/cmd/genTestCerts/go.sum b/v3/cmd/genTestCerts/go.sum index c3ec2324f..d8f2d3c92 100644 --- a/v3/cmd/genTestCerts/go.sum +++ b/v3/cmd/genTestCerts/go.sum @@ -49,6 +49,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -65,6 +66,7 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -85,6 +87,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -93,6 +97,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext.go b/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext.go index e32eab51e..d22312eab 100644 --- a/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext.go +++ b/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext.go @@ -20,11 +20,12 @@ package cabf_ev import ( + "fmt" + "github.com/zmap/zcrypto/x509" "github.com/zmap/zlint/v3/lint" "github.com/zmap/zlint/v3/util" - "errors" "regexp" ) @@ -43,42 +44,46 @@ func init() { // According to EVGs 9.2.8 type OrganizationIdentifier struct { - Scheme string - Country string - State string - Reference string + ParseAsPSD bool + Scheme string + Country string + State string + Reference string } -// This is according to the EVG (stricter than ETSI EN 319 412-1) -var OrgIdPattern = `^(?P[A-Z]{3})(?P[A-Z]{2})(?:\+(?P[A-Z]{2}))?\-(?P.+)$` - -func ParseOrgId(orgIdString string, orgId *OrganizationIdentifier) error { - - re := regexp.MustCompile(OrgIdPattern) - - if !re.MatchString(orgIdString) { - return errors.New("Cannot parse organizationIdentifier: it is probably invalid") +func (o OrganizationIdentifier) Parse(orgId string) (OrganizationIdentifier, error) { + re := o.regexForOrgID() + if !re.MatchString(orgId) { + return o, fmt.Errorf("Cannot parse organizationIdentifier ('%s'): it is probably invalid", orgId) } - names := re.SubexpNames() - match := re.FindStringSubmatch(orgIdString) - + match := re.FindStringSubmatch(orgId) // Initialize a map to hold group names and values result := make(map[string]string) - // Populate the map for i, name := range names { if i != 0 && name != "" { // Skip the whole match and unnamed groups result[name] = match[i] } } + o.Scheme = result["scheme"] + o.Country = result["country"] + o.State = result["state"] + o.Reference = result["reference"] + return o, nil +} - orgId.Scheme = result["scheme"] - orgId.Country = result["country"] - orgId.State = result["state"] - orgId.Reference = result["reference"] - - return nil +func (o OrganizationIdentifier) regexForOrgID() *regexp.Regexp { + // This is according to the EVG (stricter than ETSI EN 319 412-1) + const OrgIdPattern = `^(?P[A-Z]{3})(?P[A-Z]{2})(?:\+(?P[A-Z]{2}))?\-(?P.+)$` + const PsdOrgIdPattern = `^(?P[A-Z]{3})(?P[A-Z]{2})(?:\+(?P[A-Z]{2}))?\-(?P[A-Z]*)\-(?P.+)$` + var pattern string + if o.ParseAsPSD { + pattern = PsdOrgIdPattern + } else { + pattern = OrgIdPattern + } + return regexp.MustCompile(pattern) } type orgIdInconsistentSubjAndExt struct{} @@ -96,8 +101,7 @@ func (l *orgIdInconsistentSubjAndExt) CheckApplies(c *x509.Certificate) bool { func (l *orgIdInconsistentSubjAndExt) Execute(c *x509.Certificate) *lint.LintResult { // It should be safe to assume there is only one element in OrganizationIDs - var orgId OrganizationIdentifier - err := ParseOrgId(c.Subject.OrganizationIDs[0], &orgId) + orgId, err := OrganizationIdentifier{ParseAsPSD: false}.Parse(c.Subject.OrganizationIDs[0]) if err != nil { return &lint.LintResult{ Status: lint.Error, @@ -109,9 +113,30 @@ func (l *orgIdInconsistentSubjAndExt) Execute(c *x509.Certificate) *lint.LintRes (c.CABFOrganizationIdentifier.State != orgId.State) || (c.CABFOrganizationIdentifier.Reference != orgId.Reference) { - return &lint.LintResult{ - Status: lint.Error, - Details: "CABFOrganizationIdentifier is NOT consistent with organizationIdentifier"} + if orgId.Scheme != "PSD" { + + return &lint.LintResult{ + Status: lint.Error, + Details: "CABFOrganizationIdentifier is NOT consistent with organizationIdentifier"} + } + + psdOrgId, err := OrganizationIdentifier{ParseAsPSD: true}.Parse(c.Subject.OrganizationIDs[0]) + if err != nil { + return &lint.LintResult{ + Status: lint.Error, + Details: "the organizationIdentifier Subject attribute probably has an invalid value"} + } + + if (c.CABFOrganizationIdentifier.Scheme != psdOrgId.Scheme) || + (c.CABFOrganizationIdentifier.Country != psdOrgId.Country) || + (c.CABFOrganizationIdentifier.State != psdOrgId.State) || + (c.CABFOrganizationIdentifier.Reference != psdOrgId.Reference) { + + return &lint.LintResult{ + Status: lint.Error, + Details: "CABFOrganizationIdentifier is NOT consistent with organizationIdentifier"} + } + } return &lint.LintResult{Status: lint.Pass} diff --git a/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext_test.go b/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext_test.go index a8592c41b..5adcf7296 100644 --- a/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext_test.go +++ b/v3/lints/cabf_ev/lint_ev_orgid_inconsistent_subj_and_ext_test.go @@ -72,6 +72,10 @@ func TestOrgIdInconsistentSubjAndExt(t *testing.T) { input: "orgid_subj_and_ext_ok_05.pem", want: lint.NA, }, + { + input: "orgid_subj_and_ext_ok_06.pem", + want: lint.Pass, + }, { input: "orgid_subj_and_ext_ko_01.pem", want: lint.Error, diff --git a/v3/testdata/orgid_subj_and_ext_ok_06.pem b/v3/testdata/orgid_subj_and_ext_ok_06.pem new file mode 100644 index 000000000..e22544bcd --- /dev/null +++ b/v3/testdata/orgid_subj_and_ext_ok_06.pem @@ -0,0 +1,84 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1726042924 (0x66e1532c) + Signature Algorithm: sha256WithRSAEncryption + Issuer: organizationIdentifier=PSDIT-BI-1234, CN=example.com + Validity + Not Before: Sep 11 08:22:03 2024 GMT + Not After : Sep 11 08:22:03 2025 GMT + Subject: organizationIdentifier=PSDIT-BI-1234, CN=example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a4:73:99:8a:7c:9a:49:9b:91:5b:dd:d3:34:22: + a9:04:97:29:41:6f:d1:dd:34:30:28:d1:8c:8a:5a: + 4d:48:cd:27:63:38:53:85:77:cf:82:6b:4b:b4:cb: + 32:53:04:20:ae:98:7d:51:05:cd:fe:80:f4:ed:df: + 7d:c9:89:ed:70:bd:7c:a1:d7:b9:75:d9:5f:06:05: + 6a:18:f3:80:17:d6:b6:d6:ea:71:10:db:e7:68:d3: + 57:94:1f:79:0e:f4:72:99:66:ef:a8:e2:63:67:4b: + f0:1d:98:75:4c:62:7b:6a:18:5e:4e:60:d4:cc:19: + 93:26:0e:a1:de:d7:47:84:36:10:99:ac:9a:4f:23: + 03:67:5e:34:ff:d5:f2:7b:df:39:e7:26:71:bd:8d: + 13:da:72:b2:76:65:a3:d0:7e:ab:8d:e6:e3:bd:cc: + 56:59:0b:17:e0:41:fb:a5:10:01:23:1e:c8:24:9e: + 7c:29:15:40:cd:6e:54:3b:54:99:8c:51:49:77:82: + 13:ee:63:b7:90:76:3e:9c:1d:4d:b7:13:dd:b5:f8: + 1f:62:bb:d4:cd:32:a2:61:fd:11:1f:4d:75:97:22: + 3a:76:cf:e0:65:0d:39:40:8e:7a:7c:48:6c:42:f0: + 7b:f9:fd:e0:82:df:53:d4:53:40:87:81:a0:c0:df: + ca:99 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Certificate Policies: + Policy: 2.23.140.1.1 + X509v3 Subject Key Identifier: + 55:E4:74:FC:B6:EF:25:AB:4E:B9:C8:7E:EB:3C:9F:8B:A8:00:9C:1B + 2.23.140.3.1: + 0...PSD..IT..1234 + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 11:34:72:b8:bb:52:71:78:11:93:ef:a4:09:c5:1a:17:18:32: + 6d:34:e6:e6:58:be:42:65:bd:2e:1e:2e:c2:c7:bc:ab:21:a1: + 12:88:d3:85:80:9d:5e:0a:77:c4:67:b8:fc:09:54:c0:4d:e0: + cb:6d:d8:80:9b:6f:f8:26:92:96:7c:55:ba:a4:85:66:f1:d1: + 8f:ae:59:e0:8e:53:90:06:f1:ff:4a:66:a0:18:ee:da:84:ec: + 3d:9c:7a:14:8f:65:c6:ae:84:02:b9:81:ba:75:0e:3e:47:f1: + 8f:2e:06:cb:be:2b:97:2a:02:19:05:89:6c:d7:8c:f5:b2:96: + be:7d:62:14:8c:fa:34:37:9b:55:5d:ba:f9:a3:78:a5:c7:5f: + 85:bb:c9:30:bf:d7:d2:72:16:a4:0f:10:a4:c9:37:66:9a:60: + a1:1d:23:5c:e5:cc:10:66:1f:55:56:82:86:1a:f4:ae:33:53: + c0:02:51:c5:23:0e:21:e3:d3:1c:6f:2a:73:fe:14:47:77:11: + e9:c2:f5:05:b6:ea:69:e6:9c:b7:a9:0d:7d:e7:e5:5f:9c:45: + 12:de:1b:a8:6e:80:5f:33:c9:68:85:8e:d9:04:8f:36:87:f2: + fc:73:f6:0d:a4:67:85:05:7c:1d:df:ec:cd:cd:54:47:9d:e7: + 7a:d2:30:41 +-----BEGIN CERTIFICATE----- +MIIDajCCAlKgAwIBAgIEZuFTLDANBgkqhkiG9w0BAQsFADAuMRYwFAYDVQRhDA1Q +U0RJVC1CSS0xMjM0MRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0yNDA5MTEwODIy +MDNaFw0yNTA5MTEwODIyMDNaMC4xFjAUBgNVBGEMDVBTRElULUJJLTEyMzQxFDAS +BgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEApHOZinyaSZuRW93TNCKpBJcpQW/R3TQwKNGMilpNSM0nYzhThXfPgmtLtMsy +UwQgrph9UQXN/oD07d99yYntcL18ode5ddlfBgVqGPOAF9a21upxENvnaNNXlB95 +DvRymWbvqOJjZ0vwHZh1TGJ7ahheTmDUzBmTJg6h3tdHhDYQmayaTyMDZ140/9Xy +e9855yZxvY0T2nKydmWj0H6rjebjvcxWWQsX4EH7pRABIx7IJJ58KRVAzW5UO1SZ +jFFJd4IT7mO3kHY+nB1NtxPdtfgfYrvUzTKiYf0RH011lyI6ds/gZQ05QI56fEhs +QvB7+f3ggt9T1FNAh4GgwN/KmQIDAQABo4GPMIGMMAwGA1UdEwEB/wQCMAAwDgYD +VR0PAQH/BAQDAgWgMBIGA1UdIAQLMAkwBwYFZ4EMAQEwHQYDVR0OBBYEFFXkdPy2 +7yWrTrnIfus8n4uoAJwbMBoGBWeBDAMBBBEwDxMDUFNEEwJJVAwEMTIzNDAdBgNV +HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBABE0 +cri7UnF4EZPvpAnFGhcYMm005uZYvkJlvS4eLsLHvKshoRKI04WAnV4Kd8RnuPwJ +VMBN4Mtt2ICbb/gmkpZ8VbqkhWbx0Y+uWeCOU5AG8f9KZqAY7tqE7D2cehSPZcau +hAK5gbp1Dj5H8Y8uBsu+K5cqAhkFiWzXjPWylr59YhSM+jQ3m1VduvmjeKXHX4W7 +yTC/19JyFqQPEKTJN2aaYKEdI1zlzBBmH1VWgoYa9K4zU8ACUcUjDiHj0xxvKnP+ +FEd3EenC9QW26mnmnLepDX3n5V+cRRLeG6hugF8zyWiFjtkEjzaH8vxz9g2kZ4UF +fB3f7M3NVEed53rSMEE= +-----END CERTIFICATE-----