Last active
October 24, 2020 21:21
-
-
Save fabiolimace/d43ef0ffb4f2e639bb0d81906963bb35 to your computer and use it in GitHub Desktop.
Generate name-based UUID in Java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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