ctt-gob-es / FirmaXadesNet45

Librería desarrollada en C# para la generación de firmas XAdES
32 stars 58 forks source link

The namespace prefix 'ds' is not defined #6

Open federico-66 opened 6 years ago

federico-66 commented 6 years ago

Hi, I have problems validating the signature when there is an xpath transformation like this:

    XadesNet.Signature.Parameters.SignatureXPathExpression xp = new XadesNet.Signature.Parameters.SignatureXPathExpression();
    xp.Namespaces.Add("ds", "http://www.w3.org/2000/09/xmldsig#");
    xp.XPathExpression = "not(ancestor-or-self::ds:Signature)";
    @params.XPathTransformations.Add(xp);
<ds:Reference Id="Reference-2a7b6767-1fd1-41e4-9243-999f84db0698" URI="">
    <ds:Transforms>
        <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
                <ds:XPath>not(ancestor-or-self::ds:Signature)</ds:XPath>
            </ds:Transform>
    </ds:Transforms>
    <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
    <ds:DigestValue>c9Qnr5yGH/uhTpLnAE6R4Sp8B4tFmw+46++bb6dsujA=</ds:DigestValue>
</ds:Reference>

The following validation generates the error: "The namespace prefix 'ds' is not defined"

using (FileStream fs = new FileStream(signed_file, FileMode.Open)) {
    xadesService = new XadesNet.XadesService();
    _signatureDocument = xadesService.Load(fs)(0);
    XadesNet.Validation.ValidationResult result = xadesService.Validate(_signatureDocument);
    if (result.IsValid) {
        MessageBox.Show("Verification success.");
    } else {
        MessageBox.Show("Verification failed: " + result.Message);
    }
}

If validate the signed xml with third-party software, validation is ok

If remove the xpath transformation, validation is ok.

TIA

dnturbanismo commented 6 years ago

Hi,

Replace "CheckDigestedReferences" method with this code:

    private bool CheckDigestedReferences()
    {
        ArrayList references = m_signature.SignedInfo.References;

        Assembly System_Security_Assembly = Assembly.Load("System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
        Type CanonicalXmlNodeList_Type = System_Security_Assembly.GetType("System.Security.Cryptography.Xml.CanonicalXmlNodeList");
        ConstructorInfo CanonicalXmlNodeList_Constructor = CanonicalXmlNodeList_Type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);

        MethodInfo CanonicalXmlNodeList_Add = CanonicalXmlNodeList_Type.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
        Object refList = CanonicalXmlNodeList_Constructor.Invoke(null);

        CanonicalXmlNodeList_Add.Invoke(refList, new object[] { this.signatureDocument });

        Type Reference_Type = typeof(Reference);
        MethodInfo Reference_CalculateHashValue = Reference_Type.GetMethod("CalculateHashValue", BindingFlags.NonPublic | BindingFlags.Instance);

        for (int i = 0; i < references.Count; ++i)
        {
            Reference digestedReference = (Reference)references[i];

            byte[] calculatedHash = (byte[])Reference_CalculateHashValue.Invoke(digestedReference, new object[] { this.signatureDocument, refList });

            if (calculatedHash.Length != digestedReference.DigestValue.Length)
                return false;

            byte[] rgb1 = calculatedHash;
            byte[] rgb2 = digestedReference.DigestValue;
            for (int j = 0; j < rgb1.Length; ++j)
            {
                if (rgb1[j] != rgb2[j]) return false;
            }
        }

        return true;
    }

I need to test it in deep but I think this will works for you... please try it and let me know if works fine to upload the fix later.

I think this issue also affects to enveloped signatures validation.

federico-66 commented 6 years ago

I'm sorry, but it does not solve the problem. The error is generated when the CalculateHashValue method is invoked! I tried to modify the xpathExpression, removing the namespace and validation is ok !!! This is the StackTrace of the InnerException:

in MS.Internal.Xml.XPath.CompiledXpathExpr.UndefinedXsltContext.LookupNamespace(String prefix)
in MS.Internal.Xml.XPath.BaseAxisQuery.SetXsltContext(XsltContext context)
in MS.Internal.Xml.XPath.BooleanFunctions.SetXsltContext(XsltContext context)
in MS.Internal.Xml.XPath.BooleanFunctions.SetXsltContext(XsltContext context)
in MS.Internal.Xml.XPath.BooleanFunctions.SetXsltContext(XsltContext context)
in MS.Internal.Xml.XPath.CompiledXpathExpr.SetContext(IXmlNamespaceResolver nsResolver)
in MS.Internal.Xml.XPath.CompiledXpathExpr.SetContext(XmlNamespaceManager nsManager)
==>> in System.Security.Cryptography.Xml.XmlDsigXPathTransform.GetOutput() <<==
in System.Security.Cryptography.Xml.TransformChain.TransformToOctetStream(Object inputObject, Type inputType, XmlResolver resolver, String baseUri)
in System.Security.Cryptography.Xml.TransformChain.TransformToOctetStream(XmlDocument document, XmlResolver resolver, String baseUri)
in System.Security.Cryptography.Xml.Reference.CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList)"   string

I'm not an expert, but it seems that XmlDsigXPathTransform.GetOutput() return xml without namespace.

Thanks anyway

dnturbanismo commented 6 years ago

Ah ok...sorry, I think I forgot to send you something else...in my test case with an enveloped signature with an xpath transformation like yours, the validation is ok.

The problem is the namespace propagation...you can try before calculate the hash to add in the reference's PropagatedNameSpaces property the "ds" namespace.

Tomorrow I will review this...

dnturbanismo commented 6 years ago

Hi,

Replace "CheckXmldsigSignature" method by this one:

    public virtual bool CheckXmldsigSignature()
    {
        bool retVal = false;
        IEnumerable<XmlAttribute> namespaces = GetAllNamespaces(GetSignatureElement());

        if (this.KeyInfo == null)
        {
            KeyInfo keyInfo = new KeyInfo();
            X509Certificate xmldsigCert = GetSigningCertificate();
            keyInfo.AddClause(new KeyInfoX509Data(xmldsigCert));
            this.KeyInfo = keyInfo;
        }

        SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;

        if (description == null)
        {
            if (this.SignedInfo.SignatureMethod == "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
            {
                CryptoConfig.AddAlgorithm(typeof(Microsoft.Xades.RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
            }
            else if (this.SignedInfo.SignatureMethod == "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512")
            {
                CryptoConfig.AddAlgorithm(typeof(Microsoft.Xades.RSAPKCS1SHA512SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
            }
        }

        foreach (Reference reference in SignedInfo.References)
        {
            foreach (System.Security.Cryptography.Xml.Transform transform in reference.TransformChain)
            {
                if (transform.GetType() == typeof(XmlDsigXPathTransform))
                {
                    Type transform_Type = typeof(XmlDsigXPathTransform);
                    FieldInfo nsm_FieldInfo = transform_Type.GetField("_nsm", BindingFlags.NonPublic | BindingFlags.Instance);
                    XmlNamespaceManager nsm = (XmlNamespaceManager)nsm_FieldInfo.GetValue(transform);                        

                    foreach (var ns in namespaces)
                    {
                        nsm.AddNamespace(ns.LocalName, ns.Value);
                    }
                }
            }
        }

        retVal = this.CheckDigestedReferences();

        if (retVal == false)
        {
            throw new CryptographicException("CheckXmldsigSignature() failed");
        }

        var key = this.GetPublicKey();
        retVal = this.CheckSignedInfo(key);

        if (retVal == false)
        {
            throw new CryptographicException("CheckXmldsigSignature() failed");
        }

        return retVal;
    }

I hope this helps you...

federico-66 commented 6 years ago

Problem solved, thanks :-) and thank you very much for sharing the project

dnturbanismo commented 6 years ago

Great!!...

federico-66 commented 6 years ago

very great, thanks again :-)

peibol71 commented 3 years ago

If you want to sign using a xpath transformation like this, is already necessary to add this code in BuildDigestedReferences(), before call Reference_UpdateHashValue.Invoke.