跨平台应用间安全消息传递实践

在现代软件开发中,不同平台的应用之间需要安全地交换消息。为了实现这一点,采用了消息队列服务,它允许设计应用程序时,消息的发送者和接收者不需要同时与消息队列交互。消息被放置在队列上后会被存储,直到接收者检索它们。

为了熟悉消息队列,使用了两种技术:Microsoft Azure IoT和RabbitMQ。本文将重点介绍通过这些队列提交和检索的实际消息。

希望通过这些队列提交对象,因为这将提供在共享不同类型的消息和答案(如执行命令、发送答案、更新配置等)方面的灵活性。通常,消息以字符串的形式使用,因此需要将对象转换为字符串(有时也称为序列化)。

如何将对象转换为字符串并反向转换

在开发使用OData和Entity Framework的应用程序时,注意到其中一个使用的库是Newtonsoft.json,这是一个流行的高性能.NET JSON框架。在浏览了他们的文档和API之后,决定尝试使用它。

在Visual Studio 2015中通过Nuget包管理器安装了该包,并准备在代码中使用它。使用了一个非常简单的基类,包含一个Token(int)和Data(string)。示例代码如下:

public class InfoBlock { public int Token { get; set; } public string Data { get; set; } }

现在,可以这样使用Newtonsoft.json序列化它:

string myString = JsonConvert.SerializeObject(anInfoBlock);

为了在字符串中包含序列化对象的“Infoblock”标识,在生产代码中使用了以下方法:

JsonSerializerSettings theJsonSerializerSettings = new JsonSerializerSettings(); theJsonSerializerSettings.TypeNameHandling = TypeNameHandling.None; myString = JsonConvert.SerializeObject(anInfoBlock, theJsonSerializerSettings);

要反序列化,可以将其强制转换为Infoblock类。

InfoBlock myInfoBlock = JsonConvert.DeserializeObject<InfoBlock>(myString, theJsonSerializerSettings);

字符串的加密和解密

当使用Microsoft Message Analyzer嗅探发送的流量时,注意到所有内容都很容易阅读,因此决定通过加密使其变得更难一些。选择了AES作为加密算法,这是一个基于Rijndael密码的对称密钥算法,由两位比利时密码学家Joan Daemen和Vincent Rijmen开发,他们向NIST提交了AES选择过程的提案。

AES加密使用相同的密钥进行加密和解密。使用的密钥大小为256位,块大小为128位。初始化向量是一个128位的随机起始值。为了能够成功解密字符串,需要知道密钥和初始化向量。密钥嵌入在代码中,这是可以的。初始化向量是随机的,因此为了共享它,将其嵌入到加密的字符串中。更喜欢将其存储在加密字符串的某个随机位置,以感觉更安全。

为了使描述更困难,必须确保每次加密相同的字符串时,加密版本都是不同的。将在测试单元中使用适当的断言来测试这一点。

实现

在.NET中,可以使用一个加密框架。不幸的是,较新的WinRT使用不同的类来完成这项工作,但通过使用条件编译,可以实现这一点。

#if WINDOWS_UWP using Windows.Security.Cryptography; using Windows.Security.Cryptography.Core; using Windows.Storage.Streams; #else using System.Security.Cryptography; #endif

在WinRT(UAP)中,使用:

myCryptoAlgorithm = SymmetricKeyAlgorithmProvider.OpenAlgorithm("AES_CBC_PKCS7");

在传统的Windows应用程序中,这是:

myCryptoAlgorithm = new AesCryptoServiceProvider(); myCryptoAlgorithm.Mode = CipherMode.CBC; myCryptoAlgorithm.Padding = PaddingMode.PKCS7; myCryptoAlgorithm.KeySize = 256; myCryptoAlgorithm.BlockSize = 128;

Cipher Block Chaining(CBC)模式引入了反馈。在加密每个明文块之前,它通过位异或操作与前一个块的密文结合。这确保了即使明文包含许多相同的块,它们也会被加密成不同的密文块。初始化向量通过位异或操作与第一个明文块结合,然后才加密该块。如果密文块的单个位被篡改,相应的明文块也会被篡改。此外,如果原始被篡改的位的位置在随后的块中,该位也会被篡改。

PKCS7是一种向文本添加字节的方法,以便完整的缓冲区被填充。PKCS #7填充字符串由一系列字节组成,每个字节都等于添加的填充字节总数。大多数明文消息不包含完全填充块的字节数。通常,没有足够的字节来填充最后一个块。当这种情况发生时,会向文本添加填充字符串。例如,如果块长度为64位,数据长度为9,填充八位等于7,数据等于FF FF FF FF FF FF FF FF FF,则使用PKCS7填充后,它变为:FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07。

字符串加密器类的示例代码如下:

public class StringEncryptor { // Encryption and Decryption methods... }

现在,有一个类来加密和解密字符串,创建了一个帮助类来序列化和加密InfoBlock,并执行反向操作(解密字符串并将其反序列化回Infoblock)。

public class InfoBlockConverter { // Serialize and Encrypt methods... // Decrypt and Deserialize methods... }

这是在测试单元中的使用方式:

[TestMethod] public void EncryptDecryptInfoBlock() { // Arrange InfoBlock InfoBlock01 = new InfoBlock(); // Act InfoBlock01.Token = InfoBlock.SOMETHINGELSE; InfoBlock01.Data = "Testing 123: Add some special characters &é@#’öçà!£$<ù}"; var encryptedString1 = InfoBlockConverter.EncodeToString(InfoBlock01); var encryptedString2 = InfoBlockConverter.EncodeToString(InfoBlock01); var InfoBlock02 = InfoBlockConverter.DecodeFromString(encryptedString2); // Assert Assert.AreNotEqual(encryptedString1, encryptedString2, "Infoblock should never be encrypted the same twice"); Assert.AreEqual(InfoBlock01.Token, InfoBlock02.Token, "Tokens should match original value"); Assert.AreEqual(InfoBlock01.Data, InfoBlock02.Data, "Data should match original value"); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485