2010-12-27 85 views
11

Vấn đề của tôi là: những gì tôi mã hóa trong Java tôi có thể giải mã hoàn hảo trong Java, nhưng PHP mcrypt không thể giải mã được. Những gì tôi mã hóa với mcrypt Tôi có thể giải mã với mcrypt, nhưng không thể bằng Java.Không thể trao đổi dữ liệu được mã hóa với AES-256 giữa Java và PHP

Tôi muốn gửi và nhận dữ liệu được mã hóa từ một ứng dụng Java đến một trang PHP, vì vậy tôi cần nó tương thích.

Dưới đây là những gì tôi có ...

JAVA ...

public static String crypt(String input, String key){ 
    byte[] crypted = null; 
    try{ 
     SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES"); 
     Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, skey); 
     crypted = cipher.doFinal(input.getBytes()); 
    }catch(Exception e){ 
    } 
    return Base64.encodeBase64String(crypted); 
} 

public static String decrypt(String input, String key){ 
    byte[] output = null; 
    try{ 
     SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES"); 
     Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, skey); 
     output = cipher.doFinal(Base64.decodeBase64(input)); 
    }catch(Exception e){ 
    } 
    return new String(output); 
} 

Chạy:

public static void main(String[] args) { 
    String key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8="; 
    String data = "example"; 
    System.out.println(Cpt.decrypt(Cpt.crypt(data, key), key)); 
} 

Output:

example 

PHP ...

function getEncrypt($sStr, $sKey) { 
    return base64_encode(
     mcrypt_encrypt(
      MCRYPT_RIJNDAEL_256, 
      $sKey, 
      $sStr, 
      MCRYPT_MODE_ECB 
     ) 
    ); 
} 

function getDecrypt($sStr, $sKey) { 
    return mcrypt_decrypt(
     MCRYPT_RIJNDAEL_256, 
     $sKey, 
     base64_decode($sStr), 
     MCRYPT_MODE_ECB 
    ); 
} 

Chạy:

$crypt = getDecrypt(getEncrypt($str, $key), $key); 
echo "<p>Crypt: $crypt</p>"; 

Output:

Crypt: example������������������������� 

Sử dụng PHP để crypt "dụ" với chìa khóa "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb + NONX/LIi8 =" Tôi nhận được "YTYhgp4zC + w5IsViTR5PUkHMX4i7JzvA6NJT1FqhoGY = ". Sử dụng Java để mã hóa cùng một điều với cùng một khóa tôi nhận được "+ tdAZqTE7WAVPXhB3Tp5 + g ==".

Tôi mã hóa và giải mã thành base64 theo thứ tự đúng và tôi đã thử nghiệm mã hóa base64 và khả năng tương thích giải mã giữa Java và PHP và nó hoạt động.

Trả lời

14

BUG # 1

MCRYPT_RIJNDAEL_256không phải là AES. Các 256 trong hằng số đó đề cập đến các khối, không phải là keyize. Sử dụng MCRYPT_RIJNDAEL_128 để có được cùng một thuật toán như AES. Các keyize được thiết lập chỉ bằng số byte trong đối số khóa mà bạn cung cấp. Vì vậy, cung cấp 32 byte và bạn nhận được AES với một khóa 256-bit.

BUG # 2

Hai dòng này không bao giờ đúng trong Java và chỉ ra một sự hiểu lầm cơ bản về bản chất của dữ liệu nhị phân tùy ý tạo ra bởi biến đổi mật mã:

output = cipher.doFinal(Base64.decodeBase64(input)); 
return new String(output); 

Không có gì sai là với việc truyền và lưu trữ trực tiếp byte[], nhưng nếu bạn chỉ sử dụng các chuỗi có thể in thì bạn nên mã hóa/giải mã base64 để làm như vậy. Như bạn đã sử dụng base64 rộng rãi mà có vẻ như con đường để đi. Tôi đoán rằng đúng hai dòng sẽ là:

output = cipher.doFinal(Base64.decodeBase64(input)); 
return new String(Base64.encodeBase64(output), "UTF-8"); 

EDIT:

Just kidding về lỗi # 2. Thực sự, tôi đã sai, tôi không nhận ra đó là hướng giải mã. Tất nhiên, nếu bạn biết giải mã byte[] là một chuỗi hợp lệ thì nó hoàn toàn chính xác để làm những gì mã của bạn làm.

+0

Tôi thấy, tôi đã phát hiện ra điều đó, nhưng tôi nghĩ tôi đã sử dụng AES-128. Vì vậy, nó thực sự là 256. Về những dòng này, tôi đã làm điều đó bởi vì tôi chắc chắn rằng dữ liệu được mã hóa sẽ luôn luôn chỉ chứa các chuỗi và không có dữ liệu nhị phân. – LZZ

1

Hãy nhớ có cùng mã hóa cho các chuỗi. Hãy thử để chuyển đổi chuỗi trong cả hai ngôn ngữ sang UTF-8, ví dụ, và vì chuyển sang dữ liệu nhị phân được mã hóa:

PHP (s utf8_encode() chức năng.):

$strAndBlob = utf8_encode("My string"); 

Java:

String str = "My string"; 
byte[] blob = str.getBytes("utf-8"); 

PHP, ví dụ: không được sử dụng UTF-8 theo mặc định.

+1

Vâng, điều đó quan trọng nhưng đó không phải là vấn đề. – LZZ

3

Xin vui lòng xem ở đây:

Vấn đề bạn đang gặp phải là một vấn đề padding- . Tôi không biết Java, nhưng AES/ECB/PKCS5Padding có vẻ như bạn đang sử dụng một PKCS # 5 (đó là cơ bản giống như PKCS # 7) đệm trong khi PHP nguyên bản chỉ hỗ trợ NULL -padding.Đó là những gì PKCS # 5/7 không:

Pad đầu vào với một chuỗi đệm của từ 1 đến 8 byte để làm cho tổng chiều dài bội chính xác của 8 byte. Giá trị của mỗi byte của chuỗi đệm được đặt thành số lượng byte được thêm - tức là 8 byte giá trị 0x08, 7 byte giá trị 0x07, ..., 2 byte 0x02 hoặc một byte giá trị 0x01.

Vì vậy, các mã PHP để làm đệm đúng là tầm thường:

$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB); 
$padding = $blockSize - (strlen($data) % $blockSize); 
$data  .= str_repeat(chr($padding), $padding); 
+0

Vâng, tôi đã làm chính xác những gì bạn nói, nhưng nó đã không cố định vấn đề, tuy nhiên, tôi nhận thấy rằng PHP có thể giải mã văn bản được mã hóa Java nếu tôi sử dụng 128bit. Tôi đoán ứng dụng Java của tôi đang mã hóa với AES-128, ngay cả với khóa 32byte. – LZZ

9

Tôi biết đây là chủ đề cũ nhưng tôi sẽ thêm giải pháp làm việc của mình.

Bạn cần phải viết lại bên PHP của kịch bản:

function getEncrypt($sStr, $sKey) { 
    return base64_encode(
    mcrypt_encrypt(
     MCRYPT_RIJNDAEL_128, 
     base64_decode($sKey), 
     $sStr, 
     MCRYPT_MODE_ECB 
    ) 
); 
} 

function getDecrypt($sStr, $sKey) { 
    return mcrypt_decrypt(
    MCRYPT_RIJNDAEL_128, 
    base64_decode($sKey), 
    base64_decode($sStr), 
    MCRYPT_MODE_ECB 
); 
} 

Bạn nên base64_decode ($ skey) vì chìa khóa của bạn là mã hóa base64.

$key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8="; 

Sau đó, bạn cần phải tạo ra chức năng này (tín dụng đi vào beltrachi từ http://www.php.net/manual/en/function.mcrypt-decrypt.php):

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

Sử dụng mã này làm mã hóa/giải mã:

$decrypt = getDecrypt("6XremNEs1jv/Nnf/fRlQob6oG1jkge+5Ut3PL489oIo=", $key); 
echo $decrypt; 
echo "\n\n"; 
echo getEncrypt(pkcs5_pad("My very secret text:)", 16), $key); 

Tôi hy vọng điều này sẽ có ích cho ai đó! :)

+0

pkcs5_pad đã cứu chúng tôi khỏi rất nhiều sự đau đớn! – kouton

Các vấn đề liên quan