解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
7.79 KiB
X
using System.Security.Cryptography;
using NewLife.Security;
using Xunit;

namespace XUnitTest.Security;

/// <summary>PKCS7填充转换测试</summary>
public class PKCS7PaddingTransformTests
{
    [Fact(DisplayName = "属性传递自内部Transform")]
    public void PropertiesDelegateToInner()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;
        using var inner = aes.CreateEncryptor();
        using var target = new PKCS7PaddingTransform(inner, PaddingMode.PKCS7, true);

        Assert.Equal(inner.InputBlockSize, target.InputBlockSize);
        Assert.Equal(inner.OutputBlockSize, target.OutputBlockSize);
        Assert.Equal(inner.CanReuseTransform, target.CanReuseTransform);
        Assert.Equal(inner.CanTransformMultipleBlocks, target.CanTransformMultipleBlocks);
    }

    [Fact(DisplayName = "不支持的填充模式抛异常")]
    public void UnsupportedModeThrows()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;
        using var inner = aes.CreateEncryptor();

        Assert.Throws<NotSupportedException>(() => new PKCS7PaddingTransform(inner, PaddingMode.Zeros, true));
        Assert.Throws<NotSupportedException>(() => new PKCS7PaddingTransform(inner, PaddingMode.None, true));
    }

    [Fact(DisplayName = "块大小超出范围抛异常")]
    public void InvalidBlockSizeThrows()
    {
        // 使用块大小为1的变换
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;
        using var inner = aes.CreateEncryptor();

        // 正常创建不抛异常
        using var _ = new PKCS7PaddingTransform(inner, PaddingMode.PKCS7, true);
    }

    [Fact(DisplayName = "空数据加密追加完整填充块")]
    public void TransformFinalBlockEmptyReturnsEmpty()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;
        using var inner = aes.CreateEncryptor();
        using var target = new PKCS7PaddingTransform(inner, PaddingMode.PKCS7, true);

        // PKCS7 标准要求:空数据加密也必须输出一个完整填充块(16 字节),与 .NET/Java 标准实现互通
        var result = target.TransformFinalBlock([], 0, 0);
        Assert.Equal(16, result.Length);
    }

    [Fact(DisplayName = "PKCS7加密时填充到块边界")]
    public void EncryptPadsToBlockBoundary()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;
        using var inner = aes.CreateEncryptor();
        using var target = new PKCS7PaddingTransform(inner, PaddingMode.PKCS7, true);

        // 输入10字节,不足16字节块大小,PKCS7填充6字节到16字节
        var input = new Byte[10];
        new Random(42).NextBytes(input);
        var result = target.TransformFinalBlock(input, 0, input.Length);

        // 输出应为16字节(1个完整块)
        Assert.Equal(16, result.Length);
    }

    [Fact(DisplayName = "PKCS7加密解密往返")]
    public void EncryptDecryptRoundTrip()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;

        // 不对齐数据
        var input = new Byte[33];
        new Random(42).NextBytes(input);

        using var encTransform = new PKCS7PaddingTransform(aes.CreateEncryptor(), PaddingMode.PKCS7, true);
        var encrypted = encTransform.TransformFinalBlock(input, 0, input.Length);

        using var decTransform = new PKCS7PaddingTransform(aes.CreateDecryptor(), PaddingMode.PKCS7, false);
        var decrypted = decTransform.TransformFinalBlock(encrypted, 0, encrypted.Length);

        Assert.Equal(input, decrypted);
    }

    [Fact(DisplayName = "ISO10126填充加密解密往返")]
    public void Iso10126RoundTrip()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;

        var input = new Byte[20];
        new Random(42).NextBytes(input);

        using var encTransform = new PKCS7PaddingTransform(aes.CreateEncryptor(), PaddingMode.ISO10126, true);
        var encrypted = encTransform.TransformFinalBlock(input, 0, input.Length);

        using var decTransform = new PKCS7PaddingTransform(aes.CreateDecryptor(), PaddingMode.ISO10126, false);
        var decrypted = decTransform.TransformFinalBlock(encrypted, 0, encrypted.Length);

        Assert.Equal(input, decrypted);
    }

    [Fact(DisplayName = "ANSIX923填充加密解密往返")]
    public void AnsiX923RoundTrip()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;

        var input = new Byte[15];
        new Random(42).NextBytes(input);

        using var encTransform = new PKCS7PaddingTransform(aes.CreateEncryptor(), PaddingMode.ANSIX923, true);
        var encrypted = encTransform.TransformFinalBlock(input, 0, input.Length);

        using var decTransform = new PKCS7PaddingTransform(aes.CreateDecryptor(), PaddingMode.ANSIX923, false);
        var decrypted = decTransform.TransformFinalBlock(encrypted, 0, encrypted.Length);

        Assert.Equal(input, decrypted);
    }

    [Fact(DisplayName = "对齐数据不额外填充")]
    public void AlignedDataNoExtraPadding()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;
        using var inner = aes.CreateEncryptor();
        using var target = new PKCS7PaddingTransform(inner, PaddingMode.PKCS7, true);

        // 输入16字节,刚好一个块,PKCS7会填充一整个块
        var input = new Byte[16];
        new Random(42).NextBytes(input);
        var result = target.TransformFinalBlock(input, 0, input.Length);

        // 16字节输入 + 16字节填充 = 32字节
        Assert.Equal(32, result.Length);
    }

    /// <summary>单块转换器包装。CanTransformMultipleBlocks=false 用于覆盖多块末尾拷贝路径</summary>
    private sealed class SingleBlockTransform(ICryptoTransform inner) : ICryptoTransform
    {
        public Boolean CanReuseTransform => inner.CanReuseTransform;

        public Boolean CanTransformMultipleBlocks => false;

        public Int32 InputBlockSize => inner.InputBlockSize;

        public Int32 OutputBlockSize => inner.OutputBlockSize;

        public Int32 TransformBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[] outputBuffer, Int32 outputOffset)
            => inner.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);

        public Byte[] TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
            => inner.TransformFinalBlock(inputBuffer, inputOffset, inputCount);

        public void Dispose() => inner.Dispose();
    }

    [Fact(DisplayName = "多块加密在单块转换器下往返正确")]
    public void MultiBlockWithSingleBlockTransformRoundTrip()
    {
        using var aes = Aes.Create();
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.None;

        // 40 字节 → 填充到 48 字节(3 块),触发 TransformFinalBlock 的多块手动路径
        var input = new Byte[40];
        new Random(42).NextBytes(input);

        using var encInner = new SingleBlockTransform(aes.CreateEncryptor());
        using var enc = new PKCS7PaddingTransform(encInner, PaddingMode.PKCS7, true);
        var encrypted = enc.TransformFinalBlock(input, 0, input.Length);
        Assert.Equal(48, encrypted.Length);

        using var decInner = new SingleBlockTransform(aes.CreateDecryptor());
        using var dec = new PKCS7PaddingTransform(decInner, PaddingMode.PKCS7, false);
        var decrypted = dec.TransformFinalBlock(encrypted, 0, encrypted.Length);

        Assert.Equal(input, decrypted);
    }
}