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
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.
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 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.
I have web services implemented that comunicate between client and server with strings. The problem I'm getting is with the conversion of the encripted byte array to string since I can't convert it back to the original content on server side.
KeyPairGenerator keyGen;
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
String publicKeyPath = new String("publicKeys.txt");
publickey = key.getPublic()
byte[] pubEncoded = key.getPublic().getEncoded();
FileOutputStream fout = new FileOutputStream(publicKeyPath);
fout.write(pubEncoded);
fout.flush();
fout.close();
String privateKeyPath = new String("privateKeys.txt");
byte[] privEncoded = key.getPrivate().getEncoded();
fout = new FileOutputStream(privateKeyPath);
fout.write(privEncoded);
fout.flush();
fout.close();
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
And on each method in client:
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publickey);
byte[] cipherText = cipher.doFinal(str.getBytes());
port.callX(chiperText.toString());
On server side:
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] newPlainText = cipher.doFinal(arg.getBytes());
This gives the padding problem of "Data must start with a zero"
There is only one KeyPair generated, for debug, I tried to encrypt and decript on the same function, and the problem relies on the conversions from byte[] to String.
I don't really wan't to change the passing of arguments to other type since operations are auto generated and all code is to strings. I've tried with different "UTF-8" and "UTF-16Le" but none works :S
Any idea?
Encrypting data directly with asymmetric keys isnt a good idea and it does not work. RSA keys cannot encrypt data bigger than its key length. So, the right way of doing that would be to generate a symmetric key and encrypt the data with the symmetric key and inturn encrypt the symmetric key with the asymmetric key. So, you need to send the encrypted data and the encrypted key both to the other party.
If you try encrypting the data directly with the asymmetric key then your byte[] may not contain the correct data or it may contain nothing at all. This happens if your data is bigger than the key length. For data smaller the key length it works fine but not with bigger data.
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.