(В контексте ванили EMV и общего ядра Спецификации)
Приводя EMV 4.3, книга 2, раздел 8.1.2:
Способ генерации Применение криптограммы принимает в качестве входных уникальный МТП Application Мастер-ключ Cryptogram MKAC и данные , выбранные, как описано в разделе 8.1.1, и вычисляют 8-байтовую криптограмму приложения на следующих двух этапах:
- Используйте функцию деривации ключа сеанса, указанную в Приложении A1.3, чтобы получить ключ сеанса сеанса криптограммы приложения из ICC. Мастер-ключ Cryptogram Application MKAC и 2-байтовое приложение. Счетчик транзакций (ATC) ICC.
- Сгенерируйте 8-байтовую криптографию приложения, применяя алгоритм MAC, указанный в Приложении A1.2, к выбранным данным и используя ключ сеанса криптограмм приложения, полученный на предыдущем шаге. Для AES 8-байтовые Криптограммы Применения создаются путем установки параметр с до 8.
Самого MKAC является производной от «Эмитента (Приложение криптограммы) мастер-ключ» (раздел 8.3):
Для криптограммы, определяемой определениями общего ядра с помощью версии криптограммы «5», главный ключ ICC должен быть получен с использованием метода опциона B, описанного в приложении 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 может использоваться для вычисления ключей, криптограммы ...).
Удачи вам!
При возникновении сомнений обратитесь к стандарту (EMV). –
Привет @MaartenBodewes, я сделал, как вы мне предложили, и это сработало. MDK соответствует стандарту EMV 4.2. Благодарю. – brienze
Отлично, рад, что сработало для вас! –