.NET签名XML前缀
有没有办法在签名的XML文档(.Net中的SignedXml类)的签名上设置前缀 因此,不是:.NET签名XML前缀,.net,xml,xml-signature,.net,Xml,Xml Signature,有没有办法在签名的XML文档(.Net中的SignedXml类)的签名上设置前缀 因此,不是: <Signature xmlns="http://www.w3.org/2000/09/xmldsig#> ... </Signature> ... 首先,这样做真的没有什么好的理由。这两种形式在功能上是等价的。任何表现良好的XML处理器都将以完全相同的方式处理它们。因此,除非您试图与没有正确实现XML名称空间的应用程序对话,否则最好(IMO)不要使用默认表单。(即使在这种
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#>
...
</Signature>
...
首先,这样做真的没有什么好的理由。这两种形式在功能上是等价的。任何表现良好的XML处理器都将以完全相同的方式处理它们。因此,除非您试图与没有正确实现XML名称空间的应用程序对话,否则最好(IMO)不要使用默认表单。(即使在这种情况下,如果可能的话,最好是修复有故障的应用程序。)
也就是说,您可以使用XPath手动设置SignedXml.GetXml()返回的XmlElement及其子元素的前缀,如下所示:
XmlElement signature = signedXml.GetXml();
foreach (XmlNode node in signature.SelectNodes(
"descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']"))
{
node.Prefix = "ds";
}
这是办不到的。如果在签名后修改XML,则可能无法对其进行验证,上例就是这种情况。依我看,这是MSFT数字签名实现中的一个缺陷,您将不得不忍受 很多人会说没有理由这么做,而且他们在技术上是正确的。但是,当你与一个大供应商(即州政府或银行)打交道时,祝他们好运,让他们改变现状。大多数参考实现都包括它
更新:签名对SignedInfo元素中的所有内容进行签名,因此如果在事实发生后更新该元素,则签名不再有效。您已经“篡改”了消息。可以这样做,但在获取SignedInfo节点的摘要值之前,有必要修改SignedXml类以添加前缀 将修改ComputeSignature方法以添加前缀参数
public void ComputeSignature(string prefix){...}
调用此方法时,它通过对SignedInfo节点的值进行摘要处理来计算签名值。如果您在没有“ds”前缀的情况下获取此值,然后添加前缀,则将获得无效签名,因此您必须在获取SignedInfo节点的摘要值之前添加前缀
此摘要值是在方法GetC14NDigest中生成的,因此将修改此方法以添加prefix参数,并在获取摘要值之前添加前缀
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = false;
XmlElement e = this.SignedInfo.GetXml(); //get the signedinfo nodes
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); /*Set the prefix before getting the HASH*/
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
好的,现在您有了带有“ds”前缀的SignedInfo节点的签名值,也就是说您还没有带有前缀的xml,因此,如果您只调用GetXml方法,您将获得不带前缀的xml,当然,由于签名值是根据ds前缀计算的,因此您将拥有无效的签名。
为了避免这种情况并获得带有前缀的xml结构,您必须修改GetXml方法,添加prefix参数,并调用SetPrefix方法,该方法将向签名xml中的所有节点添加“ds”前缀
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e); //return the xml structure with the prefix
return e;
}
我将把这些修改留在这里
自定义类
internal sealed class CustomSignedXml : SignedXml
{
XmlElement obj = null;
public CustomSignedXml (XmlDocument xml)
: base(xml)
{
}
public CustomSignedXml (XmlElement xmlElement)
: base(xmlElement)
{
}
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e);
return e;
}
public void ComputeSignature(string prefix)
{
this.BuildDigestedReferences();
AsymmetricAlgorithm signingKey = this.SigningKey;
if (signingKey == null)
{
throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
if (!(signingKey is DSA))
{
if (!(signingKey is RSA))
{
throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
}
}
else
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
}
}
SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;
if (description == null)
{
throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
}
HashAlgorithm hash = description.CreateDigest();
if (hash == null)
{
throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
}
this.GetC14NDigest(hash, prefix);
this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
}
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = false;
XmlElement e = this.SignedInfo.GetXml();
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); //Set the prefix before getting the HASH
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
private void BuildDigestedReferences()
{
Type t = typeof(SignedXml);
MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(this, new object[] { });
}
private void SetPrefix(string prefix, XmlNode node)
{
foreach (XmlNode n in node.ChildNodes)
SetPrefix(prefix, n);
node.Prefix = prefix;
}
}
及其使用方法
CustomSignedXml signedXml = new CustomSignedXml();
.
.//your code
.
//compute the signature with the "ds" prefix
signedXml.ComputeSignature("ds");
//get the xml of the signature with the "ds" prefix
XmlElement xmlDigitalSignature = signedXml.GetXml("ds");
我同意这是一样的,而且应该有效。但是,我不确定我是否能够让另一方在没有ds前缀的情况下工作。在生成sig后更改前缀的简单操作不会导致验证失败吗?它可能会破坏签名,这取决于签名元素是否包含在被签名的信息中(如果转换不排除它,它将破坏)。所以这在所有可能的情况下都不起作用。如果你看一下Reflector中的SignedXml类,它似乎很难使用前缀,所以除非你能让它从签名中排除签名元素,否则我不确定还有其他可行的选项……对于现在找到这个线程的人来说,我推荐jetbrains的dotPeek而不是RedGate的产品。2008年,reflector无疑是最好的。现在,我肯定认为dotPeek更好。关于这个问题,有什么解决方案吗?我们如何设置前缀并验证它?这段代码不起作用,因为它调用
this.SignedInfo.GetXml()代码>在GetC14NDigest
中,它在生成的XML元素中没有所需的前缀。
CustomSignedXml signedXml = new CustomSignedXml();
.
.//your code
.
//compute the signature with the "ds" prefix
signedXml.ComputeSignature("ds");
//get the xml of the signature with the "ds" prefix
XmlElement xmlDigitalSignature = signedXml.GetXml("ds");