-
Notifications
You must be signed in to change notification settings - Fork 7
Add first-class mutual TLS support to AxonServerConnectionFactory #501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| package io.axoniq.axonserver.connector; | ||
|
abuijze marked this conversation as resolved.
|
||
|
|
||
| import io.axoniq.axonserver.connector.impl.ServerAddress; | ||
| import io.axoniq.axonserver.grpc.control.ClientIdentification; | ||
| import io.axoniq.axonserver.grpc.control.PlatformInboundInstruction; | ||
| import io.axoniq.axonserver.grpc.control.PlatformInfo; | ||
| import io.axoniq.axonserver.grpc.control.PlatformOutboundInstruction; | ||
| import io.axoniq.axonserver.grpc.control.PlatformServiceGrpc; | ||
| import io.grpc.Server; | ||
| import io.grpc.TlsServerCredentials; | ||
| import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder; | ||
| import io.grpc.stub.StreamObserver; | ||
| import org.junit.jupiter.api.AfterEach; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.net.ServerSocket; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.function.UnaryOperator; | ||
|
|
||
| import static io.axoniq.axonserver.connector.testutils.AssertUtils.assertWithin; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| /** | ||
| * Test class validating the (mutual) TLS configuration options of the {@link AxonServerConnectionFactory}, against a | ||
| * stub PlatformService endpoint secured with TLS. | ||
| * | ||
| * @author Allard Buijze | ||
| */ | ||
| class MutualTlsConnectionTest { | ||
|
|
||
| private Server server; | ||
| private int serverPort; | ||
| private AxonServerConnectionFactory connectionFactory; | ||
|
|
||
| @AfterEach | ||
| void tearDown() { | ||
| if (connectionFactory != null) { | ||
| connectionFactory.shutdown(); | ||
| } | ||
| if (server != null) { | ||
| server.shutdownNow(); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| void connectsWithClientCertificateWhenServerRequiresClientAuth() throws Exception { | ||
| startServer(TlsServerCredentials.ClientAuth.REQUIRE); | ||
|
|
||
| AxonServerConnection connection = connect( | ||
| factory -> factory.useMutualTransportSecurity(tlsFile("client.crt"), | ||
| tlsFile("client.key"), | ||
| tlsFile("ca.crt")) | ||
| ); | ||
|
|
||
| assertWithin(10, TimeUnit.SECONDS, () -> assertTrue(connection.isConnected())); | ||
| } | ||
|
|
||
| @Test | ||
| void failsToConnectWithoutClientCertificateWhenServerRequiresClientAuth() throws Exception { | ||
| startServer(TlsServerCredentials.ClientAuth.REQUIRE); | ||
|
|
||
| AxonServerConnection connection = connect( | ||
| factory -> factory.useTransportSecurity(tlsFile("ca.crt")) | ||
| ); | ||
|
|
||
| assertWithin(10, TimeUnit.SECONDS, () -> assertTrue(connection.isConnectionFailed())); | ||
| assertFalse(connection.isConnected()); | ||
| } | ||
|
|
||
| @Test | ||
| void connectsWithTrustedCertificatesOnlyWhenServerDoesNotRequireClientAuth() throws Exception { | ||
| startServer(TlsServerCredentials.ClientAuth.NONE); | ||
|
|
||
| AxonServerConnection connection = connect( | ||
| factory -> factory.useTransportSecurity(tlsFile("ca.crt")) | ||
| ); | ||
|
|
||
| assertWithin(10, TimeUnit.SECONDS, () -> assertTrue(connection.isConnected())); | ||
| } | ||
|
|
||
| @Test | ||
| void mutualTransportSecurityRejectsMissingCertificateOrKey() { | ||
| AxonServerConnectionFactory.Builder builder = AxonServerConnectionFactory.forClient("mtls-test"); | ||
|
|
||
| assertThrows(IllegalArgumentException.class, | ||
|
Check warning on line 89 in src/test/java/io/axoniq/axonserver/connector/MutualTlsConnectionTest.java
|
||
| () -> builder.useMutualTransportSecurity(null, tlsFile("client.key"))); | ||
| assertThrows(IllegalArgumentException.class, | ||
|
Check warning on line 91 in src/test/java/io/axoniq/axonserver/connector/MutualTlsConnectionTest.java
|
||
| () -> builder.useMutualTransportSecurity(tlsFile("client.crt"), null)); | ||
| assertThrows(IllegalArgumentException.class, | ||
|
Check warning on line 93 in src/test/java/io/axoniq/axonserver/connector/MutualTlsConnectionTest.java
|
||
| () -> builder.useMutualTransportSecurity(new File("does-not-exist.crt"), | ||
| new File("does-not-exist.key"))); | ||
| } | ||
|
|
||
| private void startServer(TlsServerCredentials.ClientAuth clientAuth) throws Exception { | ||
| TlsServerCredentials.Builder credentials = TlsServerCredentials.newBuilder() | ||
| .keyManager(tlsFile("server.crt"), | ||
| tlsFile("server.key")) | ||
| .clientAuth(clientAuth); | ||
| if (clientAuth != TlsServerCredentials.ClientAuth.NONE) { | ||
| credentials.trustManager(tlsFile("ca.crt")); | ||
| } | ||
| server = NettyServerBuilder.forPort(freePort(), credentials.build()) | ||
| .addService(new StubPlatformService()) | ||
| .build() | ||
| .start(); | ||
| } | ||
|
|
||
| private AxonServerConnection connect(UnaryOperator<AxonServerConnectionFactory.Builder> tlsConfig) { | ||
| connectionFactory = tlsConfig.apply(AxonServerConnectionFactory.forClient("mtls-test") | ||
| .routingServers(new ServerAddress("localhost", | ||
| serverPort))) | ||
| .build(); | ||
| return connectionFactory.connect("default"); | ||
| } | ||
|
|
||
| private int freePort() throws IOException { | ||
| try (ServerSocket socket = new ServerSocket(0)) { | ||
| serverPort = socket.getLocalPort(); | ||
| return serverPort; | ||
| } | ||
| } | ||
|
|
||
| private File tlsFile(String name) { | ||
| try { | ||
| return new File(getClass().getResource("/tls/" + name).toURI()); | ||
| } catch (Exception e) { | ||
| throw new IllegalStateException("Could not locate test resource /tls/" + name, e); | ||
| } | ||
| } | ||
|
|
||
| private static class StubPlatformService extends PlatformServiceGrpc.PlatformServiceImplBase { | ||
|
|
||
| @Override | ||
| public void getPlatformServer(ClientIdentification request, StreamObserver<PlatformInfo> responseObserver) { | ||
| responseObserver.onNext(PlatformInfo.newBuilder().setSameConnection(true).build()); | ||
| responseObserver.onCompleted(); | ||
| } | ||
|
|
||
| @Override | ||
| public StreamObserver<PlatformInboundInstruction> openStream( | ||
| StreamObserver<PlatformOutboundInstruction> responseObserver) { | ||
| return new StreamObserver<PlatformInboundInstruction>() { | ||
| @Override | ||
| public void onNext(PlatformInboundInstruction value) { | ||
| // no-op: the stub only needs to keep the stream open | ||
| } | ||
|
|
||
| @Override | ||
| public void onError(Throwable t) { | ||
| // no-op | ||
| } | ||
|
|
||
| @Override | ||
| public void onCompleted() { | ||
| responseObserver.onCompleted(); | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| -----BEGIN CERTIFICATE----- | ||
| MIIDFzCCAf+gAwIBAgIULHat2vB2ieUzjtCWRdRkIXXWe00wDQYJKoZIhvcNAQEL | ||
| BQAwGjEYMBYGA1UEAwwPVGVzdCBUcnVzdGVkIENBMCAXDTI2MDcxNDA4MzU1M1oY | ||
| DzIxMjYwNjIwMDgzNTUzWjAaMRgwFgYDVQQDDA9UZXN0IFRydXN0ZWQgQ0EwggEi | ||
| MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo3ODm78QSpgh4Sy5S/USAbMqT | ||
| M6XaNe41+jGwoHrBaFaeCEXADtSSE8O/oFHrrbWnbFxpdMSrXHVCicnl2kPJxQzV | ||
| ezo5NuPDEdjKSfjA3Y3uCo71EBP3iUmUZdk9um2gX+t99dIIIq+A0stZs10IWdpr | ||
| NTPPelO5qtqO2m/3/aUjKDj9f47UfTaxi6aNseAlrll7ccNI2Csz9x+2zpftsq5y | ||
| kCI/AD2ZOkX+1dqi44sVGU6+x/QsFQB3QVRKsdgXIBp8CVzpt4PkzPzlKNq0WiEt | ||
| cAcv5kxkDe/e4ZBBnf5l+V4TxHF/z8LPwp8Mr47OpYtGCma25q+jPtK+quUxAgMB | ||
| AAGjUzBRMB0GA1UdDgQWBBQHS4hLKOqr4vBgRc8WoyJSVKLiBDAfBgNVHSMEGDAW | ||
| gBQHS4hLKOqr4vBgRc8WoyJSVKLiBDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 | ||
| DQEBCwUAA4IBAQBTkMW0nxZM5+12JyzEbXdLW+Az9LGc9YdlLz/0YAaGYXremQXy | ||
| pVun2qx730vwkS8nTvat0WPPXfIR8QzO6WTf/BNB4drWN9znqYW58KY78tnSUjNb | ||
| uf707QeHCbICH1z+tErQf48JU6HeOLPvXvyvxc4RZAm4kavWbzsKDr8IJfMSh9RC | ||
| Aiv0zE/RUHL1wmd6w6UQqmzbzjqi3mv+h0GdRPrTlpus7GngTN8R5Bu0s05ONtSy | ||
| fqMTqdQthxdt1jWlnacPrn/LZ/ldAamqecazgBXuKNzqfjfvAOzCgtVl4KWmqjKT | ||
| xwyCT49G9g5taftkgR+9NTFKf/UWYbUUY6YQ | ||
| -----END CERTIFICATE----- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| -----BEGIN PRIVATE KEY----- | ||
| MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCo3ODm78QSpgh4 | ||
| Sy5S/USAbMqTM6XaNe41+jGwoHrBaFaeCEXADtSSE8O/oFHrrbWnbFxpdMSrXHVC | ||
| icnl2kPJxQzVezo5NuPDEdjKSfjA3Y3uCo71EBP3iUmUZdk9um2gX+t99dIIIq+A | ||
| 0stZs10IWdprNTPPelO5qtqO2m/3/aUjKDj9f47UfTaxi6aNseAlrll7ccNI2Csz | ||
| 9x+2zpftsq5ykCI/AD2ZOkX+1dqi44sVGU6+x/QsFQB3QVRKsdgXIBp8CVzpt4Pk | ||
| zPzlKNq0WiEtcAcv5kxkDe/e4ZBBnf5l+V4TxHF/z8LPwp8Mr47OpYtGCma25q+j | ||
| PtK+quUxAgMBAAECggEAHuunMRJpbGLDvUdC+cZPo1aId5DnB0CF2JNyQt337G9v | ||
| IOvx3bu5d/AvkWjJZ2b9EuW4PEU5+Ajrnd3+Pt0LOaif831niW+AIEml0VDfQG2x | ||
| XgvH5p7lkReuBgLqVy/KgCngRMXdCklTqrquqsO5vtw5LxVkYdML/r0G3U6pXxbi | ||
| V0aHp/8FCZEUbIte0JeZgIUjnCHg1O80JDCMHtYhyIy3VOauTbeAVfcYGBPBOVpo | ||
| YwxOhZpBzaoDYZze0e9+6Jtcu01uId85HvAULRoMDoUmNQOBD2I96ke3sRhB1Ac+ | ||
| 6quwq3K+dzjla9zqjpLnP8W6AUyhAwQhQxICK05fOwKBgQDWLTNQ67w04G9RyS7A | ||
| 2JZQw2KnES0sswvL1HcjNkZ8PiFG5GWAN9YBNE7XiDj9EIOJdniEumav/9BCC4Td | ||
| JmSH2/s+Xp8fxIZonz27BIpHJaxWzsY8PXrnZx3NsAKZptX2bJq13hb57UDv7aZl | ||
| pgziYNMF4HEw09yHUTgDOpuJuwKBgQDJ1mtd9mv7z7j9wd90cZIqnhvd5/rzC0EH | ||
| BC6xQ9idPpofELBXHw0CZFsYTEVlZKVoBC1TZmWGLVxDCr4sZaWvtn45kZ66jWHv | ||
| yDdwnOqJsIzm1Z5+43WCpnxHEiXdAqBKPAVMglviCx8EcZMMMptA7lnQ8faN/J0/ | ||
| 20NbjoBYAwKBgDj9uBz9TYyIeDa8IQvB1mXRSAwKvwmY7zei5rzg8WqYIcS6e+Pd | ||
| d73ETIJ/xiJY5ZeLhdBTxYVpveIFLKqoT7HzMGzNQuxyIA3w6b09HQqHlM9YoFlh | ||
| RrYSs1SLVHXMdjznc1eXrGd+4Xu2skN0JxOKDj0PFy+WAKWlUBFT5BWHAoGABL+8 | ||
| yvsvUz9x9NaI8W5yp/oMYc5Bg1Jydz3L83PLNvfwHGcCHyql3baKximVQGt70NS4 | ||
| VEwGe3+5ugIYs2a43UeH9MbOW0lUUpX9Z/LpOdAjoJLKJeYoL6jHJ8zvgyG6r8R1 | ||
| w3UVwF3OnwYlFZZjPMXe6yGmUXTuJkB8dz0FHA0CgYEAzzbTyQpTAX9aMb1/Ij0W | ||
| 1hwc2eafCXmy4e5VkKf5YcVKDZMcSvkicC4VAlUxWOSQ/ZwXmv4mTamnlbXS2oW2 | ||
| Rdan6oD2FAUG5aI8LXyG1bfuJcp3dWjgfrfJB0cS134jXk+XKMdNvsi4WsV60hJa | ||
| Ksmu2ROmTUXC2meV+93V3Eg= | ||
| -----END PRIVATE KEY----- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| -----BEGIN CERTIFICATE----- | ||
| MIICuTCCAaECFGQlQgq3PBbtJnTpsXCsSIiYtvhYMA0GCSqGSIb3DQEBCwUAMBox | ||
| GDAWBgNVBAMMD1Rlc3QgVHJ1c3RlZCBDQTAgFw0yNjA3MTQwODM1NTRaGA8yMTI2 | ||
| MDYyMDA4MzU1NFowFjEUMBIGA1UEAwwLdGVzdC1jbGllbnQwggEiMA0GCSqGSIb3 | ||
| DQEBAQUAA4IBDwAwggEKAoIBAQCVBtGyAy86lzCzmhqyZlmECQJZVHgPlT2Y4cRG | ||
| MUupbFCH1QOjqsV7nuMoW7EQYPU3Xs9aBrabij3BsbRCfxcZHNGVxjEAYdjPyqi1 | ||
| uMGcLvNrbj4XM6CD/kV4QZpj6wHX2yCO1onTvGK6f40MfoTbuFAqPS6RngO6fmH9 | ||
| ODuXawTH6o4bpYMKwUeK9HFpOtJvFQW+MePTmU4J7ZPhPs/8WZW8DmLV5TVZzZ6Y | ||
| dpYHLr15UHN7E6O4BRoHF8oLXPOoNBTq+xx1UFFY3XrJP4Xr9VVQQVQnO3V4Kv3I | ||
| mAPTrS/h9tn6QFQbxkYT8uUt/m6D5zgqvQCjSMWc/A4P2QXVAgMBAAEwDQYJKoZI | ||
| hvcNAQELBQADggEBAH9HhGR+SDDHknn9kkV4BEl4uWZIHW18ppfEfzBPllhA55Y+ | ||
| ZqboPvuzoRbuYntGFL/P9Ws3svmoIDUfVQPDMmjm4Kujnjb1jDyY5bmDdQs2RjDH | ||
| HJnXPqxA0+mlNXrpbsstqqZdFa1WxeT+lqTgALrNT9teBnA4cBczoQFGHwH+ltRs | ||
| ZYaycFa4blgclOL75oScbiQigB1fWPY7QGMnEwQLGN2I6HhKYyy/dFua6wTLGIeG | ||
| 3ifbYec4T/lwLOeT+kVQz8iDTN3fn75YnUortUdkZ0j5mVFxsx5oOKXj7RYdF0GA | ||
| I223lYDUCxGRnV+euTCPYTWFYOsERXIUt3iM+sk= | ||
| -----END CERTIFICATE----- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| -----BEGIN PRIVATE KEY----- | ||
| MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVBtGyAy86lzCz | ||
| mhqyZlmECQJZVHgPlT2Y4cRGMUupbFCH1QOjqsV7nuMoW7EQYPU3Xs9aBrabij3B | ||
| sbRCfxcZHNGVxjEAYdjPyqi1uMGcLvNrbj4XM6CD/kV4QZpj6wHX2yCO1onTvGK6 | ||
| f40MfoTbuFAqPS6RngO6fmH9ODuXawTH6o4bpYMKwUeK9HFpOtJvFQW+MePTmU4J | ||
| 7ZPhPs/8WZW8DmLV5TVZzZ6YdpYHLr15UHN7E6O4BRoHF8oLXPOoNBTq+xx1UFFY | ||
| 3XrJP4Xr9VVQQVQnO3V4Kv3ImAPTrS/h9tn6QFQbxkYT8uUt/m6D5zgqvQCjSMWc | ||
| /A4P2QXVAgMBAAECggEAAWnSF84PBtwTuDeKlmB0a8iASloYrs2NMVokyyVt1wXB | ||
| 6F17qxpakWu5ZX+j7BvLTF+v3j+PJrL9MNoI85Kyfju6fIeBNyrczEqBZpw06kPW | ||
| VBGOPhiW5pTFBZkaFOWcu+my9mY8/BilJA+QUvFcb7Wg1wG2o4CcGIrymxAk0PCm | ||
| 1WeKctnS9U8W6HWSxn7cWudP2kjSBw8QjAUCocMdOUAvixuXM7EGLgdjHJ5cUPQA | ||
| 8IDiicYuZrYhYx0s3iHNL8M2dR5jW1ElphOL3nmhm78Ix406JC/s1OhFerFVLW6V | ||
| nk5NbWhklthN8NsFEBUdvz+0k8Z8T0AUUJH7Ib3AIQKBgQDN//00KBpXfboAecC3 | ||
| Ah91RuJSONvXWrGTcCEEODpnO1bkqLui6v5XamTRtlE7Iu5C2USckTLBlF6mzV7J | ||
| yEZyMJkrqm4i/AtyFkQ+4/IO9Matq/lKOMxn/1pChhMl2cMg8HQ+t5I+Ef4BHLkb | ||
| nCJWaZLgEg6IMA7iUZdjCgYU4QKBgQC5MryLB40ks0BQqhObuyKCd4SUQtmob85V | ||
| ovCO7iGYphH0O/DIFH2FgTSj8NcwDRS4cchmDnclHOctNZQFt2ShZvc5blUNXXdM | ||
| r3AsDOItrpJrrcBz8RVmi/yfRGif9m3WUJUlz33BAwz/EREBHCepKJRMHmwAFfuG | ||
| JkO86NXbdQKBgF92yT15dDOldSQxSCjHWBK+RbW62c5kHjToWrSc5hve+PbAZywp | ||
| 4LJcANrataxFFMtv6obwFuh0vKRqOgoiwNq8QM9mjOGzkg5N2VSXyB48OzJSpxVJ | ||
| Wxi60nZPseHxl6bVJ9nEsoYHSoHzcKkl4xfOlx014Pwl4U/qZdCk3YqhAoGAbjg/ | ||
| 9g58dgcok9lk6h4pn7Q593MBzzkbd+QNf0NCQ3My5ER4PNvVCIupJJjFuRdP6aXu | ||
| Oq8JQdE91K46dlx7S4PYRxnfDKE/yyfufoj8Y2uuQN+b1mT485sEslUoX2tuW0qC | ||
| OYqPsTt7lKI3mI2FHSvFdrf0Vui+VUyYq+l2vbECgYEAxVPi1MdZTQqxqspKC+io | ||
| rD0E9klYnlTvtx1D8Of3v5ZgUfDndbwMSnm+eB0eDZToJol2xibT9Z9JVVXGo4cq | ||
| 9yjQknrMOoe1uz4q2QB5d+t9+WxuXPSIy3hpx6YkR0ZBiOtmMu1IpuVC41i4gHJp | ||
| 1M4ZQJG7uuAmcvTVpbGvxCQ= | ||
| -----END PRIVATE KEY----- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| -----BEGIN CERTIFICATE----- | ||
| MIIDHDCCAgSgAwIBAgIUZCVCCrc8Fu0mdOmxcKxIiJi2+FcwDQYJKoZIhvcNAQEL | ||
| BQAwGjEYMBYGA1UEAwwPVGVzdCBUcnVzdGVkIENBMCAXDTI2MDcxNDA4MzU1M1oY | ||
| DzIxMjYwNjIwMDgzNTUzWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqG | ||
| SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBXA8g/LiGaI7WfMOAYW7q1t801MfoCe30 | ||
| pegYXVYFyeoUyVbuCo4sffa6/y56bw4aP152eCJjPIpcI5263HmQMrGFGhnpHOJo | ||
| WSpL0lVKCKCAoiQcPKSkTJQjzFZEWxSJJRgFy+766a7k4OBw9NXwnnaqzlv/A2SR | ||
| e8u0dZAgnVs1xmtcN5qfLm7eqFO2raXx8yPwrJruvcKUDktfx8YYNVQCylcZ7z1/ | ||
| Asv+W2oku423QE0x6V0u/k9iOyJAjXXQgFhE3PHUT2m+8hYsfoAfBV/3y+4SW/N1 | ||
| vmmxNo8hcCZ0U7g8MW+nSweOpLhVINk5Xn3kLnuh4KEKB1jch9vnAgMBAAGjXjBc | ||
| MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATAdBgNVHQ4EFgQUFD7KcrbNUPRK | ||
| pHQBBqChuRfX4FgwHwYDVR0jBBgwFoAUB0uISyjqq+LwYEXPFqMiUlSi4gQwDQYJ | ||
| KoZIhvcNAQELBQADggEBAFA1d2kLDTuPxPfHsd7EtY3bExysFqm1Wmjw5KhjEGdf | ||
| xq3cX6m3E/txRg1gcI0N4ZSIw+r1AyOD2+z781oeleVlG8RaZ68H5HL6XwCiVZtf | ||
| r/I/jCRjx4wWb1tQtH487MfYypWFq/bwMZy5wlgQ565dLAebN/J1EYeUXNFBLJDa | ||
| YlArnK8XpEqxwOYrzZeKoKSvPAkMBxxESSfa0VC9b7/2wVPN4fUXB7KV+gfIDXWQ | ||
| sNg4V3EXMLb1UnYnEV9M8En0mGDpeJlUndOxxFMDcQma9i+SECieUAAGt7q7kqxx | ||
| l05bNVBJPMr7LnbDMvbLFtpPwIORnYKeNtwBLUrxHvw= | ||
| -----END CERTIFICATE----- |
Uh oh!
There was an error while loading. Please reload this page.