/** * See {@link #apr1Crypt(String, String)} for details. * * @param keyBytes * plaintext string to hash. * @return the hash value * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. */ public static String apr1Crypt(final String keyBytes) { return apr1Crypt(keyBytes.getBytes(Charsets.UTF_8)); }
/** * Generates a libc crypt() compatible "$1$" MD5 based hash value. * <p> * See {@link Crypt#crypt(String, String)} for details. * * @param keyBytes * plaintext string to hash. * @param salt * salt string including the prefix and optionally garbage at the end. Will be generated randomly if * null. * @return the hash value * @throws IllegalArgumentException * if the salt does not match the allowed pattern * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. */ public static String md5Crypt(final byte[] keyBytes, final String salt) { return md5Crypt(keyBytes, salt, MD5_PREFIX); }
@Test public void testCtor() { assertNotNull(new Md5Crypt()); // for code-coverage }
public PasswordEncrypt(final String key) { final byte[] keyBytes = key.getBytes(US_ASCII); this.md5 = Md5Crypt.md5Crypt(keyBytes.clone()); this.apr1 = Md5Crypt.apr1Crypt(keyBytes.clone()); this.sha256 = Sha2Crypt.sha256Crypt(keyBytes.clone()); this.sha512 = Sha2Crypt.sha512Crypt(keyBytes.clone()); Arrays.fill(keyBytes, (byte) 0); }
public static boolean checkPassword(final String crypted, final String key) { String crypted2 = null; if (crypted == null) return false; if (crypted.length() < 24) return false; if (crypted.charAt(0) != '$') return false; final int offset2ndDolar = crypted.indexOf('$', 1); if (offset2ndDolar < 0) return false; final int offset3ndDolar = crypted.indexOf('$', offset2ndDolar + 1); if (offset3ndDolar < 0) return false; final String salt = crypted.substring(0, offset3ndDolar + 1); final byte[] keyBytes = key.getBytes(US_ASCII); if (crypted.startsWith("$1$")) { // MD5 crypted2 = Md5Crypt.md5Crypt(keyBytes.clone(), salt); } else if (crypted.startsWith("$apr1$")) { // APR1 crypted2 = Md5Crypt.apr1Crypt(keyBytes.clone(), salt); } else if (crypted.startsWith("$5$")) { // SHA2-256 crypted2 = Sha2Crypt.sha256Crypt(keyBytes.clone(), salt); } else if (crypted.startsWith("$6$")) { // SHA2-512 crypted2 = Sha2Crypt.sha512Crypt(keyBytes.clone(), salt); } Arrays.fill(keyBytes, (byte) 0); if (crypted2 == null) return false; return crypted.equals(crypted2); }
/** * Generates an Apache htpasswd compatible "$apr1$" MD5 based hash value. * <p> * The algorithm is identical to the crypt(3) "$1$" one but produces different outputs due to the different salt * prefix. * * @param keyBytes * plaintext string to hash. * @param salt * salt string including the prefix and optionally garbage at the end. Will be generated randomly if * null. * @return the hash value * @throws IllegalArgumentException * if the salt does not match the allowed pattern * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. */ public static String apr1Crypt(final String keyBytes, final String salt) { return apr1Crypt(keyBytes.getBytes(Charsets.UTF_8), salt); }
/** * See {@link #apr1Crypt(String, String)} for details. * * @param keyBytes * plaintext string to hash. * @param salt An APR1 salt. * @return the hash value * @throws IllegalArgumentException * if the salt does not match the allowed pattern * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. */ public static String apr1Crypt(final byte[] keyBytes, String salt) { // to make the md5Crypt regex happy if (salt != null && !salt.startsWith(APR1_PREFIX)) { salt = APR1_PREFIX + salt; } return Md5Crypt.md5Crypt(keyBytes, salt, APR1_PREFIX); }
/** * See {@link #apr1Crypt(String, String)} for details. * * @param keyBytes * plaintext string to hash. * @return the hash value * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. * */ public static String apr1Crypt(final byte[] keyBytes) { return apr1Crypt(keyBytes, APR1_PREFIX + B64.getRandomSalt(8)); }
/** * Generates a libc6 crypt() compatible "$1$" hash value. * <p> * See {@link Crypt#crypt(String, String)} for details. * * @param keyBytes * plaintext string to hash. * @return the hash value * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. */ public static String md5Crypt(final byte[] keyBytes) { return md5Crypt(keyBytes, MD5_PREFIX + B64.getRandomSalt(8)); }
@Test(expected = IllegalArgumentException.class) public void testApr1CryptWithInvalidSalt() { Md5Crypt.apr1Crypt(new byte[0], "!"); } }
@Test(expected = IllegalArgumentException.class) public void testMd5CryptWithEmptySalt() { Md5Crypt.md5Crypt("secret".getBytes(), ""); } }
@Test(expected = NullPointerException.class) public void testApr1CryptNullData() { Md5Crypt.apr1Crypt((byte[]) null); }
@Test(expected = NullPointerException.class) public void testMd5CryptNullData() { Md5Crypt.md5Crypt((byte[]) null); }
@Test public void testApr1CryptBytes() { // random salt final byte[] keyBytes = new byte[] { '!', 'b', 'c', '.' }; final String hash = Md5Crypt.apr1Crypt(keyBytes); assertEquals(hash, Md5Crypt.apr1Crypt("!bc.", hash)); // An empty Bytearray equals an empty String assertEquals("$apr1$foo$P27KyD1htb4EllIPEYhqi0", Md5Crypt.apr1Crypt(new byte[0], "$apr1$foo")); // UTF-8 stores \u00e4 "a with diaeresis" as two bytes 0xc3 0xa4. assertEquals("$apr1$./$EeFrYzWWbmTyGdf4xULYc.", Md5Crypt.apr1Crypt("t\u00e4st", "$apr1$./$")); // ISO-8859-1 stores "a with diaeresis" as single byte 0xe4. assertEquals("$apr1$./$kCwT1pY9qXAJElYG9q1QE1", Md5Crypt.apr1Crypt("t\u00e4st".getBytes(Charsets.ISO_8859_1), "$apr1$./$")); }
@Test(expected = IllegalArgumentException.class) public void testApr1CryptWithEmptySalt() { Md5Crypt.apr1Crypt("secret".getBytes(), ""); }
/** * Encrypts a password in a crypt(3) compatible way. * <p> * If no salt is provided, a random salt and the default algorithm (currently SHA-512) will be used. See * {@link #crypt(String, String)} for details. * * @param keyBytes * plaintext password * @param salt * salt value * @return hash value * @throws IllegalArgumentException * if the salt does not match the allowed pattern * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. */ public static String crypt(final byte[] keyBytes, final String salt) { if (salt == null) { return Sha2Crypt.sha512Crypt(keyBytes); } else if (salt.startsWith(Sha2Crypt.SHA512_PREFIX)) { return Sha2Crypt.sha512Crypt(keyBytes, salt); } else if (salt.startsWith(Sha2Crypt.SHA256_PREFIX)) { return Sha2Crypt.sha256Crypt(keyBytes, salt); } else if (salt.startsWith(Md5Crypt.MD5_PREFIX)) { return Md5Crypt.md5Crypt(keyBytes, salt); } else { return UnixCrypt.crypt(keyBytes, salt); } }
@Test public void testApr1CryptExplicitCall() { // When explicitly called the prefix is optional assertEquals("$apr1$1234$mAlH7FRST6FiRZ.kcYL.j1", Md5Crypt.apr1Crypt("secret", "1234")); // When explicitly called without salt, a random one will be used. assertTrue(Md5Crypt.apr1Crypt("secret".getBytes()).matches("^\\$apr1\\$[a-zA-Z0-9./]{0,8}\\$.{1,}$")); assertTrue(Md5Crypt.apr1Crypt("secret".getBytes(), null).matches("^\\$apr1\\$[a-zA-Z0-9./]{0,8}\\$.{1,}$")); }
/** * Generates a libc crypt() compatible "$1$" MD5 based hash value. * <p> * See {@link Crypt#crypt(String, String)} for details. * * @param keyBytes * plaintext string to hash. * @param salt * salt string including the prefix and optionally garbage at the end. Will be generated randomly if * null. * @return the hash value * @throws IllegalArgumentException * if the salt does not match the allowed pattern * @throws RuntimeException * when a {@link java.security.NoSuchAlgorithmException} is caught. */ public static String md5Crypt(final byte[] keyBytes, final String salt) { return md5Crypt(keyBytes, salt, MD5_PREFIX); }
@Test public void testApr1CryptStrings() { // A random example using htpasswd assertEquals("$apr1$TqI9WECO$LHZB2DqRlk9nObiB6vJG9.", Md5Crypt.apr1Crypt("secret", "$apr1$TqI9WECO")); // empty data assertEquals("$apr1$foo$P27KyD1htb4EllIPEYhqi0", Md5Crypt.apr1Crypt("", "$apr1$foo")); // salt gets cut at dollar sign assertEquals("$apr1$1234$mAlH7FRST6FiRZ.kcYL.j1", Md5Crypt.apr1Crypt("secret", "$apr1$1234")); assertEquals("$apr1$1234$mAlH7FRST6FiRZ.kcYL.j1", Md5Crypt.apr1Crypt("secret", "$apr1$1234$567")); assertEquals("$apr1$1234$mAlH7FRST6FiRZ.kcYL.j1", Md5Crypt.apr1Crypt("secret", "$apr1$1234$567$890")); // salt gets cut at maximum length assertEquals("$apr1$12345678$0lqb/6VUFP8JY/s/jTrIk0", Md5Crypt.apr1Crypt("secret", "$apr1$1234567890123456")); assertEquals("$apr1$12345678$0lqb/6VUFP8JY/s/jTrIk0", Md5Crypt.apr1Crypt("secret", "$apr1$123456789012345678")); }