我已成功使用javax.crypto.Cipher.getInstance("DESede/CBC/NoPadding")在Android上使用DESFire卡进行身份验证(遵循以下示例:https://stackoverflow.com/a/14160507/2095694 ).它已经在Android 4到5的几个设备上工作,但是我的Nexus 7停止工作,更新到6 Marshmallow(和6.0.1).在更新之前,它一直在使用同一设备.
似乎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的预期结果.
更新:我现在收到此警告:
不推荐使用BC提供程序,当targetSdkVersion移动到P时,此方法将抛出NoSuchAlgorithmException.要解决此问题,您应该停止指定提供程序并使用默认实现
Cipher#getInstance不应该使用ECB作为密码模式调用,或者不设置密码模式,因为android上的默认模式是ECB,这是不安全的.
所以我最终在这里使用了另一个答案(希望)适用于所有版本的Android.