Java AES加密代码转C#后输出不一致,求技术排查指导
Java AES加密代码移植到C#后输出不匹配问题排查
我多次尝试将一段Java AES加密代码移植到C#中,但加密输出始终无法匹配。以下是两端的完整代码,请帮忙指出问题所在:
Java 加密代码
import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.IvParameterSpec; public class JavaEncryption { private static SecretKeySpec secretKey; private static byte[] key; private static void processEncryptionKey(String secret) { try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(secret.getBytes("UTF-8")); byte[] base_hash = crypt.digest(); byte[] buffer = new byte[64]; for (int i = 0; i < 64; i++) buffer [i] = 0x36; for (int i = 0; i < 20; i++) buffer [i] ^= base_hash[i]; crypt.reset(); crypt.update(buffer, 0, buffer.length); byte[] final_hash = crypt.digest(); key = Arrays.copyOf(final_hash, 16); secretKey = new SecretKeySpec(key, "AES"); } catch(NoSuchAlgorithmException e) { e.printStackTrace(); } catch(UnsupportedEncodingException e) { e.printStackTrace(); } } public static String encrypt(String strToEncrypt, String secret) { try { processEncryptionKey(secret); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16])); return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8"))); } catch (Exception e) { System.out.println("Error while encrypting: " + e.toString()); } return null; } public static String decrypt(String strToDecrypt, String secret) { try { processEncryptionKey(secret); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[16])); return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt))); } catch (Exception e) { System.out.println("Error while decrypting: " + e.toString()); } return null; } public static void main(String[] args){ final String secretKey = "YVr_wlp8_fFa"; System.out.println("Secret = " + secretKey); String originalString = "testing"; System.out.println("Clear text = " + originalString); String encryptedString = JavaEncryption.encrypt(originalString, secretKey) ; System.out.println("Encrypted = " + encryptedString); String decryptedString = JavaEncryption.decrypt(encryptedString, secretKey); System.out.println("Decrypted = " + decryptedString); } }
原C#加密代码(输出不匹配)
using System; using System.IO; using System.Security.Cryptography; using System.Text; public class CSharpEncryption { public static void Main() { Console.WriteLine(new CSharpEncryption().EncryptText()); } public string EncryptText() { using (var aes = new AesManaged()) { aes.Key = CreateSecretKey(); aes.IV = new byte[16]; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; byte[] encrypted = GetEncryptedBytes("testing", aes.Key, aes.IV); string encString = Convert.ToBase64String(encrypted); return encString; } } protected byte[] GetEncryptedBytes(string textToEncrypt, byte[] Key, byte[] IV) { byte[] encrypted; using (var rij = new RijndaelManaged()) { rij.Key = Key; rij.IV = IV; ICryptoTransform encryptor = rij.CreateEncryptor(rij.Key, rij.IV); using (var msEncrypt = new MemoryStream()) { using (var cs = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (var sw = new StreamWriter(cs)) { sw.Write(textToEncrypt); } encrypted = msEncrypt.ToArray(); } } } return encrypted; } private static byte[] CreateSecretKey() { var keyHash = CreateHashedKey(); byte[] key = null; using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(keyHash, new byte[16], 1)) { key = rfc2898.GetBytes(16); } return key; } private static string CreateHashedKey() { var hashBase64 = String.Empty; using (SHA1Managed sha1 = new SHA1Managed()) { byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes("YVr_wlp8_fFa")); hashBase64 = CreateBase64Hash(hash, true); } return hashBase64; } private static string CreateBase64Hash(byte[] arr, bool withoutPadding) { var base64String = Convert.ToBase64String(arr); if (withoutPadding) { base64String = base64String.TrimEnd('='); } base64String += "\n"; return base64String; } }
问题根源及修正方案
核心问题:密钥生成逻辑完全不一致
Java代码的密钥生成逻辑是:
- 对原始密钥做SHA-1哈希,得到20字节的
base_hash - 创建64字节全为
0x36的数组,将前20字节与base_hash逐字节异或 - 对异或后的数组再次做SHA-1哈希,取前16字节作为AES密钥
而原C#代码错误地使用了Rfc2898DeriveBytes(PBKDF2算法),还把SHA-1结果转成Base64并添加换行,完全偏离了Java的逻辑,这是输出不匹配的核心原因。
另外,原C#加密实现嵌套了AesManaged和RijndaelManaged,属于冗余操作,且用StreamWriter写入文本可能引入潜在编码问题。
修正后的C#代码
using System; using System.Security.Cryptography; using System.Text; public class CSharpEncryption { public static void Main() { var secretKey = "YVr_wlp8_fFa"; var originalString = "testing"; var encryptedString = Encrypt(originalString, secretKey); Console.WriteLine($"Encrypted = {encryptedString}"); var decryptedString = Decrypt(encryptedString, secretKey); Console.WriteLine($"Decrypted = {decryptedString}"); } public static string Encrypt(string strToEncrypt, string secret) { try { var key = ProcessEncryptionKey(secret); using (var aes = Aes.Create()) { aes.Key = key; aes.IV = new byte[16]; // 和Java一致用全0IV aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); var inputBytes = Encoding.UTF8.GetBytes(strToEncrypt); var encryptedBytes = encryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length); return Convert.ToBase64String(encryptedBytes); } } catch (Exception e) { Console.WriteLine($"Error while encrypting: {e.ToString()}"); return null; } } public static string Decrypt(string strToDecrypt, string secret) { try { var key = ProcessEncryptionKey(secret); using (var aes = Aes.Create()) { aes.Key = key; aes.IV = new byte[16]; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); var inputBytes = Convert.FromBase64String(strToDecrypt); var decryptedBytes = decryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length); return Encoding.UTF8.GetString(decryptedBytes); } } catch (Exception e) { Console.WriteLine($"Error while decrypting: {e.ToString()}"); return null; } } private static byte[] ProcessEncryptionKey(string secret) { using (var sha1 = SHA1.Create()) { // 第一步:对密钥做SHA-1哈希 sha1.Reset(); var secretBytes = Encoding.UTF8.GetBytes(secret); var baseHash = sha1.ComputeHash(secretBytes); // 第二步:创建64字节0x36数组,与baseHash前20字节异或 var buffer = new byte[64]; for (int i = 0; i < 64; i++) buffer[i] = 0x36; for (int i = 0; i < baseHash.Length; i++) buffer[i] ^= baseHash[i]; // 第三步:再次SHA-1哈希,取前16字节作为AES密钥 sha1.Reset(); var finalHash = sha1.ComputeHash(buffer); var key = new byte[16]; Array.Copy(finalHash, key, 16); return key; } } }
修正后的代码严格对齐Java的密钥生成逻辑,加密/解密实现也简化为直接使用Aes.Create()处理字节数组,现在输出会和Java代码完全一致。
内容的提问来源于stack exchange,提问作者spinon
相关产品推荐
相关产品推荐

