mirror of
https://github.com/ldqk/Masuit.Tools.git
synced 2026-10-28 11:47:11 +08:00
227 lines
7.6 KiB
C#
227 lines
7.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Masuit.Tools.Security;
|
|
using Xunit;
|
|
|
|
namespace Masuit.Tools.Abstractions.Test.Security
|
|
{
|
|
public class EncryptTests
|
|
{
|
|
private const string TestString = "Hello, World!";
|
|
private const string DesKey = "12345678"; // 8位密钥
|
|
private const string AesKey = "0123456789abcdef0123456789abcdef";
|
|
private static readonly byte[] DesKeyBytes = Encoding.ASCII.GetBytes(DesKey);
|
|
private static readonly byte[] DesIVBytes = Encoding.ASCII.GetBytes(DesKey);
|
|
|
|
[Fact]
|
|
public void DesEncrypt_StringKey_ShouldEncryptAndDecrypt()
|
|
{
|
|
// Arrange
|
|
string original = TestString;
|
|
|
|
// Act
|
|
string encrypted = original.DesEncrypt(DesKey);
|
|
string decrypted = encrypted.DesDecrypt(DesKey);
|
|
|
|
// Assert
|
|
Assert.Equal(original, decrypted);
|
|
}
|
|
|
|
[Fact]
|
|
public void DesEncrypt_ByteKey_ShouldEncryptAndDecrypt()
|
|
{
|
|
// Arrange
|
|
string original = TestString;
|
|
|
|
// Act
|
|
string encrypted = original.DesEncrypt(DesKeyBytes, DesIVBytes);
|
|
string decrypted = encrypted.DesDecrypt(DesKeyBytes, DesIVBytes);
|
|
|
|
// Assert
|
|
Assert.Equal(original, decrypted);
|
|
}
|
|
|
|
[Fact]
|
|
public void AesEncrypt_StringKey_ShouldEncryptAndDecrypt()
|
|
{
|
|
string original = "Hello, 世界!";
|
|
|
|
string encrypted = original.AESEncrypt(AesKey);
|
|
string decrypted = encrypted.AESDecrypt(AesKey);
|
|
|
|
Assert.Equal(original, decrypted);
|
|
}
|
|
|
|
[Fact]
|
|
public void AesEncrypt_ShouldUseRandomIvAndAuthenticateCiphertext()
|
|
{
|
|
string first = TestString.AESEncrypt(AesKey);
|
|
string second = TestString.AESEncrypt(AesKey);
|
|
byte[] tampered = Convert.FromBase64String(first);
|
|
tampered[tampered.Length - 1] ^= 0x01;
|
|
|
|
Assert.NotEqual(first, second);
|
|
Assert.Throws<CryptographicException>(() => Convert.ToBase64String(tampered).AESDecrypt(AesKey));
|
|
}
|
|
|
|
[Fact]
|
|
public void AesEncrypt_ByteKey_ShouldEncryptAndDecrypt()
|
|
{
|
|
string original = "Hello, 世界!";
|
|
byte[] key = Encoding.UTF8.GetBytes(AesKey);
|
|
|
|
string encrypted = original.AESEncrypt(key);
|
|
string decrypted = encrypted.AESDecrypt(key);
|
|
|
|
Assert.Equal(original, decrypted);
|
|
}
|
|
|
|
[Fact]
|
|
public void AesEncrypt_AesOptions_ShouldUseAuthenticatedEnvelope()
|
|
{
|
|
using var options = Aes.Create();
|
|
options.Key = Encoding.UTF8.GetBytes(AesKey);
|
|
|
|
string encrypted = TestString.AESEncrypt(options);
|
|
string decrypted = encrypted.AESDecrypt(options);
|
|
|
|
Assert.Equal(TestString, decrypted);
|
|
}
|
|
|
|
[Fact]
|
|
public void AesDecrypt_InvalidCiphertext_ShouldThrow()
|
|
{
|
|
Assert.Throws<FormatException>(() => "not base64".AESDecrypt(AesKey));
|
|
}
|
|
|
|
[Fact]
|
|
public void AesGcmEncrypt_StringKey_ShouldEncryptAndDecrypt()
|
|
{
|
|
const string original = "Hello, AES-GCM 世界!";
|
|
byte[] associatedData = "header"u8.ToArray();
|
|
|
|
string encrypted = original.AesGcmEncrypt(AesKey, associatedData);
|
|
string decrypted = encrypted.AesGcmDecrypt(AesKey, associatedData);
|
|
|
|
Assert.NotEqual(original, encrypted);
|
|
Assert.Equal(original, decrypted);
|
|
}
|
|
|
|
[Fact]
|
|
public void AesGcmDecrypt_ModifiedCiphertext_ShouldThrow()
|
|
{
|
|
byte[] encrypted = Convert.FromBase64String("Hello, AES-GCM!".AesGcmEncrypt(AesKey));
|
|
encrypted[12] ^= 0x01;
|
|
|
|
Assert.ThrowsAny<CryptographicException>(() => Convert.ToBase64String(encrypted).AesGcmDecrypt(AesKey));
|
|
}
|
|
|
|
[Fact]
|
|
public void AesGcmDecrypt_DifferentAssociatedData_ShouldThrow()
|
|
{
|
|
string ciphertext = "authenticated data".AesGcmEncrypt(AesKey, "header"u8.ToArray());
|
|
|
|
Assert.ThrowsAny<CryptographicException>(() => ciphertext.AesGcmDecrypt(AesKey, "other header"u8.ToArray()));
|
|
}
|
|
|
|
[Fact]
|
|
public void AesFileEncryption_ShouldRoundTrip()
|
|
{
|
|
string inputPath = Path.GetTempFileName();
|
|
string encryptedPath = Path.GetTempFileName();
|
|
string decryptedPath = Path.GetTempFileName();
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(inputPath, "AES file content");
|
|
using (var input = File.OpenRead(inputPath))
|
|
{
|
|
input.AESEncryptFile(encryptedPath, AesKey);
|
|
}
|
|
|
|
using (var input = File.OpenRead(encryptedPath))
|
|
{
|
|
input.AESDecryptFile(decryptedPath, AesKey);
|
|
}
|
|
|
|
Assert.Equal("AES file content", File.ReadAllText(decryptedPath));
|
|
}
|
|
finally
|
|
{
|
|
File.Delete(inputPath);
|
|
File.Delete(encryptedPath);
|
|
File.Delete(decryptedPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void DesFileEncryption_ShouldRoundTrip()
|
|
{
|
|
string inputPath = Path.GetTempFileName();
|
|
string encryptedPath = Path.GetTempFileName();
|
|
string decryptedPath = Path.GetTempFileName();
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(inputPath, "DES file content");
|
|
using (var input = File.OpenRead(inputPath))
|
|
{
|
|
input.DesEncrypt(encryptedPath, DesKeyBytes, DesIVBytes);
|
|
}
|
|
|
|
using (var input = File.OpenRead(encryptedPath))
|
|
{
|
|
input.DesDecrypt(decryptedPath, DesKeyBytes, DesIVBytes);
|
|
}
|
|
|
|
Assert.Equal("DES file content", File.ReadAllText(decryptedPath));
|
|
}
|
|
finally
|
|
{
|
|
File.Delete(inputPath);
|
|
File.Delete(encryptedPath);
|
|
File.Delete(decryptedPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void AesDecryptFile_TamperedCiphertext_ShouldNotCreateOutput()
|
|
{
|
|
string inputPath = Path.GetTempFileName();
|
|
string encryptedPath = Path.GetTempFileName();
|
|
string decryptedPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".txt");
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(inputPath, "Authenticated AES file content");
|
|
using (var input = File.OpenRead(inputPath))
|
|
{
|
|
input.AESEncryptFile(encryptedPath, AesKey);
|
|
}
|
|
|
|
using (var encrypted = new FileStream(encryptedPath, FileMode.Open, FileAccess.ReadWrite))
|
|
{
|
|
encrypted.Position = encrypted.Length - 1;
|
|
int lastByte = encrypted.ReadByte();
|
|
encrypted.Position = encrypted.Length - 1;
|
|
encrypted.WriteByte((byte)(lastByte ^ 0x01));
|
|
}
|
|
|
|
using var tamperedInput = File.OpenRead(encryptedPath);
|
|
Assert.Throws<CryptographicException>(() => tamperedInput.AESDecryptFile(decryptedPath, AesKey));
|
|
Assert.False(File.Exists(decryptedPath));
|
|
}
|
|
finally
|
|
{
|
|
File.Delete(inputPath);
|
|
File.Delete(encryptedPath);
|
|
if (File.Exists(decryptedPath))
|
|
{
|
|
File.Delete(decryptedPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |