44 package org.openslx.imagemaster.util;
46 import java.security.MessageDigest;
47 import java.security.NoSuchAlgorithmException;
89 static private final String
SALTCHARS =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
90 static private final String
itoa64 =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
95 private static final ThreadLocal<MessageDigest>
104 return new ThreadLocal<MessageDigest>() {
106 public MessageDigest initialValue()
109 return MessageDigest.getInstance(
"SHA-512" );
110 }
catch ( NoSuchAlgorithmException e ) {
137 public static final String
Sha512_crypt(String keyStr, String saltStr,
int roundsCount)
139 MessageDigest ctx =
sha1.get();
140 MessageDigest alt_ctx =
sha2.get();
144 byte[] p_bytes = null;
145 byte[] s_bytes = null;
148 StringBuilder buffer;
149 boolean include_round_count =
false;
155 if (saltStr.startsWith(sha512_salt_prefix))
157 saltStr = saltStr.substring(sha512_salt_prefix.length());
160 if (saltStr.startsWith(sha512_rounds_prefix))
162 String num = saltStr.substring(sha512_rounds_prefix.length(), saltStr.indexOf(
'$'));
163 int srounds = Integer.valueOf(num).intValue();
164 saltStr = saltStr.substring(saltStr.indexOf(
'$')+1);
165 rounds = Math.max(ROUNDS_MIN, Math.min(srounds, ROUNDS_MAX));
166 include_round_count =
true;
171 saltStr = saltStr.substring(0, SALT_LEN_MAX);
177 if (saltStr.endsWith(
"$"))
179 saltStr = saltStr.substring(0, saltStr.length() - 1);
183 if (saltStr.indexOf(
"$") != -1)
185 saltStr = saltStr.substring(0, saltStr.indexOf(
"$"));
191 StringBuilder saltBuf =
new StringBuilder();
193 while (saltBuf.length() < 16)
196 saltBuf.append(SALTCHARS.substring(index, index+1));
199 saltStr = saltBuf.toString();
202 if (roundsCount != 0)
204 rounds = Math.max(ROUNDS_MIN, Math.min(roundsCount, ROUNDS_MAX));
207 byte[] key = keyStr.getBytes();
208 byte[] salt = saltStr.getBytes();
211 ctx.update(key, 0, key.length);
212 ctx.update(salt, 0, salt.length);
215 alt_ctx.update(key, 0, key.length);
216 alt_ctx.update(salt, 0, salt.length);
217 alt_ctx.update(key, 0, key.length);
219 alt_result = alt_ctx.digest();
221 for (cnt = key.length; cnt > 64; cnt -= 64)
223 ctx.update(alt_result, 0, 64);
226 ctx.update(alt_result, 0, cnt);
228 for (cnt = key.length; cnt > 0; cnt >>= 1)
232 ctx.update(alt_result, 0, 64);
236 ctx.update(key, 0, key.length);
240 alt_result = ctx.digest();
244 for (cnt = 0; cnt < key.length; ++cnt)
246 alt_ctx.update(key, 0, key.length);
249 temp_result = alt_ctx.digest();
251 p_bytes =
new byte[key.length];
253 for (cnt2 = 0, cnt = p_bytes.length; cnt >= 64; cnt -= 64)
255 System.arraycopy(temp_result, 0, p_bytes, cnt2, 64);
259 System.arraycopy(temp_result, 0, p_bytes, cnt2, cnt);
263 for (cnt = 0; cnt < 16 + (alt_result[0]&0xFF); ++cnt)
265 alt_ctx.update(salt, 0, salt.length);
268 temp_result = alt_ctx.digest();
270 s_bytes =
new byte[salt.length];
272 for (cnt2 = 0, cnt = s_bytes.length; cnt >= 64; cnt -= 64)
274 System.arraycopy(temp_result, 0, s_bytes, cnt2, 64);
278 System.arraycopy(temp_result, 0, s_bytes, cnt2, cnt);
283 for (cnt = 0; cnt < rounds; ++cnt)
289 ctx.update(p_bytes, 0, key.length);
293 ctx.update (alt_result, 0, 64);
298 ctx.update(s_bytes, 0, salt.length);
303 ctx.update(p_bytes, 0, key.length);
308 ctx.update(alt_result, 0, 64);
312 ctx.update(p_bytes, 0, key.length);
315 alt_result = ctx.digest();
318 buffer =
new StringBuilder(sha512_salt_prefix);
320 if (include_round_count || rounds != ROUNDS_DEFAULT)
322 buffer.append(sha512_rounds_prefix);
323 buffer.append(rounds);
327 buffer.append(saltStr);
330 buffer.append(
b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4));
331 buffer.append(
b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4));
332 buffer.append(
b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4));
333 buffer.append(
b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4));
334 buffer.append(
b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4));
335 buffer.append(
b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4));
336 buffer.append(
b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4));
337 buffer.append(
b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4));
338 buffer.append(
b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4));
339 buffer.append(
b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4));
340 buffer.append(
b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4));
341 buffer.append(
b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4));
342 buffer.append(
b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4));
343 buffer.append(
b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4));
344 buffer.append(
b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4));
345 buffer.append(
b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4));
346 buffer.append(
b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4));
347 buffer.append(
b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4));
348 buffer.append(
b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4));
349 buffer.append(
b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4));
350 buffer.append(
b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4));
351 buffer.append(
b64_from_24bit ((byte)0x00, (byte)0x00, alt_result[63], 2));
359 return buffer.toString();
364 int v = ((((int) B2) & 0xFF) << 16) | ((((int) B1) & 0xFF) << 8) | ((int)B0 & 0xff);
366 StringBuilder result =
new StringBuilder();
370 result.append(itoa64.charAt((
int) (v & 0x3f)));
374 return result.toString();
387 static public final boolean verifyPassword(String plaintextPass, String sha512CryptText)
389 if (sha512CryptText.startsWith(
"$6$"))
391 return sha512CryptText.equals(
Sha512_crypt(plaintextPass, sha512CryptText, 0));
395 throw new RuntimeException(
"Bad sha512CryptText");
406 if (!sha512CryptText.startsWith(sha512_salt_prefix))
411 sha512CryptText = sha512CryptText.substring(sha512_salt_prefix.length());
413 if (sha512CryptText.startsWith(sha512_rounds_prefix))
415 String num = sha512CryptText.substring(sha512_rounds_prefix.length(), sha512CryptText.indexOf(
'$'));
419 Integer.valueOf(num).intValue();
421 catch (NumberFormatException ex)
426 sha512CryptText = sha512CryptText.substring(sha512CryptText.indexOf(
'$')+1);
429 if (sha512CryptText.indexOf(
'$') > (SALT_LEN_MAX + 1))
434 sha512CryptText = sha512CryptText.substring(sha512CryptText.indexOf(
'$') + 1);
436 for (
int i = 0; i < sha512CryptText.length(); i++)
438 if (itoa64.indexOf(sha512CryptText.charAt(i)) == -1)
456 "$6$saltstring",
"Hello world!",
"$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1",
457 "$6$xxxxxxxx",
"geheim",
"$6$xxxxxxxx$wuSdyeOvQXjj/nNoWnjjo.6OxUWrQFRIj019kh1cDpun6l6cpr3ywSrBprYRYZXcm4Kv9lboCEFI3GzBkdNAz/",
458 "$6$rounds=10000$saltstringsaltstring",
"Hello world!",
"$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v.",
459 "$6$rounds=5000$toolongsaltstring",
"This is just a test",
"$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQzQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0",
460 "$6$rounds=1400$anotherlongsaltstring",
"a very much longer text to encrypt. This one even stretches over morethan one line.",
"$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wPvMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1",
461 "$6$rounds=77777$short",
"we have a short salt string but not a short password",
"$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0gge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0",
462 "$6$rounds=123456$asaltof16chars..",
"a short string",
"$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1",
463 "$6$rounds=10$roundstoolow",
"the minimum number is still observed",
"$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1xhLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX.",
466 System.out.println(
"Starting Sha512Crypt tests now...");
468 for (
int t=0; t<(msgs.length/3); t++)
470 String plainText = msgs[t*3+1];
471 String cryptText = msgs[t*3+2];
475 System.out.println(
"test " + t +
" result is:" + result);
476 System.out.println(
"test " + t +
" should be:" + cryptText);
478 if (result.equals(cryptText))
480 System.out.println(
"Passed Crypt well");
484 System.out.println(
"Failed Crypt Badly");
485 throw new RuntimeException();
490 System.out.println(
"Passed verifyPassword well");
494 System.out.println(
"Failed verifyPassword Badly");
495 throw new RuntimeException();
static final int SALT_LEN_MAX
static final ThreadLocal< MessageDigest > sha1
Cache of sha512 digesters.
static final ThreadLocal< MessageDigest > sha2
static ThreadLocal< MessageDigest > newThreadLocalSha512()
For instantiating TL sha512, so we don't have to copy this stuff twice.
static final String SALTCHARS
static final boolean verifyPassword(String plaintextPass, String sha512CryptText)
static final int ROUNDS_DEFAULT
static final String itoa64
static final int ROUNDS_MAX
static final String b64_from_24bit(byte B2, byte B1, byte B0, int size)
static final int ROUNDS_MIN
static final String Sha512_crypt(String keyStr, String saltStr, int roundsCount)
static final String sha512_salt_prefix
static int randomInt(int n)
Return a random integer in the range of 0 (inclusive) and n (exclusive).
static final boolean verifyHashTextFormat(String sha512CryptText)
Some utilities to make our lives easier.
static final String sha512_rounds_prefix