This question already has answers here:
Too much data for RSA block fail. What is PKCS#7?
(7 answers)
Closed 10 years ago.
A have an error in my Android project (RSA encryption/decryption).
The encryption passes OK, but when I trying to decrypt encrypted text, yhere are an error: "too much data for RSA block".
How to solve this problem?
code:
public String Decrypt(String text) throws Exception
{
try{
Log.i("Crypto.java:Decrypt", text);
RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate();
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] cipherData = cipher.doFinal(text.getBytes());// <----ERROR: too much data for RSA block
byte[] decryptedBytes = cipher.doFinal(cipherData);
String decrypted = new String(decryptedBytes);
Log.i("Decrypted", decrypted);
return decrypted;
}catch(Exception e){
System.out.println(e.getMessage());
}
return null;
}
Your issue is that you need to encode/decode the ciphertext (just text in your code) if you want to transport it using a textual representation (String in your case).
Try and look up base 64 encoding on this site, there should be a lot of information about it. Encode after encryption and decode before decryption. You should also specify a specific character encoding for your plaintext.
Finally, you should probably encrypt with a symmetric cipher, and encrypt the symmetric key using RSA. Otherwise you may run out of space within the RSA calculation, because a public key cannot encrypt data larger than its modulus (key size).
Related
at the moment im trying to encrypt with rsa in php with a public key generated in an android app and then decrypt in android app again.
My code to generate the keys in android is:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate();
With that keys i can en- and decrypt very well. The pub key looks like this:
OpenSSLRSAPublicKey{modulus=9ee9f82dd8429d9fa7f091c1d375b9c289bcf2c39ec57e175a2998b4bdd083465ef0fe6c7955c821b7e883929d017a9164a60290f1622f664a72096f5d2ffda7c7825c3d657c2d13d177445fa6cdd5d68b96346006a96040f5b09baae56d0c3efeaa77d57602f69018f5cefd60cb5c71b6b6f8a4b0472e8740367266917d8c13,publicExponent=10001}
In php im taking the modulus and exponent, creating a encrypted string with phpseclib 1.0
$rsa = new Crypt_RSA();
// $rsa->createKey();
$m = "9ee9f82dd8429d9fa7f091c1d375b9c289bcf2c39ec57e175a2998b4bdd083465ef0fe6c7955c821b7e883929d017a9164a60290f1622f664a72096f5d2ffda7c7825c3d657c2d13d177445fa6cdd5d68b96346006a96040f5b09baae56d0c3efeaa77d57602f69018f5cefd60cb5c71b6b6f8a4b0472e8740367266917d8c13";
$e = "10001";
$data = "hallo";
$modulus = new Math_BigInteger($m, 16);
$exponent = new Math_BigInteger($e, 16);
$rsa->loadKey(array('n' => $modulus, 'e' => $exponent));
$messageEncrypt = $rsa->encrypt($data);
In Android again, im loading the key, and decrypting it like this:
Cipher cipher1 = Cipher.getInstance("RSA");
cipher1.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher1.doFinal(encrypted.getBytes());
String decrypted = new String(decryptedBytes);
Im always getting a wrong decrypted plaintext or a " Caused by: java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block" error message from Android.
What i think: The problem is the encoded transfer. That php outputs a different encoded version as java uses. So I tried a lot of different ways. I tried to convert the output to String/bin/hex/byte. Then transfer it, with socket or with copy+paste directly in the Code. Convert it back from hex/bin... to a byte[] and try to decode it. Nothing works...
Anyone has a solution for this?
Since you're not specifying the encryption mode with phpseclib what that means is that you're using the (more secure and less common) OAEP encryption mode. My guess is that Java is using PKCS1 encryption by default ($rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);).
That said, with OAEP mode and the key that you're using (a 1024-bit key; 128 bytes), the limit is 86 bytes. The limit with PKCS1 mode is 117 bytes.
phpseclib 1.0 / 2.0 might not give errors because phpseclib tries to be all user friendly and will split the string up into chunks of the max size and will encrypt each chunk separately. It's unlikely that Java does that.
I have tried multiple ways to do this, for three days now and many hours. I have gotten NO WHERE. I am using Java to encrypt certain data using AES/CBC/PKCS7Padding, and trying to decrypt using the same in Python but it just won't work. I am using this Python aes.py library http://anh.cs.luc.edu/331/code/aes.py
I am getting this error:
File "/root/ascend/aes.py", line 384, in decrypt
block[(i+(j*4))] = iput[(i*4)+j]
exceptions.IndexError: list index out of range
Here is the Java Aes code:
public String aesEncrypt(String key, String data) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
SecretKey secKey = new SecretKeySpec(key.getBytes(), "AES");
KeyGenerator KeyGen = KeyGenerator.getInstance("AES");
KeyGen.init(256);
Cipher AesCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
AesCipher.init(Cipher.ENCRYPT_MODE, secKey, new IvParameterSpec(IV.getBytes("UTF-8")));
byte[] byteCipherText = AesCipher.doFinal(data.getBytes());
return Base64.encodeToString(byteCipherText, 0).trim();
}
Here is the Java key gen which provides the key for python to use as well:
public String genAESKey() {
String uuid = UUID.randomUUID().toString();
return uuid.replace("-","");
}
And here is the python code to decrypt:
self.data = aes.decryptData(user.aes_key, base64.b64decode(self.data))
#Where user.aes_key is the 256bit Aes key generated by java.
Can someone please take a look and explain what is wrong with this? They are both using the same aes 256 key, pkcs7 padding, and CBC. If anyone knows of a better library that works with such java code please do show.
Edit: Just to clarify things, Aes decryption works in Java just not in python and Python encryption using that aes key works and so does python decryption. Just not java -> python. And self.data is the java encrypted aes data.
Edit #2:
Just tried to do this with PyCrypto as well. Same exact error is occuring.
return self._cipher.decrypt(ciphertext)
exceptions.ValueError: Input strings must be a multiple of 16 in length
The problem was that for some reason Java was padding the first byte of the resulting String. Why? I don't have the slightest clue but after stripping it off in Python all was well. Both the PyCrypto code and the Aes.py code work fine.
I have observed the following when I worked with Cipher.
Encryption code:
Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.ENCRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());
Decryption code :
Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.DECRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());
I get IllegalBlockSizeException ( Input length must be multiple of 16 when ...) on running the Decrypt code.
But If I change the decrypt code to
Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding"); //I am passing the padding too
aes.init(Cipher.DECRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());
It works fine.
I understand that it is in the pattern algorithm/mode/padding. So I thought it is because I didn't mention the padding. So I tried giving mode and padding during encryption,
Encryption code:
Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");//Gave padding during encryption too
aes.init(Cipher.ENCRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());
Decryption code :
Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
aes.init(Cipher.DECRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());
But it fails with IllegalBlockSizeException.
What is the reason, why the exception and what is exactly happening underneath.
If anyone can help? Thanks in advance
UPDATE
Looks like the issue is with the string I am encrypting and decrypting. Because, even the code that I said works, doesn't always work. I am basically encrypting UUIDs (eg : 8e7307a2-ef01-4d7d-b854-e81ce152bbf6). It works with certain strings and doesn't with certain others.
The length of encrypted String is 64 which is divisible by 16. Yes, I am running it on the same machine.
Method for secret key generation:
private Key generateKey() throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA");
String passphrase = "blahbl blahbla blah";
digest.update(passphrase.getBytes());
return new SecretKeySpec(digest.digest(), 0, 16, "AES");
}
During decryption, one can only get an IllegalBlockSizeException if the input data is not a multiple of the block-size (16 bytes for AES).
If the key or the data was invalid (but correct in length), you would get a BadPaddingException because the PKCS #5 padding would be wrong in the plaintext. Very occasionally the padding would appear correct by chance and you would have no exception at all.
N.B. I would recommend you always specify the padding and mode. If you don't, you are liable to be surprised if the provider changes the defaults. AFAIK, the Sun provider converts "AES" to "AES/ECB/PKCS5Padding".
Though I haven't fully understood the internals, I have found what the issue is.
I fetch the encrypted string as a GET request parameter. As the string contains unsafe characters, over the request the string gets corrupted. The solution is, to do URL encoding and decoding.
I am able to do it successfully using the URLEncoder and URLDecoder.
Now the results are consistent. Thanks :)
I would be grateful if anyone can contribute more to this.
I have the byte array of the RSA Public Key. I found on the internet that I can create a real PublicKey object of it by using this code:
PublicKey publicKey =
KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(bytes));
But every time I run this code, I'm getting another result for the encrypted data using that key. I'm sure the data I want to encrypt is always the same, so does the byte array representing the key.
Is this normal?
Here is my code always producing another output:
byte[] keyBytes = Base64.decodeBase64(rsa_1024_public_key);
// rsa_1024_public key is a constant String
Cipher c = Cipher.getInstance("RSA");
PublicKey publicKey =
KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes));
c.init(Cipher.ENCRYPT_MODE, publicKey);
return c.doFinal(password.getBytes());
This is probably a part of the asymmetric encryption algorithm?
Thanks.
RSA is non-determinstic.
You can make it deterministic by selecting a non-random padding mode; however, that will not be secure.
This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Java AES Encrypt Entire String
I've run into a small problem. For some reason, I can't decrypt some strings using the same method that I encrypt them. For example, I'm using this code for decrypting:
SecretKeySpec key = new SecretKeySpec(salt.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
cipher.init(Cipher.DECRYPT_MODE, key);
String result = new String(cipher.doFinal(message));
System.out.println("Decrypted:" + result);
Anyway, when the salt is "1231231231231231" and the message that im trying to decrypt is "read". I get this error:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
But the encryptor (which works the same way) says the encrypted value is
I¡?Þd↨Ú#à, 7êLO*
How can I fix this or avoid the user from inputting such strings? Thansk
Solution: don't store it in a string.
Your call to Cipher.doFinal should be stored in a byte array (byte[]), and your input to the decryption should likewise be a byte array. If you're taking a String input, use .getBytes(); when you're creating the String for an output, use new String(myArray)