web-dev-qa-db-ja.com

Java内のBouncycastleを含むPBKDF2

パスワードをデータベースに安全に保存しようとしています。そのため、PBKDF2関数を使用して生成されたハッシュを保存することにしました。弾力のある城ライブラリを使用してこれを実行したいのですが、JCEインターフェースを使用して機能させることができない理由がわかりません...問題は、3つの異なるモードでハッシュを生成することです。
1。 Sunが提供するPBKDF2WithHmacSHA1秘密鍵ファクトリを使用する
2。弾む城のAPIを直接使用する
3。 JCEを介した弾力性のある城の使用
結果は2つの異なる値になります。1つは最初の2つに共通で、もう1つは3番目の値です。

これが私のコードです:

    //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());

PBKDF2はHMAC SHA1を使用して実装されていることを知っているので、最後の方法のアルゴリズムとして、弾力のある城から取得した「PBEWITHHMACSHA1」を選択しましたJava docs。

出力は次のとおりです。

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

何か案は?

39
andrei.serea

要するに、違いの理由は、モード#1および#2のPBKDF2アルゴリズムが反復キー生成にPKCS#5 v2スキーム2(PKCS5S2)を使用しているのに対し、モード#3の「PBEWITHHMACSHA1」のBouncyCastleプロバイダーはPKCS#を使用しているためです。代わりに12 v1(PKCS12)アルゴリズム。これらは完全に異なる鍵生成アルゴリズムであるため、異なる結果が得られます。

これがそうである理由、および異なるサイズの結果が得られる理由の詳細については、以下で説明します。

まず、JCE KeySpecを構築する場合、keyLengthパラメータは、必要なキーサイズをプロバイダに「優先する」ことだけを表します。 APIドキュメント から:

注:これは、可変鍵サイズの暗号の鍵の長さの設定を示すために使用されます。実際の鍵のサイズは、各プロバイダーの実装によって異なります。

Bouncy Castleプロバイダーは JCEPBEKeyのソース から判断すると、このパラメーターを尊重していないようです。そのため、 JCE API。

これは、テストコードで返された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」プロバイダーが何に対応するかを理解するために、 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プロバイダーをざっと見てみましたが、PKCS5S2を使用するany鍵生成アルゴリズムをまったく見ることができませんでした、HMAC-SHA-1でも使用しているものはもちろん。

したがって、Sunの実装(上記のモード#1)を使用して他のJVMでの移植性を失うか、Bouncy Castleクラスを直接(上記のモード#2)使用して、実行時にBCライブラリを必要とすることに悩まされていると思います。

どちらの方法でも、おそらく160ビットのキーに切り替える必要があるため、生成されたSHA-1ハッシュを不必要に切り捨てることはありません。

30
Matt Ryall

私は、BC暗号のみの方法(実際にはBCのcmsパッケージからのもの)を見つけました。これは、UTF-8ベースのパスワードエンコーディングを生成するために機能します。このようにして、互換性のあるKDF出力を生成できます

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();
    }
}
4
eckes

PBKDF2WithHmacSHA1はBouncyCastle 1.60ですでにサポートされています

https://www.bouncycastle.org/specifications.html パスワードハッシュとPBE

OpenJDKランタイム環境18.9(ビルド11.0.1 + 13)で合格したテスト:

    Security.addProvider(new BouncyCastleProvider());

    String password = "xrS7AJk+V6L8J?B%";
    SecureRandom rnd = new SecureRandom();
    int saltLength = 16;
    int keyLength = 128;
    int iterationCount = 10000;

    byte[] salt = new byte[saltLength];
    rnd.nextBytes(salt);

//SunJCE
    SecretKeyFactory factorySun = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1", "SunJCE");
    KeySpec keyspecSun = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength);
    SecretKey keySun = factorySun.generateSecret(keyspecSun);
    System.out.println(keySun.getClass().getName());
    System.out.println(Hex.toHexString(keySun.getEncoded()));

//BouncyCastle  
    SecretKeyFactory factoryBC = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1", "BC");
    KeySpec keyspecBC = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength);
    SecretKey keyBC = factoryBC.generateSecret(keyspecBC);
    System.out.println(keyBC.getClass().getName());
    System.out.println(Hex.toHexString(keyBC.getEncoded()));

    Assert.assertArrayEquals(keySun.getEncoded(), keyBC.getEncoded());

出力は次のとおりです。

com.Sun.crypto.provider.PBKDF2KeyImpl
e9b01389fa91a6172ed6e95e1e1a2611
org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey
e9b01389fa91a6172ed6e95e1e1a2611
2
Lancelot Chen