JDK使用哪个命名空间来生成一个名称为nameUUIDFromBytes的UUID?

15

太阳/Oracle JDK在java.util包中提供了一个函数来创建类型3(基于名称的)UUID:java.util.UUID.nameUUIDFromBytes(byte[] name)。

我需要能够使用nameUUIDFromBytes在Java中生成类型3 UUID,并在创建另一种语言的类型3 UUID时到达相同的UUID,假设我提供相同的字节作为源。

根据javadoc,此函数创建符合RFC 4122标准的类型3 UUID。然而,根据RFC 4122规范,必须在某个命名空间内创建类型3 UUID。大多数其他语言允许您在创建类型3 UUID时指定命名空间(例如Ruby中的UUIDTools gem)。

因此,我的问题是:当我调用nameUUIDFromBytes时,JDK使用哪个命名空间UUID?


JDK在内部使用MD5进行加密,尽管SHA-1也是允许的,甚至更受欢迎。因此,仅凭这个选择,UUID可能会有所不同。 - Joop Eggen
3个回答

9
请参见此错误报告。特别是底部的评论:

也许现在的做法是修复 javadoc,说明“nameUUIDFromBytes(byte[] namespaceAndName)”应传入一个包含命名空间UUID字节和名称字节(按该顺序)的字节数组。这是假设该方法只是针对byte[]进行MD5,并根据IETF文档设置字段。

我不知道我是否相信它能正确工作,但使用UUID规范中预定义的namespeces,与其他实现生成的相同UUID进行比较应该很容易测试。 [编辑2022-05-22:修复了Java bug数据库的链接,虽然新的bug数据库不包括先前数据库中的注释,特别是本答案中引用的注释]

链接似乎已经失效。 - Joel Aelwyn
可以从阅读JDK源代码中明显看出,UUID.nameUUIDFromBytes(byte[])的确只是对所有字节进行MD5,然后按照UUID v3所期望的设置“变体”和“版本”。 - Guss

3
UUID.nameUUIDFromBytes() 方法不使用命名空间生成 UUID v3。它只是使用 MD5 哈希运算处理 'name'。
你需要手动将命名空间字节(16 字节)与 'name' 字节连接起来,然后再调用 UUID.nameUUIDFromBytes() 方法。下面的两个示例使用 ByteBufferSystem.arraycopy() 在调用 UUID.nameUUIDFromBytes() 之前连接 'namespace' 和 'name'。

使用 '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
    }    
}

使用 '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
    }    
}

有两个库可以使用命名空间生成UUID v3和v5:


2

一个示例代码:

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());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接