Validation of digital signature

Asked

Viewed 3,639 times

8

I am developing a software integration with the legal note webservice for issuance of electronic tax notes of the city of Porto Alegre.

After some difficulties I was able to fine tune the xml so that it conforms to the standard, but now I’m facing problems with the signature validation.

Signatures are validated using signed: https://www.receita.fazenda.gov.br/Aplicacoes/SSL/ATBHE/Assinadoc/ValidadorAssinaturas.app/valida.aspx

I am developing in C# and I used the "Signxmldocument" method that is contained in a tutorial posted by Microsoft: https://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.signedxml%28v=vs.110%29.aspx

Example of my signature:

<Signature Id="Ass_1_2015100000227"
    xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" />
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
        <Reference URI="12015100000227.xml">
            <Transforms>
                <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>pAukI83FOJt5xPAkuNfmaFkxGQ0=</DigestValue>
        </Reference>
    </SignedInfo>
<SignatureValue>vkgvOXOgFT4KiyMzOF+8iYf7wiorwG1SQao5y0F9AkvYBJI3EQHtHL4nOXRoAYOomaMpL/T30hNqmi50mOOgUu1EcYVjkpfnpVSmJMJTqcXUCbVkyYdLNayuZLkP9Q1tJqMcN6CG2j+huBDqQhfECD9Hv94TUtpg0TMMMohrFGA=</SignatureValue>
    <KeyInfo>
        <X509Data>
<X509Certificate>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</X509Certificate>
        </X509Data>
    </KeyInfo>
</Signature>

Of the signature data:

  • Digest Value is the hash representation of the URI tag found in Ference.
  • Tag <x509Data> data is the public key of my certificate (which is an A3)
  • The only value generated by the "Computesignature()" method is Signature Value which made me think that it would be the problem, but looking at other source codes that emit digital signature in C# all follow the same pattern.
  • I also did a check of the Digitial signature pattern on the website: www.W3.org/TR/xmldsig-core/ and realized that everything is correct.
  • In the signing message the only thing that appears in the description of the problem is: "The signing of the document is not valid".

So the question is, why is this signature not valid?

  • You used the 12015100000227.xml as an entrance to the SignXmlDocument? That one DigestValue was produced as? And what are you sending to the assinadoc, a single XML or more than one? (I ask because it seems to me that you are signing document A and keeping the signature on document B, which at first is ok, but depending on what the signed person expects it might be necessary that the signature be on the document itself; see that related question - unfortunately also no solution - for an example).

  • P.S. I don’t have a conclusive answer to give you, I’m just exploring the points where something could have gone wrong. This seems to be a common problem - from the point of view all questions about Nfe here on the website - but unfortunately I don’t have enough experience in the subject to better guide you... :(

  • 12015100000227.xml is the file itself (in this case I was doing a test on generating the saved xml and then referencing the file in the signature, it didn’t work either), I can change and put "http://www.microsoft.com" is just the hash, it is only there because it is mandatory. First I build all the xml and then I send the signatures (according to the developers forum of the legal note this is the most secure way). Sending yourself from an XML with multiple signatures. I’ll take a look at your links, thanks.

  • The hash "is only there because it is mandatory", it is a fundamental part of the signature algorithm! If you are calculating the wrong hash, the signature will not hit. You need: 1) identify what needs to be signed (probably an element within XML, maybe the infNFe, do not know - look in the documentation); 2) Reference the element to be signed (put #id_do_elemento in the URI of Reference); 3) Hash this specific element; 4) Sign this hash and put it on SignatureValue; 5) Place in X509Data the same certificate used to sign. By the way, that’s it.

  • I understand what you mean, but several times I tried to put #id_do_element however the Uri property of the Reference class does not accept this reference and there is an error in the XML validation when the "Computesignature" method is called. Reference Reference = new Reference(); Reference.Uri = "#" + "12015100000232"; signedXml.Addreference(Reference); Keyinfo ... signedXml.Computesignature(); -> "Poorly formed Reference element" The step by step you said helped me understand the whole process, I’ll keep trying alternatives.

  • 1

    I believe I have found the reference problem when I was instânciando the class Signedxml() I must pass the document until it adds the reference and it does not exist it will end up playing an exception so the correct is: Signedxml signedXml = new Signedxml(Mydocument); The clarification about the id and its respective element was priceless, thank you very much, with xmls so extensive some things go unnoticed, I will develop a few more lines and see if the validation passes this time.

  • Despite modifying the entire code and leaving the part of the Referral round, it still does not pass the validation of the federal revenue. Seeing an example: signedXml.Signingkey = cert.Privatekey; Where the private key is retrieved and assigned to the main signature object I believe you should generate a valid signature, the way I was doing was generating a new signedXml.Signingkey = new Rsacryptoserviceprovider(); for each signature, but the correct one must be with the private key. But since my A3 certification is being very difficult to recover the private key, I’ll keep trying...

  • I have just ruled out the possibility of retrieving the private key, at the url: http://www.pronova.com.br/wiki/index.php?title=FAQ_Certificaos_Digitais , it is clear that it is not possible to extract the private key from an A3 certificate: "According to ICP-Brazil standards, it is not allowed to export the private key from the device’s memory (token or smart card), so it is not possible to back up (backup) a type A3 certificate".

Show 3 more comments

2 answers

5

After wasting considerable time I found the reason:

I was saving the xml file like this:

XmlDocument xmlDoc = new XmlDocument();

xmlDoc.PreserveWhitespace = false;

xmlDoc.InnerXml = xml;

xmlDoc.Save(System.Configuration.ConfigurationManager.AppSettings["filePath"] + "generatedXml.xml");

He must be saved thus:

StreamWriter SW;
SW = File.CreateText(@"myxml.xml");
SW.Write(signedXmlData);
SW.Close();

The whole problem was in the xml encoding, by my experiences the Save method did not generate an xml with valid encoding, so one option that worked was to use the Streamwriter class and save the loaded file straight from the stream.

The method of digital signature:

    public string SignXml(string xml, string refUri, string signatureId)
    {
        X509Certificate2 _X509Cert = LoadCertificate(@"C:\mycert.cer");

        XmlDocument doc = CreateSignXml(xml);

        // Create a SignedXml object.
        SignedXml signedXml = new SignedXml(doc);
        signedXml.Signature.Id = signatureId;
        signedXml.SigningKey = _X509Cert.PrivateKey;
        signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";

        Reference reference = new Reference("#" + refUri);
        reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
        reference.AddTransform(new XmlDsigC14NTransform());

        signedXml.AddReference(reference);

        KeyInfo keyInfo = new KeyInfo();
        keyInfo.AddClause(new KeyInfoX509Data(_X509Cert));

        signedXml.KeyInfo = keyInfo;          

        signedXml.ComputeSignature();

        XmlElement xmlDigitalSignature = signedXml.GetXml();

        return xmlDigitalSignature.OuterXml;
    }

    private static XmlDocument CreateSignXml(string xml)
    {
        XmlDocument doc = new XmlDocument();

        doc.PreserveWhitespace = false;

        doc.LoadXml(xml);

        return doc;
    }

    private static X509Certificate2 LoadCertificate(X509Certificate2 X509Cert)
    {
        X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
        X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
        X509Certificate2Collection collection1 = (X509Certificate2Collection)collection.Find(X509FindType.FindBySubjectDistinguishedName, X509Cert.Subject.ToString(), false);

        return collection1[0];//Só olhar o índice em collection1 que está o certificado desejado, no meu caso ele era o primeiro.
    }
  • Good boy, I was running out here and I couldn’t, I did what you say and it worked...... vlw.

0

To calculate the Digestvalue

   1 add the xmlns namespace with the value http://www.portalfiscal.inf.br/nfe in the tag infNFe

   2 canonize the tag infNFe

   3 Calculate Digest using SHA1 and convert the result to Base64

To sign the note

   1 add the xmlns namespace with the value http://www.w3.org/2000/09/xmldsig# on the Signedinfo tag

   2 canonize the tag Signedinfo

   3 Sign the Signedinfo tag, convert the result to Base64

The signature model is this:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
        <Reference URI="#NFe00000000000000000000000000000000000000000000">
            <Transforms>
                <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
            <DigestValue>?</DigestValue>
        </Reference>
    </SignedInfo>
    <SignatureValue>?</SignatureValue>
    <KeyInfo>
        <X509Data>
            <X509Certificate>?</X509Certificate>
        </X509Data>
    </KeyInfo>
</Signature>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.