You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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代码的密钥生成逻辑是:

  1. 对原始密钥做SHA-1哈希,得到20字节的base_hash
  2. 创建64字节全为0x36的数组,将前20字节与base_hash逐字节异或
  3. 对异或后的数组再次做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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 19:26:28