user2284140
user2284140

Reputation: 197

RSA Decryption in Android using private Key

I am using encryption - decryption in Android using public and private key respectively. I have both the keys saved in the RAW folder as a file. The encryption works perfectly, but for the decryption it always gives the following error:

java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag

I am enclosing the code snippet I have used:

public class AppUtils {


public static String encryptString(String value, Context context){
    byte[] encodedBytes = null;
    try {
        //Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE,  getPublicKey(context) );
        encodedBytes = cipher.doFinal(value.getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }

    return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
}

public static String decryptString(String value, Context context){
    byte[] decodedBytes = null;
    try {
        //Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
        Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        c.init(Cipher.DECRYPT_MODE,  getPrivateKey(context) );
        decodedBytes = c.doFinal(Base64.decode(value, Base64.DEFAULT));
    } catch (Exception e) {
        e.printStackTrace();
    }

    return new String(decodedBytes);
}


public static PrivateKey getPrivateKey(Context context){

    // reads the key_public key stored in a file
    InputStream is = context.getResources().openRawResource(R.raw.key_private);
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    List<String> lines = new ArrayList<String>();
    String line = null;

    try {


        while ((line = br.readLine()) != null)
            lines.add(line);


        // removes the first and last lines of the file (comments)
        if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size()-1).startsWith("-----")) {
           lines.remove(0);
         lines.remove(lines.size()-1);
        }

        // concats the remaining lines to a single String
        StringBuilder sb = new StringBuilder();
        for (String aLine: lines)
            sb.append(aLine);
        String keyString = sb.toString();

        byte [] encoded = Base64.decode(keyString, Base64.DEFAULT);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);

        PrivateKey myPrivKey = keyFactory.generatePrivate(keySpec);

        return myPrivKey;

    }catch (Exception e){

        e.printStackTrace();
    }
    return null;
}

public static PublicKey getPublicKey(Context context){

    // reads the key_public key stored in a file
    InputStream is = context.getResources().openRawResource(R.raw.key_public);
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    List<String> lines = new ArrayList<String>();
    String line = null;


    try {


        while ((line = br.readLine()) != null)
            lines.add(line);

        // removes the first and last lines of the file (comments)
        if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size()-1).startsWith("-----")) {
            lines.remove(0);
            lines.remove(lines.size()-1);
        }

        // concats the remaining lines to a single String
        StringBuilder sb = new StringBuilder();
        for (String aLine: lines)
            sb.append(aLine);
        String keyString = sb.toString();

        // converts the String to a PublicKey instance
        byte[] keyBytes = Base64.decode(keyString, Base64.DEFAULT);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(spec);

        return key;
    }catch (Exception e){
            e.printStackTrace();
    }

    return null;
}
}

Finally, I call the required function from MainActivity.java like this:

String encryptedString =  AppUtils.encryptString("SHANKAR", MainActivity.this);

Log.d("DX1", " Encrypted String " + encryptedString );


String decryptedString =  AppUtils.decryptString(encryptedString, MainActivity.this);

Log.d("DX1", " decrypted String " + decryptedString );

I see that, I get the encrypted string properly, but at the time of decryption the statement :

PrivateKey myPrivKey = keyFactory.generatePrivate(keySpec);

is throwing me the above error.But I can decrypt the encrypted string using the private key in any online tool. Can someone kindly help me out.

My public key:

-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgEac6cgM4Ch5vY2Rqvzw2ARaNEHv
PCbXWW1nPy3ft8CNFyLoIltwrnouY0azYECclONARh48qQUQ+UG62wNUtciLq9yX
3m0ePE7u/RYmNUnGWok6LMNZK1gGBu6TBnzNjDWi3CuR00xFzZ2TBtUMDowOa/+b
tfGTywGDLPJjgbtPAgMBAAE=
-----END PUBLIC KEY-----

My private key:

-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgEac6cgM4Ch5vY2Rqvzw2ARaNEHvPCbXWW1nPy3ft8CNFyLoIltw
rnouY0azYECclONARh48qQUQ+UG62wNUtciLq9yX3m0ePE7u/RYmNUnGWok6LMNZ
K1gGBu6TBnzNjDWi3CuR00xFzZ2TBtUMDowOa/+btfGTywGDLPJjgbtPAgMBAAEC
gYAAxTd7ukA70NAzmjI+XjZNHdrSGVUTq2fLXMQAsR8lF6T3+YZebwVISfdFTzGu
osaxEANz0v+ZEY1WnT5EdAkjqwtYauj0tDnYmuciju2uVnEYLPOaOvpkmM7e9y+a
NWTvG/C0qAXtTT/EJgAwfCyrhuigyxzwUIyqhW2xgQ8MKQJBAIgM5xPKd30HU98t
jOHzouwSjsv5NIFOM9RnLPGA6HazQGu/0h2S27UnzaU8KKjln1X2q22GMa1cSvIL
q06e+MsCQQCE3oZudjOBgJaKtk7iETiIIoVVk3K1RMSGT/56SrglB1K1kNuFOpg+
obs+8j366gQk47ZgaIZwSRfro0VhTysNAkBiLEVWv6QHgZEhG6Jsrb1j8mQ+hd5A
bGj0HVuODYIxnVmgJvP8yStnhohbcpS4g7G9e1jqmIoiWdXu4ULFYeuPAkAIcKYz
cBi3ejaV2xzJqXRg2WiE1hfsQdEGAyDUHdjyqTNsyyXWobE4EUf2qKadQK5AtaJJ
H3qiuVHmqvlmRAQlAkB4Cl2nYBpK1IdusfwtjfaKppLa/r5k1D35cybxLn1uS3Xz
wwqRUuXrDkb4+TCD3B7Lkuym/kfFE2iIpANAVkeN
-----END RSA PRIVATE KEY-----

Upvotes: 2

Views: 3642

Answers (1)

Ognev Zair
Ognev Zair

Reputation: 1626

I had the same problem and tried a couple of days to figure out how it works and finally, I got it!

Your private key not in PKCS#8 format.

A PKCS8 key starts with

-----BEGIN PRIVATE KEY-----

instead of

-----BEGIN RSA PRIVATE KEY-----.

Java works with PKCS#8 formats.

So you have two ways to fix it:

First:

Regenerate/convert your key on the server side in/to PCKS#8 format and use it in android application

Or Second:

Convert your private key in the application. For this way you need include third party library to the project.

Add this line into your app Gradle

compile 'com.madgag.spongycastle:core:1.56.0.0'

And replace your getPrivateKey method with this:

public static PrivateKey getPrivateKey(Context context) throws 
    GeneralSecurityException, IOException {
            InputStream is = context.getResources().openRawResource(R.raw.rsa_2048_priv);
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            List<String> lines = new ArrayList<String>();
            String line = null;

                while ((line = br.readLine()) != null)
                    lines.add(line);

                if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size() - 1).startsWith("-----")) {
                    lines.remove(0);
                    lines.remove(lines.size() - 1);
                }

                StringBuilder sb = new StringBuilder();
                for (String aLine : lines)
                    sb.append(aLine);
                String keyString = sb.toString();

                byte[] encodedPrivateKey = Base64.decode(keyString, Base64.DEFAULT);

                try {
                    ASN1Sequence primitive = (ASN1Sequence) ASN1Sequence
                            .fromByteArray(encodedPrivateKey);
                    Enumeration<?> e = primitive.getObjects();
                    BigInteger v = ((ASN1Integer) e.nextElement()).getValue();

                    int version = v.intValue();
                    if (version != 0 && version != 1) {
                        throw new IllegalArgumentException("wrong version for RSA private key");
                    }
                    /**
                     * In fact only modulus and private exponent are in use.
                     */
                    BigInteger modulus = ((ASN1Integer) e.nextElement()).getValue();
                    BigInteger publicExponent = ((ASN1Integer) e.nextElement()).getValue();
                    BigInteger privateExponent = ((ASN1Integer) e.nextElement()).getValue();

                    RSAPrivateKeySpec spec = new RSAPrivateKeySpec(modulus, privateExponent);
                    KeyFactory kf = KeyFactory.getInstance("RSA");
                    return kf.generatePrivate(spec);
                } catch (IOException e2) {
                    throw new IllegalStateException();
                } catch (NoSuchAlgorithmException e) {
                    throw new IllegalStateException(e);
                } catch (InvalidKeySpecException e) {
                    throw new IllegalStateException(e);
                }

        }

So it will convert private key to the PKCS#8, and you can decode your text without error of wrong tag

Hope it help :)

Here the links where I found solutions:

Getting RSA private key from PEM BASE64 Encoded private key file

JAVA RSA Decryption not working, throws InvalidKeySpecException

Convert PEM traditional private key to PKCS8 private key

How to include the Spongy Castle JAR in Android?

Upvotes: 0

Related Questions