.NET签名XML前缀

11

有没有一种方法可以设置已签名 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>
3个回答

9
无法实现。如果您在XML文件签名之后进行修改,则可能无法验证该文件,这正是上面示例的情况。在我看来,这是微软数字签名实现中的一个缺陷,您必须接受它。
很多人会说没有理由这样做,他们在技术上是正确的。但是当您与一个大型供应商(例如州政府或银行)打交道时,要想让他们在他们的端口进行更改,好运。大多数参考实现都包括它。
更新:签名将签署SignedInfo元素中的所有内容,因此如果您在事后更新该元素,则签名将不再有效。您已经“篡改”了消息。

1
有没有关于如何设置前缀并进行验证的解决方案? - Oxygen

7
首先,实际上没有什么好的理由这样做。这两种形式在功能上是等效的。任何行为良好的XML处理器都会完全相同地处理它们。因此,除非你试图与未正确实现XML命名空间的应用程序通信,否则最好(在我看来)只需保留默认形式。(即使在这种情况下,如果可能的话,最好修复有缺陷的应用程序。)
话虽如此,您可以使用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";
}

5
我同意它们是相同的,而且应该能够工作。但我不确定在没有ds前缀的情况下,我能否让另一方使用它。更改sig后的前缀会不会导致验证失败? - mjmcinto
这取决于签名元素是否包含在被签名的信息中(如果变换没有将其排除,则会破坏签名),因此它可能会破坏签名。因此,在所有可能的情况下,这并不适用。 - Eric Rosenberger
如果你在Reflector中查看SignedXml类,它似乎基本上是硬编码为不使用前缀的,所以除非你可以让它从签名中排除Signature元素,否则我不确定是否有其他可行的选项... - Eric Rosenberger
对于现在找到这个帖子的人,我建议使用JetBrains的dotPeek而不是Red Gate的产品。在2008年,Reflector无疑是最好的。但现在,我肯定认为dotPeek更好。 - Bon

1
可以做到,但需要修改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方法,该方法将向Signature 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");

这段代码无法工作,因为它在GetC14NDigest中调用了this.SignedInfo.GetXml();,而生成的XML元素中缺少所需的前缀。 - AshleyS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接