Generating cryptographically strong key/secret in Java

Upasana | July 25, 2020 | 3 min read | 647 views


In this article we will discuss approach and implementation for generating hard to guess secure keys using Java programming language.

Concept

Using secure random, we can generate a cryptographically secure hard to guess keys that can act as passwords/API Keys/ClientSecret etc.

Typical requirements for keys and secrets are:

Keys/Ids

Must be unique, not necessarily unguessable. Normally a random number (of sufficient length) is a good choice for keys. We can use Random class for generating keys but that may lead to predictability. If that bothers you, you can always use SecureRandom number generator.

Secrets

Must be unguessable and kept confidential. Any potential leak of a secret is a security incident requiring immediate revocation of the key. The only way to generate a secret is with a secure random generator. Secrets shall never be stored in clear text format.

Applications

  1. Generating unique id for a database record (better if unguessable)

  2. Generating API Keys (must be unguessable and kept private)

  3. Generating passwords

  4. Generating clientId and clientSecrets for Client Credentials flow in OAuth2

Solutions

  1. UUID (java.util.UUID) is a good candidate for generating keys

  2. Apache common lang3 RandomStringUtils

  3. Spring’s RandomValueStringGenerator

  4. Custom implementation using SecureRandom

  5. Using external library like passay: http://www.passay.org or https://github.com/vt-middleware/passay (good for generating passwords with rules)

We will explore these solutions in detail.

1. Using java.util.UUID

UUID class generates universally unique 32 character identifier (128-bit value) that can be a good candidate for keys and ids. But the UUIDs are rarely useful for generating secrets.

To generate UUID, all we have to do is:

String key = UUID.randomUUID().toString();

2. Using RandomStringUtils

We can use commons-lang3 RandomStringUtils along with SecureRandom class to generate secure keys and secrets. The advantage with this approach is that we can choose the characters to be used in composing these keys/secret values.

import org.apache.commons.lang3.RandomStringUtils;

import java.security.SecureRandom;
import java.util.UUID;

public final class RandomIdGenerator {
    private static final String VALID_CLIENT_ID_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789"; //  (1)

    private static class Holder {   (2)
        static final SecureRandom numberGenerator = new SecureRandom();
    }

    public String generateId(int length) {
        SecureRandom ng = Holder.numberGenerator;
        return RandomStringUtils.random(length, 0, VALID_CLIENT_ID_CHARS.length(), false, false,
                VALID_CLIENT_ID_CHARS.toCharArray(), ng);
    }

    public static String generateRandomId(int length) {
        RandomIdGenerator randomIdGenerator = new RandomIdGenerator();
        return randomIdGenerator.generateId(length);
    }
}
1 The set of characters to choose output from.
2 Initialization on demand using static class idiom for better performance (singleton pattern). see https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom for more details.

Once we have this class ready, we can use this as follow:

class Foo {

    @Test
    public void test() {
        String clientId = RandomIdGenerator.generateRandomId(25);
        String clientSecret = RandomIdGenerator.generateRandomId(36);
    }
}

3. Using RandomValueStringGenerator

If you are already using Spring, then you can use RandomValueStringGenerator class to generate secure keys and secrets.

Spring OAuth2 RandomValueStringGenerator
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;

public void generateApiKey() {
    String apiKey = new RandomValueStringGenerator(32).generate();  (1)
}
1 Specify the number of bytes

4. Java 8 SecureRandom

SecureRandom class ensures (over Random) that generated random number is cryptographically strong.

import java.security.SecureRandom;

class Main {

    public static String generateRandomString(int len) {
        final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; (1)
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {     (2)
            int randomIndex = random.nextInt(chars.length());
            sb.append(chars.charAt(randomIndex));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        int len = 10;
        System.out.println(generateRandomPassword(len));
    }
}
1 ASCII range - alphanumeric (0-9, a-z, A-Z)
2 Each iteration of loop choose a character randomly from the given ASCII range and append it to StringBuilder instance.

Top articles in this category:
  1. RBS Java Programming Interview Questions
  2. Multi-threading Java Interview Questions for Investment Bank
  3. What is difference between Primary key and Unique Key
  4. Sapient Global Market Java Interview Questions and Coding Exercise
  5. Cracking core java interviews - question bank
  6. ION Trading Java Interview Questions
  7. Goldman Sachs Java Interview Questions

Recommended books for interview preparation:

Find more on this topic:
Buy interview books

Java & Microservices interview refresher for experienced developers.