Si vas a implementar la facturación electrónica en tu aplicación .NET, y te has decidido por el formato facturaE, comprobarás que, a día de hoy, la documentación y las herramientas para trabajar con dicho lenguaje son muy escasas.
Si no sabes por donde empezar, estás leyendo las líneas que buscabas.
¿Por dónde empezamos? Bueno, la "facturaE" es un fichero XML firmado digitalmente. Por lo tanto lo primero que tenemos que hacer es generar ese XML. Como buen programador, lo primero que hay que hacer es leer la documentación oficial, así que en
http://www.facturae.es/es-ES/Documentacion/EsquemaFormato/Paginas/Index.aspx encontramos la tabla de campos en PDF (guárdatela, te hará falta) y el esquema en formato XSD. A día de hoy existen 3 versiones de facturaE, la 3.0, 3.1 y 3.2. La más actual, 3.2, todavía no está lo suficientemente extendida, por lo que recomiendo trabajar (repito, a día de hoy, Mayo de 2010) con la versión 3.1.
El esquema XSD que encontramos nos facilitará mucho las cosas, ya que con él y con la herramienta
xsd.exe vamos a generar una clase cuya instancia nos permitirá generar el XML de forma sencilla. Para generar el fichero *.cs necesitamos:
Con todo esto, podemos ejecutar el comando que nos dará el esperado fichero:
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\xsd" Facturaev31.xsd xmldsig-core-schema.xsd /c /n:FacturaElectronica
Ahora podemos importar ese fichero a nuestro proyecto y generar facturas electrónicas así de fácil:
//Crear objeto facturaE (obtenido a partir del XSD)
Facturae facturasE = new Facturae();
//Rellenar objeto facturaE con datos de tu factura
....
//Sacar datos a fichero
string fichero = "c:\\temp\\pruebas.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Facturae));
FileStream fs = new FileStream(fichero, FileMode.Create);
serializer.Serialize(fs, facturasE);
fs.Close();
Para rellenar el objeto facturaE correctamente, debes leer el PDF que contiene la tabla de campos; éste te indica qué campos son obligatorios, qué son, y el formato que deben tener. Y aquí es donde nos encontramos con un problema: hay algunos campos que han de tener 2, 4 ó 6 decimales, pero el tipo de datos es un "Double", ¿cómo especificamos que cuando se genere el XML salgan exactamente ese número de decimales? Bien, hay varias soluciones, la mía es la siguiente:
Generaremos 3 clases, de forma que cada clase contiene un atributo "Double". Al serializar en XML un objeto de estas clases, la representación numérica aparecerá con un número fijo de decimales. En realidad haremos 4 clases: crearemos un clase padre para no repetir el atributo en las 3 clases:
public abstract class DoubleFixedDecimalType
{
protected double value;
public DoubleFixedDecimalType()
{
value = 0;
}
public DoubleFixedDecimalType(double value)
{
this.value = value;
}
[System.Xml.Serialization.XmlText()]
public abstract string Value
{
get;
set;
}
}
public class DoubleTwoDecimalType : DoubleFixedDecimalType
{
public DoubleTwoDecimalType() : base() { }
public DoubleTwoDecimalType(double value) : base(value) { }
public static implicit operator double(DoubleTwoDecimalType o)
{
return o.value;
}
public static implicit operator DoubleTwoDecimalType(double value)
{
return new DoubleTwoDecimalType(value);
}
[System.Xml.Serialization.XmlText()]
public override string Value
{
get { return value.ToString("F2", System.Globalization.CultureInfo.InvariantCulture); }
set { this.value = System.Convert.ToDouble(value); }
}
}
public class DoubleFourDecimalType : DoubleFixedDecimalType
{
public DoubleFourDecimalType() : base() { }
public DoubleFourDecimalType(double value) : base(value) { }
public static implicit operator double(DoubleFourDecimalType o)
{
return o.value;
}
public static implicit operator DoubleFourDecimalType(double value)
{
return new DoubleFourDecimalType(value);
}
[System.Xml.Serialization.XmlText()]
public override string Value
{
get { return value.ToString("F4", System.Globalization.CultureInfo.InvariantCulture); }
set { this.value = System.Convert.ToDouble(value); }
}
}
public class DoubleSixDecimalType : DoubleFixedDecimalType
{
public DoubleSixDecimalType() : base() { }
public DoubleSixDecimalType(double value) : base(value) { }
public static implicit operator double(DoubleSixDecimalType o)
{
return o.value;
}
public static implicit operator DoubleSixDecimalType(double value)
{
return new DoubleSixDecimalType(value);
}
[System.Xml.Serialization.XmlText()]
public override string Value
{
get { return value.ToString("F6", System.Globalization.CultureInfo.InvariantCulture); }
set { this.value = System.Convert.ToDouble(value); }
}
}
Como se puede apreciar en las declaraciones, se puede convertir implícitamente un objeto "Double" a cualquier instancia de estas clases, lo cual nos facilita el trabajo ya que podemos asignar directamente un valor numérico.
Ahora queda reemplazar, en el fichero *.cs generado, el tipo de datos "Double" de las propiedades que deben tener un número fijo de decimales (ver tabla de campos) por alguno de estos 3.
Para ahorrarte algo de trabajo, aquí tienes para descargar dos ficheros, generados con xsd.exe y modificados para sacar correctamente el número de decimales, correspondientes a las versiones 3.1 y 3.2 de la facturaE:
Haz click aquí para descargar.
Llegados a este punto ya puedes generar tu facturaE. Puedes comprobar la validez de tu XML en la web del
ministerio de industria, turismo y comercio. Tu fichero debe pasar correctamente la validación de formato y, si has leído bien la tabla de campos y los importes de tu factura son correctos, también debe pasar con éxito la validación contable. Pero nos falta una cosa importante, la firma.
Para poder firmar el XML, necesitas un certificado digital expedido por una entidad certificadora reconocida. Puedes pedirlo en la web de la
fábrica nacional de moneda y timbre.
Una vez tengas el certificado (fichero en formato *.cer, *.pfx...) te podrás hacer uso de la clase
X509Certificate2 para cargarlo en memoria y realizar la firma. Llegados a este punto hay varias formas de cargar el certificado: a partir del fichero físico, buscando un certificado instalado en el equipo, presentando una lista de certificados y dando al usuario la posibilidad de elegir uno... En este ejemplo vamos a leer un certificado instalado en el equipo, usando asp.net. El usuario sobre el que se ejecuta asp.net no tiene suficientes permisos para leer la clave privada (necesaria para firmar) del certificado si lo importamos con el asistente de importación (importará la clave privada en el almacén del usuario con el que hemos iniciado sesión).
Para importar un certificado y poder usarlo con asp.net, lo haremos siguiendo estos pasos (repito que esto sólo es necesario para usar certificados con asp.net):
- Entra al sistema como administrador
- Haz click en Inicio -> Ejecutar. Escribe mmc y pulsa intro
- Pulsa en Archivo -> Agregar o quitar complemento, y haz click en "Agregar"
- Busca en la lista "Certificados" y haz doble click sobre él. Elige "cuenta de equipo" y pulsa siguiente
- Elige la opción en función de donde se encuentre el certificado (equipo local o remoto)
- Pulsa finalizar (aparece "Certificados" en la lista de complementos agregados)
- Pulsa "Aceptar"
- Despliega el nodo "Certificados"
- Haz click con el botón DERECHO en la carpeta "Personal" y elige "Todas las tareas -> Importar"
- Pulsa siguiente, busca y elige el certificado
- Elige el almacén "Personal" para guardar el certificado y finaliza
Ahora vamos a dar permisos al directorio MachineKeys:
- Navega hasta el directorio donde se almacenan las claves: C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys
- Asegúrate de NO estar usando el modo compartido simple de archivos (herramientas -> opciones de carpeta... -> Pestaña ver, al final del todo)
- Pulsa con el botón derecho en el directorio MachineKeys. Elige "Propiedades" y luego la pestaña "Seguridad"
- Añade el usuario IUSR_*** y dale permisos para todo (control total)
Una vez tenemos ya el certificado almacenado, podemos usarlo para firmar el XML generado. Vamos a firmarlo con
XAdES. Pero , ¿cómo? Bien, todavía no he dado con la forma correcta. Pero puedo decirte cómo hacerlo para que veas cómo queda el fichero, pero sólo a modo de prueba. ¿Por qué? Porque lo haremos con una librería licenciada para usarla con
factOffice (es un plugin para trabajar con facturas electrónicas desde MsOffice) y no se permite usarla fuera del complemento.
Desde la web de factOffice encontramos un enlace a dicha librería (
Descargar) que podemos incluir en nuestro proyecto, repito, para pruebas sólamente. También necesitaremos el archivo BouncyCastle.Crypto.dll que podemos encontrar en el código fuente de factOffice. Una vez importadas estas dos librerías, firmar el xml es tan sencillo como esto:
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load("c:\\temp\\pruebas.xml");
//Obtener certificado
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates.Find(X509FindType.FindBySubjectName, "NOMBRE DE LA ENTIDAD", false);
if (certCollection.Count == 1)
{
//Firmar con XAdES-EPES (formato básico con la mínima información)
XadesSignature xadesSignature = new XadesSignature();
xadesSignature.XadesEpes(doc, "Nombre Entidad", certCollection[0], "Nombre Aplicación");
//Guardar versión firmada
doc.Save("c:\\temp\\pruebas_firmado.xml");
}
//Cerrar almacén de certificados
store.Close();
Llegados a este punto tenemos generado el fichero XML de forma que pasaría la validación de formato, la validación contable y la validación del certificado.
Pronto postearé la forma de realizar la firma sin usar la librería cedida por BackTrust y así poder incluir tranquilamente el firmado de XAdES en tu aplicación.
Actualización 20/05/2010
Lo prometido es deuda (aunque esto sólo se dice cuando se cumple la promesa) y aquí os ofrezco una forma de firmar facturas electrónicas con XAdES usando librerías que se pueden distribuir tranquilamente en vuestra aplicación.
Lo que he hecho es usar
IKVM.NET para convertir las librerías Java, ofrecidas por el ministerio de industria, turismo y comercio, a librerías dll. Usando
jar2ikvmc podemos crear un script para usar con ikvmc, de forma que tendrá en cuenta las dependencias de cada fichero jar.
Una vez convertidos los jar a dll, tal cual, intenté hacer una firma XAdES-EPES, tal y como se explica en los ejemplos del ministerio, pero no funcionó debido a que el programa no reconocía la política de firma. El programa estaba intentando acceder a un fichero para cargar dinámicamente los distintos tipos de políticas de firma, para luego crear una instancia de la clase encargada de gestionar dicha política. Pero todo este proceso no funcionó con las librerías convertidas a dll. Así que tuve que modificar los fuentes java para permitir especificar las clases para la política de firma en tiempo de compilación (no es tan dinámico, pero ¡funciona en .net!), compilar y crear los ficheros jar, y volver a crear las dll's. Toda la información para usar/modificar/recompilar los fuentes se encuentra
aquí.
Una vez hecho esto he conseguido firmar facturas en formato facturaE, con XAdES-EPES, a partir de un certificado almacenado en un fichero pfx. Para hacerlo tú también sigue estos pasos:
- Incluye las siguientes dll's en tu proyecto C# (las encuentras en la descarga anterior):
- commons-logging-1.1
- facturaE_adittional
- IKVM.OpenJDK.Core
- IKVM.OpenJDK.Security
- IKVM.OpenJDK.Text
- IKVM.OpenJDK.Util
- IKVM.OpenJDK.XML.API
- IKVM.OpenJDK.XML.Parse
- IKVM.Runtime
- MITyCLibAPI-1.0.4
- MITyCLibCert-1.0.4
- MITyCLibPolicy-1.0.4
- MITyCLibTrust-1.0.4
- MITyCLibTSA-1.0.4
- MITyCLibXADES-1.0.4
- xmlsec-1.4.2-ADSI-1.0
Nota: Algunos usuarios me han informado de que la dll IKVM.OpenJDK.XML.Parse no se copia automáticamente al directorio del resultado de la solución. Llevadlo en cuenta por si es vuestro caso.
- Incluye los siguientes "using" en el código:
using java.security;
using java.io;
using java.util;
using java.security.cert;
using javax.xml.parsers;
using es.mityc.javasign.pkstore;
using es.mityc.javasign.pkstore.keystore;
using es.mityc.javasign.trust;
using es.mityc.javasign.xml.xades.policy;
using es.mityc.firmaJava.libreria.xades;
using es.mityc.javasign.xml.refs;
using es.mityc.firmaJava.libreria.utilidades;
using org.w3c.dom;
using sviudes.blogspot.com;
Usa este código para firmar facturas:
private Document LoadXML(string path)
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
return dbf.newDocumentBuilder().parse(new BufferedInputStream(new FileInputStream(path)));
}
private X509Certificate LoadCertificate(string path, string password, out PrivateKey privateKey, out Provider provider)
{
X509Certificate certificate = null;
provider = null;
privateKey = null;
//Cargar certificado de fichero PFX
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new BufferedInputStream(new FileInputStream(path)), password.ToCharArray());
IPKStoreManager storeManager = new KSStore(ks, new PassStoreKS(password));
List certificates = storeManager.getSignCertificates();
//Si encontramos el certificado...
if (certificates.size() == 1)
{
certificate = (X509Certificate)certificates.get(0);
// Obtención de la clave privada asociada al certificado
privateKey = storeManager.getPrivateKey(certificate);
// Obtención del provider encargado de las labores criptográficas
provider = storeManager.getProvider(certificate);
}
return certificate;
}
private void btnFirmar_Click(object sender, EventArgs e)
{
PrivateKey privateKey;
Provider provider;
X509Certificate certificate = LoadCertificate("c:\\temp\\certificado.pfx", "contraseña", out privateKey, out provider);
//Si encontramos el certificado...
if (certificate != null)
{
//Política de firma (Con las librerías JAVA, esto se define en tiempo de ejecución)
TrustFactory.instance = es.mityc.javasign.trust.TrustExtendFactory.newInstance();
TrustFactory.truster = es.mityc.javasign.trust.MyPropsTruster.getInstance();
PoliciesManager.POLICY_SIGN = new es.mityc.javasign.xml.xades.policy.facturae.Facturae31Manager();
PoliciesManager.POLICY_VALIDATION = new es.mityc.javasign.xml.xades.policy.facturae.Facturae31Manager();
//Crear datos a firmar
DataToSign dataToSign = new DataToSign();
dataToSign.setXadesFormat(EnumFormatoFirma.XAdES_BES); //XAdES-EPES
dataToSign.setEsquema(XAdESSchemas.XAdES_132);
dataToSign.setPolicyKey("facturae31"); //Da igual lo que pongamos aquí, la política de firma se define arriba
dataToSign.setAddPolicy(true);
dataToSign.setXMLEncoding("UTF-8");
dataToSign.setEnveloped(true);
dataToSign.addObject(new ObjectToSign(new AllXMLToSign(), "Descripcion del documento", null, "text/xml", null));
dataToSign.setDocument(LoadXML("c:\\temp\\unsigned.xml"));
//Firmar
Object[] res = new FirmaXML().signFile(certificate, dataToSign, privateKey, provider);
// Guardamos la firma a un fichero en el home del usuario
UtilidadTratarNodo.saveDocumentToOutputStream((Document)res[0], new FileOutputStream("c:\\temp\\signed.xml"), true);
}
}
Si ejecutas este código (cambiando las rutas del certificado, y los xml) se generará un xml que puedes validar
aquí y comprobar que la firma es correcta. Este ejemplo usa la política de firma del formato 3.1 de facturaE, si quieres cambiar la política de firma tendrás que cambiar la asignación de las variables PoliciesManager.POLICY_SIGN y PoliciesManager.POLICY_VALIDATION.
No he probado a firmar facturas con otro tipo de firma, igual funciona, igual no. En el fichero que puedes descargar más arriba incluyo todas las librerías del ministerio convertidas a dll, así como los fuentes modificados por si quieres hacer más modificaciones. Si necesitas más librerías de IKVM.NET las puedes descargar desde su web oficial.
Espero que este post haya sido de ayuda.