问题 .NET签名的XML前缀


有没有办法在签名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>

2615
2017-12-19 16:54


起源



答案:


首先,确实没有任何理由这样做。这两种形式在功能上是等同的。任何表现良好的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";
}

6
2017-12-19 20:41



我同意它是一样的,它应该工作。但是,我不确定如果没有ds前缀,我是否能够让另一方工作。生成sig之后更改前缀的简单行为是否会导致验证失败? - mjmcinto
它可能会破坏签名,具体取决于Signature元素是否包含在被签名的信息中(如果Transform不排除它,它将会中断)。所以这在所有可能的情况下都不起作用。 - Eric Rosenberger
如果你看一下Reflector中的SignedXml类,看起来几乎没有使用前缀,所以除非你能从签名中排除Signature元素,否则我不确定还有其他可行的选项...... - Eric Rosenberger
谢谢。你在哪里得到Reflector? - mjmcinto
red-gate.com/products/reflector。免费且绝对无价。 - Eric Rosenberger


答案:


首先,确实没有任何理由这样做。这两种形式在功能上是等同的。任何表现良好的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";
}

6
2017-12-19 20:41



我同意它是一样的,它应该工作。但是,我不确定如果没有ds前缀,我是否能够让另一方工作。生成sig之后更改前缀的简单行为是否会导致验证失败? - mjmcinto
它可能会破坏签名,具体取决于Signature元素是否包含在被签名的信息中(如果Transform不排除它,它将会中断)。所以这在所有可能的情况下都不起作用。 - Eric Rosenberger
如果你看一下Reflector中的SignedXml类,看起来几乎没有使用前缀,所以除非你能从签名中排除Signature元素,否则我不确定还有其他可行的选项...... - Eric Rosenberger
谢谢。你在哪里得到Reflector? - mjmcinto
red-gate.com/products/reflector。免费且绝对无价。 - Eric Rosenberger


它无法完成。如果您修改XML 签字后 它可能无法验证,这是上例中的情况。 IMO这是MSFT数字签名实现中的一个缺陷,您将不得不忍受。

很多人会说没有理由这样做,而且技术上是正确的。但是,当你与一个巨大的供应商(即州政府或银行)打交道时,祝他们好好改变它们。大多数参考实现包括它。

更新:签名标志 一切 在SignedInfo元素中,如果您在事后更新该元素,则签名不再有效。你已经“篡改”了这条消息。


8
2018-04-03 21:13



关于这个的任何解决方案,我们如何设置前缀并验证它? - Oxygen


可以这样做,但是在获取SignedInfo节点的摘要值之前,必须修改SignedXml类以添加前缀。

将修改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的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;
}

我将带着这些修改离开这里

CUSTOM CLASS

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");

0
2018-06-08 16:57