My Android app implements RSA encryption, however the backend can not decrypt the token generated by the app. Here is the code, the beginning and end lines of the public key have been removed before making the calls, what could be the problem?
String encryptedToken = Base64.encodeToString(encrypt(publicKey, "4111111111111111"), Base64.NO_WRAP);
public static byte[] encrypt(String publicKey, String data) {
if (TextUtils.isEmpty(publicKey) || TextUtils.isEmpty(data)) {
return null;
}
try {
// Decode the modified public key into a byte[]
byte[] publicKeyByteArray = Base64.decode(publicKey.getBytes("UTF-8"),Base64.NO_WRAP);
Cipher mCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyByteArray);
Key key = keyFactory.generatePublic(x509KeySpec);
mCipher.init(Cipher.ENCRYPT_MODE, key);
return mCipher.doFinal(data.getBytes("UTF-8"));
}
catch (UnsupportedEncodingException e) {
Log.e("RSAKEY", e.getMessage());
}
catch (NoSuchPaddingException e) {
Log.e("RSAKEY", e.getMessage());
} catch (NoSuchAlgorithmException e) {
Log.e("RSAKEY", e.getMessage());
} catch (InvalidKeyException e) {
Log.e("RSAKEY", e.getMessage());
} catch (InvalidKeySpecException e) {
Log.e("RSAKEY", e.getMessage());
} catch (IllegalBlockSizeException e) {
Log.e("RSAKEY", e.getMessage());
} catch (BadPaddingException e) {
Log.e("RSAKEY", e.getMessage());
}
return null;
}
The backend team provided the below sample code that works, but it is for desktop java. Android library does not have the Base64.getEncoder method. it is very similar to what I wrote but mine just does not work.
// Decode the modified public key into a byte[]
byte[] publicKeyByteArray = Base64.getDecoder().decode(publicKey.getBytes(StandardCharsets.UTF_8));
// Create a PublicKey from the byte array
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByteArray);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// Get an instance of the Cipher and perform the encryption
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherText = cipher.doFinal(ccNum.getBytes(StandardCharsets.UTF_8));
// Get the encrypted value as a Base64-encoded String
String encodeToStr = Base64.getEncoder().encodeToString(cipherText);
// Print out the encoded, encrypted string
System.out.println("Encrypted and Encoded String: " + encodeToStr);
I compared the byte array values at every step. The desktop cipher and android cipher got exactly the same inputs. However the results from the Android code cipher.doFinal can not be decrypted by the backend. If I put the desktop results to the REST call body they work fine, so it is not something caused by REST call.
I also tried to create a public/private key pair on Android, and use the generated public key to encrypt instead of using the public key from our backend, and decrypt using the private key and it works. So the cipher is also working, just somehow the backend is expecting something different
Android offers storage encryption using the Advanced Encryption Standard (AES). And most new Android devices provide AES via Arm's version 8 processor cryptography extensions.
Use the settings menu to open the encryption screen below by following Settings > Security > Encryption > Encrypt tablet or Encrypt phone. Enter the PIN or password that was created earlier using the screen below.
For new devices, use file-based encryption. Android 5.0 up to Android 9 support full-disk encryption. Full-disk encryption uses a single key—protected with the user's device password—to protect the whole of a device's userdata partition.
Finally someone in the team cracked this. The reason is because the Android OS uses Bouncy castle, the backend uses Sun as the provider, this caused the backend throwing a BadPaddingException. To make it work, the cipher needs to be initialized this way on Android:
mCipher.init(Cipher.ENCRYPT_MODE, key, new
OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1,
PSource.PSpecified.DEFAULT));
Check for more details in this post: http://bouncy-castle.1462172.n4.nabble.com/Problems-with-OAEP-SHA-256-hash-crypto-td1466852.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With