I have a modulus key and an exponent key, I create an RSA public key to encrypt a data, but I get this exception
java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
Here are the details:
the Modulus value:
B390F7412F2554387597814A25BC11BFFD95DB2D1456F1B66CDF52BCC1D20C7FF24F3CCE7B2D66E143213F64247454782A377C79C74477A28AF6C317BE68BC6E8FF001D375F9363B5A7161C2DFBC2ED0850697A54421552C6288996AC61AF5A9F7DE218ABBC75A145F891266615EB81D11A22B7260F7608083B373BA4BC0756B
size: 256
the Exponent value:
010001
the Data to be encrypted:
1A0498EA0DF19B45043DA4688AE3A7B3D592D61CC0EBB82FB100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
size: 256
and here is the code:
public static String encryptData(String data, BigInteger modulus, BigInteger exponent) throws Exception {
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
PublicKey pub = factory.generatePublic(spec);
Cipher rsa = Cipher.getInstance("RSA", "BC");
rsa.init(Cipher.ENCRYPT_MODE, pub);
byte[] cipherText = rsa.doFinal(data.getBytes()); // ERROR HERE
return Hex.toString(cipherText);
}
Here is the public key generated
30819F300D06092A864886F70D010101050003818D0030818902818100B390F7412F2554387597814A25BC11BFFD95DB2D1456F1B66CDF52BCC1D20C7FF24F3CCE7B2D66E143213F64247454782A377C79C74477A28AF6C317BE68BC6E8FF001D375F9363B5A7161C2DFBC2ED0850697A54421552C6288996AC61AF5A9F7DE218ABBC75A145F891266615EB81D11A22B7260F7608083B373BA4BC0756B0203010001
size: 342
Thank you!
The usual way to use public key (asymmetric) encryption with large documents is to create a random, single-use passphrase. The passphrase is used with a password-based encryption (symmetric) algorithm (e.g., AES-256). Use AES to encrypt the document and then use the public key to encrypt the passphrase.
Generally speaking, symmetric encryption algorithms tend to be a couple of orders of magnitude faster than asymmetric encryption algorithms. So, not only does RSA not lend itself to directly encrypt large documents, but it also would take much more computation to do the encryption.
As an aside I will mention that encryption is hard to get right. I would strongly urge you to use a standard library in a standard way to ensure a secure implementation.
the error is just stupid:
byte[] cipherText = rsa.doFinal(data.getBytes()); // ERROR HERE
I encrypt data.getBytes(), but actually I first need to decode data from its hex representation and only then encrypt it.
Related
I'm trying to encrypt BMP image withe RSA in java,it's supposed to create the encrypted and decrypted images.
Ok so after reading the comments and learned that it is not safe to use RSA alone;I edit my Question. and tryd Java Cryptography but cipher.doFinal() don't accepted a data longer than 245 bytes
File bmpFile = new File("C:\\Users\\acer\\Desktop\\py\\6.bmp");
BufferedImage image = ImageIO.read(bmpFile);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ImageIO.write(image, "bmp", baos );
byte[] b = baos.toByteArray();
byte[] b1=new byte[b.length];
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair pair = keyPairGen.generateKeyPair();
PublicKey publicKey = pair.getPublic();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipher.update(b);
b1 = cipher.doFinal();
bmpFile=new File("C:\\Users\\acer\\Desktop\\py\\66.bmp");
FileOutputStream fos = new FileOutputStream(bmpFile);
fos.write(b1);
fos.flush();
fos.close();
and it give :
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2048)
most of my image are 198x135
i found here in stack onverflow that
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.
and said that you have to encrypt the data with symmetric key and encrypt the symmetric key with rsa.
but i whent to encrypt the data with RSA.
And i went to ask,i have to send the encrypting image to Other pc but the problem is that p,q are random
Asymmetric encryption means encrypting for a specific target (public key).
So the steps would be:
the receiver creates its private key (p, q, e) and public key (N, d) private key (p, q, d) and public key (N, e)
the receiver sends its public key to the sender
sender uses the public key to encrypt the message
receiver can use its private key to decrypt the data
So if you want to use RSA to encrypt any data, the parameters are random for the target receiver, but given for the sender.
do i have to encrypt theme with Symmetric algorithm like RC4 and send theme with the image
As you already may find out, RSA operations are pretty slow. So common way to use RSA is hybrid encryption - encrypting data with a random symmetric encryption key and use RSA to encrypt only the random key.
image.setRGB(i, j,pixels[i][j].intValue());
This won't work. Encryption of any data will have length of the key length. Effectively you need 1024 bit for each image pixel in your case. Trimming the bigint to the intValue you are loosing information.
That's why the (already mentioned) hybrid encryption is used
it's part of my School Project
If you will use RSA for real life projects:
Textbook RSA has several weaknesses, to make the solution secure, you need to use a padding, common standards are pkcs#1 v1.5 or OAEP padding
In reality you should use the default crypto library which is much faster and resilient against side-channel attacks
JAVA
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
class AES256JavaPhp{
public static void main(String[] args) throws Exception {
Base64 base64 = new Base64();
Cipher ciper = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec key = new
SecretKeySpec("PasswordPassword".getBytes("UTF-8"),"AES");
IvParameterSpec iv = new IvParameterSpec
("dynamic#dynamic#".getBytes("UTF-8"),0,ciper.getBlockSize());
//Encrypt
ciper.init(Cipher.ENCRYPT_MODE, key,iv);
byte[] encryptedCiperBytes = base64.encode
((ciper.doFinal("Hello".getBytes())));
System.out.println("Ciper : "+new String(encryptedCiperBytes));
//Decrypt
ciper.init(Cipher.DECRYPT_MODE, key,iv);
byte[] text = ciper.doFinal(base64.decode(encryptedCiperBytes));
System.out.println("Decrypt text : "+new String(text));
}
}
Java output:
Ciper : KpgzpzCRU7mTKZePpPlEvA==
Decrypt text : Hello
PHP
<?php>
$cipherText = encrypt("Hello", 'aes-256-cbc');
exit();
function encrypt($data, $algo)
{
$key = 'PasswordPassword';
//$iv = random_bytes(openssl_cipher_iv_length($algo));
$iv = 'dynamic#dynamic#';
$cipherText = openssl_encrypt(
$data,
$algo,
$key,
OPENSSL_RAW_DATA,
$iv
);
$cipherText = base64_encode($cipherText);
printData("Ciper Text : $cipherText");
$cipherText = base64_decode($cipherText);
$plaintext = openssl_decrypt(
$cipherText,
$algo,
$key,
OPENSSL_RAW_DATA,
$iv
);
printData("Plain Text after decryption : $plaintext");
}
function printData($obj)
{
print_r($obj);
}
?>
PHP output:
Ciper Text : ef/ENVlBn9QBFlkvoN7P2Q==
Plain Text after decryption : Hello
The resulting ciphers are different, even though they are using the same key and IV. How is this possible?
Clearly you must use the same AES key and IV for a secure session. And they must be properly and securely communicated across clients. It does not matter at all what language the clients are written in. Your problem is not understanding the protocol for key agreement and session establishment.
The initialization vector is not a protected value; i.e., you are not encrypting it when communicating between clients. It must be packaged in cleartext with encrypted AES key (which you derive from some key agreement protocol).
CMS uses a KeyTransRecipientInfo to deliver this information. TLS also defines IV establishment followings its handshake. I would highly suggest following the CMS implementation instead of something contrived and almost guaranteed to contain security bugs.
Update
It is now clear that you are confused why the resulting ciphertexts are not deterministic. That is because the Java implementation is defaulting to a 128-bit encryption and has been supplied a 128-bit key, but the PHP code is requesting 256-bit strength encryption and only being supplied the same 128-bit key. Therefore, PHP must be padding the key.
Update 2
Based on your below comments, here is an example of using Java to generate a 256-bit key:
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(256); // The AES key size in number of bits
SecretKey secKey = generator.generateKey();
Your key is only 128 bit (16 bytes) long, but you're requesting AES-256 in PHP. This will lead to a padded AES key of 256 bit (32 bytes). You have to request AES-128 for this to work. That is how the OpenSSL extension works in PHP.
A key should ideally look like random noise in order to prevent brute force attacks. Your current key is anything but that. It is very predictable. You should really generate some random key and add it to your code in encoded form like Base64. Then you can decode it before use.
I am creating a project to encrypt and decrypt a file. I have these two algorithms that work fine:
public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static byte[] getRaw(String password_) throws Exception {
byte[] keyStart = password_.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
return key;
}
Now I need to explain how it works. Does it use a private key? Where is the key storage? Can anyone help me?
Note: see owlstead's answer for an excellent description of the flaws in your code example
Your encrypt() and decrypt() operations are performing AES encryption and decryption respectively, using Java's JCE libraries. A JCE provider will be selected to perform the actual cryptography - the provider chosen will be the first in the list of providers that offers an implementation of AES. You have defined the algorithm as only "AES", so the mode of operation and padding will be chosen by the provider. If you want to control this, use the form "AES/mode/padding" (see the docs for valid choices)
The getRaw method derives an AES key from a password. The raw bytes of the password provide the seed for a random number generator. The random number generator is then used to generate sufficient key material for a 128-bit AES key. A different password will produce a different seed, which should produce a different stream of random bytes and thus a different key. I suspect this approach is weakened by the lack of entropy present in most people's passwords, leading to a reduced key space and easier attacks.
There is no key storage in your example code. JCE keys are normally persisted using a KeyStore object and the storage mechanism is provider-dependent.
The above piece of code is a bunch of crap. Unfortunately it is frequently used as a code snippet for Android related code (Android code uses the same API as Java, so there is no need for an Android specific example, andt unfortunately it specifically fails on Android).
I'll explain the issues:
Using a SecureRandom as Password Based Key Derivation Function (PBKDF) is completely idiotic. The underlying implementation of the SecureRandom implementation may change. Furthermore, it is not specified by the SecureRandom that calling setSeed() as the first method will replace the seed; it may actually add the seed to the current state - and this is what certain newer android versions do.
Cipher.getInstance("AES") actually uses the provider defaults instead of specifying the mode of operation and padding mode for the given cipher. By default the Sun provider will use ECB mode which is not suitable for encrypting most data.
String.getBytes() - which is used for the password - returns the platform default encoding. Different platforms may have different default encodings. This means that different platforms will generate different keys.
Above code does not add a message authentication code (MAC or HMAC). This may lead to an attacker changing random ciphertext blocks, which leads to random plain text blocks. This may lead to loss of confidentiality as well if padding Oracle attacks apply.
It seems to me that you are a beginner in cryptography. Please use a higher level standard such as RNCryptor compatible code, or use a standard such as Cryptographic Message Syntax (CMS).
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.
I've been having trouble encrypting with an RSA public key. Here is a sample JUnit code that reproduces the problem:
public class CryptoTests {
private static KeyPair keys;
#BeforeClass
public static void init() throws NoSuchAlgorithmException{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = CryptoUtils.getSecureRandom();
keyGen.initialize(2176, random);
keys = keyGen.generateKeyPair();
}
#Test
public void testRepeatabilityPlainRSAPublic() throws EdrmCryptoException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
byte[] plaintext = new byte [10];
Random r = new Random();
r.nextBytes(plaintext);
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted1 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted2 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted3 = rsa.doFinal(plaintext);
assertArrayEquals(encrypted1, encrypted2);
assertArrayEquals(encrypted1, encrypted3);
}
}
The result? The assertion fails.
Why is this behaviour seen here? As far as I remember from my crypto classes, any key can be used for encryption. Yet this is not what happens here.
I've tested the same thing with the private key, and I get a repeatable output.
If, for some reason, RSA encryption with a public key is forbidden, then why am I not getting an exception?
What must I do to get repeatable results?
P.S. My JDK is 1.6.0_22 running on an Ubuntu 10.10 box.
My guess is that it's applying randomized padding, precisely to make it more secure. From the RSA wikipedia page:
Because RSA encryption is a deterministic encryption algorithm – i.e., has no random component – an attacker can successfully launch a chosen plaintext attack against the cryptosystem, by encrypting likely plaintexts under the public key and test if they are equal to the ciphertext. A cryptosystem is called semantically secure if an attacker cannot distinguish two encryptions from each other even if the attacker knows (or has chosen) the corresponding plaintexts. As described above, RSA without padding is not semantically secure.
...
To avoid these problems, practical RSA implementations typically embed some form of structured, randomized padding into the value m before encrypting it. This padding ensures that m does not fall into the range of insecure plaintexts, and that a given message, once padded, will encrypt to one of a large number of different possible ciphertexts.
You can confirm that what is happening is that random padding is being added by initialising your Cipher with the string "RSA/ECB/NoPadding". Now, you should see that the ciphertext is identical in each case (though for reasons stated by another answerer, you shouldn't really do this in practice).
To add extra detail to Jon's answer:
When you do Cipher.getInstance("...") you have a number of options, as you've probably gathered. The Standard Algorithm Names specify what these are.
The one you asked for, RSA is by default RSA under PKCS1, which, to quote the wikipedia article:
There are two schemes for encryption
and decryption:
RSAES-OAEP: improved encryption/decryption scheme; based on
the Optimal Asymmetric Encryption
Padding scheme proposed by Mihir
Bellare and Phillip Rogaway.
RSAES-PKCS1-v1_5: older encryption/decryption scheme as first
standardized in version 1.5 of PKCS#1.
See RSALab's PKCS1 documentation for the detail of said padding schemes.