Skip to content

Commit 6564098

Browse files
authored
DRILL-8499: new util for creating random strings (#2918)
1 parent 40500ec commit 6564098

File tree

4 files changed

+229
-4
lines changed

4 files changed

+229
-4
lines changed

LICENSE

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,15 @@
201201
See the License for the specific language governing permissions and
202202
limitations under the License.
203203

204+
---------------------------------------------------------------------------
205+
This product includes code from Apache Commons Lang3, licensed under the
206+
Apache License 2.0.
207+
208+
Apache Commons Lang
209+
Copyright 2001-2024 The Apache Software Foundation
210+
211+
- exec/java-exec/src/main/java/org/apache/drill/exec/util/SecureRandomStringUtils.java
212+
204213
---------------------------------------------------------------------------
205214
This product includes source licensed under the MIT license.
206215

drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@
3737
import javax.servlet.http.HttpSessionEvent;
3838
import javax.servlet.http.HttpSessionListener;
3939

40-
import org.apache.commons.lang3.RandomStringUtils;
4140
import org.apache.commons.logging.Log;
4241
import org.apache.commons.logging.LogFactory;
4342
import org.apache.drill.exec.server.rest.CsrfTokenInjectFilter;
4443
import org.apache.drill.exec.server.rest.CsrfTokenValidateFilter;
4544
import com.google.common.collect.ImmutableSet;
45+
import org.apache.drill.exec.util.SecureRandomStringUtils;
4646
import org.apache.drill.yarn.appMaster.Dispatcher;
4747
import org.apache.drill.yarn.core.DrillOnYarnConfig;
4848
import org.bouncycastle.asn1.x500.X500NameBuilder;
@@ -428,7 +428,7 @@ private ServerConnector createHttpsConnector(Config config) throws Exception {
428428
certificate.verify(certificate.getPublicKey());
429429

430430
// Generate a random password for keystore protection
431-
final String keyStorePasswd = RandomStringUtils.random(20);
431+
final String keyStorePasswd = SecureRandomStringUtils.random(20);
432432
final KeyStore keyStore = KeyStore.getInstance("JKS");
433433
keyStore.load(null, null);
434434
keyStore.setKeyEntry("DrillAutoGeneratedCert", keyPair.getPrivate(),

exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
*/
1818
package org.apache.drill.exec.server.rest.ssl;
1919

20-
import org.apache.commons.lang3.RandomStringUtils;
2120
import org.apache.drill.common.config.DrillConfig;
2221
import org.apache.drill.exec.ExecConstants;
2322
import org.apache.drill.exec.ssl.SSLConfig;
2423
import org.apache.drill.exec.ssl.SSLConfigBuilder;
24+
import org.apache.drill.exec.util.SecureRandomStringUtils;
2525
import org.bouncycastle.asn1.x500.X500NameBuilder;
2626
import org.bouncycastle.asn1.x500.style.BCStyle;
2727
import org.bouncycastle.cert.X509v3CertificateBuilder;
@@ -202,7 +202,7 @@ private void useAutoGeneratedSelfSignedCertificate(SslContextFactory.Server sslC
202202
certificate.verify(certificate.getPublicKey());
203203

204204
// Generate a random password for keystore protection
205-
final String keyStorePasswd = RandomStringUtils.random(20);
205+
final String keyStorePasswd = SecureRandomStringUtils.random(20);
206206
final KeyStore keyStore = KeyStore.getInstance("JKS");
207207
keyStore.load(null, null);
208208
keyStore.setKeyEntry("DrillAutoGeneratedCert", keyPair.getPrivate(),
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.drill.exec.util;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
22+
import java.security.NoSuchAlgorithmException;
23+
import java.security.SecureRandom;
24+
25+
/**
26+
* Based on Commons Lang3 RandomStringUtils, but with a SecureRandom.
27+
*
28+
* For internal Apache Drill use only.
29+
*/
30+
public class SecureRandomStringUtils {
31+
// Based on https://github.com/apache/commons-lang/blob/5e07d873e6b45714d29bf47634adffa3b5aef098/src/main/java/org/apache/commons/lang3/RandomStringUtils.java
32+
33+
private static SecureRandom RANDOM_INSTANCE;
34+
35+
private static SecureRandom random() {
36+
if (RANDOM_INSTANCE != null) {
37+
return RANDOM_INSTANCE;
38+
}
39+
try {
40+
RANDOM_INSTANCE = SecureRandom.getInstanceStrong();
41+
return RANDOM_INSTANCE;
42+
} catch (NoSuchAlgorithmException e) {
43+
throw new IllegalStateException("Cannot create SecureRandom.getInstanceStrong()", e);
44+
}
45+
}
46+
47+
// Random
48+
/**
49+
* Creates a random string whose length is the number of characters
50+
* specified.
51+
*
52+
* <p>Characters will be chosen from the set of all characters.</p>
53+
*
54+
* @param count the length of random string to create
55+
* @return the random string
56+
* @throws IllegalArgumentException if {@code count} &lt; 0.
57+
*/
58+
public static String random(final int count) {
59+
return random(count, false, false);
60+
}
61+
62+
/**
63+
* Creates a random string whose length is the number of characters
64+
* specified.
65+
*
66+
* <p>Characters will be chosen from the set of alpha-numeric
67+
* characters as indicated by the arguments.</p>
68+
*
69+
* @param count the length of random string to create
70+
* @param letters if {@code true}, generated string may include
71+
* alphabetic characters
72+
* @param numbers if {@code true}, generated string may include
73+
* numeric characters
74+
* @return the random string
75+
* @throws IllegalArgumentException if {@code count} &lt; 0.
76+
*/
77+
private static String random(final int count, final boolean letters, final boolean numbers) {
78+
return random(count, 0, 0, letters, numbers);
79+
}
80+
81+
/**
82+
* Creates a random string whose length is the number of characters
83+
* specified.
84+
*
85+
* <p>Characters will be chosen from the set of alpha-numeric
86+
* characters as indicated by the arguments.</p>
87+
*
88+
* @param count the length of random string to create
89+
* @param start the position in set of chars to start at
90+
* @param end the position in set of chars to end before
91+
* @param letters if {@code true}, generated string may include
92+
* alphabetic characters
93+
* @param numbers if {@code true}, generated string may include
94+
* numeric characters
95+
* @return the random string
96+
* @throws IllegalArgumentException if {@code count} &lt; 0.
97+
*/
98+
private static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers) {
99+
return random(count, start, end, letters, numbers, null, random());
100+
}
101+
102+
/**
103+
* Creates a random string based on a variety of options, using
104+
* supplied source of randomness.
105+
*
106+
* <p>If start and end are both {@code 0}, start and end are set
107+
* to {@code ' '} and {@code 'z'}, the ASCII printable
108+
* characters, will be used, unless letters and numbers are both
109+
* {@code false}, in which case, start and end are set to
110+
* {@code 0} and {@link Character#MAX_CODE_POINT}.
111+
*
112+
* <p>If set is not {@code null}, characters between start and
113+
* end are chosen.</p>
114+
*
115+
* <p>This method accepts a user-supplied {@link SecureRandom}
116+
* instance to use as a source of randomness. By seeding a single
117+
* {@link SecureRandom} instance with a fixed seed and using it for each call,
118+
* the same random sequence of strings can be generated repeatedly
119+
* and predictably.</p>
120+
*
121+
* @param count the length of random string to create
122+
* @param start the position in set of chars to start at (inclusive)
123+
* @param end the position in set of chars to end before (exclusive)
124+
* @param letters if {@code true}, generated string may include
125+
* alphabetic characters
126+
* @param numbers if {@code true}, generated string may include
127+
* numeric characters
128+
* @param chars the set of chars to choose randoms from, must not be empty.
129+
* If {@code null}, then it will use the set of all chars.
130+
* @param random a source of randomness.
131+
* @return the random string
132+
* @throws ArrayIndexOutOfBoundsException if there are not
133+
* {@code (end - start) + 1} characters in the set array.
134+
* @throws IllegalArgumentException if {@code count} &lt; 0 or the provided chars array is empty.
135+
*/
136+
private static String random(int count, int start, int end, final boolean letters, final boolean numbers,
137+
final char[] chars, final SecureRandom random) {
138+
if (count == 0) {
139+
return StringUtils.EMPTY;
140+
}
141+
if (count < 0) {
142+
throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
143+
}
144+
if (chars != null && chars.length == 0) {
145+
throw new IllegalArgumentException("The chars array must not be empty");
146+
}
147+
148+
if (start == 0 && end == 0) {
149+
if (chars != null) {
150+
end = chars.length;
151+
} else if (!letters && !numbers) {
152+
end = Character.MAX_CODE_POINT;
153+
} else {
154+
end = 'z' + 1;
155+
start = ' ';
156+
}
157+
} else if (end <= start) {
158+
throw new IllegalArgumentException("Parameter end (" + end + ") must be greater than start (" + start + ")");
159+
}
160+
161+
final int zeroDigitAscii = 48;
162+
final int firstLetterAscii = 65;
163+
164+
if (chars == null && (numbers && end <= zeroDigitAscii
165+
|| letters && end <= firstLetterAscii)) {
166+
throw new IllegalArgumentException("Parameter end (" + end + ") must be greater then (" + zeroDigitAscii + ") for generating digits " +
167+
"or greater then (" + firstLetterAscii + ") for generating letters.");
168+
}
169+
170+
final StringBuilder builder = new StringBuilder(count);
171+
final int gap = end - start;
172+
173+
while (count-- != 0) {
174+
final int codePoint;
175+
if (chars == null) {
176+
codePoint = random.nextInt(gap) + start;
177+
178+
switch (Character.getType(codePoint)) {
179+
case Character.UNASSIGNED:
180+
case Character.PRIVATE_USE:
181+
case Character.SURROGATE:
182+
count++;
183+
continue;
184+
}
185+
186+
} else {
187+
codePoint = chars[random.nextInt(gap) + start];
188+
}
189+
190+
final int numberOfChars = Character.charCount(codePoint);
191+
if (count == 0 && numberOfChars > 1) {
192+
count++;
193+
continue;
194+
}
195+
196+
if (letters && Character.isLetter(codePoint)
197+
|| numbers && Character.isDigit(codePoint)
198+
|| !letters && !numbers) {
199+
builder.appendCodePoint(codePoint);
200+
201+
if (numberOfChars == 2) {
202+
count--;
203+
}
204+
205+
} else {
206+
count++;
207+
}
208+
}
209+
return builder.toString();
210+
}
211+
212+
private SecureRandomStringUtils() {
213+
// empty
214+
}
215+
216+
}

0 commit comments

Comments
 (0)