2016-06-01 129 views
0

我需要加密一个unix时间戳以调用第三方API。在他们的文件,他们规定我必须使用:加密PHP中的时间戳并使用Java解密

  • 算法:128位AES,模式:CBC
  • 填充:PKCS5Padding
  • 初始化向量: “0000000000000000”

然后他们给一个例子:

客户端必须使用时间戳1464284796测试它们的实现,初始化向量:'0000000000000000 '并导致AUTH_TOKEN 6BH3hg1cqQJOK6sG8gw7Xw ==以base64密钥b35901b480ca658c8be4341eefe21a80

他们甚至给示例代码来生成加密的时间戳,问题是,他们使用的是Java,我们正在使用PHP。我在PHP中尝试的所有内容都与期望的输出不匹配,即6BH3hg1cqQJOK6sG8gw7Xw ==。

这里是他们的Java示例:

import java.io.UnsupportedEncodingException; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import javax.crypto.Cipher; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 
import org.apache.commons.codec.DecoderException; 
import org.apache.commons.codec.binary.Base64; 
import org.apache.commons.codec.binary.Hex; 
import com.google.common.primitives.Longs; 
class Encryptor { 
    private String initialVector; 
    private static final String TRANFORMATION = "AES/CBC/PKCS5Padding"; 
    private static final String ALGORITHM = "AES"; 
    String encrypt(SecretKeySpec key, long timestamp) throws Exception { 
     byte[] encryptedBytes = 
      getEncryptingCipher(key).doFinal(Longs.toByteArray(timestamp)); 
     return Base64.encodeBase64String(encryptedBytes); 
    } 
    private Cipher getEncryptingCipher(SecretKeySpec key) throws 
    NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, 
    InvalidKeyException, 
    InvalidAlgorithmParameterException { 
     Cipher encryptingCipher = Cipher.getInstance(TRANFORMATION); 
     encryptingCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(initialVector.getBytes())); 
     return encryptingCipher; 
    } 
    private SecretKeySpec getSecretKeySpec(String key) throws DecoderException { 
     byte[] keyBytes = Hex.decodeHex(key.toCharArray()); 
     return new SecretKeySpec(keyBytes, ALGORITHM); 
    } 
    void setInitialVector(String initialVector) { 
     this.initialVector = initialVector; 
    } 
} 

用法:

Encryptor encryptor = new Encryptor(); 
encryptor.setInitialVector("0000000000000000"); 
//Expensive operation so only performed once, re-use the key spec instance 
SecretKeySpec keySpec = 
encryptor.getSecretKeySpec("b35901b480ca658c8be4341eefe21a80"); 
long timestamp = System.currentTimeMillis()/1000; 
String authToken = encryptor.encrypt(keySpec, timestamp); 

我试图在PHP中:

[[email protected] ~]$ php -a 
Interactive shell 

php > echo openssl_encrypt('1464284796','AES-128-CBC','b35901b480ca658c8be4341eefe21a80',null,'0000000000000000'); 
8PM7LQM7Xmb2NCBE3Hp00g== 
php > 

然后:

<?php 

function encrypt($message, $initialVector, $secretKey) { 
    return base64_encode(
     mcrypt_encrypt(
      MCRYPT_RIJNDAEL_128, 
      $secretKey, 
      $message, 
      MCRYPT_MODE_CBC, 
      $initialVector 
     ) 
    ); 
} 

function encrypt_something($input) 
{ 
    $size = mcrypt_get_block_size('rijndael-128', 'cbc'); 
    $input = pkcs5_pad($input, $size); 

    $key = 'b35901b480ca658c8be4341eefe21a80'; 
    $td = mcrypt_module_open('rijndael-128', '', 'cbc', ''); 
    $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); 
    mcrypt_generic_init($td, $key, '0000000000000000'); 
    $data = mcrypt_generic($td, $input); 
    mcrypt_generic_deinit($td); 
    mcrypt_module_close($td); 
    $data = base64_encode($data); 
    return $data; 
} 

function pkcs5_pad ($text, $blocksize) 
{ 
    $pad = $blocksize - (strlen($text) % $blocksize); 
    return $text . str_repeat(chr($pad), $pad); 
} 

function pkcs5_unpad($text) 
{ 
    $pad = ord($text{strlen($text)-1}); 
    if ($pad > strlen($text)) return false; 
    if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; 
    return substr($text, 0, -1 * $pad); 
} 

echo encrypt_something('1464284796'); 

//echo encrypt('1464284796','0000000000000000','b35901b480ca658c8be4341eefe21a80'); 



[[email protected] ~]$ php -f api.php 
UXRvTOIPiiYfBUoDFRaC5w== 

老实说,我不知道我在做什么,特别是在JAVA中。我甚至不知道如何运行示例代码。

UPDATE

第三方伸出我们自己的样品进行修订:

注:客户必须使用时间戳 1464284796,初始化向量测试及其履行情况:“0000000000000000”并导致 的base64 AUTH_TOKEN ZnNmKbcdxRrYTDBgQKI9aQ ==的 密钥b35901b480ca658c8be4341eefe21a80

的解决方案是revome pack功能:

$ts = '1464284796'; 
$key = "b35901b480ca658c8be4341eefe21a80"; 
$authToken = openssl_encrypt($ts, 'AES-128-CBC', hextobin($key), null, '0000000000000000'); 

function hextobin($hexstr) 
{ 
    $n = strlen($hexstr); 
    $sbin =""; 
    $i =0; 
    while($i<$n) 
    { 
     $a =substr($hexstr,$i,2); 
     $c = pack("H*",$a); 
     if ($i==0){$sbin=$c;} 
     else {$sbin.=$c;} 
     $i+=2; 
    } 
    return $sbin; 
} 
+0

能系统调用运行准备Java代码的方法吗?这可以得到答案,并且可能比在PHP中实现所有这些东西更容易。 –

回答

1

数字不是简单的十进制字符串。您必须将它们编码为二进制表示。这可以通过pack完成。由于Longs.toByteArray(timestamp)编码时间戳64位大端表示法,你一定要配合这个编码:

$ts = "\0\0\0\0" . pack('N', '1464284796'); 
echo openssl_encrypt($ts, 'AES-128-CBC', hex2bin('b35901b480ca658c8be4341eefe21a80'), 
     null, '0000000000000000')); 
+0

它输出'6BH3hg1cqQJOK6sG8gw7Xw =='!非常感谢!!! –

0

看一看你使用的Java API。他们真的期望十六弦?这在Java中非常罕见。这里

你的代码说“initialVector.getBytes()”,这表明API想要一个字节数组,而不是一个十六进制字符串:)

原因的破字节数组表示的是PHP或JS代码通常使用十六进制字符串是不支持这些语言中的字节数组(至少传统上,现代JS中实际上有字节数组)。