package com.kryptnostic.kodex.v1.authentication; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import org.apache.commons.codec.binary.Hex; import com.google.common.base.Preconditions; import com.kryptnostic.kodex.v1.crypto.ciphers.BlockCiphertext; import com.kryptnostic.kodex.v1.crypto.ciphers.CryptoService; import com.kryptnostic.kodex.v1.crypto.ciphers.Cyphers; import com.kryptnostic.kodex.v1.crypto.ciphers.PasswordCryptoService; import com.kryptnostic.kodex.v1.crypto.keys.SecretKeyFactoryType; import com.kryptnostic.kodex.v1.exceptions.types.SecurityConfigurationException; public final class CredentialFactory { private static final int ITERATIONS = 1000; private static final int KEY_SIZE = 256; private CredentialFactory() {} public static CredentialPair generateCredentialPair( String password ) throws InvalidKeySpecException, NoSuchAlgorithmException, SecurityConfigurationException { byte[] salt = Cyphers.generateSalt( KEY_SIZE / 8 ); return new CredentialPair( deriveCredential( password, salt ), encryptSalt( password, salt ) ); } public static String deriveCredential( String password, BlockCiphertext encryptedSalt ) throws InvalidKeySpecException, NoSuchAlgorithmException, SecurityConfigurationException { return deriveCredential( password, decryptSalt( password, encryptedSalt ) ); } private static String deriveCredential( String password, byte[] salt ) throws InvalidKeySpecException, NoSuchAlgorithmException { SecretKeyFactory skf = SecretKeyFactoryType.PBKDF2WithHmacSHA1.getInstance(); PBEKeySpec spec = new PBEKeySpec( Preconditions.checkNotNull( password, "Password cannot be null" ) .toCharArray(), Preconditions.checkNotNull( salt, "Salt cannot be null" ), ITERATIONS, KEY_SIZE ); SecretKey key = skf.generateSecret( spec ); return new String( Hex.encodeHex( key.getEncoded() ) ); } private static BlockCiphertext encryptSalt( String password, byte[] salt ) throws SecurityConfigurationException { CryptoService cryptoService = new PasswordCryptoService( Preconditions.checkNotNull( password, "Password cannot be null" ) ); return cryptoService.encrypt( Preconditions.checkNotNull( salt, "Encrypted salt cannot be null" ) ); } private static byte[] decryptSalt( String password, BlockCiphertext encryptedSalt ) throws SecurityConfigurationException { CryptoService cryptoService = new PasswordCryptoService( Preconditions.checkNotNull( password, "Password cannot be null" ) ); return cryptoService .decryptBytes( Preconditions.checkNotNull( encryptedSalt, "Encrypted salt cannot be null" ) ); } public static final class CredentialPair { private final String credential; private final BlockCiphertext encryptedSalt; public CredentialPair( String credential, BlockCiphertext encryptedSalt ) { this.credential = credential; this.encryptedSalt = encryptedSalt; } public String getCredential() { return credential; } public BlockCiphertext getEncryptedSalt() { return encryptedSalt; } } }