If you are going to implement electronic invoicing in your application. NET, and you've decided on the format facturae, check that , to this day, the documentation and tools for working with this language is very limited.
find the table fields in PDF (keep her, you'll need) and the XSD schema format. Today there are 3 versions of facturae, 3.0, 3.1 and 3.2. The most current, 3.2, is still not widespread enough, so I recommend working (again, by day Today, May 2010) with version 3.1.
The XSD schema that we find things much easier, because with him and
xsd.exe tool we will generate a class whose instance we will generate the XML easily. To generate the *. cs need:
http://www.w3 .org / TR / XMLDSIG-core / XMLDSIG-core-schema.xsd
With all this, we run the command give us the expected file:
- Now we can import that file to our project and generate electronic invoices that easy:
- / / Create Object facturae (derived from XSD) Facturae bill = Facturae new ();
- / / Fill data object facturae your bill ....
/ / Remove
data file string file = "c: \\ temp \\ \\ pruebas.xml" FileStream fs = new FileStream (file, FileMode.Create);
serializer.Serialize (fs, invoices);
fs.Close (); serializer.Serialize (fs, invoices);
To fill the facturae object correctly, you should read the PDF containing the table fields, it tells you which fields are required, what they are and the format should be. And here is where we have a problem: there are some fields that have to have 2, 4 or 6 decimal places, but the type of data is a "Double", how do we specify that when generating the XML out exactly that number of decimal places ? Well, there are several solutions, mine is: will generate 3 classes, so that each class contains an attribute "Double." When serializing XML object of these classes, the numeric display with a fixed number of decimal places. 4 classes actually do: create a parent class to avoid repeating the attribute in the 3 classes: public abstract class
DoubleFixedDecimalType {protected double value;
public DoubleFixedDecimalType ()
{value = 0;} public DoubleFixedDecimalType ()
public DoubleFixedDecimalType (double value) { this.value = value;}
public DoubleTwoDecimalType(double value) : base(value) { }
[System.Xml.Serialization.XmlText ()]
public abstract string Value
{
get;
set;
}
}
public class DoubleTwoDecimalType : DoubleFixedDecimalType
{
public DoubleTwoDecimalType() : base() { }
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); }
}
}
As can be seen in the statements can be implicitly converted to an object "Double" to any instance of these classes, which makes us work because we can directly assign a numerical value.
now is replace the *. cs file generated, the data type "Double" of the properties must have a fixed number of decimal places (see table fields) for one of these 3.
To save you some work, here you have to download two files, generated xsd.exe and modified to get correct number of decimal places, for versions 3.1 and 3.2 facturae:
Click here to download
. At this
point you can generate your facturae. You can check the validity of your XML Web
the ministry of industry, tourism and trade
. Your file must successfully pass validation and formatting, if you read it right the table fields and the amount of your bill is correct, you must also successfully pass the validation accounts. But we have one important thing, the firm.
order to sign the XML, you need a digital certificate issued by a recognized certification authority. You order at the site of the factory
national currency and stamps
.
Once you have the certificate (*. cer file formats, *. pfx ...) you can take the class
X509Certificate2
to load into memory and perform the signature. At this point there are several ways to load the certificate: from physical file, looking for a certificate installed on your computer, including a list of certificates and giving the user the ability to choose one ... In this example we will read a certificate installed on your computer, using asp.net. The user running the asp.net does not have sufficient permissions to read the private key (required to sign) if you import the certificate with the import wizard (import the private key in the warehouse of user you are logged .)
To import a certificate and use it with asp.net, it we follow these steps (I repeat this is only necessary to use certificates with asp.net):
Sign in as administrator
Click on Start -> Run. Write mmc and hit Enter
Click on File -> Add / Remove Snap-in, and click on "Add"
Look in the "Certificates" and double click on it. Choose "Computer Account" and click Next
Choose the option depending on where the certificate is (local or remote) Press
end (see "Certificates" in the list of supplements added)
Click "OK"
Displays node "Certificates"
Make Right-click the folder "Personal" and select "All Tasks -> Import"
Click Next, find and select the certificate store
Choose the "Personal" to save the certificate and end
Now let's give permissions to the MachineKeys directory:
Navigate to the directory where you store the keys:
- C: \\ Documents and Settings \\ All Users \\ Application Data \\ Microsoft \\ Crypto \\ RSA \\ MachineKeys
- NO sure you are using the Simple File Sharing mode (Tools -> Folder Options ... -> View tab, at the very end)
- Click the right mouse button on the MachineKeys directory. Choose "Properties" and then the Security tab
- *** Add the IUSR_ user and give permissions for all (total control)
- Once we already have the certificate stored, we use it to sign the generated XML. We will sign with XAdES . But how? Well, I have not found the right way. But I can tell you how to do so to see what it looks like the file, but only a trial. Why? Because what we do with a library licensed for use with factOffice (it is a plugin to work with electronic invoices from MsOffice) and not allowed to use it outside the plug.
- From factOffice Web find a link to that library ( Download ) we can include in our project, again, for testing only. BouncyCastle.Crypto.dll file also need to be found in the source code factOffice. Once imported these two libraries, sign the xml is as simple as this:
- XmlDocument doc = new XmlDocument (); doc.PreserveWhitespace = True; doc.Load ("c: \\ temp \\ \\ pruebas.xml"); / / Get certificate X509Store store = new X509Store (StoreName.My, StoreLocation.LocalMachine) store.Open (OpenFlags . ReadOnly); X509Certificate2Collection certCollection = store.Certificates.Find (X509FindType.FindBySubjectName, "NAME OF THE COMPANY", false);
- if (certCollection.Count == 1) { / / Signing XAdES-EPES ( basic format with minimal information) XadesSignature XadesSignature xadesSignature = new (); xadesSignature.XadesEpes (doc, "Entity Name", certCollection [0], "Name Application "); / / Save version signed doc.Save (" c: \\ temp \\ \\ pruebas_firmado.xml ");}
/ / Close store.Close certificate store ();
Arrived At this point we have generated the XML file so that it would form validation, validation accounting and certificate validation. Soon I'll post how to perform the signature without using the library courtesy BackTrust so quietly signed to include XAdES in your application.
Updated 20/05/2010 As promised (although this is only said when the promise) and here I offer you a way of signing electronic invoices XAdES using libraries that can be distributed safely in your application.
What I have done is to use
IKVM.NET
to convert the Java libraries offered by the ministry of industry, tourism and trade, dll libraries. Using jar2ikvmc
can create a script for use with ikvmc, so that will take into account the dependencies of each jar file. Once converted
the jar to dll, as it tried to make a signature XAdES-EPES, as explained in the examples of the ministry, but did not work because the program did not recognize the political signature. The program was trying to access a file to dynamically load different types of signature policies, then create an instance of the class that manages the policy. But this process did not work with libraries converted to dll. So I had to modify the Java source to let you specify the classes for the signing policy at compile time (not as dynamic, but works. Net!), Compile and create the jar files, and re-create the dll's . All information to use / modify / recompile the source is
here.
After that I managed to sign invoices in facturae with XAdES-EPES, from a certificate stored in a pfx file. To do this you also follow these steps:
download the necessary files, click here to download
.
includes the following dll's in your project C # (find them in the previous download):
commons-logging-1.1
facturaE_adittional IKVM.
OpenJDK.Core
IKVM.OpenJDK.Security
IKVM.OpenJDK.Util
- IKVM.OpenJDK.XML.API
- IKVM.Runtime
- MITyCLibAPI-1.0.4-1.0.4 MITyCLibCert
- MITyCLibPolicy-
- 1.0.4-1.0.4 MITyCLibTrust
- MITyCLibTSA-1.0.4-1.0.4 MITyCLibXADES
- xmlsec-1.4.2-ADSI-1.0
- Note: Some users have informed me that the dll
- IKVM.OpenJDK.XML.Parse not automatically copied to the directory on the outcome of the solution. Take it into account if this is your case.
- includes the following "using" in code: using java.security; using java.io;
- using java.util; using java.security.cert; using javax.xml.parsers; using it. 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;
- Use this code to sign bills: private LoadXML Document (string path) {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); return dbf.newDocumentBuilder().parse(new BufferedInputStream(new FileInputStream(path)));
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));
storeManager.getSignCertificates List certificates = ();
/ / If we find the certificate ...
if (certificates.size () == 1) {
certificate = (X509Certificate) certificates.get (0);
/ / Getting private key associated with the certificate PrivateKey = storeManager.getPrivateKey
(certificate);
/ / Obtain the provider responsible for carrying out cryptographic provider = storeManager.getProvider (certificate);} return certificate;
} private void
btnFirmar_Click (object sender, EventArgs e) {
PrivateKey PrivateKey;
Provider provider;
LoadCertificate X509Certificate certificate = ("c: \\ temp \\ \\ certificado.pfx", "password", out PrivateKey, out provider);
/ / If we find the certificate ...
if (certificate! = Null) {
/ / Policy signature (with the Java libraries, this is defined at runtime)
es.mityc.javasign.trust.TrustExtendFactory.newInstance TrustFactory.instance = ();
es.mityc.javasign.trust.MyPropsTruster.getInstance TrustFactory.truster = ();
es.mityc.javasign.xml.xades.policy.facturae.Facturae31Manager PoliciesManager.POLICY_SIGN = new ();
PoliciesManager.POLICY_VALIDATION = es.mityc.javasign.xml.xades.policy.facturae.Facturae31Manager new ();
/ / Create data to sign
DataToSign DataToSign dataToSign = new ();
dataToSign.setXadesFormat (EnumFormatoFirma.XAdES_BES) / / XAdES- EPES
dataToSign.setEsquema (XAdESSchemas.XAdES_132)
dataToSign.setPolicyKey (facturae31 ") / / No matter what I say here, the signature policy defined above
dataToSign.setAddPolicy (true);
dataToSign.setXMLEncoding ( "UTF-8");
dataToSign.setEnveloped (true);
dataToSign.addObject (new ObjectToSign (new AllXMLToSign (), "Document Description", null, "text / xml", null));
dataToSign.setDocument (LoadXML ("c: \\ temp \\ \\ unsigned.xml"));
/ / Sign
Object [] res = new FirmaXML (). SignFiles (certificate, dataToSign, PrivateKey, provider);
/ / Save the file signature in the user's home
UtilidadTratarNodo.saveDocumentToOutputStream ((Document) res [0], new FileOutputStream ("c: \\ temp \\ \\ signed.xml"), true);
}}
If you run this code (changing the routes of the certificate, and xml) is generated you can validate xml
here and verify that the signature is correct. This example uses the signature policy 3.1 of facturae format, if you want to change company policy need to change the allocation PoliciesManager.POLICY_SIGN variables and PoliciesManager.POLICY_VALIDATION.
I have not tried to sign bills with another type of signature, as it works, maybe not. In the file that you downloaded above include all the libraries of the ministry converted to dll, and the modified sources if you want to make more modifications. If you need more IKVM.NET the libraries can be downloaded from its official website.
I hope this post has been helpful.
Any input, comments, or donations are welcome.
0 comments:
Post a Comment