cryptography - 密码版本 5 的方法 key 推导 (MKD)

标签 cryptography emv

我正在开发用于 EMV 交易的软件,但我面临着雇佣我的公司的文档严重缺乏的问题。
其中之一是关于用于生成 ARQC 的 MKD(在第一个 GENERATE AC 期间)。我从消息请求中知道IAD如下:

0FA501A030F8000000000000000000000F000000000000000000000000000000



根据它的密码版本是 5,但我不知道 MKD。

有没有参与过这个主题的人知道我应该用来生成 ARQC 的 MKD 吗?

我很感激任何评论。
谢谢。

最佳答案

(在 Vanilla EMV 和通用核心规范的背景下)

引用 EMV 4.3 ,第 2 册,第 8.1.2 节:

The method for Application Cryptogram generation takes as input a unique ICC Application Cryptogram Master Key MKAC and the data selected as described in section 8.1.1, and computes the 8-byte Application Cryptogram in the following two steps:

  1. Use the session key derivation function specified in Annex A1.3 to derive an Application Cryptogram Session Key SKAC from the ICC Application Cryptogram Master Key MKAC and the 2-byte Application Transaction Counter (ATC) of the ICC.
  2. Generate the 8-byte Application Cryptogram by applying the MAC algorithm specified in Annex A1.2 to the data selected and using the Application Cryptogram Session Key derived in the previous step. For AES the 8-byte Application Cryptogram is created by setting the parameter s to 8.


MKAC 本身源自“颁发者(应用程序密码)主 key ”(第 8.3 节):

For a cryptogram defined by the Common Core Definitions with a Cryptogram Version of '5', the ICC Master Key shall be derived using the Option B method described in Annex A1.4.2.



有关详细说明,请参阅 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 工具可用于计算 key 、密码...)。

祝你好运!

关于cryptography - 密码版本 5 的方法 key 推导 (MKD),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42304386/

相关文章:

hash - 硬件 token 设备如何工作?

c - 无法使用 APDU 命令从 Contact VISA 卡读取信息

emv - MasterCard/EMV 标签 - 详细定义

c# - 将 md5 哈希字节数组转换为字符串

java - 进行椭圆曲线加密时出现无效 key 异常

.net - 使用 key 和 IV 在 SQL 中解密 AES

algorithm - 能否对这段代码进行逆向工程以找到随机种子?

payment - EMV: Second Generate AC Results in 6985 SW_Error 访问条件不满足

encryption - MSR DUKPT 的 MAC 变体?

tags - 读取EMV卡序列号