/* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.generator; import org.bouncycastle.crypto.digests.SHA256Digest; import org.cryptacular.util.NonceUtil; /** * Generates random identifiers with an alphanumeric character set by default. * * @author Middleware Services */ public class RandomIdGenerator implements IdGenerator { /** Default character set. */ public static final String DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; /** Size of generated identifiers. */ private final int length; /** Identifier character set. */ private final String charset; /** * Creates a new instance with the default character set. * * @param length Number of characters in generated identifiers. */ public RandomIdGenerator(final int length) { this(length, DEFAULT_CHARSET); } /** * Creates a new instance with a defined character set. * * @param length Number of characters in generated identifiers. * @param charset Character set. */ public RandomIdGenerator(final int length, final String charset) { if (length < 1) { throw new IllegalArgumentException("Length must be positive"); } this.length = length; if (charset == null || charset.length() < 2 || charset.length() > 128) { throw new IllegalArgumentException("Charset length must be in the range 2 - 128"); } this.charset = charset; } @Override public String generate() { final StringBuilder id = new StringBuilder(length); final byte[] output = new byte[length]; final int outsize = NonceUtil.newRBG(new SHA256Digest(), 32).generate(output, null, false); if (outsize < length) { throw new IllegalStateException("Insufficient entropy"); } int index; for (int i = 0; i < output.length && id.length() < length; i++) { index = 0x7F & output[i]; id.append(charset.charAt(index % charset.length())); } return id.toString(); } }