技术文章
如何解密信用卡数据(第二部分)
客户经常会问: 我该如何解密 ID TECH 信用卡读卡器输出的数据?
答案是:您需要知道用于加密数据的算法,以及所使用的密钥。然后,您就可以使用该密钥来解密数据。
如今,几乎所有信用卡数据都使用一次性密钥进行加密,这种密钥通过一种称为 DUKPT (即"每笔交易派生唯一密钥")的特殊密钥管理方案获取。需要理解的重要一点是:在 DUKPT 体系中,每笔交易都有自己的密钥。该密钥不能用于任何其他交易;因此,重放攻击是不可能的。
问题是:如何派生出能够解锁某笔交易的 DUKPT 密钥?答案是:一般来说,您需要该笔交易的密钥序列号(KSN),以及一个称为 IPEK 的特殊值,即注入到信用卡读卡器中的初始密钥。而 IPEK 则是由一个称为 BDK(基础派生密钥)的超机密密钥派生而来的——BDK 本身从不会被注入到读卡器中。与 BDK 不同,IPEK 对应于特定的物理设备,是唯一的。(一个 BDK 可以作为众多唯一 IPEK 的来源。)如果您不知道自己设备的 IPEK(您也确实没有理由知道,因为 IPEK 不会被记录在任何地方),可以通过 KSN 和基础派生密钥,使用以下文章中描述的方法派生得到: 本文第一部分.
派生会话密钥(有时称为工作密钥,或简称"数据密钥")实际上最好被理解为一个三步流程。这些步骤是:
1. 使用 BDK 和 KSN 派生 IPEK。(请参阅 本文第一部分 了解具体操作细节。)
2. 使用 ANSI X9.24(DUKPT)密钥派生算法,从 KSN 和 IPEK 派生出基础密钥,即初始"派生密钥"。
3. 将第 2 步得到的派生密钥转换为您所需的数据密钥、PIN 密钥或 MAC 密钥。(请注意,虽然大多数信用卡读卡器被设置为使用数据变体密钥作为交易会话密钥,但有些设备实际上被设置为使用 PIN 变体。)
让我们继续看看如何获取"派生密钥"(第 2 步),因为这是整个三步流程中最为繁琐的部分。一旦获得派生密钥,我们将讨论如何将其转换为数据、PIN 或 MAC 变体,这相对容易。
下面我们将使用相当数量的伪代码,但请放心,您可以在我们颇受欢迎的工具中找到以下所有步骤的完整、可运行源代码(JavaScript 版本): 加密/解密工具(如果您还没尝试过,现在就试试吧。这是一个自包含的网页,可在任何现代浏览器中运行。)
派生密钥
要派生可用于创建数据、PIN 或 MAC 变体的基础密钥,您需要从交易 KSN 和 IPEK 开始。在获得它们之后(请再次参阅 本系列第一部分),执行以下操作:
1. 取得 10 字节 KSN 的最后(最右侧)8 字节。丢弃前两个字节。
2. 创建一个 BaseKSN 变量来保存 8 字节 KSN 的掩码版本。通过将第 1 步得到的 8 字节 KSN 与(十六进制)值 0xFFFFFFFFFFE00000 进行 AND 运算,获得掩码版本。
3. 通过将原始(未经掩码!)10 字节 KSN 的最后三个字节与 0x1FFFFF 进行 AND 运算,获取计数器位。(请记住,KSN 的最后 21 位构成交易计数器。)我们将其保存在一个变量中,名称就叫做(还能叫什么呢?) 计数器.
4. 将 16 字节的 IPEK 复制到一个名为 curKey.
5. 现在我们需要建立一个循环。每次循环时,我们将检查计数器位(从最高位即第 21 位开始;第二次循环时检查第 20 位;然后是第 19 位;以此类推)。每当发现一个置位的比特,我们就将其通过 OR 运算合并到 BaseKSN 中,然后调用 generateKey() 来更新 curKey。每次循环时,BaseKSN 都会累积比特位,而 curKey 值将随着我们发现的每一个置位的计数器位而更新。
那么 generateKey() 做了什么?问得好!如果您的编程语言支持 BigInteger 大整数运算,代码大致如下:
好。可以看到,16 字节密钥经过掩码处理后,被用来加密 8 字节的 ksn 值,得到新密钥的左半部分(左侧 8 字节)。新密钥的右半部分是使用同一 ksn、但用未经掩码的密钥所生成的密文。
最后,您需要了解 encryptRegister() 是什么样的。就是这个:
请注意,密码块链接(Cipher Block Chaining)在此实际上毫无意义,因为我们正在加密一个 8 字节的值(即一个数据块)。没有什么可"链接"的。代码中包含它,仅仅是因为加密例程恰好需要一个表示是否启用链接的参数。
另请注意,我们使用的是 8 字节密钥进行加密。当密钥仅为 8 字节长时,TDES 会默认降级为单 DES。这是因为在三重 DES 中,使用 8 字节密钥所形成的加密/解密/加密循环,其效果等同于执行一次单次加密。
用通俗的话来说,该例程使用 16 字节密钥的前 8 字节,对一个特殊值进行加密,而该特殊值是由密钥的后 8 字节与(8 字节的)ksn 进行异或(XOR)运算得到的。其结果就是 ksn 的单向哈希值。
综合起来,上述第 5 步中的循环最终会产生一个 curKey 值,作为基础密钥(basis key),我们可以由此派生出 Data、PIN 或 MAC 变体。(第 5 步中的循环是、或应当是某个函数的一部分,该函数最终返回 curKey,即基础密钥。)
现在让我们更详细地了解这三种"密钥变体"选项。
生成 Data、PIN 和 MAC 密钥变体
ANSI X9.24 允许 DUKPT 密钥呈现为三种最终形式之一,称为变体(variants)。这些形式分别是 MAC、PIN 和 Data。我们暂且不讨论这几种密钥类型各自的用途,先专注于它们是如何生成的。
任何变体的起点都是一个 DUKPT 基础密钥(即我们在前面第 5 步中所称的派生密钥 curKey )。要得到 MAC 变体,只需将基础密钥("派生密钥")与一个特殊常量进行异或运算即可:
PIN 变体的生成方式与之类似,只是使用了不同的常量:
Data 变体则需要另一个不同的常量:
对于 MAC 和 PIN 变体而言,异或运算就是生成相应会话密钥的最后一步。而对于 Data 变体,通常还要再多执行一步,即进行一次单向哈希(以防止有人将 Data 密钥反向变换回 MAC 密钥)。伪代码如下:
用文字说明:首先,使用 EDE3 扩展方法获得派生密钥的 24 字节版本。(这只是将 16 字节密钥的前 8 字节复制到密钥尾部,从而形成一个首 8 字节与末 8 字节相同的 24 字节密钥。)然后用该密钥对 16 字节派生密钥的前 8 字节进行 TDES 加密,从而生成一段 8 字节密文。这就是最终数据密钥的左半部分。要生成右半部分,则使用同一个 24 字节密钥对 derivedKey 的后 8 字节进行加密。将两段 8 字节密文(左半与右半)拼接起来,整个过程便完成了。
已知正确的参考值
如果您打算自己动手实践,可能希望用一些已知正确的参考值来核对您的结果。那么,先从 16 字节的 BDK 值 0123456789ABCDEFFEDCBA9876543210(十六进制)开始,这是大家普遍使用的测试密钥值。再尝试一个测试 KSN 值 629949012C0000000003。利用这两个值,您应该能够派生出 IPEK 值 D2943CCF80F42E88E23C12D1162FD547。(如需了解如何派生 IPEK,请参阅 本文第一部分 。)
以上述 IPEK 为起点,在派生"派生密钥"(或称 DUKPT 基础密钥)时,您应得到以下值:
在 KSN 计数器循环中首次进入 "if" 分支时,您的 BaseKSN 将为 49012C0000000002, curKey 在执行后将变为 B58CDA5C7A1E9FF5E7335B988626D01A,紧接着是 generateKey().
在第二次进入计数器循环的 "if" 分支时,您将已处理完计数器的两个"ON"位,因此您的 BaseKSN 将为 49012C0000000003,所得 curKey 为 841AB7B94ED086EBC2B8A8385DA7DFCA。(请记住,您是从最高有效位开始,依次将计数器的位按 OR 运算写入 BaseKSN。如果计数器的末位为 0x0F,那么随着您逐位进行 OR 运算,BaseKSN 将依次变为 49012C0000000008 → 49012C000000000C → 49012C000000000E → 49012C000000000F。)
由此,您的"派生密钥"将为 841AB7B94ED086EBC2B8A8385DA7DFCA。
将其与 data 变体常量进行异或运算后,派生密钥将变为 841AB7B94E2F86EBC2B8A8385D58DFCA。
对该值的前半部分和后半部分分别进行加密时,使用的 EDE3 扩展密钥为 841AB7B94E2F86EBC2B8A8385D58DFCA841AB7B94E2F86EB,您最终应得到数据密钥 F739AEF595D3877F731782D28BB6AC4F。也就是说:使用该 24 字节 EDE3 密钥加密 841AB7B94E2F86EB,应得到密文 F739AEF595D3877F;使用相同密钥加密 C2B8A8385D58DFCA,应得到密文 731782D28BB6AC4F。将两段密文拼接起来即告完成。如此,您便得到了一个 16 字节密钥,可用于解密 KSN 为 629949012C0000000003 的那笔交易数据。
示例代码:ID TECH 加密/解密工具
若想查看本文讨论的所有 DUKPT 密钥派生例程的完整源代码,请务必下载(并查阅源代码)我们基于 HTML 和 JavaScript 的 加密/解密工具,它可以计算 IPEK、派生全部 3 种 DUKPT 密钥变体、使用 TDES 或 AES 进行数据加密或解密,等等。您可以借助 Chrome 出色的开发者控制台工具集,实时单步调试该工具的代码、查看变量值的变化、设置断点等等。这是一种非常棒的学习辅助工具。而且完全免费!所以请下载该 加密/解密工具,亲自体验,并推荐给您的朋友们。据我所知,这是 Web 上唯一一款纯 JavaScript 实现的 DUKPT 工具。
