import java.util.concurrent.ThreadLocalRandom;
public String generateInsecure(int maxLength, String allowedChars) {
ThreadLocalRandom random = ThreadLocalRandom.current();
final StringBuilder otp = new StringBuilder(maxLength);
for (int i = 0; i < 8; i++) {
otp.append(allowedChars.charAt(random.nextInt(allowedChars.length())));
}
return otp.toString();
}
OtpGenerator otpGenerator = new OtpGenerator();
String otpInsecure = otpGenerator.generateInsecure(8, "123456789");
System.out.println(otpInsecure);
Secure OTP generation in Java
Upasana | October 17, 2020 | 2 min read | 0 views
In this article we will learn how to generate a cryptographically strong OTP in Java using SecureRandom class.
A naive approach for OTP generation could use java.util.Random
or ThreadLocalRandom
class, but any such implementation is cryptographically weak and should never be used in production. SecureRandom class provides much stronger implementation instead.
Lets see the security issues with ThreadLocalRandom first.
Issue with Random/ThreadLocalRandom
Random and ThreadLocalRandom based implementation are not cryptographically strong, so a hacker might get an opportunity to guess the OTP based on some previous OTP generated by the system.
An insecure & Cryptographically weak implementation for OTP generation will be something like this:
A more secure way is to use SecureRandom instead of java.util.Random
or java.util.concurrent.ThreadLocalRandom
for the security purpose.
Using SecureRandom
SecureRandom class provides a cryptographically strong random number generator (RNG). We will develop a small OTP generator that uses SecureRandom.
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class OtpGenerator {
private final SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); (1)
public OtpGenerator() throws NoSuchAlgorithmException {
}
public String generate(int maxLength) {
final StringBuilder otp = new StringBuilder(maxLength);
for (int i = 0; i < maxLength; i++) {
otp.append(secureRandom.nextInt(9));
}
return otp.toString();
}
public static void main(String[] args) throws NoSuchAlgorithmException {
OtpGenerator otpGenerator = new OtpGenerator();
String otp1 = otpGenerator.generate(8);
System.out.println(otp1);
}
}
1 | SecureRandom instance is thread-safe and can be used by multiple threads concurrently. |
If you have a constraint of using selected characters inside OTP, then the current version can be tweaked a bit to allow character selection.
public String generate(int maxLength, String allowedChars) {
final StringBuilder otp = new StringBuilder(maxLength);
for (int i = 0; i < maxLength; i++) {
otp.append(allowedChars.charAt(secureRandom.nextInt(allowedChars.length())));
}
return otp.toString();
}
...
String otp2 = otpGenerator.generate(8, "123456789ABCDE");
System.out.println(otp2);
A slight improvement in performance can be achieved by calling SecureRandom just once and getting all the random bytes in one go.
//Better performance, since secureRandom is called only once
fun generateV4(maxLength: Int, allowedChars: String): String? {
val characters = allowedChars.toCharArray()
val randomBytes = ByteArray(maxLength)
secureRandom.nextBytes(randomBytes) (1)
val chars = CharArray(randomBytes.size)
for (i in randomBytes.indices) {
chars[i] = characters[(randomBytes[i].toInt() and 0xFF) % characters.size]
}
return String(chars)
}
1 | In single go, we get all the required random bytes and then convert them one by one into String format. |
Thats' all.
Top articles in this category:
- HmacSHA256 Signature in Java
- Allow insecure SSL in Java 11 HttpClient
- Generate Random Numbers in a range using Java 8
- ConcurrentModificationException in Java
- Is Java Pure Object Oriented Language?
- Discuss internals of a ConcurrentHashmap (CHM) in Java
- Precision and scale for a Double in java