Skip to content

Commit 36e6fb7

Browse files
committed
Update to add flag for forcing https
Update version number to indicate breaking change. Fix
1 parent 2912cff commit 36e6fb7

File tree

7 files changed

+326
-270
lines changed

7 files changed

+326
-270
lines changed

pom.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<groupId>com.ironcorelabs</groupId>
99
<artifactId>tenant-security-java</artifactId>
1010
<packaging>jar</packaging>
11-
<version>7.2.3</version>
11+
<version>8.0.0</version>
1212
<name>tenant-security-java</name>
1313
<url>https://ironcorelabs.com/docs</url>
1414
<description>Java client library for the IronCore Labs Tenant Security Proxy.</description>
@@ -63,7 +63,7 @@
6363
</dependency>
6464
<dependency>
6565
<groupId>com.google.http-client</groupId>
66-
<artifactId>google-http-client-jackson2</artifactId>
66+
<artifactId>google-http-client-gson</artifactId>
6767
</dependency>
6868
<dependency>
6969
<groupId>com.google.http-client</groupId>
@@ -144,10 +144,10 @@
144144
<artifactId>maven-compiler-plugin</artifactId>
145145
<version>3.8.1</version>
146146
<configuration>
147-
<source>11</source>
148-
<target>11</target>
149-
<testSource>11</testSource>
150-
<testTarget>11</testTarget>
147+
<source>17</source>
148+
<target>17</target>
149+
<testSource>17</testSource>
150+
<testTarget>17</testTarget>
151151
<compilerArgument>-Xlint:unchecked</compilerArgument>
152152
</configuration>
153153
</plugin>

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java

Lines changed: 157 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.io.IOException;
55
import java.io.InputStream;
66
import java.io.OutputStream;
7+
import java.net.MalformedURLException;
78
import java.net.URL;
89
import java.security.SecureRandom;
910
import java.security.Security;
@@ -37,87 +38,169 @@ public final class TenantSecurityClient implements Closeable {
3738

3839
private DeterministicTenantSecurityClient deterministicClient;
3940

40-
/**
41-
* Default size of web request thread pool. Defaults to 25.
42-
*/
43-
public static int DEFAULT_REQUEST_THREADPOOL_SIZE = 25;
41+
private TenantSecurityClient(Builder builder) throws Exception {
42+
// Validate domain
43+
TenantSecurityClient.checkUrlForm(builder.tspDomain, builder.allowInsecureHttp);
4444

45-
/**
46-
* Default size of the threadpool used for AES encryptions/decryptions. Defaults to the number of
47-
* cores on the machine being run on.
48-
*/
49-
public static int DEFAULT_AES_THREADPOOL_SIZE = Runtime.getRuntime().availableProcessors();
45+
if (builder.apiKey == null || builder.apiKey.isEmpty()) {
46+
throw new IllegalArgumentException("No value provided for apiKey!");
47+
}
48+
if (builder.randomGen == null) {
49+
throw new IllegalArgumentException("No value provided for random number generator!");
50+
}
51+
if (builder.requestThreadSize < 1) {
52+
throw new IllegalArgumentException(
53+
"Value provided for request threadpool size must be greater than 0!");
54+
}
55+
if (builder.aesThreadSize < 1) {
56+
throw new IllegalArgumentException(
57+
"Value provided for AES threadpool size must be greater than 0!");
58+
}
59+
if (builder.timeout < 1) {
60+
throw new IllegalArgumentException("Value provided for timeout must be greater than 0!");
61+
}
5062

51-
/**
52-
* Default timeout in ms for the connection to the TSP.
53-
*/
54-
public static int DEFAULT_TIMEOUT_MS = 20000;
63+
this.encryptionExecutor = Executors.newFixedThreadPool(builder.aesThreadSize);
64+
this.encryptionService = new TenantSecurityRequest(builder.tspDomain, builder.apiKey,
65+
builder.requestThreadSize, builder.timeout);
66+
this.deterministicClient =
67+
new DeterministicTenantSecurityClient(this.encryptionExecutor, this.encryptionService);
5568

56-
/**
57-
* Constructor for TenantSecurityClient class that uses the SecureRandom NativePRNGNonBlocking
58-
* instance for random number generation.
59-
*
60-
* @param tspDomain Domain where the Tenant Security Proxy is running.
61-
* @param apiKey Key to use for requests to the Tenant Security Proxy.
62-
* @throws Exception If the provided domain is invalid.
63-
*/
64-
public TenantSecurityClient(String tspDomain, String apiKey) throws Exception {
65-
this(tspDomain, apiKey, DEFAULT_REQUEST_THREADPOOL_SIZE, DEFAULT_AES_THREADPOOL_SIZE,
66-
SecureRandom.getInstance("NativePRNGNonBlocking"));
69+
Security.setProperty("crypto.policy", "unlimited");
70+
this.secureRandom = builder.randomGen;
6771
}
6872

6973
/**
70-
* Constructor for TenantSecurityClient class that allows call to provide web request and AES
71-
* operation thread pool sizes. Uses the SecureRandom NativePRNGNonBlocking instance for random
72-
* number generation.
74+
* Ensures that the url is valid and if allowInsecureHttp is false that the tsp url must be https.
75+
* Will throw if the URL isn't valid or if https is enforced and not provided.
7376
*
74-
* @param tspDomain Domain where the Tenant Security Proxy is running.
75-
* @param apiKey Key to use for requests to the Tenant Security Proxy.
76-
* @param requestThreadSize Number of threads to use for fixed-size web request thread pool
77-
* @param aesThreadSize Number of threads to use for fixed-size AES operations threadpool
78-
* @throws Exception If the provided domain is invalid.
77+
* @param url The Url to check
78+
* @param allowInsecureHttp If normal http should be allowed..
7979
*/
80-
public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize,
81-
int aesThreadSize) throws Exception {
82-
this(tspDomain, apiKey, requestThreadSize, aesThreadSize,
83-
SecureRandom.getInstance("NativePRNGNonBlocking"));
80+
private static void checkUrlForm(String url, boolean allowInsecureHttp) {
81+
try {
82+
URL parsed = new URL(url);
83+
String protocol = parsed.getProtocol();
84+
if (!allowInsecureHttp && !"https".equalsIgnoreCase(protocol)) {
85+
throw new IllegalArgumentException("Insecure HTTP URL not allowed: " + url);
86+
}
87+
} catch (MalformedURLException e) {
88+
throw new IllegalArgumentException("Invalid URL: " + url, e);
89+
}
8490
}
8591

86-
/**
87-
* Constructor for TenantSecurityClient class that allows call to provide web request and AES
88-
* operation thread pool sizes. Uses the SecureRandom NativePRNGNonBlocking instance for random
89-
* number generation.
90-
*
91-
* @param tspDomain Domain where the Tenant Security Proxy is running.
92-
* @param apiKey Key to use for requests to the Tenant Security Proxy.
93-
* @param requestThreadSize Number of threads to use for fixed-size web request thread pool
94-
* @param aesThreadSize Number of threads to use for fixed-size AES operations threadpool
95-
* @param timeout Request to TSP read and connect timeout in ms.
96-
*
97-
* @throws Exception If the provided domain is invalid.
98-
*/
99-
public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize,
100-
int aesThreadSize, int timeout) throws Exception {
101-
this(tspDomain, apiKey, requestThreadSize, aesThreadSize,
102-
SecureRandom.getInstance("NativePRNGNonBlocking"), timeout);
103-
}
92+
public static class Builder {
93+
94+
/**
95+
* Default size of web request thread pool. Defaults to 25.
96+
*/
97+
public static int DEFAULT_REQUEST_THREADPOOL_SIZE = 25;
98+
99+
/**
100+
* Default size of the threadpool used for AES encryptions/decryptions. Defaults to the number
101+
* of cores on the machine being run on.
102+
*/
103+
public static int DEFAULT_AES_THREADPOOL_SIZE = Runtime.getRuntime().availableProcessors();
104+
105+
/**
106+
* Default timeout in ms for the connection to the TSP.
107+
*/
108+
public static int DEFAULT_TIMEOUT_MS = 20000;
109+
110+
private final String tspDomain;
111+
private final String apiKey;
112+
113+
private int requestThreadSize = DEFAULT_REQUEST_THREADPOOL_SIZE;
114+
private int aesThreadSize = DEFAULT_AES_THREADPOOL_SIZE;
115+
private int timeout = DEFAULT_TIMEOUT_MS;
116+
private boolean allowInsecureHttp = false;
117+
// If this is null when build is called we set it to the default. Don't set it here
118+
// in case the default isn't available on their OS.
119+
private SecureRandom randomGen = null;
120+
121+
/**
122+
* Builder for TenantSecurityClient class.
123+
*
124+
* @param tspDomain Domain where the Tenant Security Proxy is running.
125+
* @param apiKey Key to use for requests to the Tenant Security Proxy.
126+
* @param tspDomain
127+
* @param apiKey
128+
*/
129+
public Builder(String tspDomain, String apiKey) {
130+
this.tspDomain = tspDomain;
131+
this.apiKey = apiKey;
132+
}
104133

105-
/**
106-
* Constructor for TenantSecurityClient class that allows for modifying the random number
107-
* generator used for encryption. Sets a default connect and read timeout of 20s.
108-
*
109-
* @param tspDomain Domain where the Tenant Security Proxy is running.
110-
* @param apiKey Key to use for requests to the Tenant Security Proxy.
111-
* @param requestThreadSize Number of threads to use for fixed-size web request thread pool
112-
* @param aesThreadSize Number of threads to use for fixed-size AES operations threadpool
113-
* @param randomGen Instance of SecureRandom to use for PRNG when performing encryption
114-
* operations.
115-
* @throws Exception If the provided domain is invalid or the provided SecureRandom instance is
116-
* not set.
117-
*/
118-
public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize,
119-
int aesThreadSize, SecureRandom randomGen) throws Exception {
120-
this(tspDomain, apiKey, requestThreadSize, aesThreadSize, randomGen, DEFAULT_TIMEOUT_MS);
134+
/**
135+
* Sets the web request pool size. Defaults to DEFAULT_REQUEST_THREADPOOL_SIZE.
136+
*
137+
* @param size Number of threads to use for fixed-size web request thread pool.
138+
* @return The builder
139+
*/
140+
public Builder requestThreadSize(int size) {
141+
this.requestThreadSize = size;
142+
return this;
143+
}
144+
145+
/**
146+
* Sets the number of threads to use for fixed-size AES operations threadpool. Defaults to
147+
* DEFAULT_AES_THREADPOOL_SIZE
148+
*
149+
* @param size The size of the aes thread pool.
150+
* @return The builder
151+
*/
152+
public Builder aesThreadSize(int size) {
153+
this.aesThreadSize = size;
154+
return this;
155+
}
156+
157+
/**
158+
* Sets the timeout in milliseconds for communicating with the TSP.
159+
*
160+
* @param timeout Timeout in milliseconds for the TSP requests.
161+
* @return The builder
162+
*/
163+
public Builder timeoutMs(int timeout) {
164+
this.timeout = timeout;
165+
return this;
166+
}
167+
168+
/**
169+
* Sets the random number generator. This should be set with care as the generator must be
170+
* cryptographically secure. Defaults to "NativePRNGNonBlocking"
171+
*
172+
* @param random A new random number generator to use.
173+
* @return The builder
174+
*/
175+
public Builder random(SecureRandom random) {
176+
this.randomGen = random;
177+
return this;
178+
}
179+
180+
/**
181+
* Sets allowInsecureHttp. Defaults to false.
182+
*
183+
* @param allow If the TSP is allowed to be reachable via http.
184+
* @return The builder
185+
*/
186+
public Builder allowInsecureHttp(boolean allow) {
187+
this.allowInsecureHttp = allow;
188+
return this;
189+
}
190+
191+
/**
192+
* Construct the TenantSecurityClient fron the builder.
193+
*
194+
* @return The newly constructed TenantSecurityClient.
195+
* @throws Exception If the tsp url isn't valid or if HTTPS is required and not provided.
196+
*/
197+
public TenantSecurityClient build() throws Exception {
198+
// Check this here in case they don't have support for NativePRNGNonBlocking.
199+
if (this.randomGen == null) {
200+
this.randomGen = SecureRandom.getInstance("NativePRNGNonBlocking");
201+
}
202+
return new TenantSecurityClient(this);
203+
}
121204
}
122205

123206
/**
@@ -135,7 +218,8 @@ public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSi
135218
* not set.
136219
*/
137220
public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSize,
138-
int aesThreadSize, SecureRandom randomGen, int timeout) throws Exception {
221+
int aesThreadSize, SecureRandom randomGen, int timeout, boolean allowInsecureHttp)
222+
throws Exception {
139223
// Use the URL class to validate the form of the provided TSP domain URL
140224
new URL(tspDomain);
141225
if (apiKey == null || apiKey.isEmpty()) {
@@ -162,8 +246,6 @@ public TenantSecurityClient(String tspDomain, String apiKey, int requestThreadSi
162246
this.deterministicClient =
163247
new DeterministicTenantSecurityClient(this.encryptionExecutor, this.encryptionService);
164248

165-
// Update the crypto policy to allow us to use 256 bit AES keys
166-
Security.setProperty("crypto.policy", "unlimited");
167249
this.secureRandom = randomGen;
168250
}
169251

@@ -191,7 +273,8 @@ public DeterministicTenantSecurityClient getDeterministicClient() {
191273
* @return CompletableFuture that resolves in a instance of the TenantSecurityClient class.
192274
*/
193275
public static CompletableFuture<TenantSecurityClient> create(String tspDomain, String apiKey) {
194-
return CompletableFutures.tryCatchNonFatal(() -> new TenantSecurityClient(tspDomain, apiKey));
276+
return CompletableFutures
277+
.tryCatchNonFatal(() -> new TenantSecurityClient.Builder(tspDomain, apiKey).build());
195278
}
196279

197280
/**

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import com.google.api.client.http.json.JsonHttpContent;
2121
import com.google.api.client.json.JsonFactory;
2222
import com.google.api.client.json.JsonObjectParser;
23-
import com.google.api.client.json.jackson2.JacksonFactory;
23+
import com.google.api.client.json.gson.GsonFactory;
2424
import com.google.api.client.util.Value;
2525
import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException;
2626
import com.ironcorelabs.tenantsecurity.kms.v1.exception.TspServiceException;
@@ -35,7 +35,11 @@
3535
* works to parse out error codes on wrap/unwrap failures.
3636
*/
3737
final class TenantSecurityRequest implements Closeable {
38-
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
38+
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
39+
40+
private static String stripTrailingSlash(String s) {
41+
return (s == null) ? null : s.replaceAll("/+$", "");
42+
}
3943

4044
// Fixed sized thread pool for web requests. Limit the amount of parallel web
4145
// 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 {
6771
headers.put("x-icl-tsc-version", sdkVersion);
6872

6973
this.httpHeaders = headers;
70-
71-
String tspApiPrefix = tspDomain + "/api/1/";
74+
String tspApiPrefix = stripTrailingSlash(tspDomain) + "/api/1/";
7275
this.wrapEndpoint = new GenericUrl(tspApiPrefix + "document/wrap");
7376
this.batchWrapEndpoint = new GenericUrl(tspApiPrefix + "document/batch-wrap");
7477
this.unwrapEndpoint = new GenericUrl(tspApiPrefix + "document/unwrap");

src/test/java/com/ironcorelabs/tenantsecurity/kms/v1/DevIntegrationTest.java

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,6 @@ public class DevIntegrationTest {
2424
private String AZURE_TENANT_ID = "INTEGRATION-TEST-AZURE";
2525
private String INTEGRATION_API_KEY = System.getenv("API_KEY");
2626

27-
@Test(expectedExceptions = java.net.MalformedURLException.class)
28-
public void constructorUrlTest() throws Exception {
29-
new TenantSecurityClient("foobaz", "apiKey").close();
30-
}
31-
32-
@Test(expectedExceptions = IllegalArgumentException.class)
33-
public void missingApiKeyTest() throws Exception {
34-
new TenantSecurityClient("http://localhost", null).close();
35-
}
36-
37-
@Test(expectedExceptions = IllegalArgumentException.class)
38-
public void emptyApiKeyTest() throws Exception {
39-
new TenantSecurityClient("http://localhost", "").close();
40-
}
41-
42-
@Test(expectedExceptions = IllegalArgumentException.class)
43-
public void invalidRequestThreadpoolSize() throws Exception {
44-
new TenantSecurityClient("http://localhost", "apiKey", 0, 1).close();
45-
}
46-
47-
@Test(expectedExceptions = IllegalArgumentException.class)
48-
public void invalidCryptoThreadpoolSize() throws Exception {
49-
new TenantSecurityClient("http://localhost", "apiKey", 1, 0).close();
50-
}
51-
52-
@Test(expectedExceptions = IllegalArgumentException.class)
53-
public void missingRandomGen() throws Exception {
54-
new TenantSecurityClient("http://localhost", "apiKey",
55-
TenantSecurityClient.DEFAULT_REQUEST_THREADPOOL_SIZE,
56-
TenantSecurityClient.DEFAULT_AES_THREADPOOL_SIZE, null).close();
57-
}
58-
5927
private void assertEqualBytes(byte[] one, byte[] two) throws Exception {
6028
assertEquals(new String(one, "UTF-8"), new String(two, "UTF-8"));
6129
}

0 commit comments

Comments
 (0)