RSA Encryption Decryption in Android
Solution 1
In RSA you should use the public key for encryption and the private key for decryption.
Your sample code uses for encryption and decryption the public key - this can not work.
Hence in the decryption part you should initialize the cipher this way:
cipher1.init(Cipher.DECRYPT_MODE, privateKey);
Furthermor your code has a second significant bug:
You are converting a byte array with binary content to a String.
Never ever convert binary data to a String!
Strings are for string characters, not binary data. If you want to pack binary data into a String encode it to printable characters for example using Hex or Base64.
The following example uses the hexadecimal encoder fro org.apache.common.codec package - a third party library with has to be installed.
public byte[] RSAEncrypt(final String plain) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plain.getBytes());
System.out.println("EEncrypted?????" + new String(org.apache.commons.codec.binary.Hex.encodeHex(encryptedBytes)));
return encryptedBytes;
}
public String RSADecrypt(final byte[] encryptedBytes) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
cipher1 = Cipher.getInstance("RSA");
cipher1.init(Cipher.DECRYPT_MODE, privateKey);
decryptedBytes = cipher1.doFinal(encryptedBytes);
decrypted = new String(decryptedBytes);
System.out.println("DDecrypted?????" + decrypted);
return decrypted;
}
Solution 2
My class:
package com.infovale.cripto;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
public class RSA {
KeyPairGenerator kpg;
KeyPair kp;
PublicKey publicKey;
PrivateKey privateKey;
byte[] encryptedBytes, decryptedBytes;
Cipher cipher, cipher1;
String encrypted, decrypted;
public String Encrypt (String plain) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plain.getBytes());
encrypted = bytesToString(encryptedBytes);
return encrypted;
}
public String Decrypt (String result) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
cipher1=Cipher.getInstance("RSA");
cipher1.init(Cipher.DECRYPT_MODE, privateKey);
decryptedBytes = cipher1.doFinal(stringToBytes(result));
decrypted = new String(decryptedBytes);
return decrypted;
}
public String bytesToString(byte[] b) {
byte[] b2 = new byte[b.length + 1];
b2[0] = 1;
System.arraycopy(b, 0, b2, 1, b.length);
return new BigInteger(b2).toString(36);
}
public byte[] stringToBytes(String s) {
byte[] b2 = new BigInteger(s, 36).toByteArray();
return Arrays.copyOfRange(b2, 1, b2.length);
}
}
Solution 3
Here is an example for Android of:
- generating a private/public RSA key pair
- encrypting a string
- decrypting the encrypted string
These methods deal with all the base 64 encoding/decoding.
public void TestEncryptData(String dataToEncrypt) {
// generate a new public/private key pair to test with (note. you should only do this once and keep them!)
KeyPair kp = getKeyPair();
PublicKey publicKey = kp.getPublic();
byte[] publicKeyBytes = publicKey.getEncoded();
String publicKeyBytesBase64 = new String(Base64.encode(publicKeyBytes, Base64.DEFAULT));
PrivateKey privateKey = kp.getPrivate();
byte[] privateKeyBytes = privateKey.getEncoded();
String privateKeyBytesBase64 = new String(Base64.encode(privateKeyBytes, Base64.DEFAULT));
// test encryption
String encrypted = encryptRSAToString(dataToEncrypt, publicKeyBytesBase64);
// test decryption
String decrypted = decryptRSAToString(encrypted, privateKeyBytesBase64);
}
public static KeyPair getKeyPair() {
KeyPair kp = null;
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
kp = kpg.generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
}
return kp;
}
public static String encryptRSAToString(String clearText, String publicKey) {
String encryptedBase64 = "";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKey.trim().getBytes(), Base64.DEFAULT));
Key key = keyFac.generatePublic(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes("UTF-8"));
encryptedBase64 = new String(Base64.encode(encryptedBytes, Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedBase64.replaceAll("(\\r|\\n)", "");
}
public static String decryptRSAToString(String encryptedBase64, String privateKey) {
String decryptedString = "";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey.trim().getBytes(), Base64.DEFAULT));
Key key = keyFac.generatePrivate(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
// encrypt the plain text using the public key
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedBytes = Base64.decode(encryptedBase64, Base64.DEFAULT);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
decryptedString = new String(decryptedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedString;
}
Solution 4
I think the problem is that you should use the same key pair to encrypt and decrypt the cipher. Referring to the JavaDoc:
genKeyPair() This will generate a new key pair every time it is called.
Riddhi Barbhaya
Updated on June 14, 2020Comments
-
Riddhi Barbhaya almost 4 years
I am implementing a demo for RSA Encryption and Decryption in Android. I can Perform Encryption very well, but In Decryption I get an Exception:
>>java.security.InvalidKeyException: unknown key type passed to RSA
.KeyPairGenerator kpg; KeyPair kp; PublicKey publicKey; PrivateKey privateKey; byte [] encryptedBytes,decryptedBytes; Cipher cipher,cipher1; String encrypted,decrypted; public String RSAEncrypt (final String plain) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(1024); kp = kpg.genKeyPair(); publicKey = kp.getPublic(); privateKey = kp.getPrivate(); cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); encryptedBytes = cipher.doFinal(plain.getBytes()); encrypted = new String(encryptedBytes); System.out.println("EEncrypted?????"+encrypted); return encrypted; } public String RSADecrypt (final String result) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { cipher1=Cipher.getInstance("RSA"); cipher1.init(Cipher.DECRYPT_MODE, privateKey); decryptedBytes = cipher1.doFinal(result.getBytes()); decrypted = new String(decryptedBytes); System.out.println("DDecrypted?????"+decrypted); return decrypted; }
And I am calling the function from here:
encrypt.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { try { RSAEncrypt rsaencrypt=new RSAEncrypt(); rsaencrypt.RSAEncrypt(name); result=rsaencrypt.RSAEncrypt(name); Toast.makeText(getBaseContext(), result.toString(),Toast.LENGTH_SHORT).show(); System.out.println("Result:"+result); } catch(Exception e) { e.printStackTrace(); Toast.makeText(getBaseContext(), e.toString(),Toast.LENGTH_LONG).show(); } } }); decrypt.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { { try { RSAEncrypt rsadecrypt=new RSAEncrypt(); rsadecrypt.RSADecrypt(result); ans=rsadecrypt.RSADecrypt(result); System.out.println("Result is"+ans); Toast.makeText(getBaseContext(), ans.toString(),Toast.LENGTH_LONG).show(); } catch(Exception e) { e.printStackTrace(); Toast.makeText(getBaseContext(), e.toString(),Toast.LENGTH_LONG).show(); System.out.println("Exception is>>"+e); } } });
-
Riddhi Barbhaya over 11 years@jaredzhang..I edited my code..but I get the same Exception..Please Review my Edited code..
-
Riddhi Barbhaya over 11 years@jaredzhang..I think you are right..But Problem is that Now I am getting java.lang.NullPointerException..
-
Riddhi Barbhaya over 11 yearsThanks a lot..You are Right..I had done Mistake in That..But Now I am getting Exception>> System.err(416): java.security.InvalidKeyException: unknown key type passed to RSA Please Help me..Thanks in Advance..
-
jaredzhang over 11 yearsyour new code seems unclear, please ensure the private key you are using to decrypt is the same you generated previously.
-
Riddhi Barbhaya over 11 years@jaredzhang..Hi..I have Edited my code again..so that..it seems clear to you..Please help me..and Thanks a lot..And see my question also..Exception I got is also Different as of now....
-
Pascal about 11 yearsThanks for that. Just one remarks: keys are symmetrical. So You can either encode with public then decode with private (make sure to whom you send encrypted data) , OR, encode with private, then decode with public (to make sure who sent you the encrypted data). Doing the 2 (with 2 different key set), allow you to enforce both 'from who' and 'to whom' security aspects.
-
Robert about 11 yearsThe "encode with private" case in known as signature. But you should never directly sign data - always sign only the has otherwise you may run into some attacks (see IT security literature).
-
steveen zoleko over 7 yearsalways sign only what?
-
Robert over 7 years"always sign only the hash" (SHA-256 or SHA-1 or ...)
-
AaA almost 7 yearsThose are class properties, as method accessing those keys are not static, thus it is not necessary for properties to be static. In fact it is a very bad choice to have static properties specially when you might need to create multiple instances of class with different data values.
-
Kiran Maniya almost 6 yearsSystem.out.println("EEncrypted?????" + org.apache.commons.codec.binary.Hex.encodeHexString(encryptedBytes)); should be Replaced with System.out.println(new String (org.apache.commons.codec.binary.Hex.encodeHexString(encryptedBytes))); because there is a library issue. for further info see github.com/jscep/jscep/issues/19
-
Robert almost 6 years@Kiran Maniya considering the linked issue report your comment is also wrong, because it uses
encodeHexString()
instead ofencodeHex()
as mentioned in the issue. -
Franklin over 5 yearsJust to have the terminology right [Confidentiality : Encrypt with Public-Key and Decrypt with Private-Key] [Signature: Sign with Private-Key and Validate with Public-Key] - Is this correct?
-
Robert over 5 years@Franklin correct. Keep in mind that usually you don't use asymmetric crypto for encrypting large data, only for encrypting a generated symmetric key (e.g. AES key).
-
Vir Rajpurohit about 4 years@jaredzhang So this can not be used to send data from one app to another as receiver app will call genKeyPair() and will create different key. Any solution?
-
dave o grady about 4 yearsThis answer is awesome thank u so much for posting it!