diff options
| author | Simon Rettberg | 2014-03-28 17:51:24 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2014-03-28 17:51:24 +0100 |
| commit | fbbfee0a32ce83f5bfe36d78eddafed7226a041c (patch) | |
| tree | f3d44e8b9e2bca678165cb3dd97ec1aa1380f244 /src/main/java/org/openslx/imagemaster/util | |
| download | masterserver-fbbfee0a32ce83f5bfe36d78eddafed7226a041c.tar.gz masterserver-fbbfee0a32ce83f5bfe36d78eddafed7226a041c.tar.xz masterserver-fbbfee0a32ce83f5bfe36d78eddafed7226a041c.zip | |
Initial Commit
Diffstat (limited to 'src/main/java/org/openslx/imagemaster/util')
3 files changed, 608 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/imagemaster/util/Hash.java b/src/main/java/org/openslx/imagemaster/util/Hash.java new file mode 100644 index 0000000..8ac0e5f --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/util/Hash.java @@ -0,0 +1,100 @@ +package org.openslx.imagemaster.util; + +import java.security.MessageDigest; +import java.nio.charset.Charset; +import java.security.NoSuchAlgorithmException; + +public class Hash +{ + // Cache of md5 digesters + private static final ThreadLocal<MessageDigest> md5hash = new ThreadLocal<MessageDigest>() { + @Override + public MessageDigest initialValue() + { + try { + return MessageDigest.getInstance( "MD5" ); + } catch ( NoSuchAlgorithmException e ) { + e.printStackTrace(); + System.exit(1); + return null; + } + } + }; + // Cache of sha256 digesters + private static final ThreadLocal<MessageDigest> sha256hash = new ThreadLocal<MessageDigest>() { + @Override + public MessageDigest initialValue() + { + try { + return MessageDigest.getInstance( "SHA-256" ); + } catch ( NoSuchAlgorithmException e ) { + e.printStackTrace(); + System.exit(1); + return null; + } + } + }; + // Cache of sha512 digesters + private static final ThreadLocal<MessageDigest> sha512hash = new ThreadLocal<MessageDigest>() { + @Override + public MessageDigest initialValue() + { + try { + return MessageDigest.getInstance( "SHA-512" ); + } catch ( NoSuchAlgorithmException e ) { + e.printStackTrace(); + System.exit(1); + return null; + } + } + }; + // For converting to hex string + private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + // Constant + private static final Charset UTF8 = Charset.forName( "UTF-8" ); + + // MD5 + + public static String md5( final byte[] bytes ) + { + return toHexString( md5hash.get().digest( bytes ) ); + } + + public static String md5( final String text ) + { + return md5( text.getBytes( UTF8 )); + } + + // SHA-256 + + public static String sha256( final byte[] bytes ) + { + return toHexString( sha256hash.get().digest( bytes ) ); + } + + public static String sha256( final String text ) + { + return sha256( text.getBytes( UTF8 )); + } + + // SHA-512 + + public static MessageDigest getSha512Digest() + { + return sha512hash.get(); + } + + // Helper + + private static String toHexString( final byte[] bytes ) + { + final char[] hexChars = new char[bytes.length * 2]; + for ( int j = 0; j < bytes.length; ++j ) { + final int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_CHARS[v >>> 4]; + hexChars[j * 2 + 1] = HEX_CHARS[v & 0x0F]; + } + return new String( hexChars ); + } + +} diff --git a/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java new file mode 100644 index 0000000..472ead9 --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java @@ -0,0 +1,492 @@ +/* + Sha512Crypt.java + + Created: 18 December 2007 + + Java Port By: James Ratcliff, falazar@arlut.utexas.edu + + This class implements the new generation, scalable, SHA512-based + Unix 'crypt' algorithm developed by a group of engineers from Red + Hat, Sun, IBM, and HP for common use in the Unix and Linux + /etc/shadow files. + + The Linux glibc library (starting at version 2.7) includes support + for validating passwords hashed using this algorithm. + + The algorithm itself was released into the Public Domain by Ulrich + Drepper <drepper@redhat.com>. A discussion of the rationale and + development of this algorithm is at + + http://people.redhat.com/drepper/sha-crypt.html + + and the specification and a sample C language implementation is at + + http://people.redhat.com/drepper/SHA-crypt.txt + + This Java Port is + + Copyright (c) 2008-2013 The University of Texas at Austin. + + All rights reserved. + + Redistribution and use in source and binary form are permitted + provided that distributions retain this entire copyright notice + and comment. Neither the name of the University nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. + +*/ + +package org.openslx.imagemaster.util; + +import java.security.MessageDigest; + +/*------------------------------------------------------------------------------ + class + Sha512Crypt + +------------------------------------------------------------------------------*/ + +/** + * <p>This class defines a method, {@link + * Sha512Crypt#Sha512_crypt(java.lang.String, java.lang.String, int) + * Sha512_crypt()}, which takes a password and a salt string and + * generates a Sha512 encrypted password entry.</p> + * + * <p>This class implements the new generation, scalable, SHA512-based + * Unix 'crypt' algorithm developed by a group of engineers from Red + * Hat, Sun, IBM, and HP for common use in the Unix and Linux + * /etc/shadow files.</p> + * + * <p>The Linux glibc library (starting at version 2.7) includes + * support for validating passwords hashed using this algorithm.</p> + * + * <p>The algorithm itself was released into the Public Domain by + * Ulrich Drepper <drepper@redhat.com>. A discussion of the + * rationale and development of this algorithm is at</p> + * + * <p>http://people.redhat.com/drepper/sha-crypt.html</p> + * + * <p>and the specification and a sample C language implementation is + * at</p> + * + * <p>http://people.redhat.com/drepper/SHA-crypt.txt</p> + */ + +public final class Sha512Crypt +{ + static private final String sha512_salt_prefix = "$6$"; + static private final String sha512_rounds_prefix = "rounds="; + static private final int SALT_LEN_MAX = 16; + static private final int ROUNDS_DEFAULT = 5000; + static private final int ROUNDS_MIN = 1000; + static private final int ROUNDS_MAX = 999999999; + static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + static private MessageDigest getSHA512() + { + try + { + return MessageDigest.getInstance("SHA-512"); + } + catch (java.security.NoSuchAlgorithmException ex) + { + throw new RuntimeException(ex); + } + } + + /** + * <p>This method actually generates an Sha512 crypted password hash + * from a plaintext password and a salt.</p> + * + * <p>The resulting string will be in the form + * '$6$<rounds=n>$<salt>$<hashed mess></p> + * + * @param keyStr Plaintext password + * + * @param saltStr An encoded salt/roundes which will be consulted to determine the salt + * and round count, if not null + * + * @param roundsCount If this value is not 0, this many rounds will + * used to generate the hash text. + * + * @return The Sha512 Unix Crypt hash text for the keyStr + */ + + public static final String Sha512_crypt(String keyStr, String saltStr, int roundsCount) + { + MessageDigest ctx = getSHA512(); + MessageDigest alt_ctx = getSHA512(); + + byte[] alt_result; + byte[] temp_result; + byte[] p_bytes = null; + byte[] s_bytes = null; + int cnt, cnt2; + int rounds = ROUNDS_DEFAULT; // Default number of rounds. + StringBuilder buffer; + boolean include_round_count = false; + + /* -- */ + + if (saltStr != null) + { + if (saltStr.startsWith(sha512_salt_prefix)) + { + saltStr = saltStr.substring(sha512_salt_prefix.length()); + } + + if (saltStr.startsWith(sha512_rounds_prefix)) + { + String num = saltStr.substring(sha512_rounds_prefix.length(), saltStr.indexOf('$')); + int srounds = Integer.valueOf(num).intValue(); + saltStr = saltStr.substring(saltStr.indexOf('$')+1); + rounds = Math.max(ROUNDS_MIN, Math.min(srounds, ROUNDS_MAX)); + include_round_count = true; + } + + if (saltStr.length() > SALT_LEN_MAX) + { + saltStr = saltStr.substring(0, SALT_LEN_MAX); + } + + // gnu libc's crypt(3) implementation allows the salt to end + // in $ which is then ignored. + + if (saltStr.endsWith("$")) + { + saltStr = saltStr.substring(0, saltStr.length() - 1); + } + else + { + if (saltStr.indexOf("$") != -1) + { + saltStr = saltStr.substring(0, saltStr.indexOf("$")); + } + } + } + else + { + java.util.Random randgen = new java.util.Random(); + StringBuilder saltBuf = new StringBuilder(); + + while (saltBuf.length() < 16) + { + int index = (int) (randgen.nextFloat() * SALTCHARS.length()); + saltBuf.append(SALTCHARS.substring(index, index+1)); + } + + saltStr = saltBuf.toString(); + } + + if (roundsCount != 0) + { + rounds = Math.max(ROUNDS_MIN, Math.min(roundsCount, ROUNDS_MAX)); + } + + byte[] key = keyStr.getBytes(); + byte[] salt = saltStr.getBytes(); + + ctx.reset(); + ctx.update(key, 0, key.length); + ctx.update(salt, 0, salt.length); + + alt_ctx.reset(); + alt_ctx.update(key, 0, key.length); + alt_ctx.update(salt, 0, salt.length); + alt_ctx.update(key, 0, key.length); + + alt_result = alt_ctx.digest(); + + for (cnt = key.length; cnt > 64; cnt -= 64) + { + ctx.update(alt_result, 0, 64); + } + + ctx.update(alt_result, 0, cnt); + + for (cnt = key.length; cnt > 0; cnt >>= 1) + { + if ((cnt & 1) != 0) + { + ctx.update(alt_result, 0, 64); + } + else + { + ctx.update(key, 0, key.length); + } + } + + alt_result = ctx.digest(); + + alt_ctx.reset(); + + for (cnt = 0; cnt < key.length; ++cnt) + { + alt_ctx.update(key, 0, key.length); + } + + temp_result = alt_ctx.digest(); + + p_bytes = new byte[key.length]; + + for (cnt2 = 0, cnt = p_bytes.length; cnt >= 64; cnt -= 64) + { + System.arraycopy(temp_result, 0, p_bytes, cnt2, 64); + cnt2 += 64; + } + + System.arraycopy(temp_result, 0, p_bytes, cnt2, cnt); + + alt_ctx.reset(); + + for (cnt = 0; cnt < 16 + (alt_result[0]&0xFF); ++cnt) + { + alt_ctx.update(salt, 0, salt.length); + } + + temp_result = alt_ctx.digest(); + + s_bytes = new byte[salt.length]; + + for (cnt2 = 0, cnt = s_bytes.length; cnt >= 64; cnt -= 64) + { + System.arraycopy(temp_result, 0, s_bytes, cnt2, 64); + cnt2 += 64; + } + + System.arraycopy(temp_result, 0, s_bytes, cnt2, cnt); + + /* Repeatedly run the collected hash value through SHA512 to burn + CPU cycles. */ + + for (cnt = 0; cnt < rounds; ++cnt) + { + ctx.reset(); + + if ((cnt & 1) != 0) + { + ctx.update(p_bytes, 0, key.length); + } + else + { + ctx.update (alt_result, 0, 64); + } + + if (cnt % 3 != 0) + { + ctx.update(s_bytes, 0, salt.length); + } + + if (cnt % 7 != 0) + { + ctx.update(p_bytes, 0, key.length); + } + + if ((cnt & 1) != 0) + { + ctx.update(alt_result, 0, 64); + } + else + { + ctx.update(p_bytes, 0, key.length); + } + + alt_result = ctx.digest(); + } + + buffer = new StringBuilder(sha512_salt_prefix); + + if (include_round_count || rounds != ROUNDS_DEFAULT) + { + buffer.append(sha512_rounds_prefix); + buffer.append(rounds); + buffer.append("$"); + } + + buffer.append(saltStr); + buffer.append("$"); + + buffer.append(b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4)); + buffer.append(b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4)); + buffer.append(b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4)); + buffer.append(b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4)); + buffer.append(b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4)); + buffer.append(b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4)); + buffer.append(b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4)); + buffer.append(b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4)); + buffer.append(b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4)); + buffer.append(b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4)); + buffer.append(b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4)); + buffer.append(b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4)); + buffer.append(b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4)); + buffer.append(b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4)); + buffer.append(b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4)); + buffer.append(b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4)); + buffer.append(b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4)); + buffer.append(b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4)); + buffer.append(b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4)); + buffer.append(b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4)); + buffer.append(b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4)); + buffer.append(b64_from_24bit ((byte)0x00, (byte)0x00, alt_result[63], 2)); + + /* Clear the buffer for the intermediate result so that people + attaching to processes or reading core dumps cannot get any + information. */ + + ctx.reset(); + + return buffer.toString(); + } + + private static final String b64_from_24bit(byte B2, byte B1, byte B0, int size) + { + int v = ((((int) B2) & 0xFF) << 16) | ((((int) B1) & 0xFF) << 8) | ((int)B0 & 0xff); + + StringBuilder result = new StringBuilder(); + + while (--size >= 0) + { + result.append(itoa64.charAt((int) (v & 0x3f))); + v >>>= 6; + } + + return result.toString(); + } + + /** + * <p>This method tests a plaintext password against a SHA512 Unix + * Crypt'ed hash and returns true if the password matches the + * hash.</p> + * + * @param plaintextPass The plaintext password text to test. + * @param sha512CryptText The hash text we're testing against. + * We'll extract the salt and the round count from this String. + */ + + static public final boolean verifyPassword(String plaintextPass, String sha512CryptText) + { + if (sha512CryptText.startsWith("$6$")) + { + return sha512CryptText.equals(Sha512_crypt(plaintextPass, sha512CryptText, 0)); + } + else + { + throw new RuntimeException("Bad sha512CryptText"); + } + } + + /** + * <p>Returns true if sha512CryptText is a valid Sha512Crypt hashtext, + * false if not.</p> + */ + + public static final boolean verifyHashTextFormat(String sha512CryptText) + { + if (!sha512CryptText.startsWith(sha512_salt_prefix)) + { + return false; + } + + sha512CryptText = sha512CryptText.substring(sha512_salt_prefix.length()); + + if (sha512CryptText.startsWith(sha512_rounds_prefix)) + { + String num = sha512CryptText.substring(sha512_rounds_prefix.length(), sha512CryptText.indexOf('$')); + + try + { + int srounds = Integer.valueOf(num).intValue(); + } + catch (NumberFormatException ex) + { + return false; + } + + sha512CryptText = sha512CryptText.substring(sha512CryptText.indexOf('$')+1); + } + + if (sha512CryptText.indexOf('$') > (SALT_LEN_MAX + 1)) + { + return false; + } + + sha512CryptText = sha512CryptText.substring(sha512CryptText.indexOf('$') + 1); + + for (int i = 0; i < sha512CryptText.length(); i++) + { + if (itoa64.indexOf(sha512CryptText.charAt(i)) == -1) + { + return false; + } + } + + return true; + } + + /** + * <p>Validate our implementation using test data from Ulrich + * Drepper's C implementation.</p> + */ + + private static void selfTest() + { + String msgs[] = + { + "$6$saltstring", "Hello world!", "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1", + "$6$xxxxxxxx", "geheim", "$6$xxxxxxxx$wuSdyeOvQXjj/nNoWnjjo.6OxUWrQFRIj019kh1cDpun6l6cpr3ywSrBprYRYZXcm4Kv9lboCEFI3GzBkdNAz/", + "$6$rounds=10000$saltstringsaltstring", "Hello world!", "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v.", + "$6$rounds=5000$toolongsaltstring", "This is just a test", "$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQzQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0", + "$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", + "$6$rounds=77777$short", "we have a short salt string but not a short password", "$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0gge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0", + "$6$rounds=123456$asaltof16chars..", "a short string", "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1", + "$6$rounds=10$roundstoolow", "the minimum number is still observed", "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1xhLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX.", + }; + + System.out.println("Starting Sha512Crypt tests now..."); + + for (int t=0; t<(msgs.length/3); t++) + { + String saltPrefix = msgs[t*3]; + String plainText = msgs[t*3+1]; + String cryptText = msgs[t*3+2]; + + String result = Sha512_crypt(plainText, cryptText, 0); + + System.out.println("test " + t + " result is:" + result); + System.out.println("test " + t + " should be:" + cryptText); + + if (result.equals(cryptText)) + { + System.out.println("Passed Crypt well"); + } + else + { + System.out.println("Failed Crypt Badly"); + } + + if (verifyPassword(plainText, cryptText)) + { + System.out.println("Passed verifyPassword well"); + } + else + { + System.out.println("Failed verifyPassword Badly"); + } + } + } + + /** + * Test rig + */ + + public static void main(String arg[]) + { + selfTest(); + } +} diff --git a/src/main/java/org/openslx/imagemaster/util/Util.java b/src/main/java/org/openslx/imagemaster/util/Util.java new file mode 100644 index 0000000..ff0b8c1 --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/util/Util.java @@ -0,0 +1,16 @@ +package org.openslx.imagemaster.util; + +public class Util +{ + + public static void notNullFatal( Object something, String message ) + { + if ( something == null ) { + if ( message != null ) + System.out.println( "[NOTNULL] " + message ); + System.out.println( Thread.currentThread().getStackTrace().toString() ); + System.exit( 2 ); + } + } + +} |
