RSA Encryption Decryption in Android

74,383

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.
Share:
74,383
Riddhi Barbhaya
Author by

Riddhi Barbhaya

Updated on June 14, 2020

Comments

  • Riddhi Barbhaya
    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
    Riddhi Barbhaya over 11 years
    @jaredzhang..I edited my code..but I get the same Exception..Please Review my Edited code..
  • Riddhi Barbhaya
    Riddhi Barbhaya over 11 years
    @jaredzhang..I think you are right..But Problem is that Now I am getting java.lang.NullPointerException..
  • Riddhi Barbhaya
    Riddhi Barbhaya over 11 years
    Thanks 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
    jaredzhang over 11 years
    your new code seems unclear, please ensure the private key you are using to decrypt is the same you generated previously.
  • Riddhi Barbhaya
    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
    Pascal about 11 years
    Thanks 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
    Robert about 11 years
    The "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
    steveen zoleko over 7 years
    always sign only what?
  • Robert
    Robert over 7 years
    "always sign only the hash" (SHA-256 or SHA-1 or ...)
  • AaA
    AaA almost 7 years
    Those 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
    Kiran Maniya almost 6 years
    System.out.println("EEncrypted?????" + org.apache.commons.codec.binary.Hex.encodeHexString(encrypte‌​dBytes)); should be Replaced with System.out.println(new String (org.apache.commons.codec.binary.Hex.encodeHexString(encrypt‌​edBytes))); because there is a library issue. for further info see github.com/jscep/jscep/issues/19
  • Robert
    Robert almost 6 years
    @Kiran Maniya considering the linked issue report your comment is also wrong, because it uses encodeHexString() instead of encodeHex() as mentioned in the issue.
  • Franklin
    Franklin over 5 years
    Just 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
    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
    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
    dave o grady about 4 years
    This answer is awesome thank u so much for posting it!