Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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: https://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?

like image 736
Nublodeveloper Avatar asked Dec 15 '15 10:12

Nublodeveloper


2 Answers

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.

Update: I now get this warning:

The BC provider is deprecated and when targetSdkVersion is moved to P this method will throw a NoSuchAlgorithmException. To fix this you should stop specifying a provider and use the default implementation
Cipher#getInstance should not be called with ECB as the cipher mode or without setting the cipher mode because the default mode on android is ECB, which is insecure.

So I have ended up using the other answer here that will (hopefully) work on all versions of Android.

like image 50
Nublodeveloper Avatar answered Oct 24 '22 19:10

Nublodeveloper


there is a android bug issued: https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=triple%20des&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened&groupby=&sort=&id=189292

you can also solve your problem by changing you key to 24 bytes len as below:

    MessageDigest md = MessageDigest.getInstance("MD5");
seed_key = md.digest(new String(key).getBytes());

if (seed_key.length == 16) {
    byte[] tempkey = new byte[24];
    System.arraycopy(seed_key, 0, tempkey, 0, 16);
    System.arraycopy(seed_key, 0, tempkey, 16, 8);

    seed_key = tempkey;
}
SecretKeySpec keySpec = new SecretKeySpec(seed_key, "DESede");
nCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
byte[] IVector = new byte[] { 27, 9, 45, 27, 0, 72, (byte) 171, 54 };
IvParameterSpec iv = new IvParameterSpec(IVector);
nCipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

byte[] cipherbyte = nCipher.doFinal(data.getBytes());
encodeTxt = new String(Base64.encodeBase64(cipherbyte));
like image 29
cahit beyaz Avatar answered Oct 24 '22 18:10

cahit beyaz