Extremely compact UUID (using all alphanumeric characters) - java

I need an extremely compact UUID, the shorter the better.
To that end, I wrote:
public String getBase36UIID() {
// More compact version of UUID
String strUUID = UUID.randomUUID().toString().replace("-", "");
return new BigInteger(strUUID, 16).toString(36);
}
By executing this code, I get, for example:
5luppaye6086d5wp4fqyz57xb
That's good, but it's not the best. Base 36 uses all numeric digits and lowercase letters, but does not use uppercase letters.
If it were possible to use uppercase letters as separate digits from lowercase letters, it would be possible to theorize a numerical base 62, composed of these digits:
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
I could theorize numerical bases also using accented characters such as "è" or "é", or special characters such as "$" or "!", further increasing the number of digits available.
The use of these accented or special characters, however, may cause me problems, so for the moment I prefer not to consider them.
After all these premises, how can I convert the BigInteger representing my UUID into the base 62 above theorized, in order to make it even more compact? Thanks
I have already verified that a code like the following is not usable, because every base over 36 is treated as base 10:
return new BigInteger(strUUID, 16).toString(62);
After all, in mathematics there is no base 62 as I imagined it, but I suppose that in Java it can be created.

The general algorithm for converting a number to any base is based on division with remainder.
You start by dividing the number by the base. The remainder gives you the last digit of the number - you map it to a symbol. If the quotient is nonzero, you divide it by the base. The remainder gives you the second to last digit. And you repeat the process with the quotient.
In Java, with BigInteger:
String toBase62(BigInteger number) {
String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
BigInteger base = BigInteger.valueOf(symbols.length());
StringBuilder result = new StringBuilder();
do {
BigInteger[] quotientAndRemainder = number.divideAndRemainder(base);
number = quotientAndRemainder[0];
result.append(symbols.charAt(quotientAndRemainder[1].intValue()));
} while (number.compareTo(BigInteger.ZERO) > 0);
return result.reverse().toString();
}
Do you need the identifier to be a UUID though? Couldn't it be just any random sequence of letters and numbers? If that's acceptable, you don't have to deal with number base conversions.
String randomString(int length) {
String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random rnd = new Random();
StringBuilder str = new StringBuilder();
for (int i = 0; i < length; i++) {
str.append(symbols.charAt(rnd.nextInt(symbols.length())));
}
return str.toString();
}

This should not be difficult. Converting a number to a string is a basic programming task. The fact that you're using base 62 makes no difference.
Decide how many characters you're willing to use, and then convert your large number to that base. Map each "digit" onto one of the characters.
Pseudocode:
b = the base (say, 62)
valid_chars = an array of 'b' characters
u = the uuid
while u != 0:
digit = u % b;
char = valid_chars[digit];
u = u / b;
This produces the digits right-to-left but you should get the idea.

Main idea is the same as previous posts, but the implementation have some differences.
Also note that if wanted different occurrence probability for each chars this can be adjusted also.(mainly add a character more time on a data structure and change his probability)
Here is fair-probability for each chars (equals, 1/62)
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RCode {
String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static void main(String[] args)
{
RCode r = new RCode();
System.out.println("symbols="+r.symbols.length());
System.out.println("code_10(+1)="+r.generate(10));
System.out.println("code_70(+2)="+r.generate(70));
//System.out.println("code_124(+3)="+r.generate(124));
}
public String generate(int length)
{
int num = length/symbols.length()+1;
List<Character> list = new ArrayList<Character>();
for(int i=0; i<symbols.length(); i++)
{
//if needed to change probability of char occurrence then adapt here
for(int j=0;j<=num;j++)
{
list.add(symbols.charAt(i));
}
}
//basically is the same as random
Collections.shuffle(list);
StringBuffer sb = new StringBuffer();
for(int i=0; i<length; i++)
{
sb.append(list.get(i));
}
return sb.toString();
}
}
Output:
symbols=62
//each char is added once(+1)
code_10(+1)=hFW9ZFEAeU
code_70(+2)=hrHQCEdQ3F28apcJPnfjAaOu55Xso12xabkJ7MrU97U0HYkYhWwGEqVAiLOp3X3QSuq6qp
Note: Algorithm have a defect, just try to figured out why the sequence will be never generate on 10 (aaaaaaaaaa). Easy to fix ... but i was focused on the idea.
Now, as it is, basically is generating up to num each character. (random and maybe for someone will be useful the output)

Related

Random seed generator

EDIT: Sorry for wrong posting, I'll check the forum locations better next time. I selected an answer as accepted, I think this considers the question closed. Thanks for the helpful replies and tips!
Original:
I need to upgrade to the new Iota wallet today. It doesn't have a random seed generator, so I built my own and ran it from NetBeans. Can you give me your opinion? It has to be 81 characters long, and contain A through Z and the number 9. Nothing else. Here's the entire code.
Does this leave anything insecure? Could the code have been cleaner from a standpoint of convention?
class SeedGenerator {
/*
This is a program to randomize a seed for Iota wallet
*/
public static void main(String[] args) {
System.out.println("*****");
int seedLength = 81;
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ9"; //Only characters allowed in Iota seed are A-Z and number 9
char[] charArray = alphabet.toCharArray(); //Turn string into array of characters to be referenced by index
String[] seed = new String[seedLength];
System.out.print("Random wallet seed is: ");
for (int i = 0; i < seedLength; i++) {
Random newRandomNumber = new Random();
int seedIndex = newRandomNumber.nextInt(alphabet.length()); //This is an array of index numbers to pull from charArray
// System.out.print(seedIndex + " "); //Used for testing the random character index range
seed[i] += charArray[seedIndex];
System.out.print(charArray[seedIndex] + "");
}
System.out.println();
System.out.println("*****");
}
}
When asking for code to be reviewed, you should post it here. But regardless of that, there are much more efficient ways to generate a random character.
One such way would be to generate a random character between 65 and 90, the decimal values for A-Z on the ASCII table. And then, just cast the value as a char to get the actual letter corresponding to the number. However, you say that you want the number 9 to also be included, so you can extend this to 91, and if you get 91, which on the ASCII table is [, add the number 9 to your string instead of that.
This code accomplishes that quite easily:
String mySeed = "";
for(int i=0; i<81; i++)
{
int randomNum = (int)(Math.random()*27) + 65;
if(randomNum==91)
mySeed+="9";
else
mySeed+=(char)randomNum;
}
System.out.println(mySeed);
And, as mentioned by #O.O. you can look at generating a secure random number here.
I recommend to use the offical IOTA Java library named Jota.
Class SeedRandomGenerator has a generateNewSeed implementation:
public static String generateNewSeed() {
char[] chars = Constants.TRYTE_ALPHABET.toCharArray();
StringBuilder builder = new StringBuilder();
SecureRandom random = new SecureRandom();
for (int i = 0; i < Constants.SEED_LENGTH_MAX; i++) {
char c = chars[random.nextInt(chars.length)];
builder.append(c);
}
return builder.toString();
}
Find the constants for TRYTES_ALPHABET and SEED_LENGTH_MAX in Constants class.

Generate all Palindromic numbers in a given number system?

I need to generate all palindromic numbers for a given number base (which should be able to be of size up to 10,000), in a given range. I need a efficient way to do it.
I stumbled upon this answer, which is related to base 10 directly. I'm trying to adapt it to work for "all" bases:
public static Set<String> allPalindromic(long limit, int base, char[] list) {
Set<String> result = new HashSet<String>();
for (long i = 0; i <= base-1 && i <= limit; i++) {
result.add(convert(i, base, list));
}
boolean cont = true;
for (long i = 1; cont; i++) {
StringBuffer rev = new StringBuffer("" + convert(i, base, list)).reverse();
cont = false;
for (char d : list) {
String n = "" + convert(i, base, list) + d + rev;
if (convertBack(n, base, list) <= limit) {
cont = true;
result.add(n);
}
}
}
return result;
}
convert() method converts a number to a string representation of that number in a given base using a list of chars for digits.
convertBack() converts back the string representation of a number to base 10.
When testing my method for base 10, it leaves out two-digit palindromes and then the next ones it leaves out are 1001,1111,1221... and so on.
I'm not sure why.
Here are the conversion methods if needed.
Turns out, this gets slower with my other code because of constant conversions since I need the all numbers in order and in decimal. I'll just stick to iterating over every integer and converting it to every base and then checking if its a palindrome.
I don't have enough reputation to comment, but if you are only missing even length palindromes, then most probably there is something wrong with your list. Most probably you have forgot to add an empty entry in list as to generate 1001, it should be like num(10) + empty("") + rev(01).
There is no so many appropriate chars for digits in all possible bases (like 0xDEADBEEF for hex, and I suppose that convert has some limit like 36), so forget about exotic digits, and use simple lists or arrays like [8888, 123, 5583] for digits in 10000-base.
Then convert limit into need base, store it.
Now generate symmetric arrays of odd and even length like
[175, 2, 175] or [13, 221, 221, 13]. If length is the same as limit length, compare array values and reject too high numbers.
You can also use limit array as starting and generate only palindromes with lesser values.

From string to ASCII to binary back to ASCII to string in Java

I have sort of a funky question (that I hope hasn't been asked and answered yet). To start, I'll tell you the order of what I'm trying to do and how I'm doing it and then tell you where I'm having a problem:
Convert a string of characters into ASCII numbers
Convert those ASCII numbers into binary and store them in a string
Convert those binary numbers back into ASCII numbers
Convert the ASCII numbers back into normal characters
Here are the methods I've written so far:
public static String strToBinary(String inputString){
int[] ASCIIHolder = new int[inputString.length()];
//Storing ASCII representation of characters in array of ints
for(int index = 0; index < inputString.length(); index++){
ASCIIHolder[index] = (int)inputString.charAt(index);
}
StringBuffer binaryStringBuffer = new StringBuffer();
/* Now appending values of ASCIIHolder to binaryStringBuffer using
* Integer.toBinaryString in a for loop. Should not get an out of bounds
* exception because more than 1 element will be added to StringBuffer
* each iteration.
*/
for(int index =0;index <inputString.length();index ++){
binaryStringBuffer.append(Integer.toBinaryString
(ASCIIHolder[index]));
}
String binaryToBeReturned = binaryStringBuffer.toString();
binaryToBeReturned.replace(" ", "");
return binaryToBeReturned;
}
public static String binaryToString(String binaryString){
int charCode = Integer.parseInt(binaryString, 2);
String returnString = new Character((char)charCode).toString();
return returnString;
}
I'm getting a NumberFormatException when I run the code and I think it's because the program is trying to convert the binary digits as one entire binary number rather than as separate letters. Based on what you see here, is there a better way to do this overall and/or how can I tell the computer to recognize the ASCII characters when it's iterating through the binary code? Hope that's clear and if not I'll be checking for comments.
So I used OP's code with some modifications and it works really well for me.
I'll post it here for future people. I don't think OP needs it anymore because he probably figured it out in the past 2 years.
public class Convert
{
public String strToBinary(String inputString){
int[] ASCIIHolder = new int[inputString.length()];
//Storing ASCII representation of characters in array of ints
for(int index = 0; index < inputString.length(); index++){
ASCIIHolder[index] = (int)inputString.charAt(index);
}
StringBuffer binaryStringBuffer = new StringBuffer();
/* Now appending values of ASCIIHolder to binaryStringBuffer using
* Integer.toBinaryString in a for loop. Should not get an out of bounds
* exception because more than 1 element will be added to StringBuffer
* each iteration.
*/
for(int index =0;index <inputString.length();index ++){
binaryStringBuffer.append(Integer.toBinaryString
(ASCIIHolder[index]));
}
String binaryToBeReturned = binaryStringBuffer.toString();
binaryToBeReturned.replace(" ", "");
return binaryToBeReturned;
}
public String binaryToString(String binaryString){
String returnString = "";
int charCode;
for(int i = 0; i < binaryString.length(); i+=7)
{
charCode = Integer.parseInt(binaryString.substring(i, i+7), 2);
String returnChar = new Character((char)charCode).toString();
returnString += returnChar;
}
return returnString;
}
}
I'd like to thank OP for writing most of it out for me. Fixing errors is much easier than writing new code.
You've got at least two problems here:
You're just concatenating the binary strings, with no separators. So if you had "1100" and then "0011" you'd get "11000011" which is the same result as if you had "1" followed by "1000011".
You're calling String.replace and ignoring the return result. This sort of doesn't matter as you're replacing spaces, and there won't be any spaces anyway... but there should be!
Of course you don't have to use separators - but if you don't, you need to make sure that you include all 16 bits of each UTF-16 code point. (Or validate that your string only uses a limited range of characters and go down to an appropriate number of bits, e.g. 8 bits for ISO-8859-1 or 7 bits for ASCII.)
(I have to wonder what the point of all of this is. Homework? I can't see this being useful in real life.)

Algorithm for incrementing a String in a non-obvious manner

I want to create random-looking 5 or 6 character alpha-numeric strings, something like:
Vg78KY
Creating (pseudo-)random Strings has been answered, but I am wondering if there is an algorithm for incrementing a String in a non-obvious manner. A simple increment of the above String might yield:
Vg78KZ
But I don't want this next String to be guessable, I want it to look completely different. Of course, successive increments should not yield a previous result as each should be unique.
Any thoughts on how to achieve this much appreciated!
Thanks
An easy approach that avoids the need for lookup tables would be:
Increment an integer normally
Permute the bits in a non-obvious way (a fixed permutation is probably fine, but if you want something more sophisticated you could use something like George Marsaglia's XORShift algorithm that produces a psuedorandom sequence of integers that only repeats after a very long cycle)
Convert to Base64 encoded strings
If we assume there must be a 1:1 mapping from "sequence number" to "random-looking string", then the truncated hash approach will not work as there is no guarantee that the truncated hash won't be subject to collisions.
I'd do something like this:
Take the next integer in sequence.
Xor with a fixed number.
Permute the bits.
Encode the number using Base64, Base36, or whatever.
Note that this will be subject to easy analysis by a determined attacker with access to a sufficiently large set of sequence numbers.
What exactly do you mean by increment? If you just want some values that is the result of the original value, the you can use a hash code (possibly a cryptographic hash). Then simply encode it a way that uses the characters you want to use (for example Base64 or something similar) and cut it off at the number of characters you want.
This is a one-way operation, however. That means that you can easily get successor of a value, but can't easily get the predecessor.
import java.util.UUID;
public class RandomStringUUID {
public static void main(String[] args) {
UUID uuid = UUID.randomUUID();
String randomUUIDString = uuid.toString();
System.out.println("Random UUID String = " + randomUUIDString);
System.out.println("UUID version = " + uuid.version());
System.out.println("UUID variant = " + uuid.variant());
}
}
If you want to generate collision safe strings just use UUIDs
If you want it to be incremented it means you have some transformation function F() that transforms from one space to another.
So you probably have a function from {Z} -> {SomeString}
So what you need to do, is just apply the opposite of F() (F-1) to the string, get the original number, increment it, and generate it again.
in pseudocode:
int i = 1;
String s = Transform(i);
int num = UnTransform(s);
num++;
String next = Transform(num);
What about this one:
convert the number to binary format;
change the order of digits by fixed manual mapping (last digit to 6th place, etc);
convert the number back to hash
Another simple way to do this would be:
$hash_key = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
$hash_table = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
$init = 10000000;
$hash_value = str_replace($hash_key, $hash_table, strval($init));
//$hash_value = 'BAAAAAAA'
//For next value:
$init++;
$hash_value = str_replace($hash_key, $hash_table, strval($init));
//$hash_value = 'BAAAAAAB'
//If you want to increment only the string without knowing the digits:
$prev_hash = $hash_value;
$prev_init = intval(str_replace($hash_table, $hash_key, $prev_hash));
//$prev_init = 10000001
$next_init = $prev_init + 1;
$next_hash = str_replace($hash_key, $hash_table, strval($next_init));
//$next_hash = 'BAAAAAAC'
Hope this helps. :-)
One of possible solutions would be to pre-generate the entire dictionary of all possible strings and then use SecureRandom to point to an index of that dictionary. If a particular element would already be "reserved", you'd simply go to the next available one (this operation can also be pre-generated btw).
The obvious disadvantage of this solution is non-determinism. But this was not requested by OP. And I'm not even sure if determinism is possible in this situation.
Lazy method: keep a hashtable or set to store all existing strings, and each time you generate a random string, check to see if it's in the set. If so, generate a new one until you get one that's not in the set.
This would probably be both memory- and processor-intensive in the long run, though.
You might try and convert the following Python to the language of your choice...
>>> import string, random, itertools
>>> digits = list(string.ascii_lowercase + string.ascii_uppercase + string.digits + '_')
>>> ''.join(digits)
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
>>> digit_count = 4
>>> alldigits = [digits[:] for i in range(digit_count)]
>>> for d in alldigits: random.shuffle(d)
>>> numbergen = (''.join(d) for d in itertools.product(*alldigits))
>>> numbergen.__next__()
'B1xx'
>>> numbergen.__next__()
'B1x1'
>>> numbergen.__next__()
'B1xQ'
>>> numbergen.__next__()
'B1x7'
Well since you want the string to be alphanumeric, then it's pretty straightforward. Create a character array of size 62. This is 26 lowercase letters, 26 uppercase letters, and the 10 digits 0-9. After you fill in the array, loop through N times, where N is the length of your string, selecting a random index each time. So it should look something like this:
char [] arrayOfCharacters = new char[62];
int index = 0;
for(char a = 'a';a<= 'z';a++)
{
arrayOfCharacters[index++] = a;
}//copy this loop for the upper case characters and 0-9
String randomString = "";
for(int x = 0;x < N; x++)
{
randomString += arrayOfCharacters[(int)(Math.random() * arrayOfCharacters.length)];
}
That's my code.. it does exactly what you asked for using the UUID to generate a string then execute (-) from it.
import java.util.*;
class KeyGen {
public static void main(String[] args) {
String uuid = UUID.randomUUID().toString();
String str = uuid.replaceAll("[-]", "").toUpperCase();
String s = "";
Scanner scan = new Scanner(System.in);
String[] array = str.split("");
Random rnd = new Random();
int N = rnd.nextInt(str.length());
System.out.println("How many keys you want?");
int keys = scan.nextInt();
String[] rndstr = new String[keys];
System.out.println("How many letters for the first key?");
int count = scan.nextInt();
for (int t = 0; t < keys; t++)
{
s="";
count++;
for(int i=0; i < count; i++)
{
uuid = UUID.randomUUID().toString();
str = uuid.replaceAll("[-]", "").toUpperCase();
int len = str.length();
N= rnd.nextInt(len) + 1;
s = s + array[N];
}
rndstr[t] = s;
}
for (int j=0; j < rndstr.length; j++)
{
System.out.println(rndstr[j]);
}
}
}
Simple output:
How many keys you want?
4
How many letters for the first key?
6
Here are your keys:
5F2934A
C8A456A6
B06E49240
FE3AE40CCE
Make your string the result of a hash operation. For example, using your random strings as input:
String input1 = "Vg78KY";
String output1 = String.valueOf(input1.hashCode());
String input2 = "Vg78KZ";
String output2 = String.valueOf(input2.hashCode());
output1 and output2 will be completely different.

How to generate a random alpha-numeric string

I've been looking for a simple Java algorithm to generate a pseudo-random alpha-numeric string. In my situation it would be used as a unique session/key identifier that would "likely" be unique over 500K+ generation (my needs don't really require anything much more sophisticated).
Ideally, I would be able to specify a length depending on my uniqueness needs. For example, a generated string of length 12 might look something like "AEYGF7K0DM1X".
Algorithm
To generate a random string, concatenate characters drawn randomly from the set of acceptable symbols until the string reaches the desired length.
Implementation
Here's some fairly simple and very flexible code for generating random identifiers. Read the information that follows for important application notes.
public class RandomString {
/**
* Generate a random string.
*/
public String nextString() {
for (int idx = 0; idx < buf.length; ++idx)
buf[idx] = symbols[random.nextInt(symbols.length)];
return new String(buf);
}
public static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String lower = upper.toLowerCase(Locale.ROOT);
public static final String digits = "0123456789";
public static final String alphanum = upper + lower + digits;
private final Random random;
private final char[] symbols;
private final char[] buf;
public RandomString(int length, Random random, String symbols) {
if (length < 1) throw new IllegalArgumentException();
if (symbols.length() < 2) throw new IllegalArgumentException();
this.random = Objects.requireNonNull(random);
this.symbols = symbols.toCharArray();
this.buf = new char[length];
}
/**
* Create an alphanumeric string generator.
*/
public RandomString(int length, Random random) {
this(length, random, alphanum);
}
/**
* Create an alphanumeric strings from a secure generator.
*/
public RandomString(int length) {
this(length, new SecureRandom());
}
/**
* Create session identifiers.
*/
public RandomString() {
this(21);
}
}
Usage examples
Create an insecure generator for 8-character identifiers:
RandomString gen = new RandomString(8, ThreadLocalRandom.current());
Create a secure generator for session identifiers:
RandomString session = new RandomString();
Create a generator with easy-to-read codes for printing. The strings are longer than full alphanumeric strings to compensate for using fewer symbols:
String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";
RandomString tickets = new RandomString(23, new SecureRandom(), easy);
Use as session identifiers
Generating session identifiers that are likely to be unique is not good enough, or you could just use a simple counter. Attackers hijack sessions when predictable identifiers are used.
There is tension between length and security. Shorter identifiers are easier to guess, because there are fewer possibilities. But longer identifiers consume more storage and bandwidth. A larger set of symbols helps, but might cause encoding problems if identifiers are included in URLs or re-entered by hand.
The underlying source of randomness, or entropy, for session identifiers should come from a random number generator designed for cryptography. However, initializing these generators can sometimes be computationally expensive or slow, so effort should be made to re-use them when possible.
Use as object identifiers
Not every application requires security. Random assignment can be an efficient way for multiple entities to generate identifiers in a shared space without any coordination or partitioning. Coordination can be slow, especially in a clustered or distributed environment, and splitting up a space causes problems when entities end up with shares that are too small or too big.
Identifiers generated without taking measures to make them unpredictable should be protected by other means if an attacker might be able to view and manipulate them, as happens in most web applications. There should be a separate authorization system that protects objects whose identifier can be guessed by an attacker without access permission.
Care must be also be taken to use identifiers that are long enough to make collisions unlikely given the anticipated total number of identifiers. This is referred to as "the birthday paradox." The probability of a collision, p, is approximately n2/(2qx), where n is the number of identifiers actually generated, q is the number of distinct symbols in the alphabet, and x is the length of the identifiers. This should be a very small number, like 2‑50 or less.
Working this out shows that the chance of collision among 500k 15-character identifiers is about 2‑52, which is probably less likely than undetected errors from cosmic rays, etc.
Comparison with UUIDs
According to their specification, UUIDs are not designed to be unpredictable, and should not be used as session identifiers.
UUIDs in their standard format take a lot of space: 36 characters for only 122 bits of entropy. (Not all bits of a "random" UUID are selected randomly.) A randomly chosen alphanumeric string packs more entropy in just 21 characters.
UUIDs are not flexible; they have a standardized structure and layout. This is their chief virtue as well as their main weakness. When collaborating with an outside party, the standardization offered by UUIDs may be helpful. For purely internal use, they can be inefficient.
Java supplies a way of doing this directly. If you don't want the dashes, they are easy to strip out. Just use uuid.replace("-", "")
import java.util.UUID;
public class randomStringGenerator {
public static void main(String[] args) {
System.out.println(generateString());
}
public static String generateString() {
String uuid = UUID.randomUUID().toString();
return "uuid = " + uuid;
}
}
Output
uuid = 2d7428a6-b58c-4008-8575-f05549f16316
static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static SecureRandom rnd = new SecureRandom();
String randomString(int len){
StringBuilder sb = new StringBuilder(len);
for(int i = 0; i < len; i++)
sb.append(AB.charAt(rnd.nextInt(AB.length())));
return sb.toString();
}
If you're happy to use Apache classes, you could use org.apache.commons.text.RandomStringGenerator (Apache Commons Text).
Example:
RandomStringGenerator randomStringGenerator =
new RandomStringGenerator.Builder()
.withinRange('0', 'z')
.filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
.build();
randomStringGenerator.generate(12); // toUpperCase() if you want
Since Apache Commons Lang 3.6, RandomStringUtils is deprecated.
You can use an Apache Commons library for this, RandomStringUtils:
RandomStringUtils.randomAlphanumeric(20).toUpperCase();
In one line:
Long.toHexString(Double.doubleToLongBits(Math.random()));
Source: Java - generating a random string
This is easily achievable without any external libraries.
1. Cryptographic Pseudo Random Data Generation (PRNG)
First you need a cryptographic PRNG. Java has SecureRandom for that and typically uses the best entropy source on the machine (e.g. /dev/random). Read more here.
SecureRandom rnd = new SecureRandom();
byte[] token = new byte[byteLength];
rnd.nextBytes(token);
Note: SecureRandom is the slowest, but most secure way in Java of generating random bytes. I do however recommend not considering performance here since it usually has no real impact on your application unless you have to generate millions of tokens per second.
2. Required Space of Possible Values
Next you have to decide "how unique" your token needs to be. The whole and only point of considering entropy is to make sure that the system can resist brute force attacks: the space of possible values must be so large that any attacker could only try a negligible proportion of the values in non-ludicrous time1.
Unique identifiers such as random UUID have 122 bit of entropy (i.e., 2^122 = 5.3x10^36) - the chance of collision is "*(...) for there to be a one in a billion chance of duplication, 103 trillion version 4 UUIDs must be generated2". We will choose 128 bits since it fits exactly into 16 bytes and is seen as highly sufficient for being unique for basically every, but the most extreme, use cases and you don't have to think about duplicates. Here is a simple comparison table of entropy including simple analysis of the birthday problem.
For simple requirements, 8 or 12 byte length might suffice, but with 16 bytes you are on the "safe side".
And that's basically it. The last thing is to think about encoding so it can be represented as a printable text (read, a String).
3. Binary to Text Encoding
Typical encodings include:
Base64 every character encodes 6 bit, creating a 33% overhead. Fortunately there are standard implementations in Java 8+ and Android. With older Java you can use any of the numerous third-party libraries. If you want your tokens to be URL safe use the URL-safe version of RFC4648 (which usually is supported by most implementations). Example encoding 16 bytes with padding: XfJhfv3C0P6ag7y9VQxSbw==
Base32 every character encodes 5 bit, creating a 40% overhead. This will use A-Z and 2-7, making it reasonably space efficient while being case-insensitive alpha-numeric. There isn't any standard implementation in the JDK. Example encoding 16 bytes without padding: WUPIL5DQTZGMF4D3NX5L7LNFOY
Base16 (hexadecimal) every character encodes four bit, requiring two characters per byte (i.e., 16 bytes create a string of length 32). Therefore hexadecimal is less space efficient than Base32, but it is safe to use in most cases (URL) since it only uses 0-9 and A to F. Example encoding 16 bytes: 4fa3dd0f57cb3bf331441ed285b27735. See a Stack Overflow discussion about converting to hexadecimal here.
Additional encodings like Base85 and the exotic Base122 exist with better/worse space efficiency. You can create your own encoding (which basically most answers in this thread do), but I would advise against it, if you don't have very specific requirements. See more encoding schemes in the Wikipedia article.
4. Summary and Example
Use SecureRandom
Use at least 16 bytes (2^128) of possible values
Encode according to your requirements (usually hex or base32 if you need it to be alpha-numeric)
Don't
... use your home brew encoding: better maintainable and readable for others if they see what standard encoding you use instead of weird for loops creating characters at a time.
... use UUID: it has no guarantees on randomness; you are wasting 6 bits of entropy and have a verbose string representation
Example: Hexadecimal Token Generator
public static String generateRandomHexToken(int byteLength) {
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[byteLength];
secureRandom.nextBytes(token);
return new BigInteger(1, token).toString(16); // Hexadecimal encoding
}
//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd
Example: Base64 Token Generator (URL Safe)
public static String generateRandomBase64Token(int byteLength) {
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[byteLength];
secureRandom.nextBytes(token);
return Base64.getUrlEncoder().withoutPadding().encodeToString(token); //base64 encoding
}
//generateRandomBase64Token(16) -> EEcCCAYuUcQk7IuzdaPzrg
Example: Java CLI Tool
If you want a ready-to-use CLI tool you may use dice:
Example: Related issue - Protect Your Current Ids
If you already have an id you can use (e.g., a synthetic long in your entity), but don't want to publish the internal value, you can use this library to encrypt it and obfuscate it: https://github.com/patrickfav/id-mask
IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());
String maskedId = idMask.mask(id);
// Example: NPSBolhMyabUBdTyanrbqT8
long originalId = idMask.unmask(maskedId);
Using Dollar should be as simple as:
// "0123456789" + "ABCDE...Z"
String validCharacters = $('0', '9').join() + $('A', 'Z').join();
String randomString(int length) {
return $(validCharacters).shuffle().slice(length).toString();
}
#Test
public void buildFiveRandomStrings() {
for (int i : $(5)) {
System.out.println(randomString(12));
}
}
It outputs something like this:
DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7
Here it is in Java:
import static java.lang.Math.round;
import static java.lang.Math.random;
import static java.lang.Math.pow;
import static java.lang.Math.abs;
import static java.lang.Math.min;
import static org.apache.commons.lang.StringUtils.leftPad
public class RandomAlphaNum {
public static String gen(int length) {
StringBuffer sb = new StringBuffer();
for (int i = length; i > 0; i -= 12) {
int n = min(12, abs(i));
sb.append(leftPad(Long.toString(round(random() * pow(36, n)), 36), n, '0'));
}
return sb.toString();
}
}
Here's a sample run:
scala> RandomAlphaNum.gen(42)
res3: java.lang.String = uja6snx21bswf9t89s00bxssu8g6qlu16ffzqaxxoy
A short and easy solution, but it uses only lowercase and numerics:
Random r = new java.util.Random ();
String s = Long.toString (r.nextLong () & Long.MAX_VALUE, 36);
The size is about 12 digits to base 36 and can't be improved further, that way. Of course you can append multiple instances.
Surprising, no one here has suggested it, but:
import java.util.UUID
UUID.randomUUID().toString();
Easy.
The benefit of this is UUIDs are nice, long, and guaranteed to be almost impossible to collide.
Wikipedia has a good explanation of it:
" ...only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%."
The first four bits are the version type and two for the variant, so you get 122 bits of random. So if you want to, you can truncate from the end to reduce the size of the UUID. It's not recommended, but you still have loads of randomness, enough for your 500k records easy.
An alternative in Java 8 is:
static final Random random = new Random(); // Or SecureRandom
static final int startChar = (int) '!';
static final int endChar = (int) '~';
static String randomString(final int maxLength) {
final int length = random.nextInt(maxLength + 1);
return random.ints(length, startChar, endChar + 1)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
}
public static String generateSessionKey(int length){
String alphabet =
new String("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); // 9
int n = alphabet.length(); // 10
String result = new String();
Random r = new Random(); // 11
for (int i=0; i<length; i++) // 12
result = result + alphabet.charAt(r.nextInt(n)); //13
return result;
}
import java.util.Random;
public class passGen{
// Version 1.0
private static final String dCase = "abcdefghijklmnopqrstuvwxyz";
private static final String uCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String sChar = "!##$%^&*";
private static final String intChar = "0123456789";
private static Random r = new Random();
private static StringBuilder pass = new StringBuilder();
public static void main (String[] args) {
System.out.println ("Generating pass...");
while (pass.length () != 16){
int rPick = r.nextInt(4);
if (rPick == 0){
int spot = r.nextInt(26);
pass.append(dCase.charAt(spot));
} else if (rPick == 1) {
int spot = r.nextInt(26);
pass.append(uCase.charAt(spot));
} else if (rPick == 2) {
int spot = r.nextInt(8);
pass.append(sChar.charAt(spot));
} else {
int spot = r.nextInt(10);
pass.append(intChar.charAt(spot));
}
}
System.out.println ("Generated Pass: " + pass.toString());
}
}
This just adds the password into the string and... yeah, it works well. Check it out... It is very simple; I wrote it.
Using UUIDs is insecure, because parts of the UUID aren't random at all. The procedure of erickson is very neat, but it does not create strings of the same length. The following snippet should be sufficient:
/*
* The random generator used by this class to create random keys.
* In a holder class to defer initialization until needed.
*/
private static class RandomHolder {
static final Random random = new SecureRandom();
public static String randomKey(int length) {
return String.format("%"+length+"s", new BigInteger(length*5/*base 32,2^5*/, random)
.toString(32)).replace('\u0020', '0');
}
}
Why choose length*5? Let's assume the simple case of a random string of length 1, so one random character. To get a random character containing all digits 0-9 and characters a-z, we would need a random number between 0 and 35 to get one of each character.
BigInteger provides a constructor to generate a random number, uniformly distributed over the range 0 to (2^numBits - 1). Unfortunately 35 is not a number which can be received by 2^numBits - 1.
So we have two options: Either go with 2^5-1=31 or 2^6-1=63. If we would choose 2^6 we would get a lot of "unnecessary" / "longer" numbers. Therefore 2^5 is the better option, even if we lose four characters (w-z). To now generate a string of a certain length, we can simply use a 2^(length*numBits)-1 number. The last problem, if we want a string with a certain length, random could generate a small number, so the length is not met, so we have to pad the string to its required length prepending zeros.
I found this solution that generates a random hex encoded string. The provided unit test seems to hold up to my primary use case. Although, it is slightly more complex than some of the other answers provided.
/**
* Generate a random hex encoded string token of the specified length
*
* #param length
* #return random hex string
*/
public static synchronized String generateUniqueToken(Integer length){
byte random[] = new byte[length];
Random randomGenerator = new Random();
StringBuffer buffer = new StringBuffer();
randomGenerator.nextBytes(random);
for (int j = 0; j < random.length; j++) {
byte b1 = (byte) ((random[j] & 0xf0) >> 4);
byte b2 = (byte) (random[j] & 0x0f);
if (b1 < 10)
buffer.append((char) ('0' + b1));
else
buffer.append((char) ('A' + (b1 - 10)));
if (b2 < 10)
buffer.append((char) ('0' + b2));
else
buffer.append((char) ('A' + (b2 - 10)));
}
return (buffer.toString());
}
#Test
public void testGenerateUniqueToken(){
Set set = new HashSet();
String token = null;
int size = 16;
/* Seems like we should be able to generate 500K tokens
* without a duplicate
*/
for (int i=0; i<500000; i++){
token = Utility.generateUniqueToken(size);
if (token.length() != size * 2){
fail("Incorrect length");
} else if (set.contains(token)) {
fail("Duplicate token generated");
} else{
set.add(token);
}
}
}
Change String characters as per as your requirements.
String is immutable. Here StringBuilder.append is more efficient than string concatenation.
public static String getRandomString(int length) {
final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJLMNOPQRSTUVWXYZ1234567890!##$%^&*()_+";
StringBuilder result = new StringBuilder();
while(length > 0) {
Random rand = new Random();
result.append(characters.charAt(rand.nextInt(characters.length())));
length--;
}
return result.toString();
}
import java.util.Date;
import java.util.Random;
public class RandomGenerator {
private static Random random = new Random((new Date()).getTime());
public static String generateRandomString(int length) {
char[] values = {'a','b','c','d','e','f','g','h','i','j',
'k','l','m','n','o','p','q','r','s','t',
'u','v','w','x','y','z','0','1','2','3',
'4','5','6','7','8','9'};
String out = "";
for (int i=0;i<length;i++) {
int idx=random.nextInt(values.length);
out += values[idx];
}
return out;
}
}
I don't really like any of these answers regarding a "simple" solution :S
I would go for a simple ;), pure Java, one liner (entropy is based on random string length and the given character set):
public String randomString(int length, String characterSet) {
return IntStream.range(0, length).map(i -> new SecureRandom().nextInt(characterSet.length())).mapToObj(randomInt -> characterSet.substring(randomInt, randomInt + 1)).collect(Collectors.joining());
}
#Test
public void buildFiveRandomStrings() {
for (int q = 0; q < 5; q++) {
System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); // The character set can basically be anything
}
}
Or (a bit more readable old way)
public String randomString(int length, String characterSet) {
StringBuilder sb = new StringBuilder(); // Consider using StringBuffer if needed
for (int i = 0; i < length; i++) {
int randomInt = new SecureRandom().nextInt(characterSet.length());
sb.append(characterSet.substring(randomInt, randomInt + 1));
}
return sb.toString();
}
#Test
public void buildFiveRandomStrings() {
for (int q = 0; q < 5; q++) {
System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); // The character set can basically be anything
}
}
But on the other hand you could also go with UUID which has a pretty good entropy:
UUID.randomUUID().toString().replace("-", "")
I'm using a library from Apache Commons to generate an alphanumeric string:
import org.apache.commons.lang3.RandomStringUtils;
String keyLength = 20;
RandomStringUtils.randomAlphanumeric(keylength);
It's fast and simple!
You mention "simple", but just in case anyone else is looking for something that meets more stringent security requirements, you might want to take a look at jpwgen. jpwgen is modeled after pwgen in Unix, and is very configurable.
import java.util.*;
import javax.swing.*;
public class alphanumeric {
public static void main(String args[]) {
String nval, lenval;
int n, len;
nval = JOptionPane.showInputDialog("Enter number of codes you require: ");
n = Integer.parseInt(nval);
lenval = JOptionPane.showInputDialog("Enter code length you require: ");
len = Integer.parseInt(lenval);
find(n, len);
}
public static void find(int n, int length) {
String str1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder sb = new StringBuilder(length);
Random r = new Random();
System.out.println("\n\t Unique codes are \n\n");
for(int i=0; i<n; i++) {
for(int j=0; j<length; j++) {
sb.append(str1.charAt(r.nextInt(str1.length())));
}
System.out.println(" " + sb.toString());
sb.delete(0, length);
}
}
}
Here is the one-liner by abacus-common:
String.valueOf(CharStream.random('0', 'z').filter(c -> N.isLetterOrDigit(c)).limit(12).toArray())
Random doesn't mean it must be unique. To get unique strings, use:
N.uuid() // E.g.: "e812e749-cf4c-4959-8ee1-57829a69a80f". length is 36.
N.guid() // E.g.: "0678ce04e18945559ba82ddeccaabfcd". length is 32 without '-'
You can use the following code, if your password mandatory contains numbers and alphabetic special characters:
private static final String NUMBERS = "0123456789";
private static final String UPPER_ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LOWER_ALPHABETS = "abcdefghijklmnopqrstuvwxyz";
private static final String SPECIALCHARACTERS = "##$%&*";
private static final int MINLENGTHOFPASSWORD = 8;
public static String getRandomPassword() {
StringBuilder password = new StringBuilder();
int j = 0;
for (int i = 0; i < MINLENGTHOFPASSWORD; i++) {
password.append(getRandomPasswordCharacters(j));
j++;
if (j == 3) {
j = 0;
}
}
return password.toString();
}
private static String getRandomPasswordCharacters(int pos) {
Random randomNum = new Random();
StringBuilder randomChar = new StringBuilder();
switch (pos) {
case 0:
randomChar.append(NUMBERS.charAt(randomNum.nextInt(NUMBERS.length() - 1)));
break;
case 1:
randomChar.append(UPPER_ALPHABETS.charAt(randomNum.nextInt(UPPER_ALPHABETS.length() - 1)));
break;
case 2:
randomChar.append(SPECIALCHARACTERS.charAt(randomNum.nextInt(SPECIALCHARACTERS.length() - 1)));
break;
case 3:
randomChar.append(LOWER_ALPHABETS.charAt(randomNum.nextInt(LOWER_ALPHABETS.length() - 1)));
break;
}
return randomChar.toString();
}
You can use the UUID class with its getLeastSignificantBits() message to get 64 bit of random data, and then convert it to a radix 36 number (i.e. a string consisting of 0-9,A-Z):
Long.toString(Math.abs( UUID.randomUUID().getLeastSignificantBits(), 36));
This yields a string up to 13 characters long. We use Math.abs() to make sure there isn't a minus sign sneaking in.
Here it is a Scala solution:
(for (i <- 0 until rnd.nextInt(64)) yield {
('0' + rnd.nextInt(64)).asInstanceOf[Char]
}) mkString("")
Using an Apache Commons library, it can be done in one line:
import org.apache.commons.lang.RandomStringUtils;
RandomStringUtils.randomAlphanumeric(64);
Documentation
public static String randomSeriesForThreeCharacter() {
Random r = new Random();
String value = "";
char random_Char ;
for(int i=0; i<10; i++)
{
random_Char = (char) (48 + r.nextInt(74));
value = value + random_char;
}
return value;
}
I think this is the smallest solution here, or nearly one of the smallest:
public String generateRandomString(int length) {
String randomString = "";
final char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890".toCharArray();
final Random random = new Random();
for (int i = 0; i < length; i++) {
randomString = randomString + chars[random.nextInt(chars.length)];
}
return randomString;
}
The code works just fine. If you are using this method, I recommend you to use more than 10 characters. A collision happens at 5 characters / 30362 iterations. This took 9 seconds.
public class Utils {
private final Random RANDOM = new SecureRandom();
private final String ALPHABET = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
private String generateRandomString(int length) {
StringBuffer buffer = new StringBuffer(length);
for (int i = 0; i < length; i++) {
buffer.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
}
return new String(buffer);
}
}

Categories