Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lints: enforce Mozilla PKI policy RSASSA-PSS encoding requirements #377

Merged
merged 15 commits into from
Mar 12, 2020
Merged
101 changes: 101 additions & 0 deletions v2/lints/mozilla/lint_mp_pss_parameters_encoding_correct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package mozilla

/*
* ZLint Copyright 2020 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.
*/

/************************************************

https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/policy/

Section 5.1.1 RSA

RSASSA-PSS with SHA-256, MGF-1 with SHA-256, and a salt length of 32 bytes.

The encoded AlgorithmIdentifier MUST match the following hex-encoded bytes:

304106092a864886f70d01010a3034a00f300d0609608648016503040201
0500a11c301a06092a864886f70d010108300d0609608648016503040201
0500a203020120

RSASSA-PSS with SHA-384, MGF-1 with SHA-384, and a salt length of 48 bytes.

The encoded AlgorithmIdentifier MUST match the following hex-encoded bytes:

304106092a864886f70d01010a3034a00f300d0609608648016503040202
0500a11c301a06092a864886f70d010108300d0609608648016503040202
0500a203020130

RSASSA-PSS with SHA-512, MGF-1 with SHA-512, and a salt length of 64 bytes.

The encoded AlgorithmIdentifier MUST match the following hex-encoded bytes:

304106092a864886f70d01010a3034a00f300d0609608648016503040203
0500a11c301a06092a864886f70d010108300d0609608648016503040203
0500a203020140
************************************************/

import (
"bytes"
"encoding/hex"
"fmt"

"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v2/lint"
"github.com/zmap/zlint/v2/util"
)

type rsaPssAidEncoding struct{}

var RSASSAPSSAlgorithmIDToDER = [3][]byte{
// RSASSA-PSS with SHA-256, MGF-1 with SHA-256, salt length 32 bytes
{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20},
// RSASSA-PSS with SHA-384, MGF-1 with SHA-384, salt length 48 bytes
{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x30},
// RSASSA-PSS with SHA-512, MGF-1 with SHA-512, salt length 64 bytes
{0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x40},
}

func (l *rsaPssAidEncoding) Initialize() error {
return nil
}

func (l *rsaPssAidEncoding) CheckApplies(c *x509.Certificate) bool {
return c.SignatureAlgorithmOID.Equal(util.OidRSASSAPSS)
sleevi marked this conversation as resolved.
Show resolved Hide resolved
}

func (l *rsaPssAidEncoding) Execute(c *x509.Certificate) *lint.LintResult {
signatureAlgoID, err := util.GetSignatureAlgorithmInTBSEncoded(c)
if err != nil {
return &lint.LintResult{Status: lint.Error, Details: "error reading signatureAlgorithm from TBS"}
}

for _, encoding := range RSASSAPSSAlgorithmIDToDER {
if bytes.Equal(signatureAlgoID, encoding) {
return &lint.LintResult{Status: lint.Pass}
}
}

return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("RSASSA-PSS parameters are not properly encoded. %v presentations are allowed but got the unsupported %s", len(RSASSAPSSAlgorithmIDToDER), hex.EncodeToString(signatureAlgoID))}
}

func init() {
lint.RegisterLint(&lint.Lint{
Name: "e_mp_rsassa-pss_parameters_encoding_in_signature_algorithm_correct",
Description: "The encoded AlgorithmIdentifier for RSASSA-PSS in the signature algorithm MUST match specific bytes",
Citation: "Mozilla Root Store Policy / Section 5.1.1",
Source: lint.MozillaRootStorePolicy,
EffectiveDate: util.MozillaPolicy27Date,
Lint: &rsaPssAidEncoding{},
})
}
70 changes: 70 additions & 0 deletions v2/lints/mozilla/lint_mp_pss_parameters_encoding_correct_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package mozilla

/*
* ZLint Copyright 2020 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/v2/lint"
"github.com/zmap/zlint/v2/test"
)

func TestPssAidEncoding(t *testing.T) {
testCases := []struct {
Name string
InputFilename string
ExpectedResult lint.LintStatus
}{
{
Name: "Standard RSASSA-PSS with SHA256",
InputFilename: "rsassapssWithSHA256.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Standard RSASSA-PSS with SHA256 but the hash parameters are empty instead of NULL",
InputFilename: "rsassapssWithSHA256EmptyHashParams.pem",
ExpectedResult: lint.Error,
},
{
Name: "Standard RSASSA-PSS with SHA384",
InputFilename: "rsassapssWithSHA384.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Standard RSASSA-PSS with SHA384 but the hash parameters are empty instead of NULL",
InputFilename: "rsassapssWithSHA384EmptyHashParams.pem",
ExpectedResult: lint.Error,
},
{
Name: "Standard RSASSA-PSS with SHA512",
InputFilename: "rsassapssWithSHA512.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Standard RSASSA-PSS with SHA512 but the hash parameters are empty instead of NULL",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: These variations all only exercise a single mis-encoding (empty hash parameters). Would it be useful to include explicit hash parameters that are more esoteric, such as a different mask length?

I'm not sure if I'm underestimating the difficulty, so I thought I'd check here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test case for irregular (maybe invalid or unsupported is a better name for this?) salt length. Other test cases (e.g. irregular trailerField) are indeed more work to implement also because the crypto libraries do not support it out-of-the-box.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtgag Thanks for adding another test case. I think the coverage in-branch is sufficient to see the lint merged. It would be nice to have more malformed test cases but it sounds like the difficulty would mean holding up the PR longer and I'd prefer to see it merged and iterated on as time allows.

Since @sleevi gave this branch a +1 without requesting the test case change as blocking feedback I'm going to go ahead and merge. @mtgag If you think you'd be up to adding the more difficult test cases would you mind filing an issue to act as a marker/discussion point?

Thanks all!

InputFilename: "rsassapssWithSHA512EmptyHashParams.pem",
ExpectedResult: lint.Error,
},
}

for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
result := test.TestLint("e_mp_rsassa-pss_parameters_encoding_in_signature_algorithm_correct", tc.InputFilename)
if result.Status != tc.ExpectedResult {
t.Errorf("expected result %v was %v", tc.ExpectedResult, result.Status)
}
})
}
}
78 changes: 78 additions & 0 deletions v2/testdata/rsassapssWithSHA256EmptyHashParams.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 256 (0x100)
Signature Algorithm: rsassaPss
Hash Algorithm: sha256
Mask Algorithm: mgf1 with sha256
Salt Length: 0x20
Trailer Field: 0xBC (default)
Issuer: CN = Lint CA, OU = Test, O = MTG, C = DE
Validity
Not Before: Jan 2 09:00:00 2020 GMT
Not After : Jan 2 09:00:00 2022 GMT
Subject: CN = PSS Certificate, C = DE
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:83:49:7e:49:11:77:25:1d:f2:85:20:5b:d4:86:
6a:46:c3:94:0e:4e:16:b4:1f:87:75:85:43:c6:a9:
da:32:02:84:64:c2:db:c6:0a:2b:52:da:59:70:0e:
0c:d9:38:e4:ff:27:8b:de:05:26:78:3d:c9:c4:ca:
3e:d9:95:d1:bc:39:31:3d:a0:97:8e:88:44:1c:c8:
1d:83:6d:e1:55:3c:ff:67:11:be:47:41:38:52:f4:
b3:8b:f8:ae:05:fb:41:82:c2:6a:77:ff:87:98:ab:
e2:d6:6d:1a:88:81:7c:c7:26:df:a8:4f:10:c3:3d:
1e:fa:1c:1f:26:08:a0:64:95:ab:85:e6:8d:10:97:
ff:8f:b8:be:db:c5:07:51:64:83:3e:e3:00:96:97:
ec:53:6d:c1:da:2d:16:32:50:97:e9:c7:54:bf:f1:
78:bc:cc:9d:2c:82:34:60:0f:12:5a:e9:13:7e:44:
03:0d:10:5f:6a:c4:12:1a:da:33:54:4e:ad:4d:e6:
1c:be:3b:30:73:98:3e:54:43:96:80:16:2c:c6:51:
fc:dc:2b:18:29:38:85:a0:e6:f1:75:49:7e:72:bf:
60:ad:32:0c:46:20:07:e2:99:e8:bf:dd:19:7e:74:
0f:86:6f:d0:c7:fb:85:d2:8a:9f:c0:d7:6f:1b:f4:
8e:67
Exponent: 65537 (0x10001)
Signature Algorithm: rsassaPss
Hash Algorithm: sha256
Mask Algorithm: mgf1 with sha256
Salt Length: 0x20
Trailer Field: 0xBC (default)

2b:c1:9b:ee:ee:66:11:91:a8:d6:94:22:70:e7:04:4a:1e:38:
01:ae:89:d1:0d:ae:bd:9d:cb:53:d3:46:91:8a:55:a4:7c:db:
90:39:9e:14:09:25:b2:f0:a5:a8:e8:2b:07:0e:12:22:25:f8:
1b:a2:78:ae:b0:0b:6e:bb:66:22:a6:97:26:7c:4f:f4:f0:65:
2d:cb:c3:06:17:a5:25:09:e6:5c:6b:30:99:ab:68:3c:02:11:
a9:ae:6c:d2:ef:ed:56:bc:2b:2f:42:bb:9e:aa:c0:fc:c5:b4:
5c:61:ea:95:10:82:e9:3a:cd:ef:67:b8:33:25:28:fa:95:12:
70:4b:4e:80:b6:ef:e9:c9:72:df:89:1b:27:6a:45:a7:9c:b7:
de:cc:c8:89:88:9c:22:30:c2:63:ca:6c:fb:57:ad:25:6f:4a:
0f:a7:b4:d3:72:04:fc:05:56:31:f9:a8:8b:89:fe:16:f4:34:
ae:87:c4:48:e2:99:b1:1f:a0:9c:ef:ea:27:ac:32:7b:7f:72:
09:1d:a2:fc:d1:55:e9:42:ad:23:19:d6:1c:dd:ef:94:a8:d2:
9d:99:44:01:ac:bf:78:93:3c:82:a2:01:1f:f6:cf:91:83:10:
16:eb:bb:62:af:8c:e9:c2:1b:df:86:27:eb:20:fd:e0:89:6f:
c2:1a:ac:5d
-----BEGIN CERTIFICATE-----
MIIDPTCCAfWgAwIBAgICAQAwPQYJKoZIhvcNAQEKMDCgDTALBglghkgBZQMEAgGh
GjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCASAwPDEQMA4GA1UEAwwHTGlu
dCBDQTENMAsGA1UECwwEVGVzdDEMMAoGA1UECgwDTVRHMQswCQYDVQQGEwJERTAe
Fw0yMDAxMDIwOTAwMDBaFw0yMjAxMDIwOTAwMDBaMCcxGDAWBgNVBAMMD1BTUyBD
ZXJ0aWZpY2F0ZTELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCDSX5JEXclHfKFIFvUhmpGw5QOTha0H4d1hUPGqdoyAoRkwtvGCitS
2llwDgzZOOT/J4veBSZ4PcnEyj7ZldG8OTE9oJeOiEQcyB2DbeFVPP9nEb5HQThS
9LOL+K4F+0GCwmp3/4eYq+LWbRqIgXzHJt+oTxDDPR76HB8mCKBklauF5o0Ql/+P
uL7bxQdRZIM+4wCWl+xTbcHaLRYyUJfpx1S/8Xi8zJ0sgjRgDxJa6RN+RAMNEF9q
xBIa2jNUTq1N5hy+OzBzmD5UQ5aAFizGUfzcKxgpOIWg5vF1SX5yv2CtMgxGIAfi
mei/3Rl+dA+Gb9DH+4XSip/A128b9I5nAgMBAAEwPQYJKoZIhvcNAQEKMDCgDTAL
BglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCASADggEB
ACvBm+7uZhGRqNaUInDnBEoeOAGuidENrr2dy1PTRpGKVaR825A5nhQJJbLwpajo
KwcOEiIl+BuieK6wC267ZiKmlyZ8T/TwZS3LwwYXpSUJ5lxrMJmraDwCEamubNLv
7Va8Ky9Cu56qwPzFtFxh6pUQguk6ze9nuDMlKPqVEnBLToC27+nJct+JGydqRaec
t97MyImInCIwwmPKbPtXrSVvSg+ntNNyBPwFVjH5qIuJ/hb0NK6HxEjimbEfoJzv
6iesMnt/cgkdovzRVelCrSMZ1hzd75So0p2ZRAGsv3iTPIKiAR/2z5GDEBbru2Kv
jOnCG9+GJ+sg/eCJb8IarF0=
-----END CERTIFICATE-----
78 changes: 78 additions & 0 deletions v2/testdata/rsassapssWithSHA384.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 256 (0x100)
Signature Algorithm: rsassaPss
Hash Algorithm: sha384
Mask Algorithm: mgf1 with sha384
Salt Length: 0x30
Trailer Field: 0xBC (default)
Issuer: CN = Lint CA, OU = Test, O = MTG, C = DE
Validity
Not Before: Jan 2 09:00:00 2020 GMT
Not After : Jan 2 09:00:00 2022 GMT
Subject: CN = PSS Certificate, C = DE
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:ce:f5:1d:67:ca:9c:2b:20:43:69:5e:2c:97:46:
12:29:ff:dd:36:b1:c7:f0:1e:7c:5d:5a:95:a7:11:
0b:a4:86:69:37:8e:e8:da:08:40:79:0d:36:b8:2e:
fc:21:9b:c1:d9:83:57:47:ed:17:4d:d4:48:cd:dc:
d7:06:e9:a1:6e:41:d1:6c:f7:21:7e:98:a5:4b:ed:
00:54:f5:b6:b9:5d:2c:ed:36:2f:cf:97:33:59:f3:
a2:df:3c:a9:96:19:ec:71:dd:d6:6d:cf:9f:3f:05:
67:31:0c:ce:93:00:b6:e1:f0:56:71:4a:1b:65:87:
a7:e8:bb:15:20:1f:7d:df:ce:73:94:00:74:7f:cb:
54:c0:56:98:3e:c9:50:1a:99:a1:d6:55:ad:aa:df:
e6:89:b4:66:e2:e5:72:3e:a7:18:26:ed:2b:60:39:
bd:b4:b6:8b:3b:69:ef:cd:c9:99:c3:6b:86:9d:43:
ab:91:46:16:a1:44:6b:9c:a2:c9:d7:43:85:cb:6c:
9e:d8:aa:59:37:3d:11:b5:e4:c0:2e:ef:10:25:85:
30:4c:89:e6:be:6d:c2:22:db:b0:4a:9a:36:52:17:
9e:4c:85:4d:3a:53:10:3b:36:95:6f:6c:cb:c6:da:
d8:45:2d:6c:39:f0:e8:4b:e8:7c:b4:24:ec:5f:4f:
6b:cd
Exponent: 65537 (0x10001)
Signature Algorithm: rsassaPss
Hash Algorithm: sha384
Mask Algorithm: mgf1 with sha384
Salt Length: 0x30
Trailer Field: 0xBC (default)

5d:cb:23:99:5e:34:b0:f1:ab:06:17:e8:31:4f:a6:09:07:75:
e8:4d:ca:62:c9:5b:5c:08:ef:23:c2:56:4a:d0:c4:46:66:8f:
de:21:34:37:04:7f:5f:1b:e2:18:29:99:d2:1c:6c:05:da:82:
7e:21:7a:45:bf:9d:3c:c8:2e:fc:7a:f2:97:9c:8c:bd:62:88:
15:e6:f4:d8:67:1c:3b:f6:bc:a7:b8:cd:e0:a0:f5:a2:2f:2a:
14:ba:67:f9:e9:67:dc:91:c6:e8:ce:39:c5:1e:81:82:a2:85:
e8:01:a0:5d:96:96:10:cf:fb:f5:f1:2f:9e:7d:b8:14:c3:3c:
09:4e:9f:6f:f4:44:d0:3e:49:11:c7:50:21:bb:c9:ea:49:f8:
d5:ef:e0:23:f3:f6:c2:22:9f:29:9a:55:74:53:5f:4b:ab:0d:
4d:06:bc:be:64:1d:4a:4d:a2:e5:43:9e:6f:95:70:ed:ab:a7:
d8:9f:7c:85:4c:f7:6d:30:16:36:74:dc:6b:e3:9a:96:95:35:
2b:4b:94:50:7a:5b:89:a1:75:9e:2d:66:ad:0b:31:ce:fd:4e:
42:8d:f7:1b:da:24:7d:c7:34:1e:de:bf:fa:1d:3d:fb:36:34:
3e:99:29:0d:9d:c5:04:6d:d2:27:7b:11:d4:65:e6:f0:ab:a8:
8c:8c:31:9d
-----BEGIN CERTIFICATE-----
MIIDRTCCAfmgAwIBAgICAQAwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAgIF
AKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKIDAgEwMDwxEDAOBgNVBAMM
B0xpbnQgQ0ExDTALBgNVBAsMBFRlc3QxDDAKBgNVBAoMA01URzELMAkGA1UEBhMC
REUwHhcNMjAwMTAyMDkwMDAwWhcNMjIwMTAyMDkwMDAwWjAnMRgwFgYDVQQDDA9Q
U1MgQ2VydGlmaWNhdGUxCzAJBgNVBAYTAkRFMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAzvUdZ8qcKyBDaV4sl0YSKf/dNrHH8B58XVqVpxELpIZpN47o
2ghAeQ02uC78IZvB2YNXR+0XTdRIzdzXBumhbkHRbPchfpilS+0AVPW2uV0s7TYv
z5czWfOi3zyplhnscd3Wbc+fPwVnMQzOkwC24fBWcUobZYen6LsVIB99385zlAB0
f8tUwFaYPslQGpmh1lWtqt/mibRm4uVyPqcYJu0rYDm9tLaLO2nvzcmZw2uGnUOr
kUYWoURrnKLJ10OFy2ye2KpZNz0RteTALu8QJYUwTInmvm3CItuwSpo2UheeTIVN
OlMQOzaVb2zLxtrYRS1sOfDoS+h8tCTsX09rzQIDAQABMEEGCSqGSIb3DQEBCjA0
oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCi
AwIBMAOCAQEAXcsjmV40sPGrBhfoMU+mCQd16E3KYslbXAjvI8JWStDERmaP3iE0
NwR/XxviGCmZ0hxsBdqCfiF6Rb+dPMgu/Hryl5yMvWKIFeb02GccO/a8p7jN4KD1
oi8qFLpn+eln3JHG6M45xR6BgqKF6AGgXZaWEM/79fEvnn24FMM8CU6fb/RE0D5J
EcdQIbvJ6kn41e/gI/P2wiKfKZpVdFNfS6sNTQa8vmQdSk2i5UOeb5Vw7aun2J98
hUz3bTAWNnTca+OalpU1K0uUUHpbiaF1ni1mrQsxzv1OQo33G9okfcc0Ht6/+h09
+zY0PpkpDZ3FBG3SJ3sR1GXm8KuojIwxnQ==
-----END CERTIFICATE-----
Loading