diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java index e1e515d89f6c..a46bf2aa992f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java @@ -6,13 +6,9 @@ import java.io.UnsupportedEncodingException; import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; -import org.hibernate.HibernateException; - import static java.util.Comparator.comparing; /** @@ -125,27 +121,121 @@ public String generateHashedConstraintName( /** * Hash a constraint name using MD5. Convert the MD5 digest to base 35 - * (full alphanumeric), guaranteeing - * that the length of the name will always be smaller than the 30 - * character identifier restriction enforced by a few dialects. + * (full alphanumeric), guaranteeing that the length of the name will + * always be smaller than the 30 character identifier restriction + * enforced by some dialects. * * @param name The name to be hashed. * * @return String The hashed name. */ public String hashedName(String name) { + final byte[] bytes; try { - final MessageDigest md5 = MessageDigest.getInstance( "MD5" ); - md5.reset(); - md5.update( charset != null ? name.getBytes( charset ) : name.getBytes() ); - final BigInteger bigInt = new BigInteger( 1, md5.digest() ); - // By converting to base 35 (full alphanumeric), we guarantee - // that the length of the name will always be smaller than the 30 - // character identifier restriction enforced by a few dialects. - return bigInt.toString( 35 ); + bytes = charset == null + ? name.getBytes() + : name.getBytes( charset ); + } + catch (UnsupportedEncodingException uee) { + throw new IllegalArgumentException(uee); + } + final byte[] digest = hash( pad( bytes ) ); + return new BigInteger( 1, digest ).toString( 35 ); + } + + // Constants for MD5 + private static final int[] S = { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 + }; + + private static final int[] K = new int[64]; + static { + for ( int i = 0; i < 64; i++ ) { + K[i] = (int)(long) ( (1L << 32) * Math.abs( Math.sin( i + 1 ) ) ); + } + } + + public static byte[] hash(byte[] message) { + int a0 = 0x67452301; + int b0 = 0xefcdab89; + int c0 = 0x98badcfe; + int d0 = 0x10325476; + + for ( int i = 0; i < message.length / 64; i++ ) { + final int[] M = new int[16]; + for (int j = 0; j < 16; j++) { + M[j] = ((message[i * 64 + j * 4] & 0xFF)) + | ((message[i * 64 + j * 4 + 1] & 0xFF) << 8) + | ((message[i * 64 + j * 4 + 2] & 0xFF) << 16) + | ((message[i * 64 + j * 4 + 3] & 0xFF) << 24); + } + + int A = a0, B = b0, C = c0, D = d0; + + for (int j = 0; j < 64; j++) { + final int F, g; + if (j < 16) { + F = (B & C) | (~B & D); + g = j; + } + else if (j < 32) { + F = (D & B) | (~D & C); + g = (5 * j + 1) % 16; + } + else if (j < 48) { + F = B ^ C ^ D; + g = (3 * j + 5) % 16; + } + else { + F = C ^ (B | ~D); + g = (7 * j) % 16; + } + + final int temp = D; + D = C; + C = B; + B = B + Integer.rotateLeft( A + F + K[j] + M[g], S[j] ); + A = temp; + } + + a0 += A; + b0 += B; + c0 += C; + d0 += D; } - catch ( NoSuchAlgorithmException | UnsupportedEncodingException e ) { - throw new HibernateException( "Unable to generate a hashed name", e ); + + // Convert final state to byte array (little-endian) + final byte[] digest = new byte[16]; + encodeInt( digest, 0, a0 ); + encodeInt( digest, 4, b0 ); + encodeInt( digest, 8, c0 ); + encodeInt( digest, 12, d0 ); + return digest; + } + + private static void encodeInt(byte[] output, int offset, int value) { + output[offset] = (byte) (value & 0xFF); + output[offset + 1] = (byte) ((value >>> 8) & 0xFF); + output[offset + 2] = (byte) ((value >>> 16) & 0xFF); + output[offset + 3] = (byte) ((value >>> 24) & 0xFF); + } + + private static byte[] pad(byte[] input) { + final int originalLength = input.length; + final int numPaddingBytes = ( 56 - (originalLength + 1) % 64 + 64 ) % 64; + + final byte[] padded = new byte[originalLength + 1 + numPaddingBytes + 8]; + System.arraycopy( input, 0, padded, 0, originalLength ); + padded[originalLength] = (byte) 0x80; + + long bitLength = (long) originalLength * 8; + for ( int i = 0; i < 8; i++ ) { + padded[padded.length - 8 + i] = (byte) ( ( bitLength >>> (8 * i) ) & 0xFF ); } + + return padded; } }