不要碰我的日志,如何使用 Xml-DSig 保护您的 XML 数据






4.44/5 (9投票s)
2005 年 8 月 1 日
4分钟阅读

57371

608
展示了一个用于数字签名生成和验证的简单类。
引言
最近,我们的一位客户要求我们保护一个日志文件,防止其被篡改。要求是维护一个日志文件,记录用户在系统中执行的主要活动。由于种种原因,不允许使用数据库,所以我们必须使用纯文件来创建这个日志。其中一些字段需要加密,并且需要一个检查机制来确保数据没有被更改。
好了,我们构建了应用程序,并将日志文件映射到一个 XML DataSet。我们为日志文件定义了一个 XML Schema 文件 (XSD)。这使得我们可以使用类型化的 DataSet,并且程序运行起来就像数据库存在一样。.NET 类型化的 DataSet 与 XML 和 XSD 结合使用是一个不错的选择。例如,有了它们,我们就可以使用我们的 Grid 组件了。
数据完整性
本文介绍了我们如何解决日志文件的防篡改问题。我们有 XML 数据、一个 XSD 和 Visual Studio 从 XSD 生成的 C# 类。这些类允许我们读/写日志文件。为了进行完整性检查,我们决定使用数字签名。数字签名的标准由 W3C 和 IETF 定义,您可以在 此 链接中阅读有关此标准的所有信息。
用于 Xml-DSig 的类
XmlSigner
是一个实现了两个方法的类。
public static void SignXml (string fileName);
public static bool CheckSignXml (string fileName)
这两个方法都接收一个 fileName
。SignXml
在文件底部添加一个数字签名块,这个块元素称为 Signature。CheckSignXml
会检查指定的文件,如果签名正确则返回 true
。您可以下载完整的实现并根据您的需要进行调整。
密钥管理
我们使用 public
-private
密钥对进行签名生成。在此类中,签名存储在机器存储中。这是由 private
内部方法 GenCsp
完成的。方法的代码如下所示:
private static RSACryptoServiceProvider GenCsp()
{
CspParameters cp = new CspParameters();
cp.KeyContainerName = keyStorage;
cp.Flags = CspProviderFlags.UseMachineKeyStore;
cp.KeyNumber = 2;
return new RSACryptoServiceProvider(cp);
}
keyStorage
是一个私有的字符串变量,它定义了密钥存储的名称,可以通过一个名为 KeyStorage
的 static
属性访问。此代码假定检查将在同一台机器上反复进行。如果您想使用其他存储,您可以导出 public
密钥并使用它进行签名,然后使用 private
密钥进行检查,也许在将来的修改中我会向您展示如何做到这一点。
用法
我们以这种方式将 XmlSigner
类与我们的类型化数据集一起使用:
MyTypedDataSet myDataset;
void SaveData()
{
myDataset.WriteXml(logFileName);
XmlSigner.SignXml(logFileName);
}
void LoadData()
{
if (XmlSigner.CheckSignXml(logFileName))
throw new Exception("Bad Digital Signature,
file was tampered");
myDataset.ReadXml(logFileName);
}
问题
此类存在以下问题:
- 使用磁盘上的文件,因此有人可以在签名之前获取文件(在一个非常偏执的场景中,可以使用
System.IO.FileSystemWatcher
来完成)。 - 密钥存储在机器存储中,假设您想通过 LAN 访问文件,您需要一个外部密钥,也许在一个智能卡或证书中。
扩展
我将扩展此类以对 XmlDocuments 和 DataSet
s 进行签名。
加密 XML 元素
这篇 文章 是一个很好的起点;事实上,这篇文章启发并引导我进行了 C# 加密。使用该文章中描述的 EncDec
类,您可以对元素进行加密。请记住,我们正在对类型化数据集进行签名。在这种情况下,类型化 DataRow 是从原始 XSD 生成的,所以一个替代方案是修改类型化 DataRow 字段的 get 和 set。当然,如果修改了 XSD,您可能会丢失代码。这里的代码展示了我如何通过更改我想要加密的字段的 get
和 set
来解决问题。
public string Id {
get {
try {
return EncDec.Decrypt(
((string)(this[this.tableMyTable.IdColumn]),
"my password");
}
catch (InvalidCastException e) {
throw new StrongTypingException("Cannot get " +
"value because it is DBNull.", e);
}
}
set {
this[this.tableVerificacion.IdColumn] =
EncDec.Encrypt(((string)(this[this.tableMyTable.IdColumn]),
"my password");
}
}
如果您通过 XmlSerialization 生成数据,可以使用相同的方法。如果您使用 DOM,会更困难,但可行。
其他加密工具
您可以在 此处 找到 XML 安全 API,支持 C++ 和 Java。我很快就会为 C# 编写一个包装器,敬请期待……。
结论
添加到数据中的 Signature 元素不会干扰其内容,并且类型化数据集的所有机制都能正常工作。当敏感数据存储在文件中,并且您不希望这些数据被更改时,最佳选择是使用数字签名。在本文中,我们使用了 XML 数字签名,因为我们需要使用 XML DataSet,在这种情况下,这是最佳选择。
源代码
如果您不想下载代码,请阅读并分析它,我会在 我的博客 上保留一个可打印的版本。
历史
- 2005 年 8 月 3 日
我们添加了一些关于元素加密的技巧。我们还确定了当前实现的潜在问题。
- 2005 年 8 月 6 日
添加了对“欢迎来到 XML 安全”的引用。