目录
1 DUKPT简介
DUKPT(Derived Unique Key Per Transaction)是被ANSI定义的一套密钥管理体系和算法,用于解决金融支付领域的信息安全传输中的密钥管理问题,应用于对称密钥加密MAC、PIN等数据安全方面。保证每一次交易流程使用唯一的密钥,采用一种不可逆的密钥转换算法,使得无法从当前交易数据信息破解上一次交易密钥。要求收单行与终端必须同步支持该项密钥管理技术。由交易发起端点(S-TRSM,如POS、ATM)与交易接收端点(R-TRSM,如收单行)两部分组成。
注:TRSM(Tamper-Resistant Security Module)是一个具备阻止攻击能力的安全模块。
以下是 DUKPT的一些关键特点:
-
唯一性:DUKPT为每个交易生成一个唯一的加密密钥,确保即使相同的主密钥在不同交易中使用,也能产生不同的派生密钥。
-
分散:DUKPT使用一种称为分散的技术,通过将密钥按照一定规则扩展为不同的密钥,以增加密钥的安全性。
-
保密性:DUKPT通过不存储或传输主密钥的完整值,而是使用一个初始的主密钥派生出每个交易的密钥,从而增加了密钥的保密性。
-
动态变化:DUKPT可以动态地变化,以适应不同的交易条件。这使得攻击者更难预测下一个派生密钥。
-
逆推困难性:由于DUKPT的分散和动态性,逆推派生密钥以获取原始主密钥是非常困难的。
-
用途:DUKPT主要用于保护磁条卡数据、PIN(个人身份号码)加密和其他金融交易中的密钥管理。
2 TDEA DUKPT与AES DUKPT的区别
TDEA版本DUKPT是2009年10月13日发布的ANSI X9.24-1-2009,而AES版本DUKPT是2017年10月11日发布的ANSI X9.24-3-2017。
AES版本与TDEA版本的DUKPT非常相似。但是,为了利用密码学技术的进步和计算机处理速度和内存大小的提高,进行了一些更改。AES版本和TDEA版本的DUKPT之间的区别如下所示:
-
用于密钥导出的基于DEA的单向函数被基于NIST SP800-108的密钥导出函数所取代,并使用AES-ECB作为底层函数。
-
在AES DUKPT中,所有密钥都使用相同的密钥导出函数来导出。TDEA DUKPT使用4种不同的密钥派生技术:triple-DEA派生初始DUKPT密钥,DES-X样式单向函数用于寄存器密钥,PIN和MAC密钥的变体,以及TDEA和变体的结合派生数据加密密钥。
-
初始密钥 ID(IKID)为64位,而不是59位。IKID由32位 BDKID和32位DERlVATION ID(DID)组成。以前,这被称为初始密钥序列号,由40 位密钥集索引和 19 位设备 ID 组成。
-
交易计数器(TC)是32位而不是21位,最多允许16个1位代替10个
-
密钥序列号(KSN)是96位而不是80位。KSN由初始密钥ID和交易计数器组成。
-
该算法支持具有21或32个密钥寄存器的交易源设备
-
该算法包括支持在现有密钥下加载新的初始密钥
3 python实现
from Crypto.Cipher import AES
from enum import Enum
import binascii
# print out a hexadecimal number in groups of 8 capitalized digits. Only for debugging.
def ToGroups(x):
n = len(x)//8
for i in range(0, n):
print(x[i*8:(i+1)*8].upper(), end="")
if i != n-1:
print(" ", end="")
# Convert a 32-bit integer to a list of bytes in big-endian order. Used to convert counter values to byte lists.
def IntToBytes(x):
return [((x >> i) & 0xff) for i in (24,16,8,0)]
# Print out a debug message at depth d. Takes an arbitrary list of strings and byte lists or bytearrays.
# Lists of bytes are pretty-printed in hex.
def D(d, *args):
global Debug
if Debug:
print(".", end="")
for i in range(0,2*d):
print(" ", end="")
for x in args:
if type(x) is str:
print(x, " ", end="")
elif type(x) is list:
value = binascii.hexlify(bytearray(x)).decode("utf-8")
ToGroups(value)
elif type(x) is bytearray:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
elif type(x) is bytes:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
else:
print(x, " ", end="")
print("")
def P(*args):
for x in args:
if type(x) is str:
print(x, " ", end="")
elif type(x) is list:
value = binascii.hexlify(bytearray(x)).decode("utf-8")
ToGroups(value)
elif type(x) is bytearray:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
elif type(x) is bytes:
value = binascii.hexlify(x).decode("utf-8")
ToGroups(value)
else:
print(x, " ", end="")
print("")
# B.3.1. Enumerations
class DerivationPurpose(Enum):
_InitialKey = 0
_DerivationOrWorkingKey = 1
class KeyType(Enum):
_2TDEA = 0
_3TDEA = 1
_AES128 = 2
_AES192 = 3
_AES256 = 4
class KeyUsage(Enum):
_KeyEncryptionKey = 0x0002
_PINEncryption = 0x1000
_MessageAuthenticationGeneration = 0x2000
_MessageAuthenticationVerification = 0x2001
_MessageAuthenticationBothWays = 0x2002
_DataEncryptionEncrypt = 0x3000
_DataEncryptionDecrypt = 0x3001
_DataEncryptionBothWays = 0x3002
_KeyDerivation = 0x8000
_KeyDerivationInitialKey = 9
# Count the number of 1 bits in a counter value. Readable, but not efficient.
def Count_One_Bits(x):
bits = 0
mask = 1 << (NUMREG-1)
while mask > 0:
if x & mask:
bits = bits + 1
mask = mask >> 1
return bits
# B.3.2. Key Length function
# Length of an algorithm's key, in bits.
def Key_Length( keyType ):
if