From 36e6fb7b5793687ff2a8b2116ee5d1ec4ef52fb2 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Fri, 14 Nov 2025 12:08:15 -0700 Subject: [PATCH 01/10] Update to add flag for forcing https Update version number to indicate breaking change. Fix --- pom.xml | 12 +- .../kms/v1/TenantSecurityClient.java | 231 ++++++++++----- .../kms/v1/TenantSecurityRequest.java | 11 +- .../kms/v1/DevIntegrationTest.java | 32 -- .../kms/v1/ErrorResponseTest.java | 278 +++++++++--------- .../tenantsecurity/kms/v1/KMSClientTest.java | 29 +- .../tenantsecurity/kms/v1/LocalBatch.java | 3 +- 7 files changed, 326 insertions(+), 270 deletions(-) diff --git a/pom.xml b/pom.xml index 14442d1..26948fb 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ com.ironcorelabs tenant-security-java jar - 7.2.3 + 8.0.0 tenant-security-java https://ironcorelabs.com/docs Java client library for the IronCore Labs Tenant Security Proxy. @@ -63,7 +63,7 @@ com.google.http-client - google-http-client-jackson2 + google-http-client-gson com.google.http-client @@ -144,10 +144,10 @@ maven-compiler-plugin 3.8.1 - 11 - 11 - 11 - 11 + 17 + 17 + 17 + 17 -Xlint:unchecked diff --git a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java index 4b3dbf0..2059dca 100644 --- a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java +++ b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.MalformedURLException; import java.net.URL; import java.security.SecureRandom; import java.security.Security; @@ -37,87 +38,169 @@ public final class TenantSecurityClient implements Closeable { private DeterministicTenantSecurityClient deterministicClient; - /** - * Default size of web request thread pool. Defaults to 25. - */ - public static int DEFAULT_REQUEST_THREADPOOL_SIZE = 25; + private TenantSecurityClient(Builder builder) throws Exception { + // Validate domain + TenantSecurityClient.checkUrlForm(builder.tspDomain, builder.allowInsecureHttp); - /** - * Default size of the threadpool used for AES encryptions/decryptions. Defaults to the number of - * cores on the machine being run on. - */ - public static int DEFAULT_AES_THREADPOOL_SIZE = Runtime.getRuntime().availableProcessors(); + if (builder.apiKey == null || builder.apiKey.isEmpty()) { + throw new IllegalArgumentException("No value provided for apiKey!"); + } + if (builder.randomGen == null) { + throw new IllegalArgumentException("No value provided for random number generator!"); + } + if (builder.requestThreadSize < 1) { + throw new IllegalArgumentException( + "Value provided for request threadpool size must be greater than 0!"); + } + if (builder.aesThreadSize < 1) { + throw new IllegalArgumentException( + "Value provided for AES threadpool size must be greater than 0!"); + } + if (builder.timeout < 1) { + throw new IllegalArgumentException("Value provided for timeout must be greater than 0!"); + } - /** - * Default timeout in ms for the connection to the TSP. - */ - public static int DEFAULT_TIMEOUT_MS = 20000; + this.encryptionExecutor = Executors.newFixedThreadPool(builder.aesThreadSize); + this.encryptionService = new TenantSecurityRequest(builder.tspDomain, builder.apiKey, + builder.requestThreadSize, builder.timeout); + this.deterministicClient = + new DeterministicTenantSecurityClient(this.encryptionExecutor, this.encryptionService); - /** - * Constructor for TenantSecurityClient class that uses the SecureRandom NativePRNGNonBlocking - * instance for random number generation. - * - * @param tspDomain Domain where the Tenant Security Proxy is running. - * @param apiKey Key to use for requests to the Tenant Security Proxy. - * @throws Exception If the provided domain is invalid. - */ - public TenantSecurityClient(String tspDomain, String apiKey) throws Exception { - this(tspDomain, apiKey, DEFAULT_REQUEST_THREADPOOL_SIZE, DEFAULT_AES_THREADPOOL_SIZE, - SecureRandom.getInstance("NativePRNGNonBlocking")); + Security.setProperty("crypto.policy", "unlimited"); + this.secureRandom = builder.randomGen; } /** - * Constructor for TenantSecurityClient class that allows call to provide web request and AES - * operation thread pool sizes. Uses the SecureRandom NativePRNGNonBlocking instance for random - * number generation. + * Ensures that the url is valid and if allowInsecureHttp is false that the tsp url must be https. + * Will throw if the URL isn't valid or if https is enforced and not provided. * - * @param tspDomain Domain where the Tenant Security Proxy is running. - * @param apiKey Key to use for requests to the Tenant Security Proxy. - * @param requestThreadSize Number of threads to use for fixed-size web request thread pool - * @param aesThreadSize Number of threads to use for fixed-size AES operations threadpool - * @throws Exception If the provided domain is invalid. + * @param url The Url to check + * @param allowInsecureHttp If normal http should be allowed.. */ - public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize, - int aesThreadSize) throws Exception { - this(tspDomain, apiKey, requestThreadSize, aesThreadSize, - SecureRandom.getInstance("NativePRNGNonBlocking")); + private static void checkUrlForm(String url, boolean allowInsecureHttp) { + try { + URL parsed = new URL(url); + String protocol = parsed.getProtocol(); + if (!allowInsecureHttp && !"https".equalsIgnoreCase(protocol)) { + throw new IllegalArgumentException("Insecure HTTP URL not allowed: " + url); + } + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid URL: " + url, e); + } } - /** - * Constructor for TenantSecurityClient class that allows call to provide web request and AES - * operation thread pool sizes. Uses the SecureRandom NativePRNGNonBlocking instance for random - * number generation. - * - * @param tspDomain Domain where the Tenant Security Proxy is running. - * @param apiKey Key to use for requests to the Tenant Security Proxy. - * @param requestThreadSize Number of threads to use for fixed-size web request thread pool - * @param aesThreadSize Number of threads to use for fixed-size AES operations threadpool - * @param timeout Request to TSP read and connect timeout in ms. - * - * @throws Exception If the provided domain is invalid. - */ - public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize, - int aesThreadSize, int timeout) throws Exception { - this(tspDomain, apiKey, requestThreadSize, aesThreadSize, - SecureRandom.getInstance("NativePRNGNonBlocking"), timeout); - } + public static class Builder { + + /** + * Default size of web request thread pool. Defaults to 25. + */ + public static int DEFAULT_REQUEST_THREADPOOL_SIZE = 25; + + /** + * Default size of the threadpool used for AES encryptions/decryptions. Defaults to the number + * of cores on the machine being run on. + */ + public static int DEFAULT_AES_THREADPOOL_SIZE = Runtime.getRuntime().availableProcessors(); + + /** + * Default timeout in ms for the connection to the TSP. + */ + public static int DEFAULT_TIMEOUT_MS = 20000; + + private final String tspDomain; + private final String apiKey; + + private int requestThreadSize = DEFAULT_REQUEST_THREADPOOL_SIZE; + private int aesThreadSize = DEFAULT_AES_THREADPOOL_SIZE; + private int timeout = DEFAULT_TIMEOUT_MS; + private boolean allowInsecureHttp = false; + // If this is null when build is called we set it to the default. Don't set it here + // in case the default isn't available on their OS. + private SecureRandom randomGen = null; + + /** + * Builder for TenantSecurityClient class. + * + * @param tspDomain Domain where the Tenant Security Proxy is running. + * @param apiKey Key to use for requests to the Tenant Security Proxy. + * @param tspDomain + * @param apiKey + */ + public Builder(String tspDomain, String apiKey) { + this.tspDomain = tspDomain; + this.apiKey = apiKey; + } - /** - * Constructor for TenantSecurityClient class that allows for modifying the random number - * generator used for encryption. Sets a default connect and read timeout of 20s. - * - * @param tspDomain Domain where the Tenant Security Proxy is running. - * @param apiKey Key to use for requests to the Tenant Security Proxy. - * @param requestThreadSize Number of threads to use for fixed-size web request thread pool - * @param aesThreadSize Number of threads to use for fixed-size AES operations threadpool - * @param randomGen Instance of SecureRandom to use for PRNG when performing encryption - * operations. - * @throws Exception If the provided domain is invalid or the provided SecureRandom instance is - * not set. - */ - public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize, - int aesThreadSize, SecureRandom randomGen) throws Exception { - this(tspDomain, apiKey, requestThreadSize, aesThreadSize, randomGen, DEFAULT_TIMEOUT_MS); + /** + * Sets the web request pool size. Defaults to DEFAULT_REQUEST_THREADPOOL_SIZE. + * + * @param size Number of threads to use for fixed-size web request thread pool. + * @return The builder + */ + public Builder requestThreadSize(int size) { + this.requestThreadSize = size; + return this; + } + + /** + * Sets the number of threads to use for fixed-size AES operations threadpool. Defaults to + * DEFAULT_AES_THREADPOOL_SIZE + * + * @param size The size of the aes thread pool. + * @return The builder + */ + public Builder aesThreadSize(int size) { + this.aesThreadSize = size; + return this; + } + + /** + * Sets the timeout in milliseconds for communicating with the TSP. + * + * @param timeout Timeout in milliseconds for the TSP requests. + * @return The builder + */ + public Builder timeoutMs(int timeout) { + this.timeout = timeout; + return this; + } + + /** + * Sets the random number generator. This should be set with care as the generator must be + * cryptographically secure. Defaults to "NativePRNGNonBlocking" + * + * @param random A new random number generator to use. + * @return The builder + */ + public Builder random(SecureRandom random) { + this.randomGen = random; + return this; + } + + /** + * Sets allowInsecureHttp. Defaults to false. + * + * @param allow If the TSP is allowed to be reachable via http. + * @return The builder + */ + public Builder allowInsecureHttp(boolean allow) { + this.allowInsecureHttp = allow; + return this; + } + + /** + * Construct the TenantSecurityClient fron the builder. + * + * @return The newly constructed TenantSecurityClient. + * @throws Exception If the tsp url isn't valid or if HTTPS is required and not provided. + */ + public TenantSecurityClient build() throws Exception { + // Check this here in case they don't have support for NativePRNGNonBlocking. + if (this.randomGen == null) { + this.randomGen = SecureRandom.getInstance("NativePRNGNonBlocking"); + } + return new TenantSecurityClient(this); + } } /** @@ -135,7 +218,8 @@ public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSi * not set. */ public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize, - int aesThreadSize, SecureRandom randomGen, int timeout) throws Exception { + int aesThreadSize, SecureRandom randomGen, int timeout, boolean allowInsecureHttp) + throws Exception { // Use the URL class to validate the form of the provided TSP domain URL new URL(tspDomain); if (apiKey == null || apiKey.isEmpty()) { @@ -162,8 +246,6 @@ public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSi this.deterministicClient = new DeterministicTenantSecurityClient(this.encryptionExecutor, this.encryptionService); - // Update the crypto policy to allow us to use 256 bit AES keys - Security.setProperty("crypto.policy", "unlimited"); this.secureRandom = randomGen; } @@ -191,7 +273,8 @@ public DeterministicTenantSecurityClient getDeterministicClient() { * @return CompletableFuture that resolves in a instance of the TenantSecurityClient class. */ public static CompletableFuture create(String tspDomain, String apiKey) { - return CompletableFutures.tryCatchNonFatal(() -> new TenantSecurityClient(tspDomain, apiKey)); + return CompletableFutures + .tryCatchNonFatal(() -> new TenantSecurityClient.Builder(tspDomain, apiKey).build()); } /** diff --git a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java index f417cc6..46107dd 100644 --- a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java +++ b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java @@ -20,7 +20,7 @@ import com.google.api.client.http.json.JsonHttpContent; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonObjectParser; -import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.json.gson.GsonFactory; import com.google.api.client.util.Value; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TspServiceException; @@ -35,7 +35,11 @@ * works to parse out error codes on wrap/unwrap failures. */ final class TenantSecurityRequest implements Closeable { - private static final JsonFactory JSON_FACTORY = new JacksonFactory(); + private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); + + private static String stripTrailingSlash(String s) { + return (s == null) ? null : s.replaceAll("/+$", ""); + } // Fixed sized thread pool for web requests. Limit the amount of parallel web // requests that we let go out at any given time. We don't want to DoS our @@ -67,8 +71,7 @@ final class TenantSecurityRequest implements Closeable { headers.put("x-icl-tsc-version", sdkVersion); this.httpHeaders = headers; - - String tspApiPrefix = tspDomain + "/api/1/"; + String tspApiPrefix = stripTrailingSlash(tspDomain) + "/api/1/"; this.wrapEndpoint = new GenericUrl(tspApiPrefix + "document/wrap"); this.batchWrapEndpoint = new GenericUrl(tspApiPrefix + "document/batch-wrap"); this.unwrapEndpoint = new GenericUrl(tspApiPrefix + "document/unwrap"); diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java index 6f8afea..c39fd8a 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java @@ -24,38 +24,6 @@ public class DevIntegrationTest { private String AZURE_TENANT_ID = "INTEGRATION-TEST-AZURE"; private String INTEGRATION_API_KEY = System.getenv("API_KEY"); - @Test(expectedExceptions = java.net.MalformedURLException.class) - public void constructorUrlTest() throws Exception { - new TenantSecurityClient("foobaz", "apiKey").close(); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void missingApiKeyTest() throws Exception { - new TenantSecurityClient("http://localhost", null).close(); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void emptyApiKeyTest() throws Exception { - new TenantSecurityClient("http://localhost", "").close(); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void invalidRequestThreadpoolSize() throws Exception { - new TenantSecurityClient("http://localhost", "apiKey", 0, 1).close(); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void invalidCryptoThreadpoolSize() throws Exception { - new TenantSecurityClient("http://localhost", "apiKey", 1, 0).close(); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void missingRandomGen() throws Exception { - new TenantSecurityClient("http://localhost", "apiKey", - TenantSecurityClient.DEFAULT_REQUEST_THREADPOOL_SIZE, - TenantSecurityClient.DEFAULT_AES_THREADPOOL_SIZE, null).close(); - } - private void assertEqualBytes(byte[] one, byte[] two) throws Exception { assertEquals(new String(one, "UTF-8"), new String(two, "UTF-8")); } diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java index a1e3cea..64a4dac 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java @@ -2,12 +2,6 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.nio.ByteBuffer; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.stream.IntStream; import org.testng.annotations.Test; import com.ironcorelabs.tenantsecurity.kms.v1.exception.KmsException; import com.ironcorelabs.tenantsecurity.kms.v1.exception.SecurityEventException; @@ -17,140 +11,142 @@ @Test(groups = {"unit"}) public class ErrorResponseTest { - public void exceptionFromErrorResponseTspServiceException() throws Exception { - final String staticMsg = "static message"; - final int staticHttpCode = 42; - - // TspServiceException - ErrorResponse unableToMakeReqError = - new ErrorResponse(TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST.getCode(), staticMsg); - TenantSecurityException unableToMakeReqException = - unableToMakeReqError.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, unableToMakeReqException, - TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST); - - ErrorResponse unknownErrResp = - new ErrorResponse(TenantSecurityErrorCodes.UNKNOWN_ERROR.getCode(), staticMsg); - TenantSecurityException unknownErrException = - unknownErrResp.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, unknownErrException, - TenantSecurityErrorCodes.UNKNOWN_ERROR); - - ErrorResponse invalidRequestBody = - new ErrorResponse(TenantSecurityErrorCodes.INVALID_REQUEST_BODY.getCode(), staticMsg); - TenantSecurityException invalidRequestException = - invalidRequestBody.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, invalidRequestException, - TenantSecurityErrorCodes.INVALID_REQUEST_BODY); - - ErrorResponse unauthorizedReqErrResp = - new ErrorResponse(TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST.getCode(), staticMsg); - TenantSecurityException unauthorizedReqException = - unauthorizedReqErrResp.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, unauthorizedReqException, - TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST); - - // KmsException - ErrorResponse noPrimaryKmsResp = new ErrorResponse( - TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION.getCode(), staticMsg); - TenantSecurityException noPrimaryKmsException = - noPrimaryKmsResp.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, noPrimaryKmsException, - TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION); - - ErrorResponse unknownTenantError = new ErrorResponse( - TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS.getCode(), - staticMsg); - TenantSecurityException unknownTenantException = - unknownTenantError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, unknownTenantException, - TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS); - - ErrorResponse kmsCfgDisabledError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED.getCode(), staticMsg); - TenantSecurityException kmsCfgDisabledException = - kmsCfgDisabledError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsCfgDisabledException, - TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED); - - ErrorResponse invalidEdekErrResp = - new ErrorResponse(TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK.getCode(), staticMsg); - TenantSecurityException invalidEdekException = - invalidEdekErrResp.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, invalidEdekException, - TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK); - - ErrorResponse unwrapError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_UNWRAP_FAILED.getCode(), staticMsg); - TenantSecurityException unwrapException = unwrapError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, unwrapException, - TenantSecurityErrorCodes.KMS_UNWRAP_FAILED); - - ErrorResponse wrapError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_WRAP_FAILED.getCode(), staticMsg); - TenantSecurityException kmsWrapException = wrapError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsWrapException, - TenantSecurityErrorCodes.KMS_WRAP_FAILED); - - ErrorResponse kmsAuthError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED.getCode(), staticMsg); - TenantSecurityException kmsAuthException = - kmsAuthError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsAuthException, - TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED); - - ErrorResponse kmsConfigInvalidError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID.getCode(), staticMsg); - TenantSecurityException kmsConfigInvalidException = - kmsConfigInvalidError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsConfigInvalidException, - TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID); - - ErrorResponse foo = - new ErrorResponse(TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE.getCode(), staticMsg); - TenantSecurityException fooException = foo.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, fooException, - TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE); - - ErrorResponse kmsUnreachableError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_UNREACHABLE.getCode(), staticMsg); - TenantSecurityException kmsUnreachableException = - kmsUnreachableError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsUnreachableException, - TenantSecurityErrorCodes.KMS_UNREACHABLE); - - // SecurityEventException - ErrorResponse securityEventRejectedError = - new ErrorResponse(TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED.getCode(), staticMsg); - TenantSecurityException securityEventRejectedException = - securityEventRejectedError.toTenantSecurityException(staticHttpCode); - assertSecurityEventException(staticMsg, staticHttpCode, securityEventRejectedException, - TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED); - } - - private void assertTspServiceException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); - assertTrue(exception instanceof TspServiceException); - } - - private void assertSecurityEventException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); - assertTrue(exception instanceof SecurityEventException); - } - - private void assertKmsException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); - assertTrue(exception instanceof KmsException); - } - - private void assertTenantSecurityException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertEquals(errorCode, exception.getErrorCode()); - assertEquals(exception.getHttpResponseCode(), expectedHttpStatusCode); - assertEquals(exception.getMessage(), expectedMsg); - } + public void exceptionFromErrorResponseTspServiceException() throws Exception { + final String staticMsg = "static message"; + final int staticHttpCode = 42; + + // TspServiceException + ErrorResponse unableToMakeReqError = new ErrorResponse( + TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST.getCode(), staticMsg); + TenantSecurityException unableToMakeReqException = + unableToMakeReqError.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, unableToMakeReqException, + TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST); + + ErrorResponse unknownErrResp = + new ErrorResponse(TenantSecurityErrorCodes.UNKNOWN_ERROR.getCode(), staticMsg); + TenantSecurityException unknownErrException = + unknownErrResp.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, unknownErrException, + TenantSecurityErrorCodes.UNKNOWN_ERROR); + + ErrorResponse invalidRequestBody = new ErrorResponse( + TenantSecurityErrorCodes.INVALID_REQUEST_BODY.getCode(), staticMsg); + TenantSecurityException invalidRequestException = + invalidRequestBody.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, invalidRequestException, + TenantSecurityErrorCodes.INVALID_REQUEST_BODY); + + ErrorResponse unauthorizedReqErrResp = new ErrorResponse( + TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST.getCode(), staticMsg); + TenantSecurityException unauthorizedReqException = + unauthorizedReqErrResp.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, unauthorizedReqException, + TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST); + + // KmsException + ErrorResponse noPrimaryKmsResp = new ErrorResponse( + TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION.getCode(), staticMsg); + TenantSecurityException noPrimaryKmsException = + noPrimaryKmsResp.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, noPrimaryKmsException, + TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION); + + ErrorResponse unknownTenantError = new ErrorResponse( + TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS.getCode(), + staticMsg); + TenantSecurityException unknownTenantException = + unknownTenantError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, unknownTenantException, + TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS); + + ErrorResponse kmsCfgDisabledError = new ErrorResponse( + TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED.getCode(), staticMsg); + TenantSecurityException kmsCfgDisabledException = + kmsCfgDisabledError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsCfgDisabledException, + TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED); + + ErrorResponse invalidEdekErrResp = new ErrorResponse( + TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK.getCode(), staticMsg); + TenantSecurityException invalidEdekException = + invalidEdekErrResp.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, invalidEdekException, + TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK); + + ErrorResponse unwrapError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_UNWRAP_FAILED.getCode(), staticMsg); + TenantSecurityException unwrapException = + unwrapError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, unwrapException, + TenantSecurityErrorCodes.KMS_UNWRAP_FAILED); + + ErrorResponse wrapError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_WRAP_FAILED.getCode(), staticMsg); + TenantSecurityException kmsWrapException = + wrapError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsWrapException, + TenantSecurityErrorCodes.KMS_WRAP_FAILED); + + ErrorResponse kmsAuthError = new ErrorResponse( + TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED.getCode(), staticMsg); + TenantSecurityException kmsAuthException = + kmsAuthError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsAuthException, + TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED); + + ErrorResponse kmsConfigInvalidError = new ErrorResponse( + TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID.getCode(), staticMsg); + TenantSecurityException kmsConfigInvalidException = + kmsConfigInvalidError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsConfigInvalidException, + TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID); + + ErrorResponse foo = + new ErrorResponse(TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE.getCode(), staticMsg); + TenantSecurityException fooException = foo.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, fooException, + TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE); + + ErrorResponse kmsUnreachableError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_UNREACHABLE.getCode(), staticMsg); + TenantSecurityException kmsUnreachableException = + kmsUnreachableError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsUnreachableException, + TenantSecurityErrorCodes.KMS_UNREACHABLE); + + // SecurityEventException + ErrorResponse securityEventRejectedError = new ErrorResponse( + TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED.getCode(), staticMsg); + TenantSecurityException securityEventRejectedException = + securityEventRejectedError.toTenantSecurityException(staticHttpCode); + assertSecurityEventException(staticMsg, staticHttpCode, securityEventRejectedException, + TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED); + } + + private void assertTspServiceException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); + assertTrue(exception instanceof TspServiceException); + } + + private void assertSecurityEventException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); + assertTrue(exception instanceof SecurityEventException); + } + + private void assertKmsException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); + assertTrue(exception instanceof KmsException); + } + + private void assertTenantSecurityException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertEquals(errorCode, exception.getErrorCode()); + assertEquals(exception.getHttpResponseCode(), expectedHttpStatusCode); + assertEquals(exception.getMessage(), expectedMsg); + } } diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSClientTest.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSClientTest.java index 0de490b..86d2bcc 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSClientTest.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSClientTest.java @@ -5,35 +5,40 @@ @Test(groups = {"unit"}) public class KMSClientTest { - @Test(expectedExceptions = java.net.MalformedURLException.class) + @Test(expectedExceptions = IllegalArgumentException.class) public void constructorUrlTest() throws Exception { - new TenantSecurityClient("foobaz", "apiKey").close(); + new TenantSecurityClient.Builder("foobaz", "apiKey").build().close(); } @Test(expectedExceptions = IllegalArgumentException.class) public void missingApiKeyTest() throws Exception { - new TenantSecurityClient("http://localhost", null).close(); + new TenantSecurityClient.Builder("https://localhost", null).build().close(); } @Test(expectedExceptions = IllegalArgumentException.class) public void emptyApiKeyTest() throws Exception { - new TenantSecurityClient("http://localhost", "").close(); + new TenantSecurityClient.Builder("https://localhost", "").build().close(); } @Test(expectedExceptions = IllegalArgumentException.class) - public void invalidRequestThreadpoolSize() throws Exception { - new TenantSecurityClient("http://localhost", "apiKey", 0, 1).close(); + public void httpsOnlyIsOnAndTryToUseHttpTest() throws Exception { + new TenantSecurityClient.Builder("http://localhost", "apiKey").build().close(); + } + + // Just a sanity check to ensure the default allows https + public void httpsOnlyIsOnAndTryToUseHttpsTest() throws Exception { + new TenantSecurityClient.Builder("https://localhost", "apiKey").build().close(); } @Test(expectedExceptions = IllegalArgumentException.class) - public void invalidCryptoThreadpoolSize() throws Exception { - new TenantSecurityClient("http://localhost", "apiKey", 1, 0).close(); + public void invalidRequestThreadpoolSize() throws Exception { + new TenantSecurityClient.Builder("https://localhost", "apiKey").requestThreadSize(0).build() + .close(); } @Test(expectedExceptions = IllegalArgumentException.class) - public void missingRandomGen() throws Exception { - new TenantSecurityClient("http://localhost", "apiKey", - TenantSecurityClient.DEFAULT_REQUEST_THREADPOOL_SIZE, - TenantSecurityClient.DEFAULT_AES_THREADPOOL_SIZE, null).close(); + public void invalidCryptoThreadpoolSize() throws Exception { + new TenantSecurityClient.Builder("https://localhost", "apiKey").aesThreadSize(0).build() + .close(); } } diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalBatch.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalBatch.java index f97dd3c..c2185a5 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalBatch.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalBatch.java @@ -59,7 +59,8 @@ public void batchRoundtrip() throws Exception { DocumentMetadata context = new DocumentMetadata(this.TENANT_ID, "integrationTest", "sample"); TenantSecurityClient client = - new TenantSecurityClient(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, this.API_KEY); + new TenantSecurityClient.Builder(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, + this.API_KEY).build(); int batchSize = 25; int batchRepetitions = 50; From 1009ac0110d1385aaceb43257a41ecdac96428e5 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Fri, 14 Nov 2025 12:25:28 -0700 Subject: [PATCH 02/10] Remove unused and update the version --- .../kms/v1/TenantSecurityClient.java | 46 ------------------- .../kms/v1/TenantSecurityRequest.java | 2 +- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java index 2059dca..00e96a1 100644 --- a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java +++ b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java @@ -203,52 +203,6 @@ public TenantSecurityClient build() throws Exception { } } - /** - * Constructor for TenantSecurityClient class that allows for modifying the random number - * generator used for encryption. - * - * @param tspDomain Domain where the Tenant Security Proxy is running. - * @param apiKey Key to use for requests to the Tenant Security Proxy. - * @param requestThreadSize Number of threads to use for fixed-size web request thread pool - * @param aesThreadSize Number of threads to use for fixed-size AES operations threadpool - * @param randomGen Instance of SecureRandom to use for PRNG when performing encryption - * operations. - * @param timeout Request to TSP read and connect timeout in ms. - * @throws Exception If the provided domain is invalid or the provided SecureRandom instance is - * not set. - */ - public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize, - int aesThreadSize, SecureRandom randomGen, int timeout, boolean allowInsecureHttp) - throws Exception { - // Use the URL class to validate the form of the provided TSP domain URL - new URL(tspDomain); - if (apiKey == null || apiKey.isEmpty()) { - throw new IllegalArgumentException("No value provided for apiKey!"); - } - if (randomGen == null) { - throw new IllegalArgumentException("No value provided for random number generator!"); - } - if (requestThreadSize < 1) { - throw new IllegalArgumentException( - "Value provided for request threadpool size must be greater than 0!"); - } - if (aesThreadSize < 1) { - throw new IllegalArgumentException( - "Value provided for AES threadpool size must be greater than 0!"); - } - if (timeout < 1) { - throw new IllegalArgumentException("Value provided for timeout must be greater than 0!"); - } - - this.encryptionExecutor = Executors.newFixedThreadPool(aesThreadSize); - this.encryptionService = - new TenantSecurityRequest(tspDomain, apiKey, requestThreadSize, timeout); - this.deterministicClient = - new DeterministicTenantSecurityClient(this.encryptionExecutor, this.encryptionService); - - this.secureRandom = randomGen; - } - public void close() throws IOException { this.encryptionService.close(); this.encryptionExecutor.shutdown(); diff --git a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java index 46107dd..3135cf4 100644 --- a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java +++ b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java @@ -59,7 +59,7 @@ private static String stripTrailingSlash(String s) { private final int timeout; // TSC version that will be sent to the TSP. - static final String sdkVersion = "7.2.3"; + static final String sdkVersion = "8.0.0"; TenantSecurityRequest(String tspDomain, String apiKey, int requestThreadSize, int timeout) { HttpHeaders headers = new HttpHeaders(); From 468c2966dc29d92917c603427350dd0bfda34803 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Fri, 14 Nov 2025 12:34:17 -0700 Subject: [PATCH 03/10] formatting --- .../kms/v1/ErrorResponseTest.java | 272 +++++++++--------- 1 file changed, 135 insertions(+), 137 deletions(-) diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java index 64a4dac..003e08d 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/ErrorResponseTest.java @@ -11,142 +11,140 @@ @Test(groups = {"unit"}) public class ErrorResponseTest { - public void exceptionFromErrorResponseTspServiceException() throws Exception { - final String staticMsg = "static message"; - final int staticHttpCode = 42; - - // TspServiceException - ErrorResponse unableToMakeReqError = new ErrorResponse( - TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST.getCode(), staticMsg); - TenantSecurityException unableToMakeReqException = - unableToMakeReqError.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, unableToMakeReqException, - TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST); - - ErrorResponse unknownErrResp = - new ErrorResponse(TenantSecurityErrorCodes.UNKNOWN_ERROR.getCode(), staticMsg); - TenantSecurityException unknownErrException = - unknownErrResp.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, unknownErrException, - TenantSecurityErrorCodes.UNKNOWN_ERROR); - - ErrorResponse invalidRequestBody = new ErrorResponse( - TenantSecurityErrorCodes.INVALID_REQUEST_BODY.getCode(), staticMsg); - TenantSecurityException invalidRequestException = - invalidRequestBody.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, invalidRequestException, - TenantSecurityErrorCodes.INVALID_REQUEST_BODY); - - ErrorResponse unauthorizedReqErrResp = new ErrorResponse( - TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST.getCode(), staticMsg); - TenantSecurityException unauthorizedReqException = - unauthorizedReqErrResp.toTenantSecurityException(staticHttpCode); - assertTspServiceException(staticMsg, staticHttpCode, unauthorizedReqException, - TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST); - - // KmsException - ErrorResponse noPrimaryKmsResp = new ErrorResponse( - TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION.getCode(), staticMsg); - TenantSecurityException noPrimaryKmsException = - noPrimaryKmsResp.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, noPrimaryKmsException, - TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION); - - ErrorResponse unknownTenantError = new ErrorResponse( - TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS.getCode(), - staticMsg); - TenantSecurityException unknownTenantException = - unknownTenantError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, unknownTenantException, - TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS); - - ErrorResponse kmsCfgDisabledError = new ErrorResponse( - TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED.getCode(), staticMsg); - TenantSecurityException kmsCfgDisabledException = - kmsCfgDisabledError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsCfgDisabledException, - TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED); - - ErrorResponse invalidEdekErrResp = new ErrorResponse( - TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK.getCode(), staticMsg); - TenantSecurityException invalidEdekException = - invalidEdekErrResp.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, invalidEdekException, - TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK); - - ErrorResponse unwrapError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_UNWRAP_FAILED.getCode(), staticMsg); - TenantSecurityException unwrapException = - unwrapError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, unwrapException, - TenantSecurityErrorCodes.KMS_UNWRAP_FAILED); - - ErrorResponse wrapError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_WRAP_FAILED.getCode(), staticMsg); - TenantSecurityException kmsWrapException = - wrapError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsWrapException, - TenantSecurityErrorCodes.KMS_WRAP_FAILED); - - ErrorResponse kmsAuthError = new ErrorResponse( - TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED.getCode(), staticMsg); - TenantSecurityException kmsAuthException = - kmsAuthError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsAuthException, - TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED); - - ErrorResponse kmsConfigInvalidError = new ErrorResponse( - TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID.getCode(), staticMsg); - TenantSecurityException kmsConfigInvalidException = - kmsConfigInvalidError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsConfigInvalidException, - TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID); - - ErrorResponse foo = - new ErrorResponse(TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE.getCode(), staticMsg); - TenantSecurityException fooException = foo.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, fooException, - TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE); - - ErrorResponse kmsUnreachableError = - new ErrorResponse(TenantSecurityErrorCodes.KMS_UNREACHABLE.getCode(), staticMsg); - TenantSecurityException kmsUnreachableException = - kmsUnreachableError.toTenantSecurityException(staticHttpCode); - assertKmsException(staticMsg, staticHttpCode, kmsUnreachableException, - TenantSecurityErrorCodes.KMS_UNREACHABLE); - - // SecurityEventException - ErrorResponse securityEventRejectedError = new ErrorResponse( - TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED.getCode(), staticMsg); - TenantSecurityException securityEventRejectedException = - securityEventRejectedError.toTenantSecurityException(staticHttpCode); - assertSecurityEventException(staticMsg, staticHttpCode, securityEventRejectedException, - TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED); - } - - private void assertTspServiceException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); - assertTrue(exception instanceof TspServiceException); - } - - private void assertSecurityEventException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); - assertTrue(exception instanceof SecurityEventException); - } - - private void assertKmsException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); - assertTrue(exception instanceof KmsException); - } - - private void assertTenantSecurityException(String expectedMsg, int expectedHttpStatusCode, - TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { - assertEquals(errorCode, exception.getErrorCode()); - assertEquals(exception.getHttpResponseCode(), expectedHttpStatusCode); - assertEquals(exception.getMessage(), expectedMsg); - } + public void exceptionFromErrorResponseTspServiceException() throws Exception { + final String staticMsg = "static message"; + final int staticHttpCode = 42; + + // TspServiceException + ErrorResponse unableToMakeReqError = + new ErrorResponse(TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST.getCode(), staticMsg); + TenantSecurityException unableToMakeReqException = + unableToMakeReqError.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, unableToMakeReqException, + TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST); + + ErrorResponse unknownErrResp = + new ErrorResponse(TenantSecurityErrorCodes.UNKNOWN_ERROR.getCode(), staticMsg); + TenantSecurityException unknownErrException = + unknownErrResp.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, unknownErrException, + TenantSecurityErrorCodes.UNKNOWN_ERROR); + + ErrorResponse invalidRequestBody = + new ErrorResponse(TenantSecurityErrorCodes.INVALID_REQUEST_BODY.getCode(), staticMsg); + TenantSecurityException invalidRequestException = + invalidRequestBody.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, invalidRequestException, + TenantSecurityErrorCodes.INVALID_REQUEST_BODY); + + ErrorResponse unauthorizedReqErrResp = + new ErrorResponse(TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST.getCode(), staticMsg); + TenantSecurityException unauthorizedReqException = + unauthorizedReqErrResp.toTenantSecurityException(staticHttpCode); + assertTspServiceException(staticMsg, staticHttpCode, unauthorizedReqException, + TenantSecurityErrorCodes.UNAUTHORIZED_REQUEST); + + // KmsException + ErrorResponse noPrimaryKmsResp = new ErrorResponse( + TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION.getCode(), staticMsg); + TenantSecurityException noPrimaryKmsException = + noPrimaryKmsResp.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, noPrimaryKmsException, + TenantSecurityErrorCodes.NO_PRIMARY_KMS_CONFIGURATION); + + ErrorResponse unknownTenantError = new ErrorResponse( + TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS.getCode(), + staticMsg); + TenantSecurityException unknownTenantException = + unknownTenantError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, unknownTenantException, + TenantSecurityErrorCodes.UNKNOWN_TENANT_OR_NO_ACTIVE_KMS_CONFIGURATIONS); + + ErrorResponse kmsCfgDisabledError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED.getCode(), staticMsg); + TenantSecurityException kmsCfgDisabledException = + kmsCfgDisabledError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsCfgDisabledException, + TenantSecurityErrorCodes.KMS_CONFIGURATION_DISABLED); + + ErrorResponse invalidEdekErrResp = + new ErrorResponse(TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK.getCode(), staticMsg); + TenantSecurityException invalidEdekException = + invalidEdekErrResp.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, invalidEdekException, + TenantSecurityErrorCodes.INVALID_PROVIDED_EDEK); + + ErrorResponse unwrapError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_UNWRAP_FAILED.getCode(), staticMsg); + TenantSecurityException unwrapException = unwrapError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, unwrapException, + TenantSecurityErrorCodes.KMS_UNWRAP_FAILED); + + ErrorResponse wrapError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_WRAP_FAILED.getCode(), staticMsg); + TenantSecurityException kmsWrapException = wrapError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsWrapException, + TenantSecurityErrorCodes.KMS_WRAP_FAILED); + + ErrorResponse kmsAuthError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED.getCode(), staticMsg); + TenantSecurityException kmsAuthException = + kmsAuthError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsAuthException, + TenantSecurityErrorCodes.KMS_AUTHORIZATION_FAILED); + + ErrorResponse kmsConfigInvalidError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID.getCode(), staticMsg); + TenantSecurityException kmsConfigInvalidException = + kmsConfigInvalidError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsConfigInvalidException, + TenantSecurityErrorCodes.KMS_CONFIGURATION_INVALID); + + ErrorResponse foo = + new ErrorResponse(TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE.getCode(), staticMsg); + TenantSecurityException fooException = foo.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, fooException, + TenantSecurityErrorCodes.KMS_ACCOUNT_ISSUE); + + ErrorResponse kmsUnreachableError = + new ErrorResponse(TenantSecurityErrorCodes.KMS_UNREACHABLE.getCode(), staticMsg); + TenantSecurityException kmsUnreachableException = + kmsUnreachableError.toTenantSecurityException(staticHttpCode); + assertKmsException(staticMsg, staticHttpCode, kmsUnreachableException, + TenantSecurityErrorCodes.KMS_UNREACHABLE); + + // SecurityEventException + ErrorResponse securityEventRejectedError = + new ErrorResponse(TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED.getCode(), staticMsg); + TenantSecurityException securityEventRejectedException = + securityEventRejectedError.toTenantSecurityException(staticHttpCode); + assertSecurityEventException(staticMsg, staticHttpCode, securityEventRejectedException, + TenantSecurityErrorCodes.SECURITY_EVENT_REJECTED); + } + + private void assertTspServiceException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); + assertTrue(exception instanceof TspServiceException); + } + + private void assertSecurityEventException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); + assertTrue(exception instanceof SecurityEventException); + } + + private void assertKmsException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertTenantSecurityException(expectedMsg, expectedHttpStatusCode, exception, errorCode); + assertTrue(exception instanceof KmsException); + } + + private void assertTenantSecurityException(String expectedMsg, int expectedHttpStatusCode, + TenantSecurityException exception, TenantSecurityErrorCodes errorCode) { + assertEquals(errorCode, exception.getErrorCode()); + assertEquals(exception.getHttpResponseCode(), expectedHttpStatusCode); + assertEquals(exception.getMessage(), expectedMsg); + } } From f095141b5eca02ed0dd24fb2e672434e037fa227 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 17 Nov 2025 09:19:17 -0700 Subject: [PATCH 04/10] Need to allow insecure for the tests --- .../tenantsecurity/kms/v1/DevIntegrationTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java index c39fd8a..ffa5aa0 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java @@ -14,7 +14,7 @@ import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; import com.ironcorelabs.tenantsecurity.logdriver.v1.EventMetadata; import com.ironcorelabs.tenantsecurity.logdriver.v1.UserEvent; - +import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; import org.testng.annotations.Test; @Test(groups = {"dev-integration"}) @@ -29,8 +29,9 @@ private void assertEqualBytes(byte[] one, byte[] two) throws Exception { } private CompletableFuture getClient() { - return TenantSecurityClient.create(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, - this.INTEGRATION_API_KEY); + return CompletableFutures.tryCatchNonFatal( + () -> new TenantSecurityClient.Builder(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, + this.INTEGRATION_API_KEY).allowInsecureHttp(true).build()); } private Map getRoundtripDataToEncrypt() throws Exception { From 1372cb098d21355f40c116eca8c0fbb6d183cb82 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 17 Nov 2025 09:21:05 -0700 Subject: [PATCH 05/10] Code review comments. --- pom.xml | 2 +- .../tenantsecurity/kms/v1/TenantSecurityClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 26948fb..8952cc8 100644 --- a/pom.xml +++ b/pom.xml @@ -184,7 +184,7 @@ true - com.google.http-client:google-http-client-jackson2 + com.google.http-client:google-http-client-gson com.google.http-client:google-http-client-apache-v2 com.google.guava:guava com.google.protobuf:protobuf-java diff --git a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java index 00e96a1..0074447 100644 --- a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java +++ b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java @@ -75,7 +75,7 @@ private TenantSecurityClient(Builder builder) throws Exception { * Will throw if the URL isn't valid or if https is enforced and not provided. * * @param url The Url to check - * @param allowInsecureHttp If normal http should be allowed.. + * @param allowInsecureHttp If normal http should be allowed. */ private static void checkUrlForm(String url, boolean allowInsecureHttp) { try { From 081375d60f63b63b46bceec50464d97a844f030c Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 17 Nov 2025 09:49:34 -0700 Subject: [PATCH 06/10] Fix another test. --- .../tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java index 249413c..4cdd7ec 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java @@ -9,6 +9,7 @@ import java.util.concurrent.ExecutionException; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; +import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; import org.testng.annotations.Test; @Test(groups = {"dev-integration"}) @@ -61,8 +62,9 @@ private void assertEqualBytes(byte[] one, byte[] two) throws Exception { } private CompletableFuture getClient() { - return TenantSecurityClient.create(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, - NotPrimaryAndDisabledConfigs.INTEGRATION_API_KEY); + return CompletableFutures.tryCatchNonFatal( + () -> new TenantSecurityClient.Builder(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, + NotPrimaryAndDisabledConfigs.INTEGRATION_API_KEY).allowInsecureHttp(true).build()); } private DocumentMetadata getRoundtripMetadata() { From 010b7c738eb0f7e46298ae9011bf8ce08825b3c4 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 17 Nov 2025 10:34:50 -0700 Subject: [PATCH 07/10] Fix up tests and examples --- .../ironcorelabs/large/LargeDocuments.java | 31 ++-- .../ironcorelabs/logging/LoggingExample.java | 8 +- .../com/ironcorelabs/rekey/RekeyExample.java | 149 ++++++++--------- .../ironcorelabs/simple/SimpleRoundtrip.java | 154 +++++++++++------- .../tenantsecurity/TestUtils.java | 20 +++ .../kms/v1/DevIntegrationTest.java | 7 +- .../tenantsecurity/kms/v1/KMSRequestTest.java | 13 +- .../tenantsecurity/kms/v1/LocalRoundTrip.java | 20 +-- .../kms/v1/NotPrimaryAndDisabledConfigs.java | 7 +- 9 files changed, 231 insertions(+), 178 deletions(-) create mode 100644 src/test/java/com/ironcorelabs/tenantsecurity/TestUtils.java diff --git a/examples/large-documents/src/main/java/com/ironcorelabs/large/LargeDocuments.java b/examples/large-documents/src/main/java/com/ironcorelabs/large/LargeDocuments.java index 7c390ec..6eb3ba8 100644 --- a/examples/large-documents/src/main/java/com/ironcorelabs/large/LargeDocuments.java +++ b/examples/large-documents/src/main/java/com/ironcorelabs/large/LargeDocuments.java @@ -40,7 +40,9 @@ public static void main(String[] args) throws Exception { // Initialize the client with a Tenant Security Proxy domain and API key. // Typically this would be done once when the application or service initializes - TenantSecurityClient client = TenantSecurityClient.create("http://localhost:32804", API_KEY).get(); + TenantSecurityClient client = + new TenantSecurityClient.Builder("http://localhost:32804", API_KEY) + .allowInsecureHttp(true).build(); // Create metadata used to associate this document to a tenant, name the // document, and identify the service or user making the call @@ -64,7 +66,8 @@ public static void main(String[] args) throws Exception { System.out.println("Writing encrypted files to: " + tmpFileDir); ObjectMapper objectMapper = new ObjectMapper(); - BigDoc sourceObj = objectMapper.readValue(new File("./resources/" + filename), BigDoc.class); + BigDoc sourceObj = + objectMapper.readValue(new File("./resources/" + filename), BigDoc.class); // Reduce the document to a map of all the sub documents to be encrypted with // the same key @@ -115,14 +118,17 @@ public static void main(String[] args) throws Exception { String subDocId2 = "4e57e8bd-d88a-4083-9fac-05a635110e2a"; // Read the two files out first - byte[] encryptedFile1 = Files.readAllBytes(Paths.get(tmpFileDir.toString(), subDocId1 + ".enc")); - byte[] encryptedFile2 = Files.readAllBytes(Paths.get(tmpFileDir.toString(), subDocId2 + ".enc")); + byte[] encryptedFile1 = + Files.readAllBytes(Paths.get(tmpFileDir.toString(), subDocId1 + ".enc")); + byte[] encryptedFile2 = + Files.readAllBytes(Paths.get(tmpFileDir.toString(), subDocId2 + ".enc")); // In a DB situation this edek could be stored with the large doc (if sub docs // are only decrypted in that context) or it could be stored alongside each // sub-document. In the latter case you make it harder to accidentally // cryptoshred data by de-syncing edeks at the cost of row size - String edek = new String(Files.readAllBytes(Paths.get(tmpFileDir.toString(), filename + ".edek"))); + String edek = new String( + Files.readAllBytes(Paths.get(tmpFileDir.toString(), filename + ".edek"))); // each of the documents could be individually decrypted with their own calls, // but by combining them into one structure we ensure we only make one call to @@ -133,15 +139,18 @@ public static void main(String[] args) throws Exception { EncryptedDocument encryptedPartialBigDoc = new EncryptedDocument(encryptedPartDocMap, edek); // Decrypt the two subdocuments - PlaintextDocument decryptedPartialBigDoc = client.decrypt(encryptedPartialBigDoc, metadata).get(); + PlaintextDocument decryptedPartialBigDoc = + client.decrypt(encryptedPartialBigDoc, metadata).get(); // Turn the decrypted bytes back into objects - SubDoc reSubDoc1 = objectMapper - .readValue(new String(decryptedPartialBigDoc.getDecryptedFields().get(subDocId1)), SubDoc.class); - SubDoc reSubDoc2 = objectMapper - .readValue(new String(decryptedPartialBigDoc.getDecryptedFields().get(subDocId2)), SubDoc.class); + SubDoc reSubDoc1 = objectMapper.readValue( + new String(decryptedPartialBigDoc.getDecryptedFields().get(subDocId1)), + SubDoc.class); + SubDoc reSubDoc2 = objectMapper.readValue( + new String(decryptedPartialBigDoc.getDecryptedFields().get(subDocId2)), + SubDoc.class); // just so we can write it out nicely - BigDoc rePartialBigDoc = new BigDoc("x", "x", "x", new SubDoc[] { reSubDoc1, reSubDoc2 }); + BigDoc rePartialBigDoc = new BigDoc("x", "x", "x", new SubDoc[] {reSubDoc1, reSubDoc2}); // Write out the rehydrated docs as proof that things round tripped fine Files.write(Paths.get(tmpFileDir.toString(), "partial-large-document.json"), diff --git a/examples/logging-example/src/main/java/com/ironcorelabs/logging/LoggingExample.java b/examples/logging-example/src/main/java/com/ironcorelabs/logging/LoggingExample.java index a76f2eb..bef5a59 100644 --- a/examples/logging-example/src/main/java/com/ironcorelabs/logging/LoggingExample.java +++ b/examples/logging-example/src/main/java/com/ironcorelabs/logging/LoggingExample.java @@ -28,7 +28,9 @@ public static void main(String[] args) throws Exception { // Initialize the client with a Tenant Security Proxy domain and API key. // Typically this would be done once when the application or service initializes - TenantSecurityClient client = TenantSecurityClient.create("http://localhost:32804", API_KEY).get(); + TenantSecurityClient client = + new TenantSecurityClient.Builder("http://localhost:32804", API_KEY).allowInsecureHttp(true) + .build(); // Example 1: logging a user-related event // @@ -38,8 +40,8 @@ public static void main(String[] args) throws Exception { Map otherData = new HashMap<>(); otherData.put("field1", "gumby"); otherData.put("field2", "gumby"); - EventMetadata metadata1 = new EventMetadata(TENANT_ID, "userId1", "PII", otherData, "Rq8675309", "127.0.0.1", - "userId1", System.currentTimeMillis()); + EventMetadata metadata1 = new EventMetadata(TENANT_ID, "userId1", "PII", otherData, "Rq8675309", + "127.0.0.1", "userId1", System.currentTimeMillis()); try { client.logSecurityEvent(UserEvent.LOGIN, metadata1).get(); System.out.println("Successfully logged user login event."); diff --git a/examples/rekey-example/src/main/java/com/ironcorelabs/rekey/RekeyExample.java b/examples/rekey-example/src/main/java/com/ironcorelabs/rekey/RekeyExample.java index 747d4c7..43f7e0e 100644 --- a/examples/rekey-example/src/main/java/com/ironcorelabs/rekey/RekeyExample.java +++ b/examples/rekey-example/src/main/java/com/ironcorelabs/rekey/RekeyExample.java @@ -2,6 +2,7 @@ import com.ironcorelabs.tenantsecurity.kms.v1.*; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; +import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -12,11 +13,14 @@ /** * Three parts: * - *

Encrypt a customer record + *

+ * Encrypt a customer record * - *

Rekey the encrypted record to a new tenant + *

+ * Rekey the encrypted record to a new tenant * - *

Decrypt the encrypted record using the new tenant + *

+ * Decrypt the encrypted record using the new tenant */ public class RekeyExample { @@ -44,77 +48,69 @@ public static void main(String[] args) throws Exception { // Initialize the client with a Tenant Security Proxy domain and API key. // Typically this would be done once when the application or service initializes. - CompletableFuture rekeyedRoundtrip = - TenantSecurityClient.create(TSP_ADDR, API_KEY) - .thenCompose( - client -> { - try { - // - // Part 1: Encrypting a customer record - // - - // Create metadata used to associate this document to the first tenant, name the - // document, and identify the service or user making the call - DocumentMetadata metadata = - new DocumentMetadata(TENANT_ID, "serviceOrUserId", "PII"); - - // Create a map containing your data - Map custRecord = new HashMap<>(); - custRecord.put("ssn", "000-12-2345".getBytes("UTF-8")); - custRecord.put( - "address", "2825-519 Stone Creek Rd, Bozeman, MT 59715".getBytes("UTF-8")); - custRecord.put("name", "Jim Bridger".getBytes("UTF-8")); - - System.out.println("Encrypting using tenant " + TENANT_ID); - // Request a key from the KMS and use it to encrypt the document - CompletableFuture encryptedDocument = - client.encrypt(custRecord, metadata); - - // - // Part 2: Rekey the encrypted record to a new tenant - // - - final String NEW_TENANT_ID = "tenant-aws"; - - System.out.println("Rekeying to tenant " + NEW_TENANT_ID); - - CompletableFuture rekeyedDocument = - encryptedDocument.thenCompose( - // Rekey the document to `tenant-aws` using their primary config. The - // metadata's name and identifying information could also be changed at - // this time. - encrypted -> - client.rekeyEdek(encrypted.getEdek(), metadata, NEW_TENANT_ID) - .thenApply( - newDoc -> - new EncryptedDocument(encrypted.getEncryptedFields(), - newDoc) - )); - - - - // - // Part 3: Decrypt the encrypted record using the new tenant - // - - // Create new metadata for this document indicating that it was - // rekeyed to the second tenant. The name and identifying information - // could also be changed at this time. - DocumentMetadata newMetadata = - new DocumentMetadata(NEW_TENANT_ID, "serviceOrUserId", "PII"); - - System.out.println("Decrypting with tenant " + NEW_TENANT_ID); - - CompletableFuture decryptedDocument = - rekeyedDocument.thenCompose( - // Decrypt the document encrypted to `tenant-aws` - rekeyed -> client.decrypt(rekeyed, newMetadata)); - - return decryptedDocument; - } catch (Exception e) { - throw new CompletionException(e); - } - }); + CompletableFuture rekeyedRoundtrip = CompletableFutures.tryCatchNonFatal( + () -> new TenantSecurityClient.Builder(TSP_ADDR, API_KEY).allowInsecureHttp(true).build()) + .thenCompose(client -> { + try { + // + // Part 1: Encrypting a customer record + // + + // Create metadata used to associate this document to the first tenant, name the + // document, and identify the service or user making the call + DocumentMetadata metadata = new DocumentMetadata(TENANT_ID, "serviceOrUserId", "PII"); + + // Create a map containing your data + Map custRecord = new HashMap<>(); + custRecord.put("ssn", "000-12-2345".getBytes("UTF-8")); + custRecord.put("address", + "2825-519 Stone Creek Rd, Bozeman, MT 59715".getBytes("UTF-8")); + custRecord.put("name", "Jim Bridger".getBytes("UTF-8")); + + System.out.println("Encrypting using tenant " + TENANT_ID); + // Request a key from the KMS and use it to encrypt the document + CompletableFuture encryptedDocument = + client.encrypt(custRecord, metadata); + + // + // Part 2: Rekey the encrypted record to a new tenant + // + + final String NEW_TENANT_ID = "tenant-aws"; + + System.out.println("Rekeying to tenant " + NEW_TENANT_ID); + + CompletableFuture rekeyedDocument = encryptedDocument.thenCompose( + // Rekey the document to `tenant-aws` using their primary config. The + // metadata's name and identifying information could also be changed at + // this time. + encrypted -> client.rekeyEdek(encrypted.getEdek(), metadata, NEW_TENANT_ID) + .thenApply( + newDoc -> new EncryptedDocument(encrypted.getEncryptedFields(), newDoc))); + + + + // + // Part 3: Decrypt the encrypted record using the new tenant + // + + // Create new metadata for this document indicating that it was + // rekeyed to the second tenant. The name and identifying information + // could also be changed at this time. + DocumentMetadata newMetadata = + new DocumentMetadata(NEW_TENANT_ID, "serviceOrUserId", "PII"); + + System.out.println("Decrypting with tenant " + NEW_TENANT_ID); + + CompletableFuture decryptedDocument = rekeyedDocument.thenCompose( + // Decrypt the document encrypted to `tenant-aws` + rekeyed -> client.decrypt(rekeyed, newMetadata)); + + return decryptedDocument; + } catch (Exception e) { + throw new CompletionException(e); + } + }); try { // access decrypted fields @@ -122,9 +118,8 @@ public static void main(String[] args) throws Exception { System.out.println( "Decrypted SSN: " + new String(decryptedValuesMap.get("ssn"), StandardCharsets.UTF_8)); - System.out.println( - "Decrypted address: " - + new String(decryptedValuesMap.get("address"), StandardCharsets.UTF_8)); + System.out.println("Decrypted address: " + + new String(decryptedValuesMap.get("address"), StandardCharsets.UTF_8)); System.out.println( "Decrypted name: " + new String(decryptedValuesMap.get("name"), StandardCharsets.UTF_8)); } catch (ExecutionException e) { diff --git a/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java b/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java index 6a741c9..db0e9df 100644 --- a/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java +++ b/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java @@ -63,39 +63,55 @@ public static void main(String[] args) throws Exception { CompletableFuture roundtrip = // Initialize the client with a Tenant Security Proxy domain and API key. // Typically this would be done once when the application or service initializes - TenantSecurityClient.create(TSP_ADDR, API_KEY).thenCompose(client -> { - - try { - return client.encrypt(custRecord, metadata) - .thenCompose(encryptedResults -> { - // persist the EDEK and encryptedDocument to your persistence layer - String edek = encryptedResults.getEdek(); - Map encryptedDocument = encryptedResults.getEncryptedFields(); - - // un-comment if you want to print out the encrypted data - //System.out.println("Encrypted SSN: " + new String(encryptedDocument.get("ssn"), StandardCharsets.UTF_8)); - //System.out.println("Encrypted address: " + new String(encryptedDocument.get("address"), StandardCharsets.UTF_8)); - //System.out.println("Encrypted name: " + new String(encryptedDocument.get("name"), StandardCharsets.UTF_8)); - - - // retrieve the EDEK and encryptedDocument from your persistence layer - EncryptedDocument retrievedEncryptedDocument = new EncryptedDocument(encryptedDocument, edek); - - // decrypt back into plaintext - return client.decrypt(encryptedResults, metadata); - }); - } catch (Exception e) { - throw new CompletionException(e); - } - }); + CompletableFutures + .tryCatchNonFatal(() -> new TenantSecurityClient.Builder(TSP_ADDR, API_KEY) + .allowInsecureHttp(true).build()) + .thenCompose(client -> { + try { + return client.encrypt(custRecord, metadata) + .thenCompose(encryptedResults -> { + // persist the EDEK and encryptedDocument to your + // persistence layer + String edek = encryptedResults.getEdek(); + Map encryptedDocument = + encryptedResults.getEncryptedFields(); + + // un-comment if you want to print out the encrypted + // data + // System.out.println("Encrypted SSN: " + new + // String(encryptedDocument.get("ssn"), + // StandardCharsets.UTF_8)); + // System.out.println("Encrypted address: " + new + // String(encryptedDocument.get("address"), + // StandardCharsets.UTF_8)); + // System.out.println("Encrypted name: " + new + // String(encryptedDocument.get("name"), + // StandardCharsets.UTF_8)); + + + // retrieve the EDEK and encryptedDocument from your + // persistence layer + EncryptedDocument retrievedEncryptedDocument = + new EncryptedDocument(encryptedDocument, edek); + + // decrypt back into plaintext + return client.decrypt(encryptedResults, metadata); + }); + } catch (Exception e) { + throw new CompletionException(e); + } + }); try { - // access decrypted fields + // access decrypted fields Map decryptedValuesMap = roundtrip.get().getDecryptedFields(); - System.out.println("Decrypted SSN: " + new String(decryptedValuesMap.get("ssn"), StandardCharsets.UTF_8)); - System.out.println("Decrypted address: " + new String(decryptedValuesMap.get("address"), StandardCharsets.UTF_8)); - System.out.println("Decrypted name: " + new String(decryptedValuesMap.get("name"), StandardCharsets.UTF_8)); + System.out.println("Decrypted SSN: " + + new String(decryptedValuesMap.get("ssn"), StandardCharsets.UTF_8)); + System.out.println("Decrypted address: " + + new String(decryptedValuesMap.get("address"), StandardCharsets.UTF_8)); + System.out.println("Decrypted name: " + + new String(decryptedValuesMap.get("name"), StandardCharsets.UTF_8)); } catch (ExecutionException e) { if (e.getCause() instanceof TenantSecurityException) { TenantSecurityException kmsError = (TenantSecurityException) e.getCause(); @@ -121,41 +137,57 @@ public static void main(String[] args) throws Exception { CompletableFuture roundtripFile = // Initialize the client with a Tenant Security Proxy domain and API key. // Typically this would be done once when the application or service initializes - TenantSecurityClient.create(TSP_ADDR, API_KEY).thenCompose(client -> { - - try { - return client.encrypt(toEncrypt, metadata) - .thenCompose(encryptedResults -> { - // write the encrypted file and the encrypted key to the filesystem - try { - Files.write(Paths.get(sourceFile + ".enc"), encryptedResults.getEncryptedFields().get("file")); - Files.write(Paths.get(sourceFile + ".edek"), encryptedResults.getEdek().getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - throw new CompletionException(e); - } - - // some time later... read the file from the disk - try { - byte[] encryptedBytes = Files.readAllBytes(Paths.get(sourceFile + ".enc")); - byte[] encryptedDek = Files.readAllBytes(Paths.get(sourceFile + ".edek")); - - EncryptedDocument fileAndEdek = new EncryptedDocument(Collections.singletonMap("file", encryptedBytes), new String(encryptedDek, StandardCharsets.UTF_8)); - - // decrypt - return client.decrypt(fileAndEdek, metadata); - - } catch (IOException e) { - throw new CompletionException(e); - } - }); - } catch (Exception e) { - throw new CompletionException(e); - } - }); + CompletableFutures + .tryCatchNonFatal(() -> new TenantSecurityClient.Builder(TSP_ADDR, API_KEY) + .allowInsecureHttp(true).build()) + .thenCompose(client -> { + + try { + return client.encrypt(toEncrypt, metadata) + .thenCompose(encryptedResults -> { + // write the encrypted file and the encrypted key to the + // filesystem + try { + Files.write(Paths.get(sourceFile + ".enc"), + encryptedResults.getEncryptedFields() + .get("file")); + Files.write(Paths.get(sourceFile + ".edek"), + encryptedResults.getEdek() + .getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new CompletionException(e); + } + + // some time later... read the file from the disk + try { + byte[] encryptedBytes = Files.readAllBytes( + Paths.get(sourceFile + ".enc")); + byte[] encryptedDek = Files.readAllBytes( + Paths.get(sourceFile + ".edek")); + + EncryptedDocument fileAndEdek = + new EncryptedDocument( + Collections.singletonMap("file", + encryptedBytes), + new String(encryptedDek, + StandardCharsets.UTF_8)); + + // decrypt + return client.decrypt(fileAndEdek, metadata); + + } catch (IOException e) { + throw new CompletionException(e); + } + }); + } catch (Exception e) { + throw new CompletionException(e); + } + }); try { // write the decrypted file back to the filesystem - Files.write(Paths.get("decrypted.jpg"), roundtripFile.get().getDecryptedFields().get("file")); + Files.write(Paths.get("decrypted.jpg"), + roundtripFile.get().getDecryptedFields().get("file")); } catch (ExecutionException e) { if (e.getCause() instanceof TenantSecurityException) { TenantSecurityException kmsError = (TenantSecurityException) e.getCause(); diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/TestUtils.java b/src/test/java/com/ironcorelabs/tenantsecurity/TestUtils.java new file mode 100644 index 0000000..6c0a205 --- /dev/null +++ b/src/test/java/com/ironcorelabs/tenantsecurity/TestUtils.java @@ -0,0 +1,20 @@ +package com.ironcorelabs.tenantsecurity; + +import java.util.concurrent.CompletableFuture; +import com.ironcorelabs.tenantsecurity.kms.v1.TenantSecurityClient; +import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; + +public class TestUtils { + public static CompletableFuture createTscWithAllowInsecure( + String tspAddress, String apiKey) { + return CompletableFutures.tryCatchNonFatal( + () -> new TenantSecurityClient.Builder(tspAddress, apiKey).allowInsecureHttp(true).build()); + } + + public static String ensureLeadingColon(String input) { + if (input == null || input.isEmpty()) { + return ":"; + } + return input.charAt(0) == ':' ? input : ":" + input; + } +} diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java index ffa5aa0..d68c6ca 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java @@ -11,10 +11,10 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; +import com.ironcorelabs.tenantsecurity.TestUtils; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; import com.ironcorelabs.tenantsecurity.logdriver.v1.EventMetadata; import com.ironcorelabs.tenantsecurity.logdriver.v1.UserEvent; -import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; import org.testng.annotations.Test; @Test(groups = {"dev-integration"}) @@ -29,9 +29,8 @@ private void assertEqualBytes(byte[] one, byte[] two) throws Exception { } private CompletableFuture getClient() { - return CompletableFutures.tryCatchNonFatal( - () -> new TenantSecurityClient.Builder(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, - this.INTEGRATION_API_KEY).allowInsecureHttp(true).build()); + return TestUtils.createTscWithAllowInsecure(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, + this.INTEGRATION_API_KEY); } private Map getRoundtripDataToEncrypt() throws Exception { diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSRequestTest.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSRequestTest.java index 7aa805b..61a17fc 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSRequestTest.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/KMSRequestTest.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import com.ironcorelabs.tenantsecurity.TestUtils; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; import org.testng.annotations.Test; @@ -32,7 +33,7 @@ private Map getDocument() { public void errorCodeWhenServiceNotReachable() throws Exception { CompletableFuture encrypt = - TenantSecurityClient.create("http://thisdomaindoesnotexist.eta", "apiKey") + TenantSecurityClient.create("https://thisdomaindoesnotexist.eta", "apiKey") .thenCompose(client -> client.encrypt(getDocument(), getMetadata())); try { @@ -47,9 +48,9 @@ public void errorCodeWhenServiceNotReachable() throws Exception { } public void errorCodeWhenApiKeyIsWrong() throws Exception { - CompletableFuture encrypt = - TenantSecurityClient.create(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, "wrongKey") - .thenCompose(client -> client.encrypt(getDocument(), getMetadata())); + CompletableFuture encrypt = TestUtils + .createTscWithAllowInsecure(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, "wrongKey") + .thenCompose(client -> client.encrypt(getDocument(), getMetadata())); try { encrypt.get(); @@ -66,8 +67,8 @@ public void errorCodeWhenEdekFormatIsWrong() throws Exception { documentMap.put("doc", new byte[] {3, 73, 82, 79, 78}); EncryptedDocument eDoc = new EncryptedDocument(documentMap, "d2hhdCBhIHdhc3RlIG9mIHRpbWUK"); - CompletableFuture decrypt = TenantSecurityClient - .create(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, + CompletableFuture decrypt = TestUtils + .createTscWithAllowInsecure(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, NotPrimaryAndDisabledConfigs.INTEGRATION_API_KEY) .thenCompose(client -> client.decrypt(eDoc, getMetadata())); diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalRoundTrip.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalRoundTrip.java index 463dce9..2930ee9 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalRoundTrip.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/LocalRoundTrip.java @@ -8,9 +8,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; +import com.ironcorelabs.tenantsecurity.TestUtils; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; import com.ironcorelabs.tenantsecurity.logdriver.v1.EventMetadata; import com.ironcorelabs.tenantsecurity.logdriver.v1.UserEvent; +import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; import org.testng.annotations.Test; @Test(groups = {"local-integration"}) @@ -39,20 +41,18 @@ public void roundtripTest() throws Exception { Map envVars = System.getenv(); String tsp_address = envVars.getOrDefault("TSP_ADDRESS", TestSettings.TSP_ADDRESS); - String tsp_port = envVars.getOrDefault("TSP_PORT", TestSettings.TSP_PORT); + String tsp_port = + TestUtils.ensureLeadingColon(envVars.getOrDefault("TSP_PORT", TestSettings.TSP_PORT)); String api_key = envVars.getOrDefault("API_KEY", API_KEY); String tenant_id = envVars.getOrDefault("TENANT_ID", TENANT_ID); - if (tsp_port.charAt(0) != ':') { - tsp_port = ":" + tsp_port; - } DocumentMetadata context = new DocumentMetadata(tenant_id, "integrationTest", "sample", customFields, "customRayID"); Map documentMap = getRoundtripDataToEncrypt(); - CompletableFuture roundtrip = - TenantSecurityClient.create(tsp_address + tsp_port, api_key).thenCompose(client -> { + CompletableFuture roundtrip = TestUtils + .createTscWithAllowInsecure(tsp_address + tsp_port, api_key).thenCompose(client -> { try { return client.encrypt(documentMap, context).thenCompose(encryptedResults -> { @@ -94,17 +94,13 @@ public void logSecurityEventBadTenant() throws Exception { String api_key = envVars.getOrDefault("API_KEY", API_KEY); String tenant_id = "bad-tenant-id"; - if (tsp_port.charAt(0) != ':') { - tsp_port = ":" + tsp_port; - } - EventMetadata metadata = new EventMetadata(tenant_id, "integrationTest", "sample", "app-request-id"); // even though this tenant is bad, the response here will be success as the security // event was enqueued for further processing. - CompletableFuture logEvent = - TenantSecurityClient.create(tsp_address + tsp_port, api_key).thenCompose(client -> { + CompletableFuture logEvent = TestUtils + .createTscWithAllowInsecure(tsp_address + tsp_port, api_key).thenCompose(client -> { return client.logSecurityEvent(UserEvent.ADD, metadata); }); diff --git a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java index 4cdd7ec..7ac8037 100644 --- a/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java +++ b/src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/NotPrimaryAndDisabledConfigs.java @@ -7,7 +7,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; - +import com.ironcorelabs.tenantsecurity.TestUtils; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; import org.testng.annotations.Test; @@ -62,9 +62,8 @@ private void assertEqualBytes(byte[] one, byte[] two) throws Exception { } private CompletableFuture getClient() { - return CompletableFutures.tryCatchNonFatal( - () -> new TenantSecurityClient.Builder(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, - NotPrimaryAndDisabledConfigs.INTEGRATION_API_KEY).allowInsecureHttp(true).build()); + return TestUtils.createTscWithAllowInsecure(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT, + NotPrimaryAndDisabledConfigs.INTEGRATION_API_KEY); } private DocumentMetadata getRoundtripMetadata() { From 1d9810e2c00730fe09e3ae0974312a3dd9e8b77e Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 17 Nov 2025 11:08:32 -0700 Subject: [PATCH 08/10] go to 8.0.1 because of publishing mistake and fix examples --- examples/README.md | 4 ++-- examples/large-documents/pom.xml | 2 +- examples/logging-example/pom.xml | 4 ++-- examples/rekey-example/pom.xml | 4 ++-- examples/simple-roundtrip/pom.xml | 4 ++-- .../main/java/com/ironcorelabs/simple/SimpleRoundtrip.java | 2 +- pom.xml | 4 ++-- .../tenantsecurity/kms/v1/TenantSecurityRequest.java | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/README.md b/examples/README.md index 7cd06ed..e311d24 100644 --- a/examples/README.md +++ b/examples/README.md @@ -20,11 +20,11 @@ have included this configuration in the repository as a convenience. Also note t created in IronCore's staging infrastructure. The following command will get a TSP and LD running together on your computer with the provided configuration. -The `docker-compose` command will pull both container images, then start them up together on a subnetwork, so they can +The `docker compose` command will pull both container images, then start them up together on a subnetwork, so they can communicate with each other. ```bash -docker-compose -f docker-compose.yml up +docker compose -f docker-compose.yml up ``` The TSP will be listening locally on port 32804. diff --git a/examples/large-documents/pom.xml b/examples/large-documents/pom.xml index 078652b..ccdd1d1 100644 --- a/examples/large-documents/pom.xml +++ b/examples/large-documents/pom.xml @@ -29,7 +29,7 @@ com.ironcorelabs tenant-security-java - 4.0.1 + 8.0.1 com.google.guava diff --git a/examples/logging-example/pom.xml b/examples/logging-example/pom.xml index 1079401..b809044 100644 --- a/examples/logging-example/pom.xml +++ b/examples/logging-example/pom.xml @@ -29,7 +29,7 @@ com.ironcorelabs tenant-security-java - 4.0.0 + 8.0.1 @@ -82,4 +82,4 @@ - \ No newline at end of file + diff --git a/examples/rekey-example/pom.xml b/examples/rekey-example/pom.xml index b4402d1..54d45e4 100644 --- a/examples/rekey-example/pom.xml +++ b/examples/rekey-example/pom.xml @@ -28,7 +28,7 @@ com.ironcorelabs tenant-security-java - 4.1.0 + 8.0.1 @@ -80,4 +80,4 @@ - \ No newline at end of file + diff --git a/examples/simple-roundtrip/pom.xml b/examples/simple-roundtrip/pom.xml index 778d2db..634d535 100644 --- a/examples/simple-roundtrip/pom.xml +++ b/examples/simple-roundtrip/pom.xml @@ -29,7 +29,7 @@ com.ironcorelabs tenant-security-java - 4.0.0 + 8.0.1 @@ -82,4 +82,4 @@ - \ No newline at end of file + diff --git a/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java b/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java index db0e9df..bfbdab6 100644 --- a/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java +++ b/examples/simple-roundtrip/src/main/java/com/ironcorelabs/simple/SimpleRoundtrip.java @@ -2,7 +2,7 @@ import com.ironcorelabs.tenantsecurity.kms.v1.*; import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException; - +import com.ironcorelabs.tenantsecurity.utils.CompletableFutures; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; diff --git a/pom.xml b/pom.xml index 8952cc8..2b997f8 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ com.ironcorelabs tenant-security-java jar - 8.0.0 + 8.0.1 tenant-security-java https://ironcorelabs.com/docs Java client library for the IronCore Labs Tenant Security Proxy. @@ -197,7 +197,7 @@ io.grpc:grpc-context io.grpc:grpc-api io.opencensus:opencensus-contrib-http-util - com.fasterxml.jackson.core:jackson-core + com.google.code.gson:gson diff --git a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java index 3135cf4..e30c5c3 100644 --- a/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java +++ b/src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java @@ -59,7 +59,7 @@ private static String stripTrailingSlash(String s) { private final int timeout; // TSC version that will be sent to the TSP. - static final String sdkVersion = "8.0.0"; + static final String sdkVersion = "8.0.1"; TenantSecurityRequest(String tspDomain, String apiKey, int requestThreadSize, int timeout) { HttpHeaders headers = new HttpHeaders(); From e148377b974d0a60b2caf26d6aa8acc50d35fe40 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 17 Nov 2025 11:51:00 -0700 Subject: [PATCH 09/10] Release notes --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ae7115..664cf66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v8.0.1 + +- TenantSecurityClient now enforces HTTPS connections to the TSP by default. You can opt out of this restriction using the new `TenantSecurityClient.Builder`, using `allowInsecureHttp(true)`. This should only be done in the case of testing. +- We’ve removed the direct constructors for `TenantSecurityClient` and replaced them with a builder-based API. The static TenantSecurityClient.create method is still provided for convenience. + +## v8.0.0 + +- Accidental release. Incomplete. Use 8.0.1 instead. + ## v7.2.3 - No code change, changed publishing to new sonatype. From 297d9146a7fd699eca3d64f9df7d09c6202fd6b3 Mon Sep 17 00:00:00 2001 From: Colt Frederickson Date: Mon, 17 Nov 2025 11:52:43 -0700 Subject: [PATCH 10/10] Reorder changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 664cf66..622b0e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## v8.0.1 -- TenantSecurityClient now enforces HTTPS connections to the TSP by default. You can opt out of this restriction using the new `TenantSecurityClient.Builder`, using `allowInsecureHttp(true)`. This should only be done in the case of testing. - We’ve removed the direct constructors for `TenantSecurityClient` and replaced them with a builder-based API. The static TenantSecurityClient.create method is still provided for convenience. +- TenantSecurityClient now enforces HTTPS connections to the TSP by default. You can opt out of this restriction using the new `TenantSecurityClient.Builder`, using `allowInsecureHttp(true)`. This should only be done in the case of testing. ## v8.0.0