.NET到Web加密互操作性填充问题

时间:2018-01-29 22:33:55

标签: .net encryption aes padding webcryptoapi

我尝试加密.NET中的某些数据(使用C#),然后使用web-crypto API在浏览器中对其进行解密(反之亦然)。由于某种原因,每一侧的加密输出略有不同。浏览器输出在数据末尾附加了16个字节,我无法找到导致它的原因。

执行加密的.NET代码是:

public static byte[] Encrypt(byte[] data)
{
    using (var aes = new AesManaged())
    {
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        var key = new byte[] { 168, 126, 39, 25, 51, 65, 246, 41, 228, 56, 66, 237, 5, 8, 211, 102, 250, 16, 99, 12, 204, 14, 162, 126, 166, 140, 15, 124, 194, 186, 141, 111 };
        var iv = new byte[] { 188, 227, 223, 253, 171, 64, 82, 150, 10, 130, 159, 79, 68, 134, 192, 50 };

        using (var encryptor = aes.CreateEncryptor(key, iv))
        using (var msEncrypt = new MemoryStream())
        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        {
            csEncrypt.Write(data, 0, data.Length);
            return msEncrypt.ToArray();
        }
    }
}

JavaScript代码如下(假设缺少的变量是正确的):

function encrypt(data, key) {
    window.crypto.subtle.encrypt(
        {
            name: "AES-CBC",
            iv: Uint8Array.from([188, 227, 223, 253, 171, 64, 82, 150, 10, 130, 159, 79, 68, 134, 192, 50]);
        },
        key,
        data 
    )
    .then(function(d){
        var encryptedBytes = new Uint8Array(d);
        // encryptedBytes had an additional 16 bytes added to the end of it for the same input.
        console.log(encryptedBytes);
    });
}

加密的字节与C#版本的字节匹配,但最后会增加16个字节。这是填充问题吗?从我能找到的文档来看,似乎PKCS7是正确的填充模式。

我认为浏览器版本可能会将初始化向量附加到数据的末尾,但我检查了它并没有匹配。

尝试解密由C#代码加密的数据,只是在浏览器中出现奇怪异常的错误。

2 个答案:

答案 0 :(得分:2)

MarkovskI的回答,虽然不是最终的解决方案,但却帮助我找到了根本原因。由于CryptoStream语句的排列方式,问题最终导致MemoryStream未将数据写入using

有两种可能的解决方案。

1)在返回FlushFinalBlock()数组之前调用MemoryStream's

csEncrypt.Write(data, 0, data.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();

只是打电话给Flush()(我曾经尝试过)不起作用。这是有道理的,因为它需要确保在计算和写入填充之前已经写入了所有数据。

2)在调用using之前,嵌套内部CryptoStream语句以确保MemoryStream已被处理掉。这通常必须在FlushFinalBlock()方法中调用Dispose

using (var aes = new AesManaged())
{
    aes.Mode = CipherMode.CBC;
    aes.Padding = PaddingMode.PKCS7;
    var key = new byte[] { 168, 126, 39, 25, 51, 65, 246, 41, 228, 56, 66, 237, 5, 8, 211, 102, 250, 16, 99, 12, 204, 14, 162, 126, 166, 140, 15, 124, 194, 186, 141, 111 };
    var iv = new byte[] { 188, 227, 223, 253, 171, 64, 82, 150, 10, 130, 159, 79, 68, 134, 192, 50 };

    using (var encryptor = aes.CreateEncryptor(key, iv))
    using (var msEncrypt = new MemoryStream())
    {
        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        {
            csEncrypt.Write(data, 0, data.Length);
        }

        return msEncrypt.ToArray();
    }
}

答案 1 :(得分:0)

AES要求长度输入为16的倍数,因此必须应用填充,这是PKCS#7的用武之地,而额外的数据来自,这是.Net实现中的一个问题。如果要删除填充,则需要将其从最终块中删除,可以使用以下方法执行此操作:

public class NoPaddingTransformWrapper : ICryptoTransform
{
    private ICryptoTransform _mTransform;
    public NoPaddingTransformWrapper(ICryptoTransform symmetricAlgoTransform)
    {
        _mTransform = symmetricAlgoTransform ?? throw new ArgumentNullException(nameof(symmetricAlgoTransform));
    }

    public bool CanReuseTransform => _mTransform.CanReuseTransform;

    public bool CanTransformMultipleBlocks => _mTransform.CanTransformMultipleBlocks;

    public int InputBlockSize => _mTransform.InputBlockSize;

    public int OutputBlockSize => _mTransform.OutputBlockSize;

    public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
    {
        return _mTransform.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
    }

    public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
    {
        if (inputCount % _mTransform.InputBlockSize == 0)
        {
            return _mTransform.TransformFinalBlock(inputBuffer, inputOffset, inputCount);
        }
        byte[] lastBlocks = new byte[inputCount / _mTransform.InputBlockSize + _mTransform.InputBlockSize];
        Buffer.BlockCopy(inputBuffer, inputOffset, lastBlocks, 0, inputCount);
        byte[] result = _mTransform.TransformFinalBlock(lastBlocks, 0, lastBlocks.Length);
        Debug.Assert(inputCount < result.Length);
        Array.Resize(ref result, inputCount);
        return result;
    }

    public void Dispose()
    {
        _mTransform.Dispose();
    }
}