4

我的公司正在开展一个将读卡器投入现场的项目。读卡器使用 DUKPT TripleDES 加密,因此我们需要开发软件来解密我们服务器上的卡数据。

我刚刚开始触及这个问题的表面,但我发现自己陷入了一个看似简单的问题……在尝试生成 IPEK(重新创建对称密钥的第一步)时。

IPEK 是通过连接两个三重 DES 加密的 8 字节十六进制字符串创建的 16 字节十六进制值。

我已经尝试过带填充和不带填充的 ECB 和 CBC(IV 为零)模式,但是当我需要与输入大小相同的结果时,每个单独编码的结果总是 16 字节或更多(2 个或更多块)。事实上,在整个过程中,密文应该与被编码的明文大小相同。

<cfset x = encrypt("FFFF9876543210E0",binaryEncode(binaryDecode("0123456789ABCDEFFEDCBA98765432100123456789ABCDEF", "hex"), "base64") ,"DESEDE/CBC/PKCS5Padding","hex",BinaryDecode("0000000000000000","hex"))>

结果:3C65DEC44CC216A686B2481BECE788D197F730A72D4A8CDD

如果您使用 NoPadding 标志,则结果是:

3C65DEC44CC216A686B2481BECE788D1

我还尝试将纯文本十六进制消息编码为 base64(因为密钥是)。在上面的示例中,返回结果为:

DE5BCC68EB1B2E14CEC35EB22AF04EFC。

如果您这样做,除了使用 NoPadding 标志外,它会出现“输入长度不是 8 字节的倍数”的错误。

我是密码学的新手,所以希望我在这里犯了一些非常基本的错误。为什么这些分组密码算法生成的密文与明文消息的长度不同?

对于更多背景知识,作为“通过它工作”练习,我一直在尝试复制此处列出的工作:

https://www.parthenonsoftware.com/blog/how-to-decrypt-magnetic-stripe-scanner-data-with-dukpt/

4

2 回答 2

2

我不确定它是否相关,它可能不是您正在寻找的答案,但我花了一些时间测试错误 ID 3842326。当使用不同的属性时,CF 在引擎盖下以不同的方式处理种子和盐。例如,如果您将变量作为要加密的字符串而不是常量(函数调用中的硬编码字符串)传入,则结果字符串每次都会更改。这可能表明不同的方法签名 - 在您的示例中,使用一个标志与另一个标志,您会看到类似的东西。

Adobe 的回应是,鉴于在任何一种情况下都可以不加密生成的字符串,这并不是真正的错误 - 更多的是需要注意的行为。你得到的字符串可以不加密吗?

于 2014-12-09T19:07:51.400 回答
1

问题是encrypt()期望输入是 UTF-8 字符串。因此,您实际上是在加密文字字符 FFFF-9.... 而不是解码为十六进制时该字符串的

相反,您需要将十六进制字符串解码为二进制,然后使用该encryptBinary()函数。(注意,我没有看到iv链接中提到的,所以我猜他们使用的是 ECB 模式,而不是 CBC。)由于该函数也返回二进制,binaryEncode因此用于将结果转换为更友好的十六进制字符串。

编辑: 切换到 ECB +“NoPadding”会产生所需的结果:

ksnInHex = "FFFF9876543210E0";
bdkInHex = "0123456789ABCDEFFEDCBA98765432100123456789ABCDEF";
ksnBytes = binaryDecode(ksnInHex, "hex");
bdkBase64 = binaryEncode(binaryDecode(bdkInHex, "hex"), "base64");
bytes = encryptBinary(ksnBytes, bdkBase64, "DESEDE/ECB/NoPadding");
leftRegister = binaryEncode(bytes, "hex");

...产生:

6AC292FAA1315B4D 

为了做到这一点,我们想从我们原来的 16 字节 BDK 开始......并用下面的掩码 XOR 它......

不幸的是,大多数 CF 数学函数仅限于 32 位整数。因此,您可能无法单独使用本机 CF 函数来完成下一步。一种选择是使用 java 的BigInteger类。从十六进制字符串创建一个大整数并使用该xor()方法应用掩码。最后,使用该toString(radix)方法将结果作为十六进制字符串返回:

bdkText ="0123456789ABCDEFFEDCBA9876543210";
maskText = "C0C0C0C000000000C0C0C0C000000000";

// use radix=16 to create integers from the hex strings
bdk = createObject("java", "java.math.BigInteger").init(bdkText, 16);
mask = createObject("java", "java.math.BigInteger").init(maskText, 16);
// apply the mask and convert the result to hex (upper case)
newKeyHex = ucase( bdk.xor(mask).toString(16) );
WriteOutput("<br>newKey="& newKeyHex);
writeOutput("<br>expected=C1E385A789ABCDEF3E1C7A5876543210");

这应该足以让你回到正轨。鉴于这里 CF 的一些限制,java 将更适合 IMO。如果您对它感到满意,您可以编写一个小型 java 类并从 CF 调用它。

于 2014-12-09T20:08:01.433 回答