Created
June 20, 2016 19:19
-
-
Save kevinmichaelchen/6e9528e61160baf4bd1d64f52d46d4a5 to your computer and use it in GitHub Desktop.
Generates PBKDF2 userPassword values for OpenDJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.apache.commons.codec.binary.Base64; | |
import org.apache.commons.io.output.ByteArrayOutputStream; | |
import org.junit.Test; | |
import javax.crypto.SecretKey; | |
import javax.crypto.SecretKeyFactory; | |
import javax.crypto.spec.PBEKeySpec; | |
import java.io.IOException; | |
import java.nio.charset.StandardCharsets; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.spec.InvalidKeySpecException; | |
/** | |
* @author Kevin Chen | |
*/ | |
public class OpenDJPBKDF2Test | |
{ | |
@Test | |
public void testOpenAMPassword() | |
{ | |
// OpenDJ PBKDF2 scheme uses a default salt length of 8 bytes | |
String saltString = "saltsalt"; | |
// OpenDJ PBKDF2 scheme uses a default hash length of 20 bytes | |
int hashLength = 20; | |
// OpenDJ PBKDF2 scheme uses a default iteration count of 10000 | |
int iterationCount = 10000; | |
String password = "mypassword"; | |
SecretKeyFactory secretKeyFactory; | |
try | |
{ | |
secretKeyFactory = SecretKeyFactory.getInstance( "PBKDF2WithHmacSHA1" ); | |
} | |
catch ( NoSuchAlgorithmException nsae ) | |
{ | |
throw new RuntimeException( nsae ); | |
} | |
byte[] value = password.getBytes( StandardCharsets.US_ASCII ); | |
byte[] salt = saltString.getBytes( StandardCharsets.US_ASCII ); | |
// Generate hash | |
byte[] actualHashBytes; | |
PBEKeySpec keySpecification = | |
new PBEKeySpec( | |
getCharacters( value ), | |
salt, | |
iterationCount, | |
hashLength * Byte.SIZE ); | |
try | |
{ | |
SecretKey secretKey = secretKeyFactory.generateSecret( keySpecification ); | |
actualHashBytes = secretKey.getEncoded(); | |
} | |
catch ( InvalidKeySpecException ikse ) | |
{ | |
throw new RuntimeException( ikse ); | |
} | |
// Base64(Hash||Salt) - the concatenation of hash bytes and salt bytes, then Base64-encoded | |
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | |
try | |
{ | |
byteArrayOutputStream.write( actualHashBytes ); | |
byteArrayOutputStream.write( salt ); | |
} | |
catch ( IOException e ) | |
{ | |
e.printStackTrace(); | |
} | |
byte[] hashSaltBytes = byteArrayOutputStream.toByteArray(); | |
byte[] base64HashSaltBytes = Base64.encodeBase64( hashSaltBytes ); | |
String base64HashSalt = new String( base64HashSaltBytes ); | |
System.out.println( "{PBKDF2}10000:" + base64HashSalt ); | |
} | |
private static char[] getCharacters( byte[] bytes ) | |
{ | |
// Unfortunately, as is the case with so many old cryptographic libraries, the PBEKeySpec | |
// class only accepts char[] input, despite practically all hashing algorithms being | |
// byte-based, including ours (PBKDF2). Fortunately, it seems the KeyFactory class does not | |
// emit upper bytes that are zero; that is, even though a Java character is two bytes, the | |
// value 5 is considered [ 0x05 ], not [ 0x00, 0x05 ], etc. I'm guessing that if you input | |
// a character with value 261, it'd yield [ 0x01, 0x05 ], not just [ 0x05 ]. Anyway, it | |
// doesn't matter, since we know the value range for one byte is necessarily from 0x00 to | |
// 0xFF. So we'll go ahead here and simply convert the byte array into a character array. | |
char[] characters; | |
characters = new char[bytes.length]; | |
for ( int i = 0; i < bytes.length; i++ ) | |
{ | |
characters[i] = (char) bytes[i]; | |
} | |
return characters; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment