在.NET中为XMLSerialized对象生成哈希签名(HMAC)的最佳方法?

时间:2009-10-26 15:56:35

标签: .net xml-serialization hmac

我需要为使用.NET框架中的XMLSerializer进行序列化的对象生成HMAC。每个对象都将包含一个名为“HMAC”的属性,该属性将包含对象值本身的哈希值,但不包括“HMAC”字段。我发现this question在CLR中提到了一个内置的解决方案,但是没有详细说明它的确切内容或我如何使用它?

示例对象看起来像这样:

[Serializable]
[XmlRoot("request", IsNullable = false)]
public class Request
{

    [XmlElement(ElementName = "hmac")]
    public string Hmac { get; set; } 

    [XmlElement(ElementName = "nonce")]
    public string Nonce { get; set; }

    [XmlElement(ElementName = "expiration")]
    public DateTime Expiration { get; set; }

    /* A bunch of other properties to be serialized */

    private Request() { }

    public Request(string hmac, string nonce, DateTime expiration)
    {
        Hmac = hmac;
        Nonce = nonce;
        Expiration = expiration;
    }
}

HMAC属性需要设置为整个对象的序列化,不包括HMAC对象本身。我的第一个想法是设置某种双程序列化,其中包括:

  1. 在第一次传递时将xmlignore属性设置为HMAC对象
  2. 序列化整个对象
  3. 散列结果,并设置HMAC属性的值
  4. 重新整理整个事物,准备传输。
  5. 这是最好的方法吗?有没有人之前做过这样的事情,你发现什么是最干净的方式呢?

1 个答案:

答案 0 :(得分:2)

我认为您必须将其序列化两次以获得您描述的确切效果。使这更容易的一种方法是不使用XmlIgnore,而是添加一个公共指定的属性(.NET XML序列化特别处理以编程方式控制是否发出类似命名的属性):

 [XmlIgnore]
 public bool HmacSpecified
 {
     get { return !String.IsNullOrEmpty(this.Hmac); }
     set { }
 }

这样做只会发出hmac XML节点(如果存在)。使用DefaultValueAttribute可以获得类似的效果,但我发现它有些不一致(例如,null在编译期间有时会被""替换)。另外,如果您的逻辑比单个标准的Sentinel值更复杂,您可以在属性中处理它,但不能在静态属性中处理。

get { return !this.IsCalculatingHmac(); }

如果格式必须与您描述的格式完全匹配,这将是我的方式。


如果输出格式具有灵活性,您可以考虑的另一种方法是使用一个消息容器,其中包含消息正文(您当前的XML文档)和HMAC签名值。这样,您只需要将文档序列化一次。

即使信封是较大的序列化文档的一部分,您也可以在信封上实现IXmlSerializable.WriteXml接口。这将允许您小心地将消息序列化为字符串,然后执行散列,然后通过XmlWriter.WriteRaw写出HMAC和消息元素。

如果表现很重要,那就是我这样做的方式。