我正在try 使用Bouly Castle C#库来加密和解密一个字符串+通过使用SHA256对其进行散列来将秘密填充到236,但我失败了.更具体地说,我无法在加密后恢复原始文本,然后解密.我很感激任何帮助或提示.

static string Process(bool encrypt, string keyString, string input)
{
    // We undo replacing of the 2 characters from Base64 which where not URL safe 
    if (!encrypt)
    {
        input = input.Replace("-", "+").Replace("_", "/");
    }

    // Get UTF8 byte array of input string for encryption
    var inputBytes = Encoding.UTF8.GetBytes(input);

    // Again, get UTF8 byte array of key for use in encryption
    var keyBytes = Encoding.UTF8.GetBytes(keyString);

    // Padding the key to 256
    var myHash = new Sha256Digest();
    myHash.BlockUpdate(keyBytes, 0, keyBytes.Length);
    var keyBytesPadded = new byte[myHash.GetDigestSize()];
    myHash.DoFinal(keyBytesPadded, 0);

    // Initialize AES CTR (counter) mode cipher from the BouncyCastle cryptography library
    var cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");

    cipher.Init(true, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", keyBytesPadded), new byte[16]));

    // As this is a stream cipher, you can process bytes chunk by chunk until complete, then close with DoFinal.
    // In our case we don't need a stream, so we simply call DoFinal() to encrypt the entire input at once.
    var encryptedBytes = cipher.DoFinal(inputBytes);

    // The encryption is complete, however we still need to get the encrypted byte array into a useful form for passing as a URL parameter
    // First, we convert the encrypted byte array to a Base64 string to make it use ASCII characters
    var base64EncryptedOutputString = Convert.ToBase64String(encryptedBytes);

    // Lastly, we replace the 2 characters from Base64 which are not URL safe ( + and / ) with ( - and _ ) as recommended in IETF RFC4648
    var urlEncodedBase64EncryptedOutputString = base64EncryptedOutputString;

    if (encrypt)
    {
        urlEncodedBase64EncryptedOutputString =
            urlEncodedBase64EncryptedOutputString.Replace("+", "-").Replace("/", "_");
    }

    // This final string is now safe to be passed around, into our web service by URL, etc.
    return urlEncodedBase64EncryptedOutputString;
}

static string Encrypt(string keyString, string input)
{
    return Process(true, keyString, input);
}

static string Decrypt(string keyString, string input)
{
    return Process(false, keyString, input);
}

const string key = "supersecret";
const string message = "hello world!";

Decrypt(key, Encrypt(key, message)).ShouldBe(message); // False!

Repository

推荐答案

该问题是由编码和解码不一致引起的.

通常在加密期间,明文是UTF-8编码的,密文是Base64(URL)编码的.在解密过程中,反过来,密文被Base64(URL)解码,解密的数据被UTF-8解码.

In the posted code, the Process() method is used for both encryption and decryption, and here the input and output encoding is inconsistently implemented:
The input data is always UTF-8 encoded, which is incorrect in the case of the ciphertext. The ciphertext must be Base64(url) decoded.
The output data is always Base64(url) encoded, which is incorrect in the case of the decrypted data. The decrypted data must be UTF-8 decoded.

由于输入和输出编码取决于加密还是解密,因此在Encrypt()/Decrypt()中实现编码/解码是最有意义的.它也可以在Process()中实现,但这将导致相应的if/Else语句.

Encrypt()/Decrypt()中的编解码实现示例:

static byte[] Process(bool encrypt, string keyMaterial, byte[] input)
{
    // Keyderivation via SHA256
    var keyMaterialBytes = Encoding.UTF8.GetBytes(keyMaterial);
    var digest = new Sha256Digest();
    digest.BlockUpdate(keyMaterialBytes, 0, keyMaterialBytes.Length);
    var keyBytes = new byte[digest.GetDigestSize()];
    digest.DoFinal(keyBytes, 0);

    // Encryption/Decryption with AES-CTR using a static IV
    var cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");
    cipher.Init(encrypt, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", keyBytes), new byte[16]));
    return cipher.DoFinal(input);
}

static string Encrypt(string keyMaterial, string plaintext)
{
    var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);                             // UTF-8 encode
    var ciphertextBytes = Process(true, keyMaterial, plaintextBytes);
    return Convert.ToBase64String(ciphertextBytes).Replace("+", "-").Replace("/", "_"); // Base64url encode
}

static string Decrypt(string keyMaterial, string ciphertext)
{
    var ciphertextBytes = Convert.FromBase64String(ciphertext.Replace("-", "+").Replace("_", "/")); // Base64url decode
    var decryptedBytes = Process(false, keyMaterial, ciphertextBytes);
    return Encoding.UTF8.GetString(decryptedBytes);                                                 // UTF-8 decode
}

请注意,该代码存在漏洞:

  • 使用静态静脉输液.与CTR一起,这一点尤其严重,第here条.相反,应该在加密过程中生成随机IV,并将其与密文一起传递给解密方,通常是串联的(IV不是秘密).
  • 使用快速摘要作为密钥派生.相反,应该应用可靠的密钥派生函数,例如Argon2或至少PBKDF2.

Base64url通常不带填充字符.如果你对此感兴趣,第here条.

Csharp相关问答推荐

有没有方法让ASP.NET Core模型绑定器使用私有设置器来设置属性?

C#自定义字典与JSON(de—)serialize

解析需要HttpClient和字符串的服务

不仅仅是一个简单的自定义按钮

. NET JSON属性自定义所需逻辑

使用Audit.EntityFramework,我如何将外键的值设置为相关实体上的属性?

如何使datagridview的列具有响应性,以便不是所有列都更改其宽度

如何在C#中使用Postman中的本地IP向本地主机上运行的本地API发出请求

在ASP.NET Core Web API项目中通过中间件修改`Request.Path`不会更改使用的控制器/操作

C#Null判断处理失败

自定义列表按字符串的部分排序

升级后发出SWITCH语句

WPF DataGrid文件名列,允许直接输入文本或通过对话框按键浏览

如何实现有条件的自定义Json转换器隐藏属性

如何在.NET MAUI中最大化GraphicsView的大小?

如何在发布NuGet包之前设置命名空间?

EF Core:如何对关系属性进行建模?

如何在Polly重试策略成功之前将HttpClient请求排队?

如何在特定时间间隔运行多个后台任务?

异步等待,如何在Windows窗体中使用它们?