您好我已使用标准AES算法加密的加密十六进制字符串和密钥。 代码:
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 < buffer.Length; ++bx, ++sx)
{
// Convert first half of byte
c = str[sx];
buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) << 4);
// Convert second half of byte
c = str[++sx];
buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0'));
}
return buffer;
}
但输出不符合预期。 请指出我出错的地方?
答案 0 :(得分:8)
你的代码有一个大问题:它混合了字符编码!
在Java中,您调用key.getBytes()
,没有参数。此方法返回UTF-8或CP1252 / ISO 8859-1编码数据,具体取决于您的操作系统和Java中的默认字符集。
在C#端,您使用的是Encoding.Unicode.GetBytes(key)
- "Unicode" in .Net is a synonym for double byte characters alias 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,则会产生很大的不同。
答案 1 :(得分:4)
我不仅需要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<TSymmetricAlgorithm>(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<TSymmetricAlgorithm>(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:
* <pre>
* String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
* ...
* String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
* </pre>
* @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的优秀实施。
我希望这可以节省人们的时间。
答案 2 :(得分:2)
试试这个组合:
aesAlg.Mode = CipherMode.ECB;
aesAlg.Padding = PaddingMode.PKCS7;
//aesAlg.IV; - use default (not assign)
答案 3 :(得分:0)
我怀疑错误是你没有在这个等式的Java方面指定填充或操作模式。我不确定Java的AES实现默认是什么,但我会从获取密码时开始指定两者。例如:
Cipher.getInstance("<algorithm>/<mode of operation>/<padding>");
您需要在Java中查找AES支持的填充方案和操作模式,然后确保将C#代码配置为使用完全相同的配置。