JAVA语言生成的3DES字串在PHP中的解密

最近在和一些Java项目在做对接。好吧,是银联的某项目。他们在传递一些加密参数的时候使用了3DES算法。而我们用的是PHP。所以就需要在PHP中对他们的字串做解密。

他们文档中的示例代码是这样的:

import java.nio.ByteBuffer; import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec;
public class TestDecryptParams {
public static void main(String[] args) throws Exception {
String params = "349591A75387E430339C49E2E7FD83D955B54C817E9A43762CE33F6322DEDF79F398C910 6D350E6A3A128DC8A8F5A6704E48C73B23A91DD5DF75344D6EDFB1190881BC3CF42940C4 E8BA97999EF8CF0A12F1EC1491BCB9FBCEFB690D05B82C192B957B8911B03373AB50EB66C 8C10BE503896B6B1EFAB38E4E68BF1CFABFB5E4C7F043CEC1DEF4F70A99A650C9DC593A5C A05DA885AC4A05668CF4469D163044330056C41DF03286AEC3DE87F144DBC6F222B45AD4 5C9C3D4C0E6CCDC7BE86223D421110B024C5099A6438095DAE502CF55C9888AB99CD978D A52EA1BFF1BF3B0CA1280B0596535E6923261CC8BA3B4DE9EC9F9EC309E45895D7CDE362E F4EFA81703511372FB717A288B5ED0993A9A9509E6FAA3025AF2A5593EDACC1A83440F37E D67591F72FF5A774E18119F0CC005F2E6B635530582010CBD392DDB7EE111C7CADD04994 C23080DC312D9338E489106519F23AD69756C2EB9ACC60D5954A7ABA976778B2107B3579 1AF3D6C49A768671923672EE6C52B376D6005142F7CB157EE67DA85D399D25D937F6F5949 7EB62861776A653F139D3BDB4DF5601651E3F26CC99F5ECF0299532DA7AC6DDB00F";
String desKey = "cc3c40af69f269961a04163fd8251fd3";
byte[] desByte = desEdeEcbPkcs5Decrypt(hexString2ByteArr(params), desKey.getBytes());
System.out.println("收到的支付信息:" + new String( desByte ,"UTF-8") ); }
public static byte[] hexString2ByteArr(String hexStr) { if (hexStr == null)
return null;
if (hexStr.length() % 2 != 0) {
return null; }
byte[] data = new byte[hexStr.length() / 2]; for (int i = 0; i < hexStr.length() / 2; i++) {
char hc = hexStr.charAt(2 * i); char lc = hexStr.charAt(2 * i + 1); byte hb = hexChar2Byte(hc); byte lb = hexChar2Byte(lc);
if (hb < 0 || lb < 0) {
return null; }
int n = hb << 4;
data[i] = (byte) (n + lb); }
return data; }
public static byte hexChar2Byte(char c) {
if (c >= '0' && c <= '9') return (byte) (c - '0');
if (c >= 'a' && c <= 'f')
return (byte) (c - 'a' + 10);
if (c >= 'A' && c <= 'F')
return (byte) (c - 'A' + 10);
return -1; }
public static byte[] desEdeEcbPkcs5Decrypt(byte[] data, byte[] keyData) throws Exception {
if (keyData.length < 24)
keyData = make3DesKey(keyData);
Key localKey = makeDesKey(keyData);
Cipher localCipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); localCipher.init(Cipher.DECRYPT_MODE, localKey);
return localCipher.doFinal(data);
}
private static final Key makeDesKey(byte[] keyData) throws Exception { DESedeKeySpec keySpec = new DESedeKeySpec(keyData); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede"); return keyFactory.generateSecret(keySpec);
}
private static byte[] make3DesKey(byte[] keyData) { byte[] key3 = null;
byte[] key1 = new byte[8];
ByteBuffer buf = ByteBuffer.wrap(keyData); buf.get(key1);
buf.clear();
buf = ByteBuffer.allocate(24); buf.put(keyData);
buf.put(key1);
buf.flip();
key3 = buf.array();
buf.clear();
return key3;
} }

然后我们在做测试的时候,就要用上面的param和desKey两个参数,解出一个“支付信息”来。

做为一个聪明的码农,当然想到了搜一把。首先想到的是mcrypt系列方法。优先选取了php.net上的文档里的note。

然后使用了 http://php.net/manual/zh/function.mdecrypt-generic.php 的最下面的一段代码,这段代码大约是在11年前写的,咳咳。

原来的代码是这样的:

<?php
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$decrypted_data = mdecrypt_generic($td, $encrypted_data);
echo "Decrypt: ".$decrypted_data;
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
?>

可以看到里面有几个重要参数:$key,$encrypted_data

于是把$key设为上面Java代码中的desKey,而把$encrypted_data设为上面的param,得到一个乱七八糟的结果。不是我们要的。

查看了一下Java的逻辑,有一个把字符串转成byte[]的过程。于是先把$param做了一个hex2bin之后再传入解码,就解密成功了。

完整测试代码如下:

<?php
$params = "349591A75387E430339C49E2E7FD83D955B54C817E9A43762CE33F6322DEDF79F398C9106D350E6A3A128DC8A8F5A6704E48C73B23A91DD5DF75344D6EDFB1190881BC3CF42940C4E8BA97999EF8CF0A12F1EC1491BCB9FBCEFB690D05B82C192B957B8911B03373AB50EB66C8C10BE503896B6B1EFAB38E4E68BF1CFABFB5E4C7F043CEC1DEF4F70A99A650C9DC593A5CA05DA885AC4A05668CF4469D163044330056C41DF03286AEC3DE87F144DBC6F222B45AD45C9C3D4C0E6CCDC7BE86223D421110B024C5099A6438095DAE502CF55C9888AB99CD978DA52EA1BFF1BF3B0CA1280B0596535E6923261CC8BA3B4DE9EC9F9EC309E45895D7CDE362EF4EFA81703511372FB717A288B5ED0993A9A9509E6FAA3025AF2A5593EDACC1A83440F37ED67591F72FF5A774E18119F0CC005F2E6B635530582010CBD392DDB7EE111C7CADD04994C23080DC312D9338E489106519F23AD69756C2EB9ACC60D5954A7ABA976778B2107B35791AF3D6C49A768671923672EE6C52B376D6005142F7CB157EE67DA85D399D25D937F6F59497EB62861776A653F139D3BDB4DF5601651E3F26CC99F5ECF0299532DA7AC6DDB00F";
$desKey = "cc3c40af69f269961a04163fd8251fd3";

$key = $desKey;

function myhex2bin($str)
{
    return pack( "H*", $str );
}

$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$key = substr($key, 0, mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
//php5.4以上才有hex2bin,向下兼容用了pack方法,这个hex2bin在网上很容易搜到
$decrypted_data = mdecrypt_generic($td, myhex2bin($params));
echo "Decrypt: ".$decrypted_data;
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
?>

跑的结果是

Decrypt: {"accBankNo":"","account":"6214 44** **** 0103","amount":"1","authNo":"","batchNo":"000001","billNo":"000000000000001","currencyCode":"156","dealDate":null,"dealStatus":"","dealTime":"","expireDate":"1912","id":"40288b134af1ada1014af1ae55e90000","issNo":"90310000   ","merchantId":"898000156911002","merchantName":"绫致服装*****","operator":"000001","orderId":"","refId":"000105012799","termId":"00019130","voucherNo":"316044"}
Process finished with exit code 0

 

Copyright © 2015. All Rights Reserved.

发表评论

电子邮件地址不会被公开。 必填项已用*标注