Skip to content

Commit bc52d23

Browse files
committed
fix(gax-httpjson): use Conscrypt TrustManagerFactory for mTLS SSLContext
The mTLS HttpTransport paired a Conscrypt SSLContext with the JDK (SunJSSE) PKIX TrustManagerFactory. On TLS 1.3 Conscrypt passes authType "GENERIC" to the trust manager, which SunJSSE rejects for CA-issued server certificates with a KeyUsage extension (e.g. Google front ends), so every mTLS HTTP/JSON handshake failed with "Unknown authType: GENERIC". Since this PR enables mTLS automatically when a workload certificate config is present, this broke HTTP/JSON clients by default on Cloud Run. Use Conscrypt's own PKIX TrustManagerFactory, which loads the same default trust store (including javax.net.ssl.trustStore overrides) as the JDK. Adds a TLS 1.3 handshake regression test against a local server presenting a CA-issued leaf with KeyUsage and requiring a client certificate.
1 parent 2d5f9ed commit bc52d23

5 files changed

Lines changed: 353 additions & 1 deletion

File tree

‎sdk-platform-java/gax-java/gax-httpjson/BUILD.bazel‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ java_library(
5353
srcs = glob(["src/test/java/**/*.java"]),
5454
javacopts = _JAVA_COPTS,
5555
plugins = ["//:auto_value_plugin"],
56+
resources = glob([
57+
"src/test/resources/com/google/api/gax/httpjson/tls13TestCa.pem",
58+
"src/test/resources/com/google/api/gax/httpjson/tls13TestServerCertAndKey.pem",
59+
]),
5660
visibility = ["//visibility:public"],
5761
deps = [":gax_httpjson"] + _COMPILE_DEPS + _TEST_COMPILE_DEPS,
5862
)

‎sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import java.util.logging.Level;
5555
import java.util.logging.Logger;
5656
import javax.net.ssl.SSLContext;
57+
import javax.net.ssl.TrustManagerFactory;
5758
import org.jspecify.annotations.NullMarked;
5859
import org.jspecify.annotations.Nullable;
5960

@@ -223,10 +224,15 @@ private NetHttpTransport.Builder configureMtls(NetHttpTransport.Builder builder)
223224
// and trust manager factory (TMF) are bound to Conscrypt's TLS implementation (supporting PQC
224225
// key exchange).
225226
SSLContext sslContext = SSLContext.getInstance("TLS", conscryptProvider);
227+
// The TrustManagerFactory must come from the same provider as the SSLContext. On TLS 1.3,
228+
// Conscrypt passes authType "GENERIC" to the trust manager, which the JDK (SunJSSE) PKIX
229+
// trust manager rejects for CA-issued server certificates that carry a KeyUsage extension
230+
// (e.g. Google front ends), failing the handshake with "Unknown authType: GENERIC".
231+
// Conscrypt's trust manager loads the same default trust store as the JDK.
226232
SslUtils.initSslContext(
227233
sslContext,
228234
null,
229-
SslUtils.getPkixTrustManagerFactory(),
235+
TrustManagerFactory.getInstance("PKIX", conscryptProvider),
230236
mtlsKeyStore,
231237
"",
232238
SslUtils.getDefaultKeyManagerFactory());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package com.google.api.gax.httpjson;
31+
32+
import static com.google.common.truth.Truth.assertThat;
33+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
34+
35+
import com.google.api.client.http.GenericUrl;
36+
import com.google.api.client.http.HttpResponse;
37+
import com.google.api.client.http.HttpTransport;
38+
import com.google.api.client.util.SecurityUtils;
39+
import com.google.api.gax.rpc.mtls.CertificateBasedAccess;
40+
import com.google.api.gax.rpc.testing.FakeMtlsProvider;
41+
import java.io.BufferedReader;
42+
import java.io.File;
43+
import java.io.FileOutputStream;
44+
import java.io.InputStream;
45+
import java.io.InputStreamReader;
46+
import java.io.OutputStream;
47+
import java.net.InetAddress;
48+
import java.nio.charset.StandardCharsets;
49+
import java.security.KeyStore;
50+
import java.security.cert.CertificateFactory;
51+
import java.security.cert.X509Certificate;
52+
import java.util.concurrent.ExecutorService;
53+
import java.util.concurrent.Executors;
54+
import java.util.concurrent.Future;
55+
import java.util.concurrent.TimeUnit;
56+
import javax.net.ssl.KeyManagerFactory;
57+
import javax.net.ssl.SSLContext;
58+
import javax.net.ssl.SSLServerSocket;
59+
import javax.net.ssl.SSLSocket;
60+
import javax.net.ssl.TrustManager;
61+
import javax.net.ssl.X509TrustManager;
62+
import org.junit.jupiter.api.AfterEach;
63+
import org.junit.jupiter.api.BeforeEach;
64+
import org.junit.jupiter.api.Test;
65+
import org.junit.jupiter.api.io.TempDir;
66+
import org.mockito.Mockito;
67+
68+
/**
69+
* Handshake test for the Conscrypt-backed mTLS {@link HttpTransport} built by {@link
70+
* InstantiatingHttpJsonChannelProvider}.
71+
*
72+
* <p>Regression test for "Unknown authType: GENERIC": on TLS 1.3, Conscrypt passes the authType
73+
* {@code "GENERIC"} to its trust manager. If the trust manager comes from the JDK (SunJSSE) rather
74+
* than Conscrypt, it rejects server certificates that are issued by a trusted CA and carry a
75+
* KeyUsage extension (as Google front ends' certificates do), and every mTLS HTTP/JSON handshake
76+
* fails. A self-signed server certificate that is itself the trust anchor does not reproduce the
77+
* issue, so the server here presents a CA-issued leaf with KeyUsage.
78+
*
79+
* <p>The transport always uses the default trust store, so this test points {@code
80+
* javax.net.ssl.trustStore} at a temporary store containing only the test CA, and restores the
81+
* previous value afterwards.
82+
*/
83+
class InstantiatingHttpJsonChannelProviderTls13Test {
84+
85+
private static final String RESOURCE_DIR = "com/google/api/gax/httpjson/";
86+
private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
87+
private static final String TRUST_STORE_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword";
88+
private static final String TRUST_STORE_PASSWORD = "changeit";
89+
90+
@TempDir File tempDir;
91+
92+
private String previousTrustStore;
93+
private String previousTrustStorePassword;
94+
private ExecutorService serverExecutor;
95+
96+
@BeforeEach
97+
void setUp() throws Exception {
98+
assumeTrue(
99+
HttpJsonConscryptUtils.getConscryptProvider() != null,
100+
"Conscrypt native library is unavailable on this platform");
101+
assumeTrue(isTls13Supported(), "TLS 1.3 is unavailable on this JDK");
102+
103+
previousTrustStore = System.getProperty(TRUST_STORE_PROPERTY);
104+
previousTrustStorePassword = System.getProperty(TRUST_STORE_PASSWORD_PROPERTY);
105+
File trustStoreFile = writeTrustStoreWithTestCa();
106+
System.setProperty(TRUST_STORE_PROPERTY, trustStoreFile.getAbsolutePath());
107+
System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, TRUST_STORE_PASSWORD);
108+
109+
serverExecutor = Executors.newSingleThreadExecutor();
110+
}
111+
112+
@AfterEach
113+
void tearDown() {
114+
restoreProperty(TRUST_STORE_PROPERTY, previousTrustStore);
115+
restoreProperty(TRUST_STORE_PASSWORD_PROPERTY, previousTrustStorePassword);
116+
if (serverExecutor != null) {
117+
serverExecutor.shutdownNow();
118+
}
119+
}
120+
121+
@Test
122+
void createHttpTransport_withMtlsAndConscrypt_completesTls13HandshakeWithCaIssuedServerCert()
123+
throws Exception {
124+
CertificateBasedAccess certificateBasedAccess = Mockito.mock(CertificateBasedAccess.class);
125+
Mockito.when(certificateBasedAccess.useMtlsClientCertificate()).thenReturn(true);
126+
InstantiatingHttpJsonChannelProvider channelProvider =
127+
InstantiatingHttpJsonChannelProvider.newBuilder()
128+
.setEndpoint("localhost:443")
129+
.setMtlsProvider(
130+
new FakeMtlsProvider(FakeMtlsProvider.createTestMtlsKeyStore(), "", false))
131+
.setCertificateBasedAccess(certificateBasedAccess)
132+
.build();
133+
HttpTransport transport = channelProvider.createHttpTransport();
134+
assertThat(transport).isNotNull();
135+
136+
final SSLServerSocket serverSocket = createTls13ServerSocket();
137+
try {
138+
Future<Boolean> clientCertificatePresented =
139+
serverExecutor.submit(() -> serveSingleRequest(serverSocket));
140+
141+
HttpResponse response =
142+
transport
143+
.createRequestFactory()
144+
.buildGetRequest(
145+
new GenericUrl("https://localhost:" + serverSocket.getLocalPort() + "/"))
146+
.execute();
147+
try {
148+
assertThat(response.getStatusCode()).isEqualTo(200);
149+
assertThat(response.parseAsString()).isEqualTo("ok");
150+
} finally {
151+
response.disconnect();
152+
}
153+
assertThat(clientCertificatePresented.get(10, TimeUnit.SECONDS)).isTrue();
154+
} finally {
155+
serverSocket.close();
156+
}
157+
}
158+
159+
/** Accepts one connection, answers one HTTP request, and reports if a client cert was sent. */
160+
private static boolean serveSingleRequest(SSLServerSocket serverSocket) throws Exception {
161+
SSLSocket socket = (SSLSocket) serverSocket.accept();
162+
try {
163+
socket.startHandshake();
164+
boolean clientCertificatePresented = socket.getSession().getPeerCertificates().length > 0;
165+
BufferedReader reader =
166+
new BufferedReader(
167+
new InputStreamReader(socket.getInputStream(), StandardCharsets.US_ASCII));
168+
String line;
169+
while ((line = reader.readLine()) != null && !line.isEmpty()) {
170+
// Drain request headers.
171+
}
172+
OutputStream out = socket.getOutputStream();
173+
out.write(
174+
("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok")
175+
.getBytes(StandardCharsets.US_ASCII));
176+
out.flush();
177+
return clientCertificatePresented;
178+
} finally {
179+
socket.close();
180+
}
181+
}
182+
183+
/**
184+
* Creates a JDK TLS 1.3 server that presents a CA-issued leaf certificate with KeyUsage and
185+
* requires (but does not validate) a client certificate.
186+
*/
187+
private static SSLServerSocket createTls13ServerSocket() throws Exception {
188+
KeyStore serverKeyStore;
189+
InputStream certAndKey = openResource("tls13TestServerCertAndKey.pem");
190+
try {
191+
serverKeyStore = SecurityUtils.createMtlsKeyStore(certAndKey);
192+
} finally {
193+
certAndKey.close();
194+
}
195+
KeyManagerFactory keyManagerFactory =
196+
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
197+
keyManagerFactory.init(serverKeyStore, new char[0]);
198+
199+
SSLContext serverContext = SSLContext.getInstance("TLSv1.3");
200+
serverContext.init(
201+
keyManagerFactory.getKeyManagers(), new TrustManager[] {new AcceptAllTrustManager()}, null);
202+
SSLServerSocket serverSocket =
203+
(SSLServerSocket)
204+
serverContext
205+
.getServerSocketFactory()
206+
.createServerSocket(0, 1, InetAddress.getLoopbackAddress());
207+
serverSocket.setEnabledProtocols(new String[] {"TLSv1.3"});
208+
serverSocket.setNeedClientAuth(true);
209+
return serverSocket;
210+
}
211+
212+
private File writeTrustStoreWithTestCa() throws Exception {
213+
X509Certificate caCertificate;
214+
InputStream caPem = openResource("tls13TestCa.pem");
215+
try {
216+
caCertificate =
217+
(X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(caPem);
218+
} finally {
219+
caPem.close();
220+
}
221+
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
222+
trustStore.load(null, null);
223+
trustStore.setCertificateEntry("gax-test-ca", caCertificate);
224+
File trustStoreFile = new File(tempDir, "truststore");
225+
OutputStream out = new FileOutputStream(trustStoreFile);
226+
try {
227+
trustStore.store(out, TRUST_STORE_PASSWORD.toCharArray());
228+
} finally {
229+
out.close();
230+
}
231+
return trustStoreFile;
232+
}
233+
234+
private static InputStream openResource(String name) {
235+
InputStream stream =
236+
InstantiatingHttpJsonChannelProviderTls13Test.class
237+
.getClassLoader()
238+
.getResourceAsStream(RESOURCE_DIR + name);
239+
if (stream == null) {
240+
throw new IllegalStateException("Missing test resource: " + RESOURCE_DIR + name);
241+
}
242+
return stream;
243+
}
244+
245+
private static boolean isTls13Supported() {
246+
try {
247+
SSLContext.getInstance("TLSv1.3");
248+
return true;
249+
} catch (Exception e) {
250+
return false;
251+
}
252+
}
253+
254+
private static void restoreProperty(String key, String value) {
255+
if (value == null) {
256+
System.clearProperty(key);
257+
} else {
258+
System.setProperty(key, value);
259+
}
260+
}
261+
262+
/** Server-side trust manager: the test only checks that a client certificate is presented. */
263+
private static final class AcceptAllTrustManager implements X509TrustManager {
264+
@Override
265+
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
266+
267+
@Override
268+
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
269+
270+
@Override
271+
public X509Certificate[] getAcceptedIssuers() {
272+
return new X509Certificate[0];
273+
}
274+
}
275+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDHzCCAgegAwIBAgIUZEyXXbTT3KXPPzN6Z6b30/GQ26wwDQYJKoZIhvcNAQEL
3+
BQAwFjEUMBIGA1UEAwwLR0FYIFRlc3QgQ0EwIBcNMjYwOTMwMDMzNzM5WhgPMjEy
4+
NjA5MDYwMzM3MzlaMBYxFDASBgNVBAMMC0dBWCBUZXN0IENBMIIBIjANBgkqhkiG
5+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkLx+Kh78x1ktPpvGf3ITPS7xwr7rU6Pt/Z34
6+
AKodCSBCrt/d3BhkGiy8w3IqWN7mIy2ujdoKeUau4NJiYdz3A16XZ1nhvp7J3tK5
7+
a6SSxO95lVwy70nafTO8ApFYKawKLKNWvGqChM95AI6+Q7dTzzGArpwJxCK0p7dp
8+
4bKjhpSOZaW46VdpCsLwoPJmXNlDpAL5O3nrvdnfilHVCqWHoAZk7ESwQcwm8vP6
9+
5suLUSvpagPfMRabFTEv91PsmVhB8KjSu8/kGPAC1F7RLyZK31a0Kb5wNwW+69cb
10+
kxjUEhNux7/SHTF250kF2vQ+nFrnUQZSGM8cHU8qpeaOsTFDRwIDAQABo2MwYTAd
11+
BgNVHQ4EFgQU7pHELECvL5hdK8Y3N1x6GZSzSjkwHwYDVR0jBBgwFoAU7pHELECv
12+
L5hdK8Y3N1x6GZSzSjkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
13+
DQYJKoZIhvcNAQELBQADggEBACCyzuu0K4HF0iMu1rj+adlpYUzl6uwkKBuL6fRJ
14+
sTxe0ftgWWcFXJV+P6T+maGu7DMANqbADpWqQbAHMNbSXxPSfGgOd2tS+jg/OOef
15+
go1MhsMUpgxSN9PvWAfhiVIhgF7pdjdVP2qiObx7F/Qo/Xmr0MEy61mmeCbWENBj
16+
jC7DsCo5SWo6kMjvb6dz5G+f+4LElQKCQbBGAOm00XlERE/W7WfUMBzNFhLCHrVs
17+
CRq5lIGxek8DEIC8oXtt/7g0KoKVkOCNj2ZXljEQgi4awoYHqlTRXw/nlctJJ8xs
18+
DLmvfUObHIo66rliDG1Pco/Ce9FVewUJz2WQXJK/mGuuesA=
19+
-----END CERTIFICATE-----
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDTTCCAjWgAwIBAgIUA8zck3uCk3uCK7FqAgRrbQjayGEwDQYJKoZIhvcNAQEL
3+
BQAwFjEUMBIGA1UEAwwLR0FYIFRlc3QgQ0EwIBcNMjYwOTMwMDMzNzM5WhgPMjEy
4+
NjA5MDYwMzM3MzlaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcN
5+
AQEBBQADggEPADCCAQoCggEBAMLalJEkZTlTkPbSayD72lYxibZ8JLtRP4OaK/Mr
6+
yvhv8JX5JgSxY3fW2FkpYLBFO3fKFyyPRf2pJTHTpvBjyy/xZnyMHltT9uurF58b
7+
kfAzZDLphJdICTXVL9cEHzCJ+AVFJ85bbLDhCcAd5urCZClqOD6QoM7aLsSurwLQ
8+
eeAP44XwcX+2+VvNfB6YFUgjVreTD+c1xD9OsbMFQuWzv/uDkl6KciOkeuAGOOJh
9+
8ylcugFDOfb9ZWWQRiDo9BiIc/6HIhNk8jo8hCgofcxR/MRZNRKOQ4ilsbNkrjo0
10+
MusuRQq2mcC4yLZRRo7Jtk2FQYysg2e0mbHKroQhmRIP2wcCAwEAAaOBkjCBjzAa
11+
BgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
12+
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFL5u77FZtvQz
13+
x06t35j/m9a+PVVNMB8GA1UdIwQYMBaAFO6RxCxAry+YXSvGNzdcehmUs0o5MA0G
14+
CSqGSIb3DQEBCwUAA4IBAQBwAaP5bHnvfObJcl7rKOSfXFKhjm0j5De8BNbS5Dcs
15+
Z22gkuri5qgj8MEwGt4Wu4mxopYD18AbWKt8e7mC5+mJgkydSLvRtJxx5geNw0CD
16+
8TJK8MTpJGJtBKoxC26kxZyHTM6GLO3iAXMzuDFbeW4xpESezoaKsctwlUaAQNO+
17+
taASUFDy9CJ24SDsiWBmSrTZoNqcCbB3yyYS6Wrx1Et1GYmeTtvx8qo9GjGoil/t
18+
9mWgYhb52Wg+7EO5NQa1eIZ23ZgWg4w7CRqPyjqmOM/ygCmHvoNGuOdbMzSc8o5P
19+
i9S6gTwaU7OBa8vrKdMk+LPkmcHs82qujVYqvU1rMV5a
20+
-----END CERTIFICATE-----
21+
-----BEGIN PRIVATE KEY-----
22+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDC2pSRJGU5U5D2
23+
0msg+9pWMYm2fCS7UT+DmivzK8r4b/CV+SYEsWN31thZKWCwRTt3yhcsj0X9qSUx
24+
06bwY8sv8WZ8jB5bU/brqxefG5HwM2Qy6YSXSAk11S/XBB8wifgFRSfOW2yw4QnA
25+
HebqwmQpajg+kKDO2i7Erq8C0HngD+OF8HF/tvlbzXwemBVII1a3kw/nNcQ/TrGz
26+
BULls7/7g5JeinIjpHrgBjjiYfMpXLoBQzn2/WVlkEYg6PQYiHP+hyITZPI6PIQo
27+
KH3MUfzEWTUSjkOIpbGzZK46NDLrLkUKtpnAuMi2UUaOybZNhUGMrINntJmxyq6E
28+
IZkSD9sHAgMBAAECggEAB9NPdCTKC+tXPxJkSwbSVfPXXATQVNWGf3qJmVKA7ElS
29+
mDNRW47kN8PhHHOU9n9RTBZJp/h5Dx7j54Rgh/b6Cgrf2tClx3UvzXcpk7I/wi3O
30+
MF1AffLeq9LMAQ6hUgm4dD26w4bYbqIbCiQpSfRUoXUqnRih3KzWg/+f97T3sA/h
31+
SSNH67sQ/SmaqujmCDsXoVF8Ad0bGzEkEVbn7QgXX+C+DxxA6wGvRER7YRfzhxaz
32+
e1B0rdRLTwlAycKOGPvp+7mVrs1KTELyj8rJp6dNzTpll1W4Y5MeJivSjTpEtPzN
33+
Fo6Wh6+/+wLyXcYUknJMfTlt3GcYdAzhREEXBM34gQKBgQDz0/HtQPulpv8jtoms
34+
m7TiIcRuOHbK+ojt5s9dQ6ikYuCOJ3CnCPLX5T7Eo7yl/NvPhqSr15GbqbHNutLt
35+
FL9Gh5L8H+Z7i/hmmAou59WXIcQOfOS+/ZFi1rpE8Fhp10/dYRSznIG4vaN19DBF
36+
Og4a4n5CR+mRgxcQvt32URiCvwKBgQDMlMJ/rCfN2SAQYXwIE8+7viknMVxyoC1T
37+
ggAVfj+XUdEgPeD8+cKIuQ2E0ino4+wLve6zo5skcw+COKFS7QZcL8xT0oYjRX0I
38+
SZFUGhUnuSDv8rbFx25m27IfQ/FicFFdOHK3n1MbX+I0IfKyYvFrL3DfSuKuZKam
39+
ODIy6PFhuQKBgQCxKxvrDQDNSR6y0HwRE8LHXUnh3N1Ud97vHnsmhXcQm1gXcskE
40+
Vhg+j7CgKWl2ItmFXYxh4O1IcnFjz5Bb/GtP0EsZMATFWNtRkMA1fPdSLZLMBCwi
41+
KxVtACGw3gMgRPcfIN9t8xa/KeyQVLKOWrTNDC458w7gUKR94nJd/GcW+QKBgHiI
42+
HJ/D+h+ZkP6VsNHBel6OhwiEWIdgP9Q3f30qSKUKmz93tZrWO/r5rDFY8UA5KNUy
43+
LfTAq6Mp9zUt0D5fT6P4tVWdb1JQmn3LekgmpdglhnZYdb7I5Q7M2YD6Mrlgm09k
44+
9pGg2QID1+4HIkpfaQVRihrEsBE3U8gSy+CaxbxhAoGBALZn4isHqbH/+H3em7Mc
45+
ITRF2xPhHlCv0SHHn6UHoWVFYXmgF+bjX43wVSjmPL9PusSvH+gBxeYpKQSm9Hjp
46+
eoQirtVJPCVmS5e8pKmmJBAUbRHk1umUR7lwgfTlTnIwEOCD+ApaqWByTdsHNPL1
47+
fazDXasxJ7y760qDNo7UCbec
48+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)