【问题标题】:Decrypting Payment Data with Visa Checkout使用 Visa Checkout 解密支付数据
【发布时间】:2017-06-20 00:42:35
【问题描述】:

我正在从Visa Checkout 获取加密格式的信息。他们网站上的指南提供了以下说明:

首先,您必须解密动态密钥(encKey),然后使用解密后的动态密钥值解密支付数据负载(encPaymentData)。

按照以下四个步骤解密encKey:

  1. Base64 解码 encKey。
  2. 删除解码值的前 32 个字节。这是 HMAC(哈希消息验证码)。计算
    的 SHA-256 HMAC 使用您的 API 共享密钥解码的其余数据并进行比较
    从前 32 个字节到 HMAC。
  3. 接下来的 16 个字节应被删除并用作解密算法的 IV(初始化向量)。
  4. 使用 AES-256-CBC、第 3 步中的 IV 和 API 共享密钥的 SHA-256 哈希对剩余数据进行解密。

按照这四个步骤使用 解密后的encKey:

  1. Base64 解码 encPaymentData。
  2. 删除解码值的前 32 个字节。这是 HMAC。使用以下方法计算其余解码数据的 SHA-256 HMAC 这 解密 encKey 并将其与前 32 个中的 HMAC 进行比较 字节。
  3. 接下来的 16 个字节应被删除并用作解密算法的 IV。
  4. 使用 AES-256-CBC、步骤 3 中的 IV 和 解密后的 encKey。

我尝试使用 ColdFusion,但我对加密问题有些迷茫,无法修复代码。下面我有什么需要的。我被困在第 3 步和第 4 步,他们说比较然后解密。有人可以指导如何解决它吗?

enckey:

2M2WWOD4wANsGwWTmPqQIQYdz9WPwgiR0ntJHGaxm23b5a1sWUtCBcUQUMMtc9hvcYavJ6yqPgETOGZgDOdd9qjDwIb2aV9DLZT1iIcB3zNN5Ddhxd9iiui6TAlJxU/O

encPaymentData:

X2TXp0ZmwHrtfzSP5TPjUOjdZb0rjsHeDSqr8TwIF/VR8sMQhWN5hP4IRhQxWT CZcQhxZoUHP 0g/E/ot sjREAJ8YQf7c7jSzKsXRH/wrew5rQit2wJBlVSSZ YoLeIHbLrTz CfIoFv09hixl7ff27u0YCyV0zjP5vNfCBEIwfqyriqwXK2J QEOxQiKzDUW4br3o1t31aymCQC9eBBpoVKjFfSKlNXM9QEdNZBcLMZ8Wlv8lF/ua bnwshbM9u7Uhudqvut94RZEW NzkRD8MfBo12e/XhnL35qxGpHeQNPClC4EQDK6U/HmegeOj BZLbIIYBs6t9E8Q3AKBwfiPOFgB gSVnhXKnd3nKvllaG BaGrQJtk 7QAtnHMHxQAO5rdiS9465HCdiHa8zlv7SkvWh8EwcKCiT4qiZSM6QuYAeRSzDpPS1gsZ54Q9LizUnueH7yyzSd47cLPd0VlOQxobKtNN2LrsRb3IwOfzuwGnSRf2cNp49hBmmGP1b0BC hhB6UpCqP2ixTPvui NwMYzqZUe336bF1mfnKzEbEZIvIrPyx3uMiLDAns2g7S80gMNnHb/09i49xbfY3V7oudeiHV99FCh67DuG3uHE3/HzIZbcnxJwVJoJj6/3DuzK/Kw1JqSorE0M1qxUqoNkJC4aNCBrqfTlR7/eErrvB554TUZwcyQXqKCwrKv4NJEw6S0n3W1VASkfA0atbJQX2aLgx9kqnhYdDbaU8UcFIoeA45 yEuQ9vXzo2ILQhvamsAAFQd3i4mEOZ KNtMu25dDFlORn5C/oTZ1t1dzJoYMvq44gejp6L3IK e7JCugGchr963a2kd8NFa3wctRDHF8ChHxawVlU0aY7nasrVireMFLiM 9XIb4abfDtct/j1Q8IGN0hRkgCHO6dlnOrAaaQDYYH3axaMDp5Onb04whULYqGbn/XSR8Sn8gnoFbYqVJbR5aCp5Pe9TpfwxlEvV3z8ZfsASqW2y So9gpwg2y16K/FX3Io6kqeqUlAxbTRDfN/ofDIJaO H PUu2teqjvwvCkjPciGOQdXT5JxqoCiHqRwD0zeZPcG3b9Nfrq3Caf6zjwhf /CMeGc3dNHhSkXox R50MP8BlLWk/bXZRScTE/HSrVxE n073utHAnbVOM3gVha0Hr8TmoV8z1vBg5fY253so6kQX61ZIfHneCAZo0qeKRgDgLUryVPmUNc5 yKP8DxtmHl/0YUztpgyEx5njsrn1L 3EHMMUhui8d LQdNZoEpZ9U1Xb7XVsV5gnwR/mOITNOKJQsine4zMMHBcomHclrM0CuI58YrKPqworCmK6CYfzSc8UmXxXUe5dzND/DS9XgqDttQic2/OqTSAK63ynnrNqzr3D56VpDBeDeQjk3mc/0zmuFAPEXoAQoQKfD6HEuajvWJebQ6QIPgA TshqsnPlktbpftr4lsuB1tHS/W8D7SYVFMC/Kxy9QuYWs0cmRTtzfWEKIRHeDElOTQCX5JB5PgzVhhi5kYTi488Ba8j4zvNUw55hEoMxONYO7eMjJosmNjULsT492LGw3EfAgmgx9h3yFLQRZgfylg0h4PfLlcPOAdsnVX9/yLElD xu7Atwc4S7pBWTHvwue7PpRvWpTeqkU5sqiX4KcV5x8rk mBtxm48a8fsmp GNf 4IjwXu9cQaU9WLipiEnkqFsYo7/aAsmmKWBETyQg9BFXYK 165vrzSX8WTsv6ZZDnVjcE1n4Ov8Jl2cnAigoQbB0ROPpIRzZ3zH2diUv1vzlSuh9gbEJf3uQRKlYRVUbpboC0RbQ/7jgznfJAWyLykyDQ0EB8fVEOtbP1l4JEz39QwAU18ph3btnWWuKEV4 ghYvNG4m1DYntSF57s2ajRS6rPtR oYvGjrJL9zbHBhKHlfkIPC0TKotOCi96mqpikbBEfIZSomHxYgDwYCSvt60zaDIjlBxZ1UBdK JL0554Wia9W3Wg91bmYS9Q4SXMT8r4xGYB7OutEV24n7p088rVm/w2SZSiqlLqai539k6WGkzEQf19ytPtIE81a N z7aijTjy 7FCuVPF90svI5/NoGpSINqv84HUcMU71BvXUIT53Ea6CCpiWvvOPpo/XZar44emlIG0UgeB kfP6C6sis=

密码:

zRf7WZ3nM7ON{U0E6J5S}KpVm@k2ReDyq#1lG9go

CF 代码:

<cfset str = "2M2WWOD4wANsGwWTmPqQIQYdz9WPwgiR0ntJHGaxm23b5a1sWUtCBcUQUMMtc9hvcYavJ6yqPgETOGZgDOdd9qjDwIb2aV9DLZT1iIcB3zNN5Ddhxd9iiui6TAlJxU/O">
<cfset tobas = tobase64(str)>
<cfset getFirst32bytes = Left(tobas,32)>
<cfset tobas2 = RemoveChars(tobas,1,32)>
<cfdump var="#tobas2#">
<cfset key = "zRf7WZ3nM7ON{U0E6J5S}KpVm@k2ReDyq##1lG9go">
<cfset x = hmac("#tobas2#","#key#","HMACSHA256")>
<cfset y = hmac("#getFirst32bytes#","#key#","HMACSHA256")>
<cfset decalgo = Left(x,16)>
<cfset decremainingData = RemoveChars(x,1,16)>
<cfset getDec = Decrypt(decalgo,"#key#","AES")>
<cfdump var="#x#"><br>
<cfdump var="#y#"><br>
<cfdump var="#decalgo#">
<cfdump var="#decremainingData#">
<cfdump var="#getDec#">

这是他们网站上的 java 示例:

private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String HASH_ALGORITHM = "SHA-256";
private static final String HMAC_ALGORITHM = "HmacSHA256";
private static final int IV_LENGTH = 16, HMAC_LENGTH = 32;
private static final Charset utf8 = Charset.forName("UTF-8");
private static final Provider bcProvider;
static {
   bcProvider = new BouncyCastleProvider();
   if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
    Security.addProvider(bcProvider);
   }
}

private static byte[] decrypt(byte[] key, byte[] data) throws GeneralSecurityException {
   byte[] decodedData = Base64.decode(data);
   if (decodedData == null || decodedData.length <= IV_LENGTH) {
    throw new RuntimeException("Bad input data.");
   }
   byte[] hmac = new byte[HMAC_LENGTH];
   System.arraycopy(decodedData, 0, hmac, 0, HMAC_LENGTH);
   if (!Arrays.equals(hmac,
     hmac(key, decodedData, HMAC_LENGTH, decodedData.length– HMAC_LENGTH))) {
    throw new RuntimeException("HMAC validation failed.");
   }
   byte[] iv = new byte[IV_LENGTH];
   System.arraycopy(decodedData, HMAC_LENGTH, iv, 0, IV_LENGTH);
   Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, bcProvider);
   cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hash(key), "AES"),
    new IvParameterSpec(iv));
   return cipher.doFinal(decodedData, HMAC_LENGTH + IV_LENGTH,
    decodedData.length– HMAC_LENGTH– IV_LENGTH);
}

private static byte[] hash(byte[] key) throws NoSuchAlgorithmException {
   MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
   md.update(key);
   return md.digest();
}

private static byte[] hmac(byte[] key, byte[] data, int offset, int length)
throws GeneralSecurityException {
   Mac mac = Mac.getInstance(HMAC_ALGORITHM, bcProvider);
   mac.init(new SecretKeySpec(key, HMAC_ALGORITHM));
   mac.update(data, offset, length);
   return mac.doFinal();
}

【问题讨论】:

  • 在 Visa 的示例中,他们使用的是 Bouncy Castle。您可能需要考虑该路线。这就是我最终必须做的才能完成所有加密和解析。 (经过多次敲打我的头。)但那是 3 年前......也许今天在冷聚变领域的情况会更好......但我怀疑不会。祝你好运。 :)
  • 最好不要公开加密密钥和密码。至少你没有暴露你的名字。但雇主总有一天会找到你。

标签: java encryption coldfusion coldfusion-11


【解决方案1】:

了解示例代码的重要一点是它引用字节。您的 CF 代码正在使用字符。这似乎是一个微不足道的区别,但它们是完全不同的东西,会产生非常非常不同的结果。为了成功解密,您需要使用给定字符串的字节(或二进制)而不是字符。

虽然可以使用核心 CF 函数(如 arraySlice())来操作 二进制 数组,但语法有时会变得有点笨重/笨拙。原因是二进制数组是不同于标准 CF 数组的对象类型,即 byte[] 与 java.util.List。因此,根据使用的函数,您可能需要javacast 将变量强制转换为预期类型。考虑到这一点..

第一部分 - 解密 encKey

​​>
  1. Base64 解码 encKey。
  2. 删除解码值的前 32 个字节。这是 HMAC(哈希消息验证码)。计算 SHA-256 HMAC 使用您的 API Shared Secret 解码的其余数据并进行比较 从前 32 个字节到 HMAC。

首先使用binaryDecode将base64字符串转换成二进制。然后从返回的数组中提取适当数量的字节。这是预期的 HMAC 值:

hmacSize = 32;
binaryToDecrypt  = binaryDecode(encryptedKey, "base64");
expectedHMAC = binaryEncode( javacast("byte[]", arraySlice(binaryToDecrypt, 1, hmacSize))
                          , "hex" );

接下来,提取所有剩余的字节,并使用它们来计算实际的 HMAC。根据预期值验证它。如果两者不匹配,则出现问题。

remainData = arraySlice(binaryToDecrypt, hmacSize + 1);
actualHMAC = hmac( javacast("byte[]", remainData ), sharedSecret, "HMACSHA256");

if (compare(actualHMAC, expectedHMAC) != 0) {
    throw("ERROR: Invalid HMAC ["& actualHMAC &"]. Expected ["& expectedHMAC &"]");
}
  1. 接下来的 16 个字节应被删除并用作解密算法的 IV(初始化向量)。

其余字节包含一个 IV,后跟加密值。在解密后者之前,您需要将两者提取并分离:

ivSize = 16;
ivValue = javacast("byte[]", arraySlice(remainData, 1, ivSize));
encryptedValue = javacast("byte[]", arraySlice(remainData, ivSize + 1));
  1. 使用 AES-256-CBC、第 3 步中的 IV 和 API 共享密钥的 SHA-256 哈希对剩余数据进行解密。

解密前的最后一步是通过对共享密钥进行散列处理来生成解密密钥。不幸的是,CF 的hash() 函数总是返回一个十六进制字符串。所以必须转换成base64格式才能兼容解密功能。​​

keyHex = hash(sharedSecret, "SHA-256", "utf-8");
keyBase64 = binaryEncode(binaryDecode(keyHex, "hex"), "base64");

最后,使用所有三个值来解密。返回的二进制文件将包含第二部分中使用的加密密钥。

decryptedKeyBinary = decryptBinary( encryptedValue
                           , keyBase64
                           , "AES/CBC/PKCS5Padding"
                           , ivValue);

第二部分 - 解密 encPaymentData

使用与第一部分完全相同的过程,只需交换变量:

  • 使用encPaymentData 而不是encryptedKey
  • 使用decryptedKeyBinary 而不是sharedSecret

最终的解密结果将是二进制的。使用charsetEncode 将其转换回人类可读的字符串:

result = charsetEncode(decryptedResult, "utf-8");

注意:您发布的示例值似乎已损坏,因为它们甚至不适用于 java 示例。当使用 valid 值(键、数据等)时,上述步骤确实会产生正确的结果。

【讨论】:

    猜你喜欢
    • 2017-03-21
    • 2023-04-11
    • 2016-09-01
    • 1970-01-01
    • 2018-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多