我需要为使用.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对象本身。我的第一个想法是设置某种双程序列化,其中包括:
这是最好的方法吗?有没有人之前做过这样的事情,你发现什么是最干净的方式呢?
答案 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和消息元素。
如果表现很重要,那就是我这样做的方式。