From ef6e6eb1407add4343c923a3bbe55c869cb63daa Mon Sep 17 00:00:00 2001 From: Anton Nikitiuk Date: Thu, 13 Aug 2026 17:20:00 +0300 Subject: [PATCH 1/2] Replace CertificatePinner with custom TrustManagerFactory trust store --- .../src/main/java/com/duosecurity/Client.java | 170 +----- .../service/CertificateHelper.java | 139 +++++ .../com/duosecurity/service/DuoConnector.java | 46 +- .../src/main/resources/ca_certs.pem | 489 ++++++++++++++++++ .../test/java/com/duosecurity/ClientTest.java | 16 +- .../service/CertificateHelperTest.java | 83 +++ .../duosecurity/service/DuoConnectorTest.java | 87 ++-- 7 files changed, 820 insertions(+), 210 deletions(-) create mode 100644 duo-universal-sdk/src/main/java/com/duosecurity/service/CertificateHelper.java create mode 100644 duo-universal-sdk/src/main/resources/ca_certs.pem create mode 100644 duo-universal-sdk/src/test/java/com/duosecurity/service/CertificateHelperTest.java diff --git a/duo-universal-sdk/src/main/java/com/duosecurity/Client.java b/duo-universal-sdk/src/main/java/com/duosecurity/Client.java index 5adf076..e7da20d 100644 --- a/duo-universal-sdk/src/main/java/com/duosecurity/Client.java +++ b/duo-universal-sdk/src/main/java/com/duosecurity/Client.java @@ -15,7 +15,10 @@ import com.duosecurity.model.HealthCheckResponse; import com.duosecurity.model.Token; import com.duosecurity.model.TokenResponse; +import com.duosecurity.service.CertificateHelper; import com.duosecurity.service.DuoConnector; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; /** @@ -162,152 +165,10 @@ public static class Builder { private Integer proxyPort; private final String redirectUri; private Boolean useDuoCodeAttribute; - private String[] caCerts; + private String[] customPemCerts; private boolean caPinningDisabled; private String userAgent; - private static final String[] DEFAULT_CA_CERTS = { - //Source URL: https://www.amazontrust.com/repository/AmazonRootCA1.cer - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=Amazon Root CA 1,O=Amazon,C=US - //Issuer: CN=Amazon Root CA 1,O=Amazon,C=US - //Expiration Date: 2038-01-17 00:00:00 - //Serial Number: 66C9FCF99BF8C0A39E2F0788A43E696365BCA - //SHA256 Fingerprint: 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e - "sha256/++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=", - //Source URL: https://www.amazontrust.com/repository/AmazonRootCA2.cer - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=Amazon Root CA 2,O=Amazon,C=US - //Issuer: CN=Amazon Root CA 2,O=Amazon,C=US - //Expiration Date: 2040-05-26 00:00:00 - //Serial Number: 66C9FD29635869F0A0FE58678F85B26BB8A37 - //SHA256 Fingerprint: 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 - "sha256/f0KW/FtqTjs108NpYj42SrGvOB2PpxIVM8nWxjPqJGE=", - //Source URL: https://www.amazontrust.com/repository/AmazonRootCA3.cer - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=Amazon Root CA 3,O=Amazon,C=US - //Issuer: CN=Amazon Root CA 3,O=Amazon,C=US - //Expiration Date: 2040-05-26 00:00:00 - //Serial Number: 66C9FD5749736663F3B0B9AD9E89E7603F24A - //SHA256 Fingerprint: 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 - "sha256/NqvDJlas/GRcYbcWE8S/IceH9cq77kg0jVhZeAPXq8k=", - //Source URL: https://www.amazontrust.com/repository/AmazonRootCA4.cer - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=Amazon Root CA 4,O=Amazon,C=US - //Issuer: CN=Amazon Root CA 4,O=Amazon,C=US - //Expiration Date: 2040-05-26 00:00:00 - //Serial Number: 66C9FD7C1BB104C2943E5717B7B2CC81AC10E - //SHA256 Fingerprint: e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 - "sha256/9+ze1cZgR9KO1kZrVDxA4HQ6voHRCSVNz4RdTCx4U8U=", - //Source URL: https://www.amazontrust.com/repository/SFSRootCAG2.cer - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=Starfield Services Root Certificate Authority - G2, - // O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US - //Issuer: CN=Starfield Services Root Certificate Authority - G2, - // O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US - //Expiration Date: 2037-12-31 23:59:59 - //Serial Number: 0 - //SHA256 Fingerprint: 568d6905a2c88708a4b3025190edcfedb1974a606a13c6e5290fcb2ae63edab5 - "sha256/KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I=", - //Source URL: https://cacerts.digicert.com/DigiCertHighAssuranceEVRootCA.crt - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US - //Issuer: CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US - //Expiration Date: 2031-11-10 00:00:00 - //Serial Number: 2AC5C266A0B409B8F0B79F2AE462577 - //SHA256 Fingerprint: 7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf - "sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=", - //Source URL: https://cacerts.digicert.com/DigiCertTLSECCP384RootG5.crt - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US - //Issuer: CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US - //Expiration Date: 2046-01-14 23:59:59 - //Serial Number: 9E09365ACF7D9C8B93E1C0B042A2EF3 - //SHA256 Fingerprint: 018e13f0772532cf809bd1b17281867283fc48c6e13be9c69812854a490c1b05 - "sha256/oC+voZLIy4HLE0FVT5wFtxzKKokLDRKY1oNkfJYe+98=", - //Source URL: https://cacerts.digicert.com/DigiCertTLSRSA4096RootG5.crt - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=DigiCert TLS RSA4096 Root G5,O=DigiCert\, Inc.,C=US - //Issuer: CN=DigiCert TLS RSA4096 Root G5,O=DigiCert\, Inc.,C=US - //Expiration Date: 2046-01-14 23:59:59 - //Serial Number: 8F9B478A8FA7EDA6A333789DE7CCF8A - //SHA256 Fingerprint: 371a00dc0533b3721a7eeb40e8419e70799d2b0a0f2c1d80693165f7cec4ad75 - "sha256/ape1HIIZ6T5d7GS61YBs3rD4NVvkfnVwELcCRW4Bqv0=", - //Source URL: https://secure.globalsign.com/cacert/rootr46.crt - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE - //Issuer: CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE - //Expiration Date: 2046-03-20 00:00:00 - //Serial Number: 11D2BBB9D723189E405F0A9D2DD0DF2567D1 - //SHA256 Fingerprint: 4fa3126d8d3a11d1c4855a4f807cbad6cf919d3a5a88b03bea2c6372d93c40c9 - "sha256/rn+WLLnmp9v3uDP7GPqbcaiRdd+UnCMrap73yz3yu/w=", - //Source URL: https://secure.globalsign.com/cacert/roote46.crt - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE - //Issuer: CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE - //Expiration Date: 2046-03-20 00:00:00 - //Serial Number: 11D2BBBA336ED4BCE62468C50D841D98E843 - //SHA256 Fingerprint: cbb9c44d84b8043e1050ea31a69f514955d7bfd2e2c6b49301019ad61d9f5058 - "sha256/4EoCLOMvTM8sf2BGKHuCijKpCfXnUUR/g/0scfb9gXM=", - //Source URL: https://i.pki.goog/r2.crt - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=GTS Root R2,O=Google Trust Services LLC,C=US - //Issuer: CN=GTS Root R2,O=Google Trust Services LLC,C=US - //Expiration Date: 2036-06-22 00:00:00 - //Serial Number: 203E5AEC58D04251AAB1125AA - //SHA256 Fingerprint: 8d25cd97229dbf70356bda4eb3cc734031e24cf00fafcfd32dc76eb5841c7ea8 - "sha256/Vfd95BwDeSQo+NUYxVEEIlvkOlWY2SalKK1lPhzOx78=", - //Source URL: https://i.pki.goog/r4.crt - //Certificate #1 Details: - //Original Format: DER - //Subject: CN=GTS Root R4,O=Google Trust Services LLC,C=US - //Issuer: CN=GTS Root R4,O=Google Trust Services LLC,C=US - //Expiration Date: 2036-06-22 00:00:00 - //Serial Number: 203E5C068EF631A9C72905052 - //SHA256 Fingerprint: 349dfa4058c5e263123b398ae795573c4e1313c83fe68f93556cd5e8031b3c7d - "sha256/mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=", - //Source URL: https://www.identrust.com/file-download/download/public/5718 - //Certificate #1 Details: - //Original Format: PKCS7-DER - //Subject: CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US - //Issuer: CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US - //Expiration Date: 2034-01-16 18:12:23 - //Serial Number: A0142800000014523C844B500000002 - //SHA256 Fingerprint: 5d56499be4d2e08bcfcad08a3e38723d50503bde706948e42f55603019e528ae - "sha256/B+hU8mp8vTiZJ6oEG/7xts0h3RQ4GK2UfcZVqeWH/og=", - //Source URL: https://www.identrust.com/file-download/download/public/5842 - //Certificate #1 Details: - //Original Format: PKCS7-PEM - //Subject: CN=IdenTrust Commercial Root TLS ECC CA 2,O=IdenTrust,C=US - //Issuer: CN=IdenTrust Commercial Root TLS ECC CA 2,O=IdenTrust,C=US - //Expiration Date: 2039-04-11 21:11:10 - //Serial Number: 40018ECF000DE911D7447B73E4C1F82E - //SHA256 Fingerprint: 983d826ba9c87f653ff9e8384c5413e1d59acf19ddc9c98cecae5fdea2ac229c - "sha256/uu5PB+MS9L3/ffB/PuTG6A+WjsTtTaF52qqjrcHFXRU=", - //Source URL: https://ssl-ccp.secureserver.net/repository/sfroot-g2.crt - //Certificate #1 Details: - //Original Format: PEM - //Subject: CN=Starfield Root Certificate Authority - G2, - // O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US - //Issuer: CN=Starfield Root Certificate Authority - G2, - // O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US - //Expiration Date: 2037-12-31 23:59:59 - //Serial Number: 0 - //SHA256 Fingerprint: 2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5 - "sha256/gI1os/q0iEpflxrOfRBVDXqVoWN3Tz7Dav/7IT++THQ=", - }; - /** * Builder. * @@ -322,7 +183,6 @@ public Builder(String clientId, String clientSecret, String apiHost, this.clientSecret = clientSecret; this.apiHost = apiHost; this.redirectUri = redirectUri; - this.caCerts = DEFAULT_CA_CERTS; this.useDuoCodeAttribute = true; this.userAgent = computeUserAgent(); } @@ -349,7 +209,6 @@ public Builder(String clientId, String clientSecret, String proxyHost, this.proxyHost = proxyHost; this.proxyPort = proxyPort; this.redirectUri = redirectUri; - this.caCerts = DEFAULT_CA_CERTS; this.useDuoCodeAttribute = true; this.userAgent = computeUserAgent(); } @@ -364,11 +223,23 @@ public Builder(String clientId, String clientSecret, String proxyHost, public Client build() throws DuoException { validateClientParams(clientId, clientSecret, apiHost, redirectUri); - if (caPinningDisabled && caCerts != DEFAULT_CA_CERTS) { + if (caPinningDisabled && customPemCerts != null) { throw new DuoException( "Cannot both disable CA pinning and provide custom certificates"); } + SSLSocketFactory sslSocketFactory = null; + X509TrustManager trustManager = null; + + if (!caPinningDisabled) { + if (customPemCerts != null) { + trustManager = CertificateHelper.createTrustManager(customPemCerts); + } else { + trustManager = CertificateHelper.loadDefaultTrustManager(); + } + sslSocketFactory = CertificateHelper.createSslSocketFactory(trustManager); + } + Client client = new Client(); client.clientId = clientId; client.clientSecret = clientSecret; @@ -379,21 +250,22 @@ public Client build() throws DuoException { client.userAgent = format("%s ca_bundle/%s (ca_pinning=%s)", userAgent, CA_BUNDLE_VERSION, caPinningStatus); client.duoConnector = new DuoConnector(apiHost, proxyHost, proxyPort, - caPinningDisabled ? null : caCerts); + sslSocketFactory, trustManager); return client; } /** * Optionally use custom CA Certificates when validating connections to Duo. + * Each string should be a PEM-encoded certificate. * - * @param userCaCerts List of CA Certificates to use + * @param userCaCerts List of PEM-encoded CA Certificates to use * * @return the Builder */ public Builder setCACerts(String[] userCaCerts) { if (validateCaCert(userCaCerts)) { - this.caCerts = userCaCerts; + this.customPemCerts = userCaCerts; } return this; } diff --git a/duo-universal-sdk/src/main/java/com/duosecurity/service/CertificateHelper.java b/duo-universal-sdk/src/main/java/com/duosecurity/service/CertificateHelper.java new file mode 100644 index 0000000..68c15d5 --- /dev/null +++ b/duo-universal-sdk/src/main/java/com/duosecurity/service/CertificateHelper.java @@ -0,0 +1,139 @@ +package com.duosecurity.service; + +import com.duosecurity.exception.DuoException; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +/** + * Utility class for loading CA certificates and creating custom TrustManagers. + */ +public class CertificateHelper { + + private static final String DEFAULT_CA_CERTS_RESOURCE = "ca_certs.pem"; + + /** + * Loads the bundled ca_certs.pem from classpath and returns an X509TrustManager + * that trusts only those certificates. + * + * @return X509TrustManager configured with bundled CA certificates + * @throws DuoException if certificates cannot be loaded + */ + public static X509TrustManager loadDefaultTrustManager() throws DuoException { + String pemContent = loadResourceAsString(DEFAULT_CA_CERTS_RESOURCE); + return createTrustManager(pemContent); + } + + /** + * Creates an X509TrustManager from PEM-encoded certificate strings. + * + * @param pemCerts array of PEM-encoded certificate strings + * @return X509TrustManager configured with the provided certificates + * @throws DuoException if certificates cannot be parsed + */ + public static X509TrustManager createTrustManager(String[] pemCerts) throws DuoException { + StringBuilder combined = new StringBuilder(); + for (String pem : pemCerts) { + combined.append(pem).append("\n"); + } + return createTrustManager(combined.toString()); + } + + /** + * Creates an X509TrustManager from a String containing PEM-encoded certificates. + * + * @param pemContent String containing PEM-encoded certificates + * @return X509TrustManager configured with the parsed certificates + * @throws DuoException if certificates cannot be parsed or trust manager cannot be created + */ + public static X509TrustManager createTrustManager(String pemContent) throws DuoException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream pemStream = new ByteArrayInputStream( + pemContent.getBytes(StandardCharsets.UTF_8)); + Collection certs = + cf.generateCertificates(pemStream); + + if (certs.isEmpty()) { + throw new DuoException("No certificates found in provided PEM input"); + } + + int index = 0; + for (java.security.cert.Certificate cert : certs) { + String alias = "duo-ca-" + index; + if (cert instanceof X509Certificate) { + alias = ((X509Certificate) cert).getSubjectDN().getName() + "-" + index; + } + keyStore.setCertificateEntry(alias, cert); + index++; + } + + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keyStore); + + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + return (X509TrustManager) tm; + } + } + throw new DuoException("No X509TrustManager found in TrustManagerFactory"); + } catch (KeyStoreException | CertificateException + | NoSuchAlgorithmException | IOException e) { + throw new DuoException("Failed to create trust manager: " + e.getMessage(), e); + } + } + + /** + * Creates an SSLSocketFactory using the provided TrustManager. + * + * @param trustManager the X509TrustManager to use + * @return SSLSocketFactory configured with the trust manager + * @throws DuoException if the SSLContext cannot be initialized + */ + public static SSLSocketFactory createSslSocketFactory(X509TrustManager trustManager) + throws DuoException { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[]{trustManager}, null); + return sslContext.getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new DuoException("Failed to create SSLSocketFactory: " + e.getMessage(), e); + } + } + + private static String loadResourceAsString(String resourceName) throws DuoException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + CertificateHelper.class.getClassLoader().getResourceAsStream(resourceName), + StandardCharsets.UTF_8))) { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + return sb.toString(); + } catch (NullPointerException e) { + throw new DuoException("Cannot find bundled CA certificates resource: " + resourceName); + } catch (IOException e) { + throw new DuoException("Failed to read CA certificates resource: " + e.getMessage(), e); + } + } +} diff --git a/duo-universal-sdk/src/main/java/com/duosecurity/service/DuoConnector.java b/duo-universal-sdk/src/main/java/com/duosecurity/service/DuoConnector.java index 353ba7c..4338d21 100644 --- a/duo-universal-sdk/src/main/java/com/duosecurity/service/DuoConnector.java +++ b/duo-universal-sdk/src/main/java/com/duosecurity/service/DuoConnector.java @@ -9,7 +9,8 @@ import java.net.InetSocketAddress; import java.net.Proxy; import java.util.concurrent.TimeUnit; -import okhttp3.CertificatePinner; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -40,12 +41,14 @@ public class DuoConnector { * DuoConnector Constructor. * * @param apiHost This value is the api host provided by Duo in the admin panel. - * @param caCerts CA Certificates used to connect to Duo + * @param sslSocketFactory Custom SSLSocketFactory, or null for system defaults + * @param trustManager Custom X509TrustManager, or null for system defaults * * @throws DuoException For issues getting and validating the URL */ - public DuoConnector(String apiHost, String[] caCerts) throws DuoException { - this(apiHost, null, null, caCerts); + public DuoConnector(String apiHost, SSLSocketFactory sslSocketFactory, + X509TrustManager trustManager) throws DuoException { + this(apiHost, null, null, sslSocketFactory, trustManager); } /** @@ -54,36 +57,31 @@ public DuoConnector(String apiHost, String[] caCerts) throws DuoException { * @param apiHost This value is the api host provided by Duo in the admin panel. * @param proxyHost This value is the proxy server hostname * @param proxyPort This value is the proxy server port - * @param caCerts CA Certificates used to connect to Duo, or null to disable pinning + * @param sslSocketFactory Custom SSLSocketFactory, or null for system defaults + * @param trustManager Custom X509TrustManager, or null for system defaults * * @throws DuoException For issues getting and validating the URL */ - public DuoConnector(String apiHost, String proxyHost, Integer proxyPort, String[] caCerts) + public DuoConnector(String apiHost, String proxyHost, Integer proxyPort, + SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) throws DuoException { - CertificatePinner certificatePinner; - if (caCerts != null) { - certificatePinner = new CertificatePinner.Builder() - .add(apiHost, caCerts).build(); - } else { - certificatePinner = CertificatePinner.DEFAULT; - } ConnectionPool connectionPool = new ConnectionPool( MAX_IDLE_CONNECTIONS, CONNECTION_KEEP_ALIVE_SECONDS, TimeUnit.SECONDS); - OkHttpClient client; + + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .connectionPool(connectionPool); + + if (sslSocketFactory != null && trustManager != null) { + builder.sslSocketFactory(sslSocketFactory, trustManager); + } + if (proxyHost != null && proxyPort != null) { Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)); - client = new OkHttpClient.Builder() - .certificatePinner(certificatePinner) - .connectionPool(connectionPool) - .proxy(proxy) - .build(); - } else { - client = new OkHttpClient.Builder() - .certificatePinner(certificatePinner) - .connectionPool(connectionPool) - .build(); + builder.proxy(proxy); } + OkHttpClient client = builder.build(); + retrofit = new Retrofit.Builder() .baseUrl(getAndValidateUrl(apiHost, "").toString()) .addConverterFactory(JacksonConverterFactory.create()) diff --git a/duo-universal-sdk/src/main/resources/ca_certs.pem b/duo-universal-sdk/src/main/resources/ca_certs.pem new file mode 100644 index 0000000..564146a --- /dev/null +++ b/duo-universal-sdk/src/main/resources/ca_certs.pem @@ -0,0 +1,489 @@ +# Source URL: https://www.amazontrust.com/repository/AmazonRootCA1.cer +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=Amazon Root CA 1,O=Amazon,C=US +# Issuer: CN=Amazon Root CA 1,O=Amazon,C=US +# Expiration Date: 2038-01-17 00:00:00 +# Serial Number: 66C9FCF99BF8C0A39E2F0788A43E696365BCA +# SHA256 Fingerprint: 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e + +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + + +# Source URL: https://www.amazontrust.com/repository/AmazonRootCA2.cer +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=Amazon Root CA 2,O=Amazon,C=US +# Issuer: CN=Amazon Root CA 2,O=Amazon,C=US +# Expiration Date: 2040-05-26 00:00:00 +# Serial Number: 66C9FD29635869F0A0FE58678F85B26BB8A37 +# SHA256 Fingerprint: 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 + +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + + +# Source URL: https://www.amazontrust.com/repository/AmazonRootCA3.cer +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=Amazon Root CA 3,O=Amazon,C=US +# Issuer: CN=Amazon Root CA 3,O=Amazon,C=US +# Expiration Date: 2040-05-26 00:00:00 +# Serial Number: 66C9FD5749736663F3B0B9AD9E89E7603F24A +# SHA256 Fingerprint: 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 + +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + + +# Source URL: https://www.amazontrust.com/repository/AmazonRootCA4.cer +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=Amazon Root CA 4,O=Amazon,C=US +# Issuer: CN=Amazon Root CA 4,O=Amazon,C=US +# Expiration Date: 2040-05-26 00:00:00 +# Serial Number: 66C9FD7C1BB104C2943E5717B7B2CC81AC10E +# SHA256 Fingerprint: e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 + +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + + +# Source URL: https://www.amazontrust.com/repository/SFSRootCAG2.cer +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US +# Issuer: CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US +# Expiration Date: 2037-12-31 23:59:59 +# Serial Number: 0 +# SHA256 Fingerprint: 568d6905a2c88708a4b3025190edcfedb1974a606a13c6e5290fcb2ae63edab5 + +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + + +# Source URL: https://cacerts.digicert.com/DigiCertHighAssuranceEVRootCA.crt +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US +# Issuer: CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US +# Expiration Date: 2031-11-10 00:00:00 +# Serial Number: 2AC5C266A0B409B8F0B79F2AE462577 +# SHA256 Fingerprint: 7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + + +# Source URL: https://cacerts.digicert.com/DigiCertTLSECCP384RootG5.crt +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US +# Issuer: CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US +# Expiration Date: 2046-01-14 23:59:59 +# Serial Number: 9E09365ACF7D9C8B93E1C0B042A2EF3 +# SHA256 Fingerprint: 018e13f0772532cf809bd1b17281867283fc48c6e13be9c69812854a490c1b05 + +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + + +# Source URL: https://cacerts.digicert.com/DigiCertTLSRSA4096RootG5.crt +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=DigiCert TLS RSA4096 Root G5,O=DigiCert\, Inc.,C=US +# Issuer: CN=DigiCert TLS RSA4096 Root G5,O=DigiCert\, Inc.,C=US +# Expiration Date: 2046-01-14 23:59:59 +# Serial Number: 8F9B478A8FA7EDA6A333789DE7CCF8A +# SHA256 Fingerprint: 371a00dc0533b3721a7eeb40e8419e70799d2b0a0f2c1d80693165f7cec4ad75 + +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + + +# Source URL: https://secure.globalsign.com/cacert/rootr46.crt +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE +# Issuer: CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE +# Expiration Date: 2046-03-20 00:00:00 +# Serial Number: 11D2BBB9D723189E405F0A9D2DD0DF2567D1 +# SHA256 Fingerprint: 4fa3126d8d3a11d1c4855a4f807cbad6cf919d3a5a88b03bea2c6372d93c40c9 + +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + + +# Source URL: https://secure.globalsign.com/cacert/roote46.crt +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE +# Issuer: CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE +# Expiration Date: 2046-03-20 00:00:00 +# Serial Number: 11D2BBBA336ED4BCE62468C50D841D98E843 +# SHA256 Fingerprint: cbb9c44d84b8043e1050ea31a69f514955d7bfd2e2c6b49301019ad61d9f5058 + +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + + +# Source URL: https://i.pki.goog/r2.crt +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=GTS Root R2,O=Google Trust Services LLC,C=US +# Issuer: CN=GTS Root R2,O=Google Trust Services LLC,C=US +# Expiration Date: 2036-06-22 00:00:00 +# Serial Number: 203E5AEC58D04251AAB1125AA +# SHA256 Fingerprint: 8d25cd97229dbf70356bda4eb3cc734031e24cf00fafcfd32dc76eb5841c7ea8 + +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE----- + + +# Source URL: https://i.pki.goog/r4.crt +# Certificate #1 Details: +# Original Format: DER +# Subject: CN=GTS Root R4,O=Google Trust Services LLC,C=US +# Issuer: CN=GTS Root R4,O=Google Trust Services LLC,C=US +# Expiration Date: 2036-06-22 00:00:00 +# Serial Number: 203E5C068EF631A9C72905052 +# SHA256 Fingerprint: 349dfa4058c5e263123b398ae795573c4e1313c83fe68f93556cd5e8031b3c7d + +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + + +# Source URL: https://www.identrust.com/file-download/download/public/5718 +# Certificate #1 Details: +# Original Format: PKCS7-DER +# Subject: CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US +# Issuer: CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US +# Expiration Date: 2034-01-16 18:12:23 +# Serial Number: A0142800000014523C844B500000002 +# SHA256 Fingerprint: 5d56499be4d2e08bcfcad08a3e38723d50503bde706948e42f55603019e528ae + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + + +# Source URL: https://www.identrust.com/file-download/download/public/5842 +# Certificate #1 Details: +# Original Format: PKCS7-PEM +# Subject: CN=IdenTrust Commercial Root TLS ECC CA 2,O=IdenTrust,C=US +# Issuer: CN=IdenTrust Commercial Root TLS ECC CA 2,O=IdenTrust,C=US +# Expiration Date: 2039-04-11 21:11:10 +# Serial Number: 40018ECF000DE911D7447B73E4C1F82E +# SHA256 Fingerprint: 983d826ba9c87f653ff9e8384c5413e1d59acf19ddc9c98cecae5fdea2ac229c + +-----BEGIN CERTIFICATE----- +MIICbDCCAc2gAwIBAgIQQAGOzwAN6RHXRHtz5MH4LjAKBggqhkjOPQQDBDBSMQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MS8wLQYDVQQDEyZJZGVuVHJ1 +c3QgQ29tbWVyY2lhbCBSb290IFRMUyBFQ0MgQ0EgMjAeFw0yNDA0MTEyMTExMTFa +Fw0zOTA0MTEyMTExMTBaMFIxCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlJZGVuVHJ1 +c3QxLzAtBgNVBAMTJklkZW5UcnVzdCBDb21tZXJjaWFsIFJvb3QgVExTIEVDQyBD +QSAyMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBwomiZTgLg8KqEImMmnO5rNPb +Oo9sv5w4nJh45CXs9Gcu8YET9ulxsyVBCVSfSYeppdtXFEWYyBi0QRCAlp5YZHQB +H675v5rWVKRXvhzsuUNi9Xw0Zy1bAXaikmsrY/J0L52j2RulW4q4WvE7f23VFwZu +d82J8k0YG+M4MpmdOho1rsKjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBQhNGgGrnXhVx/FuQqjXpuH+IlbwzAKBggqhkjOPQQD +BAOBjAAwgYgCQgDc9F4WOxAgci2uQWfsX9cjeIvDXaaeVjDz31Ycc+ZdPrK1JKrB +f6CuTwWy8VojtGxdM3PJMkJC4LGPuhcvkHLo4gJCAV5h+PXe4bDJ3QxE8hkGFoUW +Ak6KtMCIpbLyt5pHrROi+YW9MpScoNGJkg96G1ETvJTWz6dv0uQYjKXt3jlOfQ7g +-----END CERTIFICATE----- + + +# Source URL: https://ssl-ccp.secureserver.net/repository/sfroot-g2.crt +# Certificate #1 Details: +# Original Format: PEM +# Subject: CN=Starfield Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US +# Issuer: CN=Starfield Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US +# Expiration Date: 2037-12-31 23:59:59 +# Serial Number: 0 +# SHA256 Fingerprint: 2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5 + +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + diff --git a/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java b/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java index c2fcd23..c664a99 100644 --- a/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java +++ b/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java @@ -32,6 +32,18 @@ class ClientTest { private static final String STATE = "abcdefghijklmnopqrstuvwxyz123456"; private static final String USERNAME = "username"; + private static final String TEST_PEM_CERT; + + static { + try { + TEST_PEM_CERT = new String( + ClientTest.class.getClassLoader().getResourceAsStream("ca_certs.pem").readAllBytes(), + java.nio.charset.StandardCharsets.UTF_8); + } catch (java.io.IOException e) { + throw new RuntimeException(e); + } + } + private Client client; @BeforeEach @@ -301,7 +313,7 @@ void disableCaPinning_builds_successfully() throws DuoException { void disableCaPinning_with_custom_certs_throws_exception() { try { new Client.Builder(CLIENT_ID, CLIENT_SECRET, API_HOST, HTTPS_REDIRECT_URI) - .setCACerts(new String[]{"sha256/test"}) + .setCACerts(new String[]{TEST_PEM_CERT}) .disableCaPinning() .build(); Assertions.fail(); @@ -315,7 +327,7 @@ void disableCaPinning_then_custom_certs_throws_exception() { try { new Client.Builder(CLIENT_ID, CLIENT_SECRET, API_HOST, HTTPS_REDIRECT_URI) .disableCaPinning() - .setCACerts(new String[]{"sha256/test"}) + .setCACerts(new String[]{TEST_PEM_CERT}) .build(); Assertions.fail(); } catch (DuoException e) { diff --git a/duo-universal-sdk/src/test/java/com/duosecurity/service/CertificateHelperTest.java b/duo-universal-sdk/src/test/java/com/duosecurity/service/CertificateHelperTest.java new file mode 100644 index 0000000..e8aca6d --- /dev/null +++ b/duo-universal-sdk/src/test/java/com/duosecurity/service/CertificateHelperTest.java @@ -0,0 +1,83 @@ +package com.duosecurity.service; + +import com.duosecurity.exception.DuoException; +import org.junit.jupiter.api.Test; + +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; +import java.security.cert.X509Certificate; + +import static org.junit.jupiter.api.Assertions.*; + +class CertificateHelperTest { + + private static final int EXPECTED_CERT_COUNT = 15; + + @Test + void loadDefaultTrustManager_loadsAllCerts() throws DuoException { + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + assertNotNull(tm); + X509Certificate[] acceptedIssuers = tm.getAcceptedIssuers(); + assertEquals(EXPECTED_CERT_COUNT, acceptedIssuers.length); + } + + @Test + void createTrustManager_withPemArray_succeeds() throws DuoException { + String pemContent = loadResourceAsString("ca_certs.pem"); + X509TrustManager tm = CertificateHelper.createTrustManager(new String[]{pemContent}); + assertNotNull(tm); + assertEquals(EXPECTED_CERT_COUNT, tm.getAcceptedIssuers().length); + } + + @Test + void createTrustManager_withString_succeeds() throws DuoException { + String pemContent = loadResourceAsString("ca_certs.pem"); + X509TrustManager tm = CertificateHelper.createTrustManager(pemContent); + assertNotNull(tm); + assertEquals(EXPECTED_CERT_COUNT, tm.getAcceptedIssuers().length); + } + + @Test + void createTrustManager_withEmptyString_throwsDuoException() { + try { + CertificateHelper.createTrustManager(""); + fail("Expected DuoException"); + } catch (DuoException e) { + assertTrue(e.getMessage().contains("No certificates found")); + } + } + + @Test + void createTrustManager_withInvalidPem_throwsDuoException() { + String invalidPem = "-----BEGIN CERTIFICATE-----\nNOT_VALID_BASE64!!!\n-----END CERTIFICATE-----\n"; + try { + CertificateHelper.createTrustManager(invalidPem); + fail("Expected DuoException"); + } catch (DuoException e) { + assertNotNull(e.getMessage()); + } + } + + @Test + void createSslSocketFactory_returnsNonNull() throws DuoException { + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + assertNotNull(sf); + } + + private static String loadResourceAsString(String resourceName) { + try (java.io.BufferedReader reader = new java.io.BufferedReader( + new java.io.InputStreamReader( + CertificateHelperTest.class.getClassLoader().getResourceAsStream(resourceName), + java.nio.charset.StandardCharsets.UTF_8))) { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + return sb.toString(); + } catch (Exception e) { + throw new RuntimeException("Failed to load resource: " + resourceName, e); + } + } +} diff --git a/duo-universal-sdk/src/test/java/com/duosecurity/service/DuoConnectorTest.java b/duo-universal-sdk/src/test/java/com/duosecurity/service/DuoConnectorTest.java index 3bfabaa..7cc6266 100644 --- a/duo-universal-sdk/src/test/java/com/duosecurity/service/DuoConnectorTest.java +++ b/duo-universal-sdk/src/test/java/com/duosecurity/service/DuoConnectorTest.java @@ -3,7 +3,6 @@ import com.duosecurity.exception.DuoException; import com.duosecurity.model.HealthCheckResponse; import com.duosecurity.model.TokenResponse; -import okhttp3.CertificatePinner; import okhttp3.OkHttpClient; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -12,6 +11,8 @@ import retrofit2.Response; import retrofit2.Retrofit; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.lang.reflect.Field; @@ -21,14 +22,12 @@ class DuoConnectorTest { private static final String API_HOST = "my_api_host.com"; - private static final String[] CA_CERT = {"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="}; - - private DuoConnector duoConnector; @Test void duoHealthcheck() throws IOException, DuoException { - DuoConnector duoConnector = new DuoConnector(API_HOST, CA_CERT); + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, sf, tm); Retrofit retrofit = Mockito.mock(Retrofit.class); duoConnector.retrofit = retrofit; DuoService duoService = Mockito.mock(DuoService.class); @@ -45,7 +44,9 @@ void duoHealthcheck() throws IOException, DuoException { @Test void duoHealthcheck_network_failure() throws IOException, DuoException { - DuoConnector duoConnector = new DuoConnector(API_HOST, CA_CERT); + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, sf, tm); Retrofit retrofit = Mockito.mock(Retrofit.class); duoConnector.retrofit = retrofit; DuoService duoService = Mockito.mock(DuoService.class); @@ -56,9 +57,8 @@ void duoHealthcheck_network_failure() throws IOException, DuoException { when(duoService.duoHealthCheck("client_id", "client_assertion")).thenReturn(callSync); when(callSync.execute()).thenThrow(new IOException("Timeout")); - HealthCheckResponse result = null; try { - result = duoConnector.duoHealthcheck("client_id", "client_assertion"); + duoConnector.duoHealthcheck("client_id", "client_assertion"); Assertions.fail(); } catch (DuoException e) { assertEquals("Timeout", e.getMessage()); @@ -66,24 +66,45 @@ void duoHealthcheck_network_failure() throws IOException, DuoException { } @Test - void constructor_with_null_caCerts_disables_pinning() throws Exception { - DuoConnector duoConnector = new DuoConnector(API_HOST, null); + void constructor_with_null_ssl_uses_system_defaults() throws Exception { + DuoConnector duoConnector = new DuoConnector(API_HOST, null, null); + OkHttpClient client = getOkHttpClient(duoConnector); + // When null is passed, we don't call builder.sslSocketFactory(), + // so OkHttp uses its default (OS trust store). + // Verify it's the same as a default OkHttpClient would have. + OkHttpClient defaultClient = new OkHttpClient(); + assertEquals(defaultClient.sslSocketFactory().getClass(), + client.sslSocketFactory().getClass()); + } + + @Test + void constructor_with_null_ssl_and_proxy_uses_system_defaults() throws Exception { + DuoConnector duoConnector = new DuoConnector(API_HOST, "proxy.example.com", 8080, + null, null); OkHttpClient client = getOkHttpClient(duoConnector); - assertTrue(getPins(client.certificatePinner()).isEmpty()); + assertNotNull(client.sslSocketFactory()); + assertNotNull(client.proxy()); } @Test - void constructor_with_null_caCerts_and_proxy_disables_pinning() throws Exception { - DuoConnector duoConnector = new DuoConnector(API_HOST, "proxy.example.com", 8080, null); + void constructor_with_custom_ssl_uses_provided_factory() throws Exception { + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, sf, tm); OkHttpClient client = getOkHttpClient(duoConnector); - assertTrue(getPins(client.certificatePinner()).isEmpty()); + assertSame(sf, client.sslSocketFactory()); + // Verify our trust store contains exactly 15 bundled CA certs + assertEquals(15, tm.getAcceptedIssuers().length); } @Test - void constructor_with_caCerts_enables_pinning() throws Exception { - DuoConnector duoConnector = new DuoConnector(API_HOST, CA_CERT); + void constructor_with_custom_ssl_and_proxy_uses_provided_factory() throws Exception { + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, "proxy.example.com", 8080, sf, tm); OkHttpClient client = getOkHttpClient(duoConnector); - assertFalse(getPins(client.certificatePinner()).isEmpty()); + assertSame(sf, client.sslSocketFactory()); + assertNotNull(client.proxy()); } private OkHttpClient getOkHttpClient(DuoConnector connector) throws Exception { @@ -95,16 +116,11 @@ private OkHttpClient getOkHttpClient(DuoConnector connector) throws Exception { return (OkHttpClient) clientField.get(retrofit); } - @SuppressWarnings("unchecked") - private java.util.Set getPins(CertificatePinner pinner) throws Exception { - Field pinsField = CertificatePinner.class.getDeclaredField("pins"); - pinsField.setAccessible(true); - return (java.util.Set) pinsField.get(pinner); - } - @Test void exchangeAuthorizationCodeFor2FAResult() throws IOException, DuoException { - DuoConnector duoConnector = new DuoConnector(API_HOST, CA_CERT); + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, sf, tm); Retrofit retrofit = Mockito.mock(Retrofit.class); duoConnector.retrofit = retrofit; DuoService duoService = Mockito.mock(DuoService.class); @@ -123,7 +139,9 @@ void exchangeAuthorizationCodeFor2FAResult() throws IOException, DuoException { @Test void exchangeAuthorizationCodeFor2FAResult_network_failure() throws IOException, DuoException { - DuoConnector duoConnector = new DuoConnector(API_HOST, CA_CERT); + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, sf, tm); Retrofit retrofit = Mockito.mock(Retrofit.class); duoConnector.retrofit = retrofit; DuoService duoService = Mockito.mock(DuoService.class); @@ -135,9 +153,8 @@ void exchangeAuthorizationCodeFor2FAResult_network_failure() throws IOException, "client_assertion_type", "client_assertion")).thenReturn(callSync); when(callSync.execute()).thenThrow(new IOException("Timeout")); - TokenResponse result = null; try { - result = duoConnector.exchangeAuthorizationCodeFor2FAResult("user-agent", "grant_type", "duo_code", "redirect_uri", + duoConnector.exchangeAuthorizationCodeFor2FAResult("user-agent", "grant_type", "duo_code", "redirect_uri", "client_assertion_type", "client_assertion"); Assertions.fail(); } catch (DuoException e) { @@ -147,7 +164,9 @@ void exchangeAuthorizationCodeFor2FAResult_network_failure() throws IOException, @Test void exchangeAuthorizationCodeFor2FAResult_error_code() throws IOException, DuoException { - DuoConnector duoConnector = new DuoConnector(API_HOST, CA_CERT); + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, sf, tm); Retrofit retrofit = Mockito.mock(Retrofit.class); duoConnector.retrofit = retrofit; DuoService duoService = Mockito.mock(DuoService.class); @@ -156,7 +175,6 @@ void exchangeAuthorizationCodeFor2FAResult_error_code() throws IOException, DuoE when(duoService.exchangeAuthorizationCodeFor2FAResult("user-agent", "grant_type", "duo_code", "redirect_uri", "client_assertion_type", "client_assertion")).thenReturn(callSync); - // Create a 400 response (body doesn't matter) okhttp3.ResponseBody body = okhttp3.ResponseBody.create(null, ""); when(callSync.execute()).thenReturn(Response.error(400, body)); @@ -171,7 +189,9 @@ void exchangeAuthorizationCodeFor2FAResult_error_code() throws IOException, DuoE @Test void exchangeAuthorizationCodeFor2FAResult_null_body() throws IOException, DuoException { - DuoConnector duoConnector = new DuoConnector(API_HOST, CA_CERT); + X509TrustManager tm = CertificateHelper.loadDefaultTrustManager(); + SSLSocketFactory sf = CertificateHelper.createSslSocketFactory(tm); + DuoConnector duoConnector = new DuoConnector(API_HOST, sf, tm); Retrofit retrofit = Mockito.mock(Retrofit.class); duoConnector.retrofit = retrofit; DuoService duoService = Mockito.mock(DuoService.class); @@ -180,7 +200,6 @@ void exchangeAuthorizationCodeFor2FAResult_null_body() throws IOException, DuoEx when(duoService.exchangeAuthorizationCodeFor2FAResult("user-agent", "grant_type", "duo_code", "redirect_uri", "client_assertion_type", "client_assertion")).thenReturn(callSync); - // Create a successful (200) response with a null body when(callSync.execute()).thenReturn(Response.success(200, null)); try { @@ -188,8 +207,6 @@ void exchangeAuthorizationCodeFor2FAResult_null_body() throws IOException, DuoEx "client_assertion_type", "client_assertion"); Assertions.fail(); } catch (DuoException e) { - // Response.success() is the error message because that's the default message when manually crafting - // a successful (200) response. This still verifies we're properly creating the DuoException. assertEquals("Response.success()", e.getMessage()); } } From 6aaadb2b454d13943aabe8285f6ba8d106c09d32 Mon Sep 17 00:00:00 2001 From: Anton Nikitiuk Date: Thu, 13 Aug 2026 17:46:34 +0300 Subject: [PATCH 2/2] replace InputStream.readAllBytes() with BufferedReader --- .../src/test/java/com/duosecurity/ClientTest.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java b/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java index c664a99..db30476 100644 --- a/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java +++ b/duo-universal-sdk/src/test/java/com/duosecurity/ClientTest.java @@ -35,10 +35,16 @@ class ClientTest { private static final String TEST_PEM_CERT; static { - try { - TEST_PEM_CERT = new String( - ClientTest.class.getClassLoader().getResourceAsStream("ca_certs.pem").readAllBytes(), - java.nio.charset.StandardCharsets.UTF_8); + try (java.io.BufferedReader reader = new java.io.BufferedReader( + new java.io.InputStreamReader( + ClientTest.class.getClassLoader().getResourceAsStream("ca_certs.pem"), + java.nio.charset.StandardCharsets.UTF_8))) { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + TEST_PEM_CERT = sb.toString(); } catch (java.io.IOException e) { throw new RuntimeException(e); }