"Visa2" 密钥多样化如何在 Gemalto 卡和 GPP 工具中发挥作用?
How does "Visa2" key diversification works in Gemalto cards and in GPP tool?
我有一堆 Gemalto java 卡,如下所示,我可以使用 GlobalPlatformPro:
进行相互身份验证
C:\globalPlatformPro> gp -visa2 -key 47454d5850524553534f53414d504c45 -list -debug -verbose -info
Reader: ACS ACR1281 1S Dual Reader ICC 0
ATR: 3B7D96000080318065B0831111E583009000
A>> 00A40400 00
A<< 6F198408A000000018434D00A50D9F6E061291921101009F6501FF 9000
***** Card info:
A>> 80CA9F7F 00
A<< 9F7F2A4090612812919211010041849D08192420C3033241840333418403344184000003250000000000000000 9000
***** KEY INFO
A>> 80CA00E0 00
A<< E012C00401FF8010C00402FF8010C00403FF8010 9000
VER:255 ID:1 TYPE:DES3 LEN:16
VER:255 ID:2 TYPE:DES3 LEN:16
VER:255 ID:3 TYPE:DES3 LEN:16
Key version suggests factory keys
A>> 80500000 08 2CA286A611F6CAFD 00
A<< 4D0041849D08192420C3FF0131D644E9913234DDE1F0A6A462C71805 9000
A>> 84820100 10 CC2D0CC35F6BD64F816A774D3ADB18F2
A<< 9000
//Useless lines for censored!
C:\globalPlatformPro>
由于 VISA 文档不公开,我查看了 GlobalPlatformPro 源代码以了解 visa2
中密钥多样化是如何发生的,我在那里找到了这些方法:
public static GPKeySet diversify(GPKeySet keys, byte[] diversification_data, Diversification mode, int scp) throws GPException {
try {
GPKeySet result = new GPKeySet();
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
for (KeyType v : KeyType.values()) {
if (v == KeyType.RMAC)
continue;
byte [] kv = null;
// shift around and fill initialize update data as required.
if (mode == Diversification.VISA2) {
kv = fillVisa(diversification_data, v);
} else if (mode == Diversification.EMV) {
kv = fillEmv(diversification_data, v);
}
// Encrypt with current master key
cipher.init(Cipher.ENCRYPT_MODE, keys.getKey(v).getKey(Type.DES3));
byte [] keybytes = cipher.doFinal(kv);
// Replace the key, possibly changing type. G&D SCE 6.0 uses EMV 3DES and resulting keys
// must be interpreted as AES-128
GPKey nk = new GPKey(keybytes, scp == 3 ? Type.AES : Type.DES3);
result.setKey(v, nk);
}
return result;
} catch (BadPaddingException |InvalidKeyException | IllegalBlockSizeException e) {
throw new GPException("Diversification failed.", e);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("Diversification failed.", e);
}
}
public static byte[] fillVisa(byte[] init_update_response, KeyType key) {
byte[] data = new byte[16];
System.arraycopy(init_update_response, 0, data, 0, 2);
System.arraycopy(init_update_response, 4, data, 2, 4);
data[6] = (byte) 0xF0;
data[7] = key.getValue();
System.arraycopy(init_update_response, 0, data, 8, 2);
System.arraycopy(init_update_response, 4, data, 10, 4);
data[14] = (byte) 0x0F;
data[15] = key.getValue();
return data;
}
所以我尝试为上述通信重复主机密码生成。我有:
主密钥 = 47454d5850524553534f53414d504c45
基于GlobalPlatform v 2.3 Card Specification:
Host_Challenge = 2CA286A611F6CAFD
初始更新 响应:4D0041849D08192420C3 FF01 31D644E9913234DD E1F0A6A462C71805
- 主要多元化数据=
4D0041849D08192420C3
- 关键信息=
FF01
:所以SCP01被使用了。
- 纸牌挑战=
31D644E9913234DD
- 卡片密码 =
E1F0A6A462C71805
所以,基于上面的GPP源代码:
- Diversification_Data =
4D00 9D081924 F001 4D00 9D081924 0F01
然后静态 ENC 密钥是:
Static_ENC = Encrypt(MasterKey, Diversification_Data )
所以,使用 this online tool,我有:
意思是:
Static_ENC_KEY = 84f2a84ecdade8cacc9e7e07faebe4e6
为了计算 ENC 会话密钥,我再次使用了 GlobalPlatform 规范:
所以我有:
- Derivation_Data =
913234DD 2CA286A6 31D644E9 11F6CAFD
所以 ENC_Session_Key
是:
ENC_Session_Key = b1ed5ea3f69978274d2ffe0de467ec1c
最后,主机密码的生成和验证是通过将 8 字节的卡质询和 8 字节的主机质询连接起来产生一个 16 字节的块并将这个 16 字节的数组与 80 00 00 00 00 00 00 00
.然后使用零 ICV 在 CBC 模式下使用 ENC 会话密钥对其进行签名:
Data2Encrypt = 31D644E9913234DD 2CA286A611F6CAFD 8000000000000000
我有:
好吧,我尝试了两次上述步骤,每次都在最后遇到错误的 host_cryptogram 值!但是当我重复这些步骤并在我的问题中逐行写下这些步骤时,我终于注意到我得到的最终结果与我问题第一个的 GPP 结果相同!因此,我没有删除我的问题,而是更愿意将其保留在这里以供将来的观众使用。
所以结论:
在智能卡中使用密钥多样化方案,在 GlobalPlatform 卡规范中提到的用于计算卡密码和 MAC 值的步骤中增加了一个步骤。该步骤是计算静态密钥。
Diversification_Data 用于静态键计算是 (source):
INITAL UPDATE 响应数据的前两个字节相同 xxh xxh
并且它的字节 [4:8] 是 IC Serial Number
.
使用主密钥、returns 静态密钥在 ECB 模式下使用三重 DES 算法加密多样化数据。
要检查卡密码,您必须将 8 字节的主机挑战和 8 字节的卡挑战以及“8000000000000000”连接起来。
生成字符串后,然后使用零 ICV 在 CBC 模式下使用 ENC 会话密钥对其进行签名。
我有一堆 Gemalto java 卡,如下所示,我可以使用 GlobalPlatformPro:
进行相互身份验证C:\globalPlatformPro> gp -visa2 -key 47454d5850524553534f53414d504c45 -list -debug -verbose -info
Reader: ACS ACR1281 1S Dual Reader ICC 0
ATR: 3B7D96000080318065B0831111E583009000
A>> 00A40400 00
A<< 6F198408A000000018434D00A50D9F6E061291921101009F6501FF 9000
***** Card info:
A>> 80CA9F7F 00
A<< 9F7F2A4090612812919211010041849D08192420C3033241840333418403344184000003250000000000000000 9000
***** KEY INFO
A>> 80CA00E0 00
A<< E012C00401FF8010C00402FF8010C00403FF8010 9000
VER:255 ID:1 TYPE:DES3 LEN:16
VER:255 ID:2 TYPE:DES3 LEN:16
VER:255 ID:3 TYPE:DES3 LEN:16
Key version suggests factory keys
A>> 80500000 08 2CA286A611F6CAFD 00
A<< 4D0041849D08192420C3FF0131D644E9913234DDE1F0A6A462C71805 9000
A>> 84820100 10 CC2D0CC35F6BD64F816A774D3ADB18F2
A<< 9000
//Useless lines for censored!
C:\globalPlatformPro>
由于 VISA 文档不公开,我查看了 GlobalPlatformPro 源代码以了解 visa2
中密钥多样化是如何发生的,我在那里找到了这些方法:
public static GPKeySet diversify(GPKeySet keys, byte[] diversification_data, Diversification mode, int scp) throws GPException {
try {
GPKeySet result = new GPKeySet();
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
for (KeyType v : KeyType.values()) {
if (v == KeyType.RMAC)
continue;
byte [] kv = null;
// shift around and fill initialize update data as required.
if (mode == Diversification.VISA2) {
kv = fillVisa(diversification_data, v);
} else if (mode == Diversification.EMV) {
kv = fillEmv(diversification_data, v);
}
// Encrypt with current master key
cipher.init(Cipher.ENCRYPT_MODE, keys.getKey(v).getKey(Type.DES3));
byte [] keybytes = cipher.doFinal(kv);
// Replace the key, possibly changing type. G&D SCE 6.0 uses EMV 3DES and resulting keys
// must be interpreted as AES-128
GPKey nk = new GPKey(keybytes, scp == 3 ? Type.AES : Type.DES3);
result.setKey(v, nk);
}
return result;
} catch (BadPaddingException |InvalidKeyException | IllegalBlockSizeException e) {
throw new GPException("Diversification failed.", e);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("Diversification failed.", e);
}
}
public static byte[] fillVisa(byte[] init_update_response, KeyType key) {
byte[] data = new byte[16];
System.arraycopy(init_update_response, 0, data, 0, 2);
System.arraycopy(init_update_response, 4, data, 2, 4);
data[6] = (byte) 0xF0;
data[7] = key.getValue();
System.arraycopy(init_update_response, 0, data, 8, 2);
System.arraycopy(init_update_response, 4, data, 10, 4);
data[14] = (byte) 0x0F;
data[15] = key.getValue();
return data;
}
所以我尝试为上述通信重复主机密码生成。我有:
主密钥 = 47454d5850524553534f53414d504c45
基于GlobalPlatform v 2.3 Card Specification:
Host_Challenge = 2CA286A611F6CAFD
初始更新 响应:4D0041849D08192420C3 FF01 31D644E9913234DD E1F0A6A462C71805
- 主要多元化数据=
4D0041849D08192420C3
- 关键信息=
FF01
:所以SCP01被使用了。 - 纸牌挑战=
31D644E9913234DD
- 卡片密码 =
E1F0A6A462C71805
所以,基于上面的GPP源代码:
- Diversification_Data =
4D00 9D081924 F001 4D00 9D081924 0F01
然后静态 ENC 密钥是:
Static_ENC = Encrypt(MasterKey, Diversification_Data )
所以,使用 this online tool,我有:
意思是:
Static_ENC_KEY = 84f2a84ecdade8cacc9e7e07faebe4e6
为了计算 ENC 会话密钥,我再次使用了 GlobalPlatform 规范:
所以我有:
- Derivation_Data =
913234DD 2CA286A6 31D644E9 11F6CAFD
所以 ENC_Session_Key
是:
ENC_Session_Key = b1ed5ea3f69978274d2ffe0de467ec1c
最后,主机密码的生成和验证是通过将 8 字节的卡质询和 8 字节的主机质询连接起来产生一个 16 字节的块并将这个 16 字节的数组与 80 00 00 00 00 00 00 00
.然后使用零 ICV 在 CBC 模式下使用 ENC 会话密钥对其进行签名:
Data2Encrypt = 31D644E9913234DD 2CA286A611F6CAFD 8000000000000000
我有:
好吧,我尝试了两次上述步骤,每次都在最后遇到错误的 host_cryptogram 值!但是当我重复这些步骤并在我的问题中逐行写下这些步骤时,我终于注意到我得到的最终结果与我问题第一个的 GPP 结果相同!因此,我没有删除我的问题,而是更愿意将其保留在这里以供将来的观众使用。
所以结论:
在智能卡中使用密钥多样化方案,在 GlobalPlatform 卡规范中提到的用于计算卡密码和 MAC 值的步骤中增加了一个步骤。该步骤是计算静态密钥。
Diversification_Data 用于静态键计算是 (source):
INITAL UPDATE 响应数据的前两个字节相同 xxh xxh
并且它的字节 [4:8] 是 IC Serial Number
.
使用主密钥、returns 静态密钥在 ECB 模式下使用三重 DES 算法加密多样化数据。
要检查卡密码,您必须将 8 字节的主机挑战和 8 字节的卡挑战以及“8000000000000000”连接起来。 生成字符串后,然后使用零 ICV 在 CBC 模式下使用 ENC 会话密钥对其进行签名。