我一直在使用我使用SHA1编写的旧代码来处理SHA256或SHA512. 我是密码学领域的新手,我想知道有没有人能告诉我,为什么我在ENCRYPT方法中的输入值通过DECRYPT方法返回时会丢失1-5个字节.

我可以用短字符串运行这些方法,没有问题,但显然不会更大,例如16个字节+.

有问题的两种方法是:

    public string Decrypt(string data, EncryptionValue eV, string passPhrase)
{
    byte[] bytes = Encoding.ASCII.GetBytes(eV.InitVector);
    byte[] rgbSalt = Encoding.ASCII.GetBytes(eV.SaltValue);
    byte[] buffer = Convert.FromBase64String(data);
    byte[] rgbKey = new Rfc2898DeriveBytes(passPhrase, rgbSalt, eV.PassIterations, hash).GetBytes(eV.KeySize / 8);

    var managed = Aes.Create("AesManaged");
    managed.Padding = PaddingMode.PKCS7;
    managed.Mode = CipherMode.CBC;
    ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
    MemoryStream stream = new(buffer);
    CryptoStream stream2 = new(stream, transform, CryptoStreamMode.Read);
    byte[] buffer5 = new byte[buffer.Length];
    int count = stream2.Read(buffer5, 0, buffer5.Length);
    stream.Close();
    stream2.Close();
    return Encoding.UTF8.GetString(buffer5, 0, count);
}
public string Encrypt(string data, EncryptionValue eV, string passPhrase)
{
    byte[] bytes = Encoding.ASCII.GetBytes(eV.InitVector);
    byte[] rgbSalt = Encoding.ASCII.GetBytes(eV.SaltValue);
    byte[] buffer = Encoding.UTF8.GetBytes(data);
    byte[] rgbKey = new Rfc2898DeriveBytes(passPhrase, rgbSalt, eV.PassIterations, hash).GetBytes(eV.KeySize / 8);

    var managed = Aes.Create("AesManaged");
    managed.Padding = PaddingMode.PKCS7;
    
    managed.Mode = CipherMode.CBC;
    ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
    MemoryStream stream = new();
    CryptoStream stream2 = new(stream, transform, CryptoStreamMode.Write);
    stream2.Write(buffer, 0, buffer.Length);
    stream2.FlushFinalBlock();
    byte[] inArray = stream.ToArray();
    stream.Close();
    stream2.Close();
    return Convert.ToBase64String(inArray);
}

加密值类别包含随机化的值,这不是这里的问题,但我还是要在这里发布它:

 public int PassIterations { get; set; }
 public int KeySize { get; set; }
 public string InitVector { get; set; }
 public string SaltValue { get; set; }

在我的所有测试中,迭代次数都在1000次左右.我已经做了越来越多的测试. KeySize始终为256或128, InitVector="~1B2C3D4e5F6g7H8";

我运行了一些测试,正如我所说的,较小的字符串是可以的,但我希望它能够存储至少120个字节.我以为这能行得通?

推荐答案

问题实际上出在Decrypt方法中的stream2.Read调用上.流的Read方法不能保证它将读取buffer5.Length个字节.它很有可能在一次调用中读取更少的字节.这是完全有效的行为,并且取决于特定的CryptoStream实现(取决于运行您的程序的.NET版本和/或主机操作系统)或配置在读取操作方面的行为的精确度.

您需要判断stream2.Read已经读取了多少字节,如果它读取的字节数少于 Buffer5长度字节,则需要一次又一次地调用stream2.Read,直到Buffer5完全填满,或者stream2.Read通过返回0来指示流结束.比实现此方法更简单的解决方案可能是使用BinaryReader.ReadBytes方法,该方法将读取与请求的字节一样多的字节(当然,除非在读取所有请求的字节之前到达流的末尾).

请参阅揭示此行为的dotnetfiddle示例:https://dotnetfiddle.net/ATLVTm
请注意,我添加的"廉价"Console.WriteLine插装使此行为可见.


P.S.: Just a nitpicky side note unrelated to your problem: Instead of manually closing all those streams, it would be better to use the `using` statement in conjunction with the streams (because streams are `IDisposable`'s) which would automatically take care of closing/disposing the streams when the respective stream variable goes out of scope, even when exceptions are being thrown by your code.

Csharp相关问答推荐

在Linq中调用需要limit和offset的方法''''

System.Text.Json数据化的C#类映射

ITypeLib2.GetLibStatistics()在C#中总是抛出AccessViolationException

当通过Google的Gmail Api发送邮件时,签名会产生dkim = neutral(正文散列未验证)'

在实时数据库中匹配两个玩家的问题

用C#调用由缓冲区指针参数组成的C API

如何使用新的Microsoft.IdentityModel.JsonWebToken创建JwtSecurityToken?

由于POST中的应用程序/JWT,出现不支持的内容类型异常

当索引和外键是不同的数据类型时,如何设置导航属性?

在C#中,非静态接口方法的抽象和虚拟是冗余的吗?

如何将MemberInitExpression添加到绑定中其他Lambda MemberInitExpression

DateTime ToString()未以指定格式打印

依赖项注入、工厂方法和处置困境

如何管理Azure认证客户端响应和证书 fingerprint

如何在绑定到数据库的datagridview中向上或向下移动行

我可以阻止类型上的Object.ToString()吗?

我什么时候不应该在Dispose中调用EgSuppressFinalize(This)?

如何在C#中用Serilog记录类路径、方法名和行编号

无法通过服务控制台启动.NET Core 6.0服务(错误1053)

C#:我需要根据换行符拆分字符串,而不是在字符串中用双引号分隔换行符