6

I generate UUIDs, and valid them against a Regex in my code; I just ran into problems that confused me

Here is the code that generates UUIDs (in a mongodb context)

import java.util.UUID;
... ...

Document setOnInsert = new Document(Params.sender, UUID.randomUUID())
                                    .append(Params.userDevice, userDevice)
                                    .append(Params.hostId,"");

This is the code of validating an UUID; I had copied the Regex from this post

static final Pattern UUID = Pattern.compile("([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})");

    public static boolean isUUID(String uuid){
        if(uuid == null){
            return false;
        }else{
            return UUID.matcher(uuid).matches();
        }
    }

and below are the 2 UUIDs that I have problems with

aa4aaa2c-c6ca-d5f5-b8b2-0b5c78ee2cb7
b24dd64c-de6b-5bf6-6283-aa2167cc93a7

These two UUIDs had been generated by the code mentioned above; the validating method (isUUID()) judged them as invalid in my latest debug; yet I posted these UUIDs to an online validator , and it says ok

This is my system information

wjz@bj:~$ java -version 
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
wjz@bj:~$ 
wjz@bj:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.1 LTS
Release:    16.04
Codename:   xenial
wjz@bj:~$ 

Some background: I had been working on jdk 1.8.0_111; these UUIDs had been generated then, and had no problems. then I upgraded to 1.8.0_121 today, and run into this problem...

So my question is: Whether the above mentioned UUIDs are correct or wrong? who to believe, the generator or the validation

Community
  • 1
  • 1
George Wang
  • 765
  • 2
  • 13
  • 28
  • 5
    Why not just use the UUID class to validate them? `UUID.fromString()` – wvdz Jan 19 '17 at 08:36
  • 6
    Well the problem is with this part of the pattern: `[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}`. In the first one you've got a "d" where you should have 1-5; in the second one you've got 6 where you should have 8-b. – Jon Skeet Jan 19 '17 at 08:37
  • 6
    [RFC 4122](https://tools.ietf.org/html/rfc4122) has no such restrictions on the first hex digit of those groups, it just says the group consists of two `hexOctet` (which is two hex digits each). You are restricting it more than the specification does. Trust the built-in generator, distrust the "found somewhere on the internet" validator. – Amadan Jan 19 '17 at 08:38
  • actually, the isUUID() is a protection before UUID.fromString() in my code; otherwise, i might got an exception if my feed of string is not an UUID – George Wang Jan 19 '17 at 08:52
  • UUID of java is correct, so the prime suspect is the regex, I posted a valid regex below. – Christophe Roussy Jun 21 '17 at 13:28
  • Possible duplicate of [Foolproof way of differentiating String and UUID](https://stackoverflow.com/questions/18724750/foolproof-way-of-differentiating-string-and-uuid) – Victor Mar 10 '18 at 17:47
  • [RFC 4122](https://tools.ietf.org/html/rfc4122) does restrict the first hex digit of those groups in [§4.1.3](https://tools.ietf.org/html/rfc4122#section-4.1.3) and [§4.1.1](https://tools.ietf.org/html/rfc4122#section-4.1.1) for the Version and Variant of the UUID. – roverwolf Jul 03 '19 at 15:46

4 Answers4

8

My suggestion is, do not reinvent the wheel.

Basically, if you generate the ids with UUID.randomUUID(), there is no need to validate them. If you are anyway curious that they might get manipulated manually. You can just use UUID.fromString(yourUUID) and catch the IllegalArgumentExcepetion and the NumberFormatException that might be thrown.

Throws IllegalArgumentExcepetion:

If name does not conform to the string representation as described in toString()

Furthermore, you can check behind, if the UUID got converted correctly with

UUID id = UUID.fromString(yourUUID);
if(id.toString().equals(yourUUID){
    //success
}
JacksOnF1re
  • 3,336
  • 24
  • 55
  • 1
    In Java 8 fromString will not throw an exception if the UUID is not valid. It will simply cut some digits and consider that the UUID is valid. This is a known bug, see here https://bugs.java.com/view_bug.do?bug_id=8159339 . As @JacksOnF1re mentions equals gets the job done. – ampofila Aug 08 '18 at 11:59
  • 1
    One thing to look after is that `UUID.fromString` does not specify the expected UUID version, so it will guess and take whatever works. So you may also check for the version using `yourUUID.version() == 4` for example. – Christophe Roussy Oct 08 '19 at 15:32
4

You can use UUID.randomUUID() which will generate a valid UUID, you dont need the regx.

Jobin
  • 5,610
  • 5
  • 38
  • 53
  • thank for giving me the confidence on the generator. well, the scenario is that UUIDs were generated, yet not consumed and expired right away; instead, they might live for quite some time, might be save, copied and transmitted. so it is a precaution against any exception before using – George Wang Jan 19 '17 at 09:00
  • @Jobin: we don't see where `isUUID` is called and the question already contained `UUID.randomUUID()`. The regex was just used to validate the input, so it's ok to use it. – Roland Jan 19 '17 at 10:26
  • 2
    I dont think he need to validate the UUID which is generated using `UUID.randomUUID()` (will return a valid uuid) – Jobin Jan 19 '17 at 10:30
1

Had a look at some other SO answers, other languages ..., and here is a pure Java solution which handles v4 cases (as used by default in Java 8):

 UUID myUuid = UUID.fromString(uuidStr); // Step 1, throws errors !
 ... myUuid.version() == 4 // Step 2, check for the version you desire

See: https://docs.oracle.com/javase/9/docs/api/java/util/UUID.html#version--

Here is an example of a perfectly valid UUID string but version 1:

  public static void main(final String[] args) {
    final UUID myUuid = UUID.fromString("61614667-d279-11e7-a5ac-f941ac8dfc39");
    System.out.println(myUuid.version()); // Prints 1, not 4 !
  }

To try out more v4 UUIDs use UUID.randomUUID() or online: https://www.uuidgenerator.net/version4

Note: UUID.randomUUID() of Java will work well see How good is Java's UUID.randomUUID?

Security note:

Static factory to retrieve a type 4 (pseudo randomly generated) UUID. The UUID is generated using a cryptographically strong pseudo random number generator

Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85
1

There are several aspects:

  • Total UUID length
  • Characters used
  • Valid size of groups (between dashes)
  • Lower case or upper case
  • Valid version
  • Valid variant
  • Nil allowed

To have a code example, see the UUID validator implementation I recently added to Apache Commons Validator. It's not yet been merged, but you can vote for it here: https://github.com/apache/commons-validator/pull/68

Daniel Heid
  • 199
  • 1
  • 5
  • Excellent validator! – fabiolimace Jan 12 '22 at 06:11
  • 1
    Thanks, so much @fabiolimace! I also added a bean validation constraint to the Hibernate Validators. They reviewed it, but the merge takes time. Vote if you want to support it: https://github.com/hibernate/hibernate-validator/pull/1199 – Daniel Heid Jan 13 '22 at 07:09