AES 加密问题(加密的字符串不是它应该的 - Java & .NET)

Problems with AES encryption (encrypted string is not what it should be - Java & .NET)

我正在尝试使用 AES 加密加密纯文本字符串以与第三方系统集成。接收者没有任何文件来解释他们的加密算法是什么,他们只是分享了下面的 Java 代码来解释加密的工作原理:

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
    
public class CipherData {
    private static final String SALT = "pa(MS";                               //Salt. Required for key generation
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";           //Encryption Algorithm/Cipher/Padding
    private static String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
    private static int KEY_SIZE = 256;  
    private static final String TOKEN = "Pre123454sk";        //Password. Used for Key Generation
    private static String initVector = "pre1234Init12345";    //IV. Required for Key generation
    private static int KEY_ITERATIONS = 22123;

    public static String encrypt(String value) throws Exception {    //Encryption Module
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        Key key = generateKey();
        cipher.init(1, key, iv);
        byte[] encrypted = cipher.doFinal(value.getBytes());
        return base64Encode(encrypted);
    }

    public static String decrypt(String value) throws Exception {   //Decryption module
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        Key key = generateKey();
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(2, key, iv);
        byte[] original = cipher.doFinal(base64Decode(value));
        return new String(original);
    }

    private static Key generateKey() throws Exception {   //AES Key Generation
        byte[] saltBytes = SALT.getBytes("UTF-8");
        SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
        PBEKeySpec spec = new PBEKeySpec(TOKEN.toCharArray(), saltBytes, KEY_ITERATIONS, KEY_SIZE);
        SecretKey secretKey = skf.generateSecret(spec);
        SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");
        return key;
    }

    private static String base64Encode(byte[] token) {
        String encoded = DatatypeConverter.printBase64Binary(token);
        return encoded;
    }

    private static byte[] base64Decode(String token) {
        return DatatypeConverter.parseBase64Binary(token);
    }

    public static void main(String[] args) {
        String clearPAN="ABCDE1234F", encrPAN="", decrPAN="";
        try {
            encrPAN=encrypt(clearPAN);
            decrPAN=decrypt(encrPAN);
            System.out.println("Clear PAN: " + clearPAN);
            System.out.println("Encrypted PAN: " + encrPAN);
            System.out.println("Decrypted PAN: " + decrPAN);
        }
        catch (Exception e) {
            System.out.print("Exception Occured in main()");
            e.printStackTrace();
        }
    }
}

我正在 .NET 中开发我的应用程序,但我无法通过使用上述 Java 代码获得与接收方相同的字符串,而且我们不知道我们应该做什么正在做。

这是我的.NET算法(我刚刚从Java代码中推断出这个逻辑,这是我第一次使用Java,所以如果我做了一个愚蠢的错误):

private static String TOKEN = "Pre123454sk";
    private static String initVector = "pre1234Init12345";
    private static string Encryption(string plainText)
    {
        using (var aesProvider = new AesCryptoServiceProvider())
        {
            //String SALT = "pa(MS";

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
            pdb.IterationCount = 22123;
            aesProvider.KeySize = 256;
            aesProvider.Padding = PaddingMode.PKCS7;
            aesProvider.Mode = CipherMode.CBC;
            aesProvider.Key = pdb.GetBytes(16);
            aesProvider.IV = Encoding.UTF8.GetBytes(initVector);

            Byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            ICryptoTransform encryptor = aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV);
            using (var memStream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                    cryptoStream.FlushFinalBlock();
                    Byte[] cipherTextBytes = memStream.ToArray();
                    memStream.Close();
                    memStream.Flush();
                    cryptoStream.Close();
                    return Convert.ToBase64String(cipherTextBytes);
                }
            }
        }
    }

    private static string Decryption(string plainText)
    {
        using (var aesProvider = new AesCryptoServiceProvider())
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
            pdb.IterationCount = 22123;
            aesProvider.KeySize = 256;
            aesProvider.Padding = PaddingMode.Zeros;
            aesProvider.Mode = CipherMode.CBC;
            aesProvider.Key = pdb.GetBytes(16);
            aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
            byte[] cipherTextBytes1 = Convert.FromBase64String(plainText);
            ICryptoTransform decryptor = aesProvider.CreateDecryptor(aesProvider.Key, aesProvider.IV);
            using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes1))
            {
                using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
                    {
                        return streamReader.ReadToEnd();
                    }
                }
            }
        }
    }

我已经尝试用 PasswordDeriveBytes() 函数中的 IV 值替换 TOKEN 值,但它仍然给我同样的错误(接收系统无法解密此请求)。

我现在没主意了,也得不到另一端的支持。如果有人能帮我解决就太好了。

多亏了 Topaco(问题中的第一条评论),我才得以解决。

这是 Topaco 的原始答案:

Java代码使用PBKDF2和HMAC/SHA1,C#代码基于PBKDF1算法。对于 C# 代码中的 PBKDF2,PasswordDeriveBytes 必须替换为 Rfc2898DeriveBytes(默认为 HMAC/SHA1)。请注意,.NET 实现需要至少 8 个字节的盐。此外,Java 使用 32 字节密钥,C# 代码使用 16 字节密钥。通过一致的密钥派生和密钥大小,生成的密文是相同的。 – Topaco

我唯一需要更改的是从 .NET 5 升级到 .NET 6。