martes, diciembre 17, 2013

Firma Electronica XML Banco Central del Ecuador BCE

Saludos en esta entrada publicare como firmar un archivo XML con la firma electrónica en archivo p12 proveída por el Banco Central del Ecuador.

Esta firma es la mas barata del mercado.

En formato:

Extención .p12
Estándar: X.509
Algoritmo: RSA-SHA1

Podre disponible para los lenguajes de programación Java y C#.

Java (Probado con el JDK de oracle):

1. Lo que tenemos que hacer es bajarnos las bibliotecas recomendadas inclusive en la documentacion del SRI.

Ingresamos a:

http://oficinavirtual.mityc.es/componentes/

Luego en descagas:

http://oficinavirtual.mityc.es/componentes/downloads.html

Nos bajamos los componentes incluyendo las dependencias.

componentes-1.1.7-with-deps.zip (Version al momento de escribir esta entrada).

2. Agregamos las referencias al proyecto donde estamos desarrollando. Preferiblemente incluir todas y las librerias de la firma.

Los jasrs estan dentro de la caperpeta deps y core.

3. De acuerdo a la documentacion debemos crear una clase que sirva como clase generica para la firma y una clase especifiquemos las lineas de codigo para nuestro tipo de firma.

En la documentacion del SRI explica que debemos usar el tipo de firma XAdES_BES. Por lo que la segunda clase nos ayudara para espicificar esto.

Clase FirmaGenerica (Clase abstracta como se muestra en la documentacion):

public abstract class GenericXMLSignature {


    //Path de la firma electronica
    private String pathSignature;
    //calve de la firma electronica
    private String passSignature;
    
    /**
    *
    * Ejecución del ejemplo. La ejecución consistirá en la firma de los datos
    * creados por el método abstracto createDataToSign mediante el
    * certificado declarado en la constante PKCS12_FILE. El
    * resultado del proceso de firma será almacenado en un fichero XML en el
    * directorio correspondiente a la constante OUTPUT_DIRECTORY
    * del usuario bajo el nombre devuelto por el método abstracto
    * getSignFileName
    *

    */
  
    /*Metodos Getters y Setters (Propiedades)*/
    public String getPathSignature() {
        return pathSignature;
    }

    public void setPathSignature(String pathSignature) {
        this.pathSignature = pathSignature;
    }

    public String getPassSignature() {
        return passSignature;
    }

    public void setPassSignature(String passSignature) {
        this.passSignature = passSignature;
    }
  
    protected void execute() {
    
        // Obtencion del gestor de claves
        KeyStore keyStore = getKeyStore();
      
        if(keyStore==null){
            System.err.println("No se pudo obtener almacen de firma.");
            return;
        }
        String alias=getAlias(keyStore);
      
        // Obtencion del certificado para firmar. Utilizaremos el primer
        // certificado del almacen.          
        X509Certificate certificate = null;
        try {
            certificate = (X509Certificate)keyStore.getCertificate(alias);
            if (certificate == null) {
                System.err.println("No existe ningún certificado para firmar.");
                return;
            }
        } catch (KeyStoreException e1) {
            e1.printStackTrace();
        }
    
        // Obtención de la clave privada asociada al certificado
        PrivateKey privateKey = null;
        KeyStore tmpKs = keyStore;
        try {
            privateKey = (PrivateKey) tmpKs.getKey(alias, this.passSignature.toCharArray());
        } catch (UnrecoverableKeyException e) {
            System.err.println("No existe clave privada para firmar.");
            e.printStackTrace();
        } catch (KeyStoreException e) {
            System.err.println("No existe clave privada para firmar.");
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            System.err.println("No existe clave privada para firmar.");
            e.printStackTrace();
        }
    
         // Obtención del provider encargado de las labores criptográficas
         Provider provider = keyStore.getProvider();

         /*
          * Creación del objeto que contiene tanto los datos a firmar como la
          * configuración del tipo de firma
          */
         DataToSign dataToSign = createDataToSign();

         /*
          * Creación del objeto encargado de realizar la firma
          */
         FirmaXML firma = new FirmaXML();

         // Firmamos el documento
         Document docSigned = null;
         try {
             Object[] res = firma.signFile(certificate, dataToSign, privateKey, provider);
             docSigned = (Document) res[0];
         } catch (Exception ex) {
             System.err.println("Error realizando la firma");
             ex.printStackTrace();
             return;
         }

         // Guardamos la firma a un fichero en el home del usuario
         String filePath = getPathOut() + File.separatorChar + getSignatureFileName();
         System.out.println("Firma salvada en en: " + filePath);
       
         saveDocumenteDisk(docSigned, filePath);
    }
    
     /**
      *
      * Crea el objeto DataToSign que contiene toda la información de la firma
      * que se desea realizar. Todas las implementaciones deberán proporcionar
      * una implementación de este método
      *

      *
     * @return El objeto DataToSign que contiene toda la información de la firma
      *         a realizar
      */
    protected abstract DataToSign createDataToSign();

    
    protected abstract String getSignatureFileName();
  
    protected abstract String getPathOut();
   

  
     protected Document getDocument(String resource) {
         Document doc = null;
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
         dbf.setNamespaceAware(true);
         File file = new File(resource);
         try {
            DocumentBuilder db = dbf.newDocumentBuilder();
          
            doc=db.parse(file);
         } catch (ParserConfigurationException ex) {
             System.err.println("Error al parsear el documento");
             ex.printStackTrace();
             System.exit(-1);
         } catch (SAXException ex) {
             System.err.println("Error al parsear el documento");
             ex.printStackTrace();
             System.exit(-1);
         } catch (IOException ex) {
             System.err.println("Error al parsear el documento");
             ex.printStackTrace();
             System.exit(-1);
         } catch (IllegalArgumentException ex) {
            System.err.println("Error al parsear el documento");
             ex.printStackTrace();
            System.exit(-1);
         }
         return doc;
     }

   
     private KeyStore getKeyStore()
     {
         KeyStore ks = null;
        try {
            ks = KeyStore.getInstance("PKCS12");
            ks.load(new FileInputStream(pathSignature), passSignature.toCharArray());
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
         return ks;
     }
   
   

     private static String getAlias(KeyStore keyStore)
     {
         String alias = null;
         Enumeration nombres;
           try {
               nombres = keyStore.aliases();
                 
               while(nombres.hasMoreElements())
               {
                   String tmpAlias = (String)nombres.nextElement();
                   if(keyStore.isKeyEntry(tmpAlias))
                   alias=tmpAlias;
               }
           }
           catch (KeyStoreException e) {
               e.printStackTrace();
           }
           return alias;
     }






   

 public static void saveDocumenteDisk(Document document,String pathXml)
     {
         try {
                 DOMSource source = new DOMSource(document);
                 StreamResult result = new StreamResult(new File(pathXml));

                 TransformerFactory transformerFactory = TransformerFactory.newInstance();
                 Transformer transformer;
                 transformer = transformerFactory.newTransformer();
                 transformer.transform(source, result);
         } catch (TransformerConfigurationException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
         } catch (TransformerException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
         }
     }
}


////Esta es la clase que extiende de Generic.
public class XAdESBESSignature extends GenericXMLSignature{


   
        private static String nameFile;
        private static String pathFile;
        /**
        *
        * Recurso a firmar
        *

        */
        private String fileToSign;
         
        /**
        *
        * Fichero donde se desea guardar la firma
        *

        */
        public XAdESBESSignature(String fileToSign) {
            super();
            this.fileToSign = fileToSign;
        }

        /**
           *
           * Punto de entrada al programa
           *

           *
           * @param args
           *            Argumentos del programa
           */
     

        public static void firmar(String xmlPath,String pathSignature,String passSignature,String pathOut,String nameFileOut)
        {           
              //Document document=UtilApplication.convertStringToDocument(xml);

              //String pathXml=UtilApplication.getTempPath()+"\\"+UUID.randomUUID().toString()+".xml";

              //UtilApplication.saveDocumenteDisk(document, pathXml);

           
           
              XAdESBESSignature signature = new XAdESBESSignature(xmlPath);
              signature.setPassSignature(passSignature);
              signature.setPathSignature(pathSignature);
              pathFile=pathOut;
              nameFile=nameFileOut;
             
              signature.execute();
          }
         
         
          @Override
          protected DataToSign createDataToSign() {
             
              DataToSign datosAFirmar = new DataToSign();

              datosAFirmar.setXadesFormat(es.mityc.javasign.EnumFormatoFirma.XAdES_BES);
              
              datosAFirmar.setEsquema(XAdESSchemas.XAdES_132);
              datosAFirmar.setXMLEncoding("UTF-8");
              datosAFirmar.setEnveloped(true);
              datosAFirmar.addObject(new ObjectToSign(new InternObjectToSign("comprobante"), "contenido comprobante", null, "text/xml", null));
              datosAFirmar.setParentSignNode("comprobante");

              Document docToSign = getDocument(fileToSign);
              datosAFirmar.setDocument(docToSign);

              return datosAFirmar;
          }
     

        @Override
        protected String getSignatureFileName() {
            return XAdESBESSignature.nameFile;
        }
       
        @Override
        protected String getPathOut() {
            return XAdESBESSignature.pathFile;
        }
   
}

//Como usar dicha clase

XAdESBESSignature.firmar(String xmlPath,String pathFirma,String clavefirma,String directoriosalida, String nombreArchivoSalida);

//Hasta aqui la firma.

Los jars de referencias y necesarios son:
bcmail-jdk16-1.45.jar
bcprov-jdk16-1.45.jar
bctsp-jdk16-1.45.jar
commons-codec-1.8.jar
commons-httpclient-3.0.1.jar
commons-lang-2.4.jar
commons-logging-1.1.3.jar
DNIeJCAProvider-1.4.jar
iaikPkcs11Wrapper-1.1.7.jar
jss-4.2.5.jar
MITyCLibAPI-1.1.7.jar
MITyCLibCert-1.1.7.jar
MITyCLibOCSP-1.1.7.jar
MITyCLibTrust-1.1.7.jar
MITyCLibTSA-1.1.7.jar
MITyCLibXADES-1.1.7.jar
serializer-2.7.1.jar
sunpkcs11-1.0.jar
xalan-2.7.1.jar
xml-apis-1.3.04.jar
xmlsec-1.4.2-ADSI-1.1.jar
xmlsec-1.5.5.jar


///Para c# al momento lo que he logrado es esto por cuestiones de tiempo no he logrado terminar si alguien tiene terminao hagamelo saber.

public class Signature
    {

        Random rnd = new Random(DateTime.Now.
Millisecond);

        public const string XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";

        public void Xml(String pathXml,String pathSignature,String passFirma)
        {
            //Declaro variable XMLDocument
            XmlDocument xmlDoc = new XmlDocument();
            // Cargo el documento en el xmlDoc
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.Load(pathXml);

            //Obtengo la firma en el Certificado X509
            X509Certificate2 uidCert = new X509Certificate2(pathSignature, passFirma, X509KeyStorageFlags.DefaultKeySet);

            //Inicializo el RSA
            RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)uidCert.PrivateKey;

            //Agrego el SgnedXml que permite firmar el xml
            SignedXml signedXml = new SignedXml(xmlDoc);

            // Add the key to the SignedXml document.
            signedXml.SigningKey = rsaKey;

            //signedXml.Signature.Id = newID("Signature");
           

            //Agregamos el metodo de firmado
            signedXml.SignedInfo.SignatureMethod = XmlDsigRSASHA1Url;

            //signedXml.SignedInfo.Id = newID("Signature-SignedInfo");

            // Create a reference to be signed.
            Reference reference = new Reference();
            //reference.Id = newID("SignedPropertiesID");
            reference.Uri = "";

            // Add an enveloped transformation to the reference.
            XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
            reference.AddTransform(env);

            // Add the reference to the SignedXml object.
            signedXml.AddReference(reference);


            // Add an RSAKeyValue KeyInfo (optional; helps recipient find key to validate).
            KeyInfo keyInfo = new KeyInfo();

            KeyInfoX509Data clause = new KeyInfoX509Data();
            clause.AddSubjectName(uidCert.Subject);
            clause.AddCertificate(uidCert);
            keyInfo.AddClause(clause);

            //keyInfo.Id = newID("Certificate1");

            signedXml.KeyInfo = keyInfo;

            // Compute the signature.
            signedXml.ComputeSignature();


            Boolean respuesta = signedXml.CheckSignature();
            System.Console.WriteLine(respuesta);

            // Get the XML representation of the signature and save
            // it to an XmlElement object.
            XmlElement xmlDigitalSignature = signedXml.GetXml();


            //XmlElement signature = signedXml.GetXml();
            foreach (XmlNode node in xmlDigitalSignature.SelectNodes(
                "descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']"))
            {
                node.Prefix = "ds";
            }


            System.Console.WriteLine(signedXml.GetXml().InnerXml);
            // Append the element to the XML document.
            xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));

            xmlDoc.Save(@"D:\Xml\firmado.xml");

        }


        private String newID(String prefix)
        {
            String newID = prefix + rnd.Next(1048576);
            newID = prefix + rnd.Next(1048576);
            return newID;
        }
    }


A mis amigos de los diferentes lenguajes que no sean java les recomiendo tratar de consumir los webservices del por medio de soap.  Existe un software llamado SOAPUI. que permite o muestra como se consume en webservice. http://sourceforge.net/projects/soapui/files/

En .net he tenido problemas si se agrega como referencia no se porque no llena los objetos de retorno cuando se consume.

Es mi experiencia si alguie lo tiene no dude en compartir.


Disculpas no puedo subir proyectos por la sutacion comercial que puedan dar al mismo mi intencion es dar directrices que lo puedan lograr.

Por favor si algo no funciona sean mas especificos. error exacto.

Mas información (Comercial): http://www.corvustec.com/index.php?option=com_content&view=article&id=58&Itemid=87

Consumir WebService SRI Ecuador

Saludos poco a poco iré actualizando esta entrada.


Los web services a consumir son los siguintes:


https://celcer.sri.gob.ec/comprobantes-electronicos-ws/RecepcionComprobantes?wsdl

https://celcer.sri.gob.ec/comprobantes-electronicos-
ws/AutorizacionComprobantes?wsdl

Para consumir desde java hay que usar el jar cliente-ws,jar la misma que esta disponible cuando se instala el cliente de los comprobantes electronicos del SRI.

Y usar las Clases RecepcionComprontesService AutorizacionComprobantesService


Los objetos con las respuestas son RespuestaSolicitud RespuestaComprobante.

No olvidar que si enviamos varias veces el comprobante en la autoirzacion obtendremos varias respuesta.


Para .net si solo se agrega como referencia no funciona.

Por lo pronto no he podido probar por cuestiones de tiempo con soap.

Al parecer HttpWebRequest permite consumir por medio de soap al webservice.

Recomomiendo ver la herramienta soapui si van por ese camino.

Mil disculpas no he podido probar para .net prometo darme un tiempo para eso.

La firma para .net no he logrado poner todos los tags necesarios. como uri y algunas cosas mas.

Talvez alguien logro transformar los jar en dll.

Si alguien quiere compartir algo adicional haganmelo saber.

Les dejo este enlace.

http://geekswithblogs.net/marcel/archive/2007/03/26/109886.aspx

Se debe hacer lo que dice en el punto 3.

Tengo problemas con mi soapui.

Alguien puede poner en los comentarios la peticion que genera el soapui. Del webservice del sr.


Mas información (Comercial): http://www.corvustec.com/index.php?option=com_content&view=article&id=58&Itemid=87