From 71d5e4b1cbf7636331bbf1c839d24abb4a945633 Mon Sep 17 00:00:00 2001 From: Paul van Brouwershaven Date: Sun, 27 Aug 2023 23:26:07 +0200 Subject: [PATCH] Reintroduce lint for inconsistent KU and EKU (#708) * Add function to get human friendly KeyUsage names * Add lint to check for KU and EKU inconsistency * Add func to get EKU strings * Sort KeyUsage strings for consistency in messages * Consider multiple purposes * Update result for integration test * Fix formatting * Add KU/EKU inconsistent test cases * No error on undefined extended key usage * Move sort from util to lint and include comment * Add some comments around the cyclomatic complexity * Update count for test corpus incl email certs --- v3/integration/config.json | 3 + v3/integration/small.config.json | 2 +- ...age_and_extended_key_usage_inconsistent.go | 213 ++++++++++++++++++ ...nd_extended_key_usage_inconsistent_test.go | 58 +++++ v3/testdata/kuEkuConsistent.pem | 39 ++++ v3/testdata/kuEkuConsistentMp.pem | 40 ++++ v3/testdata/kuEkuInconsistent.pem | 39 ++++ v3/testdata/kuEkuInconsistentMp.pem | 40 ++++ v3/util/eku.go | 44 +++- v3/util/ku.go | 17 +- 10 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent.go create mode 100644 v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent_test.go create mode 100644 v3/testdata/kuEkuConsistent.pem create mode 100644 v3/testdata/kuEkuConsistentMp.pem create mode 100644 v3/testdata/kuEkuInconsistent.pem create mode 100644 v3/testdata/kuEkuInconsistentMp.pem diff --git a/v3/integration/config.json b/v3/integration/config.json index 0ca05803a..5408158db 100644 --- a/v3/integration/config.json +++ b/v3/integration/config.json @@ -542,6 +542,9 @@ "e_invalid_certificate_version": {}, "e_issuer_dn_country_not_printable_string": {}, "e_issuer_field_empty": {}, + "e_key_usage_and_extended_key_usage_inconsistent": { + "ErrCount": 31843 + }, "e_key_usage_incorrect_length": {}, "e_mp_authority_key_identifier_correct": { "ErrCount": 3704 diff --git a/v3/integration/small.config.json b/v3/integration/small.config.json index f8b92f1dd..10b6ebd0c 100644 --- a/v3/integration/small.config.json +++ b/v3/integration/small.config.json @@ -153,7 +153,7 @@ "e_issuer_dn_country_not_printable_string": {}, "e_issuer_field_empty": {}, "e_key_usage_and_extended_key_usage_inconsistent": { - "ErrCount": 18858 + "ErrCount": 1020 }, "e_mp_authority_key_identifier_correct": { "ErrCount": 125 diff --git a/v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent.go b/v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent.go new file mode 100644 index 000000000..fecb6517b --- /dev/null +++ b/v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent.go @@ -0,0 +1,213 @@ +package rfc + +/* + * ZLint Copyright 2021 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ( + "fmt" + "sort" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/util" +) + +type KUAndEKUInconsistent struct{} + +func init() { + lint.RegisterLint(&lint.Lint{ + Name: "e_key_usage_and_extended_key_usage_inconsistent", + Description: "The certificate MUST only be used for a purpose consistent with both key usage extension and extended key usage extension.", + Citation: "RFC 5280, Section 4.2.1.12.", + Source: lint.RFC5280, + EffectiveDate: util.RFC5280Date, + Lint: NewKUAndEKUInconsistent, + }) +} + +func NewKUAndEKUInconsistent() lint.LintInterface { + return &KUAndEKUInconsistent{} +} + +func (l *KUAndEKUInconsistent) Initialize() error { + return nil +} + +// CheckApplies returns true when the certificate contains both a key usage +// extension and an extended key usage extension. +func (l *KUAndEKUInconsistent) CheckApplies(c *x509.Certificate) bool { + return util.IsSubscriberCert(c) && util.IsExtInCert(c, util.EkuSynOid) && util.IsExtInCert(c, util.KeyUsageOID) +} + +// Execute returns an Error level lint.LintResult if the purposes of the certificate +// being linted is not consistent with both extensions. +func (l *KUAndEKUInconsistent) Execute(c *x509.Certificate) *lint.LintResult { + if len(c.ExtKeyUsage) > 1 { + return l.multiPurpose(c) + } + return l.strictPurpose(c) +} + +// RFC 5280 4.2.1.12 on multiple purposes: +// +// If multiple purposes are indicated the application need not recognize all purposes +// indicated, as long as the intended purpose is present. +func (l *KUAndEKUInconsistent) multiPurpose(c *x509.Certificate) *lint.LintResult { + // Create a map with each KeyUsage combination that is authorized for the + // included extKeyUsage(es). + var mp = map[x509.KeyUsage]bool{} + for _, extKeyUsage := range c.ExtKeyUsage { + var i int + if _, ok := eku[extKeyUsage]; !ok { + return &lint.LintResult{Status: lint.Pass} + } + for ku := range eku[extKeyUsage] { + // There is nothing to merge for the first EKU. + if i > 0 { + // We could see this EKU combined with any other EKU so + // create that possibility. + for mpku := range mp { + mp[mpku|ku] = true + } + } + + mp[ku] = true + i++ + } + } + if !mp[c.KeyUsage] { + // Sort the included KeyUsage strings for consistent error messages + // The order does not matter for this lint, but the consistency makes + // it easier to identify common errors. + keyUsage := util.GetKeyUsageStrings(c.KeyUsage) + sort.Strings(keyUsage) + + return &lint.LintResult{ + Status: lint.Error, + Details: fmt.Sprintf("KeyUsage %v (%08b) inconsistent with multiple purpose ExtKeyUsage %v", keyUsage, c.KeyUsage, util.GetEKUStrings(c.ExtKeyUsage)), + } + } + return &lint.LintResult{Status: lint.Pass} +} + +// strictPurpose checks if the Key Usages (KU) included are permitted for each +// indicated Extended Key Usage (EKU) +func (l *KUAndEKUInconsistent) strictPurpose(c *x509.Certificate) *lint.LintResult { + for _, extKeyUsage := range c.ExtKeyUsage { + if _, ok := eku[extKeyUsage]; !ok { + continue + } + if !eku[extKeyUsage][c.KeyUsage] { + return &lint.LintResult{ + Status: lint.Error, + Details: fmt.Sprintf("KeyUsage %v (%08b) inconsistent with ExtKeyUsage %s", util.GetKeyUsageStrings(c.KeyUsage), c.KeyUsage, util.GetEKUString(extKeyUsage)), + } + } + } + return &lint.LintResult{Status: lint.Pass} +} + +var eku = map[x509.ExtKeyUsage]map[x509.KeyUsage]bool{ + + // KU combinations with Server Authentication EKU: + // RFC 5280 4.2.1.12 on KU consistency with Server Authentication EKU: + // -- TLS WWW server authentication + // -- Key usage bits that may be consistent: digitalSignature, + // -- keyEncipherment or keyAgreement + + // (digitalSignature OR (keyEncipherment XOR keyAgreement)) + x509.ExtKeyUsageServerAuth: { + x509.KeyUsageDigitalSignature: true, + x509.KeyUsageKeyEncipherment: true, + x509.KeyUsageKeyAgreement: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement: true, + }, + + // KU combinations with Client Authentication EKU: + // RFC 5280 4.2.1.12 on KU consistency with Client Authentication EKU: + // -- TLS WWW client authentication + // -- Key usage bits that may be consistent: digitalSignature + // -- and/or keyAgreement + + // (digitalSignature OR keyAgreement) + x509.ExtKeyUsageClientAuth: { + x509.KeyUsageDigitalSignature: true, + x509.KeyUsageKeyAgreement: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement: true, + }, + + // KU combinations with Code Signing EKU: + // RFC 5280 4.2.1.12 on KU consistency with Code Signing EKU: + // -- Signing of downloadable executable code + // -- Key usage bits that may be consistent: digitalSignature + + // (digitalSignature) + x509.ExtKeyUsageCodeSigning: { + x509.KeyUsageDigitalSignature: true, + }, + + // KU combinations with Email Protection EKU: + // RFC 5280 4.2.1.12 on KU consistency with Email Protection EKU: + // -- Email protection + // -- Key usage bits that may be consistent: digitalSignature, + // -- nonRepudiation, and/or (keyEncipherment or keyAgreement) + // Note: Recent editions of X.509 have renamed nonRepudiation bit to contentCommitment + + // (digitalSignature OR nonRepudiation OR (keyEncipherment XOR keyAgreement)) + x509.ExtKeyUsageEmailProtection: { + x509.KeyUsageDigitalSignature: true, + x509.KeyUsageContentCommitment: true, + x509.KeyUsageKeyEncipherment: true, + x509.KeyUsageKeyAgreement: true, + + x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement: true, + + x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageKeyAgreement: true, + + x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment: true, + x509.KeyUsageContentCommitment | x509.KeyUsageKeyAgreement: true, + }, + + // KU combinations with Time Stamping EKU: + // RFC 5280 4.2.1.12 on KU consistency with Time Stamping EKU: + // -- Binding the hash of an object to a time + // -- Key usage bits that may be consistent: digitalSignature + // -- and/or nonRepudiation + // Note: Recent editions of X.509 have renamed nonRepudiation bit to contentCommitment + + // (digitalSignature OR nonRepudiation) + x509.ExtKeyUsageTimeStamping: { + x509.KeyUsageDigitalSignature: true, + x509.KeyUsageContentCommitment: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment: true, + }, + + // KU combinations with Ocsp Signing EKU: + // RFC 5280 4.2.1.12 on KU consistency with Ocsp Signing EKU: + // -- Signing OCSP responses + // -- Key usage bits that may be consistent: digitalSignature + // -- and/or nonRepudiation + // Note: Recent editions of X.509 have renamed nonRepudiation bit to contentCommitment + + // (digitalSignature OR nonRepudiation) + x509.ExtKeyUsageOcspSigning: { + x509.KeyUsageDigitalSignature: true, + x509.KeyUsageContentCommitment: true, + x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment: true, + }, +} diff --git a/v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent_test.go b/v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent_test.go new file mode 100644 index 000000000..cce6b8491 --- /dev/null +++ b/v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent_test.go @@ -0,0 +1,58 @@ +package rfc + +/* + * ZLint Copyright 2021 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ( + "testing" + + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/test" +) + +func TestStrictFail(t *testing.T) { + inputPath := "kuEkuInconsistent.pem" + expected := lint.Error + out := test.TestLint("e_key_usage_and_extended_key_usage_inconsistent", inputPath) + if out.Status != expected { + t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status) + } +} + +func TestStrictPass(t *testing.T) { + inputPath := "kuEkuConsistent.pem" + expected := lint.Pass + out := test.TestLint("e_key_usage_and_extended_key_usage_inconsistent", inputPath) + if out.Status != expected { + t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status) + } +} + +func TestMultiPurposeFail(t *testing.T) { + inputPath := "kuEkuInconsistentMp.pem" + expected := lint.Error + out := test.TestLint("e_key_usage_and_extended_key_usage_inconsistent", inputPath) + if out.Status != expected { + t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status) + } +} + +func TestMultiPurposePass(t *testing.T) { + inputPath := "kuEkuConsistentMp.pem" + expected := lint.Pass + out := test.TestLint("e_key_usage_and_extended_key_usage_inconsistent", inputPath) + if out.Status != expected { + t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status) + } +} diff --git a/v3/testdata/kuEkuConsistent.pem b/v3/testdata/kuEkuConsistent.pem new file mode 100644 index 000000000..b2b0b2d54 --- /dev/null +++ b/v3/testdata/kuEkuConsistent.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: May 1 00:00:00 2008 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:08:95:ad:12:a9:39:ec:b1:df:31:12:e6:79:2f: + 5e:59:f1:ff:b1:25:b1:92:76:f4:e5:0b:ea:20:ba: + 02:b2:7b:bd:32:ae:f5:f3:de:77:b2:2d:08:16:8b: + 8c:df:08:27:25:cd:b9:1c:3c:dd:19:d4:5f:92:19: + ab:f7:62:3f:fb + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature + X509v3 Extended Key Usage: + TLS Web Server Authentication + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:9d:fb:3f:05:b5:28:fb:21:1e:ca:80:fe:07: + d6:92:25:12:9c:de:46:28:3e:97:f8:5c:9e:7e:17:5a:33:c5: + 60:02:20:3e:8c:a5:8c:37:b5:c2:44:7d:f5:fc:33:f6:d1:e9: + f6:89:75:39:d8:73:b2:20:fe:54:7f:83:ce:30:34:e3:98 +-----BEGIN CERTIFICATE----- +MIIBFzCBvqADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMDgwNTAxMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQIla0S +qTnssd8xEuZ5L15Z8f+xJbGSdvTlC+ogugKye70yrvXz3neyLQgWi4zfCCclzbkc +PN0Z1F+SGav3Yj/7oycwJTAOBgNVHQ8BAf8EBAMCAIAwEwYDVR0lBAwwCgYIKwYB +BQUHAwEwCgYIKoZIzj0EAwIDSAAwRQIhAJ37PwW1KPshHsqA/gfWkiUSnN5GKD6X ++FyefhdaM8VgAiA+jKWMN7XCRH31/DP20en2iXU52HOyIP5Uf4POMDTjmA== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/kuEkuConsistentMp.pem b/v3/testdata/kuEkuConsistentMp.pem new file mode 100644 index 000000000..1dc75d2fb --- /dev/null +++ b/v3/testdata/kuEkuConsistentMp.pem @@ -0,0 +1,40 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: May 1 00:00:00 2008 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:eb:8a:9c:e0:fd:6a:ad:be:c1:38:81:a5:44:c4: + 1a:ad:90:29:90:7f:6d:38:2f:83:ce:f2:66:fc:ab: + fa:e0:b5:84:6e:ca:20:4b:69:4f:17:68:17:1c:24: + ab:51:7e:fa:cb:88:18:51:78:d8:35:bf:ff:86:96: + f8:14:d9:1c:8f + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Non Repudiation + X509v3 Extended Key Usage: + E-mail Protection, TLS Web Client Authentication + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:ac:7b:1b:25:7f:56:5d:32:19:ca:de:8f:44: + e6:52:fa:db:5e:5a:43:92:4e:87:f2:b8:43:7d:be:fd:df:ec: + 38:02:20:6e:59:a6:36:4f:8d:2a:92:b8:9e:b6:43:0d:6a:1e: + 95:ca:a1:f1:7e:3d:bb:97:58:ab:c7:fb:3f:d9:5d:85:09 +-----BEGIN CERTIFICATE----- +MIIBITCByKADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMDgwNTAxMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATripzg +/WqtvsE4gaVExBqtkCmQf204L4PO8mb8q/rgtYRuyiBLaU8XaBccJKtRfvrLiBhR +eNg1v/+GlvgU2RyPozEwLzAOBgNVHQ8BAf8EBAMCAMAwHQYDVR0lBBYwFAYIKwYB +BQUHAwQGCCsGAQUFBwMCMAoGCCqGSM49BAMCA0gAMEUCIQCsexslf1ZdMhnK3o9E +5lL6215aQ5JOh/K4Q32+/d/sOAIgblmmNk+NKpK4nrZDDWoelcqh8X49u5dYq8f7 +P9ldhQk= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/kuEkuInconsistent.pem b/v3/testdata/kuEkuInconsistent.pem new file mode 100644 index 000000000..33abc0709 --- /dev/null +++ b/v3/testdata/kuEkuInconsistent.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: May 1 00:00:00 2008 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:ab:9a:98:17:73:5b:d0:cd:bd:8e:ff:a4:18:52: + ec:bd:18:e4:3a:b0:44:6a:e8:fc:75:ea:62:76:52: + 46:a7:dd:00:da:1d:4b:3b:31:f6:df:46:7f:24:8e: + 49:ec:20:a4:40:fb:11:7f:19:46:9c:b7:15:53:6d: + 5d:b7:11:77:34 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Non Repudiation + X509v3 Extended Key Usage: + TLS Web Server Authentication + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:6c:7c:22:2f:f5:87:ca:2f:ee:52:1b:37:d5:35: + d3:7b:8b:10:63:ea:ac:10:1b:0e:a6:34:78:df:be:e7:85:24: + 02:20:13:d4:bc:dc:46:07:97:35:cf:58:cf:13:cd:f3:c7:a3: + 25:d0:38:61:0d:22:b7:50:25:5e:ba:24:19:a9:92:67 +-----BEGIN CERTIFICATE----- +MIIBFjCBvqADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMDgwNTAxMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASrmpgX +c1vQzb2O/6QYUuy9GOQ6sERq6Px16mJ2Ukan3QDaHUs7MfbfRn8kjknsIKRA+xF/ +GUactxVTbV23EXc0oycwJTAOBgNVHQ8BAf8EBAMCAMAwEwYDVR0lBAwwCgYIKwYB +BQUHAwEwCgYIKoZIzj0EAwIDRwAwRAIgbHwiL/WHyi/uUhs31TXTe4sQY+qsEBsO +pjR4377nhSQCIBPUvNxGB5c1z1jPE83zx6Ml0DhhDSK3UCVeuiQZqZJn +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/kuEkuInconsistentMp.pem b/v3/testdata/kuEkuInconsistentMp.pem new file mode 100644 index 000000000..1811f02a3 --- /dev/null +++ b/v3/testdata/kuEkuInconsistentMp.pem @@ -0,0 +1,40 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: May 1 00:00:00 2008 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:57:e4:a6:3c:60:a4:c4:b4:3a:ce:88:0e:c9:59: + 75:66:95:f4:ac:2c:9d:4f:d3:83:5d:cd:af:4c:cf: + 20:ec:44:d0:23:dd:23:c8:d7:4e:ee:c7:e6:5c:ca: + 41:da:dd:69:44:d2:27:85:7d:08:08:57:d3:87:8e: + 3a:a3:04:38:ba + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Data Encipherment + X509v3 Extended Key Usage: + E-mail Protection, TLS Web Client Authentication + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:30:b1:f1:8b:7c:04:af:90:28:b7:20:5c:65:96: + a0:cd:fd:46:04:26:82:f7:01:1a:a5:bf:de:ec:54:77:75:e4: + 02:20:60:6c:51:ab:ec:86:0e:31:4b:79:f3:37:8c:12:6c:ff: + c5:86:c3:14:d6:de:d7:bd:96:39:fa:8d:50:17:79:59 +-----BEGIN CERTIFICATE----- +MIIBIDCByKADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMDgwNTAxMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARX5KY8 +YKTEtDrOiA7JWXVmlfSsLJ1P04Ndza9MzyDsRNAj3SPI107ux+ZcykHa3WlE0ieF +fQgIV9OHjjqjBDi6ozEwLzAOBgNVHQ8BAf8EBAMCAJAwHQYDVR0lBBYwFAYIKwYB +BQUHAwQGCCsGAQUFBwMCMAoGCCqGSM49BAMCA0cAMEQCIDCx8Yt8BK+QKLcgXGWW +oM39RgQmgvcBGqW/3uxUd3XkAiBgbFGr7IYOMUt58zeMEmz/xYbDFNbe172WOfqN +UBd5WQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/util/eku.go b/v3/util/eku.go index 9b2b53695..cd745da7d 100644 --- a/v3/util/eku.go +++ b/v3/util/eku.go @@ -1,6 +1,11 @@ package util -import "github.com/zmap/zcrypto/x509" +import ( + "fmt" + "sort" + + "github.com/zmap/zcrypto/x509" +) // HasEKU tests whether an Extended Key Usage (EKU) is present in a certificate. func HasEKU(cert *x509.Certificate, eku x509.ExtKeyUsage) bool { @@ -12,3 +17,40 @@ func HasEKU(cert *x509.Certificate, eku x509.ExtKeyUsage) bool { return false } + +// GetEKUString returns a human friendly Extended Key Usage (EKU) string. +func GetEKUString(eku x509.ExtKeyUsage) string { + switch eku { + case x509.ExtKeyUsageAny: + return "any" + case x509.ExtKeyUsageServerAuth: + return "serverAuth" + case x509.ExtKeyUsageClientAuth: + return "clientAuth" + case x509.ExtKeyUsageCodeSigning: + return "codeSigning" + case x509.ExtKeyUsageEmailProtection: + return "emailProtection" + case x509.ExtKeyUsageIpsecUser: + return "ipSecUser" + case x509.ExtKeyUsageIpsecTunnel: + return "ipSecTunnel" + case x509.ExtKeyUsageOcspSigning: + return "ocspSigning" + case x509.ExtKeyUsageMicrosoftServerGatedCrypto: + return "microsoftServerGatedCrypto" + case x509.ExtKeyUsageNetscapeServerGatedCrypto: + return "netscapeServerGatedCrypto" + } + return fmt.Sprintf("unknown EKU %d", eku) +} + +// GetEKUStrings returns a list of human friendly Extended Key Usage (EKU) strings. +func GetEKUStrings(eku []x509.ExtKeyUsage) []string { + var ekuStrings []string + for _, currentEku := range eku { + ekuStrings = append(ekuStrings, GetEKUString(currentEku)) + } + sort.Strings(ekuStrings) + return ekuStrings +} diff --git a/v3/util/ku.go b/v3/util/ku.go index 529e4c355..0d5e1eaa2 100644 --- a/v3/util/ku.go +++ b/v3/util/ku.go @@ -1,6 +1,10 @@ package util -import "github.com/zmap/zcrypto/x509" +import ( + "strings" + + "github.com/zmap/zcrypto/x509" +) var ( // KeyUsageToString maps an x509.KeyUsage bitmask to its name. @@ -34,3 +38,14 @@ func HasKeyUsage(c *x509.Certificate, usage x509.KeyUsage) bool { func KeyUsageIsPresent(keyUsages x509.KeyUsage, usage x509.KeyUsage) bool { return keyUsages&usage != 0 } + +// GetKeyUsageStrings returns a list of included key usages +func GetKeyUsageStrings(keyUsages x509.KeyUsage) []string { + var keyUsageStrings []string + for ku, name := range KeyUsageToString { + if KeyUsageIsPresent(keyUsages, ku) { + keyUsageStrings = append(keyUsageStrings, strings.TrimPrefix(name, "KeyUsage")) + } + } + return keyUsageStrings +}