我在使用 AES 加密算法和 SHA1PRNG 哈希加密和解密字符串的 Android 设备上使用了以下 Java 代码。我希望 Android 设备调用用 C# 编写的 .NET WCF 服务。我一直在到处寻找,试图在 C# 中找到一个可以以与 Java 代码类似的方式加密和解密的等价物,但找不到完全相同的方式来做到这一点。这是两种语言的 Encrypt() 方法:
爪哇:
public static String encrypt(String seed, String cleartext) throws Exception
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] rawKey = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(cleartext.getBytes());
return toHex(encrypted);
}
我在 C# 中创建了类似的东西,它也使用 AES 和 SHA1:
C#:
public static string Encrypt(string seed, string cleartext)
{
var objAesCrypto = new AesManaged();
var objHashSha1 = new SHA1Managed();
var byteHash = objHashSha1.ComputeHash(Encoding.ASCII.GetBytes(seed));
var truncatedHash = new byte[16];
Array.Copy(byteHash, truncatedHash, truncatedHash.Length);
objAesCrypto.Key = truncatedHash;
objAesCrypto.Mode = CipherMode.ECB;
var byteBuff = Encoding.ASCII.GetBytes(cleartext);
return Convert.ToBase64String(objAesCrypto.CreateEncryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
}
然而,这有几个问题。如您所见,使用 C# 版本的 SHA1 (SHA1Managed),它返回 20 个字节的散列,而不是 16 个。让它传递给 AES 算法的唯一方法是先将散列截断为 16 个字节。
第二个问题是,尽管两者在各自的环境中都可以正常工作,但当我尝试从 Java 传递加密字符串以及种子时,C# 代码永远无法正确解密它。两种情况下的加密字符串看起来都不一样,甚至长度也不同。Java 端的典型加密字符串如下所示: F7E8758A2E65518FB49C53BC707288FC
(32 个字符长)。而来自 C# 端的具有相同种子的完全相同的加密字符串看起来像这样: 3VysgnYgNi9OJBxL2FP+rQ==
(24 个字符长)。
我确信这与我在 C# 中截断哈希的事实有关,但这并不能解释为什么两个加密字符串看起来如此不同。(我注意到的另一件有趣的事情是,无论我在 C# 端使用什么字符串和种子,它总是 24 个字符长并以两个等号结尾 - 为什么会这样?)
所以,我的问题是,如何让两个环境能够使用相同的种子值解密彼此的加密字符串?我不在乎我是否需要在 C# 端使用与 Java 端不同的算法,我只需要 C# 代码能够读取 Java 加密的字符串。