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

ceremony: Add support for CRL onlyContainsCACerts #7064

Merged
merged 38 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1964e28
Add ceremony tool support for CRL OnlyContainsCACerts
pgporada Aug 31, 2023
c675da7
Update CP/CPS IDP lint for onlyContainsCACerts
pgporada Sep 5, 2023
c16b6d6
Add issuing-distribution-point to ceremony crl-profile
pgporada Sep 5, 2023
4ceeeca
Fix ceremony unit tests
pgporada Sep 5, 2023
697c121
Remove issuing distribution point input from ceremony tool, split CRL…
pgporada Sep 7, 2023
427929e
Lint accordingly
pgporada Sep 12, 2023
6d351cc
Fix lint tests
pgporada Sep 14, 2023
cf4752b
Remove CRL linter carve outs
pgporada Sep 14, 2023
6179a45
Fix ST1023
pgporada Sep 14, 2023
0bc38b6
Ceremony checks that each certificate in the CRL config is a CA
pgporada Sep 14, 2023
1209d5a
Update linter/lints/cabf_br/lint_crl_validity_period.go
pgporada Sep 25, 2023
be35483
Update linter/lints/cpcps/lint_crl_has_idp.go
pgporada Sep 25, 2023
5be0c4e
Update linter/lints/cabf_br/lint_crl_validity_period.go
pgporada Sep 25, 2023
a4e3c42
Merge branch 'main' into crl-only-contains-ca-certs-flag
pgporada Sep 25, 2023
b8c5f81
gofmt
pgporada Sep 25, 2023
c0d2fa8
Address comments
pgporada Sep 25, 2023
d057de1
Update cmd/ceremony/crl_test.go
pgporada Sep 26, 2023
99455e8
Update linter/lints/cabf_br/lint_crl_validity_period.go
pgporada Sep 26, 2023
063688a
Update linter/lints/cabf_br/lint_crl_validity_period.go
pgporada Sep 26, 2023
61883ce
Address comments
pgporada Sep 26, 2023
3ff8964
Add ReadASN1BooleanWithTag
pgporada Sep 26, 2023
b3f524e
Address comments
pgporada Sep 27, 2023
7830309
Address comments and create ReadOptionalASN1BooleanWithTag
pgporada Sep 27, 2023
9b7db95
Progress
pgporada Sep 27, 2023
3d65f26
Functional ReadOptionalASN1BooleanWithTag hooray
pgporada Sep 28, 2023
f418975
Refactored lint_crl_validity_period and lint_crl_has_idp
pgporada Sep 28, 2023
22fb89e
Refactor and add more tests
pgporada Sep 29, 2023
5fcf352
Shuffle comments around
pgporada Sep 29, 2023
f36ea0c
Simplify TestReadOptionalASN1BooleanWithTag and run it in parallel
pgporada Sep 29, 2023
ec59518
Don't run gosimple on one specific test line
pgporada Sep 29, 2023
25d51d3
Fix lint error by not having it possible to have the lint error :)
pgporada Oct 2, 2023
97a23c0
Update comment in ReadOptionalASN1BooleanWithTag
pgporada Oct 2, 2023
5f5772f
Attempt parsing distributionPoint prior to final checks, not during
pgporada Oct 2, 2023
847249e
Update linter/lints/cabf_br/lint_crl_validity_period.go
pgporada Oct 2, 2023
9de122d
Update linter/lints/cabf_br/lint_crl_validity_period.go
pgporada Oct 2, 2023
0b6be37
Update linter/lints/cabf_br/lint_crl_validity_period.go
pgporada Oct 2, 2023
e53ac4c
Remove duplicate file and related test
pgporada Oct 2, 2023
395de69
https not httsp silly
pgporada Oct 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/ceremony/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ ceremony --config path/to/config.yml
* `crl-signer` - creates a delegated CRL signing certificate and signs it using a signing key already on a HSM, outputting a PEM certificate
* `key` - generates a signing key on HSM, outputting a PEM public key
* `ocsp-response` - creates a OCSP response for the provided certificate and signs it using a signing key already on a HSM, outputting a base64 encoded response
* `crl` - creates a CRL from the provided profile and signs it using a signing key already on a HSM, outputting a PEM CRL
* `crl` - creates a CRL with the IDP extension and `onlyContainsCACerts = true` from the provided profile and signs it using a signing key already on a HSM, outputting a PEM CRL

These modes are set in the `ceremony-type` field of the configuration file.

Expand Down Expand Up @@ -403,7 +403,7 @@ crl-profile:
revocation-date: 2019-12-31 12:00:00
```

This config generates a CRL signed by a key in the HSM, identified by the object label `root signing key` and object ID `ffff`. The CRL will have the number `80` and will contain revocation information for the certificate `/home/user/revoked-cert.pem`
This config generates a CRL that must only contain subordinate CA certificates signed by a key in the HSM, identified by the object label `root signing key` and object ID `ffff`. The CRL will have the number `80` and will contain revocation information for the certificate `/home/user/revoked-cert.pem`. Each of the revoked certificates provided are checked to ensure they have the `IsCA` flag set to `true`.

### Certificate profile format

Expand Down
46 changes: 36 additions & 10 deletions cmd/ceremony/crl.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
Expand Down Expand Up @@ -34,17 +36,14 @@ func generateCRL(signer crypto.Signer, issuer *x509.Certificate, thisUpdate, nex
if nextUpdate.Sub(thisUpdate) > time.Hour*24*365 {
return nil, errors.New("nextUpdate must be less than 12 months after thisUpdate")
}
// Add the Issuing Distribution Point extension.
idp, err := makeIDPExt()
if err != nil {
return nil, fmt.Errorf("creating IDP extension: %w", err)
}
template.ExtraExtensions = append(template.ExtraExtensions, *idp)

err := linter.CheckCRL(template, issuer, signer, []string{
// We skip this lint because our ceremony tooling issues CRLs with validity
// periods up to 12 months, but the lint only allows up to 10 days (which
// is the limit for CRLs containing Subscriber Certificates).
"e_crl_validity_period",
// We skip this lint because it is only applicable for sharded/partitioned
// CRLs, which our Subscriber CRLs are, but our higher-level CRLs issued by
// this tool are not.
"e_crl_has_idp",
})
err = linter.CheckCRL(template, issuer, signer, []string{})
if err != nil {
return nil, fmt.Errorf("crl failed pre-issuance lint: %w", err)
}
Expand All @@ -61,3 +60,30 @@ func generateCRL(signer crypto.Signer, issuer *x509.Certificate, thisUpdate, nex

return pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: crlBytes}), nil
}

// issuingDistributionPoint represents the ASN.1 IssuingDistributionPoint
// SEQUENCE as defined in RFC 5280 Section 5.2.5. We only use one of the fields,
// all others are omitted.
// https://datatracker.ietf.org/doc/html/rfc5280#page-66
type issuingDistributionPoint struct {
OnlyContainsCACerts bool `asn1:"optional,tag:2"`
}

// makeIDPExt returns a critical IssuingDistributionPoint extension enabling the
// OnlyContainsCACerts boolean.
func makeIDPExt() (*pkix.Extension, error) {
val := issuingDistributionPoint{
OnlyContainsCACerts: true,
}

valBytes, err := asn1.Marshal(val)
if err != nil {
return nil, err
}

return &pkix.Extension{
Id: asn1.ObjectIdentifier{2, 5, 29, 28}, // id-ce-issuingDistributionPoint
Value: valBytes,
Critical: true,
}, nil
}
12 changes: 3 additions & 9 deletions cmd/ceremony/crl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,8 @@ func TestGenerateCRLLints(t *testing.T) {
cert, err = x509.ParseCertificate(certBytes)
test.AssertNotError(t, err, "failed to parse test cert")

// This CRL should fail the following lints:
// - e_crl_has_idp (because our ceremony CRLs don't have the IDP extension)
// - e_crl_validity_period (because our ceremony CRLs are valid for a long time)
// This CRL should fail the following lint:
// - e_crl_acceptable_reason_codes (because 6 is forbidden)
// However, only the last of those should show up in the error message,
// because the first two should be explicitly removed from the lint registry
// by the ceremony tool.
_, err = generateCRL(&wrappedSigner{k}, cert, time.Now().Add(time.Hour), time.Now().Add(100*24*time.Hour), 1, []x509.RevocationListEntry{
{
SerialNumber: big.NewInt(12345),
Expand All @@ -94,8 +89,6 @@ func TestGenerateCRLLints(t *testing.T) {
},
})
test.AssertError(t, err, "generateCRL did not fail")
test.AssertNotContains(t, err.Error(), "e_crl_has_idp")
test.AssertNotContains(t, err.Error(), "e_crl_validity_period")
test.AssertContains(t, err.Error(), "e_crl_acceptable_reason_codes")
}

Expand Down Expand Up @@ -138,8 +131,9 @@ func TestGenerateCRL(t *testing.T) {
_, err = asn1.Unmarshal(crlDER, &crl)
test.AssertNotError(t, err, "failed to parse CRL")
test.AssertEquals(t, crl.TBS.Version, 1) // x509v2 == 1
test.AssertEquals(t, len(crl.TBS.Extensions), 2) // AKID, CRL number
test.AssertEquals(t, len(crl.TBS.Extensions), 3) // AKID, CRL number, IssuingDistributionPoint
test.Assert(t, crl.TBS.Extensions[1].Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 20}), "unexpected OID in extension")
test.Assert(t, crl.TBS.Extensions[2].Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 28}), "unexpected OID in extension")
var number int
_, err = asn1.Unmarshal(crl.TBS.Extensions[1].Value, &number)
test.AssertNotError(t, err, "failed to parse CRL number extension")
Expand Down
3 changes: 3 additions & 0 deletions cmd/ceremony/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,9 @@ func crlCeremony(configBytes []byte) error {
if err != nil {
return fmt.Errorf("failed to load revoked certificate %q: %s", rc.CertificatePath, err)
}
if !cert.IsCA {
return fmt.Errorf("certificate with serial %d is not a CA certificate", cert.SerialNumber)
}
revokedAt, err := time.Parse(time.DateTime, rc.RevocationDate)
if err != nil {
return fmt.Errorf("unable to parse crl-profile.revoked-certificates.revocation-date")
Expand Down
78 changes: 67 additions & 11 deletions linter/lints/cabf_br/lint_crl_validity_period.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
package cabfbr

import (
"fmt"
"time"

"github.com/letsencrypt/boulder/linter/lints"
"github.com/zmap/zcrypto/encoding/asn1"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
"golang.org/x/crypto/cryptobyte"

cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
)

type crlValidityPeriod struct{}

/************************************************
Baseline Requirements, Section 4.9.7:
For the status of Subscriber Certificates [...] the value of the nextUpdate
field MUST NOT be more than ten days beyond the value of the thisUpdate field.

Although the validity period for CRLs covering the status of Subordinate CA
certificates is longer (up to 12 months), Boulder does not produce such CRLs,
so this lint only covers the Subscriber Certificate case.
* For the status of Subscriber Certificates [...] the value of the nextUpdate
field MUST NOT be more than ten days beyond the value of the thisUpdate field.
* For the status of Subordinate CA Certificates [...]. The value of the
nextUpdate field MUST NOT be more than twelve months beyond the value of the
thisUpdatefield.
************************************************/

func init() {
lint.RegisterRevocationListLint(&lint.RevocationListLint{
LintMetadata: lint.LintMetadata{
Name: "e_crl_validity_period",
Description: "CRLs must have an acceptable validity period",
Description: "Let's Encrypt CRLs must have an acceptable validity period",
Citation: "BRs: 4.9.7",
Source: lint.CABFBaselineRequirements,
EffectiveDate: util.CABFBRs_1_2_1_Date,
Expand All @@ -42,17 +47,68 @@ func (l *crlValidityPeriod) CheckApplies(c *x509.RevocationList) bool {
}

func (l *crlValidityPeriod) Execute(c *x509.RevocationList) *lint.LintResult {
validity := c.NextUpdate.Sub(c.ThisUpdate)
if validity <= 0 {
/*
Let's Encrypt issues two kinds of CRLs:

1) CRLs containing subscriber certificates, created by crl-updater.
These assert the distributionPoint and onlyContainsUserCerts
boolean.
2) CRLs containing issuer CRLs, created by the ceremony tool. These
assert the onlyContainsCACerts boolean.

We use the presence of these booleans to determine which BR-mandated
lifetime to enforce.
*/

// The only way to determine which type of CRL we're dealing with. The
// issuingDistributionPoint must be parsed and the internal fields
// inspected.
idpOID := asn1.ObjectIdentifier{2, 5, 29, 28} // id-ce-issuingDistributionPoint
idpe := lints.GetExtWithOID(c.Extensions, idpOID)
if idpe == nil {
return &lint.LintResult{
Status: lint.Warn,
Details: "CRL missing IDP",
}
}

// Step inside the outer issuingDistributionPoint sequence to get access to
// its constituent fields.
idpv := cryptobyte.String(idpe.Value)
if !idpv.ReadASN1(&idpv, cryptobyte_asn1.SEQUENCE) {
return &lint.LintResult{
Status: lint.Warn,
Details: "Failed to read IDP distributionPoint",
}
}

pgporada marked this conversation as resolved.
Show resolved Hide resolved
// Throw distributionPoint away.
distributionPointTag := cryptobyte_asn1.Tag(0).ContextSpecific().Constructed()
_ = idpv.SkipOptionalASN1(distributionPointTag)

// Default to subscriber cert CRL.
var BRValidity = 10 * 24 * time.Hour
var validityString = "10 days"

onlyContainsCACertsTag := cryptobyte_asn1.Tag(2).ContextSpecific()
occcPresent := false
if lints.ReadOptionalASN1BooleanWithTag(&idpv, &occcPresent, onlyContainsCACertsTag, false) && occcPresent {
BRValidity = 365 * lints.BRDay
validityString = "365 days"
}

parsedValidity := c.NextUpdate.Sub(c.ThisUpdate)
if parsedValidity <= 0 {
return &lint.LintResult{
Status: lint.Error,
Details: "CRL has NextUpdate at or before ThisUpdate",
}
}
if validity > 10*24*time.Hour {

if parsedValidity > BRValidity {
return &lint.LintResult{
Status: lint.Error,
Details: "CRL has validity period greater than ten days",
Details: fmt.Sprintf("CRL has validity period greater than %s", validityString),
}
}
return &lint.LintResult{Status: lint.Pass}
Expand Down
33 changes: 30 additions & 3 deletions linter/lints/cabf_br/lint_crl_validity_period_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,45 @@ func TestCrlValidityPeriod(t *testing.T) {
wantSubStr string
}{
{
name: "good",
name: "good", // CRL for subscriber certs
want: lint.Pass,
},
{
name: "good_subordinate_ca",
want: lint.Pass,
},
{
name: "negative_validity",
want: lint.Warn,
wantSubStr: "CRL missing IDP",
},
{
name: "negative_validity_subscriber_cert",
want: lint.Error,
wantSubStr: "at or before",
},
{
name: "long_validity",
name: "negative_validity_subordinate_ca",
want: lint.Error,
wantSubStr: "greater than ten days",
wantSubStr: "at or before",
},
{
name: "long_validity_subscriber_cert", // 10 days + 1 second
want: lint.Error,
wantSubStr: "CRL has validity period greater than 10 days",
},
{
name: "long_validity_subordinate_ca", // 1 year + 1 second
want: lint.Error,
wantSubStr: "CRL has validity period greater than 365 days",
},
{
// Technically this CRL is incorrect because Let's Encrypt does not
// (yet) issue CRLs containing both the distributionPoint and
// optional onlyContainsCACerts boolean, but we're still parsing the
// correct BR validity in this lint.
name: "long_validity_distributionPoint_and_subordinate_ca",
want: lint.Pass,
},
}

Expand Down
10 changes: 10 additions & 0 deletions linter/lints/cabf_br/testdata/crl_good_subordinate_ca.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBZDCB7AIBATAKBggqhkjOPQQDAzBJMQswCQYDVQQGEwJYWDEVMBMGA1UEChMM
Qm91bGRlciBUZXN0MSMwIQYDVQQDExooVEVTVCkgRWxlZ2FudCBFbGVwaGFudCBF
MRcNMjIxMDEwMjAxMjA3WhcNMjIxMDE5MjAxMjA2WjApMCcCCAOuUdtRFVo8Fw0y
MjEwMTAxOTEyMDdaMAwwCgYDVR0VBAMKAQGgRzBFMB8GA1UdIwQYMBaAFAHau3rL
JSCOXnnW+ZZCLwJBKQe+MBEGA1UdFAQKAggXHM495IK6YTAPBgNVHRwBAf8EBTAD
ggH/MAoGCCqGSM49BAMDA2cAMGQCMC8OQhSdNhq8nqHzrTowPIWHa7D9wX45Wczi
wTydR0bLRdiDSEZ9tHgxj6RHFFBrIgIwV5A+lykivTOBek/qVRdTStwtK9q25p5B
JWvbicaNns/LS9z3jDSfuJ1nzCN7n78z
-----END X509 CRL-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN X509 CRL-----
MIIBmDCCAR8CAQEwCgYIKoZIzj0EAwMwSTELMAkGA1UEBhMCWFgxFTATBgNVBAoT
DEJvdWxkZXIgVGVzdDEjMCEGA1UEAxMaKFRFU1QpIEVsZWdhbnQgRWxlcGhhbnQg
RTEXDTIyMTAxMDIwMTIwN1oXDTIyMTAxOTIwMTIwNlowKTAnAggDrlHbURVaPBcN
MjIxMDEwMTkxMjA3WjAMMAoGA1UdFQQDCgEBoHoweDAfBgNVHSMEGDAWgBQB2rt6
yyUgjl551vmWQi8CQSkHvjARBgNVHRQECgIIFxzOPeSCumEwQgYDVR0cAQH/BDgw
NqAxoC+GLWh0dHA6Ly9jLmJvdWxkZXIudGVzdC82NjI4Mzc1NjkxMzU4ODI4OC8w
LmNybIIB/zAKBggqhkjOPQQDAwNnADBkAjAvDkIUnTYavJ6h8606MDyFh2uw/cF+
OVnM4sE8nUdGy0XYg0hGfbR4MY+kRxRQayICMFeQPpcpIr0zgXpP6lUXU0rcLSva
tuaeQSVr24nGjZ7Py0vc94w0n7idZ8wje5+/Mw==
-----END X509 CRL-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBZDCB7AIBATAKBggqhkjOPQQDAzBJMQswCQYDVQQGEwJYWDEVMBMGA1UEChMM
Qm91bGRlciBUZXN0MSMwIQYDVQQDExooVEVTVCkgRWxlZ2FudCBFbGVwaGFudCBF
MRcNMjIwNzA2MTY0MzM4WhcNMjMwNzE2MTY0MzM5WjApMCcCCAOuUdtRFVo8Fw0y
MjA3MDYxNTQzMzhaMAwwCgYDVR0VBAMKAQGgRzBFMB8GA1UdIwQYMBaAFAHau3rL
JSCOXnnW+ZZCLwJBKQe+MBEGA1UdFAQKAggW/0sm37IYDzAPBgNVHRwBAf8EBTAD
ggH/MAoGCCqGSM49BAMDA2cAMGQCMFayE0WLrRoxaXzYbdPAi7AEEr53OIulDND4
vPlN0/A0RyJiIrgfXEPqsVCweqSoQQIwW7hgsE6Ke7wnxjuxc+jdK7iEyJxbbegQ
0eYs1lDH112u5l4UkOooPYThzlkcUdNC
-----END X509 CRL-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN X509 CRL-----
MIIBmDCCAR8CAQEwCgYIKoZIzj0EAwMwSTELMAkGA1UEBhMCWFgxFTATBgNVBAoT
DEJvdWxkZXIgVGVzdDEjMCEGA1UEAxMaKFRFU1QpIEVsZWdhbnQgRWxlcGhhbnQg
RTEXDTIyMDcwNjE2NDMzOFoXDTIyMDcxNjE2NDMzOVowKTAnAggDrlHbURVaPBcN
MjIwNzA2MTU0MzM4WjAMMAoGA1UdFQQDCgEBoHoweDAfBgNVHSMEGDAWgBQB2rt6
yyUgjl551vmWQi8CQSkHvjARBgNVHRQECgIIFv9LJt+yGA8wQgYDVR0cAQH/BDgw
NqAxoC+GLWh0dHA6Ly9jLmJvdWxkZXIudGVzdC82NjI4Mzc1NjkxMzU4ODI4OC8w
LmNybIEB/zAKBggqhkjOPQQDAwNnADBkAjBWshNFi60aMWl82G3TwIuwBBK+dziL
pQzQ+Lz5TdPwNEciYiK4H1xD6rFQsHqkqEECMFu4YLBOinu8J8Y7sXPo3Su4hMic
W23oENHmLNZQx9ddruZeFJDqKD2E4c5ZHFHTQg==
-----END X509 CRL-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBZDCB7AIBATAKBggqhkjOPQQDAzBJMQswCQYDVQQGEwJYWDEVMBMGA1UEChMM
Qm91bGRlciBUZXN0MSMwIQYDVQQDExooVEVTVCkgRWxlZ2FudCBFbGVwaGFudCBF
MRcNMjIwNzA2MTY0MzM4WhcNMjIwNzA2MTY0MzM3WjApMCcCCAOuUdtRFVo8Fw0y
MjA3MDYxNTQzMzhaMAwwCgYDVR0VBAMKAQGgRzBFMB8GA1UdIwQYMBaAFAHau3rL
JSCOXnnW+ZZCLwJBKQe+MBEGA1UdFAQKAggW/0sm37IYDzAPBgNVHRwBAf8EBTAD
ggH/MAoGCCqGSM49BAMDA2cAMGQCMFayE0WLrRoxaXzYbdPAi7AEEr53OIulDND4
vPlN0/A0RyJiIrgfXEPqsVCweqSoQQIwW7hgsE6Ke7wnxjuxc+jdK7iEyJxbbegQ
0eYs1lDH112u5l4UkOooPYThzlkcUdNC
-----END X509 CRL-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN X509 CRL-----
MIIBmDCCAR8CAQEwCgYIKoZIzj0EAwMwSTELMAkGA1UEBhMCWFgxFTATBgNVBAoT
DEJvdWxkZXIgVGVzdDEjMCEGA1UEAxMaKFRFU1QpIEVsZWdhbnQgRWxlcGhhbnQg
RTEXDTIyMDcwNjE2NDMzOFoXDTIyMDcwNjE2NDMzN1owKTAnAggDrlHbURVaPBcN
MjIwNzA2MTU0MzM4WjAMMAoGA1UdFQQDCgEBoHoweDAfBgNVHSMEGDAWgBQB2rt6
yyUgjl551vmWQi8CQSkHvjARBgNVHRQECgIIFv9LJt+yGA8wQgYDVR0cAQH/BDgw
NqAxoC+GLWh0dHA6Ly9jLmJvdWxkZXIudGVzdC82NjI4Mzc1NjkxMzU4ODI4OC8w
LmNybIEB/zAKBggqhkjOPQQDAwNnADBkAjBWshNFi60aMWl82G3TwIuwBBK+dziL
pQzQ+Lz5TdPwNEciYiK4H1xD6rFQsHqkqEECMFu4YLBOinu8J8Y7sXPo3Su4hMic
W23oENHmLNZQx9ddruZeFJDqKD2E4c5ZHFHTQg==
-----END X509 CRL-----
Loading
Loading