为什么智能卡回答 6982 到 EXTERNAL AUTHENTICATE 而所有的计算都是正确的?

Why Smart Card answers 6982 to EXTERNAL AUTHENTICATE while all calculations are correct?

我正在尝试使用 python 与智能卡建立安全通道 SCP02。我的智能卡使用串行端口连接到终端,我使用 pySerial 发送 APDU。

我正确发送命令 SELECT ISDINITIALIZE UPDATE,然后我尝试按照下面的 python 代码执行 EXTERNAL AUTHENTICATE

import serial
from serial.serialutil import EIGHTBITS, PARITY_NONE, STOPBITS_ONE, XON
import exchangeApdu
import numpy as np
from Crypto.Cipher import DES, DES3

CARD_KEY = [0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F]
ZERO_IV_8 = [0X00, 0X00, 0x00, 0x00, 0X00, 0X00, 0x00, 0x00]

serialObj = serial.Serial(  port = 'COM3', \
                            baudrate = 115200, \
                            parity = PARITY_NONE, \
                            bytesize = EIGHTBITS, \
                            stopbits = STOPBITS_ONE, \
                            timeout = 0.1, \
                            xonxoff = True, \
                            rtscts = True, \
                            inter_byte_timeout = None, \
                            dsrdtr = True   )
if(serialObj.is_open):
    serialObj.close()
serialObj.open()

# Select ISD
select_apdu = [0x00, 0xA4, 0x04, 0x00, 0x00]
response = exchangeApdu.exchange(serialObj, select_apdu)
print('Select ISD: ' + response.hex())

# Initialize Update
host_challenge = np.random.bytes(8)
initialize_update_apdu = [0x80, 0x50, 0x00, 0x00, 0x08] + list(host_challenge)
response = exchangeApdu.exchange(serialObj, initialize_update_apdu)
print('Initialize Update: ' + response.hex())

key_derivation_data = list(response[:10])
key_information = list(response[10:12])
sequence_counter = list(response[12:14])
card_challenge = list(response[14:20])
card_cryptogram = list(response[20:28])

derivation_data = [0X01, 0X82] + sequence_counter + \
    [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
encryptor = DES3.new(bytes(CARD_KEY), DES3.MODE_CBC, bytes(ZERO_IV_8))
S_ENC = encryptor.encrypt(bytes(derivation_data))
derivation_data = [0X01, 0X01] + sequence_counter + \
    [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
encryptor = DES3.new(bytes(CARD_KEY), DES3.MODE_CBC, bytes(ZERO_IV_8))
S_MAC = encryptor.encrypt(bytes(derivation_data))

# External Authenticate
PADDING_DES = [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
host_auth_data = sequence_counter + card_challenge + list(host_challenge) + PADDING_DES
card_auth_data = list(host_challenge) + sequence_counter + card_challenge + PADDING_DES
encryptor = DES3.new(bytes(S_ENC), DES3.MODE_CBC, bytes(ZERO_IV_8))
host_cryptogram = list(bytes((encryptor.encrypt(bytes(host_auth_data)))[-8:]))
encryptor = DES3.new(bytes(S_ENC), DES3.MODE_CBC, bytes(ZERO_IV_8))
card_cryptogram = list(bytes((encryptor.encrypt(bytes(card_auth_data)))[-8:]))

external_auth_apdu = [0x84, 0x82, 0x00, 0x00, 0x10] + list(host_cryptogram)
external_auth_apdu_padded = external_auth_apdu + [0x80, 0x00, 0x00]
cipher = DES.new(bytes(list(S_MAC[:8])), DES3.MODE_CBC, bytes(ZERO_IV_8))
step1 = cipher.encrypt(bytes(external_auth_apdu_padded))
cipher = DES.new(bytes(list(S_MAC[8:16])), DES3.MODE_ECB)
step2 = cipher.decrypt(step1[-8:])
cipher = DES.new(bytes(list(S_MAC[:8])), DES3.MODE_ECB)
apdu_mac = list(bytes(cipher.encrypt(step2[-8:])))
external_auth_apdu = external_auth_apdu + apdu_mac
response = exchangeApdu.exchange(serialObj, external_auth_apdu)
print('External Authenticate: ' + response.hex())

serialObj.close()

“exchangeApdu”是一个 class,我在其中处理了 61xx 和 6Cxx 等 APDU。这是此代码的输出:

Select ISD: 6f108408a000000151000000a5049f6501ff9000
Initialize Update: 000081210103b49d856dff02004eecc2acd14944a54f9790521d203b9000
External Authenticate: 6982

智能卡拒绝通道在串行端口上发回 6982。我已经使用在线加密工具检查了整个过程,我很确定加密部分完全正确。 我还使用 USB 智能卡 reader 检查了此过程,再次正常工作,我得到 9000。 我再次在智能卡模拟器上检查了这个,即 JCIDE,并再次 9000。 因此我确信程序的正确性。 我在Win10上用过一个串口监控应用程序检查过串口APDUs的正确性,没有报错。我想一定是智能卡本身有问题,但我猜不出是什么问题!

有人知道问题出在哪里吗?

您可以使用 GlobalPlatform 库中的工作 Test Vectors 并测试您执行的加密逻辑是否真的有效。也可能是你的卡使用了一些密钥派生方案,即你使用的密钥不能直接使用。

我找到了问题所在。字节数组未正确传送到卡,因此出现错误 6982。 这是指 ISO7816 标准,它需要通过特定延迟将字节发送到卡。我在字节之间设置了延迟,现在一切正常。