1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package es.accv.arangi.certificate.validation;
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.text.SimpleDateFormat;
29 import java.util.Arrays;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Map;
33
34 import javax.xml.parsers.DocumentBuilder;
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.xpath.XPath;
37 import javax.xml.xpath.XPathConstants;
38 import javax.xml.xpath.XPathExpression;
39 import javax.xml.xpath.XPathFactory;
40
41 import org.apache.log4j.Logger;
42 import org.bouncycastle.asn1.ASN1Encodable;
43 import org.bouncycastle.asn1.ASN1Enumerated;
44 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
45 import org.bouncycastle.asn1.ASN1Sequence;
46 import org.bouncycastle.asn1.DEROctetString;
47 import org.bouncycastle.asn1.DERSequence;
48 import org.bouncycastle.asn1.DERTaggedObject;
49 import org.w3c.dom.Document;
50 import org.w3c.dom.Node;
51 import org.w3c.dom.NodeList;
52 import org.xml.sax.InputSource;
53
54 import es.accv.arangi.base.certificate.Certificate;
55 import es.accv.arangi.base.certificate.validation.OCSPResponse;
56 import es.accv.arangi.base.certificate.validation.CertificateValidationService;
57 import es.accv.arangi.base.certificate.validation.CertificateValidationServiceResult;
58 import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
59 import es.accv.arangi.base.exception.certificate.validation.ServiceException;
60 import es.accv.arangi.base.exception.certificate.validation.ServiceNotFoundException;
61 import es.accv.arangi.base.util.Util;
62 import es.accv.arangi.base.util.validation.ValidationResult;
63
64
65
66
67
68
69
70
71
72
73
74 public class AFirmaCertificateValidationService implements CertificateValidationService {
75
76
77
78
79 public static final String PRODUCTION_URL = "http://afirma.accv.es/afirmaws/services/ValidarCertificado";
80
81
82
83
84 public static final String TEST_URL = "http://preafirma.accv.es/afirmaws/services/ValidarCertificado";
85
86
87
88
89 private static final String TEMPLATE_WITHOUT_SECURITY = "es/accv/arangi/base/template/arangi-afirma_validate_template.xml";
90
91
92
93
94 private static final String TEMPLATE_USER_PASSWORD = "es/accv/arangi/base/template/arangi-afirma_validate_secure_template.xml";
95
96
97
98
99 private static final int AFIRMA_RESULT_OK = 0;
100
101
102
103
104 private static final int AFIRMA_RESULT_NOK = 1;
105
106
107
108
109 private static final int AFIRMA_RESULT_CHAIN_VALIDATION_INVALID = 2;
110
111
112
113
114 private static final int AFIRMA_RESULT_REVOKED = 3;
115
116
117
118
119 private static final int AFIRMA_RESULT_ERROR = 4;
120
121
122
123
124 public static final SimpleDateFormat AFIRMA_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
125
126
127
128
129 Logger logger = Logger.getLogger(AFirmaCertificateValidationService.class);
130
131
132
133
134 private URL url;
135
136
137
138
139 private String idAplicacion;
140
141
142
143
144 private String user;
145
146
147
148
149 private String password;
150
151
152
153
154
155
156
157 public AFirmaCertificateValidationService() {
158 super();
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 public AFirmaCertificateValidationService(URL url, String idAplicacion, String user,
177 String password) {
178 super();
179 this.url = url;
180 this.idAplicacion = idAplicacion;
181 this.user = user;
182 this.password = password;
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 public void initialize (URL url, String idAplicacion, String user, String password) {
200 this.url = url;
201 this.idAplicacion = idAplicacion;
202 this.user = user;
203 this.password = password;
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217 public CertificateValidationServiceResult validate(Certificate certificate,
218 Map<String, Object> extraParams) throws ServiceNotFoundException, ServiceException {
219
220 logger.debug("[AFirmaCertificateValidationService.validate]::Entrada::" + Arrays.asList(new Object[] { certificate, extraParams }));
221
222
223 InputStream isTemplate;
224 if (this.user != null && this.password != null) {
225 isTemplate = new Util().getClass().getClassLoader().getResourceAsStream(TEMPLATE_USER_PASSWORD);
226 } else {
227 isTemplate = new Util().getClass().getClassLoader().getResourceAsStream(TEMPLATE_WITHOUT_SECURITY);
228 }
229
230
231 HashMap<String, String> parameters = new HashMap<String, String>();
232 if (this.user != null && this.password != null) {
233 parameters.put("user", this.user);
234 parameters.put("password", this.password);
235 }
236 try {
237 parameters.put("certificate", Util.encodeBase64(certificate.toDER()));
238 } catch (NormalizeCertificateException e) {
239
240 logger.info ("[AFirmaCertificateValidationService.validate]", e);
241 }
242 parameters.put("idAplicacion", this.idAplicacion);
243
244
245 String message;
246 try {
247 message = Util.fillTemplate(isTemplate, parameters);
248 } catch (IOException e) {
249 logger.info ("[AFirmaCertificateValidationService.validate]::Error construyendo el mensaje", e);
250 throw new ServiceException("Error construyendo el mensaje", e);
251 }
252 logger.debug("[AFirmaCertificateValidationService.validate]::Se ha obtenido el mensaje a enviar a @Firma: " + message);
253
254
255 StringBuffer respuesta = Util.sendPost(message, url);
256 logger.debug("[AFirmaCertificateValidationService.validate]::Se ha obtenido la respuesta de @Firma: " + respuesta);
257
258
259 Map<String, Object> campos = new HashMap<String, Object>();
260 int resultado = ValidationResult.RESULT_CERTIFICATE_CANNOT_BE_VALIDATED;
261 Date fechaRevocacion = null;
262 int motivoRevocacion = -1;
263 OCSPResponse respuestaOCSP = null;
264
265
266 if (respuesta.indexOf("codigoError") > -1) {
267
268
269 if (respuesta.indexOf("COD_066") > -1 || respuesta.indexOf("COD_063") > -1 || respuesta.indexOf("COD_064") > -1) {
270 logger.debug("[AFirmaCertificateValidationService.validate]::El certificado es desconocido");
271 resultado = ValidationResult.RESULT_CERTIFICATE_UNKNOWN;
272 return new CertificateValidationServiceResult(resultado, campos);
273 } else {
274 logger.info("[AFirmaCertificateValidationService.validate]::La respuesta de @Firma es de@Firma");
275 throw new ServiceException(respuesta.substring(respuesta.indexOf("codigoError>") + 15, respuesta.indexOf("</codigoError")) + " - " +
276 respuesta.substring(respuesta.indexOf("descripcion>") + 15, respuesta.indexOf("</descripcion")));
277 }
278 }
279
280
281 logger.debug("[AFirmaCertificateValidationService.validate]::La respuesta de @Firma no es de error");
282 String xml = respuesta.substring(respuesta.indexOf("<?xml"), respuesta.indexOf("</ValidarCertificadoReturn>"));
283 xml = xml.replaceAll("\\<", "<").replaceAll("\\>", ">");
284
285 Document doc;
286 XPath xpath;
287 try {
288 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
289 dbf.setNamespaceAware(true);
290 DocumentBuilder builder = dbf.newDocumentBuilder();
291 doc = builder.parse(new InputSource(new StringReader(xml)));
292 XPathFactory factory = XPathFactory.newInstance();
293 xpath = factory.newXPath();
294 } catch (Exception e) {
295 logger.info("[AFirmaCertificateValidationService.validate]::Error obteniendo el documento DOM o el XPath", e);
296 throw new ServiceException("Error obteniendo el documento DOM o el XPath", e);
297 }
298
299
300 try {
301 logger.debug("[AFirmaCertificateValidationService.validate]::Obteniendo el resultado");
302 XPathExpression expr = xpath.compile("//*[local-name()='ResultadoProcesamiento']/*[local-name()='ResultadoValidacion']/*[local-name()='resultado']");
303 Node resultNode = (Node) expr.evaluate (doc, XPathConstants.NODE);
304 int aFirmaResultado = Integer.parseInt(resultNode.getTextContent());
305 switch (aFirmaResultado) {
306 case AFIRMA_RESULT_OK:
307 resultado = ValidationResult.RESULT_VALID;
308 break;
309 case AFIRMA_RESULT_NOK:
310 resultado = ValidationResult.RESULT_CERTIFICATE_CANNOT_BE_VALIDATED;
311 break;
312 case AFIRMA_RESULT_CHAIN_VALIDATION_INVALID:
313 resultado = ValidationResult.RESULT_CERTIFICATE_CHAIN_VALIDATION_INVALID;
314 break;
315 case AFIRMA_RESULT_REVOKED:
316 resultado = ValidationResult.RESULT_CERTIFICATE_REVOKED;
317 expr = xpath.compile("//*[local-name()='InfoMetodoVerificacion']/*[local-name()='fechaRevocacion']");
318 resultNode = (Node) expr.evaluate (doc, XPathConstants.NODE);
319 String sFecha = resultNode.getTextContent().substring(0,10) + " " + resultNode.getTextContent().substring(15,23);
320 fechaRevocacion = AFIRMA_DATE_FORMAT.parse(sFecha);
321 expr = xpath.compile("//*[local-name()='InfoMetodoVerificacion']/*[local-name()='motivo']");
322 resultNode = (Node) expr.evaluate (doc, XPathConstants.NODE);
323 motivoRevocacion = Integer.parseInt(resultNode.getTextContent());
324 break;
325 case AFIRMA_RESULT_ERROR:
326 resultado = ValidationResult.RESULT_CERTIFICATE_CANNOT_BE_VALIDATED;
327 break;
328 }
329 logger.debug("[AFirmaCertificateValidationService.validate]::El resultado es " + resultado);
330 } catch (Exception e) {
331 logger.info("[AFirmaCertificateValidationService.validate]::Error obteniendo el resultado del XML de respuesta", e);
332 throw new ServiceException("Error obteniendo el resultado del XML de respuesta", e);
333 }
334
335
336 try {
337 XPathExpression expr = xpath.compile("//*[local-name()='ResultadoProcesamiento']/*[local-name()='InfoCertificado']/*[local-name()='Campo']");
338 NodeList fieldNodes = (NodeList) expr.evaluate (doc, XPathConstants.NODESET);
339 for (int i=0;i<fieldNodes.getLength();i++) {
340 Node fieldNode = fieldNodes.item(i);
341 String nombre = fieldNode.getChildNodes().item(0).getTextContent();
342 String valor = fieldNode.getChildNodes().item(1).getTextContent();
343 campos.put(nombre, valor);
344 }
345 logger.debug("[AFirmaCertificateValidationService.validate]::Se han obtenido " + campos.size() + " campos");
346
347 } catch (Exception e) {
348 logger.info("[AFirmaCertificateValidationService.validate]::Error obteniendo los campos del XML de respuesta", e);
349 }
350
351
352 try {
353 XPathExpression expr = xpath.compile("//*[local-name()='ResultadoProcesamiento']/*[local-name()='ResultadoValidacion']/*[local-name()='ValidacionEstado']/*[local-name()='InfoMetodoVerificacion']/*[local-name()='tokenOCSP']");
354 Node node = (Node) expr.evaluate (doc, XPathConstants.NODE);
355 if (node != null) {
356 logger.debug("[AFirmaCertificateValidationService.validate]::Existe la respuesta OCSP dentro de la respuesta de @Firma");
357 try {
358 respuestaOCSP = new OCSPResponse(Util.decodeBase64(node.getTextContent()));
359 } catch (Exception e) {
360
361 ASN1Sequence responseBytes = new DERSequence(new ASN1Encodable[] { new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.1"), new DEROctetString(Util.decodeBase64(node.getTextContent())) });
362 DERTaggedObject taggedObject = new DERTaggedObject(true, 0, responseBytes);
363 ASN1Sequence sequence = new DERSequence(new ASN1Encodable[] { new ASN1Enumerated(0), taggedObject });
364 respuestaOCSP = new OCSPResponse(sequence.getEncoded());
365 }
366 }
367
368 } catch (Exception e) {
369 logger.info("[AFirmaCertificateValidationService.validate]::Error obteniendo la respuesta OCSP", e);
370 }
371
372
373 CertificateValidationServiceResult certResult = new CertificateValidationServiceResult(resultado, campos);
374 if (fechaRevocacion != null) {
375 certResult.setRevocationDate(fechaRevocacion);
376 certResult.setRevocationReason(motivoRevocacion);
377 }
378 if (respuestaOCSP != null) {
379 certResult.setOcspResponse(respuestaOCSP);
380 }
381
382 return certResult;
383 }
384
385
386
387
388
389
390 public static URL getProductionURL () {
391 try {
392 return new URL(PRODUCTION_URL);
393 } catch (MalformedURLException e) {
394
395 return null;
396 }
397 }
398
399
400
401
402
403
404 public static URL getTestURL () {
405 try {
406 return new URL(TEST_URL);
407 } catch (MalformedURLException e) {
408
409 return null;
410 }
411 }
412
413
414 }