简体   繁体   English

AES加密-加密/解密添加额外的字节

[英]AES Encryption - Encryption/Decryption Adding Extra Bytes

I'm writing a simple library for encryption/decryption, using the Framework's objects. 我正在使用框架的对象编写一个用于加密/解密的简单库。 The methods are as follows: 方法如下:

public static byte[] Encrypt(byte[] key, byte[] vector, byte[] input)
{
    if(key.Length == 0)
        throw new ArgumentException("Cannot encrypt with empty key");

    if (vector.Length == 0)
        throw new ArgumentException("Cannot encrypt with empty vector");

    if (input.Length == 0)
        throw new ArgumentException("Cannot encrypt empty input");

    var unencryptedBytes = input;

    using(AesCryptoServiceProvider aes = new AesCryptoServiceProvider {Key = key, IV = vector})
    using(ICryptoTransform encryptor = aes.CreateEncryptor())
    using (MemoryStream ms = new MemoryStream())
    using (CryptoStream writer = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
    {
        writer.Write(unencryptedBytes, 0, unencryptedBytes.Length);
        writer.FlushFinalBlock();
        var byteArray = ms.ToArray();

        if(byteArray.Length == 0)
            throw new Exception("Attempted to encrypt but encryption resulted in a byte array of 0 length.");

        return byteArray;
    }
}


public static byte[] Decrypt(byte[] key, byte[] vector, byte[] encrypted)
{
    if (key.Length == 0)
        throw new ArgumentException("Cannot encrypt with empty key");

    if (vector.Length == 0)
        throw new ArgumentException("Cannot encrypt with empty vector");

    if (encrypted == null || encrypted.Length == 0)
        throw new ArgumentException("Cannot decrypt empty or null byte array");

    byte[] unencrypted;

    using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider { Key = key, IV = vector })
    using (ICryptoTransform decryptor = aes.CreateDecryptor(key, vector))
    using (MemoryStream ms = new MemoryStream(encrypted))
    using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
    {

        cs.Read(encrypted, 0, encrypted.Length);

        unencrypted =  ms.ToArray();
    }


    return unencrypted;
}

Feeding it in an encoded string, for example, as follows: 例如,将其以编码字符串的形式提供:

var expected = "This is an example message";

var messageBytes = Encoding.UTF8.GetBytes(expected);

Goes in as (in this example) 26 bytes. (在此示例中)输入26个字节。 After encryption it leaves as 32 bytes, and when this is translated back as a string it has random characters appended at the end but is otherwise perfectly valid, as follows: 加密后,它保留为32个字节,当将其转换为字符串时,它会在末尾附加随机字符,但在其他情况下则完全有效,如下所示:

"This is an example message�(4���"

Generating a new vector/key changes the random characters that are appended to the end. 生成新的向量/密钥会更改附加到末尾的随机字符。

How can I eliminate this extra-byte behavior? 如何消除这种超字节行为?

EDIT 编辑

Included a requested unit test. 包括要求的单元测试。

[TestMethod]
public void CanEncryptAndDecryptByteArray()
{
    var expected = "This is an example message";

    var messageBytes = Encoding.UTF8.GetBytes(expected);


    AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
    aes.GenerateIV();
    aes.GenerateKey();


    var byteKey = Convert.ToBase64String(aes.Key);
    var vectorKey = Convert.ToBase64String(aes.IV);

    var key = Convert.FromBase64String("Qcf+3VzYNAfPUCfBO/ePSxCLBLItkVfk8ajK86KYebs=");
    var vector = Convert.FromBase64String("aJNKWP7M2D44jilby6BzGg==");

    var encrypted = EncryptionManager.Encrypt(key, vector, messageBytes);
    var decrypted = EncryptionManager.Decrypt(key, vector, encrypted);

    var actual = Encoding.UTF8.GetString(decrypted);

    Assert.AreEqual(expected, actual);
}

The junk-bytes are because we're reading and writing to the same array. 垃圾字节是因为我们正在读取和写入同一数组。

new MemoryStream(encrypted)) Is telling the crypt to read from encrypted . new MemoryStream(encrypted))告诉隐窝从读取encrypted

cs.Read(encrypted, 0, encrypted.Length); Is telling it to write to encrypted . 告诉它要写入encrypted

The solution: 解决方案:

using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider { Key = key, IV = vector })
using (ICryptoTransform decryptor = aes.CreateDecryptor(key, vector))
using (MemoryStream ms = new MemoryStream(encrypted))
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
    var decrypted = new byte[encrypted.Length];
    var bytesRead = cs.Read(decrypted, 0, encrypted.Length);    

    return decrypted.Take(bytesRead).ToArray();
}

Note that we take bytesRead from the array, rather than the full array, as we would otherwise end up with null bytes (which fail a string comparison but appear identical in editors) 请注意,我们从数组而不是整个数组中获取bytesRead ,否则我们将以null字节结尾(这将导致字符串比较失败,但在编辑器中看起来相同)

The AES encryption is a block encryption with block size of 16 bytes (128-bit) [note: the key size can of 128-bit, 192-bit or 256-bit, but the data block size is of multiplication of 16 bytes]. AES加密是一种块加密 ,其块大小为16字节(128位)[注意:密钥大小可以为128位,192位或256位,但是数据块大小为16字节的倍数] 。 Thus, to use AES, the resulting encrypted message will be multiplication of 16 - and cannot be otherwise. 因此,要使用AES,生成的加密消息将是16的乘积-否则不能。

To decrypt it, you may want to get rid of the extra bytes it causes too. 要解密它,您可能也想摆脱它引起的额外字节。

  1. One way to do this is by append header in your message, stating how many bytes the message is. 一种方法是在邮件中附加标题,说明邮件的字节数。 Then in the decrypt side, you read this header and cut the number of bytes 然后在解密端,读取此标头并削减字节数

     [header = value of 26] + [message] 
  2. Alternatively, you could append some extra bytes at the end of your encrypted message 或者,您可以在加密邮件的末尾附加一些额外的字节

     [message][append with 000000] //to make up the block, such that the message length will be of multiplication of 16 

Of the two methods above, I personally prefer the first one since it is much clearer, while the second one assumes you receive certain text pattern which is impossible to get concatenated 0000 at the end. 在上面的两种方法中,我个人更喜欢第一种方法,因为它更清晰,而第二种方法则假定您接收到某些文本模式,该文本模式最后无法连接为0000。

I am not aware if there is a way to solve this issue apart from the two given ways above - implicitly (such as using CryptoStream ) or explicitly. 除了以上两种给定的方法之外,我不知道是否有办法解决此问题-隐式(如使用CryptoStream )或显式。

AES encryption does the encryption in 16, 24 or 32 byte (or 128, 192 or 256 bits) blocks. AES加密以16、24或32字节(或128、192或256位)的块进行加密。 So the data you get back is just random data packed onto the end. 因此,您获取的数据只是打包到最后的随机数据。

You'll have to prepend your data with a couple of bytes to represent the actual legnth: 您必须在数据前加上几个字节来表示实际的长度:

var length = new byte [] { (byte) ((unencryptedBytes.Length >> 8) & 0xff) ,
    (byte) (unencryptedBytes.Length & 0xff);
writer.Write(length, 0, length.Length); 
writer.Write(unencryptedBytes, 0, unencryptedBytes.Length);
writer.FlushFinalBlock();

and then in the reader retrieve the length: 然后在阅读器中检索长度:

unencrypted =  ms.ToArray();

int length = ((int) unencrypted[0] << 8) + unencrypted[1];

var result = new byte[length];

unencrypted.CopyTo(result, 2, 0, length);

return result;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM