2

I'd like to use the PBEWITHHMACSHA256ANDAES_256 algorithm but it's not supported. I've added the bouncy castle provider to my test in the hopes that it will work but to no avail. Can anyone please tell me how to fix the test below such that PBEWITHHMACSHA256ANDAES is added to the Supported list?

import java.security.Security;
import java.util.Set;
import java.util.TreeSet;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.jasypt.registry.AlgorithmRegistry;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

public class EncryptionTest {
    @BeforeClass
    public static void beforeClass() {
        Security.addProvider(new BouncyCastleProvider());
    }

    @Test
    public void test() {
        Set<String> supported = new TreeSet<>();
        Set<String> unsupported = new TreeSet<>();
        for (Object oAlgorithm : AlgorithmRegistry.getAllPBEAlgorithms()) {
            String algorithm = (String) oAlgorithm;
            try {
                SimpleStringPBEConfig pbeConfig = new SimpleStringPBEConfig();
                pbeConfig.setAlgorithm(algorithm);
                pbeConfig.setPassword("changeme");
                StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
                encryptor.setConfig(pbeConfig);

                String encrypted = encryptor.encrypt("foo");
                String decrypted = encryptor.decrypt(encrypted);
                Assert.assertEquals("foo", decrypted);
                supported.add(algorithm);
            } catch (EncryptionOperationNotPossibleException e) {
                unsupported.add(algorithm);
            }
        }
        System.out.println("Supported");
        supported.forEach((String alg) -> System.out.println("   " + alg)); 
        System.out.println("Unsupported");
        unsupported.forEach((String alg) -> System.out.println("   " + alg)); 
    }
}            

Output:

Supported
   PBEWITHMD2ANDDES
   PBEWITHMD5AND128BITAES-CBC-OPENSSL
   PBEWITHMD5AND192BITAES-CBC-OPENSSL
   PBEWITHMD5AND256BITAES-CBC-OPENSSL
   PBEWITHMD5ANDDES
   PBEWITHMD5ANDRC2
   PBEWITHSHA1ANDDES
   PBEWITHSHA1ANDDESEDE
   PBEWITHSHA1ANDRC2
   PBEWITHSHA1ANDRC2_128
   PBEWITHSHA1ANDRC2_40
   PBEWITHSHA1ANDRC4_128
   PBEWITHSHA1ANDRC4_40
Unsupported
   PBEWITHHMACSHA1ANDAES_128
   PBEWITHHMACSHA1ANDAES_256
   PBEWITHHMACSHA224ANDAES_128
   PBEWITHHMACSHA224ANDAES_256
   PBEWITHHMACSHA256ANDAES_128
   PBEWITHHMACSHA256ANDAES_256
   PBEWITHHMACSHA384ANDAES_128
   PBEWITHHMACSHA384ANDAES_256
   PBEWITHHMACSHA512ANDAES_128
   PBEWITHHMACSHA512ANDAES_256
   PBEWITHMD5ANDTRIPLEDES
   PBEWITHSHA256AND128BITAES-CBC-BC
   PBEWITHSHA256AND192BITAES-CBC-BC
   PBEWITHSHA256AND256BITAES-CBC-BC
   PBEWITHSHAAND128BITAES-CBC-BC
   PBEWITHSHAAND128BITRC2-CBC
   PBEWITHSHAAND128BITRC4
   PBEWITHSHAAND192BITAES-CBC-BC
   PBEWITHSHAAND2-KEYTRIPLEDES-CBC
   PBEWITHSHAAND256BITAES-CBC-BC
   PBEWITHSHAAND3-KEYTRIPLEDES-CBC
   PBEWITHSHAAND40BITRC2-CBC
   PBEWITHSHAAND40BITRC4
   PBEWITHSHAANDIDEA-CBC
   PBEWITHSHAANDTWOFISH-CBC

* Edit *

@EbbeMPedersen suggested that this algorithm is provided by SunJCE but I can see that SunJCE provider is enabled using the following code

for (Provider provider : Security.getProviders()) {
    System.out.println(provider.getName() + " " + provider.getClass().getName());
}

Output

SUN sun.security.provider.Sun
SunRsaSign sun.security.rsa.SunRsaSign
SunEC sun.security.ec.SunEC
SunJSSE com.sun.net.ssl.internal.ssl.Provider
SunJCE com.sun.crypto.provider.SunJCE
SunJGSS sun.security.jgss.SunProvider
SunSASL com.sun.security.sasl.Provider
XMLDSig org.jcp.xml.dsig.internal.dom.XMLDSigRI
SunPCSC sun.security.smartcardio.SunPCSC
SunMSCAPI sun.security.mscapi.SunMSCAPI
BC org.bouncycastle.jce.provider.BouncyCastleProvider
lance-java
  • 25,497
  • 4
  • 59
  • 101
  • Take a look at BouncyCastle supported algorithms [here](https://www.bouncycastle.org/specifications.html) .. PBEWITHHMACSHA256ANDAES_256 is not on the list. It might pop up in the list from another security provider – Ebbe M. Pedersen Nov 02 '16 at 00:50
  • It's e.g. part of [SunJCE](http://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SunJCEProvider) – Ebbe M. Pedersen Nov 02 '16 at 00:57
  • Please see my edit, `com.sun.crypto.provider.SunJCE` is in the array returned by `Security.getProviders()` – lance-java Nov 02 '16 at 09:50

1 Answers1

0

I think the issue is that Jasypt is not able to retain the derived AlgorithmParameters and re-use them when switching the cipher between ENCRYPT and DECRYPT. The underlying exception that Jasypt is obscuring is java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected, but if you provide the salt (or SaltGenerator in this case) and key obtention iterations (i.e. the number of times to invoke the HMAC/SHA-256 "key digest function"), Jasypt still complains because it doesn't understand how to pass the calculated parameters to the decrypt cipher. (You can verify this by debugging your test and putting a breakpoint in StandardPBEByteEncryptor.java at line 1055 (Jasypt 1.9.2)) -- here you can catch the underlying exception and use the expression window to verify that:

encryptCipher.getParameters().getEncoded() -> 306206092a864886f70d01050d3055303406092a864886f70d01050c30270410f5a439e8dc12642972dbbf3e1867edaf020203e8020120300c06082a864886f70d02090500301d060960864801650304012a0410caacd97ae953ae257b1b4a0bb70ccc2e

but

decryptCipher.getParameters().getEncoded() -> java.security.ProviderException: Could not construct CipherSpi instance

Here is a (Groovy) test that demonstrates the failure of Jasypt and the successful method of using your desired algorithm (note: 1000 iterations is not sufficient for robust security against modern hardware, but for demonstration purposes only):

@Test
void testShouldUseHS256andAES256() {
    // Arrange
    String algorithm = "PBEwithHMACSHA256andAES_256";
    SimpleStringPBEConfig pbeConfig = new SimpleStringPBEConfig();
    pbeConfig.setAlgorithm(algorithm);
    pbeConfig.setPassword("changeme");

    // Need an IV (derived from salt and iteration count)
    // pbeConfig.setKeyObtentionIterations(1000);
    // pbeConfig.setSaltGenerator(new RandomSaltGenerator());

    StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
    encryptor.setConfig(pbeConfig);

    // Act
    def msg = shouldFail(Exception) {
        String encrypted = encryptor.encrypt("foo");

        // Assert
        String decrypted = encryptor.decrypt(encrypted);
        Assert.assertEquals("foo", decrypted);
    }
    logger.info("Expected: ${msg}")

    // Required way
    Cipher rawCipher = Cipher.getInstance(algorithm)
    PBEKeySpec pbeKeySpec = new PBEKeySpec("changeme" as char[])
    final SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
    SecretKey tempKey = factory.generateSecret(pbeKeySpec);
    PBEParameterSpec saltParameterSpec = new PBEParameterSpec(Hex.decodeHex("0123456789ABCDEF" as char[]), 1000)
    rawCipher.init(Cipher.ENCRYPT_MODE, tempKey, saltParameterSpec)

    // Save the generated ASN.1-encoded parameters
    byte[] algorithmParameterBytes = rawCipher.getParameters().encoded

    byte[] cipherBytes = rawCipher.doFinal("foo".getBytes(StandardCharsets.UTF_8))

    AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(algorithm)
    algorithmParameters.init(algorithmParameterBytes)
    rawCipher.init(Cipher.DECRYPT_MODE, tempKey, algorithmParameters)
    byte[] plainBytes = rawCipher.doFinal(cipherBytes)
    String recovered = new String(plainBytes, StandardCharsets.UTF_8)

    assert recovered == "foo"
}
Andy
  • 13,916
  • 1
  • 36
  • 78