Skip to content

Commit

Permalink
E ext cert policy disallowed any policy qualifier refactor (#732)
Browse files Browse the repository at this point in the history
* lint about the encoding of qcstatements for PSD2

* Revert "lint about the encoding of qcstatements for PSD2"

This reverts commit 6c23670.

* util: gtld_map autopull updates for 2021-10-21T07:25:20 UTC

* always check and perform the operation in the execution

* synchronised with project

* refactored implementation, tests, and testdata

* refactored implementation

* addressing high cyclomatic complexity

* code format

* code format

---------

Co-authored-by: mtg <git@mtg.de>
Co-authored-by: GitHub <noreply@github.com>
Co-authored-by: Christopher Henderson <chris@chenderson.org>
  • Loading branch information
4 people authored Jul 9, 2023
1 parent 7602109 commit 4d38bfe
Show file tree
Hide file tree
Showing 9 changed files with 1,201 additions and 26 deletions.
531 changes: 531 additions & 0 deletions h

Large diffs are not rendered by default.

113 changes: 109 additions & 4 deletions v3/lints/rfc/lint_ext_cert_policy_disallowed_any_policy_qualifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@ package rfc
*/

import (
"errors"

"github.com/zmap/zcrypto/encoding/asn1"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
)

type unrecommendedQualifier struct{}

type policyInformation struct {
policyIdentifier asn1.ObjectIdentifier
policyQualifiersBytes asn1.RawValue
}

/*******************************************************************
RFC 5280: 4.2.1.4
To promote interoperability, this profile RECOMMENDS that policy
Expand Down Expand Up @@ -49,16 +57,113 @@ func NewUnrecommendedQualifier() lint.LintInterface {
}

func (l *unrecommendedQualifier) CheckApplies(c *x509.Certificate) bool {
return util.IsExtInCert(c, util.CertPolicyOID)

// TODO? extract to util method: HasAnyPolicyOID(c)
if !util.IsExtInCert(c, util.CertPolicyOID) {
return false
}

for _, policyIds := range c.PolicyIdentifiers {
if policyIds.Equal(util.AnyPolicyOID) {
return true
}
}
return false
}

func (l *unrecommendedQualifier) Execute(c *x509.Certificate) *lint.LintResult {
for _, firstLvl := range c.QualifierId {
for _, qualifierId := range firstLvl {
if !qualifierId.Equal(util.CpsOID) && !qualifierId.Equal(util.UserNoticeOID) {

var err, certificatePolicies = getCertificatePolicies(c)

if err != nil {
return &lint.LintResult{Status: lint.Fatal, Details: err.Error()}
}

for _, policyInformation := range certificatePolicies {

if !policyInformation.policyIdentifier.Equal(util.AnyPolicyOID) { // if the policyIdentifier is not anyPolicy do not examine further
continue
}

if len(policyInformation.policyQualifiersBytes.Bytes) == 0 { // this policy information does not have any policyQualifiers
continue
}

var policyQualifiersSeq, policyQualifierInfoSeq asn1.RawValue

empty, err := asn1.Unmarshal(policyInformation.policyQualifiersBytes.Bytes, &policyQualifiersSeq)

if err != nil || len(empty) != 0 || policyQualifiersSeq.Class != 0 || policyQualifiersSeq.Tag != 16 || !policyQualifiersSeq.IsCompound {
return &lint.LintResult{Status: lint.Fatal, Details: "policyExtensions: Could not unmarshal policyQualifiers sequence."}
}

//iterate over policyQualifiers ... SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo OPTIONAL
for policyQualifierInfoSeqProcessed := false; !policyQualifierInfoSeqProcessed; {
// these bytes belong to the next PolicyQualifierInfo
policyQualifiersSeq.Bytes, err = asn1.Unmarshal(policyQualifiersSeq.Bytes, &policyQualifierInfoSeq)
if err != nil || policyQualifierInfoSeq.Class != 0 || policyQualifierInfoSeq.Tag != 16 || !policyQualifierInfoSeq.IsCompound {
return &lint.LintResult{Status: lint.Fatal, Details: "policyExtensions: Could not unmarshal policy qualifiers"}
}
if len(policyQualifiersSeq.Bytes) == 0 { // no further PolicyQualifierInfo exists
policyQualifierInfoSeqProcessed = true
}

var policyQualifierId asn1.ObjectIdentifier
_, err = asn1.Unmarshal(policyQualifierInfoSeq.Bytes, &policyQualifierId)
if err != nil {
return &lint.LintResult{Status: lint.Fatal, Details: "policyExtensions: Could not unmarshal policyQualifierId."}
}

if !policyQualifierId.Equal(util.CpsOID) && !policyQualifierId.Equal(util.UserNoticeOID) {
return &lint.LintResult{Status: lint.Error}
}
}
}

return &lint.LintResult{Status: lint.Pass}
}

func getCertificatePolicies(c *x509.Certificate) (error, []policyInformation) {

extVal := util.GetExtFromCert(c, util.CertPolicyOID).Value

// adjusted code taken from v3/util/oid.go GetMappedPolicies, see comments there
var certificatePoliciesSeq, policyInformationSeq asn1.RawValue

empty, err := asn1.Unmarshal(extVal, &certificatePoliciesSeq)

if err != nil || len(empty) != 0 || certificatePoliciesSeq.Class != 0 || certificatePoliciesSeq.Tag != 16 || !certificatePoliciesSeq.IsCompound {
return errors.New("policyExtensions: Could not unmarshal certificatePolicies sequence."), nil
}

var certificatePolicies []policyInformation

// iterate over certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
for policyInformationSeqProcessed := false; !policyInformationSeqProcessed; {

// these bytes belong to the next PolicyInformation
certificatePoliciesSeq.Bytes, err = asn1.Unmarshal(certificatePoliciesSeq.Bytes, &policyInformationSeq)
if err != nil || policyInformationSeq.Class != 0 || policyInformationSeq.Tag != 16 || !policyInformationSeq.IsCompound {
return errors.New("policyExtensions: Could not unmarshal policyInformation sequence."), nil
}

if len(certificatePoliciesSeq.Bytes) == 0 { // no further PolicyInformation exists
policyInformationSeqProcessed = true
}

//PolicyInformation ::= SEQUENCE {
// policyIdentifier CertPolicyId,
// policyQualifiers SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo OPTIONAL }

var certPolicyId asn1.ObjectIdentifier
var policyQualifiers asn1.RawValue
policyQualifiers.Bytes, err = asn1.Unmarshal(policyInformationSeq.Bytes, &certPolicyId)
if err != nil {
return errors.New("policyExtensions: Could not unmarshal certPolicyId."), nil
}

information := policyInformation{certPolicyId, policyQualifiers}
certificatePolicies = append(certificatePolicies, information)
}
return nil, certificatePolicies
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,55 @@ import (
"github.com/zmap/zlint/v3/test"
)

func TestNoticeRef(t *testing.T) {
inputPath := "userNoticePres.pem"
expected := lint.Pass
out := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
func TestUnrecommendedQualifier(t *testing.T) {
testCases := []struct {
Name string
InputFilename string
ExpectedResult lint.LintStatus
}{
{
Name: "Certificate with certificate policies extension and without the anyPolicy policyIdentifier present",
InputFilename: "withoutAnyPolicy.pem",
ExpectedResult: lint.NA,
},
{
Name: "Certificate without certificate policies extension",
InputFilename: "CNWithoutSANSeptember2021.pem",
ExpectedResult: lint.NA,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present, without policyQualifiers",
InputFilename: "withAnyPolicyAndNoPolicyQualifiers.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present and a CPS qualifier present",
InputFilename: "withAnyPolicyAndCPSQualifier.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present and a UserNotice qualifier present",
InputFilename: "withAnyPolicyAndUserNoticeQualifier.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present and neither CPS nor UserNotice qualifier present",
InputFilename: "withAnyPolicyWithoutCPSOrUserNoticeQualifier.pem",
ExpectedResult: lint.Error,
},
{
Name: "Certificate with certificate policies extension and many combinations of policies and qualifiers",
InputFilename: "withValidPoliciesRegardingAnyPolicy.pem",
ExpectedResult: lint.Pass,
},
}
}

func TestCps(t *testing.T) {
inputPath := "userNoticeMissing.pem"
expected := lint.Pass
out := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
}
}

func TestNoticeRefUnknown(t *testing.T) {
inputPath := "userNoticeUnrecommended.pem"
expected := lint.Error
out := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
result := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", tc.InputFilename)
if result.Status != tc.ExpectedResult {
t.Errorf("expected result %v was %v", tc.ExpectedResult, result.Status)
}
})
}
}
82 changes: 82 additions & 0 deletions v3/testdata/withAnyPolicyAndCPSQualifier.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
67:32:5c:93:e9:a2:32:b8:61:f6:d6:e2
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = JLint Sub CA, O = Lint, C = DE
Validity
Not Before: Jul 1 14:48:19 2023 GMT
Not After : Jul 1 15:48:19 2024 GMT
Subject: CN = e_ext_cert_policy_disallowed_any_policy_qualifier, O = Lint, C = DE
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:c6:ee:a4:ff:af:f9:d3:57:78:a1:35:b9:b9:6e:
f1:67:fd:3e:d3:b1:e5:13:25:5a:34:eb:68:7c:ea:
ae:32:01:e1:98:15:15:32:c3:03:75:e5:d6:2e:56:
2d:03:34:28:25:e0:77:b8:db:1a:47:d9:ff:b1:d4:
31:6a:d2:8e:ab:64:3a:0e:a3:e8:53:40:4f:ff:55:
32:1d:59:a6:db:09:20:aa:c3:ee:57:ca:90:8d:de:
26:2c:f5:b3:b3:45:d6:32:81:18:46:44:ad:1e:f8:
92:a3:ed:b3:af:e5:72:80:3d:0b:c8:fc:fa:a1:e6:
20:16:d7:18:70:4b:4a:c1:5f:a7:3b:aa:26:75:36:
7a:13:62:98:2e:8f:18:5c:c0:e7:88:40:36:03:44:
91:a9:80:3c:6a:dd:36:b1:53:ff:1b:d8:8a:97:ef:
06:04:e0:ce:8b:53:4e:24:5d:89:9e:75:b1:31:75:
bf:b3:26:ba:6b:08:70:49:b8:b8:76:2c:27:07:e7:
a6:e5:ee:ac:de:f6:28:6b:b8:78:0e:b0:53:12:c2:
0e:d7:b2:b7:e6:c2:e8:1d:2c:b1:6e:ac:19:a3:88:
14:67:3e:7b:67:04:34:e5:8d:90:23:06:63:e0:c3:
6b:b0:e1:c6:75:54:7d:47:47:c9:26:14:07:1a:e1:
6f:c5
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
keyid:C4:8F:CF:FE:87:49:92:71:70:4E:93:BC:C1:34:21:EE:A0:93:65:84

X509v3 Subject Key Identifier:
22:BF:08:67:F1:B4:F2:53:77:63:B5:3A:39:74:A3:80:C1:2F:C3:D1
X509v3 Certificate Policies: critical
Policy: X509v3 Any Policy
CPS: https://example.com/cps

Signature Algorithm: sha256WithRSAEncryption
3e:38:b8:e6:68:5f:81:95:8f:de:5f:dc:9a:82:8b:93:78:6d:
16:ba:7e:dd:57:72:9e:91:72:21:07:b0:22:3a:83:68:b9:b2:
26:ed:5b:9b:b9:b5:ac:49:8a:4c:8d:6f:32:cc:24:e7:b8:99:
2b:b9:47:68:4a:55:9a:7a:74:de:06:a6:2d:58:57:00:89:76:
ac:ec:99:4f:44:69:28:21:25:31:9b:35:9d:82:46:bf:9d:0e:
05:ff:58:a5:df:df:19:d2:df:4f:e2:ed:0d:85:d7:7d:98:e8:
dd:80:d8:e1:c5:3c:82:1b:69:3e:82:03:fc:2b:d5:87:37:c3:
b1:dc:06:f3:8e:83:42:90:b8:1c:2d:91:44:8c:8b:5a:eb:5c:
dc:77:86:e7:39:7b:c2:3c:40:1f:1c:5e:ad:f0:b4:2c:ad:45:
81:82:a2:37:17:c5:05:80:d5:9c:ee:f8:24:ea:2f:91:e2:95:
32:38:a0:fd:77:3c:ad:97:58:ff:3b:ba:0e:fd:a7:1a:06:61:
a0:6c:02:08:20:df:4e:9e:ab:f0:92:62:65:09:83:54:3e:17:
b4:a3:3a:8c:2c:c4:03:4d:5c:a7:bf:84:0f:0a:39:61:c5:39:
5c:8d:8a:24:0b:31:84:d1:76:2a:74:1b:da:9b:f9:13:c9:9e:
5f:f0:c1:34
-----BEGIN CERTIFICATE-----
MIIDkDCCAnigAwIBAgIMZzJck+miMrhh9tbiMA0GCSqGSIb3DQEBCwUAMDMxFTAT
BgNVBAMMDEpMaW50IFN1YiBDQTENMAsGA1UECgwETGludDELMAkGA1UEBhMCREUw
HhcNMjMwNzAxMTQ0ODE5WhcNMjQwNzAxMTU0ODE5WjBYMTowOAYDVQQDDDFlX2V4
dF9jZXJ0X3BvbGljeV9kaXNhbGxvd2VkX2FueV9wb2xpY3lfcXVhbGlmaWVyMQ0w
CwYDVQQKDARMaW50MQswCQYDVQQGEwJERTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMbupP+v+dNXeKE1ublu8Wf9PtOx5RMlWjTraHzqrjIB4ZgVFTLD
A3Xl1i5WLQM0KCXgd7jbGkfZ/7HUMWrSjqtkOg6j6FNAT/9VMh1ZptsJIKrD7lfK
kI3eJiz1s7NF1jKBGEZErR74kqPts6/lcoA9C8j8+qHmIBbXGHBLSsFfpzuqJnU2
ehNimC6PGFzA54hANgNEkamAPGrdNrFT/xvYipfvBgTgzotTTiRdiZ51sTF1v7Mm
umsIcEm4uHYsJwfnpuXurN72KGu4eA6wUxLCDteyt+bC6B0ssW6sGaOIFGc+e2cE
NOWNkCMGY+DDa7DhxnVUfUdHySYUBxrhb8UCAwEAAaN/MH0wHwYDVR0jBBgwFoAU
xI/P/odJknFwTpO8wTQh7qCTZYQwHQYDVR0OBBYEFCK/CGfxtPJTd2O1Ojl0o4DB
L8PRMDsGA1UdIAEB/wQxMC8wLQYEVR0gADAlMCMGCCsGAQUFBwIBFhdodHRwczov
L2V4YW1wbGUuY29tL2NwczANBgkqhkiG9w0BAQsFAAOCAQEAPji45mhfgZWP3l/c
moKLk3htFrp+3VdynpFyIQewIjqDaLmyJu1bm7m1rEmKTI1vMswk57iZK7lHaEpV
mnp03gamLVhXAIl2rOyZT0RpKCElMZs1nYJGv50OBf9Ypd/fGdLfT+LtDYXXfZjo
3YDY4cU8ghtpPoID/CvVhzfDsdwG846DQpC4HC2RRIyLWutc3HeG5zl7wjxAHxxe
rfC0LK1FgYKiNxfFBYDVnO74JOovkeKVMjig/Xc8rZdY/zu6Dv2nGgZhoGwCCCDf
Tp6r8JJiZQmDVD4XtKM6jCzEA01cp7+EDwo5YcU5XI2KJAsxhNF2KnQb2pv5E8me
X/DBNA==
-----END CERTIFICATE-----
80 changes: 80 additions & 0 deletions v3/testdata/withAnyPolicyAndNoPolicyQualifiers.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
87:51:1e:16:2e:f7:22:25:c8:a6:34:15
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = JLint Sub CA, O = Lint, C = DE
Validity
Not Before: Jul 1 14:48:19 2023 GMT
Not After : Jul 1 15:48:19 2024 GMT
Subject: CN = e_ext_cert_policy_disallowed_any_policy_qualifier, O = Lint, C = DE
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:a9:ab:6e:ba:1c:b8:e9:08:e2:30:06:3a:9a:16:
ee:07:a5:aa:24:27:f0:d2:67:aa:bd:82:98:53:8d:
c8:a2:82:47:ee:30:66:94:1e:ae:37:b9:81:0a:fe:
03:72:d8:00:2b:7b:1d:81:25:be:47:3d:2e:fc:9b:
64:19:eb:91:b6:a6:0e:a6:f1:60:ce:bd:e7:ff:78:
94:68:a4:96:25:df:4e:0e:c8:a5:c6:f8:15:6f:76:
34:16:ed:01:f5:c8:6e:9e:47:dd:24:c4:33:3f:d4:
d3:62:8c:51:83:d5:d1:aa:c0:ce:52:77:80:10:6d:
98:fc:41:8c:63:64:b9:81:56:f1:0b:a8:67:70:3d:
98:77:16:93:42:64:55:88:8b:39:89:32:60:91:4b:
eb:11:30:4d:49:91:fa:f5:0e:7a:b5:18:e8:45:cc:
37:b2:e3:4a:f5:8e:d1:4f:94:2e:89:5d:8c:1a:79:
d7:79:91:1c:c8:cd:fd:85:8e:c0:75:41:e0:25:a0:
fb:4e:5d:42:88:98:85:23:35:d0:39:56:2c:7f:37:
68:cf:ab:33:0f:63:98:11:77:64:74:16:bd:20:70:
e5:2d:17:ad:f7:84:4e:39:51:6b:ab:50:73:01:31:
04:54:b1:e7:02:3d:d0:1a:41:39:03:18:86:29:45:
ef:15
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
keyid:C4:8F:CF:FE:87:49:92:71:70:4E:93:BC:C1:34:21:EE:A0:93:65:84

X509v3 Subject Key Identifier:
A3:6D:DA:40:AC:DC:B7:A4:E2:3D:D1:5B:F3:C5:F3:65:BC:57:6B:85
X509v3 Certificate Policies: critical
Policy: X509v3 Any Policy

Signature Algorithm: sha256WithRSAEncryption
b0:84:c9:75:ab:d7:b7:c7:02:cb:eb:44:06:cd:ba:38:9a:9a:
1b:d5:fe:c5:77:65:69:38:54:26:ce:f1:d9:34:e4:2f:e8:11:
cb:89:15:2d:2d:4a:fd:5c:9f:11:93:10:d9:a6:4e:71:b6:61:
c8:41:f9:91:15:70:50:af:c6:6d:5b:ed:53:ba:a6:86:1a:68:
d9:24:2a:45:da:cd:8f:bb:55:61:68:6f:1b:39:07:8d:be:5b:
df:5e:41:a1:59:95:0b:ea:e4:b5:08:67:4b:4e:36:d8:67:78:
12:08:a4:a3:49:42:1f:98:c6:5f:7c:9c:49:39:ee:4d:ef:f0:
44:de:fc:b7:92:c1:9d:30:25:c9:58:fe:11:4a:2e:8e:99:88:
24:1c:bd:72:a0:55:22:bc:d2:1c:c3:5e:3b:d2:94:00:49:4e:
e6:ba:80:6d:19:2a:e4:32:d1:08:1d:49:cd:80:3c:48:76:9c:
30:ff:1b:c5:5d:53:0b:4c:b1:70:0a:1b:02:9e:71:66:9f:61:
76:73:d7:a1:13:53:3a:21:a4:ad:b1:e5:7e:9f:46:de:58:9b:
59:83:33:85:00:2d:87:08:a6:29:9c:b7:c9:01:10:d6:65:2b:
60:76:2c:d0:e0:7c:41:3c:8e:91:70:e5:93:0a:b3:eb:59:1e:
9a:f0:fa:b1
-----BEGIN CERTIFICATE-----
MIIDajCCAlKgAwIBAgINAIdRHhYu9yIlyKY0FTANBgkqhkiG9w0BAQsFADAzMRUw
EwYDVQQDDAxKTGludCBTdWIgQ0ExDTALBgNVBAoMBExpbnQxCzAJBgNVBAYTAkRF
MB4XDTIzMDcwMTE0NDgxOVoXDTI0MDcwMTE1NDgxOVowWDE6MDgGA1UEAwwxZV9l
eHRfY2VydF9wb2xpY3lfZGlzYWxsb3dlZF9hbnlfcG9saWN5X3F1YWxpZmllcjEN
MAsGA1UECgwETGludDELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCpq266HLjpCOIwBjqaFu4HpaokJ/DSZ6q9gphTjciigkfuMGaU
Hq43uYEK/gNy2AArex2BJb5HPS78m2QZ65G2pg6m8WDOvef/eJRopJYl304OyKXG
+BVvdjQW7QH1yG6eR90kxDM/1NNijFGD1dGqwM5Sd4AQbZj8QYxjZLmBVvELqGdw
PZh3FpNCZFWIizmJMmCRS+sRME1Jkfr1Dnq1GOhFzDey40r1jtFPlC6JXYwaedd5
kRzIzf2FjsB1QeAloPtOXUKImIUjNdA5Vix/N2jPqzMPY5gRd2R0Fr0gcOUtF633
hE45UWurUHMBMQRUsecCPdAaQTkDGIYpRe8VAgMBAAGjWDBWMB8GA1UdIwQYMBaA
FMSPz/6HSZJxcE6TvME0Ie6gk2WEMB0GA1UdDgQWBBSjbdpArNy3pOI90VvzxfNl
vFdrhTAUBgNVHSABAf8ECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQELBQADggEBALCE
yXWr17fHAsvrRAbNujiamhvV/sV3ZWk4VCbO8dk05C/oEcuJFS0tSv1cnxGTENmm
TnG2YchB+ZEVcFCvxm1b7VO6poYaaNkkKkXazY+7VWFobxs5B42+W99eQaFZlQvq
5LUIZ0tONthneBIIpKNJQh+Yxl98nEk57k3v8ETe/LeSwZ0wJclY/hFKLo6ZiCQc
vXKgVSK80hzDXjvSlABJTua6gG0ZKuQy0QgdSc2APEh2nDD/G8VdUwtMsXAKGwKe
cWafYXZz16ETUzohpK2x5X6fRt5Ym1mDM4UALYcIpimct8kBENZlK2B2LNDgfEE8
jpFw5ZMKs+tZHprw+rE=
-----END CERTIFICATE-----
Loading

0 comments on commit 4d38bfe

Please sign in to comment.