RSA Encryption Decryption in Android

AndroidRsaPublic Key-EncryptionEncryption

Android Problem Overview


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);
					}
			}
		});

Android Solutions


Solution 1 - Android

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 - Android

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 - Android

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 - Android

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.

Solution 5 - Android

when using RSAEcvypt method, its fill PublicKey and private key. And when your Decrypt your generated byte[], your publicKey and privateKey being NULL. Because of that you get this error.

You should use your keys static;

enter code here

KeyPairGenerator kpg;
KeyPair kp;
static PublicKey publicKey;
static PrivateKey privateKey;
byte [] encryptedBytes,decryptedBytes;
Cipher cipher,cipher1;
String encrypted,decrypted;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRiddhi BarbhayaView Question on Stackoverflow
Solution 1 - AndroidRobertView Answer on Stackoverflow
Solution 2 - AndroidElton da CostaView Answer on Stackoverflow
Solution 3 - AndroidsebView Answer on Stackoverflow
Solution 4 - AndroidjaredzhangView Answer on Stackoverflow
Solution 5 - AndroidYiğitView Answer on Stackoverflow