diff --git a/.gitignore b/.gitignore
index a1c2a23..3c8187e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,81 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
\ No newline at end of file
diff --git a/README.md b/README.md
index fb66b23..ab9ef58 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,12 @@
# firma-factura-electronica-siat-java
Firmado de documentos XML para Factura electrónica SIAT Bolivia
+
+## Build
+[IntelliJ IDEA](https://www.jetbrains.com/es-es/idea/)
+
+## Documentation
+[Factura Electrónica -SIAT](https://siatinfo.impuestos.gob.bo/index.php/facturacion-en-linea/factura-electronica)
+
+## Authors
+[marcelo.romero](https://siatinfo.impuestos.gob.bo/index.php/facturacion-en-linea/firma-digital/firmado-de-xml)
+[Diego Guillen](https://www.linkedin.com/in/diegoezequielguillen)
\ No newline at end of file
diff --git a/TestFirma/.idea/.gitignore b/TestFirma/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/TestFirma/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/TestFirma/.idea/compiler.xml b/TestFirma/.idea/compiler.xml
new file mode 100644
index 0000000..0d416f9
--- /dev/null
+++ b/TestFirma/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestFirma/.idea/encodings.xml b/TestFirma/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/TestFirma/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestFirma/.idea/jarRepositories.xml b/TestFirma/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/TestFirma/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestFirma/.idea/misc.xml b/TestFirma/.idea/misc.xml
new file mode 100644
index 0000000..accd629
--- /dev/null
+++ b/TestFirma/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestFirma/.idea/uiDesigner.xml b/TestFirma/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/TestFirma/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/TestFirma/pom.xml b/TestFirma/pom.xml
new file mode 100644
index 0000000..62bbe35
--- /dev/null
+++ b/TestFirma/pom.xml
@@ -0,0 +1,70 @@
+
+
+ 4.0.0
+ bo.sin
+ TestFirma
+ 1.0-SNAPSHOT
+ jar
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+ org.hamcrest
+ hamcrest-core
+ 1.3
+ test
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.56
+
+
+ org.bouncycastle
+ bcpkix-jdk15on
+ 1.56
+
+
+ org.apache.santuario
+ xmlsec
+ 2.0.5
+ jar
+
+
+ commons-io
+ commons-io
+ 2.5
+ test
+ jar
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.36
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.36
+
+
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+ src/main/resources
+
+
+
+
\ No newline at end of file
diff --git a/TestFirma/src/main/java/bo/sin/testfirma/Firmador.java b/TestFirma/src/main/java/bo/sin/testfirma/Firmador.java
new file mode 100644
index 0000000..6523772
--- /dev/null
+++ b/TestFirma/src/main/java/bo/sin/testfirma/Firmador.java
@@ -0,0 +1,177 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package bo.sin.testfirma;
+
+/**
+ *
+ * @author marcelo.romero
+ */
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.xml.security.Init;
+import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.signature.XMLSignature;
+import org.apache.xml.security.transforms.Transforms;
+import org.apache.xml.security.utils.Constants;
+import org.apache.xml.security.utils.ElementProxy;
+import org.apache.xml.security.utils.XMLUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ * @author
+ */
+
+public class Firmador {
+ // http://stackoverflow.com/questions/7224626/how-to-sign-string-with-private-key
+ private static Firmador instancia;
+ private String ALG = "SHA1withRSA";
+ static {
+ Init.init();
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ /**
+ * Obtener un firmador por defecto.
+ *
+ * @return un Firmador.
+ */
+
+ public static Firmador getInstance() {
+ if (instancia == null) {
+ instancia = new Firmador();
+ }
+ return instancia;
+ }
+
+ private Firmador() {
+ }
+
+ //// Todo: Colocar en un solo directorio la llave privada con la publica
+
+ /**
+ * Esta funcion añade una firma a un documento XML.
+ *
+ * @param datos Documento a firmar XML.
+ * @param priv Clave privada.
+ * @param cert Certificado del firmante.
+ * @return Retorna el documento con una firma.
+ * @throws ParserConfigurationException
+ * @throws IOException
+ * @throws SAXException
+ * @throws XMLSecurityException
+ */
+
+ public static byte[] firmarDsig(byte[] datos, PrivateKey priv, X509Certificate... cert) throws ParserConfigurationException, IOException, SAXException, XMLSecurityException {
+ ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, "");
+ Document documento = leerXML(datos);
+ Element root = (Element) documento.getFirstChild();
+ documento.setXmlStandalone(false);
+ XMLSignature signature = new XMLSignature(documento, null, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256);
+ root.appendChild(signature.getElement());
+ Transforms transforms = new Transforms(documento);
+ transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
+ transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
+ signature.addDocument("", transforms, MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256);
+ if (cert != null) {
+ signature.addKeyInfo(cert[0]);
+ }
+ signature.sign(priv);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ XMLUtils.outputDOMc14nWithComments(documento, baos);
+ return baos.toString().getBytes();
+ }
+
+ public static Document leerXML(byte datos[]) throws ParserConfigurationException, IOException, SAXException {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder;
+ factory.setNamespaceAware(true);
+ builder = factory.newDocumentBuilder();
+ return builder.parse(new ByteArrayInputStream(datos));
+ }
+
+ private static String getKey(String filename) throws IOException {
+ // Read key from file
+ String strKeyPEM = "";
+ BufferedReader br = new BufferedReader(new FileReader(filename));
+ String line;
+ while ((line = br.readLine()) != null) {
+ strKeyPEM += line + "\n";
+ }
+ br.close();
+ return strKeyPEM;
+ }
+
+ public static RSAPrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException {
+ String privateKeyPEM = getKey(filename);
+ return getPrivateKeyFromString(privateKeyPEM);
+ }
+
+ public static RSAPrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException {
+ String privateKeyPEM = key;
+ privateKeyPEM = privateKeyPEM.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
+ privateKeyPEM = privateKeyPEM.replace("-----END RSA PRIVATE KEY-----", "");
+ byte[] encoded = Base64.decodeBase64(privateKeyPEM);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
+ RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(keySpec);
+ return privKey;
+ }
+
+ public static RSAPublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException {
+
+ String publicKeyPEM = getKey(filename);
+
+ return getPublicKeyFromString(publicKeyPEM);
+
+ }
+
+ public static RSAPublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
+ String publicKeyPEM = key;
+ publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
+ publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
+ byte[] encoded = Base64.decodeBase64(publicKeyPEM);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
+ return pubKey;
+ }
+
+ public static X509Certificate getX509Certificate(String filename) throws IOException, CertificateException
+ {
+ CertificateFactory fact = CertificateFactory.getInstance("X.509");
+ FileInputStream is = new FileInputStream (filename);
+ X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
+ PublicKey key = cer.getPublicKey();
+ return cer;
+ }
+
+}
\ No newline at end of file
diff --git a/TestFirma/src/test/java/bo/sin/testfirma/FirmadorTest.java b/TestFirma/src/test/java/bo/sin/testfirma/FirmadorTest.java
new file mode 100644
index 0000000..caff1b2
--- /dev/null
+++ b/TestFirma/src/test/java/bo/sin/testfirma/FirmadorTest.java
@@ -0,0 +1,70 @@
+package bo.sin.testfirma;
+
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.junit.Test;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FirmadorTest {
+
+ @Test
+ public void getInstance() {
+ }
+
+ @Test
+ public void firmarDsig() {
+ }
+
+ @Test
+ public void leerXML() {
+ }
+
+ @Test
+ public void getPrivateKey() {
+ }
+
+ @Test
+ public void getPrivateKeyFromString() {
+ }
+
+ @Test
+ public void getPublicKey() {
+ }
+
+ @Test
+ public void getPublicKeyFromString() {
+ }
+
+ @Test
+ public void getX509Certificate() {
+ }
+
+ @Test
+ public void firmarXML() throws URISyntaxException, ParserConfigurationException, XMLSecurityException, org.xml.sax.SAXException {
+ String xml = "" +
+ " " +
+ "AQUI VA LA FACTURA XML " +
+ "";
+ byte[] datos = xml.getBytes(StandardCharsets.UTF_8);
+ try {
+ String path = new File(Firmador.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getPath();
+ PrivateKey privateKey = Firmador.getPrivateKey(path + "/private_key.pem");
+ X509Certificate cert = Firmador.getX509Certificate(path + "/cert.crt");
+ byte[] xmlFirmado = Firmador.firmarDsig(datos, privateKey, cert);
+ String respuesta = new String(xmlFirmado);
+ System.out.println("facturaFirmada: "+respuesta);
+ } catch (IOException | GeneralSecurityException ex) {
+ Logger.getLogger(FirmadorTest.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+}
\ No newline at end of file