Skip to content

Commit 8fd2c0a

Browse files
committed
create a builder using a trustmanager
1 parent f0d0def commit 8fd2c0a

4 files changed

Lines changed: 65 additions & 49 deletions

File tree

cmdline/src/main/java/io/opentdf/platform/Command.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.google.gson.GsonBuilder;
1111
import com.google.gson.reflect.TypeToken;
1212

13+
import java.security.cert.X509Certificate;
1314
import java.text.ParseException;
1415
import com.google.gson.JsonSyntaxException;
1516
import io.opentdf.platform.sdk.AssertionConfig;
@@ -18,11 +19,11 @@
1819
import io.opentdf.platform.sdk.KeyType;
1920
import io.opentdf.platform.sdk.SDK;
2021
import io.opentdf.platform.sdk.SDKBuilder;
21-
import io.opentdf.platform.sdk.TrustProvider;
2222
import picocli.CommandLine;
2323
import picocli.CommandLine.HelpCommand;
2424
import picocli.CommandLine.Option;
2525

26+
import javax.net.ssl.X509TrustManager;
2627
import java.io.BufferedInputStream;
2728
import java.io.BufferedOutputStream;
2829
import java.io.File;
@@ -263,7 +264,12 @@ private SDK buildSDK() {
263264
SDKBuilder builder = new SDKBuilder();
264265
if (insecure) {
265266
// Trust all certificates
266-
builder.sslFactory(TrustProvider.insecure().getSslSocketFactory());
267+
X509TrustManager insecureTrustManager = new X509TrustManager() {
268+
@Override public void checkClientTrusted(X509Certificate[] chain, String authType) {}
269+
@Override public void checkServerTrusted(X509Certificate[] chain, String authType) {}
270+
@Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
271+
};
272+
builder.sslFactoryFromTrustManager(insecureTrustManager);
267273
}
268274

269275
return builder.platformEndpoint(platformEndpoint)

sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import com.connectrpc.impl.ProtocolClient;
88
import com.connectrpc.okhttp.ConnectOkHttpClient;
99
import com.connectrpc.protocols.GETConfiguration;
10-
import com.connectrpc.protocols.NetworkProtocol;
1110
import com.nimbusds.jose.JOSEException;
1211
import com.nimbusds.jose.jwk.KeyUse;
1312
import com.nimbusds.jose.jwk.RSAKey;
@@ -45,6 +44,7 @@
4544
import javax.net.ssl.X509TrustManager;
4645
import java.io.IOException;
4746
import java.nio.file.Path;
47+
import java.security.GeneralSecurityException;
4848
import java.util.Collections;
4949
import java.util.List;
5050
import java.util.UUID;
@@ -77,15 +77,18 @@ public static SDKBuilder newBuilder() {
7777
}
7878

7979
/**
80-
* Configure the SDK to use the supplied {@link SSLSocketFactory} for outbound TLS connections.
81-
* Callers using this overload bring their own pre-built socket factory; cert-chain trust
82-
* material is whatever the supplied factory was built with. For full PKIX validation under a
83-
* matching {@link X509TrustManager}, use {@link #sslFactoryFromDirectory(String)} or
84-
* {@link #sslFactoryFromKeyStore(String, String)} which build both via {@link TrustProvider}.
80+
* The SDK will trust the certs that this TrustManager trusts
81+
* @param trustManager
8582
*/
86-
public SDKBuilder sslFactory(SSLSocketFactory sslSocketFactory) {
87-
this.sslSocketFactory = sslSocketFactory;
88-
this.trustManager = null;
83+
public SDKBuilder sslFactoryFromTrustManager(X509TrustManager trustManager) {
84+
TrustProvider trustProvider;
85+
try {
86+
trustProvider = TrustProvider.fromTrustManager(trustManager);
87+
} catch (IOException | GeneralSecurityException e) {
88+
throw new SDKException("error creating trust provider", e);
89+
}
90+
this.trustManager = trustManager;
91+
this.sslSocketFactory = trustProvider.getSslSocketFactory();
8992
return this;
9093
}
9194

sdk/src/main/java/io/opentdf/platform/sdk/TrustProvider.java

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import javax.net.ssl.TrustManager;
77
import javax.net.ssl.TrustManagerFactory;
88
import javax.net.ssl.X509ExtendedTrustManager;
9+
import javax.net.ssl.X509TrustManager;
910
import java.io.File;
1011
import java.io.FileInputStream;
1112
import java.io.IOException;
@@ -36,19 +37,19 @@
3637
* work is fulfilled by whichever {@link java.security.Provider} is registered with the JVM,
3738
* including FIPS-mode providers.
3839
*/
39-
public final class TrustProvider {
40+
final class TrustProvider {
4041

4142
private final SSLSocketFactory sslSocketFactory;
42-
private final X509ExtendedTrustManager trustManager;
43+
private final X509TrustManager trustManager;
4344
private final SSLContext sslContext;
4445

45-
private TrustProvider(SSLContext sslContext, X509ExtendedTrustManager trustManager) {
46+
private TrustProvider(SSLContext sslContext, X509TrustManager trustManager) {
4647
this.sslContext = sslContext;
4748
this.trustManager = trustManager;
4849
this.sslSocketFactory = sslContext.getSocketFactory();
4950
}
5051

51-
public X509ExtendedTrustManager getTrustManager() {
52+
public X509TrustManager getTrustManager() {
5253
return trustManager;
5354
}
5455

@@ -91,6 +92,12 @@ public static TrustProvider fromDirectory(String certsDirPath) throws IOExceptio
9192
return builder.build();
9293
}
9394

95+
public static TrustProvider fromTrustManager(X509TrustManager trustManager) throws IOException, GeneralSecurityException {
96+
SSLContext sslContext = SSLContext.getInstance("TLS");
97+
sslContext.init(new KeyManager[0], new TrustManager[]{trustManager}, new SecureRandom());
98+
return new TrustProvider(sslContext, trustManager);
99+
}
100+
94101
/**
95102
* Builds a {@link TrustProvider} that trusts JVM default cacerts plus the trusted-certificate
96103
* entries in the supplied keystore.
@@ -106,21 +113,6 @@ public static TrustProvider fromKeyStore(Path keystorePath, char[] password) thr
106113
return builder().withDefaultTrustMaterial().withTrustMaterial(ks).build();
107114
}
108115

109-
/**
110-
* Builds a {@link TrustProvider} that accepts every server certificate. Intended only for
111-
* tests and {@code --insecure} CLI flows.
112-
*/
113-
public static TrustProvider insecure() {
114-
try {
115-
SSLContext ctx = SSLContext.getInstance("TLS");
116-
X509ExtendedTrustManager trustAll = new InsecureTrustManager();
117-
ctx.init(new KeyManager[0], new TrustManager[]{trustAll}, new SecureRandom());
118-
return new TrustProvider(ctx, trustAll);
119-
} catch (GeneralSecurityException e) {
120-
throw new IllegalStateException("failed to build insecure TrustProvider", e);
121-
}
122-
}
123-
124116
private static KeyStore loadKeyStore(InputStream in, char[] password)
125117
throws IOException, GeneralSecurityException {
126118
// Try JKS first since it remains the JVM default; fall back to PKCS12 which is portable
@@ -179,13 +171,6 @@ public Builder withTrustMaterial(X509Certificate... certs) {
179171
return this;
180172
}
181173

182-
public Builder withTrustMaterial(Collection<X509Certificate> certs) {
183-
if (certs != null) {
184-
this.certificates.addAll(certs);
185-
}
186-
return this;
187-
}
188-
189174
public Builder withTrustMaterial(KeyStore keyStore) {
190175
if (keyStore != null) {
191176
this.keyStores.add(keyStore);
@@ -250,16 +235,4 @@ private static KeyStore newEmptyKeyStore() throws GeneralSecurityException, IOEx
250235
return ks;
251236
}
252237
}
253-
254-
private static final class InsecureTrustManager extends X509ExtendedTrustManager {
255-
private static final X509Certificate[] EMPTY = new X509Certificate[0];
256-
257-
@Override public void checkClientTrusted(X509Certificate[] chain, String authType) { }
258-
@Override public void checkClientTrusted(X509Certificate[] chain, String authType, java.net.Socket socket) { }
259-
@Override public void checkClientTrusted(X509Certificate[] chain, String authType, javax.net.ssl.SSLEngine engine) { }
260-
@Override public void checkServerTrusted(X509Certificate[] chain, String authType) { }
261-
@Override public void checkServerTrusted(X509Certificate[] chain, String authType, java.net.Socket socket) { }
262-
@Override public void checkServerTrusted(X509Certificate[] chain, String authType, javax.net.ssl.SSLEngine engine) { }
263-
@Override public X509Certificate[] getAcceptedIssuers() { return EMPTY; }
264-
}
265238
}

sdk/src/test/java/io/opentdf/platform/sdk/SDKBuilderTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import java.security.KeyStore;
4242
import java.security.cert.CertificateFactory;
4343
import java.security.cert.X509Certificate;
44+
import javax.net.ssl.TrustManager;
45+
import javax.net.ssl.TrustManagerFactory;
4446
import java.util.Arrays;
4547
import java.util.Base64;
4648
import java.util.Collections;
@@ -100,6 +102,38 @@ void testKeystoreSSLContext() throws Exception {
100102

101103
}
102104

105+
@Test
106+
void testTrustManagerSetsSslFactory() throws Exception {
107+
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
108+
keystore.load(null, null);
109+
X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509")
110+
.generateCertificate(new ByteArrayInputStream(EXAMPLE_COM_PEM.getBytes(StandardCharsets.UTF_8)));
111+
keystore.setCertificateEntry("example.com", cert);
112+
113+
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
114+
tmf.init(keystore);
115+
X509TrustManager suppliedTrustManager = null;
116+
for (TrustManager tm : tmf.getTrustManagers()) {
117+
if (tm instanceof X509TrustManager) {
118+
suppliedTrustManager = (X509TrustManager) tm;
119+
break;
120+
}
121+
}
122+
assertNotNull(suppliedTrustManager);
123+
124+
SDKBuilder builder = SDKBuilder.newBuilder().sslFactoryFromTrustManager(suppliedTrustManager);
125+
126+
SSLSocketFactory sslSocketFactory = builder.getSslFactory();
127+
assertNotNull(sslSocketFactory);
128+
129+
// the builder should retain the exact trust manager the caller supplied
130+
assertSame(suppliedTrustManager, builder.getTrustManager());
131+
132+
X509Certificate[] acceptedIssuers = builder.getTrustManager().getAcceptedIssuers();
133+
assertEquals(1, Arrays.stream(acceptedIssuers).filter(x -> x.getIssuerX500Principal().getName()
134+
.equals("CN=example.com")).count());
135+
}
136+
103137
@Test
104138
public void testPlatformPlainTextAndIDPWithSSL() throws Exception {
105139
sdkServicesSetup(false, true);

0 commit comments

Comments
 (0)