From 1c12d23b44e98b6ab61a53f56bb11ebdf62b0427 Mon Sep 17 00:00:00 2001 From: strantalis Date: Mon, 12 Jan 2026 14:43:02 -0500 Subject: [PATCH] feat(sdk): expose base key --- README.md | 6 ++++ pom.xml | 15 ++++++++ sdk/pom.xml | 2 +- .../java/io/opentdf/platform/sdk/SDK.java | 10 ++++++ .../java/io/opentdf/platform/sdk/SDKTest.java | 35 ++++++++++++++++++- 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e83d3bd6..fc24ae39 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,12 @@ public class Example { .platformEndpoint("https://your.cluster/") .build(); + // Fetch the platform base key (if configured) + sdk.getBaseKey().ifPresent(baseKey -> { + System.out.println(baseKey.getKasUri()); + System.out.println(baseKey.getPublicKey().getKid()); + }); + // Encrypt a file try (InputStream in = new FileInputStream("input.plaintext")) { Config.TDFConfig c = Config.newTDFConfig(Config.withDataAttributes("attr1", "attr2")); diff --git a/pom.xml b/pom.xml index c7308b44..fc4654da 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ 4.29.2 1.82 10.0.0 + 1.14.12 0.8.13 jacoco @@ -156,6 +157,20 @@ bcprov-jdk18on ${bouncycastle.version} + + + net.bytebuddy + byte-buddy + ${bytebuddy.version} + + + net.bytebuddy + byte-buddy-agent + ${bytebuddy.version} + diff --git a/sdk/pom.xml b/sdk/pom.xml index 29a933c3..5767f5b3 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -592,4 +592,4 @@ - \ No newline at end of file + diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java index 3f0325f9..1194bdd7 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java @@ -4,6 +4,7 @@ import com.connectrpc.impl.ProtocolClient; import io.opentdf.platform.authorization.AuthorizationServiceClientInterface; +import io.opentdf.platform.policy.SimpleKasKey; import io.opentdf.platform.policy.attributes.AttributesServiceClientInterface; import io.opentdf.platform.policy.kasregistry.KeyAccessServerRegistryServiceClientInterface; import io.opentdf.platform.policy.namespaces.NamespaceServiceClientInterface; @@ -98,6 +99,15 @@ public Services getServices() { return this.services; } + /** + * Fetch the platform "base key" from the well-known configuration, if present. + *

+ * This is read from the {@code base_key} field returned by {@code GetWellKnownConfiguration}. + */ + public Optional getBaseKey() { + return Planner.fetchBaseKey(services.wellknown()); + } + public TDF.Reader loadTDF(SeekableByteChannel channel, Config.TDFReaderConfig config) throws SDKException, IOException { var tdf = new TDF(services); return tdf.loadTDF(channel, config, platformUrl); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/SDKTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/SDKTest.java index c5dda9eb..44e30dce 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/SDKTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/SDKTest.java @@ -1,8 +1,14 @@ package io.opentdf.platform.sdk; import com.connectrpc.impl.ProtocolClient; +import com.google.protobuf.Struct; +import com.google.protobuf.Value; +import io.opentdf.platform.policy.Algorithm; +import io.opentdf.platform.wellknownconfiguration.GetWellKnownConfigurationResponse; +import io.opentdf.platform.wellknownconfiguration.WellKnownServiceClientInterface; import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import java.io.IOException; import java.nio.ByteBuffer; @@ -30,6 +36,33 @@ void testReadingProtocolClient() { assertThat(sdk.getPlatformServicesClient()).isSameAs(platformServicesClient); } + @Test + void testGettingBaseKey() { + var platformServicesClient = mock(ProtocolClient.class); + var wellknownService = Mockito.mock(WellKnownServiceClientInterface.class); + var baseKeyJson = "{\"kas_url\":\"https://example.com/base_key\",\"public_key\":{\"algorithm\":\"ALGORITHM_RSA_2048\",\"kid\":\"thekid\",\"pem\": \"thepem\"}}"; + var val = Value.newBuilder().setStringValue(baseKeyJson).build(); + var config = Struct.newBuilder().putFields("base_key", val).build(); + var response = GetWellKnownConfigurationResponse + .newBuilder() + .setConfiguration(config) + .build(); + + Mockito.when(wellknownService.getWellKnownConfigurationBlocking(Mockito.any(), Mockito.anyMap())) + .thenReturn(TestUtil.successfulUnaryCall(response)); + + var services = new FakeServicesBuilder().setWellknownService(wellknownService).build(); + var sdk = new SDK(services, null, null, platformServicesClient, null); + + var baseKey = sdk.getBaseKey(); + assertThat(baseKey).isPresent(); + var simpleKasKey = baseKey.get(); + assertThat(simpleKasKey.getKasUri()).isEqualTo("https://example.com/base_key"); + assertThat(simpleKasKey.getPublicKey().getAlgorithm()).isEqualTo(Algorithm.ALGORITHM_RSA_2048); + assertThat(simpleKasKey.getPublicKey().getKid()).isEqualTo("thekid"); + assertThat(simpleKasKey.getPublicKey().getPem()).isEqualTo("thepem"); + } + @Test void testAuthorizationServiceClientV2() { var platformServicesClient = mock(ProtocolClient.class); @@ -108,4 +141,4 @@ void testReadingRandomBytes() { assertThat(SDK.isTDF(new SeekableInMemoryByteChannel(tdf))).isFalse(); } -} \ No newline at end of file +}