11

I'm doing some Java encryption, and cannot figure out a way to properly use the the PBEWithHmacSHA512AndAES_256 algorithm.

Encryption seems to work fine, but I am unable to properly initialize a decryption cipher.

Below is a short program that demonstrates the issue. In particular, see the "PROBLEM" comment.

Note: I have seen this very helpful answer, and I can get things to work using that scheme, but I'm curious to know what I'm doing wrong here.

import java.nio.charset.StandardCharsets;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

public final class CryptQuestion {

    private static final String ALGORITHM = "PBEWithHmacSHA512AndAES_256";
    private static final int ITERATIONS = 1000; // Aside: not sure what is a good number, here.

    public static void main(final String[] args) throws Exception {
        final String message = "This is the secret message... BOO!";
        System.out.println("Original : " + message);
        final byte[] messageBytes = message.getBytes(StandardCharsets.US_ASCII);

        final String password = "some password";
        final byte[] salt = "would be random".getBytes(StandardCharsets.US_ASCII);

        // Create the Key
        final SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
        final PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS);
        SecretKey key = factory.generateSecret(keySpec);

        // Build the encryption cipher.
        final Cipher cipherEncrypt = Cipher.getInstance(ALGORITHM);
        cipherEncrypt.init(Cipher.ENCRYPT_MODE, key);

        // Encrypt!
        final byte[] ciphertext = cipherEncrypt.doFinal(messageBytes);
        final byte[] iv = cipherEncrypt.getIV();

        // Now for decryption... The receiving end will have as input:
        // * ciphertext
        // * IV
        // * password
        // * salt

        // We just re-use 'key' from above, since it will be identical.

        final PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, ITERATIONS);
        final IvParameterSpec ivParamSpec = new IvParameterSpec(iv);

        // Build the decryption cipher.
        final Cipher cipherDecrypt = Cipher.getInstance(ALGORITHM);
        // PROBLEM: If I pass "ivParamSpec", I get "java.security.InvalidAlgorithmParameterException: Wrong parameter type: PBE expected"
        // Whereas if I pass pbeParamSpec, I get "java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected"
        // What to do?
        cipherDecrypt.init(
                Cipher.DECRYPT_MODE,
                key,
                ivParamSpec
                //pbeParamSpec
                );

        final String decrypted = new String(
                cipherDecrypt.doFinal(ciphertext),
                StandardCharsets.US_ASCII);
        System.out.println("Decrypted: " + decrypted);
    }
}
Community
  • 1
  • 1
jwd
  • 10,837
  • 3
  • 43
  • 67

3 Answers3

10
// PROBLEM: If I pass "ivParamSpec", I get "java.security.InvalidAlgorithmParameterException: Wrong parameter type: PBE expected"
// Whereas if I pass pbeParamSpec, I get "java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected"
// What to do?
cipherDecrypt.init(
    Cipher.DECRYPT_MODE,
    key,
    ivParamSpec
    //pbeParamSpec
    );

Use the AlgorithmParameters from the encrypting Cipher:

cipherDecrypt.init(
    Cipher.DECRYPT_MODE,
    key,
    cipherEncrypt.getParameters()
    );

If you want a cleaner way that doesn't involve cipherEncrypt at the decrypting site, save the algorithm parameters as a byte and transmit them along with the key data:

byte[]  algorithmParametersEncoded = cipherEncrypt.getParameters().getEncoded();

and reconstruct them at the decrypting site thus:

AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(ALGORITHM);
algorithmParameters.init(algorithmParametersEncoded);

and use algorithmParameters as the parameters argument for Cipher.init() above.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • And as a side question: the encoded algorithm params are ... implementation specific, I take it? I mean, if I wanted to encrypt in Python but decrypt in Java (or vice-versa), is there any good way to replicate whatever "algorithmParametersEncoded" represents? – jwd Mar 24 '15 at 16:05
  • @jwd Sorry, I pass. There is reference in the Javadoc to them being encoded in ASN.1, which indicates language-independence, but whether you can encode or decode them in Python is unknown to me. – user207421 Mar 24 '15 at 21:55
  • Yeah, upon further research, it does seem to be ASN.1 encoded (DER, specifically). It can be parsed with pyasn1, for instance. I don't know how strongly this format is guaranteed by Java, though. – jwd Mar 25 '15 at 19:00
  • @jwd If the Javadoc says ASN.1 you can rely on it not changing to something else. – user207421 Jun 19 '15 at 21:29
  • The javadoc merely says "The primary decoding format for parameters is ASN.1, if an ASN.1 specification for this type of parameters exists." It murky as to whether one can be confident that such an encoding exists for any particular params. In my case I got lucky, though (: – jwd Jun 19 '15 at 22:18
2

For those who jumps here from google. There is another way to specify IV for PBEwithHmacSHA512AndAES_256 cipher in java. IvParameterSpec can be added to PBEParameterSpec in constructor like this:

byte[] iv = ...;
byte[] salt = ...;
int iterations = 200000;

IvParameterSpec ivSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iterations, ivSpec);

Then such pbeSpec can be used to initialize the cipher:

cipher.init(
    Cipher.DECRYPT_MODE,
    key,
    pbeSpec
);

Anyway the accepted answer provides a much cleaner solution - to serialize all cipher parameters at once with cipherEncrypt.getParameters().getEncoded(). The resulting byte array includes IV, salt and iteration count at once. It is completely ok to pass this data next to ciphered message at least for PBEwithHmacSHA512AndAES_256 algorithm

LazyS
  • 326
  • 2
  • 7
0

I've enhanced the StandardPBEByteEncryptor from teh jasyptto support the "PBEWithHmacSHA512AndAES_256" algorithm. see code here

See the unit test here PBEEncryptorTest

Tony
  • 1,458
  • 2
  • 11
  • 13