Skip to content

Instantly share code, notes, and snippets.

@trasa
Last active May 13, 2019 18:45
Show Gist options
  • Save trasa/e821e28250e4d9c216a06c247b24165c to your computer and use it in GitHub Desktop.
Save trasa/e821e28250e4d9c216a06c247b24165c to your computer and use it in GitHub Desktop.
Generate UUIDs using urandom (nonblocking random number entropy)
// ============================================================================
// Copyright 2006-2010 Daniel W. Dyer
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ============================================================================
//
// The original file is part of the maths.uncommons.org project.
// This is modified to use /dev/urandom as its source of entropy.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.uncommons.maths.random.SeedException;
import org.uncommons.maths.random.SeedGenerator;
/**
* RNG seed strategy that gets data from {@literal /dev/urandom} on systems that
* provide it (e.g. Solaris/Linux). If {@literal /dev/urandom} does not exist or
* is not accessible, a {@link SeedException} is thrown.
*
* @author Daniel Dyer
*/
public class DevUrandomSeedGenerator implements SeedGenerator {
private static final File DEV_URANDOM = new File("/dev/urandom");
/**
* {@inheritDoc}
*
* @return The requested number of random bytes, read directly from
* {@literal /dev/urandom}.
* @throws SeedException If {@literal /dev/urandom} does not exist or is not
* accessible
*/
@Override
public byte[] generateSeed(int length) throws SeedException {
try (FileInputStream file = new FileInputStream(DEV_URANDOM)) {
byte[] randomSeed = new byte[length];
int count = 0;
while (count < length) {
int bytesRead = file.read(randomSeed, count, length - count);
if (bytesRead == -1) {
throw new SeedException("EOF encountered reading random data.");
}
count += bytesRead;
}
return randomSeed;
} catch (IOException ex) {
throw new SeedException("Failed reading from " + DEV_URANDOM.getName(), ex);
} catch (SecurityException ex) {
// Might be thrown if resource access is restricted (such as in
// an applet sandbox).
throw new SeedException("SecurityManager prevented access to " + DEV_URANDOM.getName(), ex);
}
}
@Override
public String toString() {
return "/dev/urandom";
}
}
import java.security.GeneralSecurityException;
import java.util.Random;
import java.util.UUID;
import org.slf4j.LoggerFactory;
import org.uncommons.maths.random.AESCounterRNG;
import org.uncommons.maths.random.SeedException;
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.RandomBasedGenerator;
public enum UUIDgenerator {
INSTANCE;
private final RandomBasedGenerator rbg;
@SuppressWarnings("FieldCanBeLocal")
private final Random random = new Random();
UUIDGenerator() {
Random rng = random;
try {
try {
rng = new AESCounterRNG(new DevUrandomSeedGenerator());
} catch (SeedException se) {
LoggerFactory.getLogger(UUIDGenerator.class)
.warn("Unable to initialize AESCounterRNG - /dev/urandom seed failed", se);
byte[] seed = new byte[16];
random.nextBytes(seed);
rng = new AESCounterRNG(seed);
}
} catch (GeneralSecurityException gse) {
LoggerFactory.getLogger(UUIDGenerator.class).error("Unable to initialize AES cipher for AESCounterRNG",
gse);
}
this.rbg = Generators.randomBasedGenerator(rng);
}
public static UUID randomUUID() {
return INSTANCE.rbg.generate();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment