javax.crypto.Cipher的工作方式与Android 6棉花糖不同

javax.crypto.Cipher的工作方式与Android 6棉花糖不同,java,android,openssl,android-6.0-marshmallow,javax.crypto,Java,Android,Openssl,Android 6.0 Marshmallow,Javax.crypto,我已经成功地使用javax.crypto.Cipher.getInstance(“DESede/CBC/NoPadding”)在Android上使用DESFire卡进行身份验证(如下示例:)。它一直在安卓4到5的多个设备上工作,但在我的Nexus7更新为6棉花糖(和6.0.1)时停止工作。在更新之前,它一直在同一台设备上工作 似乎密码的工作方式不同,对相同的密钥和数据给出不同的结果。正在运行以下代码 public static void testCipher() throws Exception

我已经成功地使用javax.crypto.Cipher.getInstance(“DESede/CBC/NoPadding”)在Android上使用DESFire卡进行身份验证(如下示例:)。它一直在安卓4到5的多个设备上工作,但在我的Nexus7更新为6棉花糖(和6.0.1)时停止工作。在更新之前,它一直在同一台设备上工作

似乎密码的工作方式不同,对相同的密钥和数据给出不同的结果。正在运行以下代码

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));
}
。。。提供以下输出:

KEY : 0C09030E050A0D02030A090B06100410
DATA: 29DAC0C4B84713A2
IVPS: 0000000000000000
----
RSLT: 47BC415065B8155E
正常值,它应该是什么,总是工作和卡结束认证正确,所以它做它的方式卡期望。如前所述,我在几个设备(安卓4和安卓5)上进行了测试,结果都是一样的

但在我的Nexus7上,现在有了棉花糖,我得到了其他东西(身份验证最终失败)


图书馆有什么变化吗

他们似乎改变了棉花糖中的默认提供者

一个简单的例子:

cipher.getProvider().getName();
显示了棉花糖的“AndroidOpenSSL”,之前是“BC”(我想是BouncyCastle)

正在使用另一个getInstance重载

 javax.crypto.Cipher cipher =
            javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding","BC");
…给了我与棉花糖联系的预期结果

更新:我现在收到以下警告:

BC提供程序已弃用,当targetSdkVersion移动到p时,此方法将引发NoSuchAlgorithmException。要解决此问题,应停止指定提供程序,并使用默认实现
Cipher#getInstance不应与ECB一起作为密码模式调用,或者不设置密码模式,因为android上的默认模式是ECB,这是不安全的


因此,我最终使用的将(希望)适用于所有版本的Android。

发布了一个Android错误:

您还可以通过将密钥更改为24字节len来解决问题,如下所示:

    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));

加密库中有一些更改。请参阅无聊的SSL部分,但不确定对javax.crypto包的影响。我遇到了完全相同的问题。尝试了一些基于加密软件包更新的东西,但没有任何效果。决定为3DES(DESede/CBC/NoPadding)实现(port)一个独立的代码以使其工作。我确认问题出在棉花糖端口。由于用户设备上的棉花糖(API级别23)更新,这正在破坏我的用户并快速扩展。感谢你们两位@罗德里戈:你能测试一下我的答案对你是否也有效吗?嗨@Nublodeveloper,它有效。将提供程序更改为BouncyCastle成功。谢谢。@Rodrigo好的,谢谢。如果它对我们两个都有效,我们可以放心地认为是它。伟大的发现!解决了使用DESede/CBC/PKCS7Padding时的问题。我有另一个项目,我正在使用密码,它在不指定提供程序的情况下工作得很好,算法是PBEWithMD5AndDES,知道为什么吗?@GuilhE抱歉,我只能假设一个明显的事实,该算法的默认提供程序没有更改,或者它们都工作完全相同。很好的发现!我刚刚测试过这个,它也能工作。我还在marshmallow中测试了带有“BC”的24字节len修复程序,因此我认为pre-marshmallow也可以正常工作。现在我收到了这样的警告:BC提供程序已弃用,当targetSdkVersion移动到P时,此方法将抛出NoSuchAlgorithmException。要解决此问题,您应该停止指定提供程序并使用默认实现密码#getInstance不应与ECB一起作为密码模式调用,或者不设置密码模式,因为android上的默认模式是ECB,这是不安全的。因此,我已将您的答案设置为正式答案,并将我的答案与您的答案联系起来。@Nublodeveloper非常感谢。thanx:)
    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));