指定的填充模式不适用于此算法-.net Core

3

当我将代码从.NET 4.5转换为.NET Core 2时,出现了以下错误消息。代码完全相同。我已经查看了一些帖子,但没有一个解决了这个错误。我正在使用RijndaelManaged加密。

 Specified padding mode is not valid for this algorithm.

  at Internal.Cryptography.UniversalCryptoDecryptor.DepadBlock(Byte[] block, Int32 offset, Int32 count)
   at Internal.Cryptography.UniversalCryptoDecryptor.UncheckedTransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at Internal.Cryptography.UniversalCryptoTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
   at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
   at System.IO.Stream.Close()
   at System.IO.StreamReader.Dispose(Boolean disposing)
   at System.IO.TextReader.Dispose()

以下是我正在使用的代码。我在retval = srDecrypt.ReadToEnd();处遇到错误。
 public static class EncryptionExtension
{

    #region "Enumerations"

    public enum EncryptionAlgorithms
    {

        Rijndael = 0

    }


    public enum CryptMethod
    {
        Encrypt = 0,
        Decrypt = 1
    }

    #endregion

    #region "Private Attributes"

    private static byte[] CRYPT_SALT = {
        99,
        115,
        120,
        76,
        105,
        103,
        105,
        116
    };
    private static byte[] IV_8 = new byte[] {
        2,
        63,
        9,
        36,
        235,
        174,
        78,
        12
    };
    private static byte[] IV_16 = new byte[] {
        15,
        199,
        56,
        77,
        244,
        126,
        107,
        239,
        9,
        10,
        88,
        72,
        24,
        202,
        31,
        108
    };
    private static byte[] IV_24 = new byte[] {
        37,
        28,
        19,
        44,
        25,
        170,
        122,
        25,
        25,
        57,
        127,
        5,
        22,
        1,
        66,
        65,
        14,
        155,
        224,
        64,
        9,
        77,
        18,
        251
    };
    private static byte[] IV_32 = new byte[] {
        133,
        206,
        56,
        64,
        110,
        158,
        132,
        22,
        99,
        190,
        35,
        129,
        101,
        49,
        204,
        248,
        251,
        243,
        13,
        194,
        160,
        195,
        89,
        152,
        149,
        227,
        245,
        5,
        218,
        86,
        161,
        124
        #endregion
    };

    #region "String Encryption"

    public static string EncryptString(EncryptionAlgorithms Method, string Value, string Key)
    {
        return CryptString(CryptMethod.Encrypt, Method, Value, Key);
    }

    public static string DecryptString(EncryptionAlgorithms Method, string Value,  string Key)
    {
        return CryptString(CryptMethod.Decrypt, Method, Value, Key);
    }

    public static string CryptString(CryptMethod Method, EncryptionAlgorithms Algorithm, string Value, string Key)
    {
        // Check arguments.    
        if (Value == null || Value.Length <= 0)
        {
            throw new ArgumentNullException("Data can not be empty");
        }
        if (Key == null || Key.Length <= 0)
        {
            throw new ArgumentNullException("Key can not be empty");
        }

        SymmetricAlgorithm provider = null;
        string retval = null;

        // Declare the stream used to encrypt to an in memory array of bytes.    
        MemoryStream msCrypt = null;
        ICryptoTransform ICrypt = null;

        try
        {
            // Create a Provider object    
            switch (Algorithm)
            {
                case EncryptionAlgorithms.Rijndael:
                    provider = new RijndaelManaged();
                    break;
            }

            provider.KeySize = provider.LegalKeySizes[0].MaxSize;
            provider.BlockSize = provider.LegalBlockSizes[0].MaxSize;

            provider.Key = DerivePassword(Key, provider.LegalKeySizes[0].MaxSize / 8);

            switch (provider.BlockSize / 8)
            {
                case 8:
                    provider.IV = IV_8;
                    break;
                case 16:
                    provider.IV = IV_16;
                    break;
                case 24:
                    provider.IV = IV_24;
                    break;
                case 32:
                    provider.IV = IV_32;
                    break;
            }

            if (Method == CryptMethod.Encrypt)
            {
                ////encrypt value    

                //// Create a encryptor to perform the stream transform.    
                //ICrypt = provider.CreateEncryptor(provider.Key, provider.IV);

                //// Create the streams used for encryption/decryption    
                //msCrypt = new MemoryStream();

                //using (CryptoStream csEncrypt = new CryptoStream(msCrypt, ICrypt, CryptoStreamMode.Write))
                //{
                //    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                //    {
                //        //Write all data to the stream.    
                //        swEncrypt.Write(Value);
                //    }
                //}
            }
            else
            {
                //decrypt value    

                //convert the ciphered text into a byte array    
                byte[] cipherBytes = null;
                cipherBytes = System.Convert.FromBase64String(Value);

                // Create a deccryptor to perform the stream transform.    
                ICrypt = provider.CreateDecryptor(provider.Key, provider.IV);

                // Create the streams used for decryption.    
                msCrypt = new MemoryStream(cipherBytes);

                using (CryptoStream csDecrypt = new CryptoStream(msCrypt, ICrypt, CryptoStreamMode.Write))
                {


                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {

                        //Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                          retval = srDecrypt.ReadToEnd();

                    }
                }

            }
        }
        catch (Exception ex)
        {
            //throw new AceExplorerException(ex.Message + "  " + ex.StackTrace + " " + ex.TargetSite.ToString() + " " + ex.Source, ex.InnerException);
              throw new Exception(ex.Message + "  " + ex.StackTrace + " " + ex.TargetSite.ToString() + " " + ex.Source, ex.InnerException);

        }
        finally
        {
            // Clear the Provider object.    
            if ((provider != null))
            {
                provider.Clear();
            }
        }

        if (Method == CryptMethod.Encrypt)
        {
            // Return the encrypted bytes from the memory stream.    
            return System.Convert.ToBase64String(msCrypt.ToArray());
        }
        else
        {
            // Return the unencrypted text    
            return retval;
        }

    }

    #endregion

    #region "Private Utility Functions"

    private static byte[] DerivePassword(string Password, int Length)
    {
        Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(Password, CRYPT_SALT, 5);
        return derivedBytes.GetBytes(Length);
    }

    #endregion
}

要运行它,您可以执行以下操作:

var decryptedstring = EncryptionExtension.CryptString(EncryptionExtension.CryptMethod.Decrypt, EncryptionExtension.EncryptionAlgorithms.Rijndael, "[Encrypted String]", "[key]");

更新: 我已添加了完整的类。抱歉我没有看到被封锁的部分。

更新2: 我将PaddingMode更改为None。我不再看到错误。但现在返回值是:�s)���j�U�#V�İ��H?X�

更新3: 在4.5上调试代码时,我有: - PKCS7中的Padding - BlockSize = 256 vs Core中的128 我尝试了Jimi的代码,但得到了奇怪的字符:寛Щ�챫蔧⽢쉈⩭」啌斪ᆈ锚ય杄䕳 我尝试修改它,使用以下内容:Convert.ToBase64String(DecodedText); 我没有奇怪的字符,但结果与我预期的不同。

期望的结果:

密钥: DNACTSACEENGINE

字符串: IdIFR+PP5yDggqgSlB0KfcNTG+DkRuRbPfeljJeGm+c=

结果: !vbqZgZKbu4?8

更新: .Net Core不支持块大小为256

提前感谢。任何帮助都会受到赞赏。


1
那么实际上会发生什么?完全相同的代码在.NET上可以工作,但在.NET Core上却不能?你能发布一个[mcve]吗? - Camilo Terevinto
1
我认为需要改变的第一件事是provider.Key = DerivePassword(...);。将其更改为PasswordDeriveBytes secret = new PasswordDeriveBytes(Key, Encoding.ASCII.GetBytes((provider.LegalKeySizes[0].MaxSize / 8).ToString()));。在这里,StreamReader是无用的,除非您使用一个编码(可能是Unicode),无论如何,与加密使用相同的编码。我会获取csDecrypt流,将其转换为byte[],然后使用Encoding.Unicode.GetString()进行最终转换。我不知道switch (provider.BlockSize / 8)如何工作。 - Jimi
1
什么是填充模式?你的代码没有设置它。 - glenebob
@glenebob 尝试设置添加模式,但没有成功。 - H20rider
@Jimi 谢谢。我已经发布了DerivePassword的完整代码。我该如何将csDecrypt流式传输到字符串中? - H20rider
显示剩余3条评论
1个回答

3

我已经修复了那个无法产生一致结果的部分。

但是,由于你的代码中加密方法被注释掉了,我不确定加密后的输入值是否与此处呈现的完全相同,或者它来自不同的来源和/或不同的加密方法。

我将加密和解密方法都使用的MemoryStream分配的字节数组与原始值的Unicode编码/解码进行了整合。
当然,也可以使用其他编码。(也测试过UTF8,应该是默认的)。
字符串像往常一样进行了Base64编码/解码。

我在这里只发布包含在#region "String Encryption"块中的代码部分。
其余的代码都没有改动。

测试环境: Visual Studio pro 15.8.0
.Net FrameWork: Core 2.1
C# 6.0 和 C# 7.3

加密/解密方法的调用方式如下:

string encryptedstring = EncryptionExtension.CryptString(
        EncryptionExtension.CryptMethod.Encrypt, EncryptionExtension.EncryptionAlgorithms.Rijndael, 
            "Some text to encrypt, more text to encrypt", "SomeKey");
string decryptedstring = EncryptionExtension.CryptString(
        EncryptionExtension.CryptMethod.Decrypt, EncryptionExtension.EncryptionAlgorithms.Rijndael, 
            encryptedstring, "SomeKey");

#region "String Encryption"

public static string EncryptString(EncryptionAlgorithms Method, string Value, string Key)
{
    return CryptString(CryptMethod.Encrypt, Method, Value, Key);
}

public static string DecryptString(EncryptionAlgorithms Method, string Value, string Key)
{
    return CryptString(CryptMethod.Decrypt, Method, Value, Key);
}

public static string CryptString(CryptMethod Method, EncryptionAlgorithms Algorithm, string Value, string Key)
{
    if (Value == null || Value.Length <= 0)
    {
        throw new ArgumentNullException("Data can not be empty");
    }
    if (Key == null || Key.Length <= 0)
    {
        throw new ArgumentNullException("Key can not be empty");
    }

    SymmetricAlgorithm provider = null;

    try
    {
        switch (Algorithm)
        {
            case EncryptionAlgorithms.Rijndael:
                provider = new RijndaelManaged();
                break;
        }
        provider.KeySize = provider.LegalKeySizes[0].MaxSize;
        provider.BlockSize = provider.LegalBlockSizes[0].MaxSize;
        provider.Key = DerivePassword(Key, provider.LegalKeySizes[0].MaxSize / 8);

        switch (provider.BlockSize / 8)
        {
            case 8:
                provider.IV = IV_8;
                break;
            case 16:
                provider.IV = IV_16;
                break;
            case 24:
                provider.IV = IV_24;
                break;
            case 32:
                provider.IV = IV_32;
                break;
        }

        if (Method == CryptMethod.Encrypt)
        {
            byte[] encodedText = Encoding.Unicode.GetBytes(Value);

            // Create the streams used for encryption/decryption    
            using (ICryptoTransform encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
            using (var msCrypt = new MemoryStream())
            using (var csEncrypt = new CryptoStream(msCrypt, encryptor, CryptoStreamMode.Write))
            {
                csEncrypt.Write(encodedText, 0, encodedText.Length);
                csEncrypt.FlushFinalBlock();
                return Convert.ToBase64String(msCrypt.ToArray());
            }
        }
        else
        {
            byte[] cipherBytes = Convert.FromBase64String(Value);

            // Create the streams used for decryption.    
            using (ICryptoTransform decryptor = provider.CreateDecryptor(provider.Key, provider.IV))
            using (var msCrypt = new MemoryStream(cipherBytes))
            using (var csDecrypt = new CryptoStream(msCrypt, decryptor, CryptoStreamMode.Read))
            {
                byte[] decodedText = new byte[cipherBytes.Length];
                int decryptedCount = csDecrypt.Read(decodedText, 0, decodedText.Length);
                return Encoding.Unicode.GetString(decodedText, 0, decryptedCount);
            }
        }
    }
    catch (Exception ex)
    {
        //throw new AceExplorerException(ex.Message + "  " + ex.StackTrace + " " + ex.TargetSite.ToString() + " " + ex.Source, ex.InnerException);
        throw new Exception(ex.Message + "  " + ex.StackTrace + " " + ex.TargetSite.ToString() + " " + ex.Source, ex.InnerException);
    }
    finally
    {
        // Clear the Provider object.    
        provider?.Clear();
    }
}
#endregion

private static byte[] DerivePassword(string password, int length)
{
    Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(password, CRYPT_SALT, 1000);
    return derivedBytes.GetBytes(length);
}

对我没用。我得到了如上所述的奇怪字符。此外,我添加了 provider.Padding = PaddingMode.None; 来摆脱第一个错误。 - H20rider
我已经完成了。这是预期的结果。 密钥:DNACTSACEENGINE 字符串:IdIFR+PP5yDggqgSlB0KfcNTG+DkRuRbPfeljJeGm+c= 返回值:!vbqZgZKbu4?8 当我运行上述代码时,出现了填充错误。当我更改填充方式时,它会返回一些奇怪的文本值。 同时,只需要取消注释加密部分即可。 - H20rider
我完全复制了代码。你试过使用我用的值吗? - H20rider
你不能这样做。你必须使用我在这里发布的代码对原始(明文)字符串进行加密/解密。在你的评论中,你试图解密一个Base64编码的字符串。如果你有一个加密字符串的代码库需要解密,你必须说明使用了哪些参数。否则解密将无法工作。你是否在switch (Algorithm)中更改了填充模式,添加了provider.Padding = [Some Padding Mode]?(请注意,如果未指定填充,则PKCS7是默认值)。加密字符串是使用这些参数创建的吗? - Jimi
另外,如果您在原始方法中没有使用编码,请将我发布的代码中的编码从“Unicode”更改为“UTF8”。不要混用方法:或者您使用我发布的加密/解密,或者使用您自己的。看看是否使用我的加密和解密!vbqZgZKbu4?8并使用任何填充进行加密和解密后,您能得到一致的结果。 - Jimi
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接