12

I've seen a number of similar questions, but nothing has quite worked for me. I am simply trying to convert an RSA public key that's in PEM format that I've retrieved from a server into a PublicKeyin Android. Can anyone point me in the right direction?

EDIT: I've successfully used the following code to convert the PEM into a PublicKey, but upon encoding a message, I get unexpected output...

 public PublicKey getFromString(String keystr) throws Exception
    {
        // Remove the first and last lines

        String pubKeyPEM = keystr.replace("-----BEGIN PUBLIC KEY-----\n", "");
        pubKeyPEM = pubKeyPEM.replace("-----END PUBLIC KEY-----", "");

        // Base64 decode the data

        byte [] encoded = Base64.decode(pubKeyPEM);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PublicKey pubkey = kf.generatePublic(keySpec);

        return pubkey;
    }

    public String RSAEncrypt(final String plain) throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {

        if (pubKey!=null) {
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            encryptedBytes = cipher.doFinal(plain.getBytes());
            Log.d("BYTES", new String(encryptedBytes));
            return Hex.encodeHexString(encryptedBytes);
        }
        else
            return null;
    }

The output looks like this:

b6813f8791d67c0fa82890d005c8ff554b57143b752b34784ad271ec01bfaa9a6a31e7ae08444baef1585a6f78f3f848eecb1706bf7b2868fccefc9d728c30480f3aabc9ac5c3a9b4b3c74c2f7d6f0da235234953ea24b644112e04a2ec619f6bf95306ef30563c4608ec4b53ed7c15736d5f79c7fa1e35f2444beb366ae4c71

when I expect something closer to:

JfoSJGo1qELUbpzH8d4QXtafup+J2F9wLxHCop00BQ4YS0cRdRCKDfHpFPZQYjNeyQj00HwHbz+vj8haTPbpdqT94AHAl+VZ+TPAiUw1U5EXLLyy4tzbmfVI7CwvMm26lwB4REzYUZdedha1caxMEfxQ5duB+x4ol9eRZM/savg=

Is there some formatting or file type that I'm missing?

cph2117
  • 2,651
  • 1
  • 28
  • 41
  • Have you tried the approach in http://stackoverflow.com/q/3243018/ ? – Barend Nov 11 '14 at 20:56
  • 2
    I need to be able to do everything in-app...not using command line – cph2117 Nov 12 '14 at 00:07
  • if you need a base64 output, why did you decode your key to binary in line `byte [] encoded = Base64.decode(pubKeyPEM);`? – AaA Sep 19 '17 at 01:23
  • That's the expected input for a `X509EncodedKeySpec` – cph2117 Sep 20 '17 at 16:56
  • @cph2117 I am trying to use your code with my `.pem` file, however, my `.pem` file that contains the public key does not contain the strings `"-----BEGIN PUBLIC KEY-----\n"` how should i proceed? – codeKiller Dec 15 '17 at 08:09
  • also, in your code, `Base64.decode` will give an error since it expects 2 input parameters, in your case you just input 1 – codeKiller Dec 15 '17 at 08:11
  • @codeKiller It only takes one parameter per the docs (https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html). And as for the "Begin Public Key", you could just replace the first two lines with ```String pubKeyPEM = keystr```, but it sounds weird that a properly formed PEM wouldn't contain that line. – cph2117 Dec 15 '17 at 18:34
  • @cph2117 yes you are right, that is from Java utils, I am using Android and the docs indicates 2 params https://developer.android.com/reference/android/util/Base64.html since your post had the tag `Android` it made me confused, my mistake sorry. And yes, the .pem file I got is bit weird without any `Begin` – codeKiller Dec 18 '17 at 06:51

2 Answers2

8

To answer my own question...The first output is in hex and the second output is in base 64. Just change the return statement to return new String(Base64.encode(encryptedBytes)); and you'll be good!

cph2117
  • 2,651
  • 1
  • 28
  • 41
4

This doesn't answer the question, but I find the content relevant. Posting as an answer because it doesn't fit as a comment.

PEM vs DER

  • PEM basically encapsulates a DER-encoded certificate or key.
  • DER is binary, PEM is text; so PEM can easily be copy-pasted to an email, for example.
  • What PEM does is:
    1. Encode the DER certificate or key using Base64, and
    2. Delimit the result with -----BEGIN <something>----- and -----END <something>-----.
  • The key or certificate is the same, just represented in a different format.

Mostly paraphrasing from ASN.1(wiki).

DER to Android/Java public key

The following is an example of how to use a key factory in order to instantiate a DSA public key from its encoding. Assume Alice has received a digital signature from Bob. Bob also sent her his public key (in encoded format) to verify his signature. Alice then performs the following actions:

X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);

...

Note that bobEncodedPubKey is DER-encoded in this sample.

https://developer.android.com/reference/java/security/KeyFactory

PEM to Android/Java public key

Similar to what is done for DER, but do the following beforehand:

  1. Remove the BEGIN/END delimitation, and
  2. Decode the content in Base64 to obtain the original DER.

(The question already shows code on how to do this.)

Daniel
  • 2,380
  • 29
  • 44