Skip to content

Commit 80246a9

Browse files
authored
feat(sdk): add ssl context (#58)
- Issue #57 - Add kickstart SSLFactory to allow the configuration of a custom truststore for communications.
1 parent 1edd666 commit 80246a9

File tree

6 files changed

+264
-39
lines changed

6 files changed

+264
-39
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ OpenTDF Java SDK
44

55
### Logging
66
We use [slf4j](https://www.slf4j.org/), without providing a backend. We use log4j2 in our tests.
7+
8+
### SSL - Untrusted Certificates
9+
Use the SDKBuilder.withSSL... methods to build an SDKBuilder with:
10+
- An SSLFactory: ```sdkBuilder.sslFactory(mySSLFactory)```
11+
- Directory containing trusted certificates: ```sdkBuilder.sslFactoryFromDirectory(myDirectoryWithCerts)```
12+
- Java Keystore: ```sdkBuilder.sslFactoryFromKeyStore(keystorepath, keystorePassword)```

pom.xml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<log4j.version>2.20.0</log4j.version>
1818
<grpc.version>1.63.0</grpc.version>
1919
<protobuf.version>3.25.3</protobuf.version>
20+
<sslcontext.version>8.3.5</sslcontext.version>
2021
</properties>
2122
<modules>
2223
<module>protocol</module>
@@ -69,6 +70,45 @@
6970
<version>3.4</version>
7071
<scope>provided</scope>
7172
</dependency>
73+
<dependency>
74+
<groupId>io.github.hakky54</groupId>
75+
<artifactId>sslcontext-kickstart-for-pem</artifactId>
76+
<version>${sslcontext.version}</version>
77+
<exclusions>
78+
<exclusion>
79+
<groupId>org.slf4j</groupId>
80+
<artifactId>slf4j-api</artifactId>
81+
</exclusion>
82+
</exclusions>
83+
</dependency>
84+
<dependency>
85+
<groupId>io.github.hakky54</groupId>
86+
<artifactId>sslcontext-kickstart</artifactId>
87+
<version>${sslcontext.version}</version>
88+
<exclusions>
89+
<exclusion>
90+
<groupId>org.slf4j</groupId>
91+
<artifactId>slf4j-api</artifactId>
92+
</exclusion>
93+
</exclusions>
94+
</dependency>
95+
<dependency>
96+
<groupId>io.github.hakky54</groupId>
97+
<artifactId>sslcontext-kickstart-for-netty</artifactId>
98+
<version>${sslcontext.version}</version>
99+
<exclusions>
100+
<exclusion>
101+
<groupId>org.slf4j</groupId>
102+
<artifactId>slf4j-api</artifactId>
103+
</exclusion>
104+
</exclusions>
105+
</dependency>
106+
<dependency>
107+
<groupId>io.grpc</groupId>
108+
<artifactId>grpc-netty-shaded</artifactId>
109+
<version>${grpc.version}</version>
110+
<scope>runtime</scope>
111+
</dependency>
72112
</dependencies>
73113
</dependencyManagement>
74114

sdk/pom.xml

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,6 @@
44
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
55
<modelVersion>4.0.0</modelVersion>
66
<name>sdk</name>
7-
<build>
8-
<plugins>
9-
<plugin>
10-
<groupId>org.apache.maven.plugins</groupId>
11-
<artifactId>maven-compiler-plugin</artifactId>
12-
<configuration>
13-
<source>11</source>
14-
<target>11</target>
15-
</configuration>
16-
</plugin>
17-
</plugins>
18-
</build>
197
<artifactId>sdk</artifactId>
208
<parent>
219
<artifactId>sdk-pom</artifactId>
@@ -111,5 +99,23 @@
11199
<version>1.26.1</version>
112100
<scope>test</scope>
113101
</dependency>
102+
<dependency>
103+
<groupId>io.github.hakky54</groupId>
104+
<artifactId>sslcontext-kickstart-for-pem</artifactId>
105+
</dependency>
106+
<dependency>
107+
<groupId>io.github.hakky54</groupId>
108+
<artifactId>sslcontext-kickstart</artifactId>
109+
</dependency>
110+
<dependency>
111+
<groupId>io.github.hakky54</groupId>
112+
<artifactId>sslcontext-kickstart-for-netty</artifactId>
113+
</dependency>
114+
<dependency>
115+
<groupId>com.squareup.okhttp3</groupId>
116+
<artifactId>okhttp-tls</artifactId>
117+
<version>5.0.0-alpha.14</version>
118+
<scope>test</scope>
119+
</dependency>
114120
</dependencies>
115121
</project>

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.grpc.ForwardingClientCall;
2323
import io.grpc.Metadata;
2424
import io.grpc.MethodDescriptor;
25+
import nl.altindag.ssl.SSLFactory;
2526
import org.slf4j.Logger;
2627
import org.slf4j.LoggerFactory;
2728

@@ -40,7 +41,7 @@ class GRPCAuthInterceptor implements ClientInterceptor {
4041
private final ClientAuthentication clientAuth;
4142
private final RSAKey rsaKey;
4243
private final URI tokenEndpointURI;
43-
44+
private SSLFactory sslFactory;
4445
private static final Logger logger = LoggerFactory.getLogger(GRPCAuthInterceptor.class);
4546

4647

@@ -49,11 +50,13 @@ class GRPCAuthInterceptor implements ClientInterceptor {
4950
*
5051
* @param clientAuth the client authentication to be used by the interceptor
5152
* @param rsaKey the RSA key to be used by the interceptor
53+
* @param sslFactory Optional SSLFactory for Requests
5254
*/
53-
public GRPCAuthInterceptor(ClientAuthentication clientAuth, RSAKey rsaKey, URI tokenEndpointURI) {
55+
public GRPCAuthInterceptor(ClientAuthentication clientAuth, RSAKey rsaKey, URI tokenEndpointURI, SSLFactory sslFactory) {
5456
this.clientAuth = clientAuth;
5557
this.rsaKey = rsaKey;
5658
this.tokenEndpointURI = tokenEndpointURI;
59+
this.sslFactory = sslFactory;
5760
}
5861

5962
/**
@@ -114,6 +117,9 @@ private synchronized AccessToken getToken() {
114117
TokenRequest tokenRequest = new TokenRequest(this.tokenEndpointURI,
115118
clientAuth, clientGrant, null);
116119
HTTPRequest httpRequest = tokenRequest.toHTTPRequest();
120+
if(sslFactory!=null){
121+
httpRequest.setSSLSocketFactory(sslFactory.getSslSocketFactory());
122+
}
117123

118124
DPoPProofFactory dpopFactory = new DefaultDPoPProofFactory(rsaKey, JWSAlgorithm.RS256);
119125

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

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@
1111
import com.nimbusds.oauth2.sdk.id.ClientID;
1212
import com.nimbusds.oauth2.sdk.id.Issuer;
1313
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
14-
import io.grpc.ManagedChannel;
15-
import io.grpc.ManagedChannelBuilder;
16-
import io.grpc.Status;
17-
import io.grpc.StatusRuntimeException;
14+
import io.grpc.*;
1815
import io.opentdf.platform.wellknownconfiguration.GetWellKnownConfigurationRequest;
1916
import io.opentdf.platform.wellknownconfiguration.GetWellKnownConfigurationResponse;
2017
import io.opentdf.platform.wellknownconfiguration.WellKnownServiceGrpc;
18+
import nl.altindag.ssl.SSLFactory;
19+
import nl.altindag.ssl.pem.util.PemUtils;
2120
import org.slf4j.Logger;
2221
import org.slf4j.LoggerFactory;
2322

23+
import javax.net.ssl.X509ExtendedTrustManager;
24+
import java.io.File;
25+
import java.io.FileInputStream;
2426
import java.io.IOException;
27+
import java.io.InputStream;
28+
import java.nio.file.Path;
29+
import java.util.ArrayList;
30+
import java.util.List;
2531
import java.util.UUID;
2632

2733
/**
@@ -32,6 +38,7 @@ public class SDKBuilder {
3238
private String platformEndpoint = null;
3339
private ClientAuthentication clientAuth = null;
3440
private Boolean usePlainText;
41+
private SSLFactory sslFactory;
3542

3643
private static final Logger logger = LoggerFactory.getLogger(SDKBuilder.class);
3744

@@ -44,6 +51,47 @@ public static SDKBuilder newBuilder() {
4451
return builder;
4552
}
4653

54+
public SDKBuilder sslFactory(SSLFactory sslFactory) {
55+
this.sslFactory = sslFactory;
56+
return this;
57+
}
58+
59+
/**
60+
* Add SSL Context with trusted certs from certDirPath
61+
* @param certsDirPath Path to a directory containing .pem or .crt trusted certs
62+
* @return
63+
*/
64+
public SDKBuilder sslFactoryFromDirectory(String certsDirPath) throws Exception{
65+
File certsDir = new File(certsDirPath);
66+
File[] certFiles =
67+
certsDir.listFiles((dir, name) -> name.endsWith(".pem") || name.endsWith(".crt"));
68+
logger.info("Loading certificates from: " + certsDir.getAbsolutePath());
69+
List<InputStream> certStreams = new ArrayList<>();
70+
for (File certFile : certFiles) {
71+
certStreams.add(new FileInputStream(certFile));
72+
}
73+
X509ExtendedTrustManager trustManager =
74+
PemUtils.loadTrustMaterial(certStreams.toArray(new InputStream[0]));
75+
this.sslFactory =
76+
SSLFactory.builder().withDefaultTrustMaterial().withSystemTrustMaterial()
77+
.withTrustMaterial(trustManager).build();
78+
return this;
79+
}
80+
81+
/**
82+
* Add SSL Context with default system trust material + certs contained in a Java keystore
83+
* @param keystorePath Path to keystore
84+
* @param keystorePassword Password to keystore
85+
* @return
86+
*/
87+
public SDKBuilder sslFactoryFromKeyStore(String keystorePath, String keystorePassword) {
88+
this.sslFactory =
89+
SSLFactory.builder().withDefaultTrustMaterial().withSystemTrustMaterial()
90+
.withTrustMaterial(Path.of(keystorePath), keystorePassword==null ?
91+
"".toCharArray() : keystorePassword.toCharArray()).build();
92+
return this;
93+
}
94+
4795
public SDKBuilder platformEndpoint(String platformEndpoint) {
4896
this.platformEndpoint = platformEndpoint;
4997
return this;
@@ -104,12 +152,16 @@ private GRPCAuthInterceptor getGrpcAuthInterceptor(RSAKey rsaKey) {
104152
Issuer issuer = new Issuer(platformIssuer);
105153
OIDCProviderMetadata providerMetadata;
106154
try {
107-
providerMetadata = OIDCProviderMetadata.resolve(issuer);
155+
providerMetadata = OIDCProviderMetadata.resolve(issuer, httpRequest -> {
156+
if (sslFactory!=null) {
157+
httpRequest.setSSLSocketFactory(sslFactory.getSslSocketFactory());
158+
}
159+
});
108160
} catch (IOException | GeneralException e) {
109161
throw new SDKException("Error resolving the OIDC provider metadata", e);
110162
}
111163

112-
return new GRPCAuthInterceptor(clientAuth, rsaKey, providerMetadata.getTokenEndpointURI());
164+
return new GRPCAuthInterceptor(clientAuth, rsaKey, providerMetadata.getTokenEndpointURI(), sslFactory);
113165
}
114166

115167
SDK.Services buildServices() {
@@ -141,12 +193,21 @@ public SDK build() {
141193
* @return {@type ManagedChannelBuilder<?>} configured with the SDK options
142194
*/
143195
private ManagedChannelBuilder<?> getManagedChannelBuilder(String endpoint) {
144-
ManagedChannelBuilder<?> channelBuilder = ManagedChannelBuilder
145-
.forTarget(endpoint);
196+
ManagedChannelBuilder<?> channelBuilder;
197+
if (sslFactory != null) {
198+
channelBuilder = Grpc.newChannelBuilder(endpoint, TlsChannelCredentials.newBuilder()
199+
.trustManager(sslFactory.getTrustManager().get()).build());
200+
}else{
201+
channelBuilder = ManagedChannelBuilder.forTarget(endpoint);
202+
}
146203

147204
if (usePlainText) {
148205
channelBuilder = channelBuilder.usePlaintext();
149206
}
150207
return channelBuilder;
151208
}
209+
210+
SSLFactory getSslFactory(){
211+
return this.sslFactory;
212+
}
152213
}

0 commit comments

Comments
 (0)