当前位置: 首页 > 知识库问答 >
问题:

为什么加密/解密大于127的字节数组不正确?

贲凌
2023-03-14

当我用AES加密和解密字节[128]{1,2,…,126,127}时,一切正常:

// Create Key & iv
byte[] key = GenerateKey();
byte[] iv = GenerateIV();

// Create raw byte array 0-127
byte[] raw = new byte[128];
for (byte i = 0; i < 128; i++)
{
    raw[i] = i;
}

// Encrypt
var encrypted = Encrypt(raw, key, iv);
// Decrypt
var decrypted = Decrypt(encrypted, key, iv);

解密后将输出字节[128]{1,2,…,126,127}。但当我将raw改为字节[127]{128,129,…,253,254}为相同的加密/解密逻辑时,结果变成字节[381],在[2391189]的循环中:

// Create Key & iv
byte[] key = GenerateKey();
byte[] iv = GenerateIV();

// Create raw byte array 128-254    
byte[] raw = new byte[127];
for (byte i = 128; i <= 254; i++)
{
    raw[i-128] = i;
}

// Encrypt
var encrypted = Encrypt(raw, key, iv);
// Decrypt
var decrypted = Decrypt(encrypted, key, iv);

现在解密后将输出字节[381]{239,191,189,…,239,191,189}

一开始我认为超过127是不同的,直到我发现以下字节数组也可以工作:

// Create Key & iv
byte[] key = GenerateKey();
byte[] iv = GenerateIV();

// Create raw byte array from Poruguese ÇÃ
string rawPortuguese = "ÇÃ";
byte[] raw = Encoding.UTF8.GetBytes(rawPortuguese);

现在原始是byte[4]{195, 135, 195, 131},每个数字都大于127。

// Encrypt
var encrypted = Encrypt(raw, key, iv);
// Decrypt
var decrypted = Decrypt(encrypted, key, iv);

但也能正确解密,解密的是字节[4]{195,135,195,131}现在我完全搞糊涂了,为什么原始数据字节[127]{128,129,…,253,254}不能正确解密?

密钥/IV/加密/解密代码

static byte[] GenerateKey()
{
    using (AesCng cng = new AesCng())
    {
        cng.GenerateKey();
        return cng.Key;
    }
}

static byte[] GenerateIV()
{
    using (AesCng cng = new AesCng())
    {
        cng.GenerateIV();
        return cng.IV;
    }
}

static byte[] Encrypt(byte[] raw, byte[] key, byte[] iv, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
    byte[] encrypted;

    using (AesCng cng = new AesCng())
    {
        cng.Mode = mode;
        cng.Padding = padding;

        cng.Key = key;
        cng.IV = iv;

        using (ICryptoTransform encryptor = cng.CreateEncryptor())
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                csEncrypt.Write(raw, 0, raw.Length);
            }

            encrypted = msEncrypt.ToArray();
        }
    }

    return encrypted;
}

static byte[] Decrypt(byte[] encrypted, byte[] key, byte[] iv, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
    byte[] decryptedData;
    string plaintext = null;
    byte[] plainData = null;

    using (AesCng cng = new AesCng())
    {
        cng.Mode = mode;
        cng.Padding = padding;

        cng.Key = key;
        decryptedData = encrypted;
        cng.IV = iv;

        using (ICryptoTransform decryptor = cng.CreateDecryptor())
        {
            using (MemoryStream msDecrypt = new MemoryStream(decryptedData))
            {
               using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
               {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        plaintext = srDecrypt.ReadToEnd();
                    }
                    plainData = Encoding.UTF8.GetBytes(plaintext);
                }
            }
        }
    }

    return plainData;
}

共有1个答案

仇航
2023-03-14

这就是问题所在:

using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
    plaintext = srDecrypt.ReadToEnd();
}
plainData = Encoding.UTF8.GetBytes(plaintext);

您将纯文本数据视为UTF-8,将其转换为字符串,然后将其转换回字节(再次使用UTF-8)。如果纯文本数据真的是UTF-8编码的文本(就像您的葡萄牙语示例中那样),那么这很好,但对于任意字节数组,情况并非如此。字节序列0x80、0x81、0x82、0x83。。。0xff不是有效的UTF-8。

除非你知道数据是有效文本,否则你不应该将其视为文本——这总是会导致这样的问题。在这种情况下,“纯文本”这个名称并不真正意味着文本——这是一个不幸的术语。它只是意味着“未加密的数据”。

如果您只想有效地读取任意流并从中创建数组,请使用另一个内存流,将数据复制到该流,然后使用内存流。ToArray要将其转换为字节[],请执行以下操作:

using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
    using (var plainData = new MemoryStream())
    {
        csDescrypt.CopyTo(plainData);
        return plainData.ToArray();
    }
}
 类似资料:
  • 我正在从https://docs.AWS.amazon.com/cli/latest/reference/kms/encrypt.html和https://docs.AWS.amazon.com/cli/latest/reference/kms/decrypt.html读取AWS加密cli文档。我发现我可以在不创建数据密钥的情况下加密/解密。当我阅读https://docs.aws.amazon.

  • 我使用密码加密和解密字节数组在Android发送到服务器。我没有使用字符串,但我仍然收到非法块大小异常。 2021-08-12 20:55:17.606 15222-15222/com.example.packageW/System.err:javax.crypto.IllegalBlockSizeExcture:错误:1e00006a:密码函数:OPENSSL_internal:DATA_NOT

  • 问题内容: 我正在使用以下LINK 进行加密,并使用Strings进行了尝试,并且可以正常工作。但是,由于我要处理图像,因此我需要对字节数组进行加密/解密过程。因此,我将该链接中的代码修改为以下内容: 而检查器类是: 但是我的输出是: 因此,解密后的文本与纯文本不同。知道我在原始链接中尝试了该示例并且该示例可与Strings一起使用时,该怎么办才能解决此问题? 问题答案: 您所看到的是数组的toS

  • 为什么数学模块返回错误的结果? 结果 在这里,结果是正确的。 结果 这里的结果是不正确的。 为什么会这样呢?

  • 问题内容: 我究竟做错了什么?我希望Java程序打印“专用”。我的目标是尝试用Java编写MessageEncryptor.decrypt ruby​​方法。 Ruby加密(大多数代码来自MessageEncryptor,但未修改为Marshal),但我已将其提取出来,以便更轻松地了解正在发生的情况: 哪个打印: tzFUIVllG2FcYD7xqGPmHQ ==-UAPvdm3oN3Hog9ND

  • 问题内容: 我有以下加密数据: 对其进行解密的密码是: (这是来自胡言乱语的例子) 在命令行中使用openssl: 输出为: 使用我的NodeJS应用程序: 我在一行中遇到以下错误。 我想念什么吗?谢谢。 问题答案: 加密的数据以8字节的“魔术”开头,表示存在盐(的ASCII编码)。然后接下来的8个字节是盐。现在是个坏消息:Node.js似乎没有对EVP_BytesToKey方法使用盐: 那是盐。