Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions sdk/src/main/java/io/opentdf/platform/sdk/DefaultSrtSigner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.opentdf.platform.sdk;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.jwk.RSAKey;

final class DefaultSrtSigner implements SrtSigner {
private static final JWSHeader HEADER = new JWSHeader.Builder(JWSAlgorithm.RS256).build();
private final RSASSASigner signer;

DefaultSrtSigner(RSAKey rsaKey) {
try {
this.signer = new RSASSASigner(rsaKey);
} catch (JOSEException e) {
throw new SDKException("error creating SRT signer", e);
}
}

@Override
public byte[] sign(byte[] input) throws java.security.GeneralSecurityException {
try {
return signer.sign(HEADER, input).decode();
} catch (JOSEException e) {
throw new java.security.GeneralSecurityException("error signing SRT payload", e);
}
}

@Override
public String alg() {
return JWSAlgorithm.RS256.getName();
}
}
55 changes: 46 additions & 9 deletions sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.jca.JCAContext;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import io.opentdf.platform.kas.AccessServiceClient;
Expand All @@ -28,6 +29,7 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Set;
import java.util.function.BiFunction;

import static io.opentdf.platform.sdk.TDF.GLOBAL_KEY_SALT;
Expand All @@ -42,7 +44,7 @@ class KASClient implements SDK.KAS {
private final OkHttpClient httpClient;
private final BiFunction<OkHttpClient, String, ProtocolClient> protocolClientFactory;
private final boolean usePlaintext;
private final RSASSASigner signer;
private final JWSSigner signer;
private AsymDecryption decryptor;
private String clientPublicKey;
private KASKeyCache kasKeyCache;
Expand All @@ -53,17 +55,16 @@ class KASClient implements SDK.KAS {
* A client that communicates with KAS
*
* communicate
* @param dpopKey
* @param srtSigner
*/
KASClient(OkHttpClient httpClient, BiFunction<OkHttpClient, String, ProtocolClient> protocolClientFactory, RSAKey dpopKey, boolean usePlaintext) {
KASClient(OkHttpClient httpClient, BiFunction<OkHttpClient, String, ProtocolClient> protocolClientFactory, SrtSigner srtSigner, boolean usePlaintext) {
this.httpClient = httpClient;
this.protocolClientFactory = protocolClientFactory;
this.usePlaintext = usePlaintext;
try {
this.signer = new RSASSASigner(dpopKey);
} catch (JOSEException e) {
throw new SDKException("error creating dpop signer", e);
if (srtSigner == null) {
throw new SDKException("srtSigner must be provided");
}
this.signer = new SrtJwsSigner(srtSigner);
this.kasKeyCache = new KASKeyCache();
}

Expand Down Expand Up @@ -197,4 +198,40 @@ synchronized AccessServiceClient getStub(String url) {
return new AccessServiceClient(client);
});
}

private static final class SrtJwsSigner implements JWSSigner {
private static final JWSAlgorithm EXPECTED_ALG = JWSAlgorithm.RS256;
private final SrtSigner srtSigner;
private final JCAContext jcaContext = new JCAContext();

private SrtJwsSigner(SrtSigner srtSigner) {
this.srtSigner = srtSigner;
if (!EXPECTED_ALG.getName().equals(srtSigner.alg())) {
throw new SDKException("unsupported SRT signing algorithm: " + srtSigner.alg());
}
}

@Override
public Base64URL sign(JWSHeader header, byte[] signingInput) throws JOSEException {
if (!EXPECTED_ALG.equals(header.getAlgorithm())) {
throw new JOSEException("SRT signer algorithm mismatch: " + header.getAlgorithm());
}

try {
return Base64URL.encode(srtSigner.sign(signingInput));
} catch (java.security.GeneralSecurityException e) {
throw new JOSEException("error signing SRT payload", e);
}
}

@Override
public Set<JWSAlgorithm> supportedJWSAlgorithms() {
return Collections.singleton(EXPECTED_ALG);
}

@Override
public JCAContext getJCAContext() {
return jcaContext;
}
}
}
8 changes: 7 additions & 1 deletion sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class SDK implements AutoCloseable {
private final Interceptor authInterceptor;
private final String platformUrl;
private final ProtocolClient platformServicesClient;
private final SrtSigner srtSigner;

/**
* Closes the SDK, including its associated services.
Expand Down Expand Up @@ -87,12 +88,13 @@ public Optional<Interceptor> getAuthInterceptor() {
return Optional.ofNullable(authInterceptor);
}

SDK(Services services, TrustManager trustManager, Interceptor authInterceptor, ProtocolClient platformServicesClient, String platformUrl) {
SDK(Services services, TrustManager trustManager, Interceptor authInterceptor, ProtocolClient platformServicesClient, String platformUrl, SrtSigner srtSigner) {
this.platformUrl = platformUrl;
this.services = services;
this.trustManager = trustManager;
this.authInterceptor = authInterceptor;
this.platformServicesClient = platformServicesClient;
this.srtSigner = srtSigner;
}

public Services getServices() {
Expand Down Expand Up @@ -122,6 +124,10 @@ public ProtocolClient getPlatformServicesClient() {
return this.platformServicesClient;
}

public Optional<SrtSigner> getSrtSigner() {
return Optional.ofNullable(srtSigner);
}

/**
* Checks to see if this has the structure of a Z-TDF in that it is a zip file
* containing
Expand Down
22 changes: 16 additions & 6 deletions sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class SDKBuilder {
private SSLFactory sslFactory;
private AuthorizationGrant authzGrant;
private ProtocolType protocolType = ProtocolType.CONNECT;
private SrtSigner srtSigner;

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

Expand Down Expand Up @@ -177,6 +178,11 @@ public SDKBuilder protocol(ProtocolType protocolType) {
return this;
}

public SDKBuilder srtSigner(SrtSigner signer) {
this.srtSigner = signer;
return this;
}

private Interceptor getAuthInterceptor(RSAKey rsaKey) {
if (platformEndpoint == null) {
throw new SDKException("cannot build an SDK without specifying the platform endpoint");
Expand Down Expand Up @@ -236,14 +242,16 @@ static class ServicesAndInternals {
final Interceptor interceptor;
final TrustManager trustManager;
final ProtocolClient protocolClient;
final SrtSigner srtSigner;

final SDK.Services services;

ServicesAndInternals(Interceptor interceptor, TrustManager trustManager, SDK.Services services, ProtocolClient protocolClient) {
ServicesAndInternals(Interceptor interceptor, TrustManager trustManager, SDK.Services services, ProtocolClient protocolClient, SrtSigner srtSigner) {
this.interceptor = interceptor;
this.trustManager = trustManager;
this.services = services;
this.protocolClient = protocolClient;
this.srtSigner = srtSigner;
}
}

Expand All @@ -267,7 +275,8 @@ ServicesAndInternals buildServices() {

this.platformEndpoint = AddressNormalizer.normalizeAddress(this.platformEndpoint, this.usePlainText);
var authInterceptor = getAuthInterceptor(dpopKey);
var kasClient = getKASClient(dpopKey, authInterceptor);
var srtSignerToUse = this.srtSigner == null ? new DefaultSrtSigner(dpopKey) : this.srtSigner;
var kasClient = getKASClient(srtSignerToUse, authInterceptor);
var httpClient = getHttpClient();
var client = getProtocolClient(platformEndpoint, httpClient, authInterceptor);
var attributeService = new AttributesServiceClient(client);
Expand Down Expand Up @@ -337,18 +346,19 @@ public SDK.KAS kas() {
authInterceptor,
sslFactory == null ? null : sslFactory.getTrustManager().orElse(null),
services,
client);
client,
srtSignerToUse);
}

@Nonnull
private KASClient getKASClient(RSAKey dpopKey, Interceptor interceptor) {
private KASClient getKASClient(SrtSigner srtSigner, Interceptor interceptor) {
BiFunction<OkHttpClient, String, ProtocolClient> protocolClientFactory = (OkHttpClient client, String address) -> getProtocolClient(address, client, interceptor);
return new KASClient(getHttpClient(), protocolClientFactory, dpopKey, usePlainText);
return new KASClient(getHttpClient(), protocolClientFactory, srtSigner, usePlainText);
}

public SDK build() {
var services = buildServices();
return new SDK(services.services, services.trustManager, services.interceptor, services.protocolClient, platformEndpoint);
return new SDK(services.services, services.trustManager, services.interceptor, services.protocolClient, platformEndpoint, services.srtSigner);
}

private ProtocolClient getUnauthenticatedProtocolClient(String endpoint, OkHttpClient httpClient) {
Expand Down
7 changes: 7 additions & 0 deletions sdk/src/main/java/io/opentdf/platform/sdk/SrtSigner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.opentdf.platform.sdk;

public interface SrtSigner {
byte[] sign(byte[] input) throws java.security.GeneralSecurityException;

String alg();
}
Loading
Loading