Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android public key encryption

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

like image 811
Ray Avatar asked Oct 02 '17 19:10

Ray


People also ask

Does Android use AES encryption?

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.

How do I find the encryption key on my Android phone?

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.

What encryption do Android phones use?

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.


1 Answers

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

like image 193
Ray Avatar answered Oct 05 '22 05:10

Ray