Tuesday, 21 November 2023

A Beginner's Guide to UUID Types

UUID stands for "Universally Unique Identifier." It's a way to give a unique identity to something, like a piece of information or an object, in the world of computing. The key word here is "unique" – it means that no two things should have the same UUID.

 

For example, Think of it like an Aadhar number for a person. Each person has a unique Aadhar number that helps to identify the person.

 

Different versions of UUIDs

UUID v1 (Time-based)

UUID type 1 use the current timestamp with a node identifier to generate a unique code.

 

UUID v2 (DCE Security)

UUID v2 closely resembles UUID v1, with the addition of an enhanced security layer. It strengthens security by substituting the least significant bits of the clock sequence and timestamp with a local domain number and a meaningful identifier. This modification renders UUID v2 particularly well-suited for applications with elevated security requirements.

 

UUID v3 (MD5-based name UUID)

UUID v3 generates a unique code based on a combination of a namespace and a name. It uses the MD5 hashing algorithm to convert the namespace and name into a 128-bit hash value, which is then used to create the UUID. This type of UUID is useful for identifying objects within a specific namespace, such as files in a file system.

 

UUID v4 (Random UUID)

UUID v4 is the most commonly used type of UUID. It generates a completely random 128-bit code, ensuring that the UUID is extremely unlikely to collide with any other UUID.

 

UUID v5 (SHA-1-based name UUID)

Just like UUID v3, UUID v5 creates a special code using a mix of a category and a name. The difference is that it uses SHA-1 to make this code. This type of UUID is good for applications that need both a one-of-a-kind code and extra security.

 

Deterministic and non-deterministic UUIDs

A deterministic UUID is made from an input that you can expect, so if you use the same input, you'll always get the same result. On the other hand, a non-deterministic UUID comes from a random input, so you always get a different result.

 

People often use deterministic UUIDs when they need to consistently identify the same thing over time. For instance, they're commonly employed to create UUIDs for database records. On the flip side, non-deterministic UUIDs are preferred in situations where privacy is crucial. For example, they're frequently used to generate UUIDs for user sessions.

 

UUID type 2, 3 and 5 are deterministic algorithms, whereas type 1 and 4 are non-deterministic algorithms.

 

Following utility class generate uuid for the types 1, 3, 4 and 5.

 

UUIDGenerator.java

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

public class UUIDGenerator {

	private static long getNodeId() {
		try {
			NetworkInterface networkInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
			byte[] macAddress = networkInterface.getHardwareAddress();

			if (macAddress != null) {
				long nodeID = 0;

				for (int i = 0; i < 6; i++) {
					nodeID = (nodeID << 8) | (macAddress[i] & 0xFF);
				}

				return nodeID;
			}
		} catch (Exception e) {
			// Ignore exception
		}

		return (long) Math.random() * Long.MAX_VALUE;
	}

	public static UUID generateUUID1() {
		long timestamp = System.currentTimeMillis();
		long nodeID = getNodeId();

		return new UUID(timestamp, nodeID);
	}

	public static UUID generateUUID3(String namespace, String name) {
		return generateNameBasedUUID(namespace, name, "MD5", 3);
	}

	public static UUID generateUUID4() {
		return UUID.randomUUID();
	}

	public static UUID generateUUID5(String namespace, String name) {
		return generateNameBasedUUID(namespace, name, "SHA-1", 5);
	}

	private static UUID generateNameBasedUUID(String namespace, String name, String algorithm, int version) {
		try {
			MessageDigest md = MessageDigest.getInstance(algorithm);
			md.update(namespace.getBytes(StandardCharsets.UTF_8));
			md.update(name.getBytes(StandardCharsets.UTF_8));

			byte[] hashBytes = md.digest();
			hashBytes[6] &= 0x0F; // Clear version
			hashBytes[6] |= (version << 4); // Set version
			hashBytes[8] &= 0x3F; // Clear variant
			hashBytes[8] |= 0x80; // Set variant

			return new UUID(bytesToLong(hashBytes, 0), bytesToLong(hashBytes, 8));
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("Hash algorithm not available: " + algorithm, e);
		}
	}

	private static long bytesToLong(byte[] bytes, int offset) {
		long result = 0;
		for (int i = 0; i < 8; i++) {
			result = (result << 8) | (bytes[offset + i] & 0xFF);
		}
		return result;
	}

	public static void main(String[] args) {
		// Generate and validate UUIDs for all types
		UUID uuid1 = generateUUID1();
		UUID uuid3 = generateUUID3("my-namespace", "my-name");
		UUID uuid4 = generateUUID4();
		UUID uuid5 = generateUUID5("my-namespace", "my-name");

		// Print results
		System.out.println("UUID1: " + uuid1);
		System.out.println("UUID3: " + uuid3);
		System.out.println("UUID4: " + uuid4);
		System.out.println("UUID5: " + uuid5);
	}
}

Output

UUID1: 0000018b-f105-d3b7-0000-88665a4dab02
UUID3: be96d0ea-c5a5-3fcc-b188-e7af3cbac3a7
UUID4: ea800779-1b58-4844-81eb-51a95500ffbb
UUID5: 071be3ef-e459-534e-87a9-cc4f96725002



  

You may like

Interview Questions

Implement retry handler for a task in Java

Design an utility class to capture application metrics summary in Java

Utility class to get primitive type from wrapper type and vice versa

FNV hash algorithm implementation in Java

Controlling Randomness in Java: Exploring the Role of Seeds in java.util.Random

Calculating initial capacity from expected size and load factor

No comments:

Post a Comment