65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.44/5 (9投票s)

2005 年 8 月 1 日

4分钟阅读

viewsIcon

57371

downloadIcon

608

展示了一个用于数字签名生成和验证的简单类。

引言

最近,我们的一位客户要求我们保护一个日志文件,防止其被篡改。要求是维护一个日志文件,记录用户在系统中执行的主要活动。由于种种原因,不允许使用数据库,所以我们必须使用纯文件来创建这个日志。其中一些字段需要加密,并且需要一个检查机制来确保数据没有被更改。

好了,我们构建了应用程序,并将日志文件映射到一个 XML DataSet。我们为日志文件定义了一个 XML Schema 文件 (XSD)。这使得我们可以使用类型化的 DataSet,并且程序运行起来就像数据库存在一样。.NET 类型化的 DataSet 与 XML 和 XSD 结合使用是一个不错的选择。例如,有了它们,我们就可以使用我们的 Grid 组件了。

数据完整性

本文介绍了我们如何解决日志文件的防篡改问题。我们有 XML 数据、一个 XSD 和 Visual Studio 从 XSD 生成的 C# 类。这些类允许我们读/写日志文件。为了进行完整性检查,我们决定使用数字签名。数字签名的标准由 W3CIETF 定义,您可以在 链接中阅读有关此标准的所有信息。

用于 Xml-DSig 的类

XmlSigner 是一个实现了两个方法的类。

public static void SignXml (string fileName);
 
public static bool CheckSignXml (string fileName)

这两个方法都接收一个 fileNameSignXml 在文件底部添加一个数字签名块,这个块元素称为 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 是一个私有的字符串变量,它定义了密钥存储的名称,可以通过一个名为 KeyStoragestatic 属性访问。此代码假定检查将在同一台机器上反复进行。如果您想使用其他存储,您可以导出 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);
}

问题

此类存在以下问题:

  1. 使用磁盘上的文件,因此有人可以在签名之前获取文件(在一个非常偏执的场景中,可以使用 System.IO.FileSystemWatcher 来完成)。
  2. 密钥存储在机器存储中,假设您想通过 LAN 访问文件,您需要一个外部密钥,也许在一个智能卡或证书中。

扩展

我将扩展此类以对 XmlDocuments 和 DataSets 进行签名。

加密 XML 元素

这篇 文章 是一个很好的起点;事实上,这篇文章启发并引导我进行了 C# 加密。使用该文章中描述的 EncDec 类,您可以对元素进行加密。请记住,我们正在对类型化数据集进行签名。在这种情况下,类型化 DataRow 是从原始 XSD 生成的,所以一个替代方案是修改类型化 DataRow 字段的 get 和 set。当然,如果修改了 XSD,您可能会丢失代码。这里的代码展示了我如何通过更改我想要加密的字段的 getset 来解决问题。

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 安全”的引用。

© . All rights reserved.