View Javadoc

1   /**
2    * LICENCIA LGPL:
3    * 
4    * Esta librería es Software Libre; Usted puede redistribuirla y/o modificarla
5    * bajo los términos de la GNU Lesser General Public License (LGPL) tal y como 
6    * ha sido publicada por la Free Software Foundation; o bien la versión 2.1 de 
7    * la Licencia, o (a su elección) cualquier versión posterior.
8    * 
9    * Esta librería se distribuye con la esperanza de que sea útil, pero SIN 
10   * NINGUNA GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o 
11   * ADECUACIÓN A UN PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public 
12   * License (LGPL) para más detalles
13   * 
14   * Usted debe recibir una copia de la GNU Lesser General Public License (LGPL) 
15   * junto con esta librería; si no es así, escriba a la Free Software Foundation 
16   * Inc. 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte
17   * <http://www.gnu.org/licenses/>.
18   *
19   * Copyright 2011 Agencia de Tecnología y Certificación Electrónica
20   */
21  package es.accv.arangi.certificate.data;
22  
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.StringReader;
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.Map;
31  
32  import javax.xml.parsers.DocumentBuilder;
33  import javax.xml.parsers.DocumentBuilderFactory;
34  import javax.xml.xpath.XPath;
35  import javax.xml.xpath.XPathConstants;
36  import javax.xml.xpath.XPathExpression;
37  import javax.xml.xpath.XPathFactory;
38  
39  import org.apache.log4j.Logger;
40  import org.w3c.dom.Document;
41  import org.w3c.dom.Node;
42  import org.w3c.dom.NodeList;
43  import org.xml.sax.InputSource;
44  
45  import es.accv.arangi.base.certificate.Certificate;
46  import es.accv.arangi.base.certificate.data.CertificateDataService;
47  import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
48  import es.accv.arangi.base.exception.certificate.validation.ServiceException;
49  import es.accv.arangi.base.exception.certificate.validation.ServiceNotFoundException;
50  import es.accv.arangi.base.util.Util;
51  
52  /**
53   * Clase que implementa la obtención de datos de certificados mediante llamadas
54   * a los servicios web de &#64;Firma (puede verse la documentación en la zona
55   * segura de la página web de la ACCV: 
56   * <a href="https://www.accv.es:8445/secure_area/descargas/descargas/docsatFirma/bienvenida.htm" target="zonaSegura">
57   * https://www.accv.es:8445/secure_area/descargas/descargas/docsatFirma/bienvenida.htm</a>).
58   * 
59   * @author <a href="mailto:jgutierrez@accv.es">José Manuel Gutiérrez Núñez</a>
60   *
61   */
62  public class AFirmaCertificateDataService implements CertificateDataService {
63  
64  	/**
65  	 * URL del servicio web de &#64;Firma en explotación
66  	 */
67  	public static final String PRODUCTION_URL = "http://afirma.accv.es/afirmaws/services/ObtenerInfoCertificado";
68  	
69  	/**
70  	 * URL del servicio web de &#64;Firma en test
71  	 */
72  	public static final String TEST_URL = "http://preafirma.accv.es/afirmaws/services/ObtenerInfoCertificado";
73  	
74  	/*
75  	 * Nombre de la plantilla para generar llamadas sin securizar
76  	 */
77  	private static final String TEMPLATE_WITHOUT_SECURITY = "es/accv/arangi/base/template/arangi-afirma_data_template.xml";
78  
79  	/*
80  	 * Nombre de la plantilla para generar llamadas mediante usuario y contraseña
81  	 */
82  	private static final String TEMPLATE_USER_PASSWORD = "es/accv/arangi/base/template/arangi-afirma_data_secure_template.xml";
83  
84  	/*
85  	 * Logger de la clase
86  	 */
87  	Logger logger = Logger.getLogger(AFirmaCertificateDataService.class);
88  	
89  	/*
90  	 * URL de acceso a los servicios web de &#64;Firma
91  	 */
92  	private URL url;
93  	
94  	/*
95  	 * Identificador de la aplicación en &#64;Firma
96  	 */
97  	private String idAplicacion;
98  	
99  	/*
100 	 * Usuario 
101 	 */
102 	private String user;
103 	
104 	/*
105 	 * Contraseña
106 	 */
107 	private String password;
108 	
109 	//-- Constructores	
110 	
111 	/**
112 	 * Constructor por defecto: si se usa este constructor será necesario inicializar
113 	 * el objeto.
114 	 */
115 	public AFirmaCertificateDataService() {
116 		super();
117 	}
118 	
119 	/**
120 	 * Constructor en el que pasar la información necesaria para crear 
121 	 * este objeto.
122 	 * 
123 	 * @param url URL al servico web de &#64;Firma. Los posibles valores se pueden
124 	 * 	encontrar en los campos estáticos de esta clase PRODUCTION_URL y
125 	 *  TEST_URL.
126 	 * @param idAplicacion ID de su aplicación. Este valor se le entregó en
127 	 * 	el momento en que su aplicación fue dada de alta en la plataforma de
128 	 * 	&#64;Firma.
129 	 * @param user Nombre de usuario para el caso en que se deba realizar la
130 	 * 	llamada securizada mediante usuario y contraseña.
131 	 * @param password Contraseña para el caso en que se deba realizar la
132 	 * 	llamada securizada mediante usuario y contraseña.
133 	 */
134 	public AFirmaCertificateDataService(URL url, String idAplicacion, String user,
135 			String password) {
136 		super();
137 		initialize(url, idAplicacion, user, password);
138 	}
139 	
140 	/**
141 	 * Inicializa el objeto
142 	 * 
143 	 * @param url URL al servico web de &#64;Firma. Los posibles valores se pueden
144 	 * 	encontrar en los campos estáticos de esta clase PRODUCTION_URL y
145 	 *  TEST_URL.
146 	 * @param idAplicacion ID de su aplicación. Este valor se le entregó en
147 	 * 	el momento en que su aplicación fue dada de alta en la plataforma de
148 	 * 	&#64;Firma.
149 	 * @param user Nombre de usuario para el caso en que se deba realizar la
150 	 * 	llamada securizada mediante usuario y contraseña.
151 	 * @param password Contraseña para el caso en que se deba realizar la
152 	 * 	llamada securizada mediante usuario y contraseña.
153 	 */
154 	public void initialize (URL url, String idAplicacion, String user, String password) {
155 		this.url = url;
156 		this.idAplicacion = idAplicacion;
157 		this.user = user;
158 		this.password = password;
159 	}
160 
161 	/**
162 	 * Obtiene los datos de un certificado mediante una llamada a un servicio externo.
163 	 * 
164 	 * @param certificate Certificado 
165 	 * @param extraParams Parámetros extra por si fueran necesarios para 
166 	 * 	realizar la obtención
167 	 * @return Map con los valores obtenidos del certificado
168 	 * @throws ServiceNotFoundException El servicio no se encuentra disponible
169 	 * @throws ServiceException La llamada al servicio devuelve un error
170 	 */
171 	public Map<String,String> getData (Certificate certificate, 
172 			Map<String,Object> extraParams) throws ServiceNotFoundException, ServiceException {
173 		
174 		logger.debug("[AFirmaCertificateDataService.getData]::Entrada::" + Arrays.asList(new Object[] { certificate, extraParams }));
175 		
176 		//-- Obtener el template
177 		InputStream isTemplate;
178 		if (this.user != null && this.password != null) {
179 			isTemplate = new Util().getClass().getClassLoader().getResourceAsStream(TEMPLATE_USER_PASSWORD);
180 		} else {
181 			isTemplate = new Util().getClass().getClassLoader().getResourceAsStream(TEMPLATE_WITHOUT_SECURITY);
182 		}
183 
184 		//-- Obtener los parámetros
185 		HashMap<String, String> parameters = new HashMap<String, String>();
186 		if (this.user != null && this.password != null) {
187 			parameters.put("user", this.user);
188 			parameters.put("password", this.password);
189 		} 
190 		try {
191 			parameters.put("certificate", Util.encodeBase64(certificate.toDER()));
192 		} catch (NormalizeCertificateException e) {
193 			//-- El certificado ya se normalizó al entrar, no se dará el error
194 			logger.info ("[AFirmaCertificateDataService.getData]", e);
195 		}
196 		parameters.put("idAplicacion", this.idAplicacion);
197 
198 		//-- Obtener el mensaje
199 		String message;
200 		try {
201 			message = Util.fillTemplate(isTemplate, parameters);
202 		} catch (IOException e) {
203 			logger.info ("[AFirmaCertificateDataService.getData]::Error construyendo el mensaje", e);
204 			throw new ServiceException("Error construyendo el mensaje", e);
205 		}
206 		logger.debug("[AFirmaCertificateDataService.getData]::Se ha obtenido el mensaje a enviar a @Firma: " + message);
207 		
208 		//-- Enviar el mensaje
209 		StringBuffer respuesta = Util.sendPost(message, url);
210 		logger.debug("[AFirmaCertificateDataService.getData]::Se ha obtenido la respuesta de @Firma: " + respuesta);
211 
212 		//-- Variables para obtener el resultado
213 		Map<String, String> campos = null;
214 		
215 		//-- Comprobar si es mensaje de error
216 		if (respuesta.indexOf("codigoError") > -1) {
217 				logger.info("[AFirmaCertificateValidationService.validate]::La respuesta de @Firma es de error");
218 				throw new ServiceException(respuesta.substring(respuesta.indexOf("codigoError&gt;") + 15, respuesta.indexOf("&lt;/codigoError")) + " - " +
219 						respuesta.substring(respuesta.indexOf("descripcion&gt;") + 15, respuesta.indexOf("&lt;/descripcion")));
220 		}
221 		
222 		//-- Mensaje OK, extraer la información
223 		logger.debug("[AFirmaCertificateDataService.getData]::La respuesta de @Firma no es de error");
224 		String xml = respuesta.substring(respuesta.indexOf("&lt;?xml"), respuesta.indexOf("</ObtenerInfoCertificadoReturn>"));
225 		xml = xml.replaceAll("\\&lt;", "<").replaceAll("\\&gt;", ">");
226 		
227 		Document doc;
228 		XPath xpath;
229 		try {
230 			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
231 			dbf.setNamespaceAware(true);
232 			DocumentBuilder builder = dbf.newDocumentBuilder();
233 			doc = builder.parse(new InputSource(new StringReader(xml)));
234 			XPathFactory factory = XPathFactory.newInstance();
235 			xpath = factory.newXPath();
236 		} catch (Exception e) {
237 			logger.info("[AFirmaCertificateDataService.getData]::Error obteniendo el documento DOM o el XPath", e);
238 			throw new ServiceException("Error obteniendo el documento DOM o el XPath", e);
239 		}
240 
241 		//-- Obtener los campos
242 		try {
243 			campos = new HashMap<String, String>();
244 			XPathExpression expr = xpath.compile("//*[local-name()='ResultadoProcesamiento']/*[local-name()='InfoCertificado']/*[local-name()='Campo']");
245 			NodeList fieldNodes = (NodeList) expr.evaluate (doc, XPathConstants.NODESET);
246 			for (int i=0;i<fieldNodes.getLength();i++) {
247 				Node fieldNode = fieldNodes.item(i);
248 				String nombre = fieldNode.getChildNodes().item(0).getTextContent();
249 				String valor = fieldNode.getChildNodes().item(1).getTextContent();
250 				campos.put(nombre, valor);
251 			}
252 			logger.debug("[AFirmaCertificateDataService.getData]::Se han obtenido " + campos.size() + " campos");
253 
254 		} catch (Exception e) {
255 			logger.info("[AFirmaCertificateDataService.getData]::Error obteniendo los campos del XML de respuesta", e);
256 		}
257 		
258 		return campos;
259 	}
260 	
261 	/**
262 	 * Obtiene la URL de los servicios web de &#64;Firma en producción
263 	 * 
264 	 * @return URL
265 	 */
266 	public static URL getProductionURL () {
267 		try {
268 			return new URL(PRODUCTION_URL);
269 		} catch (MalformedURLException e) {
270 			// Siempore estará bien formada
271 			return null;
272 		}
273 	}
274 
275 	/**
276 	 * Obtiene la URL de los servicios web de &#64;Firma en test
277 	 * 
278 	 * @return URL
279 	 */
280 	public static URL getTestURL () {
281 		try {
282 			return new URL(TEST_URL);
283 		} catch (MalformedURLException e) {
284 			// Siempore estará bien formada
285 			return null;
286 		}
287 	}
288 
289 
290 }