RFC 4122 requires the use of MD5 oder SHA-1 to generate name-based UUIDs, prefering SHA-1 when possible. Since the standard has not been updated since more than a decade, more robust hash functions are not included in the standard. SHA-256 for example has a better resistance against preimage and collision attacks than SHA-1 and should retain these properties even after truncation. For that reason I developed the following class using SHA-256 to create the message digest, instead of MD5 or SHA-1.
import java.security.MessageDigest ;
import java.security.NoSuchAlgorithmException ;
import java.util.Arrays ;
import java.util.Objects ;
import java.util.UUID ;
/**
* Class to create name-based UUIDs using SHA-256 as hashing function.
* <p>A lot of code in here is taken from {@link UUID}.</p>
*/
public class UUIDSha256 {
/**
* Static factory to retrieve a name-based (hashing) {@link UUID} based on
* the specified namespace and name.
* <p>This method corresponds to {@link UUID#nameUUIDFromBytes(byte[])}</p>
*
* @param namespace namespace for the UUID
* @param name byte array
* @return A {@link UUID} generated from the specified namespace and name
*/
public static UUID nameUUIDFromNamespaceAndBytes ( UUID namespace , byte [] name ) {
Objects . requireNonNull ( namespace , "namespace is null" );
Objects . requireNonNull ( name , "name is null" );
MessageDigest md ;
try {
md = MessageDigest . getInstance ( "SHA-256" );
} catch ( NoSuchAlgorithmException nsae ) {
throw new InternalError ( "SHA-256 not supported" );
}
md . update ( toBytes ( namespace ));
md . update ( name );
byte [] sha256Bytes = md . digest ();
sha256Bytes [ 6 ] &= 0x0f ; /* clear version */
sha256Bytes [ 6 ] |= 0x50 ; /* set to version 5 */
sha256Bytes [ 8 ] &= 0x3f ; /* clear variant */
sha256Bytes [ 8 ] |= 0x80 ; /* set to IETF variant */
return fromBytes ( Arrays . copyOfRange ( sha256Bytes , 0 , 16 ));
}
private static UUID fromBytes ( byte [] data ) {
long msb = 0 ;
long lsb = 0 ;
assert data . length == 16 : "data must be 16 bytes in length" ;
for ( int i = 0 ; i < 8 ; i ++) {
msb = ( msb << 8 ) | ( data [ i ] & 0xff );
}
for ( int i = 8 ; i < 16 ; i ++) {
lsb = ( lsb << 8 ) | ( data [ i ] & 0xff );
}
return new UUID ( msb , lsb );
}
private static byte [] toBytes ( UUID uuid ) {
byte [] out = new byte [ 16 ];
long msb = uuid . getMostSignificantBits ();
long lsb = uuid . getLeastSignificantBits ();
for ( int i = 0 ; i < 8 ; i ++) {
out [ i ] = ( byte ) (( msb >> (( 7 - i ) * 8 )) & 0xff );
}
for ( int i = 8 ; i < 16 ; i ++) {
out [ i ] = ( byte ) (( lsb >> (( 15 - i ) * 8 )) & 0xff );
}
return out ;
}
}