有没有办法在签名 XML 文档(.Net 中的 SignedXml 类)的签名上设置前缀?
所以而不是:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#>
...
</Signature>
我可以有以下内容:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#>
...
</ds:Signature>
有没有办法在签名 XML 文档(.Net 中的 SignedXml 类)的签名上设置前缀?
所以而不是:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#>
...
</Signature>
我可以有以下内容:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#>
...
</ds:Signature>
这是不可能的。如果您在签名后修改 XML,则可能无法验证,如上例所示。IMO 这是您必须忍受的 MSFT 数字签名实施中的一个缺陷。
很多人会说没有理由这样做,他们在技术上是正确的。但是,当您与大型供应商(即州政府或银行)打交道时,祝他们最终改变它。大多数参考实现都包含它。
更新:签名对 SignedInfo 元素中的所有内容进行签名,因此如果您在事后更新该元素,则签名不再有效。您已经“篡改”了消息。
首先,确实没有任何充分的理由这样做。这两种形式在功能上是等效的。任何表现良好的 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";
}
可以,但是需要修改SignedXml类,在获取SignedInfo节点的摘要值之前添加前缀。
ComputeSignature 方法将被修改以添加前缀参数
public void ComputeSignature(string prefix){...}
当这个方法被调用时,它通过消化 SignedInfo 节点的值来计算签名值,如果你得到这个没有“ds”前缀的值然后添加前缀你会得到一个无效的签名,所以你必须添加前缀在获取 signedinfo 节点的摘要值之前。
该摘要值是在 GetC14NDigest 方法中生成的,因此该方法将被修改为添加前缀参数并在获取摘要值之前添加前缀
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 方法,添加前缀参数,然后调用 SetPrefix 方法,该方法会将“ds”前缀添加到 Signature Xml 中的所有节点
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");