John McCann
John McCann

Reputation: 315

What namespace does the JDK use to generate a UUID with nameUUIDFromBytes?

The Sun/Oracle JDK exposes a function to create a type 3 (name based) UUID in the java.util package: java.util.UUID.nameUUIDFromBytes(byte[] name).

I need to be able to generate a type 3 UUID in Java using nameUUIDFromBytes and arrive at the same UUID when creating a type 3 UUID in another language, assuming I provide the same bytes as the source.

According to the javadocs this function creates a RFC 4122 compliant type 3 UUID. However, according to the RFC 4122 spec, a type 3 UUID must be created within some namespace. Most other languages allow you specify the namespace when creating a type 3 UUID (e.g. the UUIDTools gem in Ruby).

So my question is: what namespace UUID is used by the JDK when I invoke nameUUIDFromBytes?

Upvotes: 15

Views: 5530

Answers (3)

theglauber
theglauber

Reputation: 29625

See this bug report

Especially the comment, near the bottom:

Perhaps the course of action at this point would be to fix the javadoc stating "nameUUIDFromBytes(byte[] namespaceAndName) "one should pass-in a byte array containing the concatenation of the namespace UUID's bytes and the name bytes (in that order)" That's assuming the method just MD5's the byte[] and sets the fields as per the IETF document.

I don't know if i trust this to work correctly, but it should be easy to test using the predefined namespeces from the UUID spec, comparing with same UUID generated by some other implementation.

[Edit 2022-05-22: fixed link to working Java bug database, though the new bug database doesn't include the comments from the previous database, notably the one quoted in this answer ]

Upvotes: 9

fabiolimace
fabiolimace

Reputation: 1207

The UUID.nameUUIDFromBytes() method doesn't use a namespace to generate UUIDs v3. It just hashes the 'name' using MD5.

You must manually concatenate the namespace bytes (16 bytes) with the 'name' bytes. The 2 examples below use ByteBuffer and System.arraycopy() to concatenate 'namespace' and 'name' before calling UUID.nameUUIDFromBytes().

Example using 'ByteBuffer'

package com.example;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

public class Example1 {
    
    /**
     * Returns a name-based UUID v3 using a namespace.
     * 
     * @param namespace a UUID
     * @param name a string
     * @return a UUID
     */
    public static UUID getNameBased(UUID namespace, String name) {

        // 1. Get the NAMESPACE bytes
        final byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);

        // 2. Concatenate NAMESPACE and NAME bytes
        ByteBuffer buffer = ByteBuffer.allocate(nameBytes.length + 16);
        buffer.putLong(namespace.getMostSignificantBits());
        buffer.putLong(namespace.getLeastSignificantBits());
        buffer.put(nameBytes);
        
        // 3. Generate a name-based UUID
        return UUID.nameUUIDFromBytes(buffer.array());
    }
    
    public static void main(String[] args) {
        UUID namespace = UUID.fromString("11111111-2222-3333-4444-555555555555");
        String name = "this is a test";
        UUID uuid = getNameBased(namespace, name);
        System.out.println(uuid); // prints: 04334e59-716d-3078-9722-63e2ec20d4ec
    }    
}

Example using 'System.arraycopy()'

package com.example;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

public class Example2 {
    
    /**
     * Returns a name-based UUID v3 using a namespace.
     * 
     * @param namespace a UUID
     * @param name a string
     * @return a UUID
     */
    public static UUID getNameBased(UUID namespace, String name) {
        
        // 1. Get NAMESPACE and NAME bytes
        final byte[] msb = toBytes(namespace.getMostSignificantBits());
        final byte[] lsb = toBytes(namespace.getLeastSignificantBits());
        final byte[] nam = name.getBytes(StandardCharsets.UTF_8);
        
        // 2. Concatenate NAMESPACE and NAME bytes
        final byte[] bytes = new byte[16 + nam.length];
        System.arraycopy(msb, 0, bytes, 0, 8);
        System.arraycopy(lsb, 0, bytes, 8, 8);
        System.arraycopy(nam, 0, bytes, 16, nam.length);
        
        // 3. Generate a name-based UUID
        return UUID.nameUUIDFromBytes(bytes);
    }
    
    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) };
    }
    
    public static void main(String[] args) {
        UUID namespace = UUID.fromString("11111111-2222-3333-4444-555555555555");
        String name = "this is a test";
        UUID uuid = getNameBased(namespace, name);
        System.out.println(uuid); // prints: 04334e59-716d-3078-9722-63e2ec20d4ec
    }    
}

Libraries

There are 2 libraries that can generate UUIDs v3 and v5 using namespaces:

Upvotes: 3

vinay sudhakar
vinay sudhakar

Reputation: 200

A Sample Code:

String NameSpace_OID_string = "6ba7b812-9dad-11d1-80b4-00c04fd430c8";
UUID NameSpace_OID_uuid = UUID.fromString(NameSpace_OID_string);

long msb = NameSpace_OID_uuid.getMostSignificantBits();
long lsb = NameSpace_OID_uuid.getLeastSignificantBits();

    byte[] NameSpace_OID_buffer = new byte[16];

    for (int i = 0; i < 8; i++) {
        NameSpace_OID_buffer[i] = (byte) (msb >>> 8 * (7 - i));
    }
    for (int i = 8; i < 16; i++) {
        NameSpace_OID_buffer[i] = (byte) (lsb >>> 8 * (7 - i));
    }

    String name = "user123";
    byte[] name_buffer = name.getBytes();

ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
try {
    outputStream.write( NameSpace_OID_buffer);
    outputStream.write( name_buffer );
} catch (IOException e) {
        // TODO Auto-generated catch block
    e.printStackTrace();
}


byte byteArray[] = outputStream.toByteArray();

System.out.println(UUID.nameUUIDFromBytes(byteArray).toString());

Upvotes: 2

Related Questions