From 4be69e3f8a9c70ec36234d6373c9854eab6e9027 Mon Sep 17 00:00:00 2001 From: erm-g <110920239+erm-g@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:11:07 -0400 Subject: [PATCH] core: SpiffeUtil API for extracting Spiffe URI and loading TrustBundles (#11575) Additional API for SpiffeUtil: - extract Spiffe URI from certificate chain - load Spiffe Trust Bundle from filesystem [json spec][] [JWK spec][] JsonParser was changed to reject duplicate keys in objects. [json spec]: https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md [JWK spec]: https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md#61-publishing-spiffe-bundle-elements --- .../java/io/grpc/internal/JsonParser.java | 7 +- .../java/io/grpc/internal/SpiffeUtil.java | 190 +++++++++++++++++- .../java/io/grpc/internal/JsonParserTest.java | 9 +- .../java/io/grpc/internal/SpiffeUtilTest.java | 172 +++++++++++++++- .../io/grpc/internal/spiffebundle.json | 115 +++++++++++ .../internal/spiffebundle_corrupted_cert.json | 14 ++ .../internal/spiffebundle_duplicates.json | 23 +++ .../grpc/internal/spiffebundle_malformed.json | 4 + .../grpc/internal/spiffebundle_wrong_kid.json | 15 ++ .../grpc/internal/spiffebundle_wrong_kty.json | 12 ++ .../spiffebundle_wrong_multi_certs.json | 67 ++++++ .../internal/spiffebundle_wrong_root.json | 6 + .../internal/spiffebundle_wrong_seq_type.json | 12 ++ .../grpc/internal/spiffebundle_wrong_use.json | 13 ++ .../ObservabilityConfigImplTest.java | 3 +- .../main/resources/certs/spiffe-openssl.cnf | 12 ++ .../src/main/resources/certs/spiffe_cert.pem | 33 +++ .../certs/spiffe_multi_uri_san_cert.pem | 25 +++ 18 files changed, 725 insertions(+), 7 deletions(-) create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_corrupted_cert.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_duplicates.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_malformed.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kid.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kty.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_wrong_multi_certs.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_wrong_root.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_wrong_seq_type.json create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_wrong_use.json create mode 100644 testing/src/main/resources/certs/spiffe-openssl.cnf create mode 100644 testing/src/main/resources/certs/spiffe_cert.pem create mode 100644 testing/src/main/resources/certs/spiffe_multi_uri_san_cert.pem diff --git a/core/src/main/java/io/grpc/internal/JsonParser.java b/core/src/main/java/io/grpc/internal/JsonParser.java index 384d29754f..14f78c09e7 100644 --- a/core/src/main/java/io/grpc/internal/JsonParser.java +++ b/core/src/main/java/io/grpc/internal/JsonParser.java @@ -16,6 +16,7 @@ package io.grpc.internal; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import com.google.gson.stream.JsonReader; @@ -41,7 +42,8 @@ public final class JsonParser { /** * Parses a json string, returning either a {@code Map}, {@code List}, - * {@code String}, {@code Double}, {@code Boolean}, or {@code null}. + * {@code String}, {@code Double}, {@code Boolean}, or {@code null}. Fails if duplicate names + * found. */ public static Object parse(String raw) throws IOException { JsonReader jr = new JsonReader(new StringReader(raw)); @@ -81,6 +83,7 @@ public final class JsonParser { Map obj = new LinkedHashMap<>(); while (jr.hasNext()) { String name = jr.nextName(); + checkArgument(!obj.containsKey(name), "Duplicate key found: %s", name); Object value = parseRecursive(jr); obj.put(name, value); } @@ -105,4 +108,4 @@ public final class JsonParser { jr.nextNull(); return null; } -} +} \ No newline at end of file diff --git a/core/src/main/java/io/grpc/internal/SpiffeUtil.java b/core/src/main/java/io/grpc/internal/SpiffeUtil.java index bddce3d035..57e201d19b 100644 --- a/core/src/main/java/io/grpc/internal/SpiffeUtil.java +++ b/core/src/main/java/io/grpc/internal/SpiffeUtil.java @@ -19,15 +19,42 @@ package io.grpc.internal; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Optional; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.Map; /** - * Helper utility to work with SPIFFE URIs. + * Provides utilities to manage SPIFFE bundles, extract SPIFFE IDs from X.509 certificate chains, + * and parse SPIFFE IDs. * @see Standard */ public final class SpiffeUtil { + private static final Integer URI_SAN_TYPE = 6; + private static final String USE_PARAMETER_VALUE = "x509-svid"; + private static final String KTY_PARAMETER_VALUE = "RSA"; + private static final String CERTIFICATE_PREFIX = "-----BEGIN CERTIFICATE-----\n"; + private static final String CERTIFICATE_SUFFIX = "-----END CERTIFICATE-----"; private static final String PREFIX = "spiffe://"; private SpiffeUtil() {} @@ -96,6 +123,137 @@ public final class SpiffeUtil { + " ([a-zA-Z0-9.-_])"); } + /** + * Returns the SPIFFE ID from the leaf certificate, if present. + * + * @param certChain certificate chain to extract SPIFFE ID from + */ + public static Optional extractSpiffeId(X509Certificate[] certChain) + throws CertificateParsingException { + checkArgument(checkNotNull(certChain, "certChain").length > 0, "certChain can't be empty"); + Collection> subjectAltNames = certChain[0].getSubjectAlternativeNames(); + if (subjectAltNames == null) { + return Optional.absent(); + } + String uri = null; + // Search for the unique URI SAN. + for (List altName : subjectAltNames) { + if (altName.size() < 2 ) { + continue; + } + if (URI_SAN_TYPE.equals(altName.get(0))) { + if (uri != null) { + throw new IllegalArgumentException("Multiple URI SAN values found in the leaf cert."); + } + uri = (String) altName.get(1); + } + } + if (uri == null) { + return Optional.absent(); + } + return Optional.of(parse(uri)); + } + + /** + * Loads a SPIFFE trust bundle from a file, parsing it from the JSON format. + * In case of success, returns {@link SpiffeBundle}. + * If any element of the JSON content is invalid or unsupported, an + * {@link IllegalArgumentException} is thrown and the entire Bundle is considered invalid. + * + * @param trustBundleFile the file path to the JSON file containing the trust bundle + * @see JSON format + * @see JWK entry format + * @see x5c (certificate) parameter + */ + public static SpiffeBundle loadTrustBundleFromFile(String trustBundleFile) throws IOException { + Map trustDomainsNode = readTrustDomainsFromFile(trustBundleFile); + Map> trustBundleMap = new HashMap<>(); + Map sequenceNumbers = new HashMap<>(); + for (String trustDomainName : trustDomainsNode.keySet()) { + Map domainNode = JsonUtil.getObject(trustDomainsNode, trustDomainName); + if (domainNode.size() == 0) { + trustBundleMap.put(trustDomainName, Collections.emptyList()); + continue; + } + Long sequenceNumber = JsonUtil.getNumberAsLong(domainNode, "spiffe_sequence"); + sequenceNumbers.put(trustDomainName, sequenceNumber == null ? -1L : sequenceNumber); + List> keysNode = JsonUtil.getListOfObjects(domainNode, "keys"); + if (keysNode == null || keysNode.size() == 0) { + trustBundleMap.put(trustDomainName, Collections.emptyList()); + continue; + } + trustBundleMap.put(trustDomainName, extractCert(keysNode, trustDomainName)); + } + return new SpiffeBundle(sequenceNumbers, trustBundleMap); + } + + private static Map readTrustDomainsFromFile(String filePath) throws IOException { + Path path = Paths.get(checkNotNull(filePath, "trustBundleFile")); + String json = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + Object jsonObject = JsonParser.parse(json); + if (!(jsonObject instanceof Map)) { + throw new IllegalArgumentException( + "SPIFFE Trust Bundle should be a JSON object. Found: " + + (jsonObject == null ? null : jsonObject.getClass())); + } + @SuppressWarnings("unchecked") + Map root = (Map)jsonObject; + Map trustDomainsNode = JsonUtil.getObject(root, "trust_domains"); + checkNotNull(trustDomainsNode, "Mandatory trust_domains element is missing"); + checkArgument(trustDomainsNode.size() > 0, "Mandatory trust_domains element is missing"); + return trustDomainsNode; + } + + private static void checkJwkEntry(Map jwkNode, String trustDomainName) { + String kty = JsonUtil.getString(jwkNode, "kty"); + if (kty == null || !kty.equals(KTY_PARAMETER_VALUE)) { + throw new IllegalArgumentException(String.format("'kty' parameter must be '%s' but '%s' " + + "found. Certificate loading for trust domain '%s' failed.", KTY_PARAMETER_VALUE, + kty, trustDomainName)); + } + if (jwkNode.containsKey("kid")) { + throw new IllegalArgumentException(String.format("'kid' parameter must not be set. " + + "Certificate loading for trust domain '%s' failed.", trustDomainName)); + } + String use = JsonUtil.getString(jwkNode, "use"); + if (use == null || !use.equals(USE_PARAMETER_VALUE)) { + throw new IllegalArgumentException(String.format("'use' parameter must be '%s' but '%s' " + + "found. Certificate loading for trust domain '%s' failed.", USE_PARAMETER_VALUE, + use, trustDomainName)); + } + } + + private static List extractCert(List> keysNode, + String trustDomainName) { + List result = new ArrayList<>(); + for (Map keyNode : keysNode) { + checkJwkEntry(keyNode, trustDomainName); + List rawCerts = JsonUtil.getListOfStrings(keyNode, "x5c"); + if (rawCerts == null) { + break; + } + if (rawCerts.size() != 1) { + throw new IllegalArgumentException(String.format("Exactly 1 certificate is expected, but " + + "%s found. Certificate loading for trust domain '%s' failed.", rawCerts.size(), + trustDomainName)); + } + InputStream stream = new ByteArrayInputStream((CERTIFICATE_PREFIX + rawCerts.get(0) + "\n" + + CERTIFICATE_SUFFIX) + .getBytes(StandardCharsets.UTF_8)); + try { + Collection certs = CertificateFactory.getInstance("X509") + .generateCertificates(stream); + X509Certificate[] certsArray = certs.toArray(new X509Certificate[0]); + assert certsArray.length == 1; + result.add(certsArray[0]); + } catch (CertificateException e) { + throw new IllegalArgumentException(String.format("Certificate can't be parsed. Certificate " + + "loading for trust domain '%s' failed.", trustDomainName), e); + } + } + return result; + } + /** * Represents a SPIFFE ID as defined in the SPIFFE standard. * @see Standard @@ -119,4 +277,34 @@ public final class SpiffeUtil { } } + /** + * Represents a SPIFFE trust bundle; that is, a map from trust domain to set of trusted + * certificates. Only trust domain's sequence numbers and x509 certificates are supported. + * @see Standard + */ + public static final class SpiffeBundle { + + private final ImmutableMap sequenceNumbers; + + private final ImmutableMap> bundleMap; + + private SpiffeBundle(Map sequenceNumbers, + Map> trustDomainMap) { + this.sequenceNumbers = ImmutableMap.copyOf(sequenceNumbers); + ImmutableMap.Builder> builder = ImmutableMap.builder(); + for (Map.Entry> entry : trustDomainMap.entrySet()) { + builder.put(entry.getKey(), ImmutableList.copyOf(entry.getValue())); + } + this.bundleMap = builder.build(); + } + + public ImmutableMap getSequenceNumbers() { + return sequenceNumbers; + } + + public ImmutableMap> getBundleMap() { + return bundleMap; + } + } + } diff --git a/core/src/test/java/io/grpc/internal/JsonParserTest.java b/core/src/test/java/io/grpc/internal/JsonParserTest.java index 1e74c753d4..cfee566fa4 100644 --- a/core/src/test/java/io/grpc/internal/JsonParserTest.java +++ b/core/src/test/java/io/grpc/internal/JsonParserTest.java @@ -123,4 +123,11 @@ public class JsonParserTest { assertEquals(expected, JsonParser.parse("{\"hi\": 2}")); } -} + + @Test + public void duplicate() throws IOException { + thrown.expect(IllegalArgumentException.class); + + JsonParser.parse("{\"hi\": 2, \"hi\": 3}"); + } +} \ No newline at end of file diff --git a/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java b/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java index c3a98ce33e..244539501a 100644 --- a/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java +++ b/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java @@ -17,12 +17,28 @@ package io.grpc.internal; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import com.google.common.base.Optional; +import io.grpc.internal.SpiffeUtil.SpiffeBundle; +import io.grpc.internal.SpiffeUtil.SpiffeId; +import io.grpc.testing.TlsTesting; +import io.grpc.util.CertificateUtils; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collection; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.runners.Enclosed; +import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; @@ -159,7 +175,8 @@ public class SpiffeUtilTest { SpiffeUtil.parse(longTrustDomain.toString())); assertEquals("Trust Domain maximum length is 255 characters", iae.getMessage()); - StringBuilder longSpiffe = new StringBuilder(String.format("spiffe://mydomain%scom/", "%21")); + @SuppressWarnings("OrphanedFormatString") + StringBuilder longSpiffe = new StringBuilder("spiffe://mydomain%21com/"); for (int i = 0; i < 405; i++) { longSpiffe.append("qwert"); } @@ -193,4 +210,157 @@ public class SpiffeUtilTest { + "underscores ([a-zA-Z0-9.-_])", iae.getMessage()); } } + + public static class CertificateApiTest { + private static final String SPIFFE_PEM_FILE = "spiffe_cert.pem"; + private static final String MULTI_URI_SAN_PEM_FILE = "spiffe_multi_uri_san_cert.pem"; + private static final String SERVER_0_PEM_FILE = "server0.pem"; + private static final String TEST_DIRECTORY_PREFIX = "io/grpc/internal/"; + private static final String SPIFFE_TRUST_BUNDLE = "spiffebundle.json"; + private static final String SPIFFE_TRUST_BUNDLE_MALFORMED = "spiffebundle_malformed.json"; + private static final String SPIFFE_TRUST_BUNDLE_CORRUPTED_CERT = + "spiffebundle_corrupted_cert.json"; + private static final String SPIFFE_TRUST_BUNDLE_WRONG_KTY = "spiffebundle_wrong_kty.json"; + private static final String SPIFFE_TRUST_BUNDLE_WRONG_KID = "spiffebundle_wrong_kid.json"; + private static final String SPIFFE_TRUST_BUNDLE_WRONG_USE = "spiffebundle_wrong_use.json"; + private static final String SPIFFE_TRUST_BUNDLE_WRONG_MULTI_CERTS = + "spiffebundle_wrong_multi_certs.json"; + private static final String SPIFFE_TRUST_BUNDLE_DUPLICATES = "spiffebundle_duplicates.json"; + private static final String SPIFFE_TRUST_BUNDLE_WRONG_ROOT = "spiffebundle_wrong_root.json"; + private static final String SPIFFE_TRUST_BUNDLE_WRONG_SEQ = "spiffebundle_wrong_seq_type.json"; + private static final String DOMAIN_ERROR_MESSAGE = + " Certificate loading for trust domain 'google.com' failed."; + + + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); + + private X509Certificate[] spiffeCert; + private X509Certificate[] multipleUriSanCert; + private X509Certificate[] serverCert0; + + @Before + public void setUp() throws Exception { + spiffeCert = CertificateUtils.getX509Certificates(TlsTesting.loadCert(SPIFFE_PEM_FILE)); + multipleUriSanCert = CertificateUtils.getX509Certificates(TlsTesting + .loadCert(MULTI_URI_SAN_PEM_FILE)); + serverCert0 = CertificateUtils.getX509Certificates(TlsTesting.loadCert(SERVER_0_PEM_FILE)); + } + + private String copyFileToTmp(String fileName) throws Exception { + Path tempFilePath = tempFolder.newFile(fileName).toPath(); + try (InputStream resourceStream = SpiffeUtilTest.class.getClassLoader() + .getResourceAsStream(TEST_DIRECTORY_PREFIX + fileName)) { + Files.copy(resourceStream, tempFilePath, StandardCopyOption.REPLACE_EXISTING); + } + return tempFilePath.toString(); + } + + @Test + public void extractSpiffeIdSuccessTest() throws Exception { + Optional spiffeId = SpiffeUtil.extractSpiffeId(spiffeCert); + assertTrue(spiffeId.isPresent()); + assertEquals("foo.bar.com", spiffeId.get().getTrustDomain()); + assertEquals("/client/workload/1", spiffeId.get().getPath()); + } + + @Test + public void extractSpiffeIdFailureTest() throws Exception { + Optional spiffeId = SpiffeUtil.extractSpiffeId(serverCert0); + assertFalse(spiffeId.isPresent()); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .extractSpiffeId(multipleUriSanCert)); + assertEquals("Multiple URI SAN values found in the leaf cert.", iae.getMessage()); + + } + + @Test + public void extractSpiffeIdFromChainTest() throws Exception { + // Check that the SPIFFE ID is extracted only from the leaf cert in the chain (spiffeCert + // contains it, but serverCert0 does not). + X509Certificate[] leafWithSpiffeChain = new X509Certificate[]{spiffeCert[0], serverCert0[0]}; + assertTrue(SpiffeUtil.extractSpiffeId(leafWithSpiffeChain).isPresent()); + X509Certificate[] leafWithoutSpiffeChain = + new X509Certificate[]{serverCert0[0], spiffeCert[0]}; + assertFalse(SpiffeUtil.extractSpiffeId(leafWithoutSpiffeChain).isPresent()); + } + + @Test + public void extractSpiffeIdParameterValidityTest() { + NullPointerException npe = assertThrows(NullPointerException.class, () -> SpiffeUtil + .extractSpiffeId(null)); + assertEquals("certChain", npe.getMessage()); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .extractSpiffeId(new X509Certificate[]{})); + assertEquals("certChain can't be empty", iae.getMessage()); + } + + @Test + public void loadTrustBundleFromFileSuccessTest() throws Exception { + SpiffeBundle tb = SpiffeUtil.loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE)); + assertEquals(2, tb.getSequenceNumbers().size()); + assertEquals(12035488L, (long) tb.getSequenceNumbers().get("example.com")); + assertEquals(-1L, (long) tb.getSequenceNumbers().get("test.example.com")); + assertEquals(3, tb.getBundleMap().size()); + assertEquals(0, tb.getBundleMap().get("test.google.com.au").size()); + assertEquals(1, tb.getBundleMap().get("example.com").size()); + assertEquals(2, tb.getBundleMap().get("test.example.com").size()); + Optional spiffeId = SpiffeUtil.extractSpiffeId(tb.getBundleMap().get("example.com") + .toArray(new X509Certificate[0])); + assertTrue(spiffeId.isPresent()); + assertEquals("foo.bar.com", spiffeId.get().getTrustDomain()); + } + + @Test + public void loadTrustBundleFromFileFailureTest() { + // Check the exception if JSON root element is different from 'trust_domains' + NullPointerException npe = assertThrows(NullPointerException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_WRONG_ROOT))); + assertEquals("Mandatory trust_domains element is missing", npe.getMessage()); + // Check the exception if JSON root element is different from 'trust_domains' + ClassCastException cce = assertThrows(ClassCastException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_WRONG_SEQ))); + assertTrue(cce.getMessage().contains("Number expected to be long")); + // Check the exception if JSON file doesn't contain an object + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_MALFORMED))); + assertTrue(iae.getMessage().contains("SPIFFE Trust Bundle should be a JSON object.")); + // Check the exception if JSON contains duplicates + iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_DUPLICATES))); + assertEquals("Duplicate key found: google.com", iae.getMessage()); + // Check the exception if 'x5c' value cannot be parsed + iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_CORRUPTED_CERT))); + assertEquals("Certificate can't be parsed." + DOMAIN_ERROR_MESSAGE, iae.getMessage()); + // Check the exception if 'kty' value differs from 'RSA' + iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_WRONG_KTY))); + assertEquals("'kty' parameter must be 'RSA' but 'null' found." + DOMAIN_ERROR_MESSAGE, + iae.getMessage()); + // Check the exception if 'kid' has a value + iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_WRONG_KID))); + assertEquals("'kid' parameter must not be set." + DOMAIN_ERROR_MESSAGE, iae.getMessage()); + // Check the exception if 'use' value differs from 'x509-svid' + iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_WRONG_USE))); + assertEquals("'use' parameter must be 'x509-svid' but 'i_am_not_x509-svid' found." + + DOMAIN_ERROR_MESSAGE, iae.getMessage()); + // Check the exception if multiple certs are provided for 'x5c' + iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_WRONG_MULTI_CERTS))); + assertEquals("Exactly 1 certificate is expected, but 2 found." + DOMAIN_ERROR_MESSAGE, + iae.getMessage()); + } + + @Test + public void loadTrustBundleFromFileParameterValidityTest() { + NullPointerException npe = assertThrows(NullPointerException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(null)); + assertEquals("trustBundleFile", npe.getMessage()); + NoSuchFileException nsfe = assertThrows(NoSuchFileException.class, () -> SpiffeUtil + .loadTrustBundleFromFile("i_do_not_exist")); + assertEquals("i_do_not_exist", nsfe.getMessage()); + } + } } \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle.json b/core/src/test/resources/io/grpc/internal/spiffebundle.json new file mode 100644 index 0000000000..f968f730d9 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle.json @@ -0,0 +1,115 @@ +{ + "trust_domains": { + "test.google.com.au": {}, + "example.com": { + "spiffe_sequence": 12035488, + "keys": [ + { + "kty": "RSA", + "use": "x509-svid", + "x5c": ["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 + GA91fn0891b5eEW8BJHXX0jri0aN8g=="], + "n": "", + "e": "AQAB" + } + ] + }, + "test.example.com": { + "keys": [ + { + "kty": "RSA", + "use": "x509-svid", + "x5c": ["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 + GA91fn0891b5eEW8BJHXX0jri0aN8g=="], + "n": "", + "e": "AQAB" + }, + { + "kty": "RSA", + "use": "x509-svid", + "x5c": ["MIIELTCCAxWgAwIBAgIUVXGlXjNENtOZbI12epjgIhMaShEwDQYJKoZIhvcNAQEL + BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM + GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0 + MDkxNzE2MTk0NFoXDTM0MDkxNTE2MTk0NFowTjELMAkGA1UEBhMCVVMxCzAJBgNV + BAgMAkNBMQwwCgYDVQQHDANTVkwxDTALBgNVBAoMBGdSUEMxFTATBgNVBAMMDHRl + c3QtY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOcTjjcS + SfG/EGrr6G+f+3T2GXyHHfroQFi9mZUz80L7uKBdECOImID+YhoK8vcxLQjPmEEv + FIYgJT5amugDcYIgUhMjBx/8RPJaP/nGmBngAqsuuNCaZfyaHBRqN8XdS/AwmsI5 + Wo+nru0+0/7aQFdqqtd2+e9dHjUWwgHxXvMgC4hkHpsdCGIZWVzWyBliwTYQYb1Y + yYe1LzqqQA5OMbZfKOY9MYDCEYOliRiunOn30iIOHj9V5qLzWGfSyxCRuvLRdEP8 + iDeNweHbdaKuI80nQmxuBdRIspE9k5sD1WA4vLZpeg3zggxp4rfLL5zBJgb/33D3 + d9Rkm14xfDPihhkCAwEAAaOB+jCB9zBZBgNVHREEUjBQhiZzcGlmZmU6Ly9mb28u + YmFyLmNvbS9jbGllbnQvd29ya2xvYWQvMYYmc3BpZmZlOi8vZm9vLmJhci5jb20v + Y2xpZW50L3dvcmtsb2FkLzIwHQYDVR0OBBYEFG9GkBgdBg/p0U9/lXv8zIJ+2c2N + MHsGA1UdIwR0MHKhWqRYMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 + YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMM + BnRlc3RjYYIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQELBQADggEB + AJ4Cbxv+02SpUgkEu4hP/1+8DtSBXUxNxI0VG4e3Ap2+Rhjm3YiFeS/UeaZhNrrw + UEjkSTPFODyXR7wI7UO9OO1StyD6CMkp3SEvevU5JsZtGL6mTiTLTi3Qkywa91Bt + GlyZdVMghA1bBJLBMwiD5VT5noqoJBD7hDy6v9yNmt1Sw2iYBJPqI3Gnf5bMjR3s + UICaxmFyqaMCZsPkfJh0DmZpInGJys3m4QqGz6ZE2DWgcSr1r/ML7/5bSPjjr8j4 + WFFSqFR3dMu8CbGnfZTCTXa4GTX/rARXbAO67Z/oJbJBK7VKayskL+PzKuohb9ox + jGL772hQMbwtFCOFXu5VP0s="] + } + ] + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_corrupted_cert.json b/core/src/test/resources/io/grpc/internal/spiffebundle_corrupted_cert.json new file mode 100644 index 0000000000..9ca51733ff --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_corrupted_cert.json @@ -0,0 +1,14 @@ +{ + "trust_domains": { + "google.com": { + "spiffe_sequence": 123, + "keys": [ + { + "kty": "RSA", + "use": "x509-svid", + "x5c": ["UNPARSABLE_CERTIFICATE"] + } + ] + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_duplicates.json b/core/src/test/resources/io/grpc/internal/spiffebundle_duplicates.json new file mode 100644 index 0000000000..3f015bd156 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_duplicates.json @@ -0,0 +1,23 @@ +{ + "trust_domains": { + "google.com": { + "spiffe_sequence": 123, + "keys": [ + { + "x5c": "VALUE_DOESN'T_MATTER" + } + ] + }, + "google.com": { + "spiffe_sequence": 123, + "keys": [ + { + "use": "x509-svid", + "kid": "some_value", + "x5c": "VALUE_DOESN'T_MATTER" + } + ] + }, + "test.google.com.au": {} + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_malformed.json b/core/src/test/resources/io/grpc/internal/spiffebundle_malformed.json new file mode 100644 index 0000000000..a2488eeb3c --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_malformed.json @@ -0,0 +1,4 @@ +[ + "test.google.com", + "test.google.com.au" +] \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kid.json b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kid.json new file mode 100644 index 0000000000..f93af634a5 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kid.json @@ -0,0 +1,15 @@ +{ + "trust_domains": { + "google.com": { + "spiffe_sequence": 123, + "keys": [ + { + "kty": "RSA", + "use": "x509-svid", + "kid": "some_value", + "x5c": "VALUE_DOESN'T_MATTER" + } + ] + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kty.json b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kty.json new file mode 100644 index 0000000000..384da03fd6 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_kty.json @@ -0,0 +1,12 @@ +{ + "trust_domains": { + "google.com": { + "spiffe_sequence": 123, + "keys": [ + { + "x5c": "VALUE_DOESN'T_MATTER" + } + ] + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_multi_certs.json b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_multi_certs.json new file mode 100644 index 0000000000..5e85635bb0 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_multi_certs.json @@ -0,0 +1,67 @@ +{ + "trust_domains": { + "google.com": { + "spiffe_sequence": 123, + "keys": [ + { + "kty": "RSA", + "use": "x509-svid", + "x5c": ["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 + GA91fn0891b5eEW8BJHXX0jri0aN8g==", + "MIIELTCCAxWgAwIBAgIUVXGlXjNENtOZbI12epjgIhMaShEwDQYJKoZIhvcNAQEL + BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM + GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0 + MDkxNzE2MTk0NFoXDTM0MDkxNTE2MTk0NFowTjELMAkGA1UEBhMCVVMxCzAJBgNV + BAgMAkNBMQwwCgYDVQQHDANTVkwxDTALBgNVBAoMBGdSUEMxFTATBgNVBAMMDHRl + c3QtY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOcTjjcS + SfG/EGrr6G+f+3T2GXyHHfroQFi9mZUz80L7uKBdECOImID+YhoK8vcxLQjPmEEv + FIYgJT5amugDcYIgUhMjBx/8RPJaP/nGmBngAqsuuNCaZfyaHBRqN8XdS/AwmsI5 + Wo+nru0+0/7aQFdqqtd2+e9dHjUWwgHxXvMgC4hkHpsdCGIZWVzWyBliwTYQYb1Y + yYe1LzqqQA5OMbZfKOY9MYDCEYOliRiunOn30iIOHj9V5qLzWGfSyxCRuvLRdEP8 + iDeNweHbdaKuI80nQmxuBdRIspE9k5sD1WA4vLZpeg3zggxp4rfLL5zBJgb/33D3 + d9Rkm14xfDPihhkCAwEAAaOB+jCB9zBZBgNVHREEUjBQhiZzcGlmZmU6Ly9mb28u + YmFyLmNvbS9jbGllbnQvd29ya2xvYWQvMYYmc3BpZmZlOi8vZm9vLmJhci5jb20v + Y2xpZW50L3dvcmtsb2FkLzIwHQYDVR0OBBYEFG9GkBgdBg/p0U9/lXv8zIJ+2c2N + MHsGA1UdIwR0MHKhWqRYMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 + YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMM + BnRlc3RjYYIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQELBQADggEB + AJ4Cbxv+02SpUgkEu4hP/1+8DtSBXUxNxI0VG4e3Ap2+Rhjm3YiFeS/UeaZhNrrw + UEjkSTPFODyXR7wI7UO9OO1StyD6CMkp3SEvevU5JsZtGL6mTiTLTi3Qkywa91Bt + GlyZdVMghA1bBJLBMwiD5VT5noqoJBD7hDy6v9yNmt1Sw2iYBJPqI3Gnf5bMjR3s + UICaxmFyqaMCZsPkfJh0DmZpInGJys3m4QqGz6ZE2DWgcSr1r/ML7/5bSPjjr8j4 + WFFSqFR3dMu8CbGnfZTCTXa4GTX/rARXbAO67Z/oJbJBK7VKayskL+PzKuohb9ox + jGL772hQMbwtFCOFXu5VP0s="] + } + ] + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_root.json b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_root.json new file mode 100644 index 0000000000..90d2847dc0 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_root.json @@ -0,0 +1,6 @@ +{ + "trustDomains": { + "test.google.com": {}, + "test.google.com.au": {} + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_seq_type.json b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_seq_type.json new file mode 100644 index 0000000000..4e0aeacc89 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_seq_type.json @@ -0,0 +1,12 @@ +{ + "trust_domains": { + "google.com": { + "spiffe_sequence": 123.5, + "keys": [ + { + "x5c": "VALUE_DOESN'T_MATTER" + } + ] + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_use.json b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_use.json new file mode 100644 index 0000000000..166be04846 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_wrong_use.json @@ -0,0 +1,13 @@ +{ + "trust_domains": { + "google.com": { + "keys": [ + { + "kty": "RSA", + "use": "i_am_not_x509-svid", + "x5c": "VALUE_DOESN'T_MATTER" + } + ] + } + } +} \ No newline at end of file diff --git a/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java b/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java index a9e0d6e223..f409a149bf 100644 --- a/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java +++ b/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java @@ -108,8 +108,7 @@ public class ObservabilityConfigImplTest { private static final String PROJECT_ID = "{\n" + " \"project_id\": \"grpc-testing\",\n" - + " \"cloud_logging\": {},\n" - + " \"project_id\": \"grpc-testing\"\n" + + " \"cloud_logging\": {}\n" + "}"; private static final String EMPTY_CONFIG = "{}"; diff --git a/testing/src/main/resources/certs/spiffe-openssl.cnf b/testing/src/main/resources/certs/spiffe-openssl.cnf new file mode 100644 index 0000000000..40c473da7e --- /dev/null +++ b/testing/src/main/resources/certs/spiffe-openssl.cnf @@ -0,0 +1,12 @@ +[spiffe_client] +subjectAltName = @alt_names + +[spiffe_client_multi] +subjectAltName = @alt_names_multi + +[alt_names] +URI = spiffe://foo.bar.com/client/workload/1 + +[alt_names_multi] +URI.1 = spiffe://foo.bar.com/client/workload/1 +URI.2 = spiffe://foo.bar.com/client/workload/2 \ No newline at end of file diff --git a/testing/src/main/resources/certs/spiffe_cert.pem b/testing/src/main/resources/certs/spiffe_cert.pem new file mode 100644 index 0000000000..bc070042f6 --- /dev/null +++ b/testing/src/main/resources/certs/spiffe_cert.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFsjCCA5qgAwIBAgIURygVMMzdr+Q7rsUaz189JozyHMwwDQYJKoZIhvcNAQEL +BQAwTjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxDTAL +BgNVBAoMBGdSUEMxFTATBgNVBAMMDHRlc3QtY2xpZW50MTAeFw0yMTEyMjMxODQy +NTJaFw0zMTEyMjExODQyNTJaME4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEM +MAoGA1UEBwwDU1ZMMQ0wCwYDVQQKDARnUlBDMRUwEwYDVQQDDAx0ZXN0LWNsaWVu +dDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ4AqpGetyVSqGUuBJ +LVFla+7bEfca7UYzfVSSZLZ/X+JDmWIVN8UIPuFib5jhMEc3XaUnFXUmM7zEtz/Z +G5hapwLwOb2C3ZxOP6PQjYCJxbkLie+b43UQrFu1xxd3vMhVJgcj/AIxEpmszuqO +a6kUrkYifjJADQ+64kZgl66bsTdXMCzpxyFl9xUfff59L8OX+HUfAcoZz3emjg3Z +JPYURQEmjdZTOau1EjFilwHgd989Jt7NKgx30NXoHmw7nusVBIY94fL2VKN3f1XV +m0dHu5NI279Q6zr0ZBU7k5T3IeHnzsUesQS4NGlklDWoVTKk73Uv9Pna8yQsSW75 +7PEbHOGp9Knu4bnoGPOlsG81yIPipO6hTgGFK24pF97M9kpGbWqYX4+2vLlrCAfc +msHqaUPmQlYeRVTT6vw7ctYo2kyUYGtnODXk76LqewRBVvkzx75QUhfjAyb740Yc +DmIenc56Tq6gebJHjhEmVSehR6xIpXP7SVeurTyhPsEQnpJHtgs4dcwWOZp7BvPN +zHXmJqfr7vsshie3vS5kQ0u1e1yqAqXgyDjqKXOkx+dpgUTehSJHhPNHvTc5LXRs +vvXKYz6FrwR/DZ8t7BNEvPeLjFgxpH7QVJFLCvCbXs5K6yYbsnLfxFIBPRnrbJkI +sK+sQwnRdnsiUdPsTkG5B2lQfQIDAQABo4GHMIGEMB0GA1UdDgQWBBQ2lBp0PiRH +HvQ5IRURm8aHsj4RETAfBgNVHSMEGDAWgBQ2lBp0PiRHHvQ5IRURm8aHsj4RETAP +BgNVHRMBAf8EBTADAQH/MDEGA1UdEQQqMCiGJnNwaWZmZTovL2Zvby5iYXIuY29t +L2NsaWVudC93b3JrbG9hZC8xMA0GCSqGSIb3DQEBCwUAA4ICAQA1mSkgRclAl+E/ +aS9zJ7t8+Y4n3T24nOKKveSIjxXm/zjhWqVsLYBI6kglWtih2+PELvU8JdPqNZK3 +4Kl0Q6FWpVSGDdWN1i6NyORt2ocggL3ke3iXxRk3UpUKJmqwz81VhA2KUHnMlyE0 +IufFfZNwNWWHBv13uJfRbjeQpKPhU+yf4DeXrsWcvrZlGvAET+mcplafUzCp7Iv+ +PcISJtUerbxbVtuHVeZCLlgDXWkLAWJN8rf0dIG4x060LJ+j6j9uRVhb9sZn1HJV ++j4XdIYm1VKilluhOtNwP2d3Ox/JuTBxf7hFHXZPfMagQE5k5PzmxRaCAEMJ1l2D +vUbZw+shJfSNoWcBo2qadnUaWT3BmmJRBDh7ZReib/RQ1Rd4ygOyzP3E0vkV4/gq +yjLdApXh5PZP8KLQZ+1JN/sdWt7VfIt9wYOpkIqujdll51ESHzwQeAK9WVCB4UvV +z6zdhItB9CRbXPreWC+wCB1xDovIzFKOVsLs5+Gqs1m7VinG2LxbDqaKyo/FB0Hx +x0acBNzezLWoDwXYQrN0T0S4pnqhKD1CYPpdArBkNezUYAjS725FkApuK+mnBX3U +0msBffEaUEOkcyar1EW2m/33vpetD/k3eQQkmvQf4Hbiu9AF+9cNDm/hMuXEw5EX +GA91fn0891b5eEW8BJHXX0jri0aN8g== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/testing/src/main/resources/certs/spiffe_multi_uri_san_cert.pem b/testing/src/main/resources/certs/spiffe_multi_uri_san_cert.pem new file mode 100644 index 0000000000..eb5c879abf --- /dev/null +++ b/testing/src/main/resources/certs/spiffe_multi_uri_san_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIELTCCAxWgAwIBAgIUVXGlXjNENtOZbI12epjgIhMaShEwDQYJKoZIhvcNAQEL +BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0 +MDkxNzE2MTk0NFoXDTM0MDkxNTE2MTk0NFowTjELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMQwwCgYDVQQHDANTVkwxDTALBgNVBAoMBGdSUEMxFTATBgNVBAMMDHRl +c3QtY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOcTjjcS +SfG/EGrr6G+f+3T2GXyHHfroQFi9mZUz80L7uKBdECOImID+YhoK8vcxLQjPmEEv +FIYgJT5amugDcYIgUhMjBx/8RPJaP/nGmBngAqsuuNCaZfyaHBRqN8XdS/AwmsI5 +Wo+nru0+0/7aQFdqqtd2+e9dHjUWwgHxXvMgC4hkHpsdCGIZWVzWyBliwTYQYb1Y +yYe1LzqqQA5OMbZfKOY9MYDCEYOliRiunOn30iIOHj9V5qLzWGfSyxCRuvLRdEP8 +iDeNweHbdaKuI80nQmxuBdRIspE9k5sD1WA4vLZpeg3zggxp4rfLL5zBJgb/33D3 +d9Rkm14xfDPihhkCAwEAAaOB+jCB9zBZBgNVHREEUjBQhiZzcGlmZmU6Ly9mb28u +YmFyLmNvbS9jbGllbnQvd29ya2xvYWQvMYYmc3BpZmZlOi8vZm9vLmJhci5jb20v +Y2xpZW50L3dvcmtsb2FkLzIwHQYDVR0OBBYEFG9GkBgdBg/p0U9/lXv8zIJ+2c2N +MHsGA1UdIwR0MHKhWqRYMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 +YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMM +BnRlc3RjYYIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQELBQADggEB +AJ4Cbxv+02SpUgkEu4hP/1+8DtSBXUxNxI0VG4e3Ap2+Rhjm3YiFeS/UeaZhNrrw +UEjkSTPFODyXR7wI7UO9OO1StyD6CMkp3SEvevU5JsZtGL6mTiTLTi3Qkywa91Bt +GlyZdVMghA1bBJLBMwiD5VT5noqoJBD7hDy6v9yNmt1Sw2iYBJPqI3Gnf5bMjR3s +UICaxmFyqaMCZsPkfJh0DmZpInGJys3m4QqGz6ZE2DWgcSr1r/ML7/5bSPjjr8j4 +WFFSqFR3dMu8CbGnfZTCTXa4GTX/rARXbAO67Z/oJbJBK7VKayskL+PzKuohb9ox +jGL772hQMbwtFCOFXu5VP0s= +-----END CERTIFICATE----- \ No newline at end of file