javax.crypto.Cipher 自 Android 6 Marshmallow 以来工作方式有所不同
javax.crypto.Cipher working differently since Android 6 Marshmallow
我已成功使用 javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding") 在 Android 上使用 DESFire 卡进行身份验证(按照此处的示例:)。它一直在 Android 4 到 5 的多个设备上运行,但在更新到 6 Marshmallow(和 6.0.1)的 Nexus 7 上停止运行。它在更新之前一直在同一台设备上工作。
似乎 Cipher 的工作方式不同,对于相同的密钥和数据给出不同的结果。 运行 下面的代码...
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,这是不安全的。
所以我最终使用了 ,它将(希望)适用于所有版本的 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));
我已成功使用 javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding") 在 Android 上使用 DESFire 卡进行身份验证(按照此处的示例:)。它一直在 Android 4 到 5 的多个设备上运行,但在更新到 6 Marshmallow(和 6.0.1)的 Nexus 7 上停止运行。它在更新之前一直在同一台设备上工作。
似乎 Cipher 的工作方式不同,对于相同的密钥和数据给出不同的结果。 运行 下面的代码...
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,这是不安全的。
所以我最终使用了
您也可以通过将密钥更改为 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));