In this article, we will discuss about RSA(Rivest–Shamir–Adleman) cryptography encryption and decryption in java. We will be generating public and private keys using KeyPairGenerator and use these keys for asymmetric encryption and decryption.
You can use this online tool for generating RSA keys and perform RSA encryption and decryption online.
As we know, there are 2 basic types of encryption - Asymmetric and Symmetric encryption. Asymmetric encryption uses two different keys as public and private keys. Here, you can encrypt sensitive information with a public key and a matching private key is used to decrypt the same. Asymmetric encryption is mostly used when there are 2 different endpoints are involved such as VPN client and server, SSH etc.
The example shown here can be used to generate base64 encoded public keys and the same keys can be shared with javascript or Android client to encrypt all the request that has to travel through the wires to reach to the server and the server can decrypt that using the private key.
We have another encryption technique called as Symmetric encryption. This type of encryption uses a single key known as private key or secret key to encrypt and decrypt sensitive information. Here is an article where I have discussed about AES encryption in Java
Note that since asymmetric encryption doesn’t share the encryption key, it’s more secure than symmetric encryption. Still, it is much slower than symmetric encryption. Therefore, to improve software performance while keeping its security at the highest level, pro Java developers use asymmetric encryption just to encrypt a traditional single key used in symmetric encryption.
Generating RSA Public Private Key
We can use factory method to generate these keys using KeyPairGenerator. For the demo purpose we are using a key size of 1024. By default, the private key is generated in
In the following example we will be Base64 encoding the public and private keys to ease the sharing of these keys. For this purpose, we will be using Java 8 provided Base64.
RSAKeyPairGenerator.javapackage com.devglan.rsa; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.security.*; import java.util.Base64; public class RSAKeyPairGenerator { private PrivateKey privateKey; private PublicKey publicKey; public RSAKeyPairGenerator() throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair pair = keyGen.generateKeyPair(); this.privateKey = pair.getPrivate(); this.publicKey = pair.getPublic(); } public void writeToFile(String path, byte[] key) throws IOException { File f = new File(path); f.getParentFile().mkdirs(); FileOutputStream fos = new FileOutputStream(f); fos.write(key); fos.flush(); fos.close(); } public PrivateKey getPrivateKey() { return privateKey; } public PublicKey getPublicKey() { return publicKey; } public static void main(String[] args) throws NoSuchAlgorithmException, IOException { RSAKeyPairGenerator keyPairGenerator = new RSAKeyPairGenerator(); keyPairGenerator.writeToFile("RSA/publicKey", keyPairGenerator.getPublicKey().getEncoded()); keyPairGenerator.writeToFile("RSA/privateKey", keyPairGenerator.getPrivateKey().getEncoded()); System.out.println(Base64.getEncoder().encodeToString(keyPairGenerator.getPublicKey().getEncoded())); System.out.println(Base64.getEncoder().encodeToString(keyPairGenerator.getPrivateKey().getEncoded())); } }
This will output the public and private keys.Following is the screenshot.
RSA Encryption In Java
Now We have RSAUtil.java that has methods defined for RSA encryption and decryption.Let us discuss about encryption first. As we discussed above the public key generated is in X.509 format and we use public key for encryption.Hence, we need X509EncodedKeySpec class to convert it again to RSA public key.Remember, that we have base64 encoded public keys.Hence, first let us first Base64 decode and generate the public key.
Note: - RSA/ECB/PKCS1Padding has been known to be insecure and you should use RSA/None/OAEPWithSHA1AndMGF1Padding instead.
RSAUtil.javapublic static PublicKey getPublicKey(String base64PublicKey){ PublicKey publicKey = null; try{ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes())); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); publicKey = keyFactory.generatePublic(keySpec); return publicKey; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } return publicKey; }
Now, we have a simple method encrypt()
that takes the string to be enrypted and the Base64 encoded RSA key for encryption.Here getPublicKey()
is the method that we defined above.
public static byte[] encrypt(String data, String publicKey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey)); return cipher.doFinal(data.getBytes()); }
RSA Decryption In Java
For decryption we will be using private key and we discussed above that the private key is generated in PKCS#8 format.Hence, following is the code to generate the private key from base64 encoded string using PKCS8EncodedKeySpec.
public staticPrivateKey getPrivateKey(String base64PrivateKey){ PrivateKey privateKey = null; PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes())); KeyFactory keyFactory = null; try { keyFactory = KeyFactory.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } try { privateKey = keyFactory.generatePrivate(keySpec); } catch (InvalidKeySpecException e) { e.printStackTrace(); } return privateKey; }
Now following is the decrypt method that accepts RSA encrypted string and Base64 encoded RSA private key for decryption.
public static String decrypt(String data, String base64PrivateKey) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { return decrypt(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey)); }
Now, putting it all together.
RSAUtil.javapackage com.devglan.rsa; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class RSAUtil { private static String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgFGVfrY4jQSoZQWWygZ83roKXWD4YeT2x2p41dGkPixe73rT2IW04glagN2vgoZoHuOPqa5and6kAmK2ujmCHu6D1auJhE2tXP+yLkpSiYMQucDKmCsWMnW9XlC5K7OSL77TXXcfvTvyZcjObEz6LIBRzs6+FqpFbUO9SJEfh6wIDAQAB"; private static String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKAUZV+tjiNBKhlBZbKBnzeugpdYPhh5PbHanjV0aQ+LF7vetPYhbTiCVqA3a+Chmge44+prlqd3qQCYra6OYIe7oPVq4mETa1c/7IuSlKJgxC5wMqYKxYydb1eULkrs5IvvtNddx+9O/JlyM5sTPosgFHOzr4WqkVtQ71IkR+HrAgMBAAECgYAkQLo8kteP0GAyXAcmCAkA2Tql/8wASuTX9ITD4lsws/VqDKO64hMUKyBnJGX/91kkypCDNF5oCsdxZSJgV8owViYWZPnbvEcNqLtqgs7nj1UHuX9S5yYIPGN/mHL6OJJ7sosOd6rqdpg6JRRkAKUV+tmN/7Gh0+GFXM+ug6mgwQJBAO9/+CWpCAVoGxCA+YsTMb82fTOmGYMkZOAfQsvIV2v6DC8eJrSa+c0yCOTa3tirlCkhBfB08f8U2iEPS+Gu3bECQQCrG7O0gYmFL2RX1O+37ovyyHTbst4s4xbLW4jLzbSoimL235lCdIC+fllEEP96wPAiqo6dzmdH8KsGmVozsVRbAkB0ME8AZjp/9Pt8TDXD5LHzo8mlruUdnCBcIo5TMoRG2+3hRe1dHPonNCjgbdZCoyqjsWOiPfnQ2Brigvs7J4xhAkBGRiZUKC92x7QKbqXVgN9xYuq7oIanIM0nz/wq190uq0dh5Qtow7hshC/dSK3kmIEHe8z++tpoLWvQVgM538apAkBoSNfaTkDZhFavuiVl6L8cWCoDcJBItip8wKQhXwHp0O3HLg10OEd14M58ooNfpgt+8D8/8/2OOFaR0HzA+2Dm"; public static PublicKey getPublicKey(String base64PublicKey){ PublicKey publicKey = null; try{ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes())); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); publicKey = keyFactory.generatePublic(keySpec); return publicKey; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } return publicKey; } public static PrivateKey getPrivateKey(String base64PrivateKey){ PrivateKey privateKey = null; PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes())); KeyFactory keyFactory = null; try { keyFactory = KeyFactory.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } try { privateKey = keyFactory.generatePrivate(keySpec); } catch (InvalidKeySpecException e) { e.printStackTrace(); } return privateKey; } public static byte[] encrypt(String data, String publicKey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey)); return cipher.doFinal(data.getBytes()); } public static String decrypt(byte[] data, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); return new String(cipher.doFinal(data)); } public static String decrypt(String data, String base64PrivateKey) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { return decrypt(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey)); } public static void main(String[] args) throws IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, BadPaddingException { try { String encryptedString = Base64.getEncoder().encodeToString(encrypt("Dhiraj is the author", publicKey)); System.out.println(encryptedString); String decryptedString = RSAUtil.decrypt(encryptedString, privateKey); System.out.println(decryptedString); } catch (NoSuchAlgorithmException e) { System.err.println(e.getMessage()); } } }
Putting RSA and AES together
With every doubling of the RSA key length, decryption is 6-7 times times slower.Hence, when there are large messages for RSA encryption, the performance degrades.In such scenarios, we first do an AES encryption of the messages and the key used for AES encryption is RSA encrypted and sent to the server. This technique can be used by the Javascript or Android client for sending sensitive payloads to the server.We have demonstrates this in another article of RSA encryption in javacript and decryption in Java.
Conclusion
In this article, we discussed about RSA encryption and decryption in java using public and private keys.The source code can be downloaded from github.