it-swarm.com.de

PBKDF2 mit bouncycastle in Java

Ich versuche, ein Kennwort sicher in einer Datenbank zu speichern, und dafür habe ich mich entschieden, den mit der Funktion PBKDF2 generierten Hash zu speichern. Ich möchte dies mit der Hüpfburg-Bibliothek tun, aber ich weiß nicht, warum ich es nicht schaffen kann, indem ich die JCE-Schnittstelle verwende ... Das Problem ist, dass der Hash in 3 verschiedenen Modi generiert wird: 
1. Verwenden der von Sun bereitgestellten geheimen Schlüsselfabrik PBKDF2WithHmacSHA1 
2. direkt mit dem Hüpfburg api 
3. Verwendung der Hüpfburg durch JCE 
ergibt zwei unterschiedliche Werte: einen für die ersten zwei und einen für den dritten.

Hier ist mein Code:

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

Ich weiß, dass PBKDF2 mit HMAC SHA1 implementiert wird. Deshalb habe ich mich bei der letzten Methode als Algorithmus für "PBEWITHHMACSHA1" entschieden, den ich aus der Java-Dokumentation von Bouncy Castle genommen habe.

Die Ausgabe ist die folgende:

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

Irgendwelche Ideen?

36
andrei.serea

Der Grund für den Unterschied ist kurz gesagt, dass der PBKDF2-Algorithmus in den Modi # 1 und # 2 das PKCS # 5 v2-Schema 2 (PKCS5S2) für die iterative Schlüsselerzeugung verwendet, der BouncyCastle-Provider für "PBEWITHHMACSHA1" im Modus # 3 jedoch die PKCS # verwendet. 12 v1 (PKCS12) -Algorithmus stattdessen. Hierbei handelt es sich um völlig unterschiedliche Schlüsselgenerierungsalgorithmen, sodass Sie unterschiedliche Ergebnisse erhalten.

Weitere Informationen dazu, warum dies so ist und warum Sie Ergebnisse mit unterschiedlichen Größen erhalten, wird unten erläutert.

Wenn Sie eine JCE-KeySpec erstellen, drückt der keyLength -Parameter dem Provider nur "eine Einstellung" aus, welche Schlüsselgröße Sie wünschen. Von den API-Dokumenten :

Hinweis: Dies wird verwendet, um die Präferenz für die Schlüssellänge für Chiffren mit variabler Schlüsselgröße anzugeben. Die tatsächliche Schlüsselgröße hängt von der Implementierung jedes Anbieters ab.

Die Bouncy Castle-Anbieter scheinen diesen Parameter nicht zu respektieren, wenn Sie aus der Quelle von JCEPBEKey urteilen. Sie sollten also einen 160-Bit-Schlüssel von jedem BC-Anbieter erhalten, der bei Verwendung der JCE-API SHA-1 verwendet .

Sie können dies bestätigen, indem Sie programmgesteuert auf die getKeySize()-Methode für die zurückgegebene keybc-Variable in Ihrem Testcode zugreifen:

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

Um zu verstehen, was dem Anbieter "PBEWITHHMACSHA1" entspricht, können Sie Folgendes in der Quelle für BouncyCastleProvider finden:

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

Und die Implementierung von JCESecretKeyFactory.PBEWithSHA sieht folgendermaßen aus:

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

Wie Sie oben sehen können, verwendet diese Schlüsselfabrik den PKCS # 12 v1-Algorithmus ( PKCS12 ) zur iterativen Schlüsselgenerierung. Der PBKDF2-Algorithmus, den Sie für das Passwort-Hashing verwenden möchten, verwendet jedoch stattdessen das PKCS # 5 v2-Schema 2 ( PKCS5S2 ). Deshalb erhalten Sie unterschiedliche Ergebnisse.

Ich habe kurz durch die JCE-Provider geschaut, die in BouncyCastleProvider registriert sind, aber ich konnte keine any -Schlüsselgenerierungsalgorithmen sehen, die PKCS5S2 überhaupt verwendeten, geschweige denn einen, der es auch mit HMAC-SHA-1 verwendet.

Ich denke also, dass Sie entweder mit der Sun-Implementierung (Modus # 1 oben) und dem Verlust der Portierbarkeit auf anderen JVMs oder mit der direkten Verwendung der Bouncy Castle-Klassen (Modus # 2) arbeiten und die BC-Bibliothek zur Laufzeit benötigen.

In beiden Fällen sollten Sie wahrscheinlich zu 160-Bit-Schlüsseln wechseln, um den generierten SHA-1-Hash nicht unnötig abzuschneiden.

30
Matt Ryall

Ich habe eine BC-Crypto-Only-Methode (eigentlich aus dem cms-Paket von BC) gefunden, die eine UTF-8-basierte Kennwortkodierung erzeugt. Auf diese Weise kann ich eine KDF-Ausgabe erzeugen, die kompatibel ist

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

PBKDF2WithHmacSHA1 wird bereits in BouncyCastle 1.60 unterstützt

https://www.bouncycastle.org/specifications.html Passwort-Hashing und PBE

Test mit OpenJDK Runtime Environment 18.9 (Build 11.0.1 + 13) bestanden:

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

Die Ausgabe ist:

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