EldoS | Feel safer!

Software components for data protection, secure storage and transfer

XML Signature KeyInfo

Also by EldoS: CallbackFilter
A component to monitor and control disk activity, track file and directory operations (create, read, write, rename etc.), alter file data, encrypt files, create virtual files.
#12872
Posted: 03/29/2010 03:20:43
by control tecnia (Standard support level)
Joined: 03/29/2010
Posts: 5

Hi:
I am using the example code of Santiago Hernadez, but "translated" to C#. The xml obtained looks good but when I verify the file against the validator from ministry I obtein an error "** Política desconocida: self:policy/general "

In the SignedSignatureProperties there is no SigningCertificate included and i think that it will be the problem.

I attach the code that I use
Thank you for all

(Hola:
Estoy usando el codigo de ejemplo de Santiago Hernandez, pero traducido a mano a C#. El fichero xml que obtengo tiene buena pinta pero cuando lo valido contra el validador de la agencia tributaria sale el error : "** Política desconocida: self:policy/general "

Lo unico que veo raro es que en el SignedSignatureProporties no aparece el elemento SigningCertificate. Quizas sea este el problema.

Muchas gracias, por tu codigo de ejemplo.)



Code
        static int firmar(string finicial, string ffinal, TElX509Certificate elcertificado)
        {

            TElXMLDOMDocument XML_Doc = new TElXMLDOMDocument();
            TElXMLReferenceList XML_Refs = new TElXMLReferenceList();
            TElXMLReference XML_RefDocu = new TElXMLReference();
            TElXMLReference XML_RefCert = new TElXMLReference();
            TElXMLSigner XML_Signer = new TElXMLSigner();
            TElXAdESSigner XML_Xades = new TElXAdESSigner();
            TElXMLKeyInfoX509Data XML_KeyData = new TElXMLKeyInfoX509Data(false);
            string sha1 = "http://www.w3.org/2000/09/xmldsig#sha1";
            FileStream F = null;
            FileStream Fsalida = null;

            try
            {
                F = new FileStream(finicial, FileMode.Open, FileAccess.Read);
                XML_Doc.LoadFromStream(F, "", true);
                if (!XML_Doc.Loaded)
                {
                    throw new Exception("no se puede abrir el fichero " + finicial);
                }
                XML_Signer.References = XML_Refs;
                XML_Signer.SignatureMethod = SBXMLSec.Unit.xmtSig;
                XML_Signer.SignatureType = SBXMLSec.Unit.xstEnveloped;
                XML_Signer.CanonicalizationMethod = SBXMLDefs.Unit.xcmCanon;
                XML_Signer.SignatureMethod = SBXMLSec.Unit.xsmRSA_SHA1;
                XML_Signer.IncludeKey = true;
                XML_Signer.XAdESProcessor = XML_Xades;
                XML_Signer.XAdESProcessor.XAdESVersion = SBXMLAdES.Unit.XAdES_v1_3_2;
                XML_Signer.XAdESProcessor.Included = SBXMLAdESIntf.Unit.xipSignerRole;
                XML_Signer.XAdESProcessor.SignerRole.ClaimedRoles.AddText(XML_Signer.XAdESProcessor.XAdESVersion, XML_Doc, "Emisor");

                //revisar
                string s = "http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf";
                XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier = s;


                if (s.Length > 0)
                {
                    if (s.Substring(0, 4).ToLower() == "urn:")
                        XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtOIDAsURN;
                    else
                        XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtOIDAsURI;
                }
                else
                    XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtNone;

                XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtOIDAsURI;


                //revisar
                string s2 = "Política de firma electrónica para facturación electrónica con formato Facturae";

                XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Description = s2;

                XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod = sha1;
                //revisar falta el digestmethjod
                string hashcalculado = "3a18b197aba90fa6aff0dee912f0c006110bea13";
                string hash64 = SBUtils.Unit.Base64EncodeString(hashcalculado, false);
                //XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod = SBXMLSec.Unit.DigestMethodToURI(SBXMLSec.Unit.xdmSHA1);
                string x;
                x = SBUtils.Unit.Base64DecodeString(hash64);
                XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestValue = SBUtils.Unit.BytesOfString(x);


                DateTime AHORA;
                AHORA = DateTime.Now;
                AHORA = AHORA.AddDays(-1);
                XML_Signer.XAdESProcessor.SigningTime = AHORA;



                XML_Signer.XAdESProcessor.Generate();
                XML_Signer.UpdateReferencesDigest();

                XML_KeyData.IncludeKeyValue = true;
                XML_KeyData.Certificate = elcertificado; //revisar

                XML_Signer.KeyData = XML_KeyData;
                XML_RefDocu.DigestMethod = SBXMLSec.Unit.xdmSHA1;
                XML_RefDocu.URINode = XML_Doc.DocumentElement;
                XML_RefDocu.URI = "";
                XML_RefDocu.TransformChain.Add(new TElXMLEnvelopedSignatureTransform());
                XML_Refs.Add(XML_RefDocu);
                XML_Signer.UpdateReferencesDigest();

                XML_RefCert.URI = "#Certificate1";
                XML_RefCert.DigestMethod = SBXMLSec.Unit.xdmSHA1;
                XML_Refs.Add(XML_RefCert);



                XML_Signer.Sign();
                XML_Signer.Signature.KeyInfo.ID = "Certificate1";
                /*lo añado yo por mi cuenta*/
                //XML_Signer.UpdateReferencesDigest();
                /*fin lo añado yo por mi cuenta*/


                TElXMLDOMNode XML_Nodo;
                XML_Nodo = XML_Doc.DocumentElement;
                XML_Signer.Save(ref XML_Nodo);




                Fsalida = new FileStream(ffinal, FileMode.Create, FileAccess.ReadWrite);
                XML_Doc.SaveToStream(Fsalida, SBXMLDefs.Unit.xcmNone, "");
                /*SBXMLDefs.Unit.xcmNone, cbCharset.Text);
                */


            }
            catch (Exception E)
            {
                System.Console.WriteLine(E.Message);
                System.Console.ReadLine();
            }
            return 0;
        }
#12873
Posted: 03/29/2010 04:25:23
by Sergio Hernandez (Standard support level)
Joined: 03/03/2008
Posts: 14

NOTE: Sorry I am answering in Spanish: Short answer in english at the end for convenience.

Hola, el certificado es obligatorio, si no está presente, el validador te va a dar un error seguro, lo que ya no se a ciencia cierta es si el error que envías es por eso o por otra cosa más básica.

Te comento esto porque, en el ejemplo, la descripción de la política de firmado aparece "convertido" a HTML, es decir, los espacios en blanco y acentos están cambiados por cosas raras (la i acentudad de politica aparece como í pero DEBERIA ser en tu codigo una i acentuada)

En realidad, en mi codigo NO estába así, se agrego solo al copiarlo en el foro, y quizás, si tu lo pusiste tal cual en tu código, ese sea el error que te reporta.

//revisar
string s2 = "Política de firma electrónica para facturación electrónica con formato Facturae";

Por cierto, el ejemplo era mio, aunque no tiene importancia en absoluto. El hilo lo abrió Santiago y ha posteado todo el código que se le ha pedido, lo que ocurre es que yo programé todo "de un tirón" y por eso me fue sencillo hacer un corta-pega, cosa que intuyo que él no lo tendría tan fácil si lo ha dividido en procedures reutilizados y esas cosas (es lo poco bueno de se un novato en XML).

Short answer in English: Certificate is mandatory, but also check your string s2 definition, as an "acuted i" appears on this forum as "í" and it should be like this in your code (you need to code it with acute i, spaces, and so on).
#12874
Posted: 03/29/2010 04:27:53
by Sergio Hernandez (Standard support level)
Joined: 03/03/2008
Posts: 14

Vaya, ahora la i acentuada va y aparece como debe... cuando la pones dentro de un bloque de código, el forum es cuando te la pasa a html:

Code
Esta es una i acentuada vista en un bloque de codigo: í
#12875
Posted: 03/29/2010 04:45:49
by Sergio Hernandez (Standard support level)
Joined: 03/03/2008
Posts: 14

A couple of hints:

In my company site (http://www.hcsoft.net/lab/index.php?zona=actual&hoja=archivos) you have a free app "HCVisor.exe", a viewer for our own graphic format, that also allows you to sign XML files using FacturaE politic (and PDF btw), so you can check your code output against it if you want (the exe is just an installer with another exe inside, the viewer itself, it doesn't "install" nothing more, just unzip the real exe and register a filetype, and of course no spyware or similar, just a delphi executable).

Note it doesn't let you sign with any crypto card, so no way to use a eDNI to sign files with this app... maybe I will add it to our apps. and copy it to the viewer some day.

Secondly, in a previous post i said FacturaE doesn't validate a file with more than one signature node: It is ok, the format definition XSD states it, but you can have many counter singnatures inside that unique signature node, so naming the certificates as 1, 2, 3... is still a good idea if you plan to let your app do counter singning when recieving and validating a bill.
#12876
Posted: 03/29/2010 06:00:23
by control tecnia (Standard support level)
Joined: 03/29/2010
Posts: 5

Thank you Sergio:
In my code the i with acute is written just like and i with acute not a & # 237.
I attach the fragment of the xml that is generated when I used the code above.

I've tried to see the code in the HCVisor.exe but when i´ve tried to execute i´ve obteined a "Error al iniciar la aplicación porque no se encontró fbclient.dll. La reinstalación de la aplicación puede solucionar el problema. ".
Perhaps it´s because i don´t have Delphi.

(Muchas gracias Sergio:
Perdona por haberme equivocado al poner tu nombre. En el codigo que uso las i acentuadas son eso: i acentuadas y no las entidades de caracter xml & # 237) (Eso ya me lo imagine que deberia cambiarlo y puse lo caracteres de acentos correspondienes.).
Adjunto el fragmento xml que obtengo al utilizar el codigo indicado, por si te da alguna pista.

En cuanto al HCVisor lo he descargado e instalado pero al intentar ejecutarlo me sale el error "Error al iniciar la aplicación porque no se encontró fbclient.dll. La reinstalación de la aplicación puede solucionar el problema." Me imagino que es debido a que no tengo Delphi en mi ordenador. Yo utilizo C#.

No se como darte las gracias por:
- Haberte tomado el tiempo de responder a mi pregunta
- Haber publicado el codigo de ejemplo.
porque sin el estaba mas perdido que un pulpo en un garage (esto no se como traducirlo al ingles))

Muchas gracias de nuevo

#12877
Posted: 03/29/2010 06:05:29
by control tecnia (Standard support level)
Joined: 03/29/2010
Posts: 5

Hi:
In the previos post I´ve tried to attach the fragment of the xml as a printscreen, but I hav´nt done

You can see it in http://www.controltecnia.com/images/Dibujo1.JPG

(En el post anterior he intentado adjuntar la imagen del xml que consigo pero no he sabido hacerlo.
En http://www.controltecnia.com/images/Dibujo1.JPG esta el enlace correspondiente)

[URL=http://www.controltecnia.com/images/Dibujo1.JPG]Ejemplo[/URL]

Thank you
#12880
Posted: 03/29/2010 07:01:21
by Sergio Hernandez (Standard support level)
Joined: 03/03/2008
Posts: 14

De nada por tus gracias!

Cuando me toco "sufrir" todo esto, en parte por culpa de que era novatisimo en XML y en parte porque la información de la web de facturae esta un poco "dejada" en algunos puntos, tuve claro que no tenía sentido quedarme para mi lo que encontrase (aunque hacemos programas "de pago" tenemos mentalidad open source dentro de lo posible). Me alegra mucho ver que le sacas algún provecho a mi esfuerzo.

Al grano...

Mirando tu XML y el mio, veo que el HASH del PDF de política de firmado tuyo es erróneo, ESE es el problema (yo lo sufrí y me costo horrores detectarlo)!

Debería aparecerte en el XML así:

Code
<ds:DigestValue>Ohixl6upD6av8N7pEvDABhEL6hM=</ds:DigestValue>


Este valor base64 es el correcto, confirmado con varias fuentes y calculándolo a partir del PDF directamente, si en tu código se te genera otro, algo está mal.

El problema te viene de que el hash del que partes esta en hexadecimal, con lo que antes de convertirlo a base64, tienes que pasarlo primero a un array de bytes (o como se llame en C), o bien copiarte el que te envío en Base64 y listo!

Tienes facturaes de ejemplo fiables aquí (con ese hash correcto, claro):
http://inza.wordpress.com/2010/01/16/ejemplo-de-factura-en-ubl-cii-facturae/

Fijate que, sin animo de ofender, los ejemplos de Inza tienen un error: El role del firmante pone "signer", pero la política de firmado no admite ese role, solo uno de entre 3 posibles (es un detalle que el validador de facturae no te detecta, en su día avise tanto al blog que te envio como a la gente de FacturaE, pero ninguno de los dos me contesto, la verdad).

English digest: The PDF hash is in hexadecimal format, you need to convert it to binary (array of bytes in delphi) and then format it to base64 (or copy the correct one i supply).
#12888
Posted: 03/30/2010 03:36:21
by control tecnia (Standard support level)
Joined: 03/29/2010
Posts: 5

Hi Sergio:
You were ok!, the problem was the hash.
I´ve changed the code to assign the proper value to the SigPolicyHash.DigestValue
Here is the code in C#.

(Gracias Sergio:
¡Tenias toda la razon!. El problema estaba en el valor que hay que asignar al
SigPolicyHash.DigestValue.
Adjunto el codigo que ya funciona y que genera ficheros que valida ok el validador de la agencia tributaria.
¡Sergio tienes pagadas una cañas si pasas por aqui! Muchisimas gracias
)


Code
        static int firmar(string finicial, string ffinal, TElX509Certificate elcertificado)
        {

            TElXMLDOMDocument XML_Doc = new TElXMLDOMDocument();
            TElXMLReferenceList XML_Refs = new TElXMLReferenceList();
            TElXMLReference XML_RefDocu = new TElXMLReference();
            TElXMLReference XML_RefCert = new TElXMLReference();
            TElXMLSigner XML_Signer = new TElXMLSigner();
            TElXAdESSigner XML_Xades = new TElXAdESSigner();
            TElXMLKeyInfoX509Data XML_KeyData = new TElXMLKeyInfoX509Data(false);
            string sha1 = "http://www.w3.org/2000/09/xmldsig#sha1";
            FileStream F = null;
            FileStream Fsalida = null;

            try
            {
                F = new FileStream(finicial, FileMode.Open, FileAccess.Read);
                XML_Doc.LoadFromStream(F, "", true);
                if (!XML_Doc.Loaded)
                {
                    throw new Exception("no se puede abrir el fichero " + finicial);
                }
                XML_Signer.References = XML_Refs;
                XML_Signer.SignatureMethod = SBXMLSec.Unit.xmtSig;
                XML_Signer.SignatureType = SBXMLSec.Unit.xstEnveloped;
                XML_Signer.CanonicalizationMethod = SBXMLDefs.Unit.xcmCanon;
                XML_Signer.SignatureMethod = SBXMLSec.Unit.xsmRSA_SHA1;
                XML_Signer.IncludeKey = true;
                XML_Signer.XAdESProcessor = XML_Xades;
                XML_Signer.XAdESProcessor.XAdESVersion = SBXMLAdES.Unit.XAdES_v1_3_2;
                XML_Signer.XAdESProcessor.Included = SBXMLAdESIntf.Unit.xipSignerRole;
                XML_Signer.XAdESProcessor.SignerRole.ClaimedRoles.AddText(XML_Signer.XAdESProcessor.XAdESVersion, XML_Doc, "Emisor");

                //revisar
                string s = "http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf";
                XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier = s;


                if (s.Length > 0)
                {
                    if (s.Substring(0, 4).ToLower() == "urn:")
                        XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtOIDAsURN;
                    else
                        XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtOIDAsURI;
                }
                else
                    XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtNone;

                XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtOIDAsURI;


                //revisar
                string s2 = "Pol&#237;tica de firma electr&#243;nica para facturaci&#243;n electr&#243;nica con formato Facturae";

                XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Description = s2;

                XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod = sha1;
                string hashcalculado = "3a18b197aba90fa6aff0dee912f0c006110bea13";

                int longitud;
                longitud = hashcalculado.Length / 2;
                byte[] y = new byte[longitud];
                string a, b, junto;
                int valor;
                for (int i = 0; i < longitud; i++)
                {
                    a = hashcalculado.Substring(i * 2 + 0, 1);
                    b = hashcalculado.Substring(i * 2 + 1, 1);
                    junto = a + b;
                    valor = Int32.Parse(junto, System.Globalization.NumberStyles.AllowHexSpecifier);
                    y[i] = (byte)valor;
                }
                XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestValue = y;//SBUtils.Unit.BytesOfString(x);



                DateTime AHORA;
                AHORA = DateTime.Now;
                AHORA = AHORA.AddDays(-1);
                XML_Signer.XAdESProcessor.SigningTime = AHORA;



                XML_Signer.XAdESProcessor.Generate();
                XML_Signer.UpdateReferencesDigest();

                XML_KeyData.IncludeKeyValue = true;
                XML_KeyData.Certificate = elcertificado; //revisar

                XML_Signer.KeyData = XML_KeyData;
                XML_RefDocu.DigestMethod = SBXMLSec.Unit.xdmSHA1;
                XML_RefDocu.URINode = XML_Doc.DocumentElement;
                XML_RefDocu.URI = "";
                XML_RefDocu.TransformChain.Add(new TElXMLEnvelopedSignatureTransform());
                XML_Refs.Add(XML_RefDocu);
                XML_Signer.UpdateReferencesDigest();

                XML_RefCert.URI = "#Certificate1";
                XML_RefCert.DigestMethod = SBXMLSec.Unit.xdmSHA1;
                XML_Refs.Add(XML_RefCert);



                XML_Signer.Sign();
                XML_Signer.Signature.KeyInfo.ID = "Certificate1";


                TElXMLDOMNode XML_Nodo;
                XML_Nodo = XML_Doc.DocumentElement;
                XML_Signer.Save(ref XML_Nodo);




                Fsalida = new FileStream(ffinal, FileMode.Create, FileAccess.ReadWrite);
                XML_Doc.SaveToStream(Fsalida, SBXMLDefs.Unit.xcmNone, "");


            }
            catch (Exception E)
            {
                System.Console.WriteLine(E.Message);
                System.Console.ReadLine();
            }
            return 0;
        }
#31758
Posted: 12/24/2014 02:53:32
by Diego Galindo Saeta (Basic support level)
Joined: 12/24/2014
Posts: 1

Muchísimas gracias por vuestra aportación. Gracias a vuestro trabajo y a este componente he visto la luz después de muchísimas horas buscando.
#32679
Posted: 03/20/2015 03:14:25
by Mikel  (Basic support level)
Joined: 03/20/2015
Posts: 1

Hi Sergio.

Thanks for you code.

We are trying sing a facturae whith your code and when we try validate de result in the page http://sedeaplicaciones2.minetur.gob.es/FacturaE/, it fails and report "Firma inválida (firma y/o certificados alterados)".

We have generated a signed facturae with the spanish government's application (http://www.facturae.gob.es/formato/Paginas/descarga-aplicacion-escritorio.aspx)
and we have found some diferences:

1st:
With your code:

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-1363048386">

with the application:

<ds:Signature xmlns:etsi="http://uri.etsi.org/01903/v1.3.2#" Id="Signature994710">

2nd: No etsi prefix
With your code:
<QualifyingProperties Target="#Signature994710">

with the application:
<etsi:QualifyingProperties Target="#Signature994710">

3th
With your code:
There is no node <etsi:SignedDataObjectProperties>

We have tried to solve this adding some code:

Code
SignedDataObjectProperties := TElXMLSignedDataObjectProperties.Create(XML_Signer.XAdESProcessor.XAdESVersion);

        XML_Signer.XAdESProcessor.QualifyingProperties.SignedProperties.SignedDataObjectProperties:=SignedDataObjectProperties;
        DataObjectFormats := TElXMLDataObjectFormat.Create(XML_Signer.XAdESProcessor.XAdESVersion);

        DataObjectFormats.Description     := 'Factura electrónica';
        DataObjectFormats.MimeType        := 'text/xml';
        DataObjectFormats.ObjectReference := '#Ref1';
        XML_Signer.XAdESProcessor.QualifyingProperties.SignedProperties.SignedDataObjectProperties.DataObjectFormats.Add(DataObjectFormats);

XML_Signer.XAdESProcessor.QualifyingProperties.XAdESPrefix := 'etsi';


after XML_Signer.XAdESProcessor.Generate(XAdES_EPES);

But the result of validation is the same.
Also by EldoS: Callback File System
Create virtual file systems and disks, expose and manage remote data as if they were files on the local disk.

Reply

Statistics

Topic viewed 13691 times

Number of guests: 2, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!