2012-08-24 3 views
0

Mcrypt 라이브러리 :암호로 암호화. 암호화가 작품을 해독 실패 완벽하게

import java.security.NoSuchAlgorithmException; 

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

    public class MCrypt { 

      private String iv = "fedcba"; 
      private IvParameterSpec ivspec; 
      private SecretKeySpec keyspec; 
      private Cipher cipher; 

      private String SecretKey = "abcdef"; 

      public MCrypt() 
      { 
        ivspec = new IvParameterSpec(iv.getBytes()); 

        keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES"); 

        try { 
          cipher = Cipher.getInstance("AES/CBC/NoPadding"); 
        } catch (NoSuchAlgorithmException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
        } catch (NoSuchPaddingException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
        } 
      } 

      public byte[] encrypt(String text) throws Exception 
      { 
        if(text == null || text.length() == 0) 
          throw new Exception("Empty string"); 

        byte[] encrypted = null; 

        try { 
          cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); 

          encrypted = cipher.doFinal(padString(text).getBytes()); 
        } catch (Exception e) 
        {      
          throw new Exception("[encrypt] " + e.getMessage()); 
        } 

        return encrypted; 
      } 

      public byte[] decrypt(String code) throws Exception 
      { 
        if(code == null || code.length() == 0) 
          throw new Exception("Empty string"); 

        byte[] decrypted = null; 

        try { 
          cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); 

          decrypted = cipher.doFinal(hexToBytes(code)); 
        } catch (Exception e) 
        { 
          throw new Exception("[decrypt] " + e.getMessage()); 
        } 
        return decrypted; 
      } 



      public static String bytesToHex(byte[] data) 
      { 
        if (data==null) 
        { 
          return null; 
        } 

        int len = data.length; 
        String str = ""; 
        for (int i=0; i<len; i++) { 
          if ((data[i]&0xFF)<16) 
            str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF); 
          else 
            str = str + java.lang.Integer.toHexString(data[i]&0xFF); 
        } 
        return str; 
      } 


      public static byte[] hexToBytes(String str) { 
        if (str==null) { 
          return null; 
        } else if (str.length() < 2) { 
          return null; 
        } else { 
          int len = str.length()/2; 
          byte[] buffer = new byte[len]; 
          for (int i=0; i<len; i++) { 
            buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16); 
          } 
          return buffer; 
        } 
      } 



      private static String padString(String source) 
      { 
       char paddingChar = ' '; 
       int size = 16; 
       int x = source.length() % size; 
       int padLength = size - x; 

       for (int i = 0; i < padLength; i++) 
       { 
         source += paddingChar; 
       } 

       return source; 
      } 
    } 

홈페이지 :

mcrypt = new MCrypt(); 
/* Encrypt */ 
String encrypted = MCrypt.bytesToHex(mcrypt.encrypt("Text to Encrypt")); 
//Returns 9975e28df055c336a9b7090b03f88689 
/* Decrypt */ 
String decrypted = new String(mcrypt.decrypt(encrypted)); 
//Returns "Text to Encrypt " 
문제

:

String encrypted = MCrypt.bytesToHex(mcrypt.encrypt("Text to Encrypt"));
암호화 반환 :는 9975e28df055c336a9b7090b03f88689 (INCO 인 rrect)
String decrypted = new String(mcrypt.decrypt(encrypted));
해독이 반환 제대로 암호화가, 무엇을 최대 온 반영하는 ("텍스트가 암호화하는"A "나는이 라인에 좁혔습니다"

) 암호화 한 후 :
encrypted = cipher.doFinal(padString(text).getBytes());

은 내가 padString 기능을 변경하려 있도록 char paddingChar = 0; 대신에 행운과 char paddingChar = ' '; ...

제대로 암호화 할 때 "텍스트 t 암호화 "가"cb4b4ca864213684070465b38783a6c8 "로 변경되어야합니다.

답변

3

AES는 블록 사이퍼입니다. 하나의 256 비트 블록 (= 16 바이트)을 다른 256 비트 블록으로 암호화합니다. 일반 텍스트 "암호화 할 텍스트"는 15 자 또는 248 비트입니다. AES는 그대로 암호화 할 수 없지만 일부 블록을 추가하여 전체 블록으로 만들어야합니다.

명시 적으로 패딩 문자를 추가하는 경우 명시 적으로 패딩 문자를 제거해야합니다. 각각의 다른 패딩 문자는 해독에 큰 영향을 미칩니다. 평균적으로 입력 평문 블록의 한 비트를 변경하면 출력 암호 텍스트 블록의 비트가 50 % 변경됩니다.

가장 쉬운 해결책은 Java의 패딩 기능에서 buit를 사용하는 것입니다. 암호를 다음과 같이 지정하십시오 : "AES/CBC/NoPadding". 암호화 및 암호 해독을 위해이 값을 "AES/CBC/PKCS5Padding"으로 변경하십시오. cyphertext가 어떻게 생겼는지 걱정하지 말고, plaintext가 해독 된 cipher 텍스트와 문자가 일치하는지 확인하십시오.

일반적인 오류는 을 사용하여 텍스트 문자열을 바이트 배열로 변환하는 것입니다. 오류가 발생하기 쉽기 때문에 그렇게하지 마십시오. 문자와 바이트 사이에 어떤 매핑을 사용하는지 정확하게 지정해야합니다. 다음과 같은 것을 사용하십시오 :

byte[] plainBytes = plaintextString.getBytes("UTF-8"); 

및 이와 유사한 것을 사용하십시오. 시스템 기본값을 항상 동일하게 사용하지 마십시오.

+0

불특정 인코딩의 패딩과 함정을 모두 다루는 훌륭한 대답입니다. :) –

+0

나는 들여다 보았다. 그러나 나는 여전히 초기 문제를 가지고있다. 초기 문제는 내 휴대 전화의 암호화가 제대로 암호화되지 않는다는 것입니다. 정확히 16Chars 인 "Text to Encrypt"를 보내고 패드 기능을 제거하더라도 동일한 암호화를 실행하는 PHP 스크립트에서 가져온 암호화와 일치하지 않는 암호화 된 문자열을 생성합니다. 필자는 PHP가 PKCS5PAdding을 사용하려고했지만 함수 자체가이를 지원하지 않습니다. 심지어 맞춤 해결 방법을 찾았을 때도 전화와 PHP의 암호화가 일치하지 않아 전화가 여전히 문제가 있다고 생각하게됩니다. – Rawr

+0

기본적으로 뭔가가 자바 측에서 잘못되어 있으며, 나는 그것이 무엇인지 확실하지 않습니다.그것은 hex2bin 함수 일지 모르지만 나는 그것이 패딩이라고 생각하지 않습니다. 어쩌면 내 암호를 설정하는 방법 일 수 있습니다. 나는 또한 "UTF-8"에 대한 당신의 제안을 발견했고, 나는 그것을 사용했지만 여전히 변화가 없었다. 내 전화에서'Charset.defaultCharset()'을 사용하고'com.idm.icu4jni.charset.CharsetICU [UTF-8]'을 얻었고 PHP 서버를 확인했는데'Accept-Charset \t ISO-8859-1, utf-8; q = 0.7, *; q = 0.3' 그래서 그들은 둘 다 동일한 문자셋을 사용하고 있다고 생각합니다. 이것을 망칠 수있는 것이 무엇인지 모릅니다. – Rawr