Nublodeveloper Nublodeveloper - 3 months ago 24
Java Question

javax.crypto.Cipher working differently since Android 6 Marshmallow

I've been successfully using javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding") to Authenticate with DESFire cards on Android (following the example here: http://stackoverflow.com/a/14160507/2095694). It's been working on several devices from Android 4 to 5, but stopped working on my Nexus 7 updated to 6 Marshmallow (and 6.0.1). It had been working on the same device before updating.

It seems Cipher is working differently, giving different results for the same key and data. Running the following code...

public static void testCipher() throws Exception
{
byte[] KEY =
new byte[]{
(byte) 0x0C, (byte) 0x09, (byte) 0x03, (byte) 0x0E,
(byte) 0x05, (byte) 0x0A, (byte) 0x0D, (byte) 0x02,
(byte) 0x03, (byte) 0x0A, (byte) 0x09, (byte) 0x0B,
(byte) 0x06, (byte) 0x10, (byte) 0x04, (byte) 0x10
};

byte[] DATA =
new byte[]{
(byte) 0x29, (byte) 0xDA, (byte) 0xC0, (byte) 0xC4,
(byte) 0xB8, (byte) 0x47, (byte) 0x13, (byte) 0xA2};

byte[] newByte8 = new byte[8]; //Zeroes

android.util.Log.d("TEST", "KEY : " + bin2hex(KEY));
android.util.Log.d("TEST", "DATA: " + bin2hex(DATA));
android.util.Log.d("TEST", "IVPS: " + bin2hex(newByte8));
android.util.Log.d("TEST", "----");

javax.crypto.Cipher cipher =
javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding");

cipher.init(
Cipher.DECRYPT_MODE,
new javax.crypto.spec.SecretKeySpec(KEY, "DESede"),
new javax.crypto.spec.IvParameterSpec(newByte8));

byte[] result = cipher.doFinal(DATA);

android.util.Log.d("TEST", "RSLT: " + bin2hex(result));
}

public static String bin2hex(byte[] data) {
return String.format("%0" + (data.length * 2) + "X", new java.math.BigInteger(1, data));
}


... gives me the following output:

KEY : 0C09030E050A0D02030A090B06100410
DATA: 29DAC0C4B84713A2
IVPS: 0000000000000000
----
RSLT: 47BC415065B8155E


Normal value, what it should be, always worked and card ends up authenticating correctly, so it's doing it the way the card expects. As a said I tried on several devices (Android 4 and 5) and they give the same result.

But on my Nexus 7 now with Marshmallow I get something else (and the authentication ends up failing)

RSLT: F3ADA5969FA9369C


Has something changed in the libraries?

Answer

It seems they changed the default provider in Marshmallow.

A simple:

cipher.getProvider().getName();

Shows "AndroidOpenSSL" for Marshmallow, where it was "BC" (BouncyCastle I suppose) before.

Using the other getInstance overload...

 javax.crypto.Cipher cipher =
            javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding","BC");

...gives me the expected result on my Nexus with Marshmallow.