2012-08-07 73 views
1

我已經將基於C#的解密函數轉換爲Java。它工作正常,可用於解密已被C#程序加密的密碼。 這裏是源代碼:爲什麼解密函數返回垃圾代碼?

import org.apache.commons.codec.binary.Base64; 

import javax.crypto.Cipher; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 
import java.security.Key; 

public class TestDecrpt { 
    public static void main(String[] args) throws Exception { 
     String data = "encrypted data"; 
     String sEncryptionKey = "encryption key"; 
     byte[] rawData = new Base64().decode(data); 
     byte[] salt = new byte[8]; 
     System.arraycopy(rawData, 0, salt, 0, salt.length); 

     Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(sEncryptionKey, salt); 

     byte[] IV = keyGen.getBytes(128/8); 
     byte[] keyByte = keyGen.getBytes(256/8); 

     Key key = new SecretKeySpec(keyByte, "AES"); 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(IV)); 
     int pureDataLength = rawData.length - 8; 
     byte[] pureData = new byte[pureDataLength]; 
     System.arraycopy(rawData, 8, pureData, 0, pureDataLength); 
     String plaintext = new String(cipher.doFinal(pureData), "UTF-8").replaceAll("\u0000", ""); 
     System.out.println(plaintext); 
    } 
} 

我按照它的算法寫的加密功能。和代碼是:

import org.apache.commons.codec.binary.Base64; 

import javax.crypto.Cipher; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 
import java.security.Key; 
import java.security.SecureRandom; 


public class testEncrypt { 
    public static void main(String[] args) throws Exception { 
     String data = "[email protected][email protected][email protected][email protected][email protected]"; 
     String sEncryptionKey = "encryption key"; # the same key 
     byte[] rawData = new Base64().decode(data); 
     SecureRandom random = new SecureRandom(); 
     byte[] salt = new byte[8]; 
     random.nextBytes(salt); 
     Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(sEncryptionKey, salt); 

     byte[] IV = keyGen.getBytes(128/8); 
     byte[] keyByte = keyGen.getBytes(256/8); 

     Key key = new SecretKeySpec(keyByte, "AES"); 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV)); 
     byte[] out2 = cipher.doFinal(rawData); 

     byte[] out = new byte[8 + out2.length]; 
     System.arraycopy(salt, 0, out, 0, 8); 
     System.arraycopy(out2, 0, out, 8, out2.length); 
     //String outStr=new String(out,"UTF-8"); 
     String outStr = new Base64().encodeToString(out); 
     System.out.println(outStr); 
     System.out.print(outStr.length()); 

    } 
} 

然而,加密的數據不能被正確解密,它總是返回垃圾代碼,如

ꉜ뙧巓妵峩樞펶땝ꉜ뙧巓妵峩樞펶땝ꉜ뙧巓

加密函數有問題嗎?

============================================== ================================== [更新] 代碼改變爲

byte[] rawData = data.getBytes("UTF-8"); 

數據可以成功加密和解密。 但是,用Java加密的數據無法在C#中正確解密。 這裏是C#版本解密函數:

using System; 
using System.IO; 
using System.Security.Cryptography; 
using System.Text; 


namespace Test 
{ 
    class Program 
    { 
     public static void Main(string[] args) 
     { 
       string data="EncryptedData"; 
       string sEncryptionKey="EncryptionKey"; 

       byte[] rawData = Convert.FromBase64String(data); 
       byte[] salt = new byte[8]; 
       for (int i = 0; i < salt.Length; i++) 
        salt[i] = rawData[i]; 

       Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(sEncryptionKey, salt); 
       Rijndael aes = Rijndael.Create(); 
       aes.IV = keyGenerator.GetBytes(aes.BlockSize/8); 
       aes.Key = keyGenerator.GetBytes(aes.KeySize/8); 

       using (MemoryStream memoryStream = new MemoryStream()) 
       using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write)) 
       { 
        cryptoStream.Write(rawData, 8, rawData.Length - 8); 
        cryptoStream.Close(); 

        byte[] decrypted = memoryStream.ToArray(); 
        Console.Out.WriteLine(Encoding.Unicode.GetString(decrypted)); 
        Console.In.ReadLine(); 
       } 

     } 
    } 
} 

我發現,原來的代碼使用的是「統一」作爲輸出格式,

Encoding.Unicode.GetString(decrypted) 

所以我改變我的Java代碼爲「統一」。

對於解密在Java中:

String plaintext = new String(cipher.doFinal(pureData), "Unicode"); 
System.out.println(plaintext); 

對於加密在Java中:

byte[] rawData = data.getBytes("Unicode"); 

但是使用C#代碼來解密已加密的由Java程序仍然滿足垃圾碼的數據。

我該如何解決這個問題?有什麼魔術技巧嗎?


[最後更新] 使用 「UTF-16LE」,而不是 「UTF-8」 之後,這個問題已經沒有了。看起來,「UTF-16LE」是Java相當於C#的「Unicode」。

+0

這是用於研究/教育的目的是什麼?如果不是,我必須警告你,編寫自己的安全核心通常不是明智的做法。犯錯非常容易,並且有很好的標準庫和工具可用。 – 2012-08-07 06:38:16

回答

3

這就是問題所在:

String data = "[email protected][email protected][email protected][email protected][email protected]"; 
byte[] rawData = new Base64().decode(data); 

該文本是不是意味着要Base64編碼的二進制數據。這只是文字。你爲什麼試圖將它解碼爲base64數據?

你想:

byte[] rawData = data.getBytes("UTF-8"); 

這樣,當你以後寫:

String plaintext = new String(cipher.doFinal(pureData), "UTF-8") 
            .replaceAll("\u0000", ""); 

你做相反的動作。 (不可否認,你可能不應該需要呼叫replaceAll,但這是另一回事。)

對於這樣的任何事情,您需要確保您在「出」路上採取的步驟與步驟相反在途中「in」。因此,在正確的代碼,您有:

因此扭轉,我們這樣做:

Encrypted text data => encrypted binary data (decode with base64) 
Encrypted binary data => unencrypted binary data (decrypt with AES) 
Unencrypted binary data => unencrypted text data (decode via UTF-8) 
+0

謝謝你的答案。我編輯我的代碼,問題已經消失。 Java代碼之間的加密和解密工作正常。但是,我遇到了另一個垃圾代碼問題,同時使用C#代碼來解密由java加密的數據。我有更新我的問題。你可以看看這個嗎? :) – SUT 2012-08-07 04:58:32

+0

那麼,我嘗試使用Java中的「UTF-16LE」,並且問題已經消失。看起來,「UTF-16LE」是與C#默認的「Unicode」相當的java。 – SUT 2012-08-07 05:35:43

+0

@SuT:是的,這是 - 但你仍然必須在正確的地方使用它。我會建議在兩種語言中使用UTF-8,否則你的加密數據可能大約是它需要的兩倍。 – 2012-08-07 08:18:45