Allow opt-out from client certificate root checking (#15029)

* Added a new property, ValidateCertificateChain

This aligns the code with README.md, and enables chained certificate authentication to work in Azure App Service (which won't have the client certificate's root in its trust store)

* Updated reference assemblies

New ValidateCertificateChain property was missing from /ref - hadn't updated it, so automatic test was failing

* Replaced the broad property which disables certificate chain validation with a granular list of trusted issuers

Also updated documentation to remove the unused property and to indicate that it isn't possible to validate half of the chain from the OS' trust store, and half from the private root store

* Swapped property access for an AddRange call

* Updated reference assembly

* Added certificate tests

Three test cases:
- Untrusted client certificate should fail, as it does now.
- Client certificate which is part of a chain with an untrusted root but a trusted issuer should fail, since the chain as a whole isn't trusted.
- Client certificate which is part of a fully-trusted chain should pass.

Also noticed that there were a couple of issues in CreateServer, where the certificate revocation flags weren't being set properly.

Finally, the csproj was referring to a folder name ("test\Certificates") which didn't exist. It looks like the folder was renamed to "TestCertificates", so I've update the project to match.

* Update Microsoft.AspNetCore.Authentication.Certificate.netcoreapp.cs

* Reverting change of Tests csproj, which was blocking certificate tests

* Forgot to add certificates to the shared source root, so tests were failing

* Updating following code review

Replaced custom logic with a new ChainTrustValidationMode property. Updated reference assemblies

* Changed tests to account for new property

* Updated certificates for PR tests

* Updated certificates

Updating all new certificates to avoid using the alternate signature algorithm

* Dummy checkin

Compilation errors coming from CertificateTests.cs, performing this checkin to test whether or not they're limited to a specific build.

* Following up typo corrections

Commit 116799fa70 in upstream repo changed a variable name, causing compile errors on PR build. Fixing that

Co-authored-by: Hao Kung <HaoK@users.noreply.github.com>
This commit is contained in:
Edward Neal 2020-02-06 23:15:39 +00:00 committed by GitHub
parent 8c32dfa145
commit e7ea31b1c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 204 additions and 3 deletions

View File

@ -24,6 +24,8 @@ namespace Microsoft.AspNetCore.Authentication.Certificate
{
public CertificateAuthenticationOptions() { }
public Microsoft.AspNetCore.Authentication.Certificate.CertificateTypes AllowedCertificateTypes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.Security.Cryptography.X509Certificates.X509ChainTrustMode ChainTrustValidationMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.Security.Cryptography.X509Certificates.X509Certificate2Collection CustomTrustStore { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public new Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationEvents Events { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509RevocationFlag RevocationFlag { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.Security.Cryptography.X509Certificates.X509RevocationMode RevocationMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }

View File

@ -167,6 +167,15 @@ namespace Microsoft.AspNetCore.Authentication.Certificate
chainPolicy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown;
chainPolicy.ExtraStore.Add(certificate);
}
else
{
if (Options.CustomTrustStore != null)
{
chainPolicy.CustomTrustStore.AddRange(Options.CustomTrustStore);
}
chainPolicy.TrustMode = Options.ChainTrustValidationMode;
}
if (!Options.ValidateValidityPeriod)
{

View File

@ -15,10 +15,21 @@ namespace Microsoft.AspNetCore.Authentication.Certificate
/// </summary>
public CertificateTypes AllowedCertificateTypes { get; set; } = CertificateTypes.Chained;
/// <summary>
/// Collection of X509 certificates which are trusted components of the certificate chain.
/// </summary>
public X509Certificate2Collection CustomTrustStore { get; set; } = new X509Certificate2Collection();
/// <summary>
/// Method used to validate certificate chains against <see cref="CustomTrustStore"/>.
/// </summary>
/// <remarks>This property must be set to <see cref="X509ChainTrustMode.CustomRootTrust"/> to enable <see cref="CustomTrustStore"/> to be used in certificate chain validation.</remarks>
public X509ChainTrustMode ChainTrustValidationMode { get; set; } = X509ChainTrustMode.System;
/// <summary>
/// Flag indicating whether the client certificate must be suitable for client
/// authentication, either via the Client Authentication EKU, or having no EKUs
/// at all. If the certificate chains to a root CA all certificates in the chain must be validate
/// at all. If the certificate chains to a root CA all certificates in the chain must be validated
/// for the client authentication EKU.
/// </summary>
public bool ValidateCertificateUse { get; set; } = true;

View File

@ -255,6 +255,51 @@ namespace Microsoft.AspNetCore.Authentication.Certificate.Test
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
}
[Fact]
public async Task VerifyUntrustedClientCertEndsUpInForbidden()
{
var server = CreateServer(
new CertificateAuthenticationOptions
{
Events = successfulValidationEvents
}, Certificates.SignedClient);
var response = await server.CreateClient().GetAsync("https://example.com/");
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
}
[Fact]
public async Task VerifyClientCertWithUntrustedRootAndTrustedChainEndsUpInForbidden()
{
var server = CreateServer(
new CertificateAuthenticationOptions
{
Events = successfulValidationEvents,
CustomTrustStore = new X509Certificate2Collection() { Certificates.SignedSecondaryRoot },
ChainTrustValidationMode = X509ChainTrustMode.CustomRootTrust,
RevocationMode = X509RevocationMode.NoCheck
}, Certificates.SignedClient);
var response = await server.CreateClient().GetAsync("https://example.com/");
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
}
[Fact]
public async Task VerifyValidClientCertWithTrustedChainAuthenticates()
{
var server = CreateServer(
new CertificateAuthenticationOptions
{
Events = successfulValidationEvents,
CustomTrustStore = new X509Certificate2Collection() { Certificates.SelfSignedPrimaryRoot, Certificates.SignedSecondaryRoot },
ChainTrustValidationMode = X509ChainTrustMode.CustomRootTrust,
RevocationMode = X509RevocationMode.NoCheck
}, Certificates.SignedClient);
var response = await server.CreateClient().GetAsync("https://example.com/");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task VerifyHeaderIsUsedIfCertIsNotPresent()
{
@ -534,11 +579,13 @@ namespace Microsoft.AspNetCore.Authentication.Certificate.Test
{
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(options =>
{
options.CustomTrustStore = configureOptions.CustomTrustStore;
options.ChainTrustValidationMode = configureOptions.ChainTrustValidationMode;
options.AllowedCertificateTypes = configureOptions.AllowedCertificateTypes;
options.Events = configureOptions.Events;
options.ValidateCertificateUse = configureOptions.ValidateCertificateUse;
options.RevocationFlag = options.RevocationFlag;
options.RevocationMode = options.RevocationMode;
options.RevocationFlag = configureOptions.RevocationFlag;
options.RevocationMode = configureOptions.RevocationMode;
options.ValidateValidityPeriod = configureOptions.ValidateValidityPeriod;
});
}
@ -599,6 +646,15 @@ namespace Microsoft.AspNetCore.Authentication.Certificate.Test
private static class Certificates
{
public static X509Certificate2 SelfSignedPrimaryRoot { get; private set; } =
new X509Certificate2(GetFullyQualifiedFilePath("validSelfSignedPrimaryRootCertificate.cer"));
public static X509Certificate2 SignedSecondaryRoot { get; private set; } =
new X509Certificate2(GetFullyQualifiedFilePath("validSignedSecondaryRootCertificate.cer"));
public static X509Certificate2 SignedClient { get; private set; } =
new X509Certificate2(GetFullyQualifiedFilePath("validSignedClientCertificate.cer"));
public static X509Certificate2 SelfSignedValidWithClientEku { get; private set; } =
new X509Certificate2(GetFullyQualifiedFilePath("validSelfSignedClientEkuCertificate.cer"));
@ -626,3 +682,4 @@ namespace Microsoft.AspNetCore.Authentication.Certificate.Test
}
}
}

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDPTCCAiWgAwIBAgIQTmWeCzG8SbRJ0y+osLWwDjANBgkqhkiG9w0BAQsFADAp
MScwJQYDVQQDDB5WYWxpZCBTZWxmIFNpZ25lZCBQcmltYXJ5IFJvb3QwHhcNMjAw
MTIxMDAwMDAwWhcNNDUwMTIxMDAwMDAwWjApMScwJQYDVQQDDB5WYWxpZCBTZWxm
IFNpZ25lZCBQcmltYXJ5IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDMK6v6HsJ19iRlXIRQVBJiy9xnJWBddLjm+2RRkyiiffEitBExiXVyrQ8L
DlegQQH3oJR0xgXwisJctjpHHz54dfbw5LwC9j9EVtu/UgDgK4lo6X3WLNYMJ1pX
xxjGfXcyzGGhcI0KATlyWhWgOrZNFzE+v0KY/LtZvcZ290Y4X7MQLge+V/09Lohx
pj6vsHkpoK8tD8ksJp+O8jk45TXTxs4yo8BRXbIv0oMmuZ9+gVkiaGurCCe/o+nw
vjEQre9oKNFI9KOgen6l1152BVQaXMDd22vemGIz738Scl9kcBQhy1D0dPuL6QV3
rR8HoNG3i0cuYxB4xgFF5GY2fhQBAgMBAAGjYTBfMA4GA1UdDwEB/wQEAwIBhjAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQU/cAC4CkVEtEj2UCYMVS/mlFCdBAwDQYJKoZIhvcNAQELBQADggEB
AInTuEG8Kv2aWy7MJg/N/AvEmC7USMgTceFY+bKhVogYCE9m2VAa4Tz5DEEJwYQV
IBnEamQN1eWP/R7dxcg+gIck8TevZC6r7wKMUATCcn9Ti0I0Hxdplts9+YIksJJ7
GbgyPS3UWnXl2D0374KrqTKSRjEXPOzaNyJ0HB4Pr9bibuSZ6Qc0gSltz7xOPFYS
7cedTqpABJXF6hZM7tDsxPfXmBHDy2sU84yXTQQghmU5S7fLWgy3so4g/DUqxffS
hmYPagc9DsmRGc2CCZz8IHlVc7byZ/NF4FgqB3IATbqYBAw4S/RyKHfWpURie2hC
OtYLcOTzVJG4uD3FGxyXP1k=
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDTTCCAjWgAwIBAgIQaaZ/qIdm4K5JhFdDzzj53DANBgkqhkiG9w0BAQsFADAm
MSQwIgYDVQQDDBtWYWxpZCBTaWduZWQgU2Vjb25kYXJ5IFJvb3QwHhcNMjAwMTIx
MDAwMDAwWhcNMjIwMTIxMDAwMDAwWjAeMRwwGgYDVQQDDBNWYWxpZCBTaWduZWQg
Q2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu3dVA8q1GewW
i1K0Pw0gKqgv92RrX3JI6tTiLbU6FBpFdV63b1M3jgFhUSXJi7L8A/dxh2HwvKBJ
p+4KW7V+aXQXOY8iShQwrIud5IExFdtEjyGVtfFSvfYmDgbfjFKIGswxsLenlfEt
7mp303GH99JVFql1n7S+bib79vKkrjFBqixhnXisXjNlBlfH6kRBYiwQ1Gc5oyib
fZQkfakXo896UwIvQjc0W27c0tiGY6xyGLSesLih2yECADiGa+cc5rnRc7R9/IMB
N7o5gLpbe71WBopI1uq1VSuXwH9xy0bq307dZEMaX0b4SqhkuQHsBVtOV1mYAskE
K3W8VUZy7QIDAQABo38wfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUx9BZtKX7
/z2mmoO2Ec127GkibsAwHQYDVR0OBBYEFDmtlIR/fVwtDseOG/NNQ/QEv3/PMA0G
CSqGSIb3DQEBCwUAA4IBAQBQMhsmlwF5JKEkfay7uLCH9IrJZGk1ZDxK/qcVaOk5
mQ4IcCBq+Wp7Hg/D92b5diwlkXJDrYZZ7OHSEcD/PrxUKyZkoBQIvlNKDgmjp0wV
lXYUISZHaXbWZ0XNFAS0KyqoLZ8c2xmhuI21L3hyOoRcoqKleO1kzYfb2seBaRHk
Iu4la0opKGFoI/o7gC9uLrcizpj3SoPF9+vJz/FJmeBbKzKe1zA479a74tjfOODy
LZVbsGDhKRQ02GftFqXRl257hVX+6etQiOePj7S++R1B/QXRjvKrOTMs/NpMLAeK
8uWXSx+boL/8j/3u+65Udh614C5dXSrgDjMGJ7/OchC1
-----END CERTIFICATE-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIQE2HFYAdh7b5NSBsomRG9cjANBgkqhkiG9w0BAQsFADAp
MScwJQYDVQQDDB5WYWxpZCBTZWxmIFNpZ25lZCBQcmltYXJ5IFJvb3QwHhcNMjAw
MTIxMDAwMDAwWhcNMzUwMTIxMDAwMDAwWjAmMSQwIgYDVQQDDBtWYWxpZCBTaWdu
ZWQgU2Vjb25kYXJ5IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCw1Wvr8SeMdXM0ZMN3/NyZhGzXC/IcqJyI1tM1IQNpO9OxJWDkfxGh14ORRZ3f
4pTdXOXRCcPYxHk8d3kuH9EEo78WRrLV4XHw31vGrQkHAPn3ZMl/Qre1mYvzkKbn
DIpScfPYMuqydOvx1YSsTP2G37pNszOAXTkHPPH9smTo/W7Dv/1mnroAru/FU+Hv
zOMqNirIz1EpCEopLeBS41lcohyuCMzHPKJJZOnNbV3wV3AnpEriRLQVNO9WiaGs
Nwj8ffai9M4vncRQ9wLK866lx6iA7istjod9hourKQWC1284pv+RLtIeJaGrpXkV
mSbk9ebabz1fPC2/WgPtd1JVAgMBAAGjgYMwgYAwDgYDVR0PAQH/BAQDAgGGMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/MB8G
A1UdIwQYMBaAFP3AAuApFRLRI9lAmDFUv5pRQnQQMB0GA1UdDgQWBBTH0Fm0pfv/
Paaag7YRzXbsaSJuwDANBgkqhkiG9w0BAQsFAAOCAQEAT5fEUkVP3Allay2ODcjQ
GzM135mV718DS84B4bVDBWr+CW9i89bzYgRZgClTABqddotHEqmLEan/bV4suBSt
QuACy7m39Q8kj/S/ydBhvHx9fxqWnAsacQ+fuAPviBQ11UZB19zWj1zikw1/Xfow
V9OIf4gYtY2aBPyygWN3HwpszhJWQIuFGl4rwqAxli7Wp2eUBXxDtYBHAscsclG4
1rduhiV5eUZXZ11mbA7KBH9XwWKoFpRza049I0WC+V0PWqlK4H4P0QzCWUlXmTC8
kN04cnPtyciOlP9J3Uro5xTXaDC0Cge82JmRxnCovGKGBEdjIxMC4nbPB4emmcth
BA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDPTCCAiWgAwIBAgIQTmWeCzG8SbRJ0y+osLWwDjANBgkqhkiG9w0BAQsFADAp
MScwJQYDVQQDDB5WYWxpZCBTZWxmIFNpZ25lZCBQcmltYXJ5IFJvb3QwHhcNMjAw
MTIxMDAwMDAwWhcNNDUwMTIxMDAwMDAwWjApMScwJQYDVQQDDB5WYWxpZCBTZWxm
IFNpZ25lZCBQcmltYXJ5IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDMK6v6HsJ19iRlXIRQVBJiy9xnJWBddLjm+2RRkyiiffEitBExiXVyrQ8L
DlegQQH3oJR0xgXwisJctjpHHz54dfbw5LwC9j9EVtu/UgDgK4lo6X3WLNYMJ1pX
xxjGfXcyzGGhcI0KATlyWhWgOrZNFzE+v0KY/LtZvcZ290Y4X7MQLge+V/09Lohx
pj6vsHkpoK8tD8ksJp+O8jk45TXTxs4yo8BRXbIv0oMmuZ9+gVkiaGurCCe/o+nw
vjEQre9oKNFI9KOgen6l1152BVQaXMDd22vemGIz738Scl9kcBQhy1D0dPuL6QV3
rR8HoNG3i0cuYxB4xgFF5GY2fhQBAgMBAAGjYTBfMA4GA1UdDwEB/wQEAwIBhjAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQU/cAC4CkVEtEj2UCYMVS/mlFCdBAwDQYJKoZIhvcNAQELBQADggEB
AInTuEG8Kv2aWy7MJg/N/AvEmC7USMgTceFY+bKhVogYCE9m2VAa4Tz5DEEJwYQV
IBnEamQN1eWP/R7dxcg+gIck8TevZC6r7wKMUATCcn9Ti0I0Hxdplts9+YIksJJ7
GbgyPS3UWnXl2D0374KrqTKSRjEXPOzaNyJ0HB4Pr9bibuSZ6Qc0gSltz7xOPFYS
7cedTqpABJXF6hZM7tDsxPfXmBHDy2sU84yXTQQghmU5S7fLWgy3so4g/DUqxffS
hmYPagc9DsmRGc2CCZz8IHlVc7byZ/NF4FgqB3IATbqYBAw4S/RyKHfWpURie2hC
OtYLcOTzVJG4uD3FGxyXP1k=
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDTTCCAjWgAwIBAgIQaaZ/qIdm4K5JhFdDzzj53DANBgkqhkiG9w0BAQsFADAm
MSQwIgYDVQQDDBtWYWxpZCBTaWduZWQgU2Vjb25kYXJ5IFJvb3QwHhcNMjAwMTIx
MDAwMDAwWhcNMjIwMTIxMDAwMDAwWjAeMRwwGgYDVQQDDBNWYWxpZCBTaWduZWQg
Q2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu3dVA8q1GewW
i1K0Pw0gKqgv92RrX3JI6tTiLbU6FBpFdV63b1M3jgFhUSXJi7L8A/dxh2HwvKBJ
p+4KW7V+aXQXOY8iShQwrIud5IExFdtEjyGVtfFSvfYmDgbfjFKIGswxsLenlfEt
7mp303GH99JVFql1n7S+bib79vKkrjFBqixhnXisXjNlBlfH6kRBYiwQ1Gc5oyib
fZQkfakXo896UwIvQjc0W27c0tiGY6xyGLSesLih2yECADiGa+cc5rnRc7R9/IMB
N7o5gLpbe71WBopI1uq1VSuXwH9xy0bq307dZEMaX0b4SqhkuQHsBVtOV1mYAskE
K3W8VUZy7QIDAQABo38wfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUx9BZtKX7
/z2mmoO2Ec127GkibsAwHQYDVR0OBBYEFDmtlIR/fVwtDseOG/NNQ/QEv3/PMA0G
CSqGSIb3DQEBCwUAA4IBAQBQMhsmlwF5JKEkfay7uLCH9IrJZGk1ZDxK/qcVaOk5
mQ4IcCBq+Wp7Hg/D92b5diwlkXJDrYZZ7OHSEcD/PrxUKyZkoBQIvlNKDgmjp0wV
lXYUISZHaXbWZ0XNFAS0KyqoLZ8c2xmhuI21L3hyOoRcoqKleO1kzYfb2seBaRHk
Iu4la0opKGFoI/o7gC9uLrcizpj3SoPF9+vJz/FJmeBbKzKe1zA479a74tjfOODy
LZVbsGDhKRQ02GftFqXRl257hVX+6etQiOePj7S++R1B/QXRjvKrOTMs/NpMLAeK
8uWXSx+boL/8j/3u+65Udh614C5dXSrgDjMGJ7/OchC1
-----END CERTIFICATE-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIQE2HFYAdh7b5NSBsomRG9cjANBgkqhkiG9w0BAQsFADAp
MScwJQYDVQQDDB5WYWxpZCBTZWxmIFNpZ25lZCBQcmltYXJ5IFJvb3QwHhcNMjAw
MTIxMDAwMDAwWhcNMzUwMTIxMDAwMDAwWjAmMSQwIgYDVQQDDBtWYWxpZCBTaWdu
ZWQgU2Vjb25kYXJ5IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCw1Wvr8SeMdXM0ZMN3/NyZhGzXC/IcqJyI1tM1IQNpO9OxJWDkfxGh14ORRZ3f
4pTdXOXRCcPYxHk8d3kuH9EEo78WRrLV4XHw31vGrQkHAPn3ZMl/Qre1mYvzkKbn
DIpScfPYMuqydOvx1YSsTP2G37pNszOAXTkHPPH9smTo/W7Dv/1mnroAru/FU+Hv
zOMqNirIz1EpCEopLeBS41lcohyuCMzHPKJJZOnNbV3wV3AnpEriRLQVNO9WiaGs
Nwj8ffai9M4vncRQ9wLK866lx6iA7istjod9hourKQWC1284pv+RLtIeJaGrpXkV
mSbk9ebabz1fPC2/WgPtd1JVAgMBAAGjgYMwgYAwDgYDVR0PAQH/BAQDAgGGMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/MB8G
A1UdIwQYMBaAFP3AAuApFRLRI9lAmDFUv5pRQnQQMB0GA1UdDgQWBBTH0Fm0pfv/
Paaag7YRzXbsaSJuwDANBgkqhkiG9w0BAQsFAAOCAQEAT5fEUkVP3Allay2ODcjQ
GzM135mV718DS84B4bVDBWr+CW9i89bzYgRZgClTABqddotHEqmLEan/bV4suBSt
QuACy7m39Q8kj/S/ydBhvHx9fxqWnAsacQ+fuAPviBQ11UZB19zWj1zikw1/Xfow
V9OIf4gYtY2aBPyygWN3HwpszhJWQIuFGl4rwqAxli7Wp2eUBXxDtYBHAscsclG4
1rduhiV5eUZXZ11mbA7KBH9XwWKoFpRza049I0WC+V0PWqlK4H4P0QzCWUlXmTC8
kN04cnPtyciOlP9J3Uro5xTXaDC0Cge82JmRxnCovGKGBEdjIxMC4nbPB4emmcth
BA==
-----END CERTIFICATE-----