Skip to content

Commit 9346117

Browse files
authoredJun 3, 2024··
feat: Configure java connector to check CN instance name. Fixes #1995 (#1996)
This code will make that the connector checks that the server certificate's subject CN field matches the instance name before allowing the TLS handshake to proceed. This introduces new classes that are always installed as trust managers on all TLS connections to ensure that the TLS logic correctly checks that the server certificate's CN field matches the instance. - `InstanceCheckingTrustManager` - Delegates to default TLS trust manager, then checks the CN field. - `InstanceCheckingTrustManagerFactory` and `InstanceCheckingTrustManagerFactorySpi` - Installs our custom trust manager into connector TLS sockets. The `ConscryptWorkaroundTrustManagerFactory` and `ConscryptWorkaroundTrustManagerFactorySpi` classes are no longer necessary. Logic to detect Conscrypt and use the workaround `ConscryptWorkaroundTrustManager` moves into `InstanceCheckingTrustManagerFactorySpi`.
1 parent ab3973c commit 9346117

11 files changed

+346
-119
lines changed
 

‎core/src/main/java/com/google/cloud/sql/core/ConscryptWorkaroundDelegatingTrustManger.java

+28
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
package com.google.cloud.sql.core;
1818

1919
import java.net.Socket;
20+
import java.security.NoSuchAlgorithmException;
21+
import java.security.Provider;
22+
import java.security.Security;
2023
import java.security.cert.CertificateException;
2124
import java.security.cert.X509Certificate;
25+
import javax.net.ssl.SSLContext;
2226
import javax.net.ssl.SSLEngine;
2327
import javax.net.ssl.X509ExtendedTrustManager;
2428

@@ -33,6 +37,30 @@
3337
* <p>See https://github.com/google/conscrypt/issues/1033#issuecomment-982701272
3438
*/
3539
class ConscryptWorkaroundDelegatingTrustManger extends X509ExtendedTrustManager {
40+
private static final boolean CONSCRYPT_TLS;
41+
42+
static {
43+
// Provider name is "Conscrypt", hardcoded string in the library source:
44+
// https://github.com/google/conscrypt/blob/655ad5069e1cb4d1989b8117eaf090371885af99/openjdk/src/main/java/org/conscrypt/Platform.java#L149
45+
Provider p = Security.getProvider("Conscrypt");
46+
if (p != null) {
47+
try {
48+
SSLContext ctx = SSLContext.getInstance("TLS");
49+
Provider prov = ctx.getProvider();
50+
CONSCRYPT_TLS = "Conscrypt".equals(prov.getName());
51+
} catch (NoSuchAlgorithmException e) {
52+
throw new RuntimeException("Unable to load algorithm TLS", e);
53+
}
54+
} else {
55+
CONSCRYPT_TLS = false;
56+
}
57+
}
58+
59+
/** Returns true if the Conscrypt Java Crypto Extension is installed. */
60+
static boolean isWorkaroundNeeded() {
61+
return CONSCRYPT_TLS;
62+
}
63+
3664
private final X509ExtendedTrustManager tm;
3765

3866
ConscryptWorkaroundDelegatingTrustManger(X509ExtendedTrustManager tm) {

‎core/src/main/java/com/google/cloud/sql/core/ConscryptWorkaroundTrustManagerFactory.java

-82
This file was deleted.

‎core/src/main/java/com/google/cloud/sql/core/DefaultConnectionInfoRepository.java

+2-16
Original file line numberDiff line numberDiff line change
@@ -379,22 +379,8 @@ private SslData createSslData(
379379
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
380380
kmf.init(authKeyStore, new char[0]);
381381

382-
KeyStore trustedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
383-
trustedKeyStore.load(null, null);
384-
trustedKeyStore.setCertificateEntry("instance", instanceMetadata.getInstanceCaCertificate());
385-
TrustManagerFactory tmf;
386-
387-
// Note: This is a workaround for Conscrypt bug #1033
388-
// Conscrypt is the JCE provider on some Google Cloud runtimes like DataProc.
389-
// https://github.com/google/conscrypt/issues/1033
390-
//
391-
if (ConscryptWorkaroundTrustManagerFactory.isWorkaroundNeeded()) {
392-
tmf = ConscryptWorkaroundTrustManagerFactory.newInstance();
393-
} else {
394-
tmf = TrustManagerFactory.getInstance("X.509");
395-
}
396-
397-
tmf.init(trustedKeyStore);
382+
TrustManagerFactory tmf =
383+
InstanceCheckingTrustManagerFactory.newInstance(instanceName, instanceMetadata);
398384

399385
SSLContext sslContext;
400386

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.sql.core;
18+
19+
import java.io.IOException;
20+
import java.security.KeyStore;
21+
import java.security.KeyStoreException;
22+
import java.security.NoSuchAlgorithmException;
23+
import java.security.cert.CertificateException;
24+
import javax.net.ssl.TrustManagerFactory;
25+
26+
/**
27+
* Implement custom server certificate trust checks specific to Cloud SQL.
28+
*
29+
* <p>In the JVM, we need to implement 3 classes to make sure that we are capturing all the
30+
* TrustManager instances created by the Java Crypto provider so that the connector will:
31+
*
32+
* <p>class InstanceCheckingTrustManagerFactory extends TrustManagerFactory - has a bunch of final
33+
* methods that delegate to a TrustManagerFactorySpi.
34+
*
35+
* <p>class InstanceCheckingTrustManagerFactorySpi implements TrustManagerFactorySpi - can actually
36+
* intercept requests for the list of TrustManager instances and wrap them with our replacement
37+
* TrustManager.
38+
*
39+
* <p>class ConscryptWorkaroundTrustManager - the workaround for the Conscrypt bug.
40+
*
41+
* <p>class InstanceCheckingTrustManager - delegates TLS checks to the default provider and then
42+
* checks that the Subject CN field contains the Cloud SQL instance ID.
43+
*/
44+
class InstanceCheckingTrustManagerFactory extends TrustManagerFactory {
45+
46+
static InstanceCheckingTrustManagerFactory newInstance(
47+
CloudSqlInstanceName instanceName, InstanceMetadata instanceMetadata)
48+
throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
49+
50+
TrustManagerFactory delegate = TrustManagerFactory.getInstance("X.509");
51+
KeyStore trustedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
52+
trustedKeyStore.load(null, null);
53+
trustedKeyStore.setCertificateEntry("instance", instanceMetadata.getInstanceCaCertificate());
54+
55+
InstanceCheckingTrustManagerFactory tmf =
56+
new InstanceCheckingTrustManagerFactory(instanceName, delegate);
57+
58+
tmf.init(trustedKeyStore);
59+
60+
return tmf;
61+
}
62+
63+
private InstanceCheckingTrustManagerFactory(
64+
CloudSqlInstanceName instanceName, TrustManagerFactory delegate) {
65+
super(
66+
new InstanceCheckingTrustManagerFactorySpi(instanceName, delegate),
67+
delegate.getProvider(),
68+
delegate.getAlgorithm());
69+
}
70+
}

‎core/src/main/java/com/google/cloud/sql/core/ConscryptWorkaroundTrustManagerFactorySpi.java ‎core/src/main/java/com/google/cloud/sql/core/InstanceCheckingTrustManagerFactorySpi.java

+17-13
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,16 @@
2626
import javax.net.ssl.X509ExtendedTrustManager;
2727

2828
/**
29-
* This is a workaround for a known bug in Conscrypt crypto in how it handles X509 auth type.
30-
* OpenJDK interpres the X509 certificate auth type as "UNKNOWN" while Conscrypt interpret the same
31-
* certificate as auth type "GENERIC". This incompatibility causes problems in the JDK.
32-
*
33-
* <p>This adapter works around the issue by creating wrappers around all TrustManager instances. It
34-
* replaces "GENERIC" auth type with "UNKNOWN" auth type before delegating calls.
35-
*
36-
* <p>See https://github.com/google/conscrypt/issues/1033#issuecomment-982701272
29+
* Part of the InstanceCheckingTrustManagerFactory that implements custom CloudSQL server
30+
* certificate checks.
3731
*/
38-
class ConscryptWorkaroundTrustManagerFactorySpi extends TrustManagerFactorySpi {
32+
class InstanceCheckingTrustManagerFactorySpi extends TrustManagerFactorySpi {
3933
private final TrustManagerFactory delegate;
34+
private final CloudSqlInstanceName instanceName;
4035

41-
ConscryptWorkaroundTrustManagerFactorySpi(TrustManagerFactory delegate) {
36+
InstanceCheckingTrustManagerFactorySpi(
37+
CloudSqlInstanceName instanceName, TrustManagerFactory delegate) {
38+
this.instanceName = instanceName;
4239
this.delegate = delegate;
4340
}
4441

@@ -59,10 +56,17 @@ protected TrustManager[] engineGetTrustManagers() {
5956
TrustManager[] delegates = new TrustManager[tms.length];
6057
for (int i = 0; i < tms.length; i++) {
6158
if (tms[i] instanceof X509ExtendedTrustManager) {
62-
delegates[i] =
63-
new ConscryptWorkaroundDelegatingTrustManger((X509ExtendedTrustManager) tms[i]);
64-
} else {
59+
X509ExtendedTrustManager tm = (X509ExtendedTrustManager) tms[i];
6560

61+
// Note: This is a workaround for Conscrypt bug #1033
62+
// Conscrypt is the JCE provider on some Google Cloud runtimes like DataProc.
63+
// https://github.com/google/conscrypt/issues/1033
64+
if (ConscryptWorkaroundDelegatingTrustManger.isWorkaroundNeeded()) {
65+
tm = new ConscryptWorkaroundDelegatingTrustManger(tm);
66+
}
67+
68+
delegates[i] = new InstanceCheckingTrustManger(instanceName, tm);
69+
} else {
6670
delegates[i] = tms[i];
6771
}
6872
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.sql.core;
18+
19+
import java.net.Socket;
20+
import java.security.cert.CertificateException;
21+
import java.security.cert.X509Certificate;
22+
import javax.naming.InvalidNameException;
23+
import javax.naming.ldap.LdapName;
24+
import javax.naming.ldap.Rdn;
25+
import javax.net.ssl.SSLEngine;
26+
import javax.net.ssl.X509ExtendedTrustManager;
27+
28+
/**
29+
* This is a workaround for a known bug in Conscrypt crypto in how it handles X509 auth type.
30+
* OpenJDK interpres the X509 certificate auth type as "UNKNOWN" while Conscrypt interpret the same
31+
* certificate as auth type "GENERIC". This incompatibility causes problems in the JDK.
32+
*
33+
* <p>This adapter works around the issue by creating wrappers around all TrustManager instances. It
34+
* replaces "GENERIC" auth type with "UNKNOWN" auth type before delegating calls.
35+
*
36+
* <p>See https://github.com/google/conscrypt/issues/1033#issuecomment-982701272
37+
*/
38+
class InstanceCheckingTrustManger extends X509ExtendedTrustManager {
39+
private final X509ExtendedTrustManager tm;
40+
private final CloudSqlInstanceName instanceName;
41+
42+
public InstanceCheckingTrustManger(
43+
CloudSqlInstanceName instanceName, X509ExtendedTrustManager tm) {
44+
this.instanceName = instanceName;
45+
this.tm = tm;
46+
}
47+
48+
@Override
49+
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
50+
throws CertificateException {
51+
tm.checkClientTrusted(chain, authType, socket);
52+
}
53+
54+
@Override
55+
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
56+
throws CertificateException {
57+
tm.checkClientTrusted(chain, authType, engine);
58+
}
59+
60+
@Override
61+
public void checkClientTrusted(X509Certificate[] chain, String authType)
62+
throws CertificateException {
63+
tm.checkClientTrusted(chain, authType);
64+
}
65+
66+
@Override
67+
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
68+
throws CertificateException {
69+
tm.checkServerTrusted(chain, authType, socket);
70+
checkCertificateChain(chain);
71+
}
72+
73+
@Override
74+
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
75+
throws CertificateException {
76+
tm.checkServerTrusted(chain, authType, engine);
77+
checkCertificateChain(chain);
78+
}
79+
80+
@Override
81+
public void checkServerTrusted(X509Certificate[] chain, String authType)
82+
throws CertificateException {
83+
tm.checkServerTrusted(chain, authType);
84+
checkCertificateChain(chain);
85+
}
86+
87+
private void checkCertificateChain(X509Certificate[] chain) throws CertificateException {
88+
if (chain.length == 0) {
89+
throw new CertificateException("No server certificates in chain");
90+
}
91+
if (chain[0].getSubjectX500Principal() == null) {
92+
throw new CertificateException("Subject is missing");
93+
}
94+
95+
String cn = null;
96+
97+
try {
98+
String subject = chain[0].getSubjectX500Principal().getName();
99+
LdapName subjectName = new LdapName(subject);
100+
for (Rdn rdn : subjectName.getRdns()) {
101+
if ("CN".equals(rdn.getType())) {
102+
cn = (String) rdn.getValue();
103+
}
104+
}
105+
} catch (InvalidNameException e) {
106+
throw new CertificateException("Exception parsing the server certificate subject field", e);
107+
}
108+
109+
if (cn == null) {
110+
throw new CertificateException("Server certificate subject does not contain a value for CN");
111+
}
112+
113+
// parse CN from subject. CN always comes last in the list.
114+
String instName = this.instanceName.getProjectId() + ":" + this.instanceName.getInstanceId();
115+
if (!instName.equals(cn)) {
116+
throw new CertificateException(
117+
"Server certificate CN does not match instance name. Server certificate CN="
118+
+ cn
119+
+ " Expected instance name: "
120+
+ instName);
121+
}
122+
}
123+
124+
@Override
125+
public X509Certificate[] getAcceptedIssuers() {
126+
return tm.getAcceptedIssuers();
127+
}
128+
}

‎core/src/test/java/com/google/cloud/sql/core/CloudSqlCoreTestingBase.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public class CloudSqlCoreTestingBase {
6262

6363
ListenableFuture<KeyPair> clientKeyPair;
6464

65+
public CloudSqlCoreTestingBase() {}
66+
6567
// Creates a fake "accessNotConfigured" exception that can be used for testing.
6668
static HttpTransport fakeNotConfiguredException() {
6769
return fakeGoogleJsonResponseException(
@@ -130,10 +132,18 @@ public void setup() throws GeneralSecurityException {
130132
}
131133

132134
HttpTransport fakeSuccessHttpTransport(Duration certDuration) {
133-
return fakeSuccessHttpTransport(certDuration, null);
135+
return fakeSuccessHttpTransport(TestKeys.getServerCertPem(), certDuration, null);
134136
}
135137

136138
HttpTransport fakeSuccessHttpTransport(Duration certDuration, String baseUrl) {
139+
return fakeSuccessHttpTransport(TestKeys.getServerCertPem(), certDuration, baseUrl);
140+
}
141+
142+
HttpTransport fakeSuccessHttpTransport(String serverCert, Duration certDuration) {
143+
return fakeSuccessHttpTransport(serverCert, certDuration, null);
144+
}
145+
146+
HttpTransport fakeSuccessHttpTransport(String serverCert, Duration certDuration, String baseUrl) {
137147
final JsonFactory jsonFactory = new GsonFactory();
138148
return new MockHttpTransport() {
139149
@Override
@@ -153,7 +163,7 @@ public LowLevelHttpResponse execute() throws IOException {
153163
ImmutableList.of(
154164
new IpMapping().setIpAddress(PUBLIC_IP).setType("PRIMARY"),
155165
new IpMapping().setIpAddress(PRIVATE_IP).setType("PRIVATE")))
156-
.setServerCaCert(new SslCert().setCert(TestKeys.getServerCertPem()))
166+
.setServerCaCert(new SslCert().setCert(serverCert))
157167
.setDatabaseVersion("POSTGRES14")
158168
.setRegion("myRegion");
159169
settings.setFactory(jsonFactory);

‎core/src/test/java/com/google/cloud/sql/core/ConnectorTest.java

+50-6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.time.Duration;
3636
import java.time.Instant;
3737
import java.util.Collections;
38+
import javax.net.ssl.SSLHandshakeException;
3839
import org.junit.After;
3940
import org.junit.Before;
4041
import org.junit.Test;
@@ -80,6 +81,32 @@ public void create_throwsErrorForInvalidInstanceName() throws IOException {
8081
assertThat(ex).hasMessageThat().contains("Cloud SQL connection name is invalid");
8182
}
8283

84+
@Test
85+
public void create_throwsErrorForInvalidTlsCommonNameMismatch()
86+
throws IOException, InterruptedException {
87+
// The server TLS certificate matches myProject:myRegion:myInstance
88+
FakeSslServer sslServer = new FakeSslServer();
89+
ConnectionConfig config =
90+
new ConnectionConfig.Builder()
91+
.withCloudSqlInstance("myProject:myRegion:wrongwrongwrong")
92+
.withIpTypes("PRIMARY")
93+
.build();
94+
95+
int port = sslServer.start(PUBLIC_IP);
96+
97+
Connector connector = newConnector(config.getConnectorConfig(), port);
98+
SSLHandshakeException ex =
99+
assertThrows(
100+
SSLHandshakeException.class, () -> connector.connect(config, TEST_MAX_REFRESH_MS));
101+
102+
assertThat(ex)
103+
.hasMessageThat()
104+
.isEqualTo(
105+
"Server certificate CN does not match instance name. "
106+
+ "Server certificate CN=myProject:myInstance "
107+
+ "Expected instance name: myProject:wrongwrongwrong");
108+
}
109+
83110
/**
84111
* Start an SSL server on the private IP, and verifies that specifying a preference for private IP
85112
* results in a connection to the private IP.
@@ -159,18 +186,34 @@ public void create_successfulUnixSocketConnection() throws IOException, Interrup
159186

160187
@Test
161188
public void create_successfulDomainScopedConnection() throws IOException, InterruptedException {
162-
FakeSslServer sslServer = new FakeSslServer();
189+
FakeSslServer sslServer =
190+
new FakeSslServer(
191+
TestKeys.getDomainServerKeyPair().getPrivate(), TestKeys.getDomainServerCert());
192+
CredentialFactoryProvider credentialFactoryProvider =
193+
new CredentialFactoryProvider(new StubCredentialFactory("foo", null));
194+
ConnectionInfoRepositoryFactory factory =
195+
new StubConnectionInfoRepositoryFactory(
196+
fakeSuccessHttpTransport(TestKeys.getDomainServerCertPem(), Duration.ofSeconds(60)));
197+
198+
int port = sslServer.start(PUBLIC_IP);
163199
ConnectionConfig config =
164200
new ConnectionConfig.Builder()
165201
.withCloudSqlInstance("example.com:myProject:myRegion:myInstance")
166202
.withIpTypes("PRIMARY")
167203
.build();
204+
Connector c =
205+
new Connector(
206+
config.getConnectorConfig(),
207+
factory,
208+
credentialFactoryProvider.getInstanceCredentialFactory(config.getConnectorConfig()),
209+
defaultExecutor,
210+
clientKeyPair,
211+
10,
212+
TEST_MAX_REFRESH_MS,
213+
port);
168214

169-
int port = sslServer.start(PUBLIC_IP);
170-
171-
Connector connector = newConnector(config.getConnectorConfig(), port);
215+
Socket socket = c.connect(config, TEST_MAX_REFRESH_MS);
172216

173-
Socket socket = connector.connect(config, TEST_MAX_REFRESH_MS);
174217
assertThat(readLine(socket)).isEqualTo(SERVER_MESSAGE);
175218
}
176219

@@ -312,7 +355,8 @@ public void supportsCustomCredentialFactoryWithIAM() throws InterruptedException
312355
new CredentialFactoryProvider(
313356
new StubCredentialFactory("foo", Instant.now().plusSeconds(3600).toEpochMilli()));
314357
ConnectionInfoRepositoryFactory factory =
315-
new StubConnectionInfoRepositoryFactory(fakeSuccessHttpTransport(Duration.ofSeconds(0)));
358+
new StubConnectionInfoRepositoryFactory(
359+
fakeSuccessHttpTransport(TestKeys.getServerCertPem(), Duration.ofSeconds(0)));
316360

317361
int port = sslServer.start(PUBLIC_IP);
318362
ConnectionConfig config =

‎core/src/test/java/com/google/cloud/sql/core/FakeSslServer.java

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ public class FakeSslServer {
4545
cert = TestKeys.getServerCert();
4646
}
4747

48+
public FakeSslServer(PrivateKey privateKey, X509Certificate cert) {
49+
this.privateKey = privateKey;
50+
this.cert = cert;
51+
}
52+
4853
int start(final String ip) throws InterruptedException {
4954
final CountDownLatch countDownLatch = new CountDownLatch(1);
5055
final AtomicInteger pickedPort = new AtomicInteger();

‎core/src/test/java/com/google/cloud/sql/core/TestCertificateGenerator.java

+22
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,19 @@ public class TestCertificateGenerator {
6868

6969
private static final X500Name SERVER_CERT_SUBJECT =
7070
new X500Name("C=US,O=Google\\, Inc,CN=myProject:myInstance");
71+
private static final X500Name DOMAIN_SERVER_CERT_SUBJECT =
72+
new X500Name("C=US,O=Google\\, Inc,CN=example.com:myProject:myInstance");
7173

7274
private final String SHA_256_WITH_RSA = "SHA256WithRSA";
7375
private final KeyPair signingCaKeyPair;
7476
private final KeyPair serverCaKeyPair;
77+
private final KeyPair domainServerKeyPair;
7578
private final KeyPair serverKeyPair;
7679
private final KeyPair clientKeyPair;
7780
private final X509Certificate signingCaCert;
7881
private final X509Certificate serverCaCert;
7982
private final X509Certificate serverCertificate;
83+
private final X509Certificate domainServerCertificate;
8084

8185
private final String PEM_HEADER = "-----BEGIN CERTIFICATE-----";
8286
private final String PEM_FOOTER = "-----END CERTIFICATE-----";
@@ -101,6 +105,7 @@ static KeyPair generateKeyPair() {
101105
this.signingCaKeyPair = generateKeyPair();
102106
this.serverKeyPair = generateKeyPair();
103107
this.clientKeyPair = generateKeyPair();
108+
this.domainServerKeyPair = generateKeyPair();
104109

105110
try {
106111
this.serverCaCert = buildRootCertificate(SERVER_CA_SUBJECT, this.serverCaKeyPair);
@@ -114,6 +119,15 @@ static KeyPair generateKeyPair() {
114119
serverCaKeyPair.getPrivate(),
115120
ONE_YEAR_FROM_NOW,
116121
null);
122+
123+
this.domainServerCertificate =
124+
buildSignedCertificate(
125+
DOMAIN_SERVER_CERT_SUBJECT,
126+
domainServerKeyPair.getPublic(),
127+
SERVER_CA_SUBJECT,
128+
serverCaKeyPair.getPrivate(),
129+
ONE_YEAR_FROM_NOW,
130+
null);
117131
} catch (OperatorCreationException | CertificateException | IOException e) {
118132
throw new RuntimeException(e);
119133
}
@@ -135,6 +149,14 @@ public X509Certificate getServerCaCert() {
135149
return serverCaCert;
136150
}
137151

152+
public KeyPair getDomainServerKeyPair() {
153+
return domainServerKeyPair;
154+
}
155+
156+
public X509Certificate getDomainServerCertificate() {
157+
return domainServerCertificate;
158+
}
159+
138160
public X509Certificate createEphemeralCert(String cn, Duration shiftIntoPast)
139161
throws GeneralSecurityException, ExecutionException, OperatorCreationException {
140162
Duration validFor = Duration.ofHours(1);

‎core/src/test/java/com/google/cloud/sql/core/TestKeys.java

+12
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,16 @@ public static String createEphemeralCert(Duration certDuration) {
6666
certs.getEphemeralCertificate(
6767
"temporary-cert", certs.getClientKey().getPublic(), notAfter.toInstant()));
6868
}
69+
70+
public static KeyPair getDomainServerKeyPair() {
71+
return certs.getDomainServerKeyPair();
72+
}
73+
74+
public static X509Certificate getDomainServerCert() {
75+
return certs.getDomainServerCertificate();
76+
}
77+
78+
public static String getDomainServerCertPem() {
79+
return certs.getPemForCert(certs.getDomainServerCertificate());
80+
}
6981
}

0 commit comments

Comments
 (0)
Please sign in to comment.