127

I am wondering if there is a way to generate the same UUID based on a String. I tried with UUID, it looks like it does not provide this feature.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Adam Lee
  • 24,710
  • 51
  • 156
  • 236

4 Answers4

231

You can use UUID this way to get always the same UUID for your input String:

 String aString="JUST_A_TEST_STRING";
 String result = UUID.nameUUIDFromBytes(aString.getBytes()).toString();
uraimo
  • 19,081
  • 8
  • 48
  • 55
  • 16
    any js equivalent? – Abhijeet Ahuja Feb 01 '17 at 05:10
  • any PHP equivalent? What is the algorithm behind this? – mika Jul 31 '17 at 07:46
  • 3
    @mika [This PHP UUID library](https://github.com/ramsey/uuid) is somewhat equivalent. You can generate the same UUID for the given namespace + string. You can do something like: `Uuid::uuid3(Uuid::NAMESPACE_DNS, 'TEST STRING')->toString();` It uses md5 hashing in this example. [Additional info on UUID namespaces](https://stackoverflow.com/a/28776880/1514049) – segFault Oct 20 '17 at 15:13
  • 4
    is there any way I can decode this UUID to original String ? – Mayur Aug 01 '18 at 07:59
  • 2
    If the original string is part of a known set of strings (stored in your db for example), you can generate the UUID for each string and compare with the UUID you want to decode. Otherwise, it is not "technically" possible – user108828 Feb 07 '19 at 13:38
  • 4
    what are the chances that the generated UUID from a given string will clash with a UUID generated from another string? – Groppe Mar 20 '19 at 01:02
  • 1
    @Groppe very small, similar to the chances that an MD5 (UUIDv3) or SHA1 (UUIDv5) hash clash for a given string – dtech Oct 09 '19 at 12:00
  • 1
    I know that we can create UUID from a string but I want to know if we can create String reverse back from UUID ? – VManoj Apr 07 '20 at 08:55
  • Why wouldn't you just do UUID.fromString? – opticyclic Apr 21 '21 at 05:59
  • 1
    @opticyclic UUID.fromString does not generate a new UUID based on the input, but expects an existing valid UUID string representation as input. – hp58 Aug 03 '21 at 06:56
  • `UUID.nameUUIDFromBytes` generates MD5 UUIDs. This does not work if the input String was generated by a SHA1 function. – kerner1000 Mar 08 '22 at 07:58
17

UUID.nameUUIDFromBytes() only generates MD5 UUIDs. However, SHA1 is preferred over MD5, if backward compatibility is not an issue.

The utility class below generates MD5 UUIDs and SHA-1 UUIDs. Feel free to use and share.

package com.example;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * Generates UUIDv3 (MD5) and UUIDv5 (SHA1).
 *
 * It is fully compliant with RFC-4122.
 */
public class HashUuid {

    private static final int V3 = 3; // MD5
    private static final int V5 = 5; // SHA-1

    private static final String HASH_V3 = "MD5";
    private static final String HASH_V5 = "SHA-1";

    public static final UUID NAMESPACE_DNS = new UUID(0x6ba7b8109dad11d1L, 0x80b400c04fd430c8L);
    public static final UUID NAMESPACE_URL = new UUID(0x6ba7b8119dad11d1L, 0x80b400c04fd430c8L);
    public static final UUID NAMESPACE_OID = new UUID(0x6ba7b8129dad11d1L, 0x80b400c04fd430c8L);
    public static final UUID NAMESPACE_X500 = new UUID(0x6ba7b8149dad11d1L, 0x80b400c04fd430c8L);

    public static UUID v3(String name) {
        return generate(V3, HASH_V3, null, name);
    }

    public static UUID v5(String name) {
        return generate(V5, HASH_V5, null, name);
    }

    public static UUID v3(UUID namespace, String name) {
        return generate(V3, HASH_V3, namespace, name);
    }

    public static UUID v5(UUID namespace, String name) {
        return generate(V5, HASH_V5, namespace, name);
    }

    private static UUID generate(int version, String algorithm, UUID namespace, String name) {

        MessageDigest hasher = hasher(algorithm);

        if (namespace != null) {
            ByteBuffer ns = ByteBuffer.allocate(16);
            ns.putLong(namespace.getMostSignificantBits());
            ns.putLong(namespace.getLeastSignificantBits());
            hasher.update(ns.array());
        }

        hasher.update(name.getBytes(StandardCharsets.UTF_8));
        ByteBuffer hash = ByteBuffer.wrap(hasher.digest());

        final long msb = (hash.getLong() & 0xffffffffffff0fffL) | (version & 0x0f) << 12;
        final long lsb = (hash.getLong() & 0x3fffffffffffffffL) | 0x8000000000000000L;

        return new UUID(msb, lsb);
    }

    private static MessageDigest hasher(String algorithm) {
        try {
            return MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(String.format("%s not supported.", algorithm));
        }
    }

    /**
     * For tests!
     */
    public static void main(String[] args) {

        UUID namespace = UUID.randomUUID();
        String name = "JUST_A_TEST_STRING";

        System.out.println(String.format("UUID.nameUUIDFromBytes():     '%s'", UUID.nameUUIDFromBytes(name.getBytes())));
        System.out.println();
        System.out.println(String.format("HashUuid.v3(name):            '%s'", HashUuid.v3(name)));
        System.out.println(String.format("HashUuid.v5(name):            '%s'", HashUuid.v5(name)));
        System.out.println(String.format("HashUuid.v3(namespace, name): '%s'", HashUuid.v3(namespace, name)));
        System.out.println(String.format("HashUuid.v5(namespace, name): '%s'", HashUuid.v5(namespace, name)));
    }
}

This is the output:

UUID.nameUUIDFromBytes():     '9e120341-627f-32be-8393-58b5d655b751'

HashUuid.v3(name):            '9e120341-627f-32be-8393-58b5d655b751'
HashUuid.v5(name):            'e4586bed-032a-5ae6-9883-331cd94c4ffa'
HashUuid.v3(namespace, name): 'f0043437-723b-308f-a6c0-74ec36ddf9c2'
HashUuid.v5(namespace, name): '18a45fd8-8fab-5647-aad7-1d3264932180'

Alternatively, you can also use uuid-creator. See this example:

// Create a UUIDv5 (SHA1)
String name = "JUST_A_TEST_STRING";
UUID uuid = UuidCreator.getNameBasedSha1(name);
fabiolimace
  • 972
  • 11
  • 13
  • Why do you think that SHA1 should be preferred over MD5 when generating a UUID? – Zhro Feb 15 '21 at 20:27
  • I don't think it should always be preferred. It depends on the case. RFC-4122, in its section 4.3, says that If backward compatibility is not an issue, SHA-1 is preferred. I'll fix my comment. Thanks. – fabiolimace Feb 15 '21 at 21:03
6

You should use UUID v5.

Version-3 and version-5 UUIDs are generated by hashing a namespace identifier and name. Version 3 uses MD5 as the hashing algorithm, and version 5 uses SHA-1.1 - wikipedia

UUID v5 requires a namespace. That namespace should be a UUID v4, which you can just generate online. The namespace assures that for a given input, the output will always be the same.

A possible implementation of UUID v5 can be found here:

<!-- https://search.maven.org/artifact/com.github.f4b6a3/uuid-creator -->
<dependency>
  <groupId>com.github.f4b6a3</groupId>
  <artifactId>uuid-creator</artifactId>
  <version>3.6.0</version>
</dependency>

It can be used as follows:

UUID namespace = ; // todo generate a UUID v4.
String input = "input";
UUID uuid = UuidCreator.getNameBasedSha1(namespace, input);

(In a way, the namespace acts like a seed would, for a random number generator. By contrast, while a seed is supposed to be random, our namespace is a constant. And that forces our generator to always produce the same value for a given input.)

bvdb
  • 22,839
  • 10
  • 110
  • 123
3

If you are looking for a Javascript alternative, look at uuid-by-string which also gives option to use SHA-1 or MD5 hash functions.

vhtc
  • 790
  • 6
  • 12