2012-05-31 2 views
1

PHP 기능 :PHP 자바 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== 

자바 기능

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

}.

우리는 PHP 코드를 변경할 수 없습니다. 자바에서 같은 결과를 얻을 수 있도록 도와 주시겠습니까? 많은 감사합니다.

답변

10

encrypt() 루틴 내에서 가능한 한 Exception을 삼가 지 않으면 어떤 일이 벌어 졌는지 더 잘 알았을 것입니다. 함수가 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 바이트 것이다, 단지 11 자바 자입니다.

PHP mcrypt_encrypt 기능이 실제로 무엇인지 확인해야합니다. 작동하기 때문에, 그것은 약간의 패딩 방식을 사용하고 있습니다. Java 코드에서 어떤 코드인지 찾아야합니다.

Ok - 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일) : 문서화 된 base64로 코덱 자바 (8)으로, JavaSE 마지막으로 제공됩니다. 그래서 그 대신

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

의 당신은 오히려 문서화되지 않은 내부 방법을 사용하는 것보다 Base64 인코딩/디코딩을위한 제 3 자 라이브러리 (예 : commons-codec)를 사용, 또는

return Base64.Encoder.encodeToString(encrypted); 

같은 것을해야한다.

+0

고맙습니다. QuantumMechanic. 아주 잘 설명했다. – user812120

+1

데이터의 끝에있는 0 바이트와 패딩을 구별 할 수 없으므로 일반적으로 0 기반 패딩이 사용되지 않습니다.대신 PKCS # 7 패딩을 사용해야합니다 (Java에서 "PKCS5Padding"). 또한 sun. * 기능을 사용하면 모든 Java 호환성 지침에 실패합니다. –