ID TECH
全部技术文章

技术文章

如何解密信用卡数据(第一部分)

经常有人问:我从 ID TECH 读卡器获取到的磁道数据是加密的,要怎么解密?

答案是:你需要获取该笔交易对应的正确会话密钥,然后使用该密钥通过 Triple-DES (或 AES,视情况而定)对数据载荷进行解密。

解密过程本身其实平淡无奇,令人欣慰地简单。你大可使用现成的多种 TDES 或 AES 开源实现之一(无需自行实现核心加密算法),以 CBC(密码分组链接)模式运行,初始向量默认设为全零字节。只要你拥有正确的 16 字节解密密钥,解密过程就十分简单。

棘手的部分在于推导密钥。为此,你需要了解 ANSI X9.24-1,也就是俗称的 DUKPT.

走进 DUKPT 的世界

首先要明白:在读卡器中,每一笔交易产生的数据都会使用不同的密钥来加密。该密钥对每笔交易都是唯一的(这正是 DUKPT 缩写的含义:Derived Unique Key Per Transaction,"每笔交易派生唯一密钥")。任何密钥都不会被使用两次,因此重放攻击基本上是不可能的。

要理解 DUKPT 的工作原理,你需要先了解一下密钥序列号(Key Serial Number,简称 KSN)的概念。最关键的一点是:KSN 是一个 10 字节的数值,每笔交易都会变化,因为其最低 21 位构成了一个计数器。

密钥序列号(KSN)布局。

请记住:每一笔加密的卡交易都会附带一个 KSN。KSN 始终为 10 字节长,并且始终以明文形式传输,因为 KSN 本身并不泄露任何敏感信息(但它对于推导会话密钥至关重要)。

读卡器在工厂配置加密时,会被注入一个 16 字节的密钥和一个 10 字节的初始 KSN。所注入的密钥是从一个绝密密钥(永不被注入设备)派生而来的,这个绝密密钥称为 BDK,即基础派生密钥(Base Derivation Key)。(请注意,由于一个 BDK 可以派生出许多密钥,因此完全可以——而且实际上很常见——为成百上千台读卡器注入源自同一个 BDK 的唯一密钥。)派生过程本身需要使用 KSN。由于 KSN 中包含设备序列号信息(外加许多其他"命名空间"杂项信息),由特定 BDK+KSN 组合产生的散列(或密钥)实质上对每台设备都是唯一的。更妙的是,即便你知道 KSN,也永远无法从该散列反向计算出原始 BDK,因为这是一种加密学安全的单向散列。

每当发生一笔交易时,读卡器(如果它支持 DUKPT,而如今几乎所有读卡器都支持)会根据当前 KSN 值和被称为 IPEK(初始 PIN 加密密钥,Initial PIN Encryption Key)的值生成一个唯一密钥。然后使用这个一次性的会话密钥来加密交易数据中的敏感部分。

一旦加密,交易数据在到达授权目的地(可能是发卡机构)之前都不会再被解密。接收方(例如发卡机构)将使用其自己保存的 BDK 副本(再加上该笔交易的 KSN)重新推导出该交易的会话密钥,从而恢复原始(已解密的)交易数据。这是所谓的对称过程,因为加密方和解密方必须事先知晓相同的秘密(即 BDK)。也就是说,你已经事先向接收方提供了必要的"秘密",从而双方都能解密消息。

IPEK

获取 DUKPT 会话密钥的起点,永远是先推导出 IPEK(初始密钥),而这只有在你已知原始 BDK 和 KSN 的情况下才能完成。(在这一步,使用该设备的任意一个 KSN 即可,因为在此步骤中你会将计数器清零。)

要推导初始 PIN 加密密钥(IPEK),你需要执行以下步骤:

1. 如果你的 BDK 长度为 16 字节,请使用所谓的 EDE3 方法将其扩展为 24 字节。具体操作很简单:将密钥的前 8 个字节复制到密钥末尾,从而生成一个首尾 8 个字节相同的 24 字节密钥。

如果你的原始密钥(十六进制)如下所示:

那么你需要让它最终看起来像这样:

2. 将你的 10 字节初始 KSN 与十六进制值 0xFFFFFFFFFFFFFFE00000 进行按位与(AND)运算,从而对其进行掩码处理。我们将处理结果称为"掩码后的 KSN"。

3. 仅保留 10 字节掩码后 KSN 的前 8 个字节(即最左侧的 8 个字节),从而构造一个 8 字节值。换句话说,把最右侧的两个字节截掉。

4. 使用扩展后的 24 字节 BDK 作为密钥,对你在步骤 3 中获得的 8 字节掩码后 KSN 进行 TDES 加密。这里使用全零初始向量。(注意,在此处密码分组链接没有意义,因为本例中的数据只有一个分组,即 8 个字节。)保留你在此步骤中得到的 8 字节密文,它将作为 16 字节 IPEK 的左半部分。

5. 要得到 IPEK 的右半部分,首先将你原始的 16 字节 BDK 与十六进制值 0xC0C0C0C000000000C0C0C0C000000000 进行 XOR 运算。(如果你使用的编程语言支持大整数运算,这一步可以用一行代码完成;如果不支持,则需要分段、逐步对两个值进行 XOR 运算。)

6. 对步骤 5 中获得的 16 字节值进行 EDE3 扩展,得到 24 字节的密钥值。

7. 使用步骤 6 得到的 24 字节密钥,对你在步骤 3 中获得的 8 字节掩码后 KSN 进行 TDES 加密。这便是 IPEK 的右半部分。

8. 将 IPEK 的左右两半拼接起来,即可得到最终的 16 字节 IPEK。

如果你打算自己编写代码实现,可以尝试使用测试密钥值 0123456789ABCDEFFEDCBA9876543210 和 KSN 62994900000000000001 来生成 IPEK。所得到的 IPEK 应为 B5610650EBC24CA3CACDD08DDAFE8CE3。

密钥管理与加密算法的区别

顺便指出,DUKPT 中大量使用了 Triple-DES(TDES)。任何时候都不会用到 AES(即使你的读卡器配置为使用 AES 进行加密)。X9.24 标准要求使用 TDES,有时使用普通 DES。请明确一点:DUKPT 的密钥派生过程与交易数据的加密/解密过程是完全独立的。前者用于派生密钥,后者使用该密钥来执行 TDES 或 AES 编码。任何加密例程都不会关心你的密钥来自何处,也不会关心构造它使用了什么算法;唯一重要的是密钥本身能正常工作。所以,虽然你需要解锁的数据可能是用 AES 加密的,但你用于解锁该数据的密钥则是通过 DUKPT 派生的,而 DUKPT 内部使用的是 TDES。

代码在哪里?

在本文第二部分中,我们将详细介绍如何使用 IPEK 加 KSN 推导出真正的 DUKPT 会话密钥。我们将展示实际的源代码,让你可以独立完成整个过程。如果你等不及下一篇就想看源代码,可以先去看看我们颇受欢迎的 加密/解密工具,其中包含一个功能完备的 JavaScript 版 DUKPT 算法实现(我会在第二部分中讲到),并附带开源的 TDES 和 AES 实现。你可以使用该加密/解密工具来派生 DUKPT 密钥(共有 PIN、Data 和 MAC 三种变体)、加密或解密数据(使用 TDES 或 AES)、生成各种散列等等。最棒的是,由于该工具就是一个网页,因此可以在任何支持 JavaScript 的浏览器(任何平台)上运行。

想要根据 KSN 和 IPEK 派生数据变体 DUKPT 会话密钥吗? 继续阅读第二部分 本文的后续内容。