Skip to content

Commit 407327a

Browse files
mkleeneclaude
andcommitted
feat(sdk): replace ayza libraries with TrustProvider on JCA
Remove the three io.github.hakky54:ayza* dependencies and replace their TLS trust-material role with an SDK-owned TrustProvider built on provider-agnostic JCA APIs (CertificateFactory, KeyStore, TrustManagerFactory, SSLContext). This works under any registered crypto provider, including BC-FIPS, and avoids hardcoded provider names. - Add TrustProvider and package-private CompositeX509ExtendedTrustManager for combining JVM default + custom trust material. - SDKBuilder: replace SSLFactory field with SSLSocketFactory + X509TrustManager. sslFactory(SSLFactory) becomes sslFactory(SSLSocketFactory); add sslFactory(SSLSocketFactory, X509TrustManager) for callers that have a matching trust manager. sslFactoryFromDirectory / sslFactoryFromKeyStore signatures and semantics are preserved, now backed by TrustProvider internally. - TokenSource takes SSLSocketFactory directly. - Command.java --insecure path uses TrustProvider.insecure(). - SDKBuilderTest reworked to drop nl.altindag imports and use TrustProvider + standard JCA. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent df4682d commit 407327a

8 files changed

Lines changed: 490 additions & 109 deletions

File tree

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import io.opentdf.platform.sdk.KeyType;
1919
import io.opentdf.platform.sdk.SDK;
2020
import io.opentdf.platform.sdk.SDKBuilder;
21-
import nl.altindag.ssl.SSLFactory;
21+
import io.opentdf.platform.sdk.TrustProvider;
2222
import picocli.CommandLine;
2323
import picocli.CommandLine.HelpCommand;
2424
import picocli.CommandLine.Option;
@@ -262,10 +262,8 @@ void encrypt(
262262
private SDK buildSDK() {
263263
SDKBuilder builder = new SDKBuilder();
264264
if (insecure) {
265-
SSLFactory sslFactory = SSLFactory.builder()
266-
.withUnsafeTrustMaterial() // Trust all certificates
267-
.build();
268-
builder.sslFactory(sslFactory);
265+
// Trust all certificates
266+
builder.sslFactory(TrustProvider.insecure().getSslSocketFactory());
269267
}
270268

271269
return builder.platformEndpoint(platformEndpoint)

pom.xml

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
<grpc.version>1.75.0</grpc.version>
1818
<protobuf.version>4.29.2</protobuf.version>
1919
<bouncycastle.version>1.82</bouncycastle.version>
20-
<ayza.version>10.0.0</ayza.version>
2120
<bytebuddy.version>1.18.3</bytebuddy.version>
2221
<!-- JaCoCo Properties -->
2322
<jacoco.version>0.8.13</jacoco.version>
@@ -78,39 +77,6 @@
7877
<version>3.4</version>
7978
<scope>provided</scope>
8079
</dependency>
81-
<dependency>
82-
<groupId>io.github.hakky54</groupId>
83-
<artifactId>ayza-for-pem</artifactId>
84-
<version>${ayza.version}</version>
85-
<exclusions>
86-
<exclusion>
87-
<groupId>org.slf4j</groupId>
88-
<artifactId>slf4j-api</artifactId>
89-
</exclusion>
90-
</exclusions>
91-
</dependency>
92-
<dependency>
93-
<groupId>io.github.hakky54</groupId>
94-
<artifactId>ayza</artifactId>
95-
<version>${ayza.version}</version>
96-
<exclusions>
97-
<exclusion>
98-
<groupId>org.slf4j</groupId>
99-
<artifactId>slf4j-api</artifactId>
100-
</exclusion>
101-
</exclusions>
102-
</dependency>
103-
<dependency>
104-
<groupId>io.github.hakky54</groupId>
105-
<artifactId>ayza-for-netty</artifactId>
106-
<version>${ayza.version}</version>
107-
<exclusions>
108-
<exclusion>
109-
<groupId>org.slf4j</groupId>
110-
<artifactId>slf4j-api</artifactId>
111-
</exclusion>
112-
</exclusions>
113-
</dependency>
11480
<dependency>
11581
<groupId>io.grpc</groupId>
11682
<artifactId>grpc-netty-shaded</artifactId>

sdk/pom.xml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,6 @@
3131
<artifactId>oauth2-oidc-sdk</artifactId>
3232
<version>11.10.1</version>
3333
</dependency>
34-
<dependency>
35-
<groupId>io.github.hakky54</groupId>
36-
<artifactId>ayza-for-pem</artifactId>
37-
</dependency>
38-
<dependency>
39-
<groupId>io.github.hakky54</groupId>
40-
<artifactId>ayza</artifactId>
41-
</dependency>
42-
<dependency>
43-
<groupId>io.github.hakky54</groupId>
44-
<artifactId>ayza-for-netty</artifactId>
45-
</dependency>
4634
<!-- Serialization and Deserialization Dependencies -->
4735
<dependency>
4836
<groupId>com.google.code.gson</groupId>
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package io.opentdf.platform.sdk;
2+
3+
import javax.net.ssl.SSLEngine;
4+
import javax.net.ssl.X509ExtendedTrustManager;
5+
import java.net.Socket;
6+
import java.security.cert.CertificateException;
7+
import java.security.cert.X509Certificate;
8+
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.LinkedHashSet;
11+
import java.util.List;
12+
import java.util.Set;
13+
14+
final class CompositeX509ExtendedTrustManager extends X509ExtendedTrustManager {
15+
16+
private final List<X509ExtendedTrustManager> delegates;
17+
private final X509Certificate[] acceptedIssuers;
18+
19+
CompositeX509ExtendedTrustManager(List<X509ExtendedTrustManager> delegates) {
20+
if (delegates == null || delegates.isEmpty()) {
21+
throw new IllegalArgumentException("at least one trust manager is required");
22+
}
23+
this.delegates = Collections.unmodifiableList(new ArrayList<>(delegates));
24+
Set<X509Certificate> issuers = new LinkedHashSet<>();
25+
for (X509ExtendedTrustManager tm : this.delegates) {
26+
X509Certificate[] tmIssuers = tm.getAcceptedIssuers();
27+
if (tmIssuers != null) {
28+
Collections.addAll(issuers, tmIssuers);
29+
}
30+
}
31+
this.acceptedIssuers = issuers.toArray(new X509Certificate[0]);
32+
}
33+
34+
@Override
35+
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
36+
CertificateException last = null;
37+
for (X509ExtendedTrustManager tm : delegates) {
38+
try {
39+
tm.checkClientTrusted(chain, authType);
40+
return;
41+
} catch (CertificateException e) {
42+
last = e;
43+
}
44+
}
45+
throw last;
46+
}
47+
48+
@Override
49+
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
50+
CertificateException last = null;
51+
for (X509ExtendedTrustManager tm : delegates) {
52+
try {
53+
tm.checkClientTrusted(chain, authType, socket);
54+
return;
55+
} catch (CertificateException e) {
56+
last = e;
57+
}
58+
}
59+
throw last;
60+
}
61+
62+
@Override
63+
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
64+
CertificateException last = null;
65+
for (X509ExtendedTrustManager tm : delegates) {
66+
try {
67+
tm.checkClientTrusted(chain, authType, engine);
68+
return;
69+
} catch (CertificateException e) {
70+
last = e;
71+
}
72+
}
73+
throw last;
74+
}
75+
76+
@Override
77+
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
78+
CertificateException last = null;
79+
for (X509ExtendedTrustManager tm : delegates) {
80+
try {
81+
tm.checkServerTrusted(chain, authType);
82+
return;
83+
} catch (CertificateException e) {
84+
last = e;
85+
}
86+
}
87+
throw last;
88+
}
89+
90+
@Override
91+
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
92+
CertificateException last = null;
93+
for (X509ExtendedTrustManager tm : delegates) {
94+
try {
95+
tm.checkServerTrusted(chain, authType, socket);
96+
return;
97+
} catch (CertificateException e) {
98+
last = e;
99+
}
100+
}
101+
throw last;
102+
}
103+
104+
@Override
105+
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
106+
CertificateException last = null;
107+
for (X509ExtendedTrustManager tm : delegates) {
108+
try {
109+
tm.checkServerTrusted(chain, authType, engine);
110+
return;
111+
} catch (CertificateException e) {
112+
last = e;
113+
}
114+
}
115+
throw last;
116+
}
117+
118+
@Override
119+
public X509Certificate[] getAcceptedIssuers() {
120+
return acceptedIssuers.clone();
121+
}
122+
}

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

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,17 @@
3434
import io.opentdf.platform.wellknownconfiguration.GetWellKnownConfigurationResponse;
3535
import io.opentdf.platform.wellknownconfiguration.WellKnownServiceClient;
3636
import io.opentdf.platform.wellknownconfiguration.WellKnownServiceClientInterface;
37-
import nl.altindag.ssl.SSLFactory;
38-
import nl.altindag.ssl.pem.util.PemUtils;
3937
import okhttp3.OkHttpClient;
4038
import okhttp3.Protocol;
4139
import org.slf4j.Logger;
4240
import org.slf4j.LoggerFactory;
4341

4442
import javax.annotation.Nonnull;
43+
import javax.net.ssl.SSLSocketFactory;
4544
import javax.net.ssl.TrustManager;
46-
import javax.net.ssl.X509ExtendedTrustManager;
47-
import java.io.File;
48-
import java.io.FileInputStream;
45+
import javax.net.ssl.X509TrustManager;
4946
import java.io.IOException;
50-
import java.io.InputStream;
5147
import java.nio.file.Path;
52-
import java.util.ArrayList;
5348
import java.util.Collections;
5449
import java.util.List;
5550
import java.util.UUID;
@@ -63,7 +58,8 @@ public class SDKBuilder {
6358
private String platformEndpoint = null;
6459
private ClientAuthentication clientAuth = null;
6560
private Boolean usePlainText;
66-
private SSLFactory sslFactory;
61+
private SSLSocketFactory sslSocketFactory;
62+
private X509TrustManager trustManager;
6763
private AuthorizationGrant authzGrant;
6864
private ProtocolType protocolType = ProtocolType.CONNECT;
6965
private SrtSigner srtSigner;
@@ -80,42 +76,61 @@ public static SDKBuilder newBuilder() {
8076
return builder;
8177
}
8278

83-
public SDKBuilder sslFactory(SSLFactory sslFactory) {
84-
this.sslFactory = sslFactory;
79+
/**
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}.
85+
*/
86+
public SDKBuilder sslFactory(SSLSocketFactory sslSocketFactory) {
87+
this.sslSocketFactory = sslSocketFactory;
88+
this.trustManager = null;
89+
return this;
90+
}
91+
92+
/**
93+
* Configure the SDK to use the supplied {@link SSLSocketFactory} together with a matching
94+
* {@link X509TrustManager}. The trust manager is used by OkHttp for certificate pinning and
95+
* cleartext-fallback decisions; supply this overload when the caller has a trust manager that
96+
* matches the socket factory's trust material (e.g. both built from a {@link TrustProvider}).
97+
*/
98+
public SDKBuilder sslFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
99+
this.sslSocketFactory = sslSocketFactory;
100+
this.trustManager = trustManager;
85101
return this;
86102
}
87103

88104
/**
89105
* Add SSL Context with trusted certs from certDirPath
90-
*
106+
*
91107
* @param certsDirPath Path to a directory containing .pem or .crt trusted certs
92108
*/
93109
public SDKBuilder sslFactoryFromDirectory(String certsDirPath) throws Exception {
94-
File certsDir = new File(certsDirPath);
95-
File[] certFiles = certsDir.listFiles((dir, name) -> name.endsWith(".pem") || name.endsWith(".crt"));
96-
logger.info("Loading certificates from: " + certsDir.getAbsolutePath());
97-
List<InputStream> certStreams = new ArrayList<>(certFiles.length);
98-
for (File certFile : certFiles) {
99-
certStreams.add(new FileInputStream(certFile));
100-
}
101-
X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial(certStreams.toArray(new InputStream[0]));
102-
this.sslFactory = SSLFactory.builder().withDefaultTrustMaterial().withSystemTrustMaterial()
103-
.withTrustMaterial(trustManager).build();
110+
logger.info("Loading certificates from: {}", certsDirPath);
111+
TrustProvider provider = TrustProvider.fromDirectory(certsDirPath);
112+
this.sslSocketFactory = provider.getSslSocketFactory();
113+
this.trustManager = provider.getTrustManager();
104114
return this;
105115
}
106116

107117
/**
108118
* Add SSL Context with default system trust material + certs contained in a
109119
* Java keystore
110-
*
120+
*
111121
* @param keystorePath Path to keystore
112122
* @param keystorePassword Password to keystore
113123
*/
114124
public SDKBuilder sslFactoryFromKeyStore(String keystorePath, String keystorePassword) {
115-
this.sslFactory = SSLFactory.builder().withDefaultTrustMaterial().withSystemTrustMaterial()
116-
.withTrustMaterial(Path.of(keystorePath),
117-
keystorePassword == null ? "".toCharArray() : keystorePassword.toCharArray())
118-
.build();
125+
try {
126+
TrustProvider provider = TrustProvider.fromKeyStore(
127+
Path.of(keystorePath),
128+
keystorePassword == null ? "".toCharArray() : keystorePassword.toCharArray());
129+
this.sslSocketFactory = provider.getSslSocketFactory();
130+
this.trustManager = provider.getTrustManager();
131+
} catch (IOException | java.security.GeneralSecurityException e) {
132+
throw new SDKException("failed to load keystore from " + keystorePath, e);
133+
}
119134
return this;
120135
}
121136

@@ -223,8 +238,8 @@ private Interceptor getAuthInterceptor(RSAKey rsaKey) {
223238
OIDCProviderMetadata providerMetadata;
224239
try {
225240
providerMetadata = OIDCProviderMetadata.resolve(issuer, httpRequest -> {
226-
if (sslFactory != null) {
227-
httpRequest.setSSLSocketFactory(sslFactory.getSslSocketFactory());
241+
if (sslSocketFactory != null) {
242+
httpRequest.setSSLSocketFactory(sslSocketFactory);
228243
}
229244
});
230245
} catch (IOException | GeneralException e) {
@@ -234,7 +249,7 @@ private Interceptor getAuthInterceptor(RSAKey rsaKey) {
234249
if (this.authzGrant == null) {
235250
this.authzGrant = new ClientCredentialsGrant();
236251
}
237-
var ts = new TokenSource(clientAuth, rsaKey, providerMetadata.getTokenEndpointURI(), this.authzGrant, sslFactory);
252+
var ts = new TokenSource(clientAuth, rsaKey, providerMetadata.getTokenEndpointURI(), this.authzGrant, sslSocketFactory);
238253
return new AuthInterceptor(ts);
239254
}
240255

@@ -344,7 +359,7 @@ public SDK.KAS kas() {
344359

345360
return new ServicesAndInternals(
346361
authInterceptor,
347-
sslFactory == null ? null : sslFactory.getTrustManager().orElse(null),
362+
trustManager,
348363
services,
349364
client,
350365
srtSignerToUse);
@@ -378,6 +393,7 @@ private ProtocolClient getProtocolClient(String endpoint, OkHttpClient httpClien
378393
return new ProtocolClient(new ConnectOkHttpClient(httpClient), protocolClientConfig);
379394
}
380395

396+
@SuppressWarnings("deprecation")
381397
private OkHttpClient getHttpClient() {
382398
// using a single http client is apparently the best practice, subject to everyone wanting to
383399
// have the same protocols
@@ -387,17 +403,24 @@ private OkHttpClient getHttpClient() {
387403
// expect HTTP/2, and Connect protocol can communicate with gRPC servers over HTTP/2
388404
httpClient.protocols(List.of(Protocol.H2_PRIOR_KNOWLEDGE));
389405
}
390-
if (sslFactory != null) {
391-
var trustManager = sslFactory.getTrustManager();
392-
if (trustManager.isEmpty()) {
393-
throw new SDKException("SSL factory must have a trust manager");
406+
if (sslSocketFactory != null) {
407+
if (trustManager != null) {
408+
httpClient.sslSocketFactory(sslSocketFactory, trustManager);
409+
} else {
410+
// Caller supplied an SSLSocketFactory without a matching trust manager (e.g. via
411+
// sslFactory(SSLSocketFactory)). Falls back to OkHttp's reflection-based platform
412+
// default trust manager — only the SSLSocketFactory governs the actual handshake.
413+
httpClient.sslSocketFactory(sslSocketFactory);
394414
}
395-
httpClient.sslSocketFactory(sslFactory.getSslSocketFactory(), trustManager.get());
396415
}
397416
return httpClient.build();
398417
}
399418

400-
SSLFactory getSslFactory() {
401-
return this.sslFactory;
419+
SSLSocketFactory getSslFactory() {
420+
return this.sslSocketFactory;
421+
}
422+
423+
X509TrustManager getTrustManager() {
424+
return this.trustManager;
402425
}
403426
}

0 commit comments

Comments
 (0)