c# - 需要在我的 c# web api 中修复错误的加密方法
问题描述
我需要更正用 C# 编写的加密方法。
首先,一点背景知识:我负责一个现有的 Web 应用程序,它有一个 ecma6 / html 前端和 c# web api .net 标准 4.6 后端。
它与不同的客户进行了许多集成,以进行用户识别。一些集成只需导航到客户 URL 以在客户的基础架构上执行登录过程,然后返回应用程序,并在 URL 的查询字符串中使用加密的用户令牌。
此令牌使用 AES256 加密进行加密。
后端正确解密了令牌,但是当我尝试使用加密例程构建单元测试时,我发现出了点问题。当我加密然后解密消息时,解密例程会引发以下错误:
Unhandled Exception:
System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.
输入消息是“key1=value1;key2=value2”(不带引号)
我得到的加密消息是 NzcrOTc3Kzk3Nys5NzcrOTc3Kzk3Nys5NzcrOVpsVHZ2NzF3NzcrOUZ6UVlRZ3Z2djcxSVlPKy92U0V6NzcrOVNqZFY3Nys5VHpBZA==
我需要更正加密方法中的实现错误。解密方法的实现显示了预期的行为,您会注意到对加密字符串进行了双重 Base64 解码:这是给定的,因为我们与客户在 PERL 中完成的已经开发的加密例程集成在一起,我们检测到该例程加倍编码。
我检查了操作顺序,发现加密和解密不匹配,我无法检测到不一致,所以需要寻求帮助。
我为此测试合成的代码是:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class MainClass {
public static void Main (string[] args) {
var secretKey = "This is my secret key";
var secretIV = "This is my secret iv";
var originalMessage = "key1=value1;key2=value2";
var userToken = Cryptography.EncryptAES256CBCBase64x2(originalMessage, secretKey, secretIV);
Console.WriteLine(userToken);
var unencryptedToken = Cryptography.DecryptAES256CBCBase64x2(userToken, secretKey, secretIV);
if (originalMessage == unencryptedToken)
Console.WriteLine("All fine!");
else
Console.WriteLine("Error!");
}
}
public static class Cryptography
{
public static string DecryptAES256CBCBase64x2(string base64EncryptedString, string secretKey, string secretIV)
{
base64EncryptedString = SaveBase64String(base64EncryptedString);
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var ivBytes = Encoding.UTF8.GetBytes(secretIV);
var hash = SHA256.Create();
var keyHash = hash.ComputeHash(keyBytes);
Array.Resize(ref keyHash, 32);
var keyHashString = string.Empty;
foreach (byte x in keyHash)
keyHashString += string.Format("{0:x2}", x);
keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
var ivHash = hash.ComputeHash(ivBytes);
Array.Resize(ref ivHash, 16);
var ivHashString = string.Empty;
foreach (byte x in ivHash)
ivHashString += string.Format("{0:x2}", x);
ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.Mode = CipherMode.CBC;
rijAlg.Key = keyHash;
rijAlg.IV = ivHash;
var encryptedBytes =
Convert.FromBase64String(
Encoding.UTF8.GetString(
Convert.FromBase64String(base64EncryptedString)));
// Create a decryptor to perform the stream transform.
var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(encryptedBytes))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
return srDecrypt.ReadToEnd();
}
}
}
}
}
public static string EncryptAES256CBCBase64x2(string baseString, string secretKey, string secretIV)
{
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var ivBytes = Encoding.UTF8.GetBytes(secretIV);
var hash = SHA256.Create();
var keyHash = hash.ComputeHash(keyBytes);
Array.Resize(ref keyHash, 32);
var keyHashString = string.Empty;
foreach (byte x in keyHash)
keyHashString += string.Format("{0:x2}", x);
keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
var ivHash = hash.ComputeHash(ivBytes);
Array.Resize(ref ivHash, 16);
var ivHashString = string.Empty;
foreach (byte x in ivHash)
ivHashString += string.Format("{0:x2}", x);
ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.Mode = CipherMode.CBC;
rijAlg.Key = keyHash;
rijAlg.IV = ivHash;
var encryptedBytes = Encoding.UTF8.GetBytes(baseString);
// Create a encryptor to perform the stream transform.
var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (var msEncrypt = new MemoryStream(encryptedBytes))
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Read))
{
using (var srEncrypt = new StreamReader(csEncrypt))
{
// Read the encrypted bytes from the encrypting stream
// and place them in a string.
var result = srEncrypt.ReadToEnd();
return Convert.ToBase64String(
Encoding.UTF8.GetBytes(
Convert.ToBase64String(
Encoding.UTF8.GetBytes(result))));
}
}
}
}
}
public static string SaveBase64String(string data)
{
data = data.Replace("-", "+").Replace("_", "/");
var mod = data.Length % 4;
if (mod > 2)
mod = 1;
return data + string.Empty.PadRight(mod, '=');
}
}
在以下链接中,您可以尝试在线示例:https ://repl.it/@ormasoftchile/Test-encrypt-decrypt
谢谢大家。
解决方案
在当前代码中,密文存储在字符串 ( StreamReader.ReadToEnd
) 中,这通常不起作用,因为数据因此而损坏。相反,密文应该存储在字节数组中,如果需要,可以进行 Base64 编码。
解决问题
删除行:
var encryptedBytes = Encoding.UTF8.GetBytes(baseString);
并将整个 MemoryStream 块替换为:
using (var msEncrypt = new MemoryStream()) { using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { swEncrypt.Write(baseString); } var encryptedBytes = msEncrypt.ToArray(); return Convert.ToBase64String(Encoding.UTF8.GetBytes(Convert.ToBase64String(encryptedBytes))); } }
另一点是双重 Base64 编码/解码。这没有任何意义,而且只是多余的并且会降低性能。如果可能的话,这应该改变。
推荐阅读
- c# - 挪威语特殊字符未正确显示
- android - 在成功构建之前设计编辑器不可用 - 构建失败
- html - 在图像中放置文本
- android - Android Pie中的数据库导入问题?
- jenkins - 如何在 Jenkins Windows 2012 r2 中实现 https
- amazon-web-services - AWS API 通用模板 - 必须从通用模板中删除破折号
- java - 使用 ControlsFx CheckComboBox 过滤 TableView
- wordpress - 仅显示 Wordpress 中每个类别的最新帖子
- python - 无法在 Python 3.6.6 上的 Open CV 3.4.1 上运行跟踪
- c# - C#如何互相访问表单对象