I needed to implement RSA-2048 cryptography into the Android application I’m currently writing. After a lot of trial and error, I’ve finally came up with a working solution, which I’m going to share here.
The application is a messaging service, so everything here will be applied to strings.
I’m going to split the code and remove some parts to make it easier to read and explain.
First of all, I declared some constants to be used throughout the whole implementation:
Next comes the method to generate the key pair:
What happens in the
generateKeyPair() function is pretty straightforward: a
KeyPairGenerator object is instantiated using the parameters we set beforehand, his
genKeyPair() method is invoked and the resulting key pair is stored inside
kp. The actual key objects are then stored in
kp.private (as a
PrivateKey object) and
kp.public (as a
Now, here is the first interesting part. Whenever you see
prefs.something I’m just saving that value inside my shared preferences (which, by the way, is not secure at all, so don’t do it. The final implementation of this code before production will save the keys in an encrypted area). But my shared preferences can’t accept objects of type
PublicKey, so I need to extract the actual keys from the objects. To do that I use the
.key() extension functions you see on the bottom there: those will give me back simple strings which I can easily handle.
That’s great, now I have my keys as strings and I can save them wherever I want. For example, I can send my public key to the main server, so other people can use it to write me something. The main problem is, as strings, I can’t use them to encrypt or decrypt data, because I need the
PublicKey objects to do that.
What we need is a way to convert the strings back to Key objects. Unfortunately, that is a little more than the single line of the inverse process. This is the code to convert a string back to a
This is an extension function, like the
.key() we saw earlier. The first thing we do is decode the string, then tell the system again we’re using RSA and finally generate a Public Key out of that.
That’s it, we’ve got a
PublicKey object back. Now, when we want to encrypt something, we just call
.toPublicKey() on the string key we’ll get from the server.
The code to convert a string to a
PrivateKey is basically the same. The only differences are
PKCS8 spec instead of
X509 and a call to a
.generatePrivate() method instead of the public one.
Now we’ll be able to decrypt anything sent to us, because we can get our
PrivateKey back from the saved string.
Side note: please be careful. I’m just showing you the basic principle of how this thing works, but in a production environment you should always handle exceptions correctly. In both these last two methods I omitted the
try...catch blocks I’m actually using just for clarity and brevity, but you should catch the possible exceptions that could be thrown like
The setup work is finished. We can now write the methods to actually encrypt and decrypt a string. Let’s take a look at the encryption first:
This method takes the string to be encrypted and the public key you want to use to encrypt it. As you can see, we’re still working with strings only, so you can receive a public key from your server. To make that key usable we call our
.toPublicKey() function on it. We then instantiate our cipher and tell it to work in
ENCRYPT_MODE using the public key we just declared. This method returns an encrypted version of the original string which can only be decrypted using the private key associated with the public one we used.
The last thing we need to implement is a method to decrypt an encrypted message:
This one doesn’t take a key argument because we will use it to decrypt data sent to us, which means someone else encrypted that data using our public key, which means we will always use our private key to read it. The rest of the function is almost the same as the one before. Of course, the cipher now works in
DECRYPT_MODE, and we do a final cast to
String on the result.
And, that’s it! We’re done. Using these methods is extremely easy, we just have to call
generateKeyPair() once, and call
encrypt(string, key) and
decrypt(string) whenever we need. Everything else will be handled by what you see above here.
The important thing to take into consideration is that you should not use this method to directly encrypt the message you want to send. Using RSA this way will force you to a 256-byte limit on the input. The correct way is encrypting the message with a symmetric encryption like AES or 3DES and transmit it that way. You then use RSA to encrypt the AES key. The recipient will use their private key to decrypt the AES key, and use that to decrypt the original message.