将 Java AES CBC 加密转换为 C#

Converting Java AES CBC Encryption to C#

我正在尝试将以下 Java 加密片段转换为 C#

private final String encryptToken(String str) {
    byte[] generateIV = generateIV(16);
    Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
    instance.init(1, new SecretKeySpec(new byte[]{55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55}, "AES/CBC/PKCS5Padding"), new IvParameterSpec(generateIV));
    Charset charset = Charsets.UTF_8;
    if (str != null) {
        byte[] bytes = str.getBytes(charset);
        Intrinsics.checkExpressionValueIsNotNull(bytes, "(this as java.lang.String).getBytes(charset)");
        byte[] doFinal = instance.doFinal(bytes);
        StringBuilder sb = new StringBuilder();
        sb.append(new String(generateIV, Charsets.UTF_8));
        String encodeToString = Base64.encodeToString(doFinal, 0);
        Intrinsics.checkExpressionValueIsNotNull(encodeToString, "Base64.encodeToString(cipherText, Base64.DEFAULT)");
        if (encodeToString != null) {
            sb.append(StringsKt.trim((CharSequence) encodeToString).toString());
            return sb.toString();
        }
        throw new TypeCastException("null cannot be cast to non-null type kotlin.CharSequence");
    }
    throw new TypeCastException("null cannot be cast to non-null type java.lang.String");
}

这是我的 C# 代码:

        public static string Encrypt(string token)
        {
            byte[] IV;
            byte[] encrypted;
            byte[] key = new byte[] { 55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55 };
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key;
                aesAlg.IV = GenerateIv(16);
                IV = aesAlg.IV;
                aesAlg.Mode = CipherMode.CBC;
                aesAlg.Padding = PaddingMode.PKCS7;

                var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                using (var msEncrypt = new MemoryStream())
                {
                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(Encoding.UTF8.GetBytes(token));
                        }
                        encrypted = msEncrypt.ToArray();

                        StringBuilder sb = new StringBuilder();
                        sb.Append(Encoding.UTF8.GetString(IV));

                        string encodedString = Convert.ToBase64String(encrypted);

                        sb.Append(encodedString.ToString());
                        return sb.ToString();

                    }
                }
            }
            return "";
        }

我正在使用加密例程的结果来授权我无法控制的 API。使用我的 C# 代码,API returns“未授权”是我的加密例程不太正确的结果。

我注意到 Java 方法使用 PKCS5 填充,但 .NET 中唯一可用的选项是 PKCS7。但是阅读 之前的文章,我知道它们是相同的,因此应该无关紧要。

如果我使用 h62PmLFO5Yq4SxQw 的临时 IV 和 4a97adfc-d25c-485b-84af-86c93ff28b20token,Java 代码 returns:h62PmLFO5Yq4SxQwgoOqOLrjcZmnYbKxEDhl1hsDeCGUmEv8kwwP337JfYyvjuXgaDdND9vqSeAd9NpH 和我的 C# 代码 returns: h62PmLFO5Yq4SxQwfUXW396ss2Pzopk0FHC/7A==

有人能就 Java 代码和我的代码之间的任何明显差异提出建议吗?

原因是您正在使用 StreamWriter,它旨在将 text 写入流中,但随后您在那里写入了原始字节:

swEncrypt.Write(Encoding.UTF8.GetBytes(token));

此处使用的重载是 Write(object),描述为“通过对该对象调用 ToString() 方法,将对象的文本表示形式写入流”。所以基本上你是在加密“System.Byte[]”字符串,而不是令牌。

您可以删除该文本编写器并将字节直接写入加密流(另请注意 leaveOpen: true):

public static string Encrypt(string token)
{
    byte[] IV;
    byte[] encrypted;
    byte[] key = new byte[] { 55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55 };
    using (var aesAlg = new RijndaelManaged())
    {
        aesAlg.Key = key;
        var iv = Encoding.UTF8.GetBytes("h62PmLFO5Yq4SxQw");
        aesAlg.IV = iv;
        IV = aesAlg.IV;
        aesAlg.Mode = CipherMode.CBC;
        aesAlg.Padding = PaddingMode.PKCS7;

        var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

        using (var msEncrypt = new MemoryStream())
        {
            using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write, leaveOpen: true))
            {
                // just write to stream directly
                csEncrypt.Write(Encoding.UTF8.GetBytes(token));
            }
            // read here, so that we are sure writing is finished
            encrypted = msEncrypt.ToArray();

            StringBuilder sb = new StringBuilder();
            sb.Append(Encoding.UTF8.GetString(IV));

            string encodedString = Convert.ToBase64String(encrypted);

            sb.Append(encodedString.ToString());
            return sb.ToString();
        }
    }
    return "";
}

或者您可以继续使用 StreamWriter,但请正确使用:

// tell it you need UTF8 without BOM
using (var swEncrypt = new StreamWriter(csEncrypt, new UTF8Encoding(false)))
{
    // then write string there, not bytes
    swEncrypt.Write(token);
}