package org.ovirt.engine.core.uutils.crypto;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* glibc's crypt using md5.
* openssl passwd -1
* until a newer commons-codec is bundled with jboss.
*/
public class CryptMD5 {
private static final char[] b64t = {
'.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};
private static final String SALT_PREFIX = "$1$";
private static final String SALT_DELIMITER = "$";
private static final int SALT_MAX_LENGTH = 8;
protected static String encode(byte b1, byte b2, byte b3, int n) {
StringBuilder result = new StringBuilder();
int w = ((b1&0xff) << 16) | ((b2&0xff) << 8) | (b3&0xff);
while (n-- > 0) {
result.append(b64t[w&0x3f]);
w = w >> 6;
}
return result.toString();
}
public static String crypt(String password, String salt) {
try {
byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
byte[] saltBytes = salt.getBytes(StandardCharsets.UTF_8);
byte[] altresult;
if (salt.length() > SALT_MAX_LENGTH) {
salt = salt.substring(0, SALT_MAX_LENGTH);
}
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(passwordBytes);
digest.update(SALT_PREFIX.getBytes(StandardCharsets.UTF_8));
digest.update(saltBytes);
MessageDigest altdigest = MessageDigest.getInstance("MD5");
altdigest.update(passwordBytes);
altdigest.update(saltBytes);
altdigest.update(passwordBytes);
altresult = altdigest.digest();
int j = passwordBytes.length;
while (j > 16) {
digest.update(altresult, 0, 16);
j -= 16;
}
digest.update(altresult, 0, j);
altresult[0] = '\000';
for (int i = passwordBytes.length; i > 0; i >>= 1) {
digest.update((i & 1) != 0 ? altresult : passwordBytes, 0, 1);
}
altresult = digest.digest();
for (int i = 0; i < 1000; ++i) {
digest.reset();
if ((i & 1) != 0) {
digest.update(passwordBytes);
} else {
digest.update(altresult, 0, 16);
}
if ((i % 3) != 0) {
digest.update(saltBytes);
}
if ((i % 7) != 0) {
digest.update(passwordBytes);
}
if ((i & 1) != 0) {
digest.update(altresult, 0, 16);
} else {
digest.update(passwordBytes);
}
altresult = digest.digest();
}
StringBuilder builder = new StringBuilder().append(
SALT_PREFIX
).append(
salt
).append(
SALT_DELIMITER
).append(
encode(altresult[0], altresult[6], altresult[12], 4)
).append(
encode(altresult[1], altresult[7], altresult[13], 4)
).append(
encode(altresult[2], altresult[8], altresult[14], 4)
).append(
encode(altresult[3], altresult[9], altresult[15], 4)
).append(
encode(altresult[4], altresult[10], altresult[5], 4)
).append(
encode((byte) 0, (byte) 0, altresult[11], 2)
);
return builder.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static String crypt(String password) {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
byte[] r = new byte[SALT_MAX_LENGTH];
char[] salt = new char[r.length];
random.nextBytes(r);
for (int i=0;i<r.length;i++) {
salt[i] = b64t[(r[i]&0xff) % b64t.length];
}
return crypt(password, new String(salt));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}