使用Visa Checkout解密付款数据

时间:2017-02-02 19:30:47

标签: java encryption coldfusion coldfusion-11

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

  

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

     

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

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

    按照以下四个步骤使用。解密encPaymentData   解密的encKey:

         
        
    1. Base64-解码encPaymentData。
    2.   
    3. 删除解码值的前32个字节。这是HMAC。使用计算其余解码数据的SHA-256 HMAC   该       解密的encKey并将其与前32位的HMAC进行比较       字节。
    4.   
    5. 应删除接下来的16个字节,并将其用作解密算法的IV。
    6.   
    7. 使用AES-256-CBC(步骤3中的IV)和SHA256-hash解密其余的encPaymentData有效负载   解密的encKey。
    8.   

我尝试过使用ColdFusion但是我因为加密问题而丢失了一些,而且无法修复代码。下面我有所需要的。我被困在第3步& 4他们说比较它然后解密它。有人可以指导可以做些什么来修复它吗?

加密:

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();
}

1 个答案:

答案 0 :(得分:1)

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

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

第一部分 - 解密encKey

  
      
  1. Base64-decode encKey。
  2.   
  3. 删除解码值的前32个字节。这是HMAC(哈希消息认证码)。计算一个SHA-256 HMAC   其余的解码数据使用您的API共享密钥并进行比较   从前32个字节到HMAC。
  4.   

首先使用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(初始化向量)。
  2.   

其余字节包含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哈希解密剩余数据。
  2.   

解密之前的最后一步是通过散列共享密钥来生成解密密钥。不幸的是,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");

NB:您发布的示例值似乎已损坏,因为它们甚至不能与java示例一起使用。当使用有效值(密钥,数据等)时,上述步骤会产生正确的结果。