Skip to content

Instantly share code, notes, and snippets.

@fabiolimace
Last active October 24, 2020 21:21
Show Gist options
  • Save fabiolimace/d43ef0ffb4f2e639bb0d81906963bb35 to your computer and use it in GitHub Desktop.
Save fabiolimace/d43ef0ffb4f2e639bb0d81906963bb35 to your computer and use it in GitHub Desktop.
Generate name-based UUID in Java
package your.package.name;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
/**
* A UUID generator that creates hash-based or name-based UUIDs (MD5 and SHA-1).
*
* RFC-4122 compliant.
*
* Tags: uuid guid uuid-generator guid-generator generator name hash md5 sha1 rfc4122 rfc-4122
*
* @author: Fabio Lima 2020
*/
public class HashUuidCreator {
// Domain Name System
public static final UUID NAMESPACE_DNS = new UUID(0x6ba7b8109dad11d1L, 0x80b400c04fd430c8L);
// Uniform Resource Locator
public static final UUID NAMESPACE_URL = new UUID(0x6ba7b8119dad11d1L, 0x80b400c04fd430c8L);
// ISO Object ID
public static final UUID NAMESPACE_ISO_OID = new UUID(0x6ba7b8129dad11d1L, 0x80b400c04fd430c8L);
// X.500 Distinguished Name
public static final UUID NAMESPACE_X500_DN = new UUID(0x6ba7b8149dad11d1L, 0x80b400c04fd430c8L);
private static final int VERSION_3 = 3; // UUIDv3 MD5
private static final int VERSION_5 = 5; // UUIDv5 SHA1
private static final String MESSAGE_DIGEST_MD5 = "MD5"; // UUIDv3
private static final String MESSAGE_DIGEST_SHA1 = "SHA-1"; // UUIDv5
private static UUID getHashUuid(UUID namespace, String name, MessageDigest hasher, int version) {
final byte[] hash;
// Insert name space if NOT NULL
if (namespace != null) {
hasher.update(toBytes(namespace.getMostSignificantBits()));
hasher.update(toBytes(namespace.getLeastSignificantBits()));
}
// Generate the hash
hash = hasher.digest(name.getBytes(StandardCharsets.UTF_8));
// Split the hash into two parts: MSB and LSB
long msb = toNumber(hash, 0, 8); // first 8 bytes for MSB
long lsb = toNumber(hash, 8, 16); // last 8 bytes for LSB
// Apply version and variant bits (required for RFC-4122 compliance)
msb = (msb & 0xffffffffffff0fffL) | (version & 0x0f) << 12; // apply version bits
lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // apply variant bits
// Return the UUID
return new UUID(msb, lsb);
}
public static UUID getMd5Uuid(String string) {
return getHashUuid(null, string, Md5HasherLazyHolder.INSTANCE, VERSION_3);
}
public static UUID getSha1Uuid(String string) {
return getHashUuid(null, string, Sha1HasherLazyHolder.INSTANCE, VERSION_5);
}
public static UUID getMd5Uuid(UUID namespace, String string) {
return getHashUuid(namespace, string, Md5HasherLazyHolder.INSTANCE, VERSION_3);
}
public static UUID getSha1Uuid(UUID namespace, String string) {
return getHashUuid(namespace, string, Sha1HasherLazyHolder.INSTANCE, VERSION_5);
}
private static byte[] toBytes(final long number) {
return new byte[] { (byte) (number >>> 56), (byte) (number >>> 48), (byte) (number >>> 40),
(byte) (number >>> 32), (byte) (number >>> 24), (byte) (number >>> 16), (byte) (number >>> 8),
(byte) (number) };
}
private static long toNumber(final byte[] bytes, final int start, final int length) {
long result = 0;
for (int i = start; i < length; i++) {
result = (result << 8) | (bytes[i] & 0xff);
}
return result;
}
private static class Md5HasherLazyHolder {
static final MessageDigest INSTANCE = getInstance();
private static MessageDigest getInstance() {
try {
return MessageDigest.getInstance(MESSAGE_DIGEST_MD5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Message digest algorithm not supported.");
}
}
}
private static class Sha1HasherLazyHolder {
static final MessageDigest INSTANCE = getInstance();
private static MessageDigest getInstance() {
try {
return MessageDigest.getInstance(MESSAGE_DIGEST_SHA1);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Message digest algorithm not supported.");
}
}
}
/**
* For tests!
*/
public static void main(String[] args) {
String string = "JUST_A_TEST_STRING";
UUID namespace = UUID.randomUUID(); // A custom name space
System.out.println("Java's generator");
System.out.println("UUID.nameUUIDFromBytes(): '" + UUID.nameUUIDFromBytes(string.getBytes()) + "'");
System.out.println();
System.out.println("This generator");
System.out.println("HashUuidCreator.getMd5Uuid(): '" + HashUuidCreator.getMd5Uuid(string) + "'");
System.out.println("HashUuidCreator.getSha1Uuid(): '" + HashUuidCreator.getSha1Uuid(string) + "'");
System.out.println();
System.out.println("This generator WITH name space");
System.out.println("HashUuidCreator.getMd5Uuid(): '" + HashUuidCreator.getMd5Uuid(namespace, string) + "'");
System.out.println("HashUuidCreator.getSha1Uuid(): '" + HashUuidCreator.getSha1Uuid(namespace, string) + "'");
}
}
// OUTPUT:
//
// Java's generator
// UUID.nameUUIDFromBytes(): '9e120341-627f-32be-8393-58b5d655b751'
//
// This generator
// HashUuidCreator.getMd5Uuid(): '9e120341-627f-32be-8393-58b5d655b751'
// HashUuidCreator.getSha1Uuid(): 'e4586bed-032a-5ae6-9883-331cd94c4ffa'
//
// This generator WITH name space
// HashUuidCreator.getMd5Uuid(): 'b5ac8a9a-096f-3256-8b6b-25d382116360'
// HashUuidCreator.getSha1Uuid(): '8b25a9ec-e127-53fd-b473-c91a69e68853'
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment