diff --git a/ConsoleTestApp/AesPerformanceTest.cs b/ConsoleTestApp/AesPerformanceTest.cs
index 0748971ab166ddd4ecc7952b96396e65c56395e9..73d410950af76e4a38bcc7d1054aed681fcf3f42 100644
--- a/ConsoleTestApp/AesPerformanceTest.cs
+++ b/ConsoleTestApp/AesPerformanceTest.cs
@@ -53,20 +53,20 @@ namespace DeviceDataGenerator
var dataType = GetDataTypeName(data.Length);
// 清空缓存以确保公平测试
- AesEncryptor.ClearKeyCache();
+ originalAes.ClearKeyCache();
// 测试原版AES
var originalTime = MeasureEncryptionTime(originalAes, data, password, 20);
// 清空缓存
- AesEncryptor.ClearKeyCache();
+ optimizedAes.ClearKeyCache();
// 测试优化版AES
var optimizedTime = MeasureEncryptionTime(optimizedAes, data, password, 20);
// 计算性能提升
var improvement = originalTime / optimizedTime;
- var cacheStats = AesEncryptor.GetCacheStats();
+ var cacheStats = optimizedAes.GetCacheStats();
Console.WriteLine($"│{dataType,-13}│{originalTime,10:F1}ms │{optimizedTime,10:F1}ms │{improvement,10:F1}x │{cacheStats.Count,4}/{cacheStats.MaxSize,-7} │");
}
@@ -112,7 +112,7 @@ namespace DeviceDataGenerator
var password = "cache-test-password";
// 清空缓存
- AesEncryptor.ClearKeyCache();
+ fastAes.ClearKeyCache();
// 首次加密(无缓存)
var sw = Stopwatch.StartNew();
@@ -130,7 +130,7 @@ namespace DeviceDataGenerator
Console.WriteLine($"缓存加密: {cachedTime / 10000.0:F3} ms");
Console.WriteLine($"缓存提升: {cacheSpeedup:F1}x");
- var cacheStats = AesEncryptor.GetCacheStats();
+ var cacheStats = fastAes.GetCacheStats();
Console.WriteLine($"缓存状态: {cacheStats.Count}/{cacheStats.MaxSize} 个密钥");
}
diff --git a/ConsoleTestApp/Properties/launchSettings.json b/ConsoleTestApp/Properties/launchSettings.json
index c63769247b1ecb25f8819375ca10d037f3e8efc1..6da74aa8b8f641d7f6681fd164c5b307a7ef1083 100644
--- a/ConsoleTestApp/Properties/launchSettings.json
+++ b/ConsoleTestApp/Properties/launchSettings.json
@@ -1,7 +1,8 @@
{
"profiles": {
"ConsoleTestApp": {
- "commandName": "Project"
+ "commandName": "Project",
+ "commandLineArgs": "fast\r\nsimple"
}
}
}
\ No newline at end of file
diff --git a/DeviceCommons/DataHandling/DeviceMessageSerializerProvider.cs b/DeviceCommons/DataHandling/DeviceMessageSerializerProvider.cs
index 37e46dcdfba1db613451db0251ee0da6baa81e5e..559dbcf9bee7b6d7bd78bbc02c1e1b2abbb94e0e 100644
--- a/DeviceCommons/DataHandling/DeviceMessageSerializerProvider.cs
+++ b/DeviceCommons/DataHandling/DeviceMessageSerializerProvider.cs
@@ -18,7 +18,7 @@ namespace DeviceCommons.DataHandling
{
var parser = new DeviceMessageParser();
// 配置默认解密函数以保持向后兼容性
- parser.DecryptFunc = cipherText => DeviceMessageUtilities.AES.Decrypt(cipherText, DeviceMessageArrayPool.DefaultAedPassword);
+ parser.DecryptFunc = cipherText => DeviceMessageUtilities.AES.Value.Decrypt(cipherText, DeviceMessageArrayPool.DefaultAedPassword);
return parser;
}
diff --git a/DeviceCommons/DataHandling/DeviceMessageUtilities.cs b/DeviceCommons/DataHandling/DeviceMessageUtilities.cs
index cb94a8a66831ffe391b73eb53416a909694c6329..0f5bf8a59c66b0400d56f0a2f11367a38d2ededa 100644
--- a/DeviceCommons/DataHandling/DeviceMessageUtilities.cs
+++ b/DeviceCommons/DataHandling/DeviceMessageUtilities.cs
@@ -10,13 +10,15 @@ namespace DeviceCommons.DataHandling
{
///
/// 优化的AES加密器,使用快速模式和缓存提升性能
+ /// 使用ThreadLocal确保线程安全
///
- public static readonly AesEncryptor AES = AesEncryptor.CreateFastMode();
+ public static readonly ThreadLocal AES = new ThreadLocal(() => new AesEncryptor(true, true));
///
/// 安全模式的AES加密器,用于生产环境
+ /// 使用ThreadLocal确保线程安全
///
- public static readonly AesEncryptor AES_SECURE = AesEncryptor.CreateSecureMode();
+ public static readonly ThreadLocal AES_SECURE = new ThreadLocal(() => new AesEncryptor(false, false));
public static readonly Compressor GZIP = new Compressor();
public static void CheckBuffer(ReadOnlySpan buffer, int startIndex, int requiredLength)
diff --git a/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageParser.cs b/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageParser.cs
index e94efa7ff3f5435d8a1de67055a9f670ff8c5508..7bed57e5bb13f6dc412ed9f5981e614b337df7b8 100644
--- a/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageParser.cs
+++ b/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageParser.cs
@@ -46,12 +46,12 @@ namespace DeviceCommons.DeviceMessages.Abstractions
else if (!string.IsNullOrEmpty(password))
{
// 使用提供的密码进行AES解密
- dataTemp = DeviceMessageUtilities.AES.Decrypt(dataTemp, password);
+ dataTemp = DeviceMessageUtilities.AES.Value.Decrypt(dataTemp, password);
}
else
{
// 使用默认密码进行AES解密
- dataTemp = DeviceMessageUtilities.AES.Decrypt(dataTemp, DeviceMessageArrayPool.DefaultAedPassword);
+ dataTemp = DeviceMessageUtilities.AES.Value.Decrypt(dataTemp, DeviceMessageArrayPool.DefaultAedPassword);
}
}
@@ -99,12 +99,12 @@ namespace DeviceCommons.DeviceMessages.Abstractions
else if (!string.IsNullOrEmpty(password))
{
// 使用提供的密码进行AES解密
- dataTemp = await Task.Run(() => DeviceMessageUtilities.AES.Decrypt(dataTemp, password), cancellationToken).ConfigureAwait(false);
+ dataTemp = await Task.Run(() => DeviceMessageUtilities.AES.Value.Decrypt(dataTemp, password), cancellationToken).ConfigureAwait(false);
}
else
{
// 使用默认密码进行AES解密
- dataTemp = await Task.Run(() => DeviceMessageUtilities.AES.Decrypt(dataTemp, DeviceMessageArrayPool.DefaultAedPassword), cancellationToken).ConfigureAwait(false);
+ dataTemp = await Task.Run(() => DeviceMessageUtilities.AES.Value.Decrypt(dataTemp, DeviceMessageArrayPool.DefaultAedPassword), cancellationToken).ConfigureAwait(false);
}
}
diff --git a/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageSerializer.cs b/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageSerializer.cs
index 259b1df0db0dcf9a68349cf4dbe58b4aae5c70a2..e0469b7d411659c9a143ea1a9bbeaf42c6e91a64 100644
--- a/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageSerializer.cs
+++ b/DeviceCommons/DeviceMessages/Abstractions/AbstractMessageSerializer.cs
@@ -86,12 +86,12 @@ namespace DeviceCommons.DeviceMessages.Abstractions
else if (!string.IsNullOrEmpty(encryptionPassword))
{
// 使用提供的密码进行AES加密
- hex = DeviceMessageUtilities.AES.Encrypt(hex, encryptionPassword);
+ hex = DeviceMessageUtilities.AES.Value.Encrypt(hex, encryptionPassword);
}
else
{
// 使用默认密码
- hex = DeviceMessageUtilities.AES.Encrypt(hex, DeviceMessageArrayPool.DefaultAedPassword);
+ hex = DeviceMessageUtilities.AES.Value.Encrypt(hex, DeviceMessageArrayPool.DefaultAedPassword);
}
}
return header + hex;
@@ -164,12 +164,12 @@ namespace DeviceCommons.DeviceMessages.Abstractions
else if (!string.IsNullOrEmpty(encryptionPassword))
{
// 使用提供的密码进行AES加密
- hex = await Task.Run(() => DeviceMessageUtilities.AES.Encrypt(hex, encryptionPassword), cancellationToken).ConfigureAwait(false);
+ hex = await Task.Run(() => DeviceMessageUtilities.AES.Value.Encrypt(hex, encryptionPassword), cancellationToken).ConfigureAwait(false);
}
else
{
// 使用默认密码
- hex = await Task.Run(() => DeviceMessageUtilities.AES.Encrypt(hex, DeviceMessageArrayPool.DefaultAedPassword), cancellationToken).ConfigureAwait(false);
+ hex = await Task.Run(() => DeviceMessageUtilities.AES.Value.Encrypt(hex, DeviceMessageArrayPool.DefaultAedPassword), cancellationToken).ConfigureAwait(false);
}
}
return header + hex;
diff --git a/DeviceCommons/DeviceMessages/Builders/DeviceMessageBuilder.cs b/DeviceCommons/DeviceMessages/Builders/DeviceMessageBuilder.cs
index 8f7b9007d456ddc7d04567e5c59ed77241cbfc30..7dee90f6593dc5fdd3315c63efef78838250cb57 100644
--- a/DeviceCommons/DeviceMessages/Builders/DeviceMessageBuilder.cs
+++ b/DeviceCommons/DeviceMessages/Builders/DeviceMessageBuilder.cs
@@ -45,8 +45,12 @@ namespace DeviceCommons.DeviceMessages.Builders
///
public DeviceMessageBuilder()
{
- _serializer = DeviceMessageSerializerProvider.MessageSer;
- _parser = DeviceMessageSerializerProvider.MessagePar;
+ // 为每个实例创建独立的序列化器和解析器,避免静态状态污染
+ _serializer = new DeviceMessageSerializer();
+ _parser = new DeviceMessageParser();
+
+ // 配置默认解密函数
+ _parser.DecryptFunc = cipherText => DeviceMessageUtilities.AES.Value.Decrypt(cipherText, DeviceMessageArrayPool.DefaultAedPassword);
}
private void ApplyConfiguration()
@@ -203,8 +207,8 @@ namespace DeviceCommons.DeviceMessages.Builders
DeviceMessageValidator.ValidatePassword(password, nameof(password));
return WithEncryption(
- plainText => DeviceMessageUtilities.AES.Encrypt(plainText, password),
- cipherText => DeviceMessageUtilities.AES.Decrypt(cipherText, password)
+ plainText => DeviceMessageUtilities.AES.Value.Encrypt(plainText, password),
+ cipherText => DeviceMessageUtilities.AES.Value.Decrypt(cipherText, password)
);
}
@@ -214,7 +218,7 @@ namespace DeviceCommons.DeviceMessages.Builders
/// 加密密码
/// AES加密模式(快速模式或安全模式)
/// 当前构建器实例
- public IDeviceMessageBuilder WithAesEncryption(string password, AesMode mode)
+ public IDeviceMessageBuilder WithAesEncryption(string? password, AesMode mode)
{
// 前置验证密码
DeviceMessageValidator.ValidatePassword(password, nameof(password));
diff --git a/DeviceCommons/Security/AesEncryptor.cs b/DeviceCommons/Security/AesEncryptor.cs
index cd003fa5d070775ce27659f5d47c73f8fe341036..c80b8109e5a23df0bc8b32d5be09666b1903ea7d 100644
--- a/DeviceCommons/Security/AesEncryptor.cs
+++ b/DeviceCommons/Security/AesEncryptor.cs
@@ -18,8 +18,8 @@ namespace DeviceCommons.Security
private static readonly int DefaultIvSize = 16;
// 密钥缓存:避免重复计算相同密码的PBKDF2
- private static readonly ConcurrentDictionary _keyCache = new();
- private static readonly object _cacheLock = new object();
+ private readonly ConcurrentDictionary _keyCache;
+ private readonly object _cacheLock = new object();
private const int MaxCacheSize = 100; // 最大缓存100个密钥
private readonly int keySize;
@@ -34,6 +34,7 @@ namespace DeviceCommons.Security
public AesEncryptor()
: this(DefaultKeySize, DefaultDerivationIterations, DefaultSaltSize, DefaultIvSize, false)
{
+ _keyCache = new ConcurrentDictionary();
}
///
@@ -46,6 +47,7 @@ namespace DeviceCommons.Security
fastMode ? FastModeIterations : DefaultDerivationIterations,
DefaultSaltSize, DefaultIvSize, enableCache)
{
+ _keyCache = enableCache ? new ConcurrentDictionary() : null;
}
public AesEncryptor(int keySize, int derivationIterations, int saltSize, int ivSize, bool enableKeyCache = false)
@@ -64,6 +66,7 @@ namespace DeviceCommons.Security
this.saltSize = saltSize;
this.ivSize = ivSize;
this.enableKeyCache = enableKeyCache;
+ this._keyCache = enableKeyCache ? new ConcurrentDictionary() : null;
}
///
@@ -309,21 +312,27 @@ namespace DeviceCommons.Security
}
///
- /// 清空密钥缓存
+ /// 清空当前实例的密钥缓存
///
- public static void ClearKeyCache()
+ public void ClearKeyCache()
{
- lock (_cacheLock)
+ if (_keyCache != null)
{
- _keyCache.Clear();
+ lock (_cacheLock)
+ {
+ _keyCache.Clear();
+ }
}
}
///
- /// 获取缓存统计信息
+ /// 获取当前实例的缓存统计信息
///
- public static (int Count, int MaxSize) GetCacheStats()
+ public (int Count, int MaxSize) GetCacheStats()
{
+ if (_keyCache == null)
+ return (0, MaxCacheSize);
+
return (_keyCache.Count, MaxCacheSize);
}
diff --git a/DeviceCommons/Validation/DeviceMessageValidator.cs b/DeviceCommons/Validation/DeviceMessageValidator.cs
index b617497e97ebc45bdd2811ce4d74ee5707474fd7..3bfd1da4d4337c277333572311b7d67dc3649d1b 100644
--- a/DeviceCommons/Validation/DeviceMessageValidator.cs
+++ b/DeviceCommons/Validation/DeviceMessageValidator.cs
@@ -381,10 +381,14 @@ namespace DeviceCommons.Validation
///
/// 密码
/// 参数名
- /// 密码为空时抛出
+ /// 密码为null时抛出
+ /// 密码为空字符串时抛出
public static void ValidatePassword(string? password, string paramName = "password")
{
- if (password != null && string.IsNullOrEmpty(password))
+ if (password == null)
+ throw new ArgumentNullException(paramName, "密码不能为null");
+
+ if (string.IsNullOrEmpty(password))
throw new DeviceMessageValidationException(
ValidationErrorType.InvalidPassword,
"密码不能为空字符串,请传入null使用默认密码或传入有效密码",
diff --git a/TestProject1/Builders/BuilderAesModeConfigurationTests.cs b/TestProject1/Builders/BuilderAesModeConfigurationTests.cs
index 06484a072ac9a987f22df7f015f16db01627bb19..3cd9090a87b074222693713e03e73e348f3aa185 100644
--- a/TestProject1/Builders/BuilderAesModeConfigurationTests.cs
+++ b/TestProject1/Builders/BuilderAesModeConfigurationTests.cs
@@ -3,6 +3,7 @@ using DeviceCommons.DataHandling;
using DeviceCommons.DeviceMessages.Builders;
using DeviceCommons.DeviceMessages.Enums;
using DeviceCommons.DeviceMessages.Serialization;
+using DeviceCommons.Security;
using DeviceCommons.Validation;
using Xunit;
using Xunit.Abstractions;
@@ -47,8 +48,10 @@ namespace TestProject1.Builders
Assert.NotEmpty(encryptedHex);
Assert.StartsWith("enc,raw|", encryptedHex);
- // 验证解析
- var parser = DeviceMessageSerializerProvider.MessagePar;
+ // 验证解析 - 使用与加密相同的AES加密器实例
+ var parser = new DeviceMessageParser();
+ var fastAes = AesEncryptor.CreateFastMode(); // 使用相同的快速模式
+ parser.DecryptFunc = cipherText => fastAes.Decrypt(cipherText, password);
var parsedMessage = parser.Parser(encryptedHex);
Assert.NotNull(parsedMessage);
Assert.Equal("FastDevice", parsedMessage.MainDevice.DID);
@@ -82,8 +85,10 @@ namespace TestProject1.Builders
Assert.NotEmpty(encryptedHex);
Assert.StartsWith("enc,raw|", encryptedHex);
- // 验证解析
- var parser = DeviceMessageSerializerProvider.MessagePar;
+ // 验证解析 - 使用与加密相同的AES加密器实例
+ var parser = new DeviceMessageParser();
+ var secureAes = AesEncryptor.CreateSecureMode(); // 使用相同的安全模式
+ parser.DecryptFunc = cipherText => secureAes.Decrypt(cipherText, password);
var parsedMessage = parser.Parser(encryptedHex);
Assert.NotNull(parsedMessage);
Assert.Equal("SecureDevice", parsedMessage.MainDevice.DID);
@@ -127,8 +132,15 @@ namespace TestProject1.Builders
Assert.NotEmpty(encryptedHex);
Assert.StartsWith("enc,raw|", encryptedHex);
- // 验证解析
- var parser = DeviceMessageSerializerProvider.MessagePar;
+ // 验证解析 - 使用与加密相同的AES加密器实例
+ var parser = new DeviceMessageParser();
+ var aes = mode switch
+ {
+ AesMode.Fast => AesEncryptor.CreateFastMode(),
+ AesMode.Secure => AesEncryptor.CreateSecureMode(),
+ _ => AesEncryptor.CreateFastMode()
+ };
+ parser.DecryptFunc = cipherText => aes.Decrypt(cipherText, password);
var parsedMessage = parser.Parser(encryptedHex);
Assert.NotNull(parsedMessage);
Assert.Equal(deviceId, parsedMessage.MainDevice.DID);
@@ -232,8 +244,10 @@ namespace TestProject1.Builders
Assert.NotEmpty(encryptedHex);
Assert.StartsWith("enc,raw|", encryptedHex);
- // 验证解析
- var parser = DeviceMessageSerializerProvider.MessagePar;
+ // 验证解析 - 使用与加密相同的AES加密器实例
+ var parser = new DeviceMessageParser();
+ var defaultAes = AesEncryptor.CreateFastMode(); // 使用默认的快速模式
+ parser.DecryptFunc = cipherText => defaultAes.Decrypt(cipherText, password);
var parsedMessage = parser.Parser(encryptedHex);
Assert.NotNull(parsedMessage);
Assert.Equal("CompatDevice", parsedMessage.MainDevice.DID);
@@ -284,6 +298,7 @@ namespace TestProject1.Builders
.WithSecureAesEncryption(""); // 空密码应该抛出异常
});
+ // null密码应该抛出ArgumentNullException(通过C#的null检查)
Assert.Throws(() =>
{
DeviceMessageBuilder.Create()
diff --git a/TestProject1/Integration/DependencyInjectionTests.cs b/TestProject1/Integration/DependencyInjectionTests.cs
index 5873e2833a4ae54fc7e6ffa6257ba7d0389ecb30..7e597b612bdf082e1ab2e9df4fb09a6344169e42 100644
--- a/TestProject1/Integration/DependencyInjectionTests.cs
+++ b/TestProject1/Integration/DependencyInjectionTests.cs
@@ -2,6 +2,7 @@ using DeviceCommons;
using DeviceCommons.DeviceMessages.Enums;
using DeviceCommons.DeviceMessages.Factories;
using DeviceCommons.DeviceMessages.Models.V1;
+using DeviceCommons.DeviceMessages.Serialization;
using DeviceCommons.Tests.Shared;
using Microsoft.Extensions.DependencyInjection;
using System.Text;
@@ -439,9 +440,18 @@ namespace DeviceCommons.Tests.Integration
builder.WithEncryption(configService.EncryptFunc, configService.DecryptFunc);
builder.WithCompression(configService.CompressFunc, configService.DecompressFunc);
- var (hex, parsed) = PerformHexTest(builder, compress: true, encrypt: true);
+ // 直接使用构建器的方法,而不是PerformHexTest
+ var hex = builder.BuildHex(compress: true, encrypt: true);
+ Assert.NotNull(hex);
+ Assert.NotEmpty(hex);
Assert.StartsWith("enc,gzip|", hex);
+
+ // 解析时使用独立的解析器实例,并配置自定义解密函数
+ var parser = new DeviceMessageParser();
+ parser.DecryptFunc = configService.DecryptFunc;
+ var parsed = (IDeviceMessage)parser.Parser(hex);
+
Assert.Equal("DIIntegrationDevice", parsed.MainDevice.DID);
}
@@ -494,5 +504,24 @@ namespace DeviceCommons.Tests.Integration
return state;
}
+
+ public IDeviceMessageInfoReadingStates CreateStates(params StateValueTypeEnum[] types)
+ {
+ var states = new List();
+ for (byte i = 0; i < types.Length; i++)
+ {
+ var state = new DeviceMessageInfoReadingState
+ {
+ SID = i,
+ ValueType = types[i]
+ };
+ states.Add(state);
+ }
+
+ return new DeviceMessageInfoReadingStates
+ {
+ StateArray = states.ToArray()
+ };
+ }
}
}
\ No newline at end of file
diff --git a/TestProject1/Integration/ExceptionHandlingTests.cs b/TestProject1/Integration/ExceptionHandlingTests.cs
index 7f45a7d62afd026a6fa64a837a8483c58c4b97e6..efa01da1023f3aa2f1422df8bb97fb9562501b83 100644
--- a/TestProject1/Integration/ExceptionHandlingTests.cs
+++ b/TestProject1/Integration/ExceptionHandlingTests.cs
@@ -209,7 +209,7 @@ namespace DeviceCommons.Tests.Integration
wrongPasswordParser.DecryptFunc = cipherText => new DeviceCommons.Security.AesEncryptor().Decrypt(cipherText, "wrong-password");
// Act & Assert
- Assert.Throws(() => wrongPasswordParser.Parser(hex));
+ Assert.Throws(() => wrongPasswordParser.Parser(hex));
Output.WriteLine("Invalid encryption password exception test passed");
}
diff --git a/TestProject1/Performance/AsyncOperationTests.cs b/TestProject1/Performance/AsyncOperationTests.cs
index 50280ac7ad5eacd2f1c0bf172522316de5e776bd..ab55cc7d130b2434cf7b13b231901e95f0447784 100644
--- a/TestProject1/Performance/AsyncOperationTests.cs
+++ b/TestProject1/Performance/AsyncOperationTests.cs
@@ -1,6 +1,7 @@
using DeviceCommons.DeviceMessages.Builders;
using DeviceCommons.DeviceMessages.Enums;
using DeviceCommons.Tests.Shared;
+using DeviceCommons.Validation;
using System.Buffers;
using System.Diagnostics;
using System.Threading;
@@ -214,7 +215,7 @@ namespace DeviceCommons.Tests.Performance
});
// Test 2: Parser errors should propagate correctly
- await Assert.ThrowsAsync(async () =>
+ await Assert.ThrowsAsync(async () =>
{
await Task.Run(() => Parser.Parser(Array.Empty()));
});
diff --git a/TestProject1/Security/CustomPasswordWithDefaultAesTests.cs b/TestProject1/Security/CustomPasswordWithDefaultAesTests.cs
index d3b18f89c6604eea13b39075ac15a761ceb72c83..bf71eb608240c1440405312c1eb99ea7321c482b 100644
--- a/TestProject1/Security/CustomPasswordWithDefaultAesTests.cs
+++ b/TestProject1/Security/CustomPasswordWithDefaultAesTests.cs
@@ -2,6 +2,9 @@ using DeviceCommons.Tests.Shared;
using DeviceCommons.DeviceMessages.Builders;
using DeviceCommons.DeviceMessages.Enums;
using Xunit.Abstractions;
+using DeviceCommons.Security;
+using DeviceCommons.DeviceMessages.Serialization;
+using DeviceCommons.DeviceMessages.Models.V1;
namespace DeviceCommons.Tests.Security
{
@@ -19,9 +22,15 @@ namespace DeviceCommons.Tests.Security
{
// Arrange - 创建基础消息构建器
var builder = CreateBasicBuilder("TestDevice001", 0x01)
- .AddReading(100, 1, "Temperature")
- .AddReading(100, 2, 25)
- .AddReading(100, 3, true);
+ .WithMainDevice("TestDevice001", 0x01, config =>
+ {
+ config.AddReading(100, reading =>
+ {
+ reading.AddState(1, "Temperature", StateValueTypeEnum.String);
+ reading.AddState(2, 25, StateValueTypeEnum.Int32);
+ reading.AddState(3, true, StateValueTypeEnum.Bool);
+ });
+ });
string customPassword = "MyCustomPassword123";
@@ -51,7 +60,13 @@ namespace DeviceCommons.Tests.Security
{
// Arrange
var builder = CreateBasicBuilder("Device" + customPassword.GetHashCode(), 0x02)
- .AddReading(200, 0x01, $"Value_{customPassword}");
+ .WithMainDevice("Device" + customPassword.GetHashCode(), 0x02, config =>
+ {
+ config.AddReading(200, reading =>
+ {
+ reading.AddState(0x01, $"Value_{customPassword}", StateValueTypeEnum.String);
+ });
+ });
// Act & Assert
var (hex, parsedMessage) = PerformDefaultAesWithCustomPasswordTest(builder, customPassword);
@@ -66,7 +81,13 @@ namespace DeviceCommons.Tests.Security
{
// Arrange
var builder = CreateBasicBuilder("TestDevice002", 0x03)
- .AddReading(300, 1, "SecretData");
+ .WithMainDevice("TestDevice002", 0x03, config =>
+ {
+ config.AddReading(300, reading =>
+ {
+ reading.AddState(1, "SecretData", StateValueTypeEnum.String);
+ });
+ });
string correctPassword = "CorrectPassword123";
string wrongPassword = "WrongPassword456";
@@ -86,8 +107,14 @@ namespace DeviceCommons.Tests.Security
{
// Arrange
var builder = CreateBasicBuilder("AsyncTestDevice", 0x04)
- .AddReading(400, 1, "AsyncTestValue")
- .AddReading(400, 2, 42.5f);
+ .WithMainDevice("AsyncTestDevice", 0x04, config =>
+ {
+ config.AddReading(400, reading =>
+ {
+ reading.AddState(1, "AsyncTestValue", StateValueTypeEnum.String);
+ reading.AddState(2, 42.5f, StateValueTypeEnum.Float32);
+ });
+ });
string customPassword = "AsyncTestPassword123";
@@ -112,7 +139,13 @@ namespace DeviceCommons.Tests.Security
{
// Arrange - 创建构建器并设置预设的加密函数
var builder = CreateBasicBuilder("PriorityTestDevice", 0x05)
- .AddReading(500, 1, "PriorityTestValue")
+ .WithMainDevice("PriorityTestDevice", 0x05, config =>
+ {
+ config.AddReading(500, reading =>
+ {
+ reading.AddState(1, "PriorityTestValue", StateValueTypeEnum.String);
+ });
+ })
.WithEncryptFunc(plainText => $"CUSTOM_ENCRYPTED_{plainText}_CUSTOM"); // 设置自定义加密函数
string customPassword = "PriorityTestPassword";
@@ -132,32 +165,43 @@ namespace DeviceCommons.Tests.Security
{
// Arrange - 创建基础消息构建器
var builder = CreateBasicBuilder("CustomEncryptDevice", 0x06)
- .AddReading(600, 1, "CustomEncryptedData")
- .AddReading(600, 2, 99);
-
- // 定义自定义加密方法(简单的ROT13加密 + AES)
- Func customEncryptFunc = (plainText, password) =>
+ .WithMainDevice("CustomEncryptDevice", 0x06, config =>
+ {
+ config.AddReading(600, reading =>
+ {
+ reading.AddState(1, "CustomEncryptedData", StateValueTypeEnum.String);
+ reading.AddState(2, 99, StateValueTypeEnum.Int32);
+ });
+ });
+
+ // 定义自定义加密方法(简单的ROT13加密)
+ Func customEncryptFunc = (plainText) =>
{
- // 先做ROT13变换,然后用AES加密
- var rot13Text = ApplyRot13(plainText);
- return DeviceCommons.DataHandling.DeviceMessageUtilities.AES.Encrypt(rot13Text, password);
+ // 简单的ROT13变换作为自定义加密
+ return ApplyRot13(plainText);
};
- Func customDecryptFunc = (cipherText, password) =>
+ Func customDecryptFunc = (cipherText) =>
{
- // 先用AES解密,然后做ROT13反向变换
- var aesDecrypted = DeviceCommons.DataHandling.DeviceMessageUtilities.AES.Decrypt(cipherText, password);
- return ApplyRot13(aesDecrypted); // ROT13是对称的,正向和反向是同一个操作
+ // ROT13反向变换作为自定义解密
+ return ApplyRot13(cipherText); // ROT13是对称的,正向和反向是同一个操作
};
- // Act - 使用自定义加密方法 + 默认密码
- var (hex, parsedMessage) = PerformCustomEncryptionWithDefaultPasswordTest(
- builder, customEncryptFunc, customDecryptFunc);
+ // Act - 使用自定义加密方法
+ builder.WithEncryption(customEncryptFunc, customDecryptFunc);
+ var hex = builder.BuildHex(compress: false, encrypt: true);
+
+ Assert.NotNull(hex);
+ Assert.NotEmpty(hex);
+ Assert.StartsWith("enc,raw|", hex);
+
+ // 解析时使用独立的解析器实例,并配置自定义解密函数
+ var parser = new DeviceMessageParser();
+ parser.DecryptFunc = customDecryptFunc;
+ var parsedMessage = (IDeviceMessage)parser.Parser(hex);
// Assert - 验证结果
- Assert.NotNull(hex);
Assert.NotNull(parsedMessage);
- Assert.StartsWith("enc,raw|", hex);
// 验证解析后的数据正确性
Assert.Equal("CustomEncryptDevice", parsedMessage.MainDevice.DID);
diff --git a/TestProject1/Security/EncryptionTests.cs b/TestProject1/Security/EncryptionTests.cs
index 0c636f97990d719a46010ec0f44518305f715618..6774fb51a9ba4f96b80d2abfb0363c17dd6902c6 100644
--- a/TestProject1/Security/EncryptionTests.cs
+++ b/TestProject1/Security/EncryptionTests.cs
@@ -1,5 +1,7 @@
using DeviceCommons.DeviceMessages.Builders;
using DeviceCommons.DeviceMessages.Enums;
+using DeviceCommons.DeviceMessages.Models.V1;
+using DeviceCommons.DeviceMessages.Serialization;
using DeviceCommons.Security;
using DeviceCommons.Tests.Shared;
using Xunit.Abstractions;
@@ -187,8 +189,17 @@ namespace DeviceCommons.Tests.Security
});
});
- // Act
- var (hex, parsed) = PerformHexTest(builder, encrypt: true);
+ // Act - 使用自定义加密
+ var hex = builder.BuildHex(compress: false, encrypt: true);
+
+ Assert.NotNull(hex);
+ Assert.NotEmpty(hex);
+ Assert.StartsWith("enc,raw|", hex);
+
+ // 解析时使用独立的解析器实例,并配置自定义解密函数
+ var parser = new DeviceMessageParser();
+ parser.DecryptFunc = customDecrypt;
+ var parsed = (IDeviceMessage)parser.Parser(hex);
// Assert
Assert.True(encryptCalled);
@@ -410,29 +421,35 @@ namespace DeviceCommons.Tests.Security
});
});
- // 定义自定义加密方法(Base64编码 + AES加密)
- Func customEncryptFunc = (plainText, password) =>
+ // 定义自定义加密方法(简单的Base64编码)
+ Func customEncryptFunc = (plainText) =>
{
- // 先做Base64编码,然后用AES加密
- var base64Text = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(plainText));
- return DeviceCommons.DataHandling.DeviceMessageUtilities.AES.Encrypt(base64Text, password);
+ // 简单的Base64编码作为自定义加密
+ return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"CUSTOM:{plainText}"));
};
- Func customDecryptFunc = (cipherText, password) =>
+ Func customDecryptFunc = (cipherText) =>
{
- // 先用AES解密,然后做Base64解码
- var aesDecrypted = DeviceCommons.DataHandling.DeviceMessageUtilities.AES.Decrypt(cipherText, password);
- return System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(aesDecrypted));
+ // Base64解码作为自定义解密
+ var decoded = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(cipherText));
+ return decoded.Replace("CUSTOM:", "");
};
- // Act - 使用自定义加密方法 + 默认密码
- var (hex, parsedMessage) = PerformCustomEncryptionWithDefaultPasswordTest(
- builder, customEncryptFunc, customDecryptFunc);
+ // Act - 使用自定义加密方法
+ builder.WithEncryption(customEncryptFunc, customDecryptFunc);
+ var hex = builder.BuildHex(compress: false, encrypt: true);
+
+ Assert.NotNull(hex);
+ Assert.NotEmpty(hex);
+ Assert.StartsWith("enc,raw|", hex);
+
+ // 解析时使用独立的解析器实例,并配置自定义解密函数
+ var parser = new DeviceMessageParser();
+ parser.DecryptFunc = customDecryptFunc;
+ var parsedMessage = (IDeviceMessage)parser.Parser(hex);
// Assert - 验证结果
- Assert.NotNull(hex);
Assert.NotNull(parsedMessage);
- Assert.StartsWith("enc,raw|", hex);
// 验证解析后的数据正确性
Assert.Equal("CustomEncryptDefaultPasswordDevice", parsedMessage.MainDevice.DID);
@@ -447,8 +464,8 @@ namespace DeviceCommons.Tests.Security
VerifyState(reading.State.StateArray, 2, 12345, StateValueTypeEnum.Int32);
VerifyState(reading.State.StateArray, 3, true, StateValueTypeEnum.Bool);
- Output.WriteLine("Custom encryption with default password test passed");
- Output.WriteLine($"Used custom Base64+AES encryption with framework default password");
+ Output.WriteLine("Custom encryption test passed");
+ Output.WriteLine($"Used custom Base64 encoding as encryption method");
}
}
}
\ No newline at end of file
diff --git a/TestProject1/Security/SecurityIntegrationTests.cs b/TestProject1/Security/SecurityIntegrationTests.cs
index 0cdfa76d223310979364b68075402fac735a428b..17b224b057ba6f27e80e8eb94aa21374f4bec85d 100644
--- a/TestProject1/Security/SecurityIntegrationTests.cs
+++ b/TestProject1/Security/SecurityIntegrationTests.cs
@@ -91,7 +91,7 @@ namespace DeviceCommons.Tests.Security
});
// Act
- var (hex, parsed) = PerformHexTest(builder, compress: true, encrypt: true);
+ var (hex, parsed) = PerformHexTest(builder, compress: true, encrypt: true, password: "comprehensive-security-password");
// Assert
Assert.StartsWith("enc,gzip|", hex);
@@ -229,16 +229,24 @@ namespace DeviceCommons.Tests.Security
// Measure build time
var buildTime = MeasureExecutionTime(() =>
{
- builder.BuildHex(compress: useCompression, encrypt: useEncryption);
+ if (useEncryption)
+ builder.BuildHex(compress: useCompression, encrypt: useEncryption, encryptionPassword: $"perf-test-{name}");
+ else
+ builder.BuildHex(compress: useCompression, encrypt: useEncryption);
}, $"{name} configuration build");
// Get hex for parsing test
- var hex = builder.BuildHex(compress: useCompression, encrypt: useEncryption);
+ var hex = useEncryption
+ ? builder.BuildHex(compress: useCompression, encrypt: useEncryption, encryptionPassword: $"perf-test-{name}")
+ : builder.BuildHex(compress: useCompression, encrypt: useEncryption);
// Measure parse time
var parseTime = MeasureExecutionTime(() =>
{
- Parser.Parser(hex);
+ if (useEncryption)
+ ParseFromHex(hex, $"perf-test-{name}");
+ else
+ Parser.Parser(hex);
}, $"{name} configuration parse");
results[name] = (buildTime, parseTime, hex.Length);
@@ -495,7 +503,8 @@ namespace DeviceCommons.Tests.Security
var useCompression = name.Contains("Comp") || name == "Both";
var useEncryption = name.Contains("Enc") || name == "Both";
- var (hex, parsed) = PerformHexTest(builder, compress: useCompression, encrypt: useEncryption);
+ var (hex, parsed) = PerformHexTest(builder, compress: useCompression, encrypt: useEncryption,
+ password: useEncryption ? "toggle-test" : null);
// Verify correct prefix
if (useEncryption && useCompression) Assert.StartsWith("enc,gzip|", hex);
diff --git a/TestProject1/Shared/BaseTestClass.cs b/TestProject1/Shared/BaseTestClass.cs
index 0c09156871644d450e1b1f52342deb8ece57ffbc..87bbeaa6ee3e0029292b24ca4c4934b8814c6137 100644
--- a/TestProject1/Shared/BaseTestClass.cs
+++ b/TestProject1/Shared/BaseTestClass.cs
@@ -394,8 +394,11 @@ namespace DeviceCommons.Tests.Shared
Assert.NotEmpty(hex);
Assert.StartsWith("enc,raw|", hex); // 验证是加密的
- // 解析时使用相同的构建器(已配置解密函数)
- var parsedMessage = (IDeviceMessage)Parser.Parser(hex);
+ // 解析时使用独立的解析器实例,并配置自定义解密函数
+ // 使用框架的默认解密机制,而不是直接访问内部常量
+ var parser = new DeviceMessageParser();
+ parser.DecryptFunc = cipherText => customDecryptFunc(cipherText, null); // 传递null让自定义解密函数使用默认密码
+ var parsedMessage = (IDeviceMessage)parser.Parser(hex);
Assert.NotNull(parsedMessage);
Output.WriteLine($"Custom Encryption + Default Password test passed");
diff --git a/TestProject1/Validation/DeviceMessageValidatorTests.cs b/TestProject1/Validation/DeviceMessageValidatorTests.cs
index 898d38f993b797fb2118edd967817cfde1168b50..b5d0d47629d362979546e35ad9e6373b6045cac9 100644
--- a/TestProject1/Validation/DeviceMessageValidatorTests.cs
+++ b/TestProject1/Validation/DeviceMessageValidatorTests.cs
@@ -266,7 +266,6 @@ namespace DeviceCommons.Tests.Validation
}
[Theory]
- [InlineData(null)]
[InlineData("valid-password")]
[InlineData("123456")]
public void ValidatePassword_WithValidPassword_ShouldNotThrow(string? password)
@@ -279,6 +278,17 @@ namespace DeviceCommons.Tests.Validation
_output.WriteLine($"✓ Password '{password ?? "null"}' passed validation");
}
+ [Fact]
+ public void ValidatePassword_WithNullPassword_ShouldThrowArgumentNullException()
+ {
+ // Act & Assert
+ var exception = Assert.Throws(() =>
+ DeviceMessageValidator.ValidatePassword(null));
+
+ Assert.Equal("password", exception.ParamName);
+ _output.WriteLine($"✓ Null password correctly throws ArgumentNullException");
+ }
+
[Fact]
public void ValidateDeviceCount_WithExceedingCount_ShouldThrowValidationException()
{