|
6 | 6 |
|
7 | 7 | import java.io.UnsupportedEncodingException;
|
8 | 8 | import java.math.BigInteger;
|
9 |
| -import java.security.MessageDigest; |
10 |
| -import java.security.NoSuchAlgorithmException; |
11 | 9 | import java.util.Arrays;
|
12 | 10 | import java.util.List;
|
13 | 11 |
|
14 |
| -import org.hibernate.HibernateException; |
15 |
| - |
16 | 12 | import static java.util.Comparator.comparing;
|
17 | 13 |
|
18 | 14 | /**
|
@@ -125,27 +121,121 @@ public String generateHashedConstraintName(
|
125 | 121 |
|
126 | 122 | /**
|
127 | 123 | * Hash a constraint name using MD5. Convert the MD5 digest to base 35
|
128 |
| - * (full alphanumeric), guaranteeing |
129 |
| - * that the length of the name will always be smaller than the 30 |
130 |
| - * character identifier restriction enforced by a few dialects. |
| 124 | + * (full alphanumeric), guaranteeing that the length of the name will |
| 125 | + * always be smaller than the 30 character identifier restriction |
| 126 | + * enforced by some dialects. |
131 | 127 | *
|
132 | 128 | * @param name The name to be hashed.
|
133 | 129 | *
|
134 | 130 | * @return String The hashed name.
|
135 | 131 | */
|
136 | 132 | public String hashedName(String name) {
|
| 133 | + final byte[] bytes; |
137 | 134 | try {
|
138 |
| - final MessageDigest md5 = MessageDigest.getInstance( "MD5" ); |
139 |
| - md5.reset(); |
140 |
| - md5.update( charset != null ? name.getBytes( charset ) : name.getBytes() ); |
141 |
| - final BigInteger bigInt = new BigInteger( 1, md5.digest() ); |
142 |
| - // By converting to base 35 (full alphanumeric), we guarantee |
143 |
| - // that the length of the name will always be smaller than the 30 |
144 |
| - // character identifier restriction enforced by a few dialects. |
145 |
| - return bigInt.toString( 35 ); |
| 135 | + bytes = charset == null |
| 136 | + ? name.getBytes() |
| 137 | + : name.getBytes( charset ); |
| 138 | + } |
| 139 | + catch (UnsupportedEncodingException uee) { |
| 140 | + throw new IllegalArgumentException(uee); |
| 141 | + } |
| 142 | + final byte[] digest = hash( pad( bytes ) ); |
| 143 | + return new BigInteger( 1, digest ).toString( 35 ); |
| 144 | + } |
| 145 | + |
| 146 | + // Constants for MD5 |
| 147 | + private static final int[] S = { |
| 148 | + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, |
| 149 | + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, |
| 150 | + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, |
| 151 | + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 |
| 152 | + }; |
| 153 | + |
| 154 | + private static final int[] K = new int[64]; |
| 155 | + static { |
| 156 | + for ( int i = 0; i < 64; i++ ) { |
| 157 | + K[i] = (int)(long) ( (1L << 32) * Math.abs( Math.sin( i + 1 ) ) ); |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + public static byte[] hash(byte[] message) { |
| 162 | + int a0 = 0x67452301; |
| 163 | + int b0 = 0xefcdab89; |
| 164 | + int c0 = 0x98badcfe; |
| 165 | + int d0 = 0x10325476; |
| 166 | + |
| 167 | + for ( int i = 0; i < message.length / 64; i++ ) { |
| 168 | + final int[] M = new int[16]; |
| 169 | + for (int j = 0; j < 16; j++) { |
| 170 | + M[j] = ((message[i * 64 + j * 4] & 0xFF)) |
| 171 | + | ((message[i * 64 + j * 4 + 1] & 0xFF) << 8) |
| 172 | + | ((message[i * 64 + j * 4 + 2] & 0xFF) << 16) |
| 173 | + | ((message[i * 64 + j * 4 + 3] & 0xFF) << 24); |
| 174 | + } |
| 175 | + |
| 176 | + int A = a0, B = b0, C = c0, D = d0; |
| 177 | + |
| 178 | + for (int j = 0; j < 64; j++) { |
| 179 | + final int F, g; |
| 180 | + if (j < 16) { |
| 181 | + F = (B & C) | (~B & D); |
| 182 | + g = j; |
| 183 | + } |
| 184 | + else if (j < 32) { |
| 185 | + F = (D & B) | (~D & C); |
| 186 | + g = (5 * j + 1) % 16; |
| 187 | + } |
| 188 | + else if (j < 48) { |
| 189 | + F = B ^ C ^ D; |
| 190 | + g = (3 * j + 5) % 16; |
| 191 | + } |
| 192 | + else { |
| 193 | + F = C ^ (B | ~D); |
| 194 | + g = (7 * j) % 16; |
| 195 | + } |
| 196 | + |
| 197 | + final int temp = D; |
| 198 | + D = C; |
| 199 | + C = B; |
| 200 | + B = B + Integer.rotateLeft( A + F + K[j] + M[g], S[j] ); |
| 201 | + A = temp; |
| 202 | + } |
| 203 | + |
| 204 | + a0 += A; |
| 205 | + b0 += B; |
| 206 | + c0 += C; |
| 207 | + d0 += D; |
146 | 208 | }
|
147 |
| - catch ( NoSuchAlgorithmException | UnsupportedEncodingException e ) { |
148 |
| - throw new HibernateException( "Unable to generate a hashed name", e ); |
| 209 | + |
| 210 | + // Convert final state to byte array (little-endian) |
| 211 | + final byte[] digest = new byte[16]; |
| 212 | + encodeInt( digest, 0, a0 ); |
| 213 | + encodeInt( digest, 4, b0 ); |
| 214 | + encodeInt( digest, 8, c0 ); |
| 215 | + encodeInt( digest, 12, d0 ); |
| 216 | + return digest; |
| 217 | + } |
| 218 | + |
| 219 | + private static void encodeInt(byte[] output, int offset, int value) { |
| 220 | + output[offset] = (byte) (value & 0xFF); |
| 221 | + output[offset + 1] = (byte) ((value >>> 8) & 0xFF); |
| 222 | + output[offset + 2] = (byte) ((value >>> 16) & 0xFF); |
| 223 | + output[offset + 3] = (byte) ((value >>> 24) & 0xFF); |
| 224 | + } |
| 225 | + |
| 226 | + private static byte[] pad(byte[] input) { |
| 227 | + final int originalLength = input.length; |
| 228 | + final int numPaddingBytes = ( 56 - (originalLength + 1) % 64 + 64 ) % 64; |
| 229 | + |
| 230 | + final byte[] padded = new byte[originalLength + 1 + numPaddingBytes + 8]; |
| 231 | + System.arraycopy( input, 0, padded, 0, originalLength ); |
| 232 | + padded[originalLength] = (byte) 0x80; |
| 233 | + |
| 234 | + long bitLength = (long) originalLength * 8; |
| 235 | + for ( int i = 0; i < 8; i++ ) { |
| 236 | + padded[padded.length - 8 + i] = (byte) ( ( bitLength >>> (8 * i) ) & 0xFF ); |
149 | 237 | }
|
| 238 | + |
| 239 | + return padded; |
150 | 240 | }
|
151 | 241 | }
|
0 commit comments