Java中的AES加密和C#中的解密

您好我已使用标准AES算法加密的加密hex字符串和密钥。 码:

final String key = "=abcd!#Axd*G!pxP"; final javax.crypto.spec.SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); final javax.crypto.Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte [] encryptedValue = cipher.doFinal(input.getBytes()); return new String(org.apache.commons.codec.binary.Hex.encodeHex(encryptedValue)); 

现在我尝试使用C#代码解密它:

  RijndaelManaged rijndaelCipher = new RijndaelManaged(); // Assumed Mode and padding values. rijndaelCipher.Mode = CipherMode.ECB; rijndaelCipher.Padding = PaddingMode.None; // AssumedKeySize and BlockSize values. rijndaelCipher.KeySize = 0x80; rijndaelCipher.BlockSize = 0x80; // Convert Hex keys to byte Array. byte[] encryptedData = hexStringToByteArray(textToDecrypt); byte[] pwdBytes = Encoding.Unicode.GetBytes(key); byte[] keyBytes = new byte[0x10]; int len = pwdBytes.Length; if (len > keyBytes.Length) { len = keyBytes.Length; } Array.Copy(pwdBytes, keyBytes, len); rijndaelCipher.Key = keyBytes; rijndaelCipher.IV = keyBytes; // Decrypt data byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length); str = Encoding.UTF8.GetString(plainText); 

  static private byte[] HexToBytes(string str) { if (str.Length == 0 || str.Length % 2 != 0) return new byte[0]; byte[] buffer = new byte[str.Length / 2]; char c; for (int bx = 0, sx = 0; bx  '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) < '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')); } return buffer; } 

但输出并不像预期的那样。 请指出我哪里出错了?

你的代码有一个大问题:混合字符编码!

在Java中,您调用key.getBytes() ,不带参数。 此方法返回UTF-8或CP1252 / ISO 8859-1编码数据,具体取决于您的操作系统和Java中的默认字符集。

在C#端,您使用的是Encoding.Unicode.GetBytes(key) – .Net中的“Unicode”是双字节字符别名UTF-16(Little-Endian)的同义词 。 因此,您在C#中使用不同的密钥。

您应该能够通过比较Java和C#中的字节数来看到差异:

Java: "=abcd!#Axd*G!pxP".getBytes().length = 16

C#: Encoding.Unicode.GetBytes("=abcd!#Axd*G!pxP").Length = 32

我强烈建议您使用字节数组而不是字符串来定义加密密钥。

更新:另一个区别是您在C#中设置初始化向量(IV),而在Java中则没有这样做。 当您使用ECB时,不应使用IV,但如果您更改为CBC,例如这会产生很大的不同。

我不仅需要C#,还需要兼容Silverlight和Windows Phone 7。 我完全厌倦了缺乏Java和C#(以及基于Base64)可接受的完整示例。

代码不是什么花哨的东西,但有效。 请随意改进,因为我将其标记为社区维基,但请务必在提交任何更改之前对其进行测试。

这是C#代码:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; using System.IO; //Author: Doguhan Uluca //Website: www.deceivingarts.com namespace DeceivingArts.Common { public class Encryptor { private string _seed = ""; public Encryptor(string seed) { _seed = seed; } public string Encrypt(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new() { var pwdBytes = Encoding.UTF8.GetBytes(_seed); using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm()) { ICryptoTransform saEnc = sa.CreateEncryptor(pwdBytes, pwdBytes); var encBytes = Encoding.UTF8.GetBytes(input); var resultBytes = saEnc.TransformFinalBlock(encBytes, 0, encBytes.Length); return Convert.ToBase64String(resultBytes); } } public string Decrypt(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new() { var pwdBytes = Encoding.UTF8.GetBytes(_seed); using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm()) { ICryptoTransform saDec = sa.CreateDecryptor(pwdBytes, pwdBytes); var encBytes = Convert.FromBase64String(input); var resultBytes = saDec.TransformFinalBlock(encBytes, 0, encBytes.Length); return Encoding.UTF8.GetString(resultBytes); } } } } 

这是Android兼容的Java代码:

 import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * Usage: * 
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext) * ... * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto) * 

* @author ferenc.hechler * @author Doguhan Uluca */ public class Encryptor { public static String encrypt(String seed, String cleartext) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = encrypt(rawKey, cleartext.getBytes()); return toBase64(result); } public static String decrypt(String seed, String encrypted) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] enc = fromBase64(encrypted); byte[] result = decrypt(rawKey, enc); return new String(result); } private static byte[] getRawKey(byte[] seed) throws Exception { SecretKey skey = new SecretKeySpec(seed, "AES"); byte[] raw = skey.getEncoded(); return raw; } private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(raw); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec); byte[] encrypted = cipher.doFinal(clear); return encrypted; } private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(raw); cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; } public static String toBase64(byte[] buf) { return Base64.encodeBytes(buf); } public static byte[] fromBase64(String str) throws Exception { return Base64.decode(str); } }

有关Base64的转换,请参阅http://iharder.net/base64上的优秀实现。

我希望这可以节省人们的时间。

试试这个组合:

 aesAlg.Mode = CipherMode.ECB; aesAlg.Padding = PaddingMode.PKCS7; //aesAlg.IV; - use default (not assign) 

我怀疑错误是你没有在这个等式的Java方面指定填充或操作模式。 我不确定Java的AES实现默认是什么,但我会从获取密码时开始指定两者。 例如:

 Cipher.getInstance("//"); 

您需要在Java中查找AES支持的填充方案和操作模式,然后确保将C#代码配置为使用完全相同的配置。