2012-08-07 61 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