哪个是密码版本5的方法密钥派生(MKD)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哪个是密码版本5的方法密钥派生(MKD)相关的知识,希望对你有一定的参考价值。

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

0FA501A030F8000000000000000000000 F00000000000000000000000000000000

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

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

我感激任何评论。谢谢。

答案

(在香草EMV和共同核心规范的背景下)

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

应用程序密码生成方法将唯一的ICC应用程序密码主密钥MKAC和按8.1.1节所述选择的数据作为输入,并按以下两个步骤计算8字节应用程序密码:

  1. 使用附件A1.3中规定的会话密钥导出函数从ICC应用程序密码主密钥MKAC和ICC的2字节应用程序事务计数器(ATC)中导出应用程序密码会话密钥SKAC。
  2. 通过将附件A1.2中指定的MAC算法应用于所选数据并使用上一步骤中派生的应用程序密码会话密钥,生成8字节应用程序密码。对于AES,通过将参数s设置为8来创建8字节的应用程序密码。

MKAC本身源自'Issuer(Application Cryptogram)Master Key'(第8.3节):

对于由密码版本为“5”的公共核心定义定义的密码,ICC主密钥应使用附录A1.4.2中描述的选项B方法导出。

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

祝好运!

以上是关于哪个是密码版本5的方法密钥派生(MKD)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 密钥生成中调整密钥派生迭代? [关闭]

rhel 7 Linux 上的哪个密钥环后端可以在存储凭证时避免额外的密码?

如何管理在每个 git 版本中添加私有代码片段?

一周第三次课(12月13日)

Debian9.5下ssh密钥登录配置步骤(免密码登录)和ssh-keygen 命令常用参数

Rijndael加密法