/**
* LICENCIA LGPL:
*
* Esta librería es Software Libre; Usted puede redistribuirlo y/o modificarlo
* bajo los términos de la GNU Lesser General Public License (LGPL)
* tal y como ha sido publicada por la Free Software Foundation; o
* bien la versión 2.1 de la Licencia, o (a su elección) cualquier versión posterior.
*
* Esta librería se distribuye con la esperanza de que sea útil, pero SIN NINGUNA
* GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o ADECUACIÓN A UN
* PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public License (LGPL) para más
* detalles
*
* Usted debe recibir una copia de la GNU Lesser General Public License (LGPL)
* junto con esta librería; si no es así, escriba a la Free Software Foundation Inc.
* 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte
* <http://www.gnu.org/licenses/>.
*
* Copyright 2008 Ministerio de Industria, Turismo y Comercio
*
*/
package es.mityc.firmaJava.libreria.xades ;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Authenticator;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.security.Init;
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
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.IgnoreAllErrorHandler;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import es.mityc.firmaJava.configuracion.Configuracion;
import es.mityc.firmaJava.configuracion.EnumFormatoFirma;
import es.mityc.firmaJava.libreria.ConstantesXADES;
import es.mityc.firmaJava.libreria.errores.ClienteError;
import es.mityc.firmaJava.libreria.excepciones.AddXadesException;
import es.mityc.firmaJava.libreria.utilidades.Base64Coder;
import es.mityc.firmaJava.libreria.utilidades.GetPKCS12Keys;
import es.mityc.firmaJava.libreria.utilidades.I18n;
import es.mityc.firmaJava.libreria.utilidades.NombreNodo;
import es.mityc.firmaJava.libreria.utilidades.SimpleAuthenticator;
import es.mityc.firmaJava.libreria.utilidades.UtilidadFechas;
import es.mityc.firmaJava.libreria.utilidades.UtilidadFirmaElectronica;
import es.mityc.firmaJava.libreria.utilidades.UtilidadTratarNodo;
import es.mityc.firmaJava.libreria.xades.errores.BadFormedSignatureException;
import es.mityc.firmaJava.libreria.xades.errores.FirmaXMLError;
import es.mityc.firmaJava.libreria.xades.errores.PolicyException;
import es.mityc.firmaJava.ocsp.OCSPCliente;
import es.mityc.firmaJava.ocsp.OCSPClienteError;
import es.mityc.firmaJava.ocsp.RespuestaOCSP;
import es.mityc.firmaJava.ocsp.RespuestaOCSP.TIPOS_RESPONDER;
import es.mityc.firmaJava.policy.IFirmaPolicy;
import es.mityc.firmaJava.policy.PoliciesManager;
import es.mityc.firmaJava.ts.TSCliente;
import es.mityc.firmaJava.ts.TSClienteError;
/**
* Clase principal para la firma de documentos XML
*
* @author Ministerio de Industria, Turismo y Comercio
* @version 0.9 beta
*/
public class FirmaXML implements ConstantesXADES
{
static Log log = LogFactory.getLog(FirmaXML.class);
String profileDirectory = CADENA_VACIA;
Configuracion configuracion = null ;
private String xadesNS = null;
private String xadesSchema = null;
private String xmldsigNS = null;
private boolean isSelloXTipo1 = true;
private List<String> esquemasParaValidar = new LinkedList<String>();
private String esquema = null;
private boolean estadoProxy = false;
private String servidorProxy = null;
private int numeroPuertoProxy = 8080;
private String servidorTSA = null;
private String algoritmoTSA = null;
private String servidorOCSP = null;
private boolean isProxyAuth = false;
private String proxyUser = null;
private String proxyPass = null;
final static private String DIR_OCSP = "./RespuestasOCSP";
final static private String DIR_CERTS = "./Certificados";
// Almacena las id´s para el esquema 1.1.1
private ArrayList<String> idNodoSelloTiempo = new ArrayList<String>();
private String idNodoCertificateRefs = null;
private String idNodoRevocationRefs = null;
/**
* Crea una nueva instancia de FirmaXML con la configuración proporcionada
*/
public FirmaXML(Configuracion configuracion) {
this.configuracion = configuracion ;
this.xadesNS = configuracion.getValor(XML_XADES_NS);
this.xadesSchema = configuracion.getValor(XADES_SCHEMA);
this.xmldsigNS = configuracion.getValor(XML_NS);
this.isSelloXTipo1 = configuracion.comparar(ConstantesXADES.IS_SELLO_X_TIPO_1);
final String cadenaDeUrisXadesNS = configuracion.getValor(VALIDAR_XADES_SCHEMA);
StringTokenizer uriXadesNS = new StringTokenizer(cadenaDeUrisXadesNS, COMA);
boolean valor = uriXadesNS.hasMoreTokens();
while(valor)
{
esquema = uriXadesNS.nextToken().trim();
esquemasParaValidar.add(esquema);
valor = uriXadesNS.hasMoreTokens();
}
//Establece el idioma según la configuración
String locale = configuracion.getValor(LOCALE);
// Configura el idioma
I18n.setLocale(locale, locale.toUpperCase());
// Datos del servidor proxy
this.estadoProxy = configuracion.comparar(IS_PROXY);
this.servidorProxy = configuracion.getValor(PROXY_SERVER_URL);
final String puertoProxy = configuracion.getValor(PROXY_PORT_NUMBER);
this.isProxyAuth = configuracion.comparar(IS_PROXY_AUTH);
this.proxyUser = configuracion.getValor(PROXY_USER);
this.proxyPass = configuracion.getValor(PROXY_PASS);
try
{
this.numeroPuertoProxy = Integer.parseInt(puertoProxy);
}
catch (Exception e)
{
log.warn(I18n.getResource(LIBRERIAXADES_FIRMAMSL_WARN_1));
}
// Datos del servidor TSA
this.servidorTSA = configuracion.getValor(TIME_STAMP_SERVER_URL) ;
this.algoritmoTSA = configuracion.getValor(TIME_STAMP_HASH_ALG) ;
// Datos del servidor OCSP
this.servidorOCSP = configuracion.getValor(OCSP_SERVER_URL) ;
}
/**
* Firma un fichero XML
*
* @param numeroSerial Numero de serie del certificado firmante
* @param emisorDN Emisor del certificado firmante
* @param firmaCertificados X509Certificate para la firma digital
* @param xml Documento XML a firmar
* @return Array de bytes con la firma digital
* @throws java.lang.Exception En caso de error
*/
public boolean signFile(BigInteger numeroSerial, String emisorDN,
X509Certificate firmaCertificados,
File xml,
String destino,
String nombreArchivo) throws Exception{
FileInputStream fis = new FileInputStream(xml);
return signFile(numeroSerial,
emisorDN,
firmaCertificados,
fis,
null,
null,
null,
true,
destino,
nombreArchivo);
}
public Document signDoc(BigInteger numeroSerie, String emisorDN,
X509Certificate certificadoFirma,
Document xml) throws Exception {
Object[] res = signDoc(numeroSerie, emisorDN, certificadoFirma, xml, null, null, null, true);
// Si se firma XADES-C exclusivamente, se guardan las respuestaOCSP y los certificados
// con un nombre asociado al fichero de firma y en la misma ruta temporal
if (res[1] != null) {
throw new AddXadesException("Formato XAdES-C no válido con este método");
}
return (Document) res[0];
}
/**
* Firma un fichero XML
*
* @param numeroSerial Numero de serie del certificado firmante
* @param emisorDN emisor del certificado firmante
* @param firmaCertificados Certificado de firma
* @param xml Datos XML a firmar
* @param salida OutputStream para escribir la salida generada
* @throws Exception En caso de Error
*/
public void signFile(BigInteger numeroSerial, String emisorDN,
X509Certificate firmaCertificados,
InputStream xml,
OutputStream salida) throws Exception{
signFile(numeroSerial,
emisorDN,
firmaCertificados,
xml,
null,
null,
null,
salida,
true);
}
/**
* Firma un fichero XML
*
* @param pk Clave privada del certificado firmante
* @param firmaCertificado Certificado firmante
* @param xml Fichero XML a firmar
* @param directorioPerfil Directorio de configuracion de Firefox
* @throws java.lang.Exception En caso de error
* @return Array de bytes con el XML firmado
*/
public boolean signFile(PrivateKey pk,
X509Certificate firmaCertificado,
File xml,
String directorioPerfil,
String destino,
String nombreArchivo) throws Exception{
this.profileDirectory = directorioPerfil ;
FileInputStream fis = new FileInputStream(xml);
return signFile(null,
null,
firmaCertificado,
fis,
null,
null,
pk,
true,
destino,
nombreArchivo);
}
public Document signDoc(PrivateKey pk,
X509Certificate certificadoFirma,
Document doc,
String directorioPerfil) throws Exception{
this.profileDirectory = directorioPerfil ;
Object[] res = signDoc(null, null, certificadoFirma, doc, null, null, pk, true);
// Si se firma XADES-C exclusivamente, se guardan las respuestaOCSP y los certificados
// con un nombre asociado al fichero de firma y en la misma ruta temporal
if (res[1] != null) {
throw new AddXadesException("Formato XAdES-C no válido con este método");
}
return (Document) res[0];
}
/**
* Firma un fichero XML desde el explorador Mozilla Firefox
*
* @param pk Clave privada para realizar la firma
* @param firmaCertificado Certificado de firma
* @param xml Datos XML a firmar
* @param salida OutputStream para escribir la salida
* @param directorioPerfil Directorio del perfil de Mozilla
* @throws Exception En caso de error
*/
public void signFile(PrivateKey pk,
X509Certificate firmaCertificado,
InputStream xml,
OutputStream salida,
String directorioPerfil
) throws Exception{
this.profileDirectory = directorioPerfil ;
signFile(null,
null,
firmaCertificado,
xml,
salida,
null,
null,
pk,
true);
}
/**
* Firma un fichero XML
*
* @param pkcs12Fichero
* @param contraseña
* @param xml Fichero con el XML a firmar
* @param nodoRaizXml Nodo raíz de la firma
* @param nodoXmlParaFirma Nodos a firmar
* @return Boolean .- True si la firma se completó con éxito
* @throws Exception En caso de error
*/
public boolean signFile(String pkcs12Fichero,
String contrasenia,
File xml,
String nodoRaizXml,
String nodoXmlParaFirma,
String destino,
String nombreFichero) throws Exception{
// this.profileDirectory = profileDirectory ;
FileInputStream fis = new FileInputStream(xml);
GetPKCS12Keys pkcs12 = new GetPKCS12Keys(pkcs12Fichero,contrasenia);
return signFile(pkcs12.getCertificate().getSerialNumber(),
pkcs12.getCertificate().getIssuerDN().toString() ,
pkcs12.getCertificate(),
fis,
nodoRaizXml,
nodoXmlParaFirma,
pkcs12.getPrivateKey(),
false,
destino,
nombreFichero);
}
/**
* Firma un fichero XML
*
* @param numeroSerie Número de serie del certificado firmante
* @param emisorDN Emisor del certificado firmante
* @param certificadoFirma X509Certificate para la firma digital
* @param xml Fichero XML a firmar
* @param pk Clave privada del certificado firmante
* @param nodoRaizXml Elemento raiz del fichero XML a firmar donde se insertará la firma
* @param nodoParaFirmaXml Nodos XML a firmar separados por comas
* @throws java.lang.Exception En caso de error
*/
public void signFile(BigInteger numeroSerie, String emisorDN,
X509Certificate certificadoFirma, InputStream xml, OutputStream salida, String nodoRaizXml,
String nodoParaFirmaXml, PrivateKey pk, boolean navigator)
throws Exception {
//FileInputStream fis = (FileInputStream) xml;
signFile(numeroSerie, emisorDN, certificadoFirma, xml, nodoRaizXml,
nodoParaFirmaXml, pk, salida, navigator);
}
/**
* Firma un fichero XML
*
* @param numeroSerie Número de serie del certificado firmante
* @param emisorDN Emisor del certificado firmante
* @param certificadoFirma X509Certificate para la firma digital
* @param xml Fichero XML a firmar
* @param nodoRaizXml Elemento raiz del fichero XML a firmar donde se insertará la firma
* @param nodoParaFirmaXml Nodos XML a firmar separados por comas
* @param pk Clave privada para realizar la firma
* @param salida OutputStream para escribir la salida
* @param navigator Tipo de navegador
* @throws Exception En caso de error
*/
public void signFile(BigInteger numeroSerie, String emisorDN, X509Certificate certificadoFirma,
InputStream xml, String nodoRaizXml, String nodoParaFirmaXml,
PrivateKey pk, OutputStream salida, boolean navigator) throws Exception{
Object[] res = signFile(numeroSerie, emisorDN, certificadoFirma, xml, nodoRaizXml, nodoParaFirmaXml, pk, navigator);
if (res[1] != null)
throw new ClienteError("Firma XAdES-C no soportada en este método") ;
try
{
XMLUtils.outputDOM((Document)res[0], salida, true);
}
catch (Throwable t)
{
if (t.getMessage().startsWith(JAVA_HEAP_SPACE))
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
else
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_4));
}
}
/**
* Firma un fichero XML
*
* @param numeroSerie Número de serie del certificado firmante
* @param emisorDN Emisor del certificado firmante
* @param certificadoFirma X509Certificate para la firma digital
* @param xml Fichero XML a firmar
* @param nodoRaizXml Elemento raiz del fichero XML a firmar donde se insertará la firma
* @param nodoParaFirmaXml Nodos XML a firmar separados por comas
* @param pk Clave privada para realizar la firma
* @param navigator Tipo de navegador
* @param destino Ruta de destino de la firma generada
* @param nombreArchivo Nombre del archivo para salvar la firma generada
* @return Boolean True si se realizó la firma correctamente
* @throws Exception En caso de error
*/
public boolean signFile(BigInteger numeroSerie, String emisorDN, X509Certificate certificadoFirma,
InputStream xml, String nodoRaizXml, String nodoParaFirmaXml,
PrivateKey pk, boolean navigator, String destino, String nombreArchivo) throws Exception {
if (destino == null || nombreArchivo == null) {
// No se proporcionaron los datos de firma
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_31));
}
Object[] res = signFile(numeroSerie, emisorDN, certificadoFirma, xml, nodoRaizXml, nodoParaFirmaXml, pk, navigator);
// Si se firma XADES-C exclusivamente, se guardan las respuestaOCSP y los certificados
// con un nombre asociado al fichero de firma y en la misma ruta temporal
Document doc = (Document) res[0];
if (res[1] != null) {
doc = addURIXadesC(doc, saveOCSPFiles((ArrayList<RespYCerts>)res[1], destino, nombreArchivo));
}
// Se guarda la firma en su destino
File fichero = new File(destino + nombreArchivo);
FileOutputStream f = new FileOutputStream(fichero);
try
{
XMLUtils.outputDOM(doc, f,true);
}
catch (Throwable t)
{
if (t.getMessage().startsWith(JAVA_HEAP_SPACE))
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
else
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_4));
} finally {
f.close();
}
return true ;
}
public Object[] signFile(BigInteger numeroSerie, String emisorDN, X509Certificate certificadoFirma, InputStream xml,
String nodoRaizXml, String nodoParaFirmaXml, PrivateKey pk, boolean navigator) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
db.setErrorHandler(new IgnoreAllErrorHandler());
InputSource isour = new InputSource(xml);
String encoding = configuracion.getValor(ENCODING_XML) ;
isour.setEncoding(encoding);
Document doc = db.parse(isour);
return signDoc(numeroSerie, emisorDN, certificadoFirma, doc, nodoRaizXml, nodoParaFirmaXml, pk, navigator);
}
public Object[] signDoc(BigInteger numeroSerie, String emisorDN, X509Certificate certificadoFirma, Document doc,
String nodoRaizXml, String nodoParaFirmaXml, PrivateKey pk, boolean navigator) throws Exception {
ParametrosFirmaXML sp = ParametrosFirmaXML.getInstance() ;
Vector <X509Certificate> certRef = null;
byte[] respuestaOCSP = null;
ArrayList<RespYCerts> respuestas = new ArrayList<RespYCerts>();
if (navigator){
if(numeroSerie!=null && emisorDN!=null)
{
sp.setIssuerDN(emisorDN);
sp.setSerialNumber(numeroSerie);
}
else
{
sp.setModeSign(M) ;
sp.setPerfilUsuario(this.profileDirectory);
}
}
String nodosCadenaParaFirma ;
if (nodoParaFirmaXml == null){
nodosCadenaParaFirma= configuracion.getValor(XML_NODE_TO_SIGN);
}else{
nodosCadenaParaFirma = nodoParaFirmaXml;
}
StringTokenizer stTok = new StringTokenizer(nodosCadenaParaFirma,COMA);
String[] nodosParaFirma = new String[stTok.countTokens()];
int aa=0;
boolean valor = stTok.hasMoreElements();
while(valor){
nodosParaFirma[aa] = ((String)stTok.nextElement()).trim();
aa++;
valor = stTok.hasMoreElements();
}
Init.init() ;
// DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// dbf.setNamespaceAware(true);
// DocumentBuilder db = dbf.newDocumentBuilder();
// db.setErrorHandler(new IgnoreAllErrorHandler());
//
// InputSource isour = new InputSource(xml);
// String encoding = configuracion.getValor(ENCODING_XML) ;
// isour.setEncoding(encoding);
// Document doc = db.parse(isour);
Element elementoPrincipal = null;
if (nodoRaizXml == null) {
elementoPrincipal = (Element)doc.getFirstChild();
} else {
NodeList nodos = doc.getElementsByTagName(nodoRaizXml);
if(nodos.getLength() != 0)
elementoPrincipal = (Element)nodos.item(0);
else
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_2)) ;
}
XMLSignature.setDefaultPrefix(Constants.SignatureSpecNS,
configuracion.getValor(XML_NS));
XMLSignature firma = new XMLSignature(doc,
CADENA_VACIA ,
XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
firma.setId(configuracion.getValor(SIGNATURE_NODE_ID));
firma.getSignedInfo().setId(configuracion.getValor(SIGNED_INFO_NODE_ID));
firma.setXPathNamespaceContext(xmldsigNS, SCHEMA_DSIG);
EnumFormatoFirma tipoFirma = configuracion.getFormatoXades();
boolean xadesActivo = (tipoFirma.compareTo(EnumFormatoFirma.XAdES_BES)>=0);
if(xadesActivo){
firma.setXPathNamespaceContext(xadesNS, xadesSchema) ;
}
elementoPrincipal.appendChild(firma.getElement());
if(xadesActivo){
String tipoEsquema = UtilidadFirmaElectronica.obtenerTipoReference(xadesSchema);
firma.addDocument(ALMOHADILLA + configuracion.getValor(SIGNATURE_NODE_ID)
+ GUION_SIGNED_PROPERTIES,
null, Constants.ALGO_ID_DIGEST_SHA1,SIGNED_PROPERTIES_ID, tipoEsquema);
}
firma.addKeyInfo(certificadoFirma);
firma.addKeyInfo(certificadoFirma.getPublicKey()) ;
firma.getKeyInfo().setId(CERTIFICATE1) ;
// Añadimos los elementos propios de la firma en XADES
for (int a=0;a< nodosParaFirma.length ; a++) {
if (MENOR_ROOT_MAYOR.equalsIgnoreCase(nodosParaFirma[a])) {
Transforms trans = new Transforms(doc);
trans.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
firma.addDocument(CADENA_VACIA,
trans,
Constants.ALGO_ID_DIGEST_SHA1);
} else {
StringBuffer sb = new StringBuffer(ALMOHADILLA);
sb.append(nodosParaFirma[a]);
firma.addDocument(String.valueOf(sb),
null,
Constants.ALGO_ID_DIGEST_SHA1);
}
}
if(xadesActivo){
addXades(doc,
configuracion.getValor(SIGNATURE_NODE_ID),
certificadoFirma,
firma.getElement());
XAdESSchemas schema = XAdESSchemas.getXAdESSchema(xadesSchema);
if (schema == null) {
log.error("Esquema XAdES desconocido: " + xadesSchema);
throw new AddXadesException("Esquema de XAdES desconocido");
}
if ((configuracion.comparar(ConstantesXADES.LIBRERIAXADES_ADD_EPES)) || (schema.equals(XAdESSchemas.XAdES_111))) {
addXadesEPES(firma.getElement());
}
}
// Si firmamos con Microsoft tenemos que generar una clave privada
// para utilizar como parámeto. Sin embargo, esta clave
// no se utiliza para la firma.
// Mirar la implementación de SignatureSpi en MCUSignatureMS
if(sp.getSerialNumber()!=null && sp.getIssuerDN() !=null)
{
KeyPairGenerator kpgPalote = KeyPairGenerator.getInstance(RSA);
try
{
firma.sign(kpgPalote.genKeyPair().getPrivate());
}
catch (Throwable t)
{
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_32), t);
if (t.getMessage().startsWith(JAVA_HEAP_SPACE))
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
else if (t.getMessage().startsWith(FIRMA_NO_CONTIENE_DATOS))
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_1));
else
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_4));
}
}
else
{
// Si es mozilla utilizamos la clave privada real
try
{
firma.sign(pk);
}
catch (Throwable t)
{
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_4), t);
if (t.getMessage().startsWith(JAVA_HEAP_SPACE))
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_3));
else if (t.getMessage().startsWith(FIRMA_NO_CONTIENE_DATOS))
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_1));
else
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_4));
}
}
// Añadimos el Id al nodo signature value
Element elementoValorFirma = null ;
NodeList nodoValorFirma = doc.getElementsByTagNameNS(SCHEMA_DSIG, SIGNATURE_VALUE);
if(nodoValorFirma.getLength() != 0)
elementoValorFirma = (Element)nodoValorFirma.item(0);
else
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_5));
// Le añadimos el elemento ID
Attr idValorFirma = doc.createAttribute(ID);
idValorFirma.setValue(SIGNATURE_VALUE);
NamedNodeMap elementoIdAtributosValorFirma =
elementoValorFirma.getAttributes();
elementoIdAtributosValorFirma.setNamedItem(idValorFirma);
//Comprobamos si se debe de firmar añadiendo el elemento de XADES-T
boolean xadesT =(tipoFirma.compareTo(EnumFormatoFirma.XAdES_T)>=0);
log.debug(I18n.getResource(LIBRERIAXADES_FIRMAXML_DEBUG_1) + xadesT);
if(xadesT) {
try
{
// Añadimos XADES-T
if (servidorTSA!=null && !servidorTSA.trim().equals(CADENA_VACIA))
{
// Obtenemos la respuesta del servidor TSA
TSCliente tsCli = null;
if(estadoProxy)
{
System.setProperty("http.proxyHost", servidorProxy);
System.setProperty("http.proxyPort", Integer.toString(numeroPuertoProxy));
if (isProxyAuth) {
Authenticator.setDefault(new SimpleAuthenticator(proxyUser, proxyPass));
}
else {
Authenticator.setDefault(null);
}
}
tsCli = new TSCliente(servidorTSA,algoritmoTSA);
// Se añaden los elementos propios de la firma XADES-T
byte[] byteSignature = UtilidadTratarNodo.obtenerByteNodo(doc, SCHEMA_DSIG, SIGNATURE_VALUE);
addXadesT(doc,configuracion.getValor(SIGNATURE_NODE_ID),tsCli.generarSelloTiempo(byteSignature)) ;
}
else
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_6));
}
}
catch (AddXadesException e)
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_7) + e.getMessage()) ;
}
}
RespuestaOCSP respuesta = null;
// Comprobamos si se debe de firmar añadiendo los elementos de XADES-C
boolean xadesC = (tipoFirma.compareTo(EnumFormatoFirma.XAdES_C)>=0);
log.debug(I18n.getResource(LIBRERIAXADES_FIRMAXML_DEBUG_2) + xadesC);
if(xadesC){
respuesta = new RespuestaOCSP();
try
{
// Comprobamos si se ha realizado antes la firma XADES-T. En caso contrario se le avisa
// al usuario que no puede realizarse la firma XADES-C
if(xadesT){
// Añadimos XADES-C
if (servidorOCSP!=null && !servidorOCSP.trim().equals(CADENA_VACIA))
{
// Obtenemos la respuesta del servidor OCSP
String tiempoRespuesta = CADENA_VACIA;
OCSPCliente ocspCliente = null;
RespYCerts bloque = null;
try
{
if(estadoProxy)
{
System.setProperty("http.proxyHost", servidorProxy);
System.setProperty("http.proxyPort", Integer.toString(numeroPuertoProxy));
if (isProxyAuth) {
Authenticator.setDefault(new SimpleAuthenticator(proxyUser, proxyPass));
}
else {
Authenticator.setDefault(null);
}
}
ocspCliente = new OCSPCliente(servidorOCSP);
respuesta = ocspCliente.validateCert(certificadoFirma);
tiempoRespuesta = UtilidadFechas.formatFechaXML(respuesta.getTiempoRespuesta());
}
catch (OCSPClienteError ex)
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8) + ex.getMessage()) ;
}
// Solo continúa si el certificado es válido
if (respuesta.getNroRespuesta()!=0)
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_9)) ;
}
certRef = respuesta.getRefCerts();
respuestaOCSP = respuesta.getRespuesta();
bloque = new RespYCerts();
bloque.setRespOCSP(respuestaOCSP);
bloque.setX509Cert(certificadoFirma);
bloque.setTiempoRespuesta(tiempoRespuesta);
bloque.setResponder(respuesta.getValorResponder(), respuesta.getTipoResponder());
respuestas.add(bloque);
int certRefSize = certRef.size();
for (int x=1; x < certRefSize; ++x) {
X509Certificate certificado = certRef.get(x);
try {
respuesta = ocspCliente.validateCert(certificado);
tiempoRespuesta = UtilidadFechas.formatFechaXML(respuesta.getTiempoRespuesta());
}
catch (OCSPClienteError ex)
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_8) + ex.getMessage()) ;
}
// Solo continúa si el certificado es válido
if (respuesta.getNroRespuesta()!=0)
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_9)) ;
}
respuestaOCSP = respuesta.getRespuesta();
bloque = new RespYCerts();
bloque.setRespOCSP(respuestaOCSP);
bloque.setX509Cert(certificado);
bloque.setTiempoRespuesta(tiempoRespuesta);
bloque.setResponder(respuesta.getValorResponder(), respuesta.getTipoResponder());
respuestas.add(bloque);
}
// Se añaden los elementos propios de la firma XADES-C
addXadesC(doc, respuestas);
}
}
else
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_24)) ;
}
}
catch (AddXadesException e)
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_10) + e.getMessage()) ;
}
}
// Comprobamos si se debe de firmar añadiendo los elementos de XADES-X
boolean xadesX = (tipoFirma.compareTo(EnumFormatoFirma.XAdES_X)>=0);
log.debug(I18n.getResource(LIBRERIAXADES_FIRMAXML_DEBUG_3) + xadesX);
boolean xadesXL = (tipoFirma.compareTo(EnumFormatoFirma.XAdES_XL)==0);
log.debug(I18n.getResource(LIBRERIAXADES_FIRMAXML_DEBUG_4) + xadesXL);
xadesXL = xadesX; // Si es XAdES-X, se pone xades-XL a true para redondear
if(xadesX){
// Para realizar la firma XADES-XL se deben completar antes los
// formatos de firmas XADES-T y XADES-C
if (xadesT && xadesC ) {
// Se obtiene el nodo raíz de la firma
Element signatureElement = firma.getElement();
if (!(new NombreNodo(SCHEMA_DSIG, SIGNATURE).equals(
new NombreNodo(signatureElement.getNamespaceURI(), signatureElement.getLocalName())))) {
// No se encuentra el nodo Signature
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_33) + ESPACIO + SIGNATURE);
}
// A partir del nodo raíz de la firma se obtiene el nodo UnsignedSignatureProperties
Element unsignedSignaturePropertiesElement = null;
NodeList unsignedSignaturePropertiesNodes =
signatureElement.getElementsByTagNameNS(xadesSchema, UNSIGNED_SIGNATURE_PROPERTIES);
if (unsignedSignaturePropertiesNodes.getLength() != 1) {
// El nodo UnsignedSignatureProperties no existe o no es único
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_36) + ESPACIO + UNSIGNED_SIGNATURE_PROPERTIES +
ESPACIO + I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_37) + ESPACIO +
unsignedSignaturePropertiesNodes.getLength());
// El sistema no soporta nodos UnsignedSignatureProperties múltiples
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_41));
} else
unsignedSignaturePropertiesElement = (Element)unsignedSignaturePropertiesNodes.item(0);
// Se añaden los elementos propios de la firma XADES-X
if (isSelloXTipo1)
addXadesX(unsignedSignaturePropertiesElement);
else
addXadesX2(unsignedSignaturePropertiesElement);
}
else
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_25)) ;
}
}
if(xadesXL){
// Para realizar la firma XADES-XL se deben completar antes los
// formatos de firmas XADES-T, XADES-C y XADES-X
if (xadesT && xadesC && xadesX) {
try
{
// Añadimos XADES-XL
addXadesXL(doc, respuestas);
}
catch (Exception e)
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_12) + e.getMessage()) ;
}
}
else
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_13)) ;
}
}
Object[] res = new Object[2];
res[0] = doc;
if (xadesC && !xadesXL)
res[1] = respuestas;
else
res[1] = null;
return res;
}
/**
* Firma un fichero XML con multifirma.
*
* @param serialNumber Número de serie del certificado firmante
* @param issuerDN Emisor del certificado firmante
* @param DOM documento
* @param certificadoFirma X509Certificate para la firma digital
* @param pk Clave privada del certificado firmante
* @throws java.lang.Exception En caso de error
* @return byte[] XMl firmado
*/
public byte[] multiSignFile(BigInteger serialNumber,
String issuerDN,
Document doc,
X509Certificate certificadoFirma,
PrivateKey pk,
boolean navigator)
throws Exception
{
// Comprobamos si ya existe el elemento counter signature
// Si ya existe, recuperamos la ultima ocurrencia de Signature value
// añadimos un nuevo elemento signature referenciando al anterior
// Si no existe, recuperamos el objeto signature value para obtener el id
// añadimos un nuevo elemento counter signature con un primer signature
// Recogemos el nodo <CounterSignature>
Element elementoContadorFirma = null ;
Element elementoPrincipal = null ;
String ultimoId = null ;
int certificadoId = 0;
NodeList firmaContadorNodo = doc.getElementsByTagNameNS(SCHEMA_DSIG, COUNTER_SIGNATURE);
if(firmaContadorNodo.getLength() != 0)
{
// Si ya existe, recuperamos la ultima ocurrencia de Signature value
// añadimos un nuevo elemento signature referenciando al anterior
// Establecemos el nodo raíz en counter Signature.
elementoContadorFirma = (Element)firmaContadorNodo.item(0);
elementoPrincipal = elementoContadorFirma;
// Iteramos sus nodos Signature para recuperar el ultimo
NodeList firmas = elementoContadorFirma.getElementsByTagNameNS(SCHEMA_DSIG, SIGNATURE);
if(firmas != null)
{
Element ultimaFirma = (Element) firmas.item(firmas.getLength() - 1);
// Iteramos los hijos de la ultima firma para obtener el elemento signature value y sacar el Id
NodeList listaValorUltimaFirma = ultimaFirma.getElementsByTagNameNS(SCHEMA_DSIG, SIGNATURE_VALUE);
Element valorUltimaFirma = (Element) listaValorUltimaFirma.item(0);
ultimoId = valorUltimaFirma.getAttribute(ID);
certificadoId = firmas.getLength();
}
else
{
throw new ClienteError(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_14));
}
}
else
{
// Si no existe, recuperamos el objeto signature value para obtener el id
// añadimos un nuevo elemento counter signature con un primer signature
// Recogemos el nodo <ds:SignatureValue>
Element valorElementoFirma = null ;
NodeList valorNodoFirma = doc.getElementsByTagNameNS(SCHEMA_DSIG, SIGNATURE_VALUE);
if(valorNodoFirma.getLength() != 0) {
valorElementoFirma = (Element)valorNodoFirma.item(0);
ultimoId = valorElementoFirma.getAttribute(ID);
}else {
throw new Exception(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_15));
}
// Ahora creamos la estructura de Counter Signature para añadir la firma referenciada
elementoContadorFirma = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + COUNTER_SIGNATURE);
NodeList caracteristicaSinFirmarFirmaNodo = doc.getElementsByTagNameNS(xadesSchema, UNSIGNED_SIGNATURE_PROPERTIES);
Element elememtoSinFirmarCaracteristicaFirma = null;
if(caracteristicaSinFirmarFirmaNodo.getLength() != 0)
{
elememtoSinFirmarCaracteristicaFirma = (Element)caracteristicaSinFirmarFirmaNodo.item(0);
elememtoSinFirmarCaracteristicaFirma.appendChild(elementoContadorFirma);
}
// Actualiza el nodo de donde colgará la firma y el número de certificado. Por ser el primero sera el 0
elementoPrincipal = elementoContadorFirma;
}
XMLSignature.setDefaultPrefix(Constants.SignatureSpecNS,
configuracion.getValor(XML_NS));
XMLSignature firma = new XMLSignature(doc,
CADENA_VACIA ,
XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
EnumFormatoFirma tipoFirma = configuracion.getFormatoXades();
boolean xadesActivo = (tipoFirma.compareTo(EnumFormatoFirma.XAdES_BES)>=0);
firma.setXPathNamespaceContext(xmldsigNS, SCHEMA_DSIG);
if(xadesActivo){
firma.setXPathNamespaceContext(xadesNS, xadesSchema) ;
}
firma.setId(configuracion.getValor(SIGNATURE_NODE_ID) + certificadoId);
firma.getSignedInfo().setId(configuracion.getValor(SIGNED_INFO_NODE_ID) + certificadoId);
firma.addKeyInfo(certificadoFirma);
firma.addKeyInfo(certificadoFirma.getPublicKey()) ;
firma.getKeyInfo().setId( CERTIFICATE + certificadoId) ;
firma.addDocument(ALMOHADILLA + ultimoId,
null,
Constants.ALGO_ID_DIGEST_SHA1);
ParametrosFirmaXML sp = ParametrosFirmaXML.getInstance() ;
if (navigator){
if(serialNumber!=null && issuerDN!=null) {
sp.setIssuerDN(issuerDN);
sp.setSerialNumber(serialNumber);
}else{
sp.setModeSign(M) ;
sp.setPerfilUsuario(this.profileDirectory);
}
}
// Si firmamos con microsoft tenemos que gererar una clave privada
// que no se utiliza para la firma.
// Mirar la implementacion de SignatureSpi en MCUSignatureMS
if(sp.getSerialNumber()!=null && sp.getIssuerDN() != null){
KeyPairGenerator kpgPalote = KeyPairGenerator.getInstance(RSA);
firma.sign(kpgPalote.genKeyPair().getPrivate());
}else{
// Si es mozilla utilizamos la clave privada Real
firma.sign(pk);
}
elementoPrincipal.appendChild(firma.getElement());
// Ahora añadimos el id al nodo Signature Value
NodeList nodesSignatureValue = elementoPrincipal.getElementsByTagNameNS(SCHEMA_DSIG, SIGNATURE_VALUE);
Element lastSignatureValueElement = (Element)nodesSignatureValue.item(nodesSignatureValue.getLength() - 1);
// Le añadimos el elemento ID
// signatureValueElement
Attr idSignatureValue = doc.createAttribute(ID);
idSignatureValue.setValue( SIGNATURE + certificadoId);
NamedNodeMap elementIdSignatureValueAttributes = lastSignatureValueElement.getAttributes();
elementIdSignatureValueAttributes.setNamedItem(idSignatureValue);
ByteArrayOutputStream f = new ByteArrayOutputStream();
XMLUtils.outputDOM(doc, f, true);
return f.toByteArray() ;
}
/**
* Este método realiza la implementación de la firma XADES-BES
*
* @param doc Documento de firma
* @param firmaID Identificador del nodo de firma
* @param firmaCertificado Certificado que realiza la firma
* @param elementoPrincipalFirma Elemento principal del nodo de firma
* @return Documento de firma con formato XADES-BES
* @throws AddXadesException En caso de error
*/
private Document addXades(Document doc,
String firmaID,
X509Certificate firmaCertificado,
Element elementoPrincipalFirma) throws AddXadesException
{
// Creamos el QualifyingProperties
Element elemntQualifyingProperties = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + QUALIFYING_PROPERTIES);
// Creamos los atributos de QualifyingProperties
elemntQualifyingProperties.setAttributeNS(null, TARGET , ALMOHADILLA + firmaID);
// Creamos el elemento SignedProperties
Element propiedadesFirmadasElemento = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + SIGNED_PROPERTIES);
// Creamos los atributos de SignedProperties
propiedadesFirmadasElemento.setAttributeNS(null, ID, firmaID + GUION_SIGNED_PROPERTIES);
// Creamos el xades:SignedSignatureProperties
Element propiedadesFirmadasElementoFirma = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + SIGNED_SIGNATURE_PROPERTIES);
// Creamos el xades:SigningTime
Element tiempoFirmaElemento = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + SIGNING_TIME);
// Formatemos la fecha de acuerdo al estándar
// http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime
String tiempoFecha = UtilidadFechas.formatFechaXML(new Date());
tiempoFirmaElemento.appendChild(doc.createTextNode(tiempoFecha));
// Creamos el xades:SigningCertificate
Element certificadoFirmaElemento = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + SIGNING_CERTIFICATE);
// Creamos el xades:Cert
Element certificadoElemento = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CERT);
// Creamos el xades:CertDigest
Element resumenCertificadoElemento = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CERT_DIGEST);
// Creamos el xades:DigestMethod
Element metodoResumenElemento = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + DIGEST_METHOD);
metodoResumenElemento.setAttributeNS(null, ALGORITHM, MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA1);
// Creamos el xades:DigestValue
String resumenCertificado =CADENA_VACIA;
try {
MessageDigest resumenCertificadoTemp = MessageDigest.getInstance(SHA_1);
byte[] byteMessageDigest =resumenCertificadoTemp.digest(firmaCertificado.getEncoded());
resumenCertificado = new String(Base64Coder.encode(byteMessageDigest));
}
catch (NoSuchAlgorithmException nsae)
{
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_16));
}
catch (CertificateEncodingException cee)
{
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_17));
}
Element elementDigestValue = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + DIGEST_VALUE);
elementDigestValue.appendChild(doc.createTextNode(resumenCertificado));
// Creamos el xades:IssuerSerial
Element elementoEmisorSerial = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + ISSUER_SERIAL );
// Creamos el xades:X509IssuerName
Element elementoX509EmisorNombre = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + X_509_ISSUER_NAME);
elementoX509EmisorNombre.appendChild(doc.createTextNode(firmaCertificado.getIssuerX500Principal().getName()));
// Creamos el xades:X509SerialNumber
Element elementoX509NumeroSerial = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + X_509_SERIAL_NUMBER);
elementoX509NumeroSerial.appendChild(doc.createTextNode(firmaCertificado.getSerialNumber().toString()));
// Creamos el xades:SignerRole. Para ello consultamos en el fichero de propiedades
String rolesFirmante = configuracion.getValor(SIGNER_ROLES);
boolean roleFirmante = false;
Element elementoRoleFirmanteElemento = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + SIGNER_ROLE);
if(!Configuracion.isEmpty(rolesFirmante))
{
Element elementoRolesDemandadosElementos = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CLAIMED_ROLES);
elementoRoleFirmanteElemento.appendChild(elementoRolesDemandadosElementos);
// Añadimos un nuevo elemento SignerRoles
StringTokenizer roles = new StringTokenizer(rolesFirmante, COMA);
boolean valor = roles.hasMoreElements();
while(valor){
String role = (String) roles.nextElement();
Element elementClaimedRoleElement = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CLAIMED_ROLE);
elementClaimedRoleElement.appendChild(doc.createTextNode(role.trim()));
elementoRolesDemandadosElementos.appendChild(elementClaimedRoleElement);
valor = roles.hasMoreElements();
}
roleFirmante = true;
}
propiedadesFirmadasElementoFirma.appendChild(tiempoFirmaElemento);
propiedadesFirmadasElemento.appendChild(propiedadesFirmadasElementoFirma);
resumenCertificadoElemento.appendChild(metodoResumenElemento);
resumenCertificadoElemento.appendChild(elementDigestValue);
certificadoElemento.appendChild(resumenCertificadoElemento);
elementoEmisorSerial.appendChild(elementoX509EmisorNombre);
elementoEmisorSerial.appendChild(elementoX509NumeroSerial);
certificadoElemento.appendChild(elementoEmisorSerial);
certificadoFirmaElemento.appendChild(certificadoElemento);
propiedadesFirmadasElementoFirma.appendChild(certificadoFirmaElemento);
if(roleFirmante)
{
propiedadesFirmadasElementoFirma.appendChild(elementoRoleFirmanteElemento);
}
elemntQualifyingProperties.appendChild(propiedadesFirmadasElemento);
// Añadimos el objeto final
Element elementoObjeto = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + OBJECT);
elementoObjeto.setAttributeNS(null, ID, firmaID + GUION_OBJECT );
elementoObjeto.appendChild(elemntQualifyingProperties);
elementoPrincipalFirma.appendChild(elementoObjeto) ;
return null;
}
private void addXadesEPES(Element elementoPrincipalFirma) throws AddXadesException {
// Se obtiene el manager para la política indicada
String confPolicyManager = configuracion.getValor(ConstantesXADES.LIBRERIAXADES_EPES_POLICY_MANAGER);
if (Configuracion.isEmpty(confPolicyManager))
confPolicyManager = ConstantesXADES.LIBRERIAXADES_IMPLIEDPOLICY_MANAGER;
IFirmaPolicy policyManager = PoliciesManager.getInstance().getEscritorPolicy(confPolicyManager);
if (policyManager == null) {
log.error("PolicyManager pedido no disponible: " + confPolicyManager);
throw new AddXadesException("Política configurada desconocida");
}
XAdESSchemas schema = XAdESSchemas.getXAdESSchema(xadesSchema);
if (schema == null) {
log.error("Esquema XAdES desconocido: " + xadesSchema);
throw new AddXadesException("Esquema de XAdES desconocido");
}
try {
policyManager.escribePolicy(elementoPrincipalFirma, xmldsigNS, xadesNS, schema);
} catch (PolicyException ex) {
log.error("Error escribiendo politica: " + ex.getMessage(), ex);
throw new AddXadesException(ex.getMessage(), ex);
}
}
/**
* Este método añade la implementación para XADES-T
*
* @param doc Documento de firma con formato XADES-BES
* @param firmaID Identificador del nodo de firma
* @param selloTiempo Respuesta del servidor TSA con el sello de tiempo en formato binario
* @return Documento de firma con formato XADES-T
* @throws AddXadesException
*/
private Document addXadesT(Document doc, String firmaID, byte[] selloTiempo)
throws AddXadesException
{
Element elementoPrincipal = null ;
NodeList nodos = doc.getElementsByTagNameNS(xadesSchema, QUALIFYING_PROPERTIES);
if(nodos.getLength() != 0)
{
elementoPrincipal = (Element)nodos.item(0);
}
else
{
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_18)) ;
}
Element propiedadesElementosNoFirmados =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + UNSIGNED_PROPERTIES);
// Creamos los atributos de UnSignedProperties
Attr propiedadesNoFirmadasId = doc.createAttribute(ID);
propiedadesNoFirmadasId.setValue( firmaID + GUION_UNSIGNED_PROPERTIES );
NamedNodeMap atributosSinFirmarPropiedadesElemento =
propiedadesElementosNoFirmados.getAttributes();
atributosSinFirmarPropiedadesElemento.setNamedItem(propiedadesNoFirmadasId);
Element propiedadesSinFirmarFirmaElementos =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + UNSIGNED_SIGNATURE_PROPERTIES);
// Se buscan otros sellos de tiempo en la firma y se les asigna una Id si no la tienen
NodeList sellosPreexistentes = doc.getElementsByTagNameNS(xadesSchema, SIGNATURE_TIME_STAMP);
int numSellos = sellosPreexistentes.getLength();
for (int i = 0; i < numSellos; ++i) {
Element sello = (Element) sellosPreexistentes.item(i);
String selloId = sello.getAttribute(ID);
if (selloId == null) {
Attr informacionElementoSigTimeStamp = doc.createAttribute(ID);
selloId = UtilidadTratarNodo.newID(doc, SELLO_TIEMPO);
informacionElementoSigTimeStamp.setValue(selloId);
sello.getAttributes().setNamedItem(informacionElementoSigTimeStamp);
}
// Se almacena su nombre de Id por si es preciso referenciarlos
idNodoSelloTiempo.add(selloId);
}
// Se crea el nodo de sello de tiempo
Element tiempoSelloElementoFirma =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + SIGNATURE_TIME_STAMP);
// Se escribe una Id única
Attr informacionElementoSigTimeStamp = doc.createAttribute(ID);
String idSelloTiempo = UtilidadTratarNodo.newID(doc, SELLO_TIEMPO);
informacionElementoSigTimeStamp.setValue(idSelloTiempo);
idNodoSelloTiempo.add(idSelloTiempo);
tiempoSelloElementoFirma.getAttributes().setNamedItem(informacionElementoSigTimeStamp);
// Se agrega un nodo Include con una URI apuntando a SignatureValue si es esquema 1.2.2
if (SCHEMA_XADES_122.equals(xadesSchema)) {
Element informacionElementoHashDatos = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + INCLUDE);
Attr informacionElementoHashDatosUri = doc.createAttribute(URI);
informacionElementoHashDatosUri.setValue(ALMOHADILLA_SIGNATURE_VALUE);
NamedNodeMap informacionAtributosElementoHashDatos =
informacionElementoHashDatos.getAttributes();
informacionAtributosElementoHashDatos.setNamedItem(informacionElementoHashDatosUri);
tiempoSelloElementoFirma.appendChild(informacionElementoHashDatos) ;
}
Element tiempoSelloEncapsulado =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + ENCAPSULATED_TIME_STAMP);
tiempoSelloEncapsulado.appendChild(
doc.createTextNode(new String(Base64Coder.encode(selloTiempo))));
tiempoSelloElementoFirma.appendChild(tiempoSelloEncapsulado);
propiedadesSinFirmarFirmaElementos.appendChild(tiempoSelloElementoFirma);
propiedadesElementosNoFirmados.appendChild(propiedadesSinFirmarFirmaElementos);
elementoPrincipal.appendChild(propiedadesElementosNoFirmados);
return doc;
}
/**
* Este método añade la implementacion para XADES-C
*
* @param doc Documento de firma con formato XADES-T
* @param respuestas Cadena de Certificación con las respuestas OCSP del certificado de firma
* @return Documento de firma con formato XADES-C
* @throws AddXadesException En caso de error
*/
private Document addXadesC(Document doc,
ArrayList<RespYCerts> respuestas)
throws AddXadesException
{
// Recogemos el nodo UnsignedSignatureProperties del cual dependen los nodos
// que hay que añadir para completar la firma XADES-C
Element elementoPrincipal = null ;
ArrayList<X509Certificate> certRefs = null;
NodeList nodos = doc.getElementsByTagNameNS(xadesSchema, UNSIGNED_SIGNATURE_PROPERTIES);
if(nodos.getLength() != 0)
{
elementoPrincipal = (Element)nodos.item(0);
}
else
{
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_19)) ;
}
// Aqui vienen las llamadas para los certificados
Element certificadosElementosFirma =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + COMPLETE_CERTIFICATE_REFS);
Element revocacionesElementoFirma =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + COMPLETE_REVOCATION_REFS);
// Construye las referencias del certificado
int size = respuestas.size();
if (size > 0) {
certRefs = new ArrayList<X509Certificate> (size);
for(int x=0; x < size; ++x) {
certRefs.add((respuestas.get(x)).getX509Cert());
}
}
if(certRefs != null)
{
// Se le agrega una Id única
Attr informacionElementoCertRef = doc.createAttribute(ID);
idNodoCertificateRefs = UtilidadTratarNodo.newID(doc, COMPLETE_CERTIFICATE_REFS);
informacionElementoCertRef.setValue(idNodoCertificateRefs);
certificadosElementosFirma.getAttributes().setNamedItem(informacionElementoCertRef);
Element elementoCertRefs =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CERT_REFS);
certificadosElementosFirma.appendChild(elementoCertRefs);
int longitud = certRefs.size();
for (int i=1; i<longitud; i++) // Se salta el primero porque es el certificado firmante
{
X509Certificate firmaCertificado = (X509Certificate) certRefs.get(i);
Element elementCertRef = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CERT);
// Creamos los atributos de UnSignedProperties
Attr uris = doc.createAttribute(URI);
// AppPerfect: Falso positivo. No son expresiones constantes
String idNueva = UtilidadTratarNodo.newID(doc, LIBRERIAXADES_CERT_PATH);
uris.setValue( ALMOHADILLA + idNueva );
respuestas.get(i).setIdCertificado(idNueva);
NamedNodeMap atributosURI = elementCertRef.getAttributes();
atributosURI.setNamedItem(uris);
Element resumenElementoCert = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CERT_DIGEST);
// Creamos el xades:DigestMethod
Element metodoResumenElemento = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + DIGEST_METHOD);
// Creamos los atributos de DigestMethod
Attr propiedadesFirmaAlgoritmo = doc.createAttribute(ALGORITHM);
propiedadesFirmaAlgoritmo.setValue(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA1);
NamedNodeMap cualidadesMetodoResumenElemento =
metodoResumenElemento.getAttributes();
cualidadesMetodoResumenElemento.setNamedItem(propiedadesFirmaAlgoritmo);
// Creamos el xades:DigestValue
String resumenCertificado =CADENA_VACIA;
try
{
MessageDigest resumenCertificadoTemp = MessageDigest.getInstance(SHA_1);
byte[] resumenMensajeByte =resumenCertificadoTemp.digest(firmaCertificado.getEncoded());
resumenCertificado = new String(Base64Coder.encode(resumenMensajeByte));
}
catch (NoSuchAlgorithmException nsae)
{
log.error(nsae);
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_16));
} catch (CertificateEncodingException e) {
log.error(e);
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23));
}
Element elementDigestValue =
doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + DIGEST_VALUE);
elementDigestValue.appendChild(
doc.createTextNode(resumenCertificado));
// Creamos el xades:IssuerSerial
Element elementoEmisorSerial =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + ISSUER_SERIAL);
// Creamos el xades:X509IssuerName
Element elementoX509EmisorNombre =
doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + X_509_ISSUER_NAME);
elementoX509EmisorNombre.appendChild(
doc.createTextNode(firmaCertificado.getIssuerX500Principal().getName()));
// Creamos el xades:X509SerialNumber
Element elementoX509NumeroSerial =
doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + X_509_SERIAL_NUMBER);
elementoX509NumeroSerial.appendChild(
doc.createTextNode(firmaCertificado.getSerialNumber().toString()));
//Add references
elementoEmisorSerial.appendChild(elementoX509EmisorNombre);
elementoEmisorSerial.appendChild(elementoX509NumeroSerial);
resumenElementoCert.appendChild(metodoResumenElemento);
resumenElementoCert.appendChild(elementDigestValue);
elementCertRef.appendChild(resumenElementoCert);
elementCertRef.appendChild(elementoEmisorSerial);
elementoCertRefs.appendChild(elementCertRef);
}
}
// Construye el valor de la respuesta del servidor OCSP
// bajo el nodo completo de la referencia de la revocación
Element elementOCSPRefs = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + OCSP_REFS);
revocacionesElementoFirma.appendChild(elementOCSPRefs);
Element elementOCSPRef = null;
String tiempoRespuesta = null;
byte[] mensajeRespuesta = null;
if (size > 0) {
// Se le agrega una Id única
Attr informacionElementoCertRef = doc.createAttribute(ID);
idNodoRevocationRefs = UtilidadTratarNodo.newID(doc, COMPLETE_REVOCATION_REFS);
informacionElementoCertRef.setValue(idNodoRevocationRefs);
revocacionesElementoFirma.getAttributes().setNamedItem(informacionElementoCertRef);
for(int x=0; x < size; ++x) {
RespYCerts respYCert = respuestas.get(x);
tiempoRespuesta = respYCert.getTiempoRespuesta();
TIPOS_RESPONDER tipoResponder = respYCert.getTipoResponder();
String valorResponder = respYCert.getResponderID();
mensajeRespuesta = respYCert.getRespOCSP();
elementOCSPRef = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + OCSP_REF);
// Creamos los atributos de UnSignedProperties
String idNueva = UtilidadTratarNodo.newID(doc, OCSP);
respYCert.setIdOCSP(idNueva);
Element identificadorElementoOCSP = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + OCSP_IDENTIFIER);
Attr uris = doc.createAttribute(URI);
uris.setValue( ALMOHADILLA + idNueva );
NamedNodeMap atributosURI = identificadorElementoOCSP.getAttributes();
atributosURI.setNamedItem(uris);
// Creamos el xades:DigestMethod
Element elementoRespondedorId = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + RESPONDER_ID);
Element responderFinal = elementoRespondedorId;
if (!(SCHEMA_XADES_111.equals(xadesSchema)) && !(SCHEMA_XADES_122.equals(xadesSchema))) {
Element hijo = null;
if (tipoResponder.equals(TIPOS_RESPONDER.BY_NAME)) {
hijo = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + BY_NAME);
}
else {
hijo = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + BY_KEY);
}
// TODO: tener en cuenta que podria no ser ninguno de estos valores en un futuro
elementoRespondedorId.appendChild(hijo);
responderFinal = hijo;
}
responderFinal.appendChild(doc.createTextNode(valorResponder));
Element elementoProdujoEn = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + PRODUCE_AT);
elementoProdujoEn.appendChild(doc.createTextNode(tiempoRespuesta));
identificadorElementoOCSP.appendChild(elementoRespondedorId);
identificadorElementoOCSP.appendChild(elementoProdujoEn);
Element valorYResumenElemento = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + DIGEST_ALG_AND_VALUE);
// Creamos el xades:DigestMethod
Element metodoResumenElemento = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + DIGEST_METHOD);
// Creamos los atributos de DigestMethod
Attr propiedadesAlgoritmoFirmado = doc.createAttribute(ALGORITHM);
propiedadesAlgoritmoFirmado.setValue(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA1);
NamedNodeMap atributosMetodoResumenElemento = metodoResumenElemento.getAttributes();
atributosMetodoResumenElemento.setNamedItem(propiedadesAlgoritmoFirmado);
// Creamos el xades:DigestValue
// El mensaje de la respuesta es el OCSPResponse
String digestCertificado =CADENA_VACIA;
try
{
MessageDigest resumenCertificadoTemp = MessageDigest.getInstance(SHA_1);
byte[] resumenMensajeByte = resumenCertificadoTemp.digest(mensajeRespuesta);
digestCertificado = new String(Base64Coder.encode(resumenMensajeByte));
}
catch (NoSuchAlgorithmException nsae)
{
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_20));
}
Element valorResumenElemento = doc.createElementNS(SCHEMA_DSIG, xmldsigNS + DOS_PUNTOS + DIGEST_VALUE);
valorResumenElemento.appendChild(doc.createTextNode(digestCertificado));
valorYResumenElemento.appendChild(metodoResumenElemento);
valorYResumenElemento.appendChild(valorResumenElemento);
elementOCSPRef.appendChild(identificadorElementoOCSP);
elementOCSPRef.appendChild(valorYResumenElemento);
elementOCSPRefs.appendChild(elementOCSPRef);
}
}
elementoPrincipal.appendChild(certificadosElementosFirma);
elementoPrincipal.appendChild(revocacionesElementoFirma);
return doc;
}
/**
* Este metodo añade la implementación del sello de tiempo de tipo 1 (implícito) para
* XADES-X según los esquemas 1.2.2 y 1.3.2.
* Los elementos sobre los que se calcula el sello son los siguientes:
* - SignatureValue
* - SignatureTimestamp
* - CompleteCertificateRefs
* - CompleteRevocationRefs
* Opcionalmente en el esquema 1.2.2 y 1.3.2:
* - AttributeCertificateRefs
* - AttributeRevocationRefs
*
* @param Element UnsignedSignatureProperties Nodo a partir del cual se añade el nodo SigAndRefsTimeStamp
* @return Documento de firma con formato XADES-X
* @throws AddXadesException En caso de error
*/
private Document addXadesX(Element UnsignedSignatureProperties)
throws AddXadesException
{
// Se obtiene el documento que contiene al nodo UnsignedSignatureProperties
Document doc = UnsignedSignatureProperties.getOwnerDocument();
// Se obtiene el nodo Signature que contiene al nodo UnsignedSignatureProperties (es el 4º padre, según esquema XAdES)
Node padre = UnsignedSignatureProperties.getParentNode();
for (int i = 0; i < 3; ++i) {
if (padre != null)
padre = padre.getParentNode();
else
// No se encuentra el nodo Signature
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_33) + ESPACIO +
SIGNATURE);
}
Element signatureElement = null;
if (padre != null && SIGNATURE.equals(padre.getLocalName()))
signatureElement = (Element)padre;
else
// No se encuentra el nodo Signature
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_33) + ESPACIO +
SIGNATURE);
// Se crea el nodo SigAndRefsTimeStamp
Element SigAndRefsTimeStampElement =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + SIG_AND_REFS_TIME_STAMP);
// Se coloca el nodo creado al final del nodo UnsignedSignatureProperties
UnsignedSignatureProperties.appendChild(SigAndRefsTimeStampElement);
// Se obtiene el listado de elementos de un sello de tiempo XAdES X
ArrayList<Element> elementosSelloX = null;
try {
elementosSelloX = UtilidadXadesX.obtenerListadoXADESX1imp(xadesSchema, signatureElement, SigAndRefsTimeStampElement);
} catch (BadFormedSignatureException e) {
throw new AddXadesException(e.getMessage(), e);
} catch (FirmaXMLError e) {
throw new AddXadesException(e.getMessage(), e);
}
// Se añaden nodos de referencia a los nodos obtenidos para el cálculo del sello (sólo para esquema 1.2.2)
if (SCHEMA_XADES_122.equals(xadesSchema)) {
// Se obtienen las Ids de los nodos del sello de tiempo X
ArrayList<String> elementosIdSelloX = UtilidadTratarNodo.obtenerIDs(elementosSelloX);
// Se crea una estructura con los nodos Include que contienen las URIs que apuntan a estas IDs
ArrayList<Element> nodosInclude = new ArrayList<Element> (elementosIdSelloX.size());
Iterator<String> itIds = elementosIdSelloX.iterator();
while (itIds.hasNext()) {
String Id = itIds.next();
Element includeNode = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + INCLUDE);
Attr includeNodeUri = doc.createAttribute(URI);
includeNodeUri.setValue(ALMOHADILLA + Id);
NamedNodeMap atributosNodo = includeNode.getAttributes();
atributosNodo.setNamedItem(includeNodeUri);
nodosInclude.add(includeNode);
}
// Se escribe en el nodo SigAndRefsTimeStamp el listado obtenido por orden
Iterator<Element> itIncludes = nodosInclude.iterator();
while (itIncludes.hasNext()) {
Element includeNode = itIncludes.next();
SigAndRefsTimeStampElement.appendChild(includeNode);
}
}
// Se obtiene el Array de bytes de los nodos obtenidos
byte[] byteData = null;
try {
byteData = UtilidadTratarNodo.obtenerByte(elementosSelloX);
} catch (FirmaXMLError e) {
throw new AddXadesException(e.getMessage(), e);
}
// Calculamos el hash del sello de tiempo y lo escribimos en el nodo como String del array de bytes calculado
TSCliente tsCli = null;
if(estadoProxy) {
System.setProperty("http.proxyHost", servidorProxy);
System.setProperty("http.proxyPort", Integer.toString(numeroPuertoProxy));
if (isProxyAuth) {
Authenticator.setDefault(new SimpleAuthenticator(proxyUser, proxyPass));
}
else {
Authenticator.setDefault(null);
}
}
tsCli = new TSCliente(servidorTSA,algoritmoTSA);
try {
byteData = tsCli.generarSelloTiempo(byteData);
} catch (TSClienteError e) {
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_11) + e.getMessage()) ;
}
String hashSelloX = new String(Base64Coder.encode(byteData));
// Escribimos el resultado en el nodo EncapsulatedTimeStamp
Element encapsulatedTimeStampNode = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + ENCAPSULATED_TIME_STAMP);
encapsulatedTimeStampNode.appendChild(doc.createTextNode(hashSelloX));
SigAndRefsTimeStampElement.appendChild(encapsulatedTimeStampNode);
return doc;
}
/**
* Este metodo añade la implementación del sello de tiempo de tipo 2 (explícito) para
* XADES-X según los esquemas 1.2.2 y 1.3.2.
* Los elementos sobre los que se calcula el sello son los siguientes:
* - CompleteCertificateRefs
* - CompleteRevocationRefs
* Opcionalmente en el esquema 1.2.2 y 1.3.2:
* - AttributeCertificateRefs
* - AttributeRevocationRefs
*
* @param Element UnsignedSignatureProperties Nodo a partir del cual se añade el nodo SigAndRefsTimeStamp
* @return Documento de firma con formato XADES-X
* @throws AddXadesException En caso de error
*/
private Document addXadesX2(Element UnsignedSignatureProperties)
throws AddXadesException
{
// Se obtiene el documento que contiene al nodo UnsignedSignatureProperties
Document doc = UnsignedSignatureProperties.getOwnerDocument();
// Se obtiene el nodo Signature que contiene al nodo UnsignedSignatureProperties (es el 4º padre, según esquema XAdES)
Node padre = UnsignedSignatureProperties.getParentNode();
for (int i = 0; i < 3; ++i) {
if (padre != null)
padre = padre.getParentNode();
else
// No se encuentra el nodo Signature
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_33) + ESPACIO +
SIGNATURE);
}
Element signatureElement = null;
if (padre != null && SIGNATURE.equals(padre.getLocalName()))
signatureElement = (Element)padre;
else
// No se encuentra el nodo Signature
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_33) + ESPACIO +
SIGNATURE);
// Se crea el nodo RefsOnlyTimeStamp
Element RefsOnlyTimeStampElement =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + REFS_ONLY_TIME_STAMP);
// Se coloca el nodo creado al final del nodo UnsignedSignatureProperties
UnsignedSignatureProperties.appendChild(RefsOnlyTimeStampElement);
// Se obtiene el listado de elementos de un sello de tiempo de tipo 2 XAdES X
ArrayList<Element> elementosSelloX2 = null;
try {
elementosSelloX2 = UtilidadXadesX.obtenerListadoXADESX2exp(xadesSchema, signatureElement, RefsOnlyTimeStampElement);
} catch (BadFormedSignatureException e) {
throw new AddXadesException(e.getMessage(), e);
} catch (FirmaXMLError e) {
throw new AddXadesException(e.getMessage(), e);
}
// Se añaden nodos de referencia a los nodos obtenidos para el cálculo del sello (sólo para esquema 1.2.2)
if (SCHEMA_XADES_122.equals(xadesSchema)) {
// Se obtienen las Ids de los nodos del sello de tiempo X de tipo 2
ArrayList<String> elementosIdSelloX2 = UtilidadTratarNodo.obtenerIDs(elementosSelloX2);
// Se crea una estructura con los nodos Include que contienen las URIs que apuntan a estas IDs
ArrayList<Element> nodosInclude = new ArrayList<Element> (elementosIdSelloX2.size());
Iterator<String> itIds = elementosIdSelloX2.iterator();
while (itIds.hasNext()) {
String Id = itIds.next();
Element includeNode = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + INCLUDE);
Attr includeNodeUri = doc.createAttribute(URI);
includeNodeUri.setValue(ALMOHADILLA + Id);
NamedNodeMap atributosNodo = includeNode.getAttributes();
atributosNodo.setNamedItem(includeNodeUri);
nodosInclude.add(includeNode);
}
// Se escribe en el nodo SigAndRefsTimeStamp el listado obtenido por orden
Iterator<Element> itIncludes = nodosInclude.iterator();
while (itIncludes.hasNext()) {
Element includeNode = itIncludes.next();
RefsOnlyTimeStampElement.appendChild(includeNode);
}
}
// Se obtiene el Array de bytes de los nodos obtenidos
byte[] byteData = null;
try {
byteData = UtilidadTratarNodo.obtenerByte(elementosSelloX2);
} catch (FirmaXMLError e) {
throw new AddXadesException(e.getMessage(), e);
}
// Calculamos el hash del sello de tiempo y lo escribimos en el nodo como String del array de bytes calculado
TSCliente tsCli = null;
if(estadoProxy) {
System.setProperty("http.proxyHost", servidorProxy);
System.setProperty("http.proxyPort", Integer.toString(numeroPuertoProxy));
if (isProxyAuth) {
Authenticator.setDefault(new SimpleAuthenticator(proxyUser, proxyPass));
}
else {
Authenticator.setDefault(null);
}
}
tsCli = new TSCliente(servidorTSA,algoritmoTSA);
try {
byteData = tsCli.generarSelloTiempo(byteData);
} catch (TSClienteError e) {
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_11) + e.getMessage()) ;
}
String hashSelloX = new String(Base64Coder.encode(byteData));
// Escribimos el resultado en el nodo EncapsulatedTimeStamp
Element encapsulatedTimeStampNode = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + ENCAPSULATED_TIME_STAMP);
encapsulatedTimeStampNode.appendChild(doc.createTextNode(hashSelloX));
RefsOnlyTimeStampElement.appendChild(encapsulatedTimeStampNode);
return doc;
}
/**
* Este metodo añade la implementacion para XADES-XL
*
* @param doc Documento de firma con formato XADES-X
* @param respuestas Cadena de certificación junto a sus respuestas OCSP del certificado de firma
* @return Documento de firma con formato XADES-XL
* @throws AddXadesException En caso de error
*/
private Document addXadesXL(Document doc, ArrayList<RespYCerts> respuestas)
throws AddXadesException
{
// Recogemos el nodo UnsignedSignatureProperties del cual dependen los nodos
// que hay que añadir para completar la firma XADES-XL
Element elementoPrincipal = null ;
NodeList nodos = doc.getElementsByTagNameNS(xadesSchema, UNSIGNED_SIGNATURE_PROPERTIES);
if(nodos.getLength() != 0)
{
elementoPrincipal = (Element)nodos.item(0);
}
else
{
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_19));
}
// Se añaden los certificados referenciados en el nodo CertificateValues
if(respuestas != null)
{
Element valorCertificadoElemento =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + CERTIFICATE_VALUES);
Iterator<RespYCerts> itResp = respuestas.iterator();
boolean hasNext = itResp.hasNext();
while (hasNext) {
RespYCerts resp = itResp.next();
hasNext = itResp.hasNext();
X509Certificate certificado = resp.getX509Cert();
Element elementoCertificadoEncapsulado = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + ENCAPSULATED_X_509_CERTIFICATE);
try {
elementoCertificadoEncapsulado.appendChild(
doc.createTextNode(new String(Base64Coder.encode(certificado.getEncoded()))));
String id = resp.getIdCertificado();
if ((id != null) && (!CADENA_VACIA.equals(id.trim())))
elementoCertificadoEncapsulado.setAttribute(ID, id);
} catch (CertificateEncodingException e) {
log.error(e);
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23));
} catch (DOMException e) {
log.error(e);
throw new AddXadesException(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_23));
}
valorCertificadoElemento.appendChild(elementoCertificadoEncapsulado);
}
elementoPrincipal.appendChild(valorCertificadoElemento);
// Se añade la respuesta del servidor OCSP
Element valoresElementosRevocados =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + REVOCATION_VALUES);
Element valorElementOCSP =
doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + OCSP_VALUES);
itResp = respuestas.iterator();
hasNext = itResp.hasNext();
while (hasNext) {
RespYCerts resp = itResp.next();
hasNext = itResp.hasNext();
Element valorElementoEncapsuladoOCSP = doc.createElementNS(xadesSchema, xadesNS + DOS_PUNTOS + ENCAPSULATED_OCSP_VALUE);
valorElementoEncapsuladoOCSP.appendChild(
doc.createTextNode(new String(Base64Coder.encode(resp.getRespOCSP()))));
valorElementoEncapsuladoOCSP.setAttribute(ID, resp.getIdOCSP());
valorElementOCSP.appendChild(valorElementoEncapsuladoOCSP);
}
valoresElementosRevocados.appendChild(valorElementOCSP);
elementoPrincipal.appendChild(valoresElementosRevocados);
}
return doc;
}
/**
* Este método se encarga de insertar las URIs de XADES-C en la firma
*
* @param doc, Documento con la firma xml
* @param listaArchivos, Lista de nombres de la respuestaOCSP y el path de certificación
* @return Document doc, Documento firmado con las nuevas URI´s
*/
public Document addURIXadesC(Document doc, ArrayList<NombreFicheros> listaArchivos)
{
NodeList completeCertificateRefs = null;
NodeList completeRevocationRefs = null;
completeCertificateRefs = doc.getElementsByTagNameNS(xadesSchema, COMPLETE_CERTIFICATE_REFS);
completeRevocationRefs = doc.getElementsByTagNameNS(xadesSchema, COMPLETE_REVOCATION_REFS);
if (completeCertificateRefs.getLength() == 0 || completeRevocationRefs.getLength() == 0) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_29));
return doc;
}
// A continuación se sacan las referencias OCSP del nodo OCSPRefs
Node ocspRefs = (Node)completeRevocationRefs.item(0).getFirstChild();
// Si ha encontrado el nodo OCSPRefs, se pasa a capturar su contenido
if (ocspRefs != null)
{
// Se saca la lista de referencias
NodeList refs = ocspRefs.getChildNodes();
int l = refs.getLength();
for (int i=0; i<l; i++)
{
// Sacamos los nodos OCSPRef uno por uno
Element ocspRef = (Element)refs.item(i); // Sacamos OCSPRef
NodeList list = ocspRef.getElementsByTagNameNS(xadesSchema, OCSP_IDENTIFIER);
// Si existe, incluimos la URI de su respuesta OCSP
if (ocspRef != null) {
Attr uri = doc.createAttribute(URI);
uri.setValue((listaArchivos.get(i)).getNameFileOCSPResp());
NamedNodeMap nodoOCSP = list.item(0).getAttributes();
nodoOCSP.setNamedItem(uri);
}
}
}
// A continuación se sacan los Certificados del nodo CertRefs
Node certRefs = (Node)completeCertificateRefs.item(0).getFirstChild();
// Si ha encontrado el nodo CertRefs, se pasa a capturar su contenido
if (certRefs != null)
{
// Se saca la lista de certificados
NodeList certs = certRefs.getChildNodes();
int l = certs.getLength();
if (l!=(listaArchivos.size()-1)) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_30));
}
for (int i=0; i<l; i++)
{
// Sacamos los nodos Cert uno por uno
Node certificado = certs.item(i); // Sacamos cert
if (certificado != null) {
// incluimos la uri
Attr uri = doc.createAttribute(URI);
uri.setValue(listaArchivos.get(i+1).getNameFileX509Cert()); // La posicion 0 es del certificado firmante
NamedNodeMap nodoCertificado = certificado.getAttributes();
nodoCertificado.setNamedItem(uri);
}
}
}
return doc;
}
/**
* Este método se encarga de guardar los archivos OCSP
* @param respuesta Cadena de certificación junto a sus respuestas OCSP del certificado de firma
* @param rutaFicheroFirmado Path donde se almacena la firma generada, para incluir los archivos de respuesta OCSP
* @param nomFichero Nombre del fichero firmado para asociar los ficheros de las respuestas OCSP con él
* @return un ArrayList con la lista de archivos guardados
*/
public ArrayList<NombreFicheros> saveOCSPFiles(ArrayList<RespYCerts> respuesta, String rutaFicheroFirmado, String nomFichero)
{
OutputStream f = null;
File directorio = null;
NombreFicheros nameFiles = null;
ArrayList<NombreFicheros> listaArchivos = new ArrayList<NombreFicheros>();
nomFichero = nomFichero.substring(0,nomFichero.indexOf(PUNTO));
String pathDir = rutaFicheroFirmado + DIR_OCSP;
if (respuesta != null) {
int longitud = respuesta.size();
for (int x = 0; x < longitud; ++x) { // Se incluyen todas las OCSPResp y todos los Cert del path menos el firmante
RespYCerts respAndCert = respuesta.get(x);
nameFiles = new NombreFicheros();
// Guardamos la respuesta OCSP
ByteArrayInputStream respuestaOCSP = new ByteArrayInputStream(respAndCert.getRespOCSP());
// AppPerfect: Falsos positivos. No son expresiones constantes
String nomRespOCSP = DIR_OCSP + FICH_OCSP_RESP + nomFichero + GUION + (x + 1) + EXTENSION_OCS;
log.debug(I18n.getResource(LIBRERIAXADES_FIRMAXML_DEBUG_6) + ESPACIO + rutaFicheroFirmado + nomRespOCSP);
try {
directorio = new File(pathDir);
directorio.mkdir();
f = new FileOutputStream(rutaFicheroFirmado + nomRespOCSP);
int ch = respuestaOCSP.read();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((ch) >= 0) {
bos.write(ch);
ch = respuestaOCSP.read();
}
f.write(bos.toByteArray());
} catch (FileNotFoundException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_27) + ESPACIO + e.getMessage());
} catch (IOException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_27) + ESPACIO + e.getMessage());
} finally {
try {
f.flush() ;
f.close() ;
nameFiles.setNameFileOCSPResp(nomRespOCSP);
} catch (IOException e) {
log.error(e.getMessage());
}
}
if (x!=0) // Se salta el certificado que firma
{
// Guardamos la cadena de certificados
X509Certificate certificado = respAndCert.getX509Cert();
String nomCert = DIR_CERTS + FICH_CERT_REF + nomFichero + GUION + (x) + EXTENSION_CER;
log.debug(I18n.getResource(LIBRERIAXADES_FIRMAXML_DEBUG_5) + ESPACIO + rutaFicheroFirmado + nomCert);
try {
directorio = new File(rutaFicheroFirmado + DIR_CERTS);
directorio.mkdir();
f = new BufferedOutputStream(new FileOutputStream(rutaFicheroFirmado + nomCert));
f.write(certificado.getEncoded()) ; // CertRef guardado
} catch (FileNotFoundException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_26) + ESPACIO + e.getMessage());
} catch (IOException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_26) + ESPACIO + e.getMessage());
} catch (CertificateEncodingException e) {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_26) + ESPACIO + e.getMessage());
} finally {
try {
f.flush() ;
//AppPerfect: Falso positivo
f.close() ;
nameFiles.setNameFileX509Cert(nomCert);
} catch (IOException e) {
log.error(e.getMessage());
}
}
} else {
nameFiles.setNameFileX509Cert(CADENA_VACIA);
}
listaArchivos.add(nameFiles);
}
} else {
log.error(I18n.getResource(LIBRERIAXADES_FIRMAXML_ERROR_27));
return null;
}
return listaArchivos;
}
}