Question

I was trying to en- and decrypt a short byte array with RSA using the java bouncy castle library. I made the following steps:

  1. Generate a 2048 bit key pair.
  2. Create a short data byte array, where the first entry is a zero.
  3. Encrypt the data array with the generated key and RSA.
  4. Decrypt the encrypted data array with the generated key and RSA.
  5. Compare the original and decrypted data array.

I noticed that the original and decrypted data array aren't the same. The decrypted data array is missing the first entry and is therefore by 1 shorter than the original data array. This happens only when the data array's first entry is '0'. Why is this happening? Shouldn't the decryption return the same data array? Or are my assumptions, usage and understanding of the library wrong?

Here the full test case (with imports for better understanding):

import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JDKKeyPairGenerator;
import org.bouncycastle.util.encoders.Hex;
import org.hive2hive.core.H2HJUnitTest;
import org.junit.Test;

public class EncryptionUtil2Test {

    @Test
    public void testBug() throws IOException, InvalidKeyException, IllegalBlockSizeException,
        BadPaddingException, DataLengthException, IllegalStateException, InvalidCipherTextException,
        NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
        InvalidAlgorithmParameterException {

        Security.addProvider(new BouncyCastleProvider());

        // generate RSA keys
        BigInteger publicExp = new BigInteger("10001", 16); // Fermat F4, largest known fermat prime
        JDKKeyPairGenerator gen = new JDKKeyPairGenerator.RSA();
        RSAKeyGenParameterSpec params = new RSAKeyGenParameterSpec(2048, publicExp);
        gen.initialize(params, new SecureRandom());
        KeyPair keyPair = gen.generateKeyPair();

        // some data where first entry is 0
        byte[] data = { 0, 122, 12, 127, 35, 58, 87, 56, -6, 73, 10, -13, -78, 4, -122, -61 };

        // encrypt data asymmetrically
        Cipher cipher = Cipher.getInstance("RSA", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
        byte[] rsaEncryptedData = cipher.doFinal(data);

        // decrypt data asymmetrically
        cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
        byte[] dataBack = cipher.doFinal(rsaEncryptedData);

        System.out.println("data      = " + Hex.toHexString(data));
        System.out.println("data back = " + Hex.toHexString(dataBack));

        assertTrue(Arrays.equals(data, dataBack));

    }

}

Thank you for help!

Was it helpful?

Solution

This is due to not specifying the padding mechanism that has to be used; what you do is leaving the padding mechanism up to the provider, in this case Bouncy. Bouncy seems to default on using no padding mechanism.

In that case the result of the "RSA" operation is simply the number you get when performing the modular exponentiation (with the private exponent). As a number is not a byte array, it has to be transformed into one. As the number of initial zero's is not known for a number, those are simply stripped off.

To avoid this (and use RSA as it is defined, RSA requires padding), try and use "RSA/ECB/PKCS1Padding" or "RSA/ECB/OAEPWithSHA-1AndMGF1Padding" if you want a more modern/secure version. RSA without padding is known to be insecure.

Just imagine what happens if the plaintext converts to the number 1. If modular exponentiation is performed with the public exponent, the result is still simply 1. Obviously, that's not how RSA was intended.

Without randomized padding, encryption also has the nasty property of encrypting to the same result for identical ciphertext (encrypting "yes" or "no" would be easy enough to distinguish if you have a known ciphertext for "yes").

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top