2017-02-17 89 views
1

我正在开发用于EMV交易的软件,而且我正面临着雇用我的公司大量缺乏文档。 其中之一是关于生成ARQC的MKD(在第一次GENERATE AC期间)。我从消息请求的IAD是如下认识:密码学版本5的方法密钥派生(MKD)

0FA501A030F8000000000000000000000F000000000000000000000000000000

根据它的密文的版本是5,但我不知道该MKD想法。

有没有人曾参与这个主题知道一些关于我应该用来生成ARQC的MKD?

我很感激任何意见。 谢谢。

+0

如有疑问,请参考(EMV)标准。 –

+0

嗨@MaartenBodewes,我按照你的建议和你的建议工作。 MDK遵循EMV 4.2标准。谢谢。 – brienze

+0

太棒了,很高兴为你效力! –

回答

1

(在香草EMV和共同核心规格的上下文中)

援引EMV 4.3集,第2,第8.1.2节:

用于应用密文生成的方法需要输入一个 独特IC卡应用密码主密钥MKAC和数据 如8.1.1节中所述选择,并且计算8字节的 应用密码在以下两个步骤中:

  1. 使用附件A1.3中指定的会话密钥导出函数从ICC 应用密码主密钥MKAC和ICC的2字节应用 交易计数器(ATC)派生应用密码会话密钥SKAC。
  2. 通过将附件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工具可以用于计算密钥,密...)。

祝你好运!

+0

欢迎您@brienze ...祝您的项目顺利! – vlp

+0

嗨@vlp,首先非常感谢你! 我遵循了您的指导原则,我可以完成ARQC。为了生成MDK,我不得不做一些更改,因为我的PAN等于16位数字,所以我不必使用SHA-1(选项B)。我使用选项A代替。 我为SessionKey生成做的其他更改;因为我的密钥= 8n我计算了SK = ALG(MK)[R],而不是像你那样应用DES。 然后,为了生成密码我使用了ECB模式,而不是使用CBC模式,并且我正确地完成了ARQC! 谢谢! – brienze