使用.NET中的AES解密时缺少字节的最后一半
我正在尝试使用用户提供的密码在.NET中实现一些数据加密。据我了解,我使用Symetric密钥对文件进行加密,并使用用户生成的另一个密钥对此密钥进行加密。这意味着密码更改不需要更改数据,而只是对加密键的更新。
测试AES函数时,我可以加密我的256位键,但是当解密时,我只从.NET中获得了第一个16个字节:
public static byte[] Salt = new byte[64];
public static byte[] IV = new byte[16];
public static string Password1 = "PWD";
public static byte[] Key = new byte[32];
static void Main(string[] args)
{
Salt = RandomNumberGenerator.GetBytes(64);
IV = RandomNumberGenerator.GetBytes(16);
Key = RandomNumberGenerator.GetBytes(32);
var pwdK1 = RandomNumberGenerator.GetBytes(32);
byte[] aKey1 = new byte[32];
byte[] bKey1 = new byte[32];
using (Aes aes = Aes.Create())
{
aes.Mode = CipherMode.CBC;
aes.Key = pwdK1; //use key generated by user pwd
aes.IV = IV;
var str = new MemoryStream(Key);
using (var crypStr = new CryptoStream(str, aes.CreateEncryptor(), CryptoStreamMode.Read))
{
int i = crypStr.Read(aKey1, 0, 32);
}
}
using (Aes aes = Aes.Create())
{
aes.Mode = CipherMode.CBC;
aes.Key = pwdK1; //use key generated by user pwd
aes.IV = IV;
var str = new MemoryStream(aKey1);
using (var crypStr = new CryptoStream(str, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
int i = crypStr.Read(bKey1, 0, 32);
var p = bKey1.ToArray();
}
}
//we should have Key in p/bKey1, but we only have the first 16 bytes of Key.
}
shere,pwdk1
实际上是使用第三方生成的。 Argon2
库,为此文章修改了代码。
使用的密钥和IV是相同的,模式是相同的,但是在解密阶段中读取解密的键时,我只看到我在键
中看到的第一个16个字节 代码> p 。对于第一个crypstr.read
,我将返回完整的32个字节,但是解密读取仅返回i中的16个字节。其余的16个字节都是0。
有什么想法我可能做错了什么?
I'm attempting to implement some encryption of data in .NET with a user supplied password. As I understand this, I encrypt the file with a symetric key, and encrypt this key with another key that is generated by the user. This means that a password change does not require a change to the data, just an update to the encrypted key.
When testing out the AES functions, I can encrypt my 256 bit key, but when decrypting I only get the first 16 bytes back from .NET:
public static byte[] Salt = new byte[64];
public static byte[] IV = new byte[16];
public static string Password1 = "PWD";
public static byte[] Key = new byte[32];
static void Main(string[] args)
{
Salt = RandomNumberGenerator.GetBytes(64);
IV = RandomNumberGenerator.GetBytes(16);
Key = RandomNumberGenerator.GetBytes(32);
var pwdK1 = RandomNumberGenerator.GetBytes(32);
byte[] aKey1 = new byte[32];
byte[] bKey1 = new byte[32];
using (Aes aes = Aes.Create())
{
aes.Mode = CipherMode.CBC;
aes.Key = pwdK1; //use key generated by user pwd
aes.IV = IV;
var str = new MemoryStream(Key);
using (var crypStr = new CryptoStream(str, aes.CreateEncryptor(), CryptoStreamMode.Read))
{
int i = crypStr.Read(aKey1, 0, 32);
}
}
using (Aes aes = Aes.Create())
{
aes.Mode = CipherMode.CBC;
aes.Key = pwdK1; //use key generated by user pwd
aes.IV = IV;
var str = new MemoryStream(aKey1);
using (var crypStr = new CryptoStream(str, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
int i = crypStr.Read(bKey1, 0, 32);
var p = bKey1.ToArray();
}
}
//we should have Key in p/bKey1, but we only have the first 16 bytes of Key.
}
Here, pwdK1
is actually generated using a 3rd party Argon2
library, code modified for this post.
The key and IV used are the same, the mode is the same, but when reading out the decrypted key in the decrypt stage, I only see the first 16 bytes that I see in Key
stored in variable p
. For the first crypStr.Read
I get a full 32 bytes returned, but the decrypt Read returns only 16 bytes in i. The remaining 16 bytes are all 0.
Any ideas what I could be doing wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
代码中有两个错误:
在.NET 6中,
read()
的行为已更改。在通过的缓冲区完全填充或到达流的末端之前,新行为不再保证阅读。 和在这里。因此,必须实现读取循环(如第一个评论中已经提出的那样)。另外,可以使用copyto()
。关于简短的明文,也值得考虑完全不使用流,而是
transformfinalblock()
,s。 在这里。这样可以节省与流时与流相关的开销,并大大缩短代码。当然,对于更长的明文,应再次应用流模式。这是比较的选项(用于加密)。请注意,如已发布的代码,
键
是要加密的明文:read() - 循环:
copyto():
transformfinalblock():
AES实现应用PKCS#7填充,即在32个字节的全块(16个字节)(带有AES的16个字节)的情况下附加了PKCS#7填充,因此附加了Ciphertext的缓冲区需要48个字节。在解密期间,填充物再次被删除,因此解密数据的缓冲区可能仍然对应于32个字节的明文大小。
由于纯文本满足大小标准,因此,根据该标准,明文的尺寸必须与块的整数倍数相对应,因此也值得考虑是否应该禁用填充物。那么密文的缓冲区也可能是32个字节大。如果未满足尺寸标准,则当然必须使用填充。
可以使用
aes.padding = paddingmode.none;
。。禁用填充
完整的代码无需使用
transformfinalblock()
:编辑:,如注释中所述,因为.NET 6也有
encryptcbc()
anddecryptcbc()
,这使其更加紧凑:就像在发布的代码中一样,
pwdk1
是加密密钥键
是要加密的纯文本。There are two bugs in the code:
In .NET 6, the behavior of
Read()
has changed. The new behavior no longer guarantees reading until the passed buffer is completely filled or the end of the stream is reached, s. here and here. Therefore, a read loop must be implemented (as already suggested in the first comment). Alternatively,CopyTo()
can be used.With regard to the short plaintext, it is also worth considering not using streams at all, but
TransformFinalBlock()
, s. here. This saves the overhead associated with streams at runtime and also shortens the code significantly. For longer plaintexts, of course, the stream pattern should be applied again.Here are the options in comparison (for encryption). Note that as in the posted code,
Key
is the plaintext to be encrypted:Read()-loop:
CopyTo():
TransformFinalBlock():
The Aes implementation applies PKCS#7 padding by default, i.e. in case of a plaintext of 32 bytes a full block (16 bytes with AES) is appended, so that the buffer for the ciphertext requires a size of 48 bytes. During decryption the padding is removed again, so that the buffer for the decrypted data may still correspond to the plaintext size of 32 bytes.
Since the plaintext fulfills the size criterion, according to which the size of the plaintext must correspond to an integer multiple of the blocksize, it's also worth considering whether padding should be disabled. Then the buffer for the ciphertext may also be 32 bytes large. If the size criterion is not met, padding must of course be applied.
Padding can be disabled with
aes.Padding = PaddingMode.None;
.Full code without padding using
TransformFinalBlock()
:Edit: As mentioned in the comment, since .NET 6 there are also
EncryptCbc()
andDecryptCbc()
, which makes it even a little bit more compact:Like in the posted code,
pwdK1
is the encryption key andKey
is the plaintext to be encrypted.