18

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:-)

Community
  • 1
  • 1
thelawnmowerman
  • 11,956
  • 2
  • 23
  • 36
  • 1
    If you use more precise terminology you may make more progress. RSA has two uses. 1) encryption, in which a small number of bytes (usually a symmetric key) are *encrypted* with a public key and then *decrypted* with a private key. Your `decryptRSA` method above is for that case. 2) signing, in which a hash of a message is *signed* with a private key and then *verified* with a public key. This sounds like the case you want to support. – President James K. Polk Jul 18 '12 at 11:27
  • 1
    Hi @GregS, tnx for answering! I have edited my question. As you can see, I don't want to encrypt, and I don't want to verify. I want to decrypt as the iOS version of the app does, just using the public key of the private key used to encrypt the message. In theory that's possible, even not strange at all. – thelawnmowerman Jul 18 '12 at 12:17
  • What would be the point then if anyone can decrypt it? – President James K. Polk Jul 18 '12 at 21:34
  • 1
    The point is: get the original message. I can't control the web server, but I need to read the messages it sends to my app, so I need this system just to read them. – thelawnmowerman Jul 19 '12 at 00:06
  • As James also mentioned, using `RSA`, you need to encrypt with public key and decrypt with private key. the purpose is, what you encrypt only can be opened by the the owner of private key and not anyone else. I'm not sure how in theory it is possible to encrypt with private key and decrypt with public key and it is even strange after all – AaA Sep 19 '17 at 01:34
  • I believe encrypting with private key and decrypting with public key is actually creating a weaker cipher with shorter key length – AaA Sep 19 '17 at 01:48
  • And I believe you didn't even read the question;-) I didn't encrypt the message, I have nothing to do with the server of the messages. They offer an API with 1) encrypted messages using a private key and 2) the related public key. As the developer of the Android app (nothing more) I was asked to make use of that, so I just decrypt the messages using the public key. And it works, as you can see in the approved answer 5 years ago. I encourage you to open another Q/A thread to discuss about how to make proper use of public/private keys for API developers, if you want, but totally irrelevant here. – thelawnmowerman Oct 20 '17 at 23:30

4 Answers4

15

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!

thelawnmowerman
  • 11,956
  • 2
  • 23
  • 36
  • 1
    This creates a message that anyone can read. There's little point in encrypting it because anyone can access the public key therefore, anyone can decrypt the message. Effectively all your doing is signing the message. You are not actually making your message secret by using encryption. It only verifies that this message came from the holder of the private key. – chubbsondubs Jul 18 '12 at 16:56
  • 1
    Well you did say you don't want to verify the message which is misleading because that's exactly what you wanted to do. This is more for other people reading your question in the future. I think the continued use of the term encrypt and decrypt implies secrecy and with you saying NO to verification it's further confusing. You should've just said I want to verify the signature from the server, and used one of the numerous signature verification algorithms. Signature class does this already for you so you don't have to role it on your own as you did. – chubbsondubs Jul 18 '12 at 18:11
  • If you know any better way to get the original message from a server that I don't control and just sends to my app two things: 1) encrypted messages 2) and the public key, please tell me how and I will give it a try. I am really interested, in fact. – thelawnmowerman Jul 19 '12 at 00:39
  • byte[] message = ...; byte[] signature = ...; verifier = Signature.getInstance ("SHA1WithRSA") verifier.initVerify( publicKey ); verifier.update (message, 0, message.length); if( verifier.verify( signature ) ) { System.out.println("Verified!") } else { System.out.println("Not verified!"); } – chubbsondubs Jul 19 '12 at 00:53
  • Thanks for this, exactly what I needed. Totally agree you've mixed up the terminology a bit but I never understood why verify can't return you the deciphered data. – Michael Mar 06 '14 at 00:23
  • It's funny how I try to give a detailed explanation stating that the only point of all this is to get the ORIGINAL MESSAGE, and some people acts as if they don't even read the only and final purpose, xD, just to feel they are right and try to prove others are wrong, for who knows what reason... A method that returns "Verified!" or "Nor verified!" is totally useless for the objective here. You can write a method that solve equations, of course, but still has nothing to do with this question. Nobody has asked for verifications here, just you ;-P – thelawnmowerman Oct 20 '17 at 23:41
  • how do you resolve `Base64.decodeBase64`in 2018 though? – Ege Kuzubasioglu Jan 16 '18 at 13:42
  • +Ege, have you tried `Base64.decode(message, Base64.DEFAULT)`? – thelawnmowerman Jan 16 '18 at 14:06
3

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.

Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
  • Hi @Nikolay, tnxs for answering! Don't worry, I understand the theory, I have on my file the public key file *needed* to decrypt the message. A web service sends to my app the crypted message, and I already have stored their public key, the one corresponding with their private key used to crypt the message. I will try your proposal and post here the results. – thelawnmowerman Jul 18 '12 at 10:27
1

Public keys can only encrypt data. Public keys cannot decrypt data. You can only decrypt data using the private key. The whole point is you can hand out the public key to any and everyone and they can send you encrypted messages that only the holder of the private key can see.

You really need to be very careful with using encryption technology. I fear you are simply going to distribute the private key to all of your devices which will weaken your security as everyone will have the same private key. So if I want to crack your security I just go to google play and download your app and pull the private key out of your app. Viola I can see everything.

So you have your answer why it won't work, but you need advice now about design which I can't give you with knowing why you are using encryption. What are you hiding?

Update:

Sounds like you are trying to perform encryption AND signature verification like how RSA works, but you are confused how that actually works. For that you need TWO sets of private/public keys. One set of keys for the client and one set of keys for the server.

The web server would send its public key to the client. The client could send an authenticated and encrypted message to the server by using the server's public key and then signing that message using the client's private key. And vice versa for the server. The server would use the client's public key to encrypt a message and sign it with his private key to send a message to the client. The client could then decrypt the message with the client's private key and verify the signature using the server's public key.

Now are you re-implementing SSL? Stop it. Use SSL.

Here is how SSL achieves secure channel. The client receives the PUBLIC key from the web server, and 1 or more names for symmetric encryption algorithms. It picks an algorithm they share in common, then generates a secret key to use for all messages going forward. It ENCRYPTS that secret key with the web server's public key and sends that along with the algorithm it selected. The web server DECRYPTS using the PRIVATE key to get the shared secret key. After that all encryption is symmetric encryption using the shared secret which is much faster than asymmetric encryption.

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138
  • 5
    Re: 'Public keys cannot decrypt data' Really? Since when? How does RSA signature verification work then? There is no such restriction for RSA. What you 'encrypt' with one key can always be 'decrypted' with the other. – Nikolay Elenkov Jul 18 '12 at 03:27
  • Hi @chubbard, tnx for answering! Maybe I didn't explain it properly. I understand the theory pretty well. A web service encrypts a message with his private key, and sends the result to my app. In my app I have already stored the public key of the web service (not mine), and that's all I need to decrypt the message. That's the point of the private/public key. I don't know the private key of the web service. I don't even have or need private/public keys of my own. Just their public key to decrypt their message. – thelawnmowerman Jul 18 '12 at 10:33
  • 1
    @Nikolay Yes public keys CANNOT decrypt data (see wikipedia). Signature verification does not involve encryption/decryption, but does use public/private keys. A signature algorithm produces a signature given a message and a private key. This algorithm does not perform encryption. It's easier to think of it like a hash. Then a signature verification algorithm verifies the message was signed with the corresponding private key given the message, the signature, and the public key. Signature verification verifies that this message originated from someone holding the private key. – chubbsondubs Jul 18 '12 at 15:36
  • 1
    OK, so how exactly do you 'produce a signature given a message and an RSA private key'? What cryptographic operation(s) do you perform and in what order? – Nikolay Elenkov Jul 18 '12 at 16:34
  • @thelawnmowerman See my update. You don't understand the theory as well as you think. http://en.wikipedia.org/wiki/Public-key_cryptography – chubbsondubs Jul 18 '12 at 16:49
  • @chubbard I don't agree man. My server holds the private key (nobody else), and my app holds the public key. My server encrypts messages with the private key, sends them to the devices, and the devices decrypt them with the public key to get the original messages. You can call it just "verify", of course, since anyone with the public key could read the messages, but the process itself needs to decrypt a message. It is actually working, I use Cipher with DECRYPT_MODE to get the original message. Otherwise I couldn't do that. It's just a matter of terminology;-) – thelawnmowerman Jul 18 '12 at 17:03
  • I think using the terms encrypt/decrypt are misleading because you really just wanted to do signature verification. So yes we may be off here by terminology, but you didn't do much to clarify your intent on your side either. – chubbsondubs Jul 18 '12 at 18:12
  • While the advice is generally correct, the initial statement is just plain WRONG. You CAN encrypt with a private key and decrypt with a public key. It may not be sensible to do so,and wanting to do this may suggest you are not solving the problem correctly, but you CAN do it. – Chris Noldus Aug 20 '13 at 21:10
  • 2
    The term "private" and "public" is only a convention. What Nikolay said is correct, we can use either key to "encrypt" and the other to "decrypt". Just check the algorithm, it is obvious. The math explained so easily here: http://en.wikipedia.org/wiki/RSA_%28algorithm%29#A_working_example – radhoo Sep 07 '13 at 16:01
  • 1
    Just jumping in - becuase I can - radhoo is right, if you actually do the theory then you will know public and private keys are essentially the same thing - you can encrypt with one and decrypt with the other. You can also sign and verify with either key. The key is that what is done with one, can only be undone with the other. – Chris Noldus Sep 25 '13 at 04:13
  • BTW, this is not a proper answer for the question, just a related discussion. The proper answer is the one marked as the accepted one. – thelawnmowerman Mar 06 '14 at 08:45
1

To generate a RSA public key from a PEM format like you provided (openssl gen. rsa key)

-----BEGIN PUBLIC KEY-----
SOMEDES3UNREADABLETEXT+PADDING==
-----END PUBLIC KEY

and use it to read some content signed with it?

-take a look at my answer in a similar question here: https://stackoverflow.com/a/12101100/546054

Community
  • 1
  • 1
TouchBoarder
  • 6,422
  • 2
  • 52
  • 60
  • Thanks for answering! However, I had already solved my problem (see the marked answer of this thread). On the other hand, have a look at my procedure, it is much more efficient while removing the first and last lines than yours, since you have 2 "while loops" and 2 "if checks" all nested (very poor efficiency if the file would be larger). Just trying to improve all together, take it just as a suggestion, of course! – thelawnmowerman Aug 24 '12 at 11:14