javax.crypto.Cipher working differently since Android 6 Marshmallow
Asked Answered
N

2

6

I've been successfully using javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding") to Authenticate with DESFire cards on Android (following the example here: https://mcmap.net/q/1193038/-des-send-and-receive-modes-for-desfire-authentication). 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?

Newly answered 15/12, 2015 at 10:31 Comment(6)
There are some changes in cryptographic libraries. See Boring SSL section developer.android.com/about/versions/marshmallow/… But not sure about impact to javax.crypto package.Vittorio
I'm having the exact same problem. Tried a few things based on the crypto package update, but nothing worked yet. decided to implement (port) a independent code for 3DES (DESede/CBC/NoPadding) to get it working. I validated the problem is in the Marshmallow port. This is breaking my users and scaling quickly due to Marshmallow (API Level 23) updates on users devices.Juju
Thanks to both of you. @Juju Could you test if my answer works for you too?Newly
Hi @Nublodeveloper, it worked. Changing the provider to BouncyCastle worked. Thanks.Juju
@Juju OK Thanks. If it worked for both of us we can safely assume that was it.Newly
Someone should probably file a bug report at AOSP Bug Reports.Blain
F
1

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));
Frodina answered 25/8, 2016 at 6:18 Comment(3)
Good find! I've just tested this and it also works. I also tested the 24 byte len fix with "BC" in marshmallow, so I suppose pre-marshmallow will also work correctly.Newly
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 set your answer as the official one and linked my answer to yours.Newly
@Newly very much appreciated. thanx :)Frodina
N
9

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.

Newly answered 16/12, 2015 at 8:51 Comment(2)
Great find! Solved my issue when using DESede/CBC/PKCS7Padding. I have another project where I'm using Cipher and it works great without specifying the provider, the algorithm is PBEWithMD5AndDES, any idea why?Browband
@Browband Sorry I can only assume the obvious, the default provider for that algorithm hasn't changed or they both work exactly the same.Newly
F
1

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));
Frodina answered 25/8, 2016 at 6:18 Comment(3)
Good find! I've just tested this and it also works. I also tested the 24 byte len fix with "BC" in marshmallow, so I suppose pre-marshmallow will also work correctly.Newly
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 set your answer as the official one and linked my answer to yours.Newly
@Newly very much appreciated. thanx :)Frodina

© 2022 - 2024 — McMap. All rights reserved.