RSA encryption in Android - java

I am writing a program which employs RSA in Android. I have the following problem:
I am getting the RSA keys:
KeyPair kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
Using the encryption function to encrypt a test string:
String test ="test";
byte[] testbytes = test.getBytes();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherData = cipher.doFinal(testbytes);
String s = new String(cipherData);
Log.d("testbytes after encryption",s);
In the decryption function i am decrypting the data back to get the original string
Cipher cipher2 = Cipher.getInstance("RSA");
cipher2.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainData = cipher.doFinal(cipherData);
String p = new String(plainData);
Log.d("decrypted data is:",p);
The data in 'p' printed out in the log does not match the original string "test" . Where am I going wrong in this?

Here an example on how to do it, BUT in practice,
You can't really encrypt and decrypt whole files with just RSA. The
RSA algorithm can only encrypt a single block, and it is rather slow
for doing a whole file.
You can encrypt the file using
3DES or AES, and then encrypt the AES key using intended recipient's
RSA public key.
Some code:
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
kpg.initialize(1024);
KeyPair keyPair = kpg.generateKeyPair();
PrivateKey privKey = keyPair.getPrivate();
PublicKey pubKey = keyPair.getPublic();
// Encrypt
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String test = "My test string";
String ciphertextFile = "ciphertextRSA.txt";
InputStream fis = new ByteArrayInputStream(test.getBytes("UTF-8"));
FileOutputStream fos = new FileOutputStream(ciphertextFile);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
byte[] block = new byte[32];
int i;
while ((i = fis.read(block)) != -1) {
cos.write(block, 0, i);
}
cos.close();
// Decrypt
String cleartextAgainFile = "cleartextAgainRSA.txt";
cipher.init(Cipher.DECRYPT_MODE, privKey);
fis = new FileInputStream(ciphertextFile);
CipherInputStream cis = new CipherInputStream(fis, cipher);
fos = new FileOutputStream(cleartextAgainFile);
while ((i = cis.read(block)) != -1) {
fos.write(block, 0, i);
}
fos.close();
}

Related

JAVA separate AES key, initilization vector and data from encrypted file with these 3 combinations used during encryption

I have created encrypted file with combination of AES encryption, intilizatio vector and original data.
So my encrypted file contains above 3 elements in encrypted form.
Now during decryption i am stuck at separating all these 3 elements again, moreover i have to use AES key length hard coded during decryption that was generated during encryption.
public static void encrypt() throws Exception {
// RSA with ECB mode
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, generatePublicKey(readKeysFromFile("My_public.pub")));
// AES key generator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, srandom);
SecretKey skey = kgen.generateKey();
// Initialization vector 16 byte
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
try (FileOutputStream out = new FileOutputStream("dataFile" + ".enc")) {
{
byte[] b = cipher.doFinal(skey.getEncoded());
out.write(b);
System.err.println("AES Key Length: " + b.length);
}
out.write(iv);
System.err.println("IV Length: " + iv.length);
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
File inputDataFile = new File("dataFile.xml");
try (DataInputStream in = new DataInputStream(new FileInputStream(inputDataFile))) {
byte[] buffer = new byte[(int)inputDataFile.length()];
in.readFully(buffer);
in.close();
byte[] encryptedData = ci.doFinal(buffer);
out.write(encryptedData);
out.close();
}
}
}
public static void decryptRSAAES() throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, generatePrivateKey(readKeysFromFile("My_private.key")));
File file2 = new File("dataFile.enc");
RandomAccessFile raf = new RandomAccessFile(file2, "r");
// length of AES key
byte[] c = new byte[384];
// read the AES key from file
raf.read(c, 0 , 384);
byte[] fileContent = Files.readAllBytes(file2.toPath());
byte[] keyb = cipher.doFinal(c);
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
// read the initializatoin vector
byte[] iv = new byte[128/8];
raf.seek(384);
raf.read(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
raf.seek(400);
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.DECRYPT_MODE, skey, ivspec);
try (FileOutputStream out = new FileOutputStream("decryptedFileTest"+".xml")){
byte[] decryptedData = ci.doFinal(fileContent);
out.write(decryptedData);
out.close();
//processDecryptFile(ci, in, out);
}
}
Actual result: decrypted file is created with AES key and original plain data
Expected result: write only original plain data in output removing AES and initilization vector.
Let's simplify this and use the functions newly available in Java's InputStream classes:
public static void encrypt(RSAPublicKey publicKey) throws Exception {
try (FileOutputStream out = new FileOutputStream("dataFile" + ".enc")) {
// --- RSA using PKCS#1 v1.5 padding
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// --- AES key generator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey skey = kgen.generateKey();
// --- write encrypted AES key
byte[] encryptedSKey = cipher.doFinal(skey.getEncoded());
out.write(encryptedSKey);
// --- Initialization vector 16 byte
SecureRandom srandom = new SecureRandom();
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
// --- write IV
out.write(iv);
// --- initialize AES cipher
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
// --- convert file by copying to memory
try (FileInputStream in = new FileInputStream("dataFile.xml")) {
byte[] buffer = in.readAllBytes();
byte[] encryptedData = ci.doFinal(buffer);
out.write(encryptedData);
}
}
}
public static void decrypt(RSAPrivateKey privateKey) throws Exception {
try (FileInputStream in = new FileInputStream("dataFile" + ".enc")) {
// --- RSA using PKCS#1 v1.5 padding
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// --- read encrypted AES key
byte[] encryptedSKey = in.readNBytes(determineEncryptionSizeInBytes(privateKey));
byte[] decryptedSKey = cipher.doFinal(encryptedSKey);
SecretKey skey = new SecretKeySpec(decryptedSKey, "AES");
// --- Initialization vector 16 byte
byte[] iv = in.readNBytes(128 / Byte.SIZE);
IvParameterSpec ivspec = new IvParameterSpec(iv);
// --- initialize AES cipher
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.DECRYPT_MODE, skey, ivspec);
// --- convert file by copying to memory
File outputDataFile = new File("dataFile.xml2");
try (FileOutputStream out = new FileOutputStream(outputDataFile)) {
byte[] buffer = in.readAllBytes();
byte[] decryptedData = ci.doFinal(buffer);
out.write(decryptedData);
}
}
}
private static int determineEncryptionSizeInBytes(RSAPrivateKey privateKey) {
return (privateKey.getModulus().bitLength() + Byte.SIZE - 1) / Byte.SIZE;
}
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(384 * Byte.SIZE);
KeyPair pair = kpg.generateKeyPair();
encrypt((RSAPublicKey) pair.getPublic());
decrypt((RSAPrivateKey) pair.getPrivate());
}
As you can see the code is now very much more like a mirror image. I have simply copied the encryption code and then made changes to it. As you can see it now uses fewer classes by relying on InputStream#readAllBytes() (since Java 9) and InputStream#readNBytes() (since Java 11).
Note that you generally want to stream the file using a smaller buffer. As the entire plaintext and ciphertext are currently buffered your application uses much more memory than required. To encrypt data using streams you can rely on CipherInputStream and CipherOutputStream.
Needless to say the exception handling needs improvement, I've simply looked at the best way to solve your current issue. Please take another look at it when you've got things working (get things working, get things right, get things optimized).

RSA Encrypting in c# and Decrypting data in java

I'm trying to encrypt data in my C# client with public RSA key from the Java server. To do this I generated KeyPair in Java
KeyPairGenerator.java
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
Key pub = kp.getPublic();
Key pvt = kp.getPrivate();
String outFile = "rsa_key";
FileOutputStream outPvt = new FileOutputStream(outFile + ".key");
outPvt.write(pvt.getEncoded());
outPvt.close();
FileOutputStream outPub = new FileOutputStream(outFile + ".pub");
outPub.write(pub.getEncoded());
outPub.close();
It gave me two files (rsa_key.key and rsa_key.pub) and then I encoded public key with Base64 so C# could read it:
PublicKeyBase64.java
String keyFile2 = "rsa_key.pub";
Path path2 = Paths.get(keyFile2);
byte[] bytes2 = Files.readAllBytes(path2);
X509EncodedKeySpec ks2 = new X509EncodedKeySpec(bytes2);
KeyFactory kf2 = KeyFactory.getInstance("RSA");
PublicKey pub = kf2.generatePublic(ks2);
Base64.Encoder encoder = Base64.getEncoder();
String outFile = "en_rsa_key";
Writer out = new FileWriter(outFile + ".pub");
out.write(encoder.encodeToString(pub.getEncoded()));
out.close();
Then I created my C# class to encrypt data
Encrypter.cs
class Encrypter
{
private const string PATH = "..\\..\\key\\en_rsa_key.pub";
private string PublicKey;
public Encrypter()
{
PublicKey = File.ReadAllText(PATH);
Console.WriteLine(PublicKey.Length);
}
public string encryptData(string dataToEncrypt)
{
Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(PublicKey));
DerSequence publicKeySequence = (DerSequence)obj;
DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());
DerInteger modulus = (DerInteger)publicKey[0];
DerInteger exponent = (DerInteger)publicKey[1];
RsaKeyParameters keyParameters = new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(parameters);
//Console.WriteLine(dataToEncrypt);
byte[] encryptedData = rsa.Encrypt(Encoding.UTF8.GetBytes(dataToEncrypt), true);
//Console.WriteLine(Convert.ToBase64String(encryptedData));
return Convert.ToBase64String(encryptedData);
}
}
And at last my Decrypter class
public class Decrypter {
private static final String PATH = "I:\\rsa_key.key";
private File privateKeyFile = new File(PATH);
private RSAPrivateKey privateKey;
public Decrypter() throws Exception {
DataInputStream dis = new DataInputStream(new FileInputStream(privateKeyFile));
byte[] privateKeyBytes = new byte[(int)privateKeyFile.length()];
dis.read(privateKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);
}
public String decrypt(String data) throws Exception{
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return new String(cipher.doFinal(data.getBytes()));
}
}
I send encrypted data in C# via WebSocket and receive it without any problem because data while sending and receiving is the same. The problem is that data length is 344 characters (bytes) so when I decrypt my data it shows me and error: javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes
I'm using 2048 bit key so it's 256B - 11B for padding that's why it's 245B. But the problem is that it always generates 344B and it doesn't depend on message to encode length. For example encoding "A" gives 344B and "Hello world" also 344B.
In this topic people say to use symmetric key, Encrypt the data with the symmetric key and then Encrypt the symmetric key with rsa. But it won't help because any encrypted string in my code has 344B size, so I won't be able to decrypt encrypted symmetric key.
What's wrong with my code?

BadPaddingException using SecretKey generated by DiffieHellman

I have seen a lot of questions/answers but no one works for me. The problem is that, when I want to decrypt a text, I go throw BadPaddingException and doesn't know why.
Here is my code to encrypt text:
KeyAgreement keyAgreement = this.getSecretKeyAgreement(publicOtherUserKey, privateOwnKey);
SecretKey secretKey = new SecretKeySpec(keyAgreement.generateSecret(), "AES");
Cipher aesCipher = null;
aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] byteDataToEncrypt = text.getBytes();
byte[] byteCipherText = aesCipher.doFinal(byteDataToEncrypt);
byte[] encodedBytes = Base64.encodeBase64(byteCipherText);
textEncrypted = new String(encodedBytes);
Where "publicOtherUserKey" and "privateOwnKey" where generated using Diffie-Hellman Protocol.
Here is the code that decrypt the text, that goes throw BadPaddingException
KeyAgreement keyAgreement = this.getSecretKeyAgreement(publicOtherUserKey, privateOwnKey);
byte[] encodedBytes = text.getBytes();
SecretKey secretKey = new SecretKeySpec(keyAgreement.generateSecret(), "AES");
byte[] decodedBytes = Base64.decodeBase64(encodedBytes);
Cipher decrypt = Cipher.getInstance("AES");
decrypt.init(Cipher.DECRYPT_MODE, secretKey);
textDecrypted = new String(decrypt.doFinal(decodedBytes));
Where "publicOtherUserKey" and "privateOwnKey" where generated using Diffie-Hellman Protocol, and "text" is the encrypted text.
Can you help me?
EDIT
It's important to mention that all the keys and texts are encoded in Base64
EDIT 2
Code for Start key exchange with Diffie-Hellman
int bitLength = 256; // 256 bits
SecureRandom rnd = new SecureRandom();
BigInteger p = BigInteger.probablePrime(bitLength, rnd);
BigInteger g = BigInteger.probablePrime(bitLength, rnd);
DHParameterSpec dhParams = new DHParameterSpec(p, g);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH", "BC");
keyGen.initialize(dhParams, new SecureRandom());
KeyAgreement aKeyAgree = KeyAgreement.getInstance("DH", "BC");
KeyPair aPair = keyGen.generateKeyPair();
aKeyAgree.init(aPair.getPrivate());
byte[] aPairPrivateKey = aPair.getPrivate().getEncoded();
byte[] encodedBytesPrivateKey = Base64.encodeBase64(aPairPrivateKey);
String privateKey = new String(encodedBytesPrivateKey);
byte[] aPairPublicKey = aPair.getPublic().getEncoded();
byte[] encodedBytesPublicKey = Base64.encodeBase64(aPairPublicKey);
String publicKey = new String(encodedBytesPublicKey);
Where "publicKey" and "privateKey" are the keys to later generate the secret key. "publicKey" is the Key sended to the other user. "p" and "g" are the numbers to generate the Key too.
The code for complete the exchange:
DHParameterSpec dhParams = new DHParameterSpec(p, g);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH", "BC");
keyGen.initialize(dhParams, new SecureRandom());
KeyAgreement bKeyAgree = KeyAgreement.getInstance("DH", "BC");
KeyPair bPair = keyGen.generateKeyPair();
bKeyAgree.init(bPair.getPrivate());
byte[] userPublicBytesBase64 = base64EncodedPublicKey.getBytes();
byte[] userPublicKey = Base64.decodeBase64(userPublicBytesBase64);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(userPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance("DH");
PublicKey aPublicKey = keyFactory.generatePublic(keySpec);
bKeyAgree.doPhase(aPublicKey, true);
final byte[] bPairPrivateKey = bPair.getPrivate().getEncoded();
byte[] encodedBytesPrivateKey = Base64.encodeBase64(bPairPrivateKey);
String privateKey = new String(encodedBytesPrivateKey);
final byte[] bPairPublicKey = bPair.getPublic().getEncoded();
byte[] encodedBytesPublicKey = Base64.encodeBase64(bPairPublicKey);
String publicKey = new String(encodedBytesPublicKey);
"base64EncodedPublicKey" is the Key generated in the first block of Code ("publicKey"), and "p" and "g" are too the primes generated in the first block of Code.

Why do I get a BadPaddingException from RSA decryption?

I used RSA algorithm to encrypt and decrypt. When I encrypt a string, it's working properly. When I decrypt, I get an error. Below, I post my code.
public final String modulusString ="..............";
public final String publicExponentString = "AQAB";
/* Encryption */
byte[] modulebytes = Base64.decode(modulusString);
byte[] exponentbytes = Base64.decode(publicExponentString);
BigInteger module = new BigInteger(1,modulebytes);
BigInteger publicexponent = new BigInteger(1,exponentbytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(module, publicexponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = EncryptionValue.getBytes("UTF-8");
byte[] cipherData = cipher.doFinal( plainBytes );
String encryptedString = Base64.encode(cipherData);
return encryptedString;
/* Decryption */
byte[] modulebytes = Base64.decode(modulusString);
byte[] exponentbytes = Base64.decode(publicExponentString);
BigInteger modulus = new BigInteger(1, modulebytes );
BigInteger exponent = new BigInteger(1, exponentbytes);
RSAPrivateKeySpec rsaPrivKey = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(rsaPrivKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] base64String = Base64.decode(DecryptionValue);
byte[] plainBytes = new String(base64String).getBytes("UTF-8");
plainBytes = cipher.update(plainBytes);
byte[] values = cipher.doFinal(plainBytes);
return new String(values, "UTF-8");
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
at cryptocodefinal.CryptoCodeFinal.DecryptionValue(CryptoCodeFinal.java:79)
at cryptocodefinal.CryptoCodeFinal.main(CryptoCodeFinal.java:148)
You appear to be decrypting with the public key. That won't work. You need to need to decrypt with the private exponent that goes with the public exponent used for encryption.
You can't decrypt without the private key. That is the point of asymmetric cryptography.

RSA Encryption Problem

Basically, I'm trying to have an encrypted data flow between Java client and a c# server.
Before jumping into the deep water of having a multi platform encryption working, I'm trying to make a simple encryption app but I'm stuck at the very beginning.
I have the following simple code:
String text = "hello";
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.genKeyPair();
Key publicKey = kp.getPublic();
Key privateKey = kp.getPrivate();
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherData = cipher.doFinal(text.getBytes());
cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] textData = cipher.doFinal(text.getBytes());
String decrypted = new String(textData);
System.out.println(decrypted);
No exception is thrown but I don't get the original "hello" text after the decryption.
Any ideas?
10x a lot
This looks fishy:
byte[] textData = cipher.doFinal(text.getBytes());
Did you mean:
byte[] textData = cipher.doFinal(cipherData);

Categories

Resources