I am using rsa key to encrypt a long string which I will send to my server(will encrypt it with server's public key and my private key) But it throws an exception like javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
I feel that I have not understood the working of rsa properly till now(using the inbuilt libraries are the cause for this).
Can some one please explain why this exception is being thrown. Is it not at all possible to send long string encrypted?
5 Answers
The RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.
So basicly you divide the key length with 8 -11(if you have padding). For example if you have a 2048bit key you can encrypt 2048/8 = 256 bytes (- 11 bytes if you have padding). So, either use a larger key or you encrypt the data with a symmetric key, and encrypt that key with rsa (which is the recommended approach).
That will require you to:
- generate a symmetric key
- Encrypt the data with the symmetric key
- Encrypt the symmetric key with rsa
- send the encrypted key and the data
- Decrypt the encrypted symmetric key with rsa
- decrypt the data with the symmetric key
- done :)
Based on @John Snow answer, I did an example
Generate Symmetric Key (AES with 128 bits)
KeyGenerator generator = KeyGenerator.getInstance("AES"); generator.init(128); // The AES key size in number of bits SecretKey secKey = generator.generateKey();
Encrypt plain text using AES
String plainText = "Please encrypt me urgently..." Cipher aesCipher = Cipher.getInstance("AES"); aesCipher.init(Cipher.ENCRYPT_MODE, secKey); byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
Encrypt the key using RSA public key
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(2048); KeyPair keyPair = kpg.generateKeyPair(); PublicKey puKey = keyPair.getPublic(); PrivateKey prKey = keyPair.getPrivate(); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.PUBLIC_KEY, puKey); byte[] encryptedKey = cipher.doFinal(secKey.getEncoded()/*Seceret Key From Step 1*/);
Send encrypted data (byteCipherText) + encrypted AES Key (encryptedKey)
On the client side, decrypt symmetric key using RSA private key
cipher.init(Cipher.PRIVATE_KEY, prKey); byte[] decryptedKey = cipher.doFinal(encryptedKey);
Decrypt the cipher text using decrypted symmetric key
//Convert bytes to AES SecertKey SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey .length, "AES"); Cipher aesCipher = Cipher.getInstance("AES"); aesCipher.init(Cipher.DECRYPT_MODE, originalKey); byte[] bytePlainText = aesCipher.doFinal(byteCipherText); String plainText = new String(bytePlainText);`
You should not use RSA on your secret data directly. You should only ever use RSA on pseudo-random or completely random data, such as session keys or message authentication codes.
You've gotten the problem at 256 bytes -- that is because you're probably working with 2048 bit keys. The keys are able to encrypt any integer in the range 0
to 2^2048 - 1
into the same range, and that means your data must be 256 bytes or smaller.
If you intend to encrypt more than this, please use one RSA encryption to encrypt a session key for a symmetric algorithm, and use that to encrypt your data.
To follow on from John Snow's answer above I created a simple random-symmetric-crypt library that you can use to simply encrypt any length data using a private key.
You can find the library at GitHub - random-symmetric-crypto
final RandomSymmetricCipher cipher = new RandomSymmetricCipher();
// Encrypt the data and the random symmetric key.
final CryptoPacket cryptoPacket = cipher.encrypt(inputData, PRIVATE_KEY_BASE64);
// Convert the CryptoPacket into a Base64 String that can be readily reconstituted at the other end.
final CryptoPacketConverter cryptoPacketConverter = new CryptoPacketConverter();
final String base64EncryptedData = cryptoPacketConverter.convert(cryptoPacket);
System.out.println("Base64EncryptedData=" + base64EncryptedData);
// Decrypt the Base64 encoded (and encrypted) String.
final byte[] outputData = cipher.decrypt(base64EncryptedData, PUBLIC_KEY_BASE64);
you need split your data by the publicKey
int keyLength = publicKey.getModulus().bitLength() / 16;
String[] datas = splitString(data, keyLength - 11);
String mi = ""//the data after encrypted;
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
public static String bcd2Str(byte[] bytes) {
char temp[] = new char[bytes.length * 2], val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}