44import java .io .IOException ;
55import java .io .InputStream ;
66import java .io .OutputStream ;
7+ import java .net .MalformedURLException ;
78import java .net .URL ;
89import java .security .SecureRandom ;
910import 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 /**
0 commit comments