自 Android 6 Marshmallow 以来,javax.crypto.Cipher 的工作方式有所不同

标签 java android openssl android-6.0-marshmallow javax.crypto

我已经成功地使用 javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding") 在 Android 上使用 DESFire 卡进行身份验证(按照此处的示例:https://stackoverflow.com/a/14160507/2095694)。它一直在从 Android 4 到 5 的多种设备上运行,但在更新到 6 Marshmallow(和 6.0.1)的 Nexus 7 上停止运行。它在更新之前一直在同一台设备上工作。

似乎 Cipher 的工作方式不同,对于相同的 key 和数据给出不同的结果。运行以下代码...

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

正常值,它应该是什么,总是有效,卡最终正确验证,所以它按照卡期望的方式进行。如前所述,我尝试了多种设备(Android 4 和 5),它们给出了相同的结果。

但是在我的 Nexus 7 上现在有了 Marshmallow 我得到了其他东西(并且身份验证最终失败了)

RSLT: F3ADA5969FA9369C 

图书馆有什么变化吗?

最佳答案

他们似乎更改了 Marshmallow 中的默认提供程序。

一个简单的:

cipher.getProvider().getName();

为 Marshmallow 显示“AndroidOpenSSL”,之前它是“BC”(我想是 BouncyCaSTLe)。

使用其他 getInstance 重载...

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

...在我的 Nexus with Marshmallow 上给我预期的结果。

更新:我现在收到此警告:

BC 提供程序已弃用,当 targetSdkVersion 移至 P 时,此方法将抛出 NoSuchAlgorithmException。要解决此问题,您应该停止指定提供程序并使用默认实现
Cipher#getInstance 不应使用 ECB 作为密码模式或未设置密码模式调用,因为 android 上的默认模式是 ECB,这是不安全的。

所以我最终使用了 other answer here这将(希望)适用于所有版本的 Android。

关于自 Android 6 Marshmallow 以来,javax.crypto.Cipher 的工作方式有所不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34286798/

相关文章:

java - 从千兆字节文件中读取

JavaFX MediaPlayer 在 1 分钟以上后停止

android - Camera.getParameters() 在 Galaxy Tab 上返回 null

Android -- 设备解锁时会发生什么?

android - 在 ndk 中构建 openssl 时出错没有这样的文件或目录

Python pyopenssl 签名证书错误(Firefox : SEC_ERROR_REUSED_ISSUER_AND_SERIAL, chrome:ERR_CERT_AUTHORITY_INVALID)

java - 在 Netbeans+Ant 中,如何避免 wsimport 在每次构建时重建 Web 服务客户端?

java - Hibernate 一对多无法初始化集合

android - 你如何用opengl es显示fps(android)

ssl - OpenSSL TLS SRP