2012-05-31 271 views
1

PHP函數:PHP的Java AES CBC加密不同的結果

$privateKey = "1234567812345678"; 
$iv = "1234567812345678"; 
$data = "Test string"; 

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv); 

echo(base64_encode($encrypted)); 

Result: iz1qFlQJfs6Ycp+gcc2z4w== 

JAVA功能

public static String encrypt() throws Exception{ 
try{ 
    String data = "Test string"; 
    String key = "1234567812345678"; 
    String iv = "1234567812345678"; 

    javax.crypto.spec.SecretKeySpec keyspec = new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES"); 
    javax.crypto.spec.IvParameterSpec ivspec = new javax.crypto.spec.IvParameterSpec(iv.getBytes()); 

    javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/NoPadding"); 
    cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keyspec, ivspec); 
    byte[] encrypted = cipher.doFinal(data.getBytes()); 

    return new sun.misc.BASE64Encoder().encode(encrypted); 

}catch(Exception e){ 
    return null; 
} 

}

返回null。

請注意,我們不允許更改PHP代碼。有人可以幫助我們在Java中獲得相同的結果嗎?非常感謝。

回答

10

如果您不是簡單地吞下encrypt()例程中可能的Exception s,那麼您會更好地瞭解發生了什麼。如果你的函數返回null那麼很明顯發生異常,你需要知道它是什麼。

事實上,例外的是:

javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes 
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:854) 
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:828) 
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676) 
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313) 
    at javax.crypto.Cipher.doFinal(Cipher.java:2087) 
    at Encryption.encrypt(Encryption.java:20) 
    at Encryption.main(Encryption.java:6) 

果然,你的明文只有11的Java字符長,這在你的默認編碼,將是11個字節。

您需要檢查PHP mcrypt_encrypt函數的實際功能。由於它的工作原理,它顯然使用了一些填充方案。你需要找出它是哪一個,並在你的Java代碼中使用它。

好的 - 我擡頭看mcrypt_encrypt的手冊頁。它說:

將用給定的密碼和模式加密的數據。如果數據的大小不是n * blocksize,則數據將填充\0

所以你需要在Java中進行復制。這裏有一種方法:

import javax.crypto.Cipher; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 

public class Encryption 
{ 
    public static void main(String args[]) throws Exception { 
     System.out.println(encrypt()); 
    } 

    public static String encrypt() throws Exception { 
     try { 
      String data = "Test string"; 
      String key = "1234567812345678"; 
      String iv = "1234567812345678"; 

      Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 
      int blockSize = cipher.getBlockSize(); 

      // We need to pad with zeros to a multiple of the cipher block size, 
      // so first figure out what the size of the plaintext needs to be. 
      byte[] dataBytes = data.getBytes(); 
      int plaintextLength = dataBytes.length; 
      int remainder = plaintextLength % blockSize; 
      if (remainder != 0) { 
       plaintextLength += (blockSize - remainder); 
      } 

      // In java, primitive arrays of integer types have all elements 
      // initialized to zero, so no need to explicitly zero any part of 
      // the array. 
      byte[] plaintext = new byte[plaintextLength]; 

      // Copy our actual data into the beginning of the array. The 
      // rest of the array is implicitly zero-filled, as desired. 
      System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length); 

      SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); 
      IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); 

      cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); 
      byte[] encrypted = cipher.doFinal(plaintext); 

      return new sun.misc.BASE64Encoder().encode(encrypted); 

     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
} 

當我跑,我得到:

iz1qFlQJfs6Ycp+gcc2z4w== 

這是你的PHP程序得到了什麼。


更新(2016 6月12日): 從Java 8中,最後的JavaSE附帶有記載的base64編碼解碼器。因此,而不是

return new sun.misc.BASE64Encoder().encode(encrypted); 

,你應該這樣做

return Base64.Encoder.encodeToString(encrypted); 

另外,使用base64編碼/解碼第三方庫(如commons-codec)而不是使用無證內部方法。

+0

非常感謝QuantumMechanic。很好解釋。 – user812120

+1

請注意,通常不使用基於零的填充,因爲您無法區分數據末尾的零值字節和填充。您應該使用PKCS#7填充(Java中的「PKCS5Padding」)。另請注意,使用sun。*功能不符合所有Java兼容性準則。 –

相關問題