简体   繁体   中英

RSA Encryption in Java: IllegalBlockSizeException

I am trying to set up RSA Encryption in my Java project. I generated an asymmetric key with the following command in the Terminal:

keytool -genkey -keyalg RSA -alias mykey -keystore mykey.jks -storepass mykeypass -keypass mykeypass

Now I load the keystore with the following method:

public void loadKeyStore() throws KeyStoreException, CertificateException, IOException, 
NoSuchAlgorithmException {
    keyStore = KeyStore.getInstance(KEY_TYPE);
    char[] storePwdArray = STORE_PASS.toCharArray();
    FileInputStream fis = new FileInputStream(KEY_STORE_PATH);
    keyStore.load(fis, storePwdArray);
}

Now, I have two methods, one for encryption, one for decryption:

public String encrypt(String data) throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
    if (keyStore == null) {
        loadKeyStore();
    }
    Certificate cert = keyStore.getCertificate(ALIAS);
    Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    rsa.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
    byte[] encryptedBytes = rsa.doFinal(data.getBytes(StandardCharsets.UTF_8));
    return Base64.getEncoder().encodeToString(encryptedBytes);
}

public String decrypt(String encryptedData) throws UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException {
    if (keyStore == null) {
        loadKeyStore();
    }
    Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    char[] keyPwdArray = KEY_PASS.toCharArray();
    Key key = keyStore.getKey(ALIAS, keyPwdArray);
    rsa.init(Cipher.DECRYPT_MODE, key);
    byte[] decryptedBytes = rsa.doFinal(encryptedData.getBytes());
    return new String(decryptedBytes, StandardCharsets.UTF_8);
}

In the main method I try to encrypt and decrypt a String and print it, like that:

public static void main(String[] args) throws UnrecoverableKeyException, NoSuchPaddingException, IllegalBlockSizeException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
    RSAEncryptionService encryptionService = new RSAEncryptionService();
    String secretMessage = "Hello World!";
    String encryptedMessage = encryptionService.encrypt(secretMessage);
    System.out.println(encryptedMessage);
    String decryptedMessage = encryptionService.decrypt(encryptedMessage);
    System.out.println(decryptedMessage);
}

The encryption works fine and returns something like that: B61g7zzXDNW9AO/Idc/OBZOCDOJpQTwgchD9uJisEBgxy8HV1XPYZZaLEnxkJHed2sBAQXEIyCDcIAHWk5rxn40tVd4NwlIUya1rB6WNvRFLrrN30G7VjMU6NNUdwJ55n7is2Ylfu0SkwNpy/o4e9LaZyzCyr4lJsTbFEXJQJKqLsOC+ysHYdhzx61Y8UJw6mUhleju7h11OcdDBdGEtAtBcKx9WDt2cgHrdtYUgUkwmEy3vTuuyUwVVpjA4QwUsjXnN+i19FQBZt67sMYIpUT4x4yJ8egqN4mJ2N8aNLwF7m/FS7EZphXdna4KN0srKBbPquB1ER5be6RnoyMFDsg==

But when it comes to the decryption, I get the following Exception:

Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
    at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:349)
    at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:406)
    at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205)

What I tried:

I read somewhere on StackOverflow that I need to increase the size of the key. But, that also produces a longer encrypted String, and then the Exception just states "Data must not be longer than 512/1024/... bytes".

I tried using a KeyPair generated in Code, like below. That worked, but I have no idea how to get that KeyPair into my keystore.

public void initKeyStore() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, KeyStoreException, IOException, CertificateException {
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    generator.initialize(2048);
    KeyPair pair = generator.generateKeyPair();
    Cipher encryptCipher = Cipher.getInstance("RSA");
    encryptCipher.init(Cipher.ENCRYPT_MODE, pair.getPublic());
    byte[] secretMessageBytes = "secretMessage".getBytes(StandardCharsets.UTF_8);
    byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
    String encodedMessage = Base64.getEncoder().encodeToString(encryptedMessageBytes);
    System.out.println(encodedMessage);

    Cipher decryptCipher = Cipher.getInstance("RSA");
    decryptCipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
    byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
    String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
    System.out.println(decryptedMessage);
}

The reason is simple. You are using a 2048 bit certificate. it cannot encrypt any data bigger than 256 bytes because it need to be smaller than RSA modulus.

In RSA encryption, you normally need to use envelops to support encryption of any arbitrary data sizes. In nutshell, it means that you need to encrypt you data with a random symmetric key and symmetric algorithm(like AES), and then encrypt its key using RSA and send both to other side. Other side first decrypt symmetric key, and then use it to decrypt original data. So it will be something like this:

Encryption side
-----------------
1- Generate random AES key
2- Encrypt data with AES and generated key
3- Encrypt Key with RSA public key
4- Send 2 encrypted data to the other side

Decryption side
-----------------
1- Split received data to encrypted key and encrypted data
1- Decrypt AES Key with RSA private key
2- Decrypt data with AES and decrypted AES key

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM