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:

Cryptographically weak implementation for OTP generation
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);

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.

Secure and Safe OTP generation
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.

Using selected characters in OTP
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.

Calling SecureRandom just once
//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:
  1. HmacSHA256 Signature in Java
  2. Allow insecure SSL in Java 11 HttpClient
  3. Generate Random Numbers in a range using Java 8
  4. ConcurrentModificationException in Java
  5. Is Java Pure Object Oriented Language?
  6. Discuss internals of a ConcurrentHashmap (CHM) in Java
  7. Precision and scale for a Double in java

Recommended books for interview preparation:

Find more on this topic:
Buy interview books

Java & Microservices interview refresher for experienced developers.