Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: decrypt RSA text using a Public key stored in a file

I've been several days trying to do it without success.

There are plenty of similar questions here in StackOverflow and even two of them are exactly the same as mine but unanswered and unresolved: 1) Convert PHP RSA PublicKey into Android PublicKey 2) Android: how to decrypt an openssl encrypted file with RSA key?

My scenario: I have some text encrypted using RSA (not encrypted by me). I have a "public.key" file in my res/raw folder with the public key needed to decrypt it (the public key related to the private key used to encrypt the message), with a format like the following example: enter image description here

I see a lot of examples of how to decrypt a RSA text, like the following one:

public static byte[] decryptRSA( PublicKey key, byte[] text) throws Exception
      { 
          byte[] dectyptedText = null;

          Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
          cipher.init(Cipher.DECRYPT_MODE, key);
          dectyptedText = cipher.doFinal(text);
          return dectyptedText;
      }

But my question is, how to get the proper PublicKey instance from the file? No examples of this.

If I simply try:

    InputStream is = getResources().openRawResource(R.raw.public);
    DataInputStream dis = new DataInputStream(is);
    byte [] keyBytes = new byte [(int) is.available()];
    dis.readFully(keyBytes);
    dis.close();
    X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePublic(spec);

I get an InvalidKeyException in the return sentence. Would I need to decode Hex or Base64? Aren't the first and last lines of the public key file a problem (the ones with "----BEGIN PUBLIC KEY----" and so)?

Maybe we could get the answer of this properly for the first time in StackOverflow:-)

like image 425
thelawnmowerman Avatar asked Jul 18 '12 01:07

thelawnmowerman


People also ask

Can you decrypt RSA with public key?

Once the sender has the public key of their recipient, they can use it to encrypt the data that they want to keep secure. Once it has been encrypted with a public key, it can only be decrypted by the private key from the same key pair. Even the same public key can't be used to decrypt the data.

Can you decrypt a file with a public key?

Data encrypted with the public key can only be decrypted with the private key. Because of this use of two keys instead of one, public key cryptography is also known as asymmetric cryptography. It is widely used, especially for TLS/SSL, which makes HTTPS possible.

How do I decrypt RSA encrypted text?

As RSA is asymmetric encryption technique, if text is encrypted using public key then for decryption we should use the private key and vice versa. Select the Decryption Algorithm. Some Algorithms need to have key size greater than 512 bits. This should be the same algorithm you had used during encryption.


2 Answers

Finally solved!!! Drums, trumpets and a symphony of enchanting sounds!!!

public static byte[] decryptRSA(Context mContext, byte[] message) throws Exception { 

    // reads the public key stored in a file
    InputStream is = mContext.getResources().openRawResource(R.raw.sm_public);
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    List<String> lines = new ArrayList<String>();
    String line = null;
    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();
    Log.d("log", "keyString:"+keyString);

    // converts the String to a PublicKey instance
    byte[] keyBytes = Base64.decodeBase64(keyString.getBytes("utf-8"));
    X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PublicKey key = keyFactory.generatePublic(spec);

    // decrypts the message
    byte[] dectyptedText = null;
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, key);
    dectyptedText = cipher.doFinal(Base64.decodeBase64(message));
    return dectyptedText;
}

The solution was to Base64 decode not only the public key read from the file, but also the crypted message itself!

By the way, I read the public key from the file the way @Nikolay suggested (tnx again man).

Thank you all very much for your help. StackOverflow rocks!

like image 174
thelawnmowerman Avatar answered Oct 26 '22 16:10

thelawnmowerman


You are missing a key point -- public and private keys are separate, and you cannot calculate one based on the other. That is kind of the point of public key encryption. Issues with using raw RSA aside, if you have something encrypted with the public key, you need to have the corresponding private key to decrypt it. And vice versa. So if you have public key file, you can only get a public key from it. That would be only useful if your data was encrypted with the corresponding private key.

As for the actual exception: remove the '---' lines at the start and end, the use Base64.decode() to get a byte array, and use this to create your X509EncodedKeySpec. One way to do it -- use something like a BufferedReader to read line by line, ignore the '---' lines and concat the rest into one big String.

like image 23
Nikolay Elenkov Avatar answered Oct 26 '22 16:10

Nikolay Elenkov