Firstly, apologies for the amount of code I’m about to post. I’m trying to use the RSA public key from my Java application to encrypt a message in an Android app, and then send the ciphertext back to a Java environment for decryption, but upon attempting to decrypt I always get this error:
javax.crypto.BadPaddingException: Decryption error at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380) at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291) at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356) at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:382) at javax.crypto.Cipher.doFinal(Cipher.java:2087) ...
The ciphertext does contain the correct number of bytes (512), so it’s confusing to see a “bad padding” exception. Other similar posts on SO have suggested using “RSA/ECB/PKCS1Padding” as the algorithm, but this does not work.
Annoyingly, encryption and decryption in the Android environment (using Base64.URL_SAFE as the ‘base64Type’) works just fine, I just can’t seem to get the initial encryption working with the public key produced via Java.
I have extracted the bare minimum code into examples, as shown below:
Android Code
private void exampleMethod(){ String messageString = "Why does this not work in Android?"; String serverPubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoApIBIna77xq4j+M2RmyIhsB++41NHcY4KIPfX4VP4ADnkO+7ejbs4le/twrPtYGESVPF9czSMB5bzmTBZtq0jC8oT/6wiDIBlSuzo4fBrGociBIuaOjyG/j3ZhpcWpWPXuzER+ehuQ+8hZkMuJdK9IodqPR+5jmCef4rXoKObwS02LYQ1co5dEmtZVQRmmeYaVnWibd/s1d4KKGvSzXap3YBTf8peH5UGIQrLOTqvX0bo34xFxmj5U0H3xudnnwuVAlQlj9KiHPPABuwNtm1buRKJb5HZhSCveyT/2YAOmQqGrVN/nALtlZyTDZNs//Vp1zb9exSuG0t5xFc+pn4QIDAQAB"; String encryptedMessageString = getUrlEncodedCipherText(messageString, serverPubKey, Base64.NO_WRAP); /** * CipherText is ALWAYS the same and does not decrypt: DA_-RpCki-mjF6tSwiP2IhuW2UfPzZC7A9oVTTNptjT73HtROiQZvUC0Z2veJ5VwVx4toolvLErQmKKoQlqELSD756bu8ohEQwgJ4Xsu-3tXv-uEi5a9a_u19WnNLIF7tayDUhFeD2RzNvW895y1v-D30TvQRskNCFJfnjaytr_vmcVv8HrXURCmG6AMltaqdN72zh8p6VkKcjXSLiCApH957GXSqJCRzxbaQwf8X5EJfn8CQrPDGbE3gdhc2_hFwXQNIdxPxrOLtVbaFp9i_4GRWXJ6E2jHttV2bDv_uSVIz3OBzh7EkJiCnl3c904sH8QZae8c3SQyrTxVL7EpIA,, */ } public static String getUrlEncodedCipherText(String plainText, String pubKey, int base64Type){ try { final PublicKey publicKey = loadPublicKey(pubKey, base64Type); final byte[] cipherBytes = encrypt(plainText, publicKey); String cipherText = base64Encode(cipherBytes, base64Type); String urlEncodedCipherText = urlEncode(cipherText); return urlEncodedCipherText; } catch (Exception e){ e.printStackTrace(); return null; } } public static final String ALGORITHM = "RSA"; public static PublicKey loadPublicKey(String stored, int base64Type) throws GeneralSecurityException { String pubKey = stored.replace(BEGIN_PUBLIC_KEY, ""); pubKey = pubKey.replace(END_PUBLIC_KEY, ""); byte[] data = Base64.decode(pubKey, base64Type); X509EncodedKeySpec spec = new X509EncodedKeySpec(data); KeyFactory fact = KeyFactory.getInstance(ALGORITHM); PublicKey pub = fact.generatePublic(spec); return pub; } public static byte[] encrypt(String text, PublicKey key) { byte[] cipherText = null; try { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); cipherText = cipher.doFinal(text.getBytes()); } catch (Exception e) { e.printStackTrace(); } return cipherText; } public static String base64Encode(byte[] cipherBytes, int base64Type){ byte[] base64Cipher = Base64.encode(cipherBytes, base64Type); return new String(base64Cipher); } public static String urlEncode(String text){ return text.replace("+", "-").replace("/", "_").replace("=", ","); }
Java Code
private void exampleMethod(){ String pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoApIBIna77xq4j+M2RmyIhsB++41NHcY4KIPfX4VP4ADnkO+7ejbs4le/twrPtYGESVPF9czSMB5bzmTBZtq0jC8oT/6wiDIBlSuzo4fBrGociBIuaOjyG/j3ZhpcWpWPXuzER+ehuQ+8hZkMuJdK9IodqPR+5jmCef4rXoKObwS02LYQ1co5dEmtZVQRmmeYaVnWibd/s1d4KKGvSzXap3YBTf8peH5UGIQrLOTqvX0bo34xFxmj5U0H3xudnnwuVAlQlj9KiHPPABuwNtm1buRKJb5HZhSCveyT/2YAOmQqGrVN/nALtlZyTDZNs//Vp1zb9exSuG0t5xFc+pn4QIDAQAB"; String privKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCgCkgEidrvvGriP4zZGbIiGwH77jU0dxjgog99fhU/gAOeQ77t6NuziV7+3Cs+1gYRJU8X1zNIwHlvOZMFm2rSMLyhP/rCIMgGVK7Ojh8GsahyIEi5o6PIb+PdmGlxalY9e7MRH56G5D7yFmQy4l0r0ih2o9H7mOYJ5/itego5vBLTYthDVyjl0Sa1lVBGaZ5hpWdaJt3+zV3gooa9LNdqndgFN/yl4flQYhCss5Oq9fRujfjEXGaPlTQffG52efC5UCVCWP0qIc88AG7A22bVu5EolvkdmFIK97JP/ZgA6ZCoatU3+cAu2VnJMNk2z/9WnXNv17FK4bS3nEVz6mfhAgMBAAECggEBAIax4IchV0jqdbLR9cNK4yfdP0A/7jun+SImg48FLPDy1xi+v9UQZMioV3F88FDEZPrNQdI45wrWI94+wMS5V6BsMHYumOgGGxNo9m8WInrJz5GuJkdHuLMbqNZ6TlSMQOUiVUWWLSAuveOWgOJqriwRhsjDfBmbSBESUbP/wNdxX42RJudKJbVUV07urjFc3VAUrX+Sbj9KMZRe10pOzes7WKq9NMyQuitLcwCPzs2pQoXW1LdZYrVSi6MOqE1WHSL6VAgPx3cHYl7yznhspLmvDgnKTwnVbRo3BBwkxNbpvZXzgJBLEPLUtLqwNLKkU0aSF9MT5TABx4tfCaRQIiECgYEA70980I9mK74tjXlFrMKaLGigjHBss+Q/b7cRAtlQAhVOn0FCqz4Fc4iBri0iPekVIZ09lRehacEstTR8JBImMW2mqGyMwBbPaqQOf6xZ0pIoYb0ODAIjUNTWoBEr72+ko5HjaoQbxeb2QGUhMe/t3M1CMsrETEQTdA+qNP5C61UCgYEAqzOL7sSsNfTYbAF156qPPqx1IyXNqu0wbKa/zufCxGlFJDkaYoYIECKdLbpI2fqJsENqpZHgOnT0+LbqhFn07NIe1zT/zf0rh7w5fqxqy3Srs4+Mj6HwTIC7QpeXjiHxQuVrfi2W2ZatjQi8froxtEj3mpYKHsl0Ia89JSczQl0CgYAqHPXdCe8z8XK4u8esIE7bU8o1DK/EdH1JXpDqzG1NAIzmb6iY1ABHlZUknqKw/GyQjshAjXkFUE5a0RKrkloQRriWWQvn3dvAa4B1rVHdQYVDte5b5KBsYBgo8PynVSFG+6xmmTr996gMKv/NduiH+8MThyVGOpCl0v/j9X63RQKBgEmOhir6eXtdTbdqETyOPamR82o8jddIvauRIYxGa5p0GG7t0fZO3BwCo0HIbhCp4orHDIVC3fJ/2dkazjw7Yk52ISYZ8WaRxig1qQZSEjiEUll97ciwrUxRayO7ejRpRP2XEM5PzCaE5OBZxpM0cLKjPy8+E+8SY0Etx7m01ANJAoGBALUubgeKx1fut80YHLDmxOiTg9olFJi83Lj1TPQ0fRCXdJX6pHCSypBScoXuJYVwuIavHhTf8DPQ6OONq/V3DXKsGLydK/2E5yg+bz3qYfYslb3vDkZovNJDmfoyR0XakWbUTotntUQqodLk8Q9klHKp6oy+MkGY57R5OhIZBGPa"; String message = "Why does this not work in Android?"; String encryptedMessage = getUrlEncodedCipherText(message, pubKey); try { byte[] base64Decoded = Base64.decodeBase64(encryptedMessage.getBytes(Charset.forName("UTF-8"))); String decryptedMessage = decrypt(base64Decoded, loadPrivateKey(privKey)); System.out.println("decryptedMessage: " + decryptedMessage); /** * This works! Ciphertext always comes out different, as expected, and decodes successfully. */ } catch (Exception e){ e.printStackTrace(); } } public static String getUrlEncodedCipherText(String plainText, String pubKey){ try { final PublicKey publicKey = loadPublicKey(pubKey); final byte[] cipherBytes = encrypt(plainText, publicKey); String cipherText = base64Encode(cipherBytes); String urlEncodedCipherText = urlEncode(cipherText); return urlEncodedCipherText; } catch (Exception e){ e.printStackTrace(); return null; } } public static final String ALGORITHM = "RSA"; public static PublicKey loadPublicKey(String stored) throws GeneralSecurityException { String pubKey = stored.replace(BEGIN_PUBLIC_KEY, ""); pubKey = pubKey.replace(END_PUBLIC_KEY, ""); byte[] data = Base64.decodeBase64(pubKey); X509EncodedKeySpec spec = new X509EncodedKeySpec(data); KeyFactory fact = KeyFactory.getInstance(ALGORITHM); PublicKey pub = fact.generatePublic(spec); return pub; } public static byte[] encrypt(String text, PublicKey key) { byte[] cipherText = null; try { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); cipherText = cipher.doFinal(text.getBytes()); } catch (Exception e) { e.printStackTrace(); } return cipherText; } public static String decrypt(byte[] encrypted, PrivateKey key) { byte[] decryptedText = null; try { final Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key); decryptedText = cipher.doFinal(encrypted); } catch (Exception e) { e.printStackTrace(); return null; } return new String(decryptedText); } public static String base64Encode(byte[] cipherBytes){ byte[] base64Cipher = Base64.encodeBase64(cipherBytes); return new String(base64Cipher); } public static String urlEncode(String text){ return text.replace("+", "-").replace("/", "_").replace("=", ","); }
I’m aware that the problem has to be to do with difference in the way Android and Java interpret the RSA algorithm, and/or differences with the Base64 encode/decode, but I’m stumped. Any assistance greatly appreciated.
Advertisement
Answer
Solved it! The issue was that I was using the same ALGORITHM String for Cipher, KeyFactory etc. I split this into two different Strings, and the full solution for my problem is…
Android Code
private void exampleMethod(){ String messageString = "This actually works in Java AND Android!"; String serverPubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjS7T3WJ+VLucnUP5WYryeg+hhOjZZl5VxwvJAgo4GrXaXdernTPtmXnOSUlbhd928QRCip7D3rLwJNvGIwhPa6coA+YQnj+aHQC02AvCJP/9jpeNmm5MASZfYFXrdmOrMhAPpDZ4rUk1mqtvpwBkYmW3VbMtG336wT1bAIKPHCZuI2n6glupJvs8gK0NuIoAPRlxiQmQD7NCcRx1Et4JmqOMIRC+HqdGv9GGqC/0PB0Fv6LXi8GdzJQPMdoRLR0rvVykNeIzmcimejoIVjI78XUZeB1hF7p55h6W4C4Xm/PrnzKuXw4lBVehZtRhyIvNO62G/eNEZ3tup1/m+vkzHQIDAQAB"; String encryptedMessageString = getUrlEncodedCipherText(messageString, serverPubKey, Base64.NO_WRAP); System.out.println("encryptedMessageString: " + encryptedMessageString); /** * This works! Ciphertext always comes out different, as expected, and decodes successfully when fed into Java application. */ } public static String getUrlEncodedCipherText(String plainText, String pubKey, int base64Type){ try { final PublicKey publicKey = loadPublicKey(pubKey, base64Type); final byte[] cipherBytes = encrypt(plainText, publicKey); String cipherText = base64Encode(cipherBytes, base64Type); String urlEncodedCipherText = urlEncode(cipherText); return urlEncodedCipherText; } catch (Exception e){ e.printStackTrace(); return null; } } public static final String ALGORITHM = "RSA"; public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; public static PublicKey loadPublicKey(String stored, int base64Type) throws GeneralSecurityException { String pubKey = stored.replace(BEGIN_PUBLIC_KEY, ""); pubKey = pubKey.replace(END_PUBLIC_KEY, ""); byte[] data = Base64.decode(pubKey, base64Type); X509EncodedKeySpec spec = new X509EncodedKeySpec(data); KeyFactory fact = KeyFactory.getInstance(ALGORITHM); PublicKey pub = fact.generatePublic(spec); return pub; } public static byte[] encrypt(String text, PublicKey key) { byte[] cipherText = null; try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); cipherText = cipher.doFinal(text.getBytes()); } catch (Exception e) { e.printStackTrace(); } return cipherText; } public static String base64Encode(byte[] cipherBytes, int base64Type){ byte[] base64Cipher = Base64.encode(cipherBytes, base64Type); return new String(base64Cipher); } public static String urlEncode(String text){ return text.replace("+", "-").replace("/", "_").replace("=", ","); }
Java Code
private void exampleMethod(){ String pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjS7T3WJ+VLucnUP5WYryeg+hhOjZZl5VxwvJAgo4GrXaXdernTPtmXnOSUlbhd928QRCip7D3rLwJNvGIwhPa6coA+YQnj+aHQC02AvCJP/9jpeNmm5MASZfYFXrdmOrMhAPpDZ4rUk1mqtvpwBkYmW3VbMtG336wT1bAIKPHCZuI2n6glupJvs8gK0NuIoAPRlxiQmQD7NCcRx1Et4JmqOMIRC+HqdGv9GGqC/0PB0Fv6LXi8GdzJQPMdoRLR0rvVykNeIzmcimejoIVjI78XUZeB1hF7p55h6W4C4Xm/PrnzKuXw4lBVehZtRhyIvNO62G/eNEZ3tup1/m+vkzHQIDAQAB"; String privKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCNLtPdYn5Uu5ydQ/lZivJ6D6GE6NlmXlXHC8kCCjgatdpd16udM+2Zec5JSVuF33bxBEKKnsPesvAk28YjCE9rpygD5hCeP5odALTYC8Ik//2Ol42abkwBJl9gVet2Y6syEA+kNnitSTWaq2+nAGRiZbdVsy0bffrBPVsAgo8cJm4jafqCW6km+zyArQ24igA9GXGJCZAPs0JxHHUS3gmao4whEL4ep0a/0YaoL/Q8HQW/oteLwZ3MlA8x2hEtHSu9XKQ14jOZyKZ6OghWMjvxdRl4HWEXunnmHpbgLheb8+ufMq5fDiUFV6Fm1GHIi807rYb940Rne26nX+b6+TMdAgMBAAECggEAOHrlUwmWFHvBmcCZvlKx0lbyfONSJXvTwP9b+K7x5u2dYDFpfEhL4zwxZGwuaw4M3TqhDCeboDnhjD1HtLgcXarPfU/KkiRLmRKxRkTd9ENcwnCqu38odMVPHpEA06nn0O1P9Je+C0TgZvHyhtLHVf3vLB+0Ce2KJUhQYZHZgp7dBt0jgsGlEvQTtc1q3UtElNYbcPdLhg16Dcvw93+C+CWizUxHKl2VAxYBz5fGBxa+Cc6xbj4OGd0mvK9Z+71cX8sgp+WIdSV3a2SZrwYq8XxdtJJfdOwPc6LE56Ul9y3fE/biG8DM8ysu7g4C2K1tjKBTq1Rzz4NBCaGwkWQwGQKBgQDCX5nOiHYi0OYoE3WEVYVuEaf/YljurMiU4xYsy3bXzI42S+iKfVHz+/tvTrXSE0n6AdJnDHyP7mncFZodaurYR90S9VW3P+aOGoysmOmBRWW30xyIZ1g5+PJ9xPzoBfyc/AGHO5w5roY7Gg5myUC+UbucFU+9/JHUVoGV8yFz+wKBgQC58f8iY37sXf/sguv0YHpUV1suPWT90kxC25eRQz0lHWIIw4gfGXI7Tf2vtx7hUPK1z3X9lPiTsqmZIRKPrvykD8djmpfZEOzZlLxxhHUZMZppx19xSsgHaespCZir68aytBWn7FOVpehJP7MWaJurg03V8Cv0NSPU5JhN/j8xxwKBgQCkb6I78oAWtilvz/6EJKGL244HZJkd2bibFH4HCV4R9HB/CLrCpoB1a0BsCueQwFa+FVp9aTfbv/N4iCHoLzJcJHeneTu5mmqe32ERBw/MF/yUhhnGX79o0+25brQSSjZKTHuyf0CMH9RZHZL/a9TE7XpM8k6SyKBKRaC9TYGIjwKBgDZG10x4coIYZi3pgWqSBuM7pJf4v9P/YNdbNgDm+aAt1YHtYXyCdv+solJ6R39Jm69KYYylwXGLg0n5h2t9jq1tFayTYaOw9xIEAoW4Pl4eRo597fQp+f2AA261KGV2q0danb+okjVqekV3XJU778S+zSeXtZzRLiZkm7iYcGXdAoGAUEbRAsopG+blDBmuC5Fhr7Grof9lw/zRgfl516n9oujOmDY8Sl4F7jHkgKzOL1CpvMbRswr6G3tmpJMYGNy0HVX4n4QAiIvEraLUtLlh7LotUiU8zzjniK7HPANoOsQxEi6iUR6jWRf0LKgckE9aaUJjvljj1Hr4PbFpny2gjD4="; String message = "This actually works in Java AND Android!"; String encryptedMessage = getUrlEncodedCipherText(message, pubKey); try { byte[] base64Decoded = Base64.decodeBase64(encryptedMessage.getBytes(Charset.forName("UTF-8"))); String decryptedMessage = decrypt(base64Decoded, loadPrivateKey(privKey)); System.out.println("decryptedMessage: " + decryptedMessage); /** * This works! Ciphertext always comes out different, as expected, and decodes successfully. */ } catch (Exception e){ e.printStackTrace(); } } public static String getUrlEncodedCipherText(String plainText, String pubKey){ try { final PublicKey publicKey = loadPublicKey(pubKey); final byte[] cipherBytes = encrypt(plainText, publicKey); String cipherText = base64Encode(cipherBytes); String urlEncodedCipherText = urlEncode(cipherText); return urlEncodedCipherText; } catch (Exception e){ e.printStackTrace(); return null; } } public static final String ALGORITHM = "RSA"; public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; public static PublicKey loadPublicKey(String stored) throws GeneralSecurityException { String pubKey = stored.replace(BEGIN_PUBLIC_KEY, ""); pubKey = pubKey.replace(END_PUBLIC_KEY, ""); byte[] data = Base64.decodeBase64(pubKey); X509EncodedKeySpec spec = new X509EncodedKeySpec(data); KeyFactory fact = KeyFactory.getInstance(ALGORITHM); PublicKey pub = fact.generatePublic(spec); return pub; } public static byte[] encrypt(String text, PublicKey key) { byte[] cipherText = null; try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); cipherText = cipher.doFinal(text.getBytes()); } catch (Exception e) { e.printStackTrace(); } return cipherText; } public static String decrypt(byte[] encrypted, PrivateKey key) { byte[] decryptedText = null; try { final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key); decryptedText = cipher.doFinal(encrypted); } catch (Exception e) { e.printStackTrace(); return null; } return new String(decryptedText); } public static String base64Encode(byte[] cipherBytes){ byte[] base64Cipher = Base64.encodeBase64(cipherBytes); return new String(base64Cipher); } public static String urlEncode(String text){ return text.replace("+", "-").replace("/", "_").replace("=", ","); }