28

UPDATED 2019: Bouncycastle now support PBKDF2-HMAC-SHA256 since bouncycastle 1.60


Is there any reliable implementation of PBKDF2-HMAC-SHA256 for JAVA?

I used to encrypt using bouncycastle but it does not provide PBKDF2WithHmacSHA256'.

I do not want to write crypto module by myself.

Could you recommend any alternative library or algorithm (if i can stick with bouncycastle)

(here are the algorithms that bouncycastle supports) http://www.bouncycastle.org/specifications.html

dgregory
  • 1,397
  • 1
  • 12
  • 26

4 Answers4

40

Using BouncyCastle classes directly:

PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
gen.init("password".getBytes("UTF-8"), "salt".getBytes(), 4096);
byte[] dk = ((KeyParameter) gen.generateDerivedParameters(256)).getKey();
Pasi
  • 2,606
  • 18
  • 14
39

It is available in Java 8:

public static byte[] getEncryptedPassword(
                                         String password,
                                         byte[] salt,
                                         int iterations,
                                         int derivedKeyLength
                                         ) throws NoSuchAlgorithmException, InvalidKeySpecException {
    KeySpec spec = new PBEKeySpec(
                                 password.toCharArray(),
                                 salt,
                                 iterations,
                                 derivedKeyLength * 8
                                 );

    SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");

    return f.generateSecret(spec).getEncoded();
}
Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
mjrduran
  • 700
  • 6
  • 8
  • 1
    Strange, I could not get the Java 8 version to work correctly. It generated output, but a different output than Bouncy Castle and the Node.js equivalent – Kirby Jan 21 '15 at 17:54
  • 3
    @Kirby Make sure you only use ASCII, Java 8 is a bit weird in the sense that it uses only the lower 8 bits of `char` (i.e. Windows-1252 compatible character encoding). – Maarten Bodewes Sep 15 '15 at 00:28
  • Works like a charm. BTW, if you want to use 512-byte digest, just change "PBKDF2WithHmacSHA256" to "PBKDF2WithHmacSHA512". – Yev Kanivets Dec 28 '18 at 11:34
  • @MaartenBodewes It's using `UTF8.encode` to transform the `char[]` into a `ByteBuffer`. I.e. anything beyond 7-bit ASCII is going to generate multibyte sequences, which will give you unexpected results if you compare with a UTF-16 byte sequence, but that's because the encoding is unexpected, not because Java is ignoring something. – toolforger Sep 09 '20 at 18:12
  • @toolforger From `PBEKeySpec` (Java 14): *Different PBE mechanisms may consume different bits of each password character. For example, the PBE mechanism defined in PKCS #5 looks at only the low order 8 bits of each character, whereas PKCS #12 looks at all 16 bits of each character.* And PBKDF2 is specified in PKCS#5. So if UTF-8 is used, then the documentation is out of kilt. Can you show where you found that encoding function? – Maarten Bodewes Sep 09 '20 at 18:45
  • @MaartenBodewes hmm... can't find that code anymore. Maybe I accidentally hit Bouncycastle code. Anyway, the PBEKeySpec Javadoc is certainly authoritative enough. Still, I don't think advising to stick with ASCII is a good idea, that's too restrictive for many situations; people should rather encode UTF8-encoded strings where possible. (Also, PKCS formats aren't necessarily what you use PBE mechanisms for, maybe the Javadoc was just warning that the input or output may not be what you think? Not sure, lack of time to validate, sorry.) – toolforger Sep 10 '20 at 10:51
  • PKCS#5 is the "Password Based Encryption" standard. I'm not sure what you mean with "PKCS formats". The old PKCS standards by RSA labs cover a large crypto eco system, still largely in place - although waning in importance. – Maarten Bodewes Sep 10 '20 at 17:10
  • Just to clarify, the PKCS#5 standard, version 2.0 [RFC 2898](https://datatracker.ietf.org/doc/html/rfc2898#section-3) and 2.1 [RFC 8018](https://datatracker.ietf.org/doc/html/rfc8018#section-3) leave the character encoding for passwords explicitly unspecified but, "in the interests of interoperability" use common rules, e.g. ASCII or UTF-8. A well designed API would either use byte array or allow the caller to specify the character encoding. – Charlie Reitzel Jun 29 '21 at 20:03
2

Using spongycastle (java on android)

Replace spongycastle with bouncycastle if you are using bouncycastle on java directly

import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.params.KeyParameter;

public class Crypto {
    public String pbkdf2(String secret, String salt, int iterations, int keyLength) {
        PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
        byte[] secretData = secret.getBytes();
        byte[] saltData = salt.getBytes();
        gen.init(secretData, saltData, iterations);
        byte[] derivedKey = ((KeyParameter)gen.generateDerivedParameters(keyLength * 8)).getKey();    
        return toHex(derivedKey);
    }

    private static String toHex(byte[] bytes) {
        BigInteger bi = new BigInteger(1, bytes);
        return String.format("%0" + (bytes.length << 1) + "x", bi);
    }
}
Guillaume Vincent
  • 13,355
  • 13
  • 76
  • 103
1

If you are having spring boot setup, it is available in crypto library

org.springframework.security.crypto.password.Pbkdf2PasswordEncoder

Pbkdf2PasswordEncoder pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder();
pbkdf2PasswordEncoder.encode(password)

Internally it uses PBEKeySpec with the advantage of that the parameters are autoconfigured. You can also set the parameters.

Also it uses random salt so that it is quire hard to break it. To check the match, use pbkdf2PasswordEncoder.matches(rawPassword, password);

jfk
  • 4,335
  • 34
  • 27