2011-12-29 4 views
36

나는 데이터베이스에 비밀번호를 안전하게 저장하려고하는데 PBKDF2 기능을 사용하여 생성 된 해시를 저장하기로 결정했습니다. 나는 bouncy 성 라이브러리를 사용하여 이것을하고 싶지만 왜 JCE 인터페이스를 사용하여 작동시킬 수 있는지 알지 못한다 ... 문제는 3 가지 모드로 해시를 생성하는 것이다.
1. 사용 PBKDF2WithHmacSHA1 직접
3. 2 개 고유 값 결과 JCE
통해 탄력 성을 이용하여 탄력 성 API를 사용하는 일
(2)에 의해 제공되는 비밀 키 공장 : 하나의 공통 제와 두 번째 하나에있다. 내가 PBKDF2 내가 내가 탄력이 성 자바 문서에서 마지막으로 촬영 한 방법 "PBEWITHHMACSHA1"의 알고리즘으로 선택한 이유 때문에 즉 HMAC SHA1을 사용하여 구현되는 것을 알고Java에서 bouncycastle을 사용하는 PBKDF2

//Mode 1 

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 
    KeySpec keyspec = new PBEKeySpec("password".toCharArray(), salt, 1000, 128); 
    Key key = factory.generateSecret(keyspec); 
    System.out.println(key.getClass().getName()); 
    System.out.println(Arrays.toString(key.getEncoded())); 

    //Mode 2 

    PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(); 
    generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(("password").toCharArray()), salt, 1000); 
    KeyParameter params = (KeyParameter)generator.generateDerivedParameters(128); 
    System.out.println(Arrays.toString(params.getKey())); 

    //Mode 3 

    SecretKeyFactory factorybc = SecretKeyFactory.getInstance("PBEWITHHMACSHA1", "BC"); 
    KeySpec keyspecbc = new PBEKeySpec("password".toCharArray(), salt, 1000, 128); 
    Key keybc = factorybc.generateSecret(keyspecbc); 
    System.out.println(keybc.getClass().getName()); 
    System.out.println(Arrays.toString(keybc.getEncoded())); 
    System.out.println(keybc.getAlgorithm()); 

: 여기

내 코드입니다 .

com.sun.crypto.provider.SunJCE_ae 
[-53, 29, 113, -110, -25, 76, 115, -127, -64, 74, -63, 102, 75, 81, -21, 74] 
[-53, 29, 113, -110, -25, 76, 115, -127, -64, 74, -63, 102, 75, 81, -21, 74] 
org.bouncycastle.jce.provider.JCEPBEKey 
[14, -47, -87, -16, -117, -31, 91, -121, 90, -68, -82, -31, -27, 5, -93, -67, 30, -34, -64, -40] 
PBEwithHmacSHA 

어떤 아이디어 :

출력은 다음과 같다?

+0

에 호환 내가 KDF 출력을 생성 할 수있는이 방법은 내가 "모드 3"가 무엇을하고 있는지 잘 모르겠지만, 나는 그것을 무시 것입니다. 당신이 요청한 것처럼 출력이 160 비트가 아니라 128입니다. 160 비트는 SHA-1 해시의 크기입니다. 휴대 성을 위해 "모드 1"을 고수 할 것입니다. – erickson

+0

에릭슨에 동의합니다. "모드 3"을 사용해야 할 필요가 있습니까? 아니면 "모드 1"이 비밀번호를 안전하게 저장할 수 있습니까? 첫 번째 질문에 대한 좋은 질문입니다. –

+1

실제로 필요는 없습니다. BouncyCastle의 PBEWITHHMACSHA1이 왜 똑같은 일을하지 않는지 이해하려고합니다. 그리고 저는 동의합니다. 이식성 문제 때문에 두 번째 방법을 선택하지 않겠습니다. –

답변

28

즉, 모드 1과 모드 2의 PBKDF2 알고리즘은 반복 키 생성에 PKCS # 5 v2 스킴 2 (PKCS5S2)를 사용하지만 모드 3에서 "PBEWITHHMACSHA1"에 대한 BouncyCastle 공급자 대신 PKCS # 12 v1 (PKCS12) 알고리즘을 사용합니다. 이들은 완전히 다른 키 생성 알고리즘이므로 다른 결과를 얻습니다.

이것이 왜 그렇게 왜 다른 크기의 결과를 얻는 지에 대한 자세한 설명은 아래에서 설명합니다.

먼저, JCE KeySpec을 구성 할 때 keyLength 매개 변수는 공급자에게 "기본 설정"만 표시하여 원하는 키 크기를 나타냅니다. the API docs :

참고 :이 키는 가변 키 크기 암호의 키 길이에 대한 기본 설정을 나타내는 데 사용됩니다. 실제의 키 사이즈는, 각 프로 바이더의 구현에 따라서 다릅니다.

탄력이 성 제공 업체는 the source of JCEPBEKey에서 판단,이 매개 변수를 존중하는 표시되지 않습니다, 그래서 당신은 다시 JCE API를 사용하는 경우 SHA-1을 사용하는 모든 BC 공급자로부터 160 비트 키를 얻을 것으로 기대한다.

당신은 프로그래밍 테스트 코드에서 반환 된 keybc 변수에 getKeySize() 방법을 액세스하여이를 확인할 수 있습니다

Key keybc = factorybc.generateSecret(keyspecbc); 
// ... 
Method getKeySize = JCEPBEKey.class.getDeclaredMethod("getKeySize"); 
getKeySize.setAccessible(true); 
System.out.println(getKeySize.invoke(keybc)); // prints '160' 

을 이제, "PBEWITHHMACSHA1"공급자에 해당하는 이해하려면 다음 사항을 찾을 수 있습니다 the source for BouncyCastleProvider :

put("SecretKeyFactory.PBEWITHHMACSHA1", 
    "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA"); 

그리고 JCESecretKeyFactory.PBEWithSHA의 구현은 다음과 같습니다

public static class PBEWithSHA 
    extends PBEKeyFactory 
{ 
    public PBEWithSHA() 
    { 
     super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0); 
    } 
} 

이 키 팩토리는 PKCS # 12 v1 (PKCS12) 알고리즘을 반복 키 생성에 사용한다는 것을 알 수 있습니다. 그러나 암호 해싱에 사용하려는 PBKDF2 알고리즘은 대신 PKCS # 5 v2 체계 2 (PKCS5S2)를 사용합니다. 이것이 다른 결과를 얻는 이유입니다.

나는 BouncyCastleProvider에 등록 된 JCE 공급자를 통해 빠른 모습을 가지고뿐만 아니라, HMAC-SHA-1을 사용하는 혼자 일을하자, 모든 PKCS5S2을 사용 어떤 키 생성 알고리즘을 볼 수 없었다.

그래서 Sun 구현 (위의 모드 # 1)을 사용하고 다른 JVM에서 이식성을 잃거나 Bouncy Castle 클래스를 직접 사용 (위의 모드 # 2)하고 런타임시 BC 라이브러리가 필요합니다. .

어느 쪽이든, 아마 160 비트 키로 전환해야하므로 생성 된 SHA-1 해시를 불필요하게 자르지 않습니다.

2

실제로 UTF-8 기반 암호 인코딩을 생성하는 BC Crypto-Only 방법 (BC의 cms 패키지에서 실제로)이 발견되었습니다.

http://packages.python.org/passlib/lib/passlib.hash.cta_pbkdf2_sha1.html#passlib.hash.cta_pbkdf2_sha1

private byte[] calculatePasswordDigest(char[] pass, byte[] salt, int iterations) 
    throws PasswordProtectionException 
{ 
    try 
    { 
     /* JCE Version (does not work as BC uses PKCS12 encoding) 
     SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWITHHMACSHA1","BC"); 
     PBEKeySpec ks = new PBEKeySpec(pass, salt, iterations,160); 
     SecretKey digest = kf.generateSecret(ks); 
     return digest.getEncoded(); 
     */ 
     PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(); 
     gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(pass), salt, iterations); 
     byte[] derivedKey = ((KeyParameter)gen.generateDerivedParameters(160)).getKey(); 
     return derivedKey; 
    } 
    catch(Exception e) 
    { 
     LOG.error("Failed to strengthen the password with PBKDF2.",e); 
     throw new PasswordProtectionException(); 
    } 
}