(在香草EMV和共同核心规格的上下文中)
援引EMV 4.3集,第2,第8.1.2节:
用于应用密文生成的方法需要输入一个 独特IC卡应用密码主密钥MKAC和数据 如8.1.1节中所述选择,并且计算8字节的 应用密码在以下两个步骤中:
- 使用附件A1.3中指定的会话密钥导出函数从ICC 应用密码主密钥MKAC和ICC的2字节应用 交易计数器(ATC)派生应用密码会话密钥SKAC。
- 通过将附件A1.2中指定的MAC算法应用于所选数据并使用上一步中导出的应用程序密码会话密钥生成8字节应用程序密码。对于 AES 8字节应用密文是通过设置 参数s创建8.
的MKAC本身是从“发行人(应用密文)主密钥”(第8.3节)导出:
对于通用核心定义所定义的加密密钥,其密钥版本为'5',ICC主密钥应使用附录A1.4.2中描述的选项B方法推导出 。
有关详细说明,请参阅EMV Book 2的附录。
我可以提供以下的Java代码(半测试,但没有任何保证):
public static byte[] deriveMasterKey(byte[] issuerMasterKey, byte[] pan, byte[] panSequenceNumber) {
String concat;
if(((pan[pan.length-1]&0x0F)==0x0F)) {
String help=ByteArrayUtils.toString(pan);
concat = "0" + help.substring(0, help.length()-1) + ByteArrayUtils.toString(panSequenceNumber);
} else {
concat = ByteArrayUtils.toString(pan) + ByteArrayUtils.toString(panSequenceNumber);
}
logger.debug("Concat: " + concat);
byte[] concatBytes=ByteArrayUtils.fromSafeString(concat);
byte[] sha1Bytes = SwCryptUtils.sha1(concatBytes);
String sha1=ByteArrayUtils.toString(sha1Bytes);
logger.debug("X: " + sha1);
StringBuilder b1 = new StringBuilder();
StringBuilder b2 = new StringBuilder();
for(char c : sha1.toCharArray()) {
if(Character.isDigit(c)) {
b1.append(c);
} else {
b2.append((char)(c-('A'-'0')));
}
}
String y = b1.toString() + b2.toString();
logger.debug("Y': " + y);
y = y.substring(0, 16);
logger.debug("Y: " + y);
byte[] yBytes = ByteArrayUtils.fromSafeString(y);
byte[] leftBytes = SwCryptUtils.desEncryptEcb(issuerMasterKey, yBytes);
String left = ByteArrayUtils.toString(leftBytes);
logger.debug("Z_{L}': " + left);
byte[] yXorBytes = yBytes.clone();
for (int i = 0; i < yXorBytes.length; i++) {
yXorBytes[i]^=0xFF;
}
logger.debug("Y_{xor}': " + ByteArrayUtils.toString(yXorBytes));
byte[] rightBytes = SwCryptUtils.desEncryptEcb(issuerMasterKey, yXorBytes);
String right = ByteArrayUtils.toString(rightBytes);
logger.debug("Z_{R}': " + right);
String result=left+right;
logger.debug("MK:" + result);
return ByteArrayUtils.fromSafeString(result);
}
public static byte[] deriveCommonSessionKey(byte[] masterKey, byte[] atc) {
byte[] rBytes=Arrays.copyOf(atc, 8);
logger.debug("R: " + ByteArrayUtils.toString(rBytes));
byte[] f1Bytes=rBytes.clone();
f1Bytes[2]=(byte)0xF0;
logger.debug("F1: " + ByteArrayUtils.toString(f1Bytes));
byte[] f2Bytes=rBytes.clone();
f2Bytes[2]=(byte)0x0F;
logger.debug("F2: " + ByteArrayUtils.toString(f2Bytes));
byte[] f1EncBytes = SwCryptUtils.desEncryptEcb(masterKey, f1Bytes);
logger.debug("ENC(F1): " + ByteArrayUtils.toString(f1EncBytes));
byte[] f2EncBytes = SwCryptUtils.desEncryptEcb(masterKey, f2Bytes);
logger.debug("ENC(F2): " + ByteArrayUtils.toString(f2EncBytes));
byte[] result = ArrayUtils.addAll(f1EncBytes, f2EncBytes);
logger.debug("SK: " + ByteArrayUtils.toString(result));
return result;
}
public static byte[] generateApplicationCryptogram(byte[] sessionKey, byte[] terminalData, byte[] iccData) {
byte[] dataBytes = ArrayUtils.addAll(terminalData, iccData);
logger.debug("DATA: " + ByteArrayUtils.toString(dataBytes));
byte[] paddedDataBytes = ArrayUtils.add(dataBytes, (byte)0x80);
paddedDataBytes=Arrays.copyOf(paddedDataBytes, ((paddedDataBytes.length+7)/8)*8);
logger.debug("PADDED DATA: " + ByteArrayUtils.toString(paddedDataBytes));
byte[] skBytes=sessionKey;
byte[] skL = Arrays.copyOf(skBytes, 8);
logger.debug("SK_{L}: " + ByteArrayUtils.toString(skL));
byte[] skR = Arrays.copyOfRange(skBytes, 8, 16);
logger.debug("SK_{R}: " + ByteArrayUtils.toString(skR));
byte[] pom = SwCryptUtils.desEncryptCbcZeroIv(skL, paddedDataBytes);
logger.debug("POM: " + ByteArrayUtils.toString(pom));
pom=Arrays.copyOfRange(pom, pom.length-8, pom.length);
logger.debug("POM: " + ByteArrayUtils.toString(pom));
pom=SwCryptUtils.desDecryptEcb(skR, pom);
logger.debug("POM: " + ByteArrayUtils.toString(pom));
pom=SwCryptUtils.desEncryptEcb(skL, pom);
logger.debug("POM: " + ByteArrayUtils.toString(pom));
logger.debug("AC: " + ByteArrayUtils.toString(pom));
return pom;
}
的信息的非常好的来源是EFTlab website(其BP-CCalc工具可以用于计算密钥,密...)。
祝你好运!
来源
2017-02-22 22:35:15
vlp
如有疑问,请参考(EMV)标准。 –
嗨@MaartenBodewes,我按照你的建议和你的建议工作。 MDK遵循EMV 4.2标准。谢谢。 – brienze
太棒了,很高兴为你效力! –