2011-10-11 105 views
1

我在C#中有一個加密和解密字符串文本(基本的東西)的DLL,但現在我需要在Java中實現相同的加密方法,以便某些應用程序可以加密數據並將其發送到圖書館。Java加密和C#解密問題

我不能修改C#代碼,因爲它已經在生產中,但是Java沒有,所以請你在Java端做任何建議。

基本上我試圖在Java中實現相同的C#加密方法。這裏是我的C#代碼:

注意:密碼,鹽等值顯然只是參考。

const string PassPhrase = "IhDyHz6bgQyS0Ff1/1s="; 
    const string SaltValue = "0A0Qvv09OXd3GsYHVrA="; 
    const string HashAlgorithm = "SHA1";     
    const int PasswordIterations = 3;     
    const string InitVector = "GjrlRZ6INgNckBqv";  
    const int KeySize = 256; 


public static string Encrypt(string plainText) 
    { 

     byte[] initVectorBytes = Encoding.ASCII.GetBytes(InitVector); 
     byte[] saltValueBytes = Encoding.ASCII.GetBytes(SaltValue); 


     byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); 


     PasswordDeriveBytes password = new PasswordDeriveBytes(
                 PassPhrase, 
                 saltValueBytes, 
                 HashAlgorithm, 
                 PasswordIterations); 


     byte[] keyBytes = password.GetBytes(KeySize/8); 


     RijndaelManaged symmetricKey = new RijndaelManaged(); 


     symmetricKey.Mode = CipherMode.CBC; 


     ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                 keyBytes, 
                 initVectorBytes); 


     MemoryStream memoryStream = new MemoryStream(); 


     CryptoStream cryptoStream = new CryptoStream(memoryStream, 
                encryptor, 
                CryptoStreamMode.Write); 

     cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); 


     cryptoStream.FlushFinalBlock(); 


     byte[] cipherTextBytes = memoryStream.ToArray(); 


     memoryStream.Close(); 
     cryptoStream.Close(); 


     string cipherText = Convert.ToBase64String(cipherTextBytes); 


     return cipherText; 
    } 


public static string Decrypt(string cipherText) 
    { 

     byte[] initVectorBytes = Encoding.ASCII.GetBytes(InitVector); 
     byte[] saltValueBytes = Encoding.ASCII.GetBytes(SaltValue); 


     byte[] cipherTextBytes = Convert.FromBase64String(cipherText); 

     PasswordDeriveBytes password = new PasswordDeriveBytes(
                 PassPhrase, 
                 saltValueBytes, 
                 HashAlgorithm, 
                 PasswordIterations); 


     byte[] keyBytes = password.GetBytes(KeySize/8); 


     RijndaelManaged symmetricKey = new RijndaelManaged(); 


     symmetricKey.Mode = CipherMode.CBC; 


     ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
                 keyBytes, 
                 initVectorBytes); 


     MemoryStream memoryStream = new MemoryStream(cipherTextBytes); 


     CryptoStream cryptoStream = new CryptoStream(memoryStream, 
                 decryptor, 
                 CryptoStreamMode.Read); 


     byte[] plainTextBytes = new byte[cipherTextBytes.Length]; 


     int decryptedByteCount = cryptoStream.Read(plainTextBytes, 
                0, 
                plainTextBytes.Length); 


     memoryStream.Close(); 
     cryptoStream.Close(); 


     string plainText = Encoding.UTF8.GetString(plainTextBytes, 
                0, 
                decryptedByteCount); 


     return plainText; 
    } 

這裏是我的Java代碼,它加密的數據,而不是在相同的方式,C#的加密代碼,所以當我嘗試使用C#庫來解密它拋出的異常:「的長度數據的解密方法是無效的」

static final String PassPhrase = "IhDyHz6bgQyS0Ff1/1s="; 
    static final String SaltValue = "0A0Qvv09OXd3GsYHVrA=";  
    static final String HashAlgorithm = "SHA1";    
    static final int PasswordIterations = 3;     
    static final String InitVector = "GjrlRZ6INgNckBqv";  
    static final int KeySize = 256; 

public static String encrypt(String plainText) 
{ 
    char[] password = PassPhrase.toCharArray(); 
    byte[] salt = SaltValue.getBytes(); 
    byte[] iv = InitVector.getBytes(); 
    byte[] ciphertext = new byte[0]; 

    SecretKeyFactory factory; 
    try { 
     factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 

     KeySpec spec = new PBEKeySpec(password, salt, PasswordIterations, 256); 
     SecretKey tmp; 

     tmp = factory.generateSecret(spec); 

     SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); 

     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, secret); 
     AlgorithmParameters params = cipher.getParameters(); 
     //iv = params.getParameterSpec(IvParameterSpec.class).getIV(); 
     ciphertext = cipher.doFinal(plainText.getBytes("UTF-8")); 

    } catch (NoSuchAlgorithmException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (InvalidKeySpecException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (InvalidKeyException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    //catch (InvalidParameterSpecException e) { 
    // // TODO Auto-generated catch block 
    // e.printStackTrace(); 
    //} 
    catch (IllegalBlockSizeException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (BadPaddingException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (UnsupportedEncodingException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

    return Base64.encode(new String(ciphertext)); 
} 

EDIT 1:我固定在Java代碼的最後一個字節陣列轉換爲字符串,如喬恩斯基特建議。

回答

3

這是出了什麼問題,在Java代碼:

return Base64.encode(ciphertext.toString()); 

你調用toString()字節數組,這將總是給一個字符串,如[[email protected]上。

編輯:哦,只是注意到你可以改變Java方面。萬歲。

基本上,你需要使用一個Base64 API,它允許:

return Base64.encode(ciphertext); 

我總是失望Base64編碼的API其中允許你「編碼」的字符串,說實話...的base64從根本上將二進制數據編碼爲文本,並將文本數據解碼爲二進制。哦...

無論如何,如果你需要一個允許你傳遞字節數組的方法,使用this APIencodeBytes方法)。

我沒有檢查過詳細實際的加密部分,但C#代碼至少看起來像它做正確的事在編碼方面。這可能與using報表做雖然:)

+0

謝謝你,你是正確的,有在Java的那部分的錯誤代碼,我修正了它,現在我得到正確的編碼文本,但仍然無法使用C#代碼解密(例外情況),例如我使用發佈的C#方法加密:Hello World並獲取[「fawkThB32M8FsbOxn05KEw ==」]而在Java中它更長[「bigIxbgKxaHDizUuP8OlR1bCsMOnw78 =」]爲什麼會發生這種情況?我是否應該在某個地方錯過初始向量? – lidermin

+0

@lidermin:填充問題?不確定... –

+1

要嘗試的事情:#1檢查使用的base64變體是否遵循相同的規則並添加換行符等。#2檢查編碼的文本是否可以被java解碼。 – Voo

1

這裏是一個C#示例,你需要的IterationCount和PaddingMode.None

protected void Page_Load(object sender, EventArgs e) 
{ 
    string value = ""; 
    string password = ""; 
    string salt = ""; 
    string iv = ""; 


    byte[] vectorBytes = Convert.FromBase64String(Server.UrlDecode(iv)); 
    byte[] cipherText = Convert.FromBase64String(Server.UrlDecode(value)); 

    Rfc2898DeriveBytes key1 = new Rfc2898DeriveBytes(password, StringToByteArray(salt)); //same as PBKDF2WithHmacSHA1 
    key1.IterationCount = 32; 
    byte[] keyBytes = key1.GetBytes(16); 

    string Answer = DecryptDataAES(cipherText, keyBytes, vectorBytes); //vectorBytes is good 

    //litAnswer.Text = Answer; 
} 

public static string DecryptDataAES(byte[] cipherText, byte[] key, byte[] iv) 
{ 
    string plaintext = null; 

    using (Rijndael rijndael = Rijndael.Create()) 
    { 
     rijndael.Key = key; 
     rijndael.IV = iv; 
     rijndael.Padding = PaddingMode.None; 

     ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV); 

     // Create the streams used for decryption. 
     using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 
     { 
      using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
      { 
       using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
       { 
        plaintext = srDecrypt.ReadToEnd(); 
       } 
      } 
     } 
    } 
    return plaintext; 
} 

public static byte[] StringToByteArray(String hex) 
{ 
    int NumberChars = hex.Length/2; 
    byte[] bytes = new byte[NumberChars]; 
    using (var sr = new StringReader(hex)) 
    { 
     for (int i = 0; i < NumberChars; i++) 
      bytes[i] = 
       Convert.ToByte(new string(new char[2] { (char)sr.Read(), (char)sr.Read() }), 16); 
    } 
    return bytes; 
} 
+0

這個例子幫助我。不知道爲什麼,但對我的情況,我不需要設置填充。否則,這根據需要工作。謝謝。 – DaveDude