Can't decrypt with BouncyCastle FIPS a message encrypted with BouncyCastle (non-FIPS)

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Can't decrypt with BouncyCastle FIPS a message encrypted with BouncyCastle (non-FIPS)

Santiago Alejandro Agüero
Hi,

I'm testing BouncyCastle FIPS (1.0.1).

I'm unable to decrypt a message which was originally encrypted with regular BouncyCastle (ie: non fips) using (in theory) the same symmetric algorithms.

I'm using the algorithm "PBEWITHSHAAND3-KEYTRIPLEDES-CBC", which is working fine in regular BouncyCastle (both encryption and decryption)

The first thing I've encountered in the FIPS version, is that I can only use that algorithm in the Cipher phase, since the SecretKeyFactory doesn't recognize it... as a workaround I'm using PBEWITHSHAAND3-KEYTRIPLEDES for the SecretKeyFactory which seems to be working... is this mixed thing correct? BTW, this affects the integration with 3rd party libraries like Jasypt, which expects the same name algorithm in both cases.

After encrypting with regular BouncyCastle, when I decrypt with BouncyCastle FIPS it's failing with:

Exception in thread "main" javax.crypto.BadPaddingException: Error finalising cipher data: pad block corrupted
at org.bouncycastle.jcajce.provider.BaseCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)

Worth saying that using Sun JCE with PBEWithSHA1AndDESEDE algorithm decrypts successfully.

Here is a sample code for reference:

// Encrypt a message using regular BouncyCastle (non-fips)

public class EncryptSample {
    private static final String secureRandomAlgorithm = "SHA1PRNG";
    private static final int keyObtentionIterations = 1000;
    private static final int saltSizeBytes = 8;
    private static final String keyAlgorithm = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String cipherAlgorithm = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String password = "c45baf55-4569-4541-b214-f64ab166477a";

    public static void main(String[] args) throws GeneralSecurityException {
        Security.addProvider(new BouncyCastleProvider());
        String message = encrypt("Hello world");
        System.out.println(message);
    }

    public static String encrypt(final String message) throws GeneralSecurityException {
        final byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedMessage = encrypt(messageBytes);
        encryptedMessage = Base64.encodeBase64(encryptedMessage);
        return new String(encryptedMessage, StandardCharsets.US_ASCII);
    }

    private static byte[] encrypt(final byte[] message) throws GeneralSecurityException {
        final byte[] salt = generateSalt(saltSizeBytes);
        final byte[] encryptedMessage;

        final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithm, "BC");
        final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, keyObtentionIterations);
        SecretKey key = keyFact.generateSecret(pbeKeySpec);

        final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations);

        final Cipher cipher = Cipher.getInstance(cipherAlgorithm, "BC");

        synchronized (cipher) {
            cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
            encryptedMessage = cipher.doFinal(message);
        }

        return ArrayUtils.addAll(salt, encryptedMessage);
    }

    private static byte[] generateSalt(final int lengthBytes) throws GeneralSecurityException {
        final byte[] salt = new byte[lengthBytes];
        SecureRandom random = SecureRandom.getInstance(secureRandomAlgorithm);
        synchronized (random) {
            random.nextBytes(salt);
        }
        return salt;
    }
}

======================

// Decrypt a message using BouncyCastle FIPS

public class DecryptSample {
    private static final int keyObtentionIterations = 1000;
    private static final int saltSizeBytes = 8;
    private static final String keyAlgorithmFipsMode = "PBEWITHSHAAND3-KEYTRIPLEDES";
    private static final String cipherAlgorithmFipsMode = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String password = "c45baf55-4569-4541-b214-f64ab166477a";
    private static final String encryptedText = "ECadTzeentP2d3I5qOsBwSQfkqMJC3Su";

    public static void main(String[] args) throws GeneralSecurityException {
        Security.addProvider(new BouncyCastleFipsProvider());
        String message = decrypt(encryptedText);
        System.out.println(message);
    }

    public static String decrypt(final String encryptedValue) throws GeneralSecurityException {
        final byte[] encryptedValueAsBytes = Base64.decodeBase64(encryptedValue.getBytes(StandardCharsets.US_ASCII));
        final byte[] salt = getSalt(encryptedValueAsBytes);
        final byte[] encryptedMessageKernel = getEncryptedMessageKernel(encryptedValueAsBytes);
        final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations);
        return decrypt(encryptedMessageKernel, parameterSpec);
    }

    private static String decrypt(final byte[] encryptedMessageKernel, final PBEParameterSpec parameterSpec) throws GeneralSecurityException {
        final String decryptedValue;

        final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithmFipsMode, "BCFIPS");
        final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), parameterSpec.getSalt(), keyObtentionIterations);
        SecretKey key = keyFact.generateSecret(pbeKeySpec);

        final Cipher cipher = Cipher.getInstance(cipherAlgorithmFipsMode, "BCFIPS");

        synchronized (cipher) {
            cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
            decryptedValue = new String(cipher.doFinal(encryptedMessageKernel), StandardCharsets.UTF_8);

        }

        return decryptedValue;
    }

    private static byte[] getEncryptedMessageKernel(final byte[] encryptedValueAsBytes) throws  GeneralSecurityException {
        final int encMesKernelStart =
                (saltSizeBytes < encryptedValueAsBytes.length ? saltSizeBytes : encryptedValueAsBytes.length);
        final int encMesKernelSize =
                (saltSizeBytes < encryptedValueAsBytes.length ? (encryptedValueAsBytes.length
                        - saltSizeBytes) : 0);
        final byte[] encryptedMessageKernel = new byte[encMesKernelSize];
        System.arraycopy(encryptedValueAsBytes, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize);
        return encryptedMessageKernel;
    }

    private static byte[] getSalt(final byte[] encryptedValueAsBytes) throws  GeneralSecurityException {
        final int saltStart = 0;
        final int saltSize =
                (saltSizeBytes < encryptedValueAsBytes.length ? saltSizeBytes : encryptedValueAsBytes.length);
        final byte[] salt = new byte[saltSize];
        System.arraycopy(encryptedValueAsBytes, saltStart, salt, 0, saltSize);
        return salt;
    }
}


--
Santiago Alejandro Agüero
[hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: Can't decrypt with BouncyCastle FIPS a message encrypted with BouncyCastle (non-FIPS)

David Hook-3

Use:

private static String decrypt(final byte[] encryptedMessageKernel, final PBEParameterSpec parameterSpec) throws GeneralSecurityException {
    final String decryptedValue;

    final SecretKey key = new PKCS12Key(password.toCharArray());

    final Cipher cipher = Cipher.getInstance(cipherAlgorithmFipsMode, "BCFIPS");

    synchronized (cipher) {
        cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
        decryptedValue = new String(cipher.doFinal(encryptedMessageKernel), StandardCharsets.UTF_8);

    }

    return decryptedValue;
}

1.0.2 will support:

final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithmFipsMode, "BCFIPS");
final SecretKey key = keyFact.generateSecret(new PBEKeySpec(password.toCharArray()));

Which we should have probably done originally.

The issue in this case is that it doesn't make sense to pass the iteration count and
parameters to a key factory when they can't be processed until the cipher is actually constructed as an
IV also needs to be created. Unfortunately there were about 3 attempts in the JCE APIs to try and get 
PBE to work properly, the regular BC API attempts to satisfy all 3 of these (although it can't quite),
with FIPS we tried to play it more in line with how things actually turned out.

Regards,

David

On 16/01/18 06:36, Santiago Alejandro Agüero wrote:
Hi,

I'm testing BouncyCastle FIPS (1.0.1).

I'm unable to decrypt a message which was originally encrypted with regular BouncyCastle (ie: non fips) using (in theory) the same symmetric algorithms.

I'm using the algorithm "PBEWITHSHAAND3-KEYTRIPLEDES-CBC", which is working fine in regular BouncyCastle (both encryption and decryption)

The first thing I've encountered in the FIPS version, is that I can only use that algorithm in the Cipher phase, since the SecretKeyFactory doesn't recognize it... as a workaround I'm using PBEWITHSHAAND3-KEYTRIPLEDES for the SecretKeyFactory which seems to be working... is this mixed thing correct? BTW, this affects the integration with 3rd party libraries like Jasypt, which expects the same name algorithm in both cases.

After encrypting with regular BouncyCastle, when I decrypt with BouncyCastle FIPS it's failing with:

Exception in thread "main" javax.crypto.BadPaddingException: Error finalising cipher data: pad block corrupted
at org.bouncycastle.jcajce.provider.BaseCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)

Worth saying that using Sun JCE with PBEWithSHA1AndDESEDE algorithm decrypts successfully.

Here is a sample code for reference:

// Encrypt a message using regular BouncyCastle (non-fips)

public class EncryptSample {
    private static final String secureRandomAlgorithm = "SHA1PRNG";
    private static final int keyObtentionIterations = 1000;
    private static final int saltSizeBytes = 8;
    private static final String keyAlgorithm = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String cipherAlgorithm = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String password = "c45baf55-4569-4541-b214-f64ab166477a";

    public static void main(String[] args) throws GeneralSecurityException {
        Security.addProvider(new BouncyCastleProvider());
        String message = encrypt("Hello world");
        System.out.println(message);
    }

    public static String encrypt(final String message) throws GeneralSecurityException {
        final byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedMessage = encrypt(messageBytes);
        encryptedMessage = Base64.encodeBase64(encryptedMessage);
        return new String(encryptedMessage, StandardCharsets.US_ASCII);
    }

    private static byte[] encrypt(final byte[] message) throws GeneralSecurityException {
        final byte[] salt = generateSalt(saltSizeBytes);
        final byte[] encryptedMessage;

        final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithm, "BC");
        final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, keyObtentionIterations);
        SecretKey key = keyFact.generateSecret(pbeKeySpec);

        final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations);

        final Cipher cipher = Cipher.getInstance(cipherAlgorithm, "BC");

        synchronized (cipher) {
            cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
            encryptedMessage = cipher.doFinal(message);
        }

        return ArrayUtils.addAll(salt, encryptedMessage);
    }

    private static byte[] generateSalt(final int lengthBytes) throws GeneralSecurityException {
        final byte[] salt = new byte[lengthBytes];
        SecureRandom random = SecureRandom.getInstance(secureRandomAlgorithm);
        synchronized (random) {
            random.nextBytes(salt);
        }
        return salt;
    }
}

======================

// Decrypt a message using BouncyCastle FIPS

public class DecryptSample {
    private static final int keyObtentionIterations = 1000;
    private static final int saltSizeBytes = 8;
    private static final String keyAlgorithmFipsMode = "PBEWITHSHAAND3-KEYTRIPLEDES";
    private static final String cipherAlgorithmFipsMode = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String password = "c45baf55-4569-4541-b214-f64ab166477a";
    private static final String encryptedText = "ECadTzeentP2d3I5qOsBwSQfkqMJC3Su";

    public static void main(String[] args) throws GeneralSecurityException {
        Security.addProvider(new BouncyCastleFipsProvider());
        String message = decrypt(encryptedText);
        System.out.println(message);
    }

    public static String decrypt(final String encryptedValue) throws GeneralSecurityException {
        final byte[] encryptedValueAsBytes = Base64.decodeBase64(encryptedValue.getBytes(StandardCharsets.US_ASCII));
        final byte[] salt = getSalt(encryptedValueAsBytes);
        final byte[] encryptedMessageKernel = getEncryptedMessageKernel(encryptedValueAsBytes);
        final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations);
        return decrypt(encryptedMessageKernel, parameterSpec);
    }

    private static String decrypt(final byte[] encryptedMessageKernel, final PBEParameterSpec parameterSpec) throws GeneralSecurityException {
        final String decryptedValue;

        final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithmFipsMode, "BCFIPS");
        final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), parameterSpec.getSalt(), keyObtentionIterations);
        SecretKey key = keyFact.generateSecret(pbeKeySpec);

        final Cipher cipher = Cipher.getInstance(cipherAlgorithmFipsMode, "BCFIPS");

        synchronized (cipher) {
            cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
            decryptedValue = new String(cipher.doFinal(encryptedMessageKernel), StandardCharsets.UTF_8);

        }

        return decryptedValue;
    }

    private static byte[] getEncryptedMessageKernel(final byte[] encryptedValueAsBytes) throws  GeneralSecurityException {
        final int encMesKernelStart =
                (saltSizeBytes < encryptedValueAsBytes.length ? saltSizeBytes : encryptedValueAsBytes.length);
        final int encMesKernelSize =
                (saltSizeBytes < encryptedValueAsBytes.length ? (encryptedValueAsBytes.length
                        - saltSizeBytes) : 0);
        final byte[] encryptedMessageKernel = new byte[encMesKernelSize];
        System.arraycopy(encryptedValueAsBytes, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize);
        return encryptedMessageKernel;
    }

    private static byte[] getSalt(final byte[] encryptedValueAsBytes) throws  GeneralSecurityException {
        final int saltStart = 0;
        final int saltSize =
                (saltSizeBytes < encryptedValueAsBytes.length ? saltSizeBytes : encryptedValueAsBytes.length);
        final byte[] salt = new byte[saltSize];
        System.arraycopy(encryptedValueAsBytes, saltStart, salt, 0, saltSize);
        return salt;
    }
}


--
Santiago Alejandro Agüero
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Can't decrypt with BouncyCastle FIPS a message encrypted with BouncyCastle (non-FIPS)

Santiago Alejandro Agüero
Thanks for the information and for providing insights about the roadmap.

Your solution works as expected.

On Mon, Jan 15, 2018 at 9:31 PM, David Hook <[hidden email]> wrote:

Use:

private static String decrypt(final byte[] encryptedMessageKernel, final PBEParameterSpec parameterSpec) throws GeneralSecurityException {
    final String decryptedValue;

    final SecretKey key = new PKCS12Key(password.toCharArray());

    final Cipher cipher = Cipher.getInstance(cipherAlgorithmFipsMode, "BCFIPS");

    synchronized (cipher) {
        cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
        decryptedValue = new String(cipher.doFinal(encryptedMessageKernel), StandardCharsets.UTF_8);

    }

    return decryptedValue;
}

1.0.2 will support:

final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithmFipsMode, "BCFIPS");
final SecretKey key = keyFact.generateSecret(new PBEKeySpec(password.toCharArray()));

Which we should have probably done originally.

The issue in this case is that it doesn't make sense to pass the iteration count and
parameters to a key factory when they can't be processed until the cipher is actually constructed as an
IV also needs to be created. Unfortunately there were about 3 attempts in the JCE APIs to try and get 
PBE to work properly, the regular BC API attempts to satisfy all 3 of these (although it can't quite),
with FIPS we tried to play it more in line with how things actually turned out.

Regards,

David

On 16/01/18 06:36, Santiago Alejandro Agüero wrote:
Hi,

I'm testing BouncyCastle FIPS (1.0.1).

I'm unable to decrypt a message which was originally encrypted with regular BouncyCastle (ie: non fips) using (in theory) the same symmetric algorithms.

I'm using the algorithm "PBEWITHSHAAND3-KEYTRIPLEDES-CBC", which is working fine in regular BouncyCastle (both encryption and decryption)

The first thing I've encountered in the FIPS version, is that I can only use that algorithm in the Cipher phase, since the SecretKeyFactory doesn't recognize it... as a workaround I'm using PBEWITHSHAAND3-KEYTRIPLEDES for the SecretKeyFactory which seems to be working... is this mixed thing correct? BTW, this affects the integration with 3rd party libraries like Jasypt, which expects the same name algorithm in both cases.

After encrypting with regular BouncyCastle, when I decrypt with BouncyCastle FIPS it's failing with:

Exception in thread "main" javax.crypto.BadPaddingException: Error finalising cipher data: pad block corrupted
at org.bouncycastle.jcajce.provider.BaseCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)

Worth saying that using Sun JCE with PBEWithSHA1AndDESEDE algorithm decrypts successfully.

Here is a sample code for reference:

// Encrypt a message using regular BouncyCastle (non-fips)

public class EncryptSample {
    private static final String secureRandomAlgorithm = "SHA1PRNG";
    private static final int keyObtentionIterations = 1000;
    private static final int saltSizeBytes = 8;
    private static final String keyAlgorithm = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String cipherAlgorithm = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String password = "c45baf55-4569-4541-b214-f64ab166477a";

    public static void main(String[] args) throws GeneralSecurityException {
        Security.addProvider(new BouncyCastleProvider());
        String message = encrypt("Hello world");
        System.out.println(message);
    }

    public static String encrypt(final String message) throws GeneralSecurityException {
        final byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedMessage = encrypt(messageBytes);
        encryptedMessage = Base64.encodeBase64(encryptedMessage);
        return new String(encryptedMessage, StandardCharsets.US_ASCII);
    }

    private static byte[] encrypt(final byte[] message) throws GeneralSecurityException {
        final byte[] salt = generateSalt(saltSizeBytes);
        final byte[] encryptedMessage;

        final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithm, "BC");
        final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, keyObtentionIterations);
        SecretKey key = keyFact.generateSecret(pbeKeySpec);

        final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations);

        final Cipher cipher = Cipher.getInstance(cipherAlgorithm, "BC");

        synchronized (cipher) {
            cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
            encryptedMessage = cipher.doFinal(message);
        }

        return ArrayUtils.addAll(salt, encryptedMessage);
    }

    private static byte[] generateSalt(final int lengthBytes) throws GeneralSecurityException {
        final byte[] salt = new byte[lengthBytes];
        SecureRandom random = SecureRandom.getInstance(secureRandomAlgorithm);
        synchronized (random) {
            random.nextBytes(salt);
        }
        return salt;
    }
}

======================

// Decrypt a message using BouncyCastle FIPS

public class DecryptSample {
    private static final int keyObtentionIterations = 1000;
    private static final int saltSizeBytes = 8;
    private static final String keyAlgorithmFipsMode = "PBEWITHSHAAND3-KEYTRIPLEDES";
    private static final String cipherAlgorithmFipsMode = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC";
    private static final String password = "c45baf55-4569-4541-b214-f64ab166477a";
    private static final String encryptedText = "ECadTzeentP2d3I5qOsBwSQfkqMJC3Su";

    public static void main(String[] args) throws GeneralSecurityException {
        Security.addProvider(new BouncyCastleFipsProvider());
        String message = decrypt(encryptedText);
        System.out.println(message);
    }

    public static String decrypt(final String encryptedValue) throws GeneralSecurityException {
        final byte[] encryptedValueAsBytes = Base64.decodeBase64(encryptedValue.getBytes(StandardCharsets.US_ASCII));
        final byte[] salt = getSalt(encryptedValueAsBytes);
        final byte[] encryptedMessageKernel = getEncryptedMessageKernel(encryptedValueAsBytes);
        final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations);
        return decrypt(encryptedMessageKernel, parameterSpec);
    }

    private static String decrypt(final byte[] encryptedMessageKernel, final PBEParameterSpec parameterSpec) throws GeneralSecurityException {
        final String decryptedValue;

        final SecretKeyFactory keyFact = SecretKeyFactory.getInstance(keyAlgorithmFipsMode, "BCFIPS");
        final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), parameterSpec.getSalt(), keyObtentionIterations);
        SecretKey key = keyFact.generateSecret(pbeKeySpec);

        final Cipher cipher = Cipher.getInstance(cipherAlgorithmFipsMode, "BCFIPS");

        synchronized (cipher) {
            cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
            decryptedValue = new String(cipher.doFinal(encryptedMessageKernel), StandardCharsets.UTF_8);

        }

        return decryptedValue;
    }

    private static byte[] getEncryptedMessageKernel(final byte[] encryptedValueAsBytes) throws  GeneralSecurityException {
        final int encMesKernelStart =
                (saltSizeBytes < encryptedValueAsBytes.length ? saltSizeBytes : encryptedValueAsBytes.length);
        final int encMesKernelSize =
                (saltSizeBytes < encryptedValueAsBytes.length ? (encryptedValueAsBytes.length
                        - saltSizeBytes) : 0);
        final byte[] encryptedMessageKernel = new byte[encMesKernelSize];
        System.arraycopy(encryptedValueAsBytes, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize);
        return encryptedMessageKernel;
    }

    private static byte[] getSalt(final byte[] encryptedValueAsBytes) throws  GeneralSecurityException {
        final int saltStart = 0;
        final int saltSize =
                (saltSizeBytes < encryptedValueAsBytes.length ? saltSizeBytes : encryptedValueAsBytes.length);
        final byte[] salt = new byte[saltSize];
        System.arraycopy(encryptedValueAsBytes, saltStart, salt, 0, saltSize);
        return salt;
    }
}


--
Santiago Alejandro Agüero
[hidden email]





--
Santiago Alejandro Agüero
[hidden email]