1 package es.caib.signatura.impl;
2
3 import java.io.*;
4 import java.lang.reflect.Constructor;
5 import java.security.InvalidKeyException;
6 import java.security.NoSuchAlgorithmException;
7 import java.security.NoSuchProviderException;
8 import java.security.SignatureException;
9 import java.security.cert.CertificateException;
10 import java.security.cert.CertificateExpiredException;
11 import java.security.cert.CertificateNotYetValidException;
12 import java.security.cert.X509Certificate;
13 import java.util.Collection;
14 import java.util.Iterator;
15 import java.util.List;
16
17 import es.caib.signatura.api.CertificateVerifyException;
18 import es.caib.signatura.api.ParsedCertificate;
19 import es.caib.signatura.api.SignatureProviderException;
20 import es.caib.signatura.api.SignatureVerifyException;
21 import es.caib.signatura.api.Certificate;
22
23 public class CertificateImpl implements Certificate {
24 private X509Certificate[] certificateChain = null;
25
26 /*
27 * Constructora de la clase
28 */
29 public CertificateImpl(X509Certificate[] certificateChain){
30 this.certificateChain = certificateChain;
31 }
32
33 /**
34 * Obtiene el nombre de la entidad certificadora
35 * @return nombre de la entidad certificadora
36 */
37 public String getCertCaName(){
38 if(certificateChain == null || certificateChain.length == 0){
39 throw new Error("No se encuentra cadena de certificación.");
40 }
41 return certificateChain[certificateChain.length-1].getSubjectX500Principal().getName();
42 }
43
44 /**
45 * Obtiene el nombre del certificado
46 * @return nombre del certificado (CommonName)
47 */
48
49 public String getCertSubjectCommonName(){
50 ParsedCertificate Parsed = this.getParsedCertificate();
51 return(Parsed.getName());
52 }
53
54 /**
55 * Obtiene la concatenación del SubjectAlternateName del certificado del usuario en la forma
56 * nombre0 = valor, nombre1 = valor, ...
57 * @return cadena con el SubjectAlternateName del certificado del usuario
58 */
59
60 public String getCertSubjectAlternativeNames(){
61 StringBuffer altNameSB = new StringBuffer("");
62 String altNameString = null;
63 try {
64 Collection generalNames = certificateChain[0].getSubjectAlternativeNames();
65 Iterator itr = generalNames.iterator();
66 while (itr.hasNext()) {
67 List list = (List) itr.next();
68
69 int tagNo = ((Integer) list.get(0)).intValue();
70 switch (tagNo) {
71 case 0:
72 altNameSB.append("," + "otherName=");
73 break;
74
75 case 1:
76 altNameSB.append("," + "rfc822Name=");
77 break;
78
79 case 2:
80 altNameSB.append("," + "dNSName=");
81 break;
82
83 case 3:
84 altNameSB.append("," + "x400Address=");
85 break;
86
87 case 4:
88 altNameSB.append("," + "directoryName=");
89 break;
90
91 case 5:
92 altNameSB.append("," + "ediPartyName=");
93 break;
94
95 case 6:
96 altNameSB.append("," + "uniformResourceIdentifier=");
97 break;
98
99 case 7:
100 altNameSB.append("," + "iPAddress=");
101 break;
102
103 case 8:
104 altNameSB.append("," + "registeredID=");
105 break;
106
107 }
108 altNameSB.append(list.get(1).toString());
109
110 // Quitamos la coma del principio
111 if (altNameSB.length() > 0) {
112 altNameString = altNameSB.substring(1, altNameSB.length());
113 }
114
115 }
116 }
117
118 catch (Exception ex) {
119 return null;
120 }
121
122 return altNameString;
123 }
124
125
126 /**
127 * Devuelve el certificado X509
128 * @return X509Certificate
129 */
130 public X509Certificate getCert(){
131 return certificateChain[0];
132 }
133
134 /**
135 * Devuelve el Seycon Principal a partir del certificado
136 * @return SeyconPrincipal
137 */
138 public ParsedCertificate getParsedCertificate(){
139 try {
140 ClassLoader cl = ClassLoaderFactory.getFactory().getMasterClassLoader();
141 Class clazz = cl .loadClass( "es.caib.signatura.provider.impl.common.ParsedCertificatImpl" );
142 Constructor constructor = clazz.getConstructor(new Class[] {certificateChain.getClass(), Boolean.TYPE});
143 ParsedCertificate parsed = (ParsedCertificate) constructor.newInstance(new Object [] {certificateChain, new Boolean(false)});
144 return new ParsedCertificateProxy (parsed);
145 } catch (Exception e) {
146 throw new RuntimeException(e);
147 }
148 }
149
150 /**
151 *
152 * Verifica el certificado y su cadena de certificación
153 * @param contentStream flujo de bytes del certificado original
154 * @return <code>true</code> si la verificación es correcta y <code>false</code> en caso contrario
155 * @throws SignatureProviderException si no se ha podido acceder a la API del proveedor de firma electrónica
156 * @throws IOException si no se ha podido acceder al fichero o si no se ha podido contactar con
157 * el servidor de sello de tiempo
158 * @throws SignatureVerifyException si no se ha podido realizar el proceso de verificación
159 */
160 public boolean verify()
161 throws IOException, CertificateVerifyException{
162 boolean isValid = true;
163 isValid = isValid && verifyCertificateChain();
164 if(!isValid){
165 /*
166 * Podría darse el caso que la cadena de certificación estuviera invertida
167 */
168 invertCertificateChain();
169 isValid = isValid && verifyCertificateChain();
170 if(!isValid){
171 /*
172 * Si no es válida la cadena de certificación una vez invertida se asume
173 * que la cadena de certificación original es la válida
174 */
175 invertCertificateChain();
176 }
177 }
178 return isValid;
179 }
180
181 /**
182 * Operación que invierte la cadena de certificación que contiene la clase
183 */
184 private void invertCertificateChain(){
185 for(int i = 0;i < certificateChain.length / 2;i++){
186 X509Certificate temporalCertificate = certificateChain[certificateChain.length - 1 - i];
187 certificateChain[certificateChain.length - 1 - i] = certificateChain[i];
188 certificateChain[i] = temporalCertificate;
189 }
190 }
191
192 /**
193 *
194 * Verifica el certificado y su cadena de certificación
195 * @param contentStream flujo de bytes del certificado original
196 * @return <code>true</code> si la verificación es correcta y <code>false</code> en caso contrario
197 * @throws SignatureProviderException si no se ha podido acceder a la API del proveedor de firma electrónica
198 * @throws IOException si no se ha podido acceder al fichero o si no se ha podido contactar con
199 * el servidor de sello de tiempo
200 * @throws SignatureVerifyException si no se ha podido realizar el proceso de verificación
201 */
202 private boolean verifyCertificateChain()
203 throws IOException, CertificateVerifyException{
204 boolean isValid = true;
205 /*
206 * verificación basada en fechas
207 */
208 for(int i = 0;i < certificateChain.length && isValid;i++){
209 try {
210 certificateChain[i].checkValidity();
211 } catch (CertificateExpiredException cee) {
212 isValid = false;
213 } catch (CertificateNotYetValidException cve) {
214 isValid = false;
215 }
216 }
217 /*
218 * verificación de firmas
219 */
220 for(int i = 0;(i < certificateChain.length - 1) && isValid;i++){
221 try {
222 certificateChain[i].verify(certificateChain[i + 1].getPublicKey());
223 } catch (InvalidKeyException e) {
224 isValid = false;
225 } catch (CertificateException e) {
226 isValid = false;
227 } catch (NoSuchAlgorithmException e) {
228 throw new CertificateVerifyException(e);
229 } catch (NoSuchProviderException e) {
230 throw new CertificateVerifyException(e);
231 } catch (SignatureException e) {
232 isValid = false;
233 }
234 }
235 /*
236 * verificación mediante Web Services
237 * En dicha verificación se valida el certificado raiz
238 */
239 try{
240 isValid = isValid && verifyCertificateWebServices(certificateChain);
241 }catch(Exception e){
242 throw new CertificateVerifyException(e);
243 }
244 return isValid;
245 }
246
247 /*
248 * Validación mediante Web Services de la cadena de certificación
249 */
250 private boolean verifyCertificateWebServices(X509Certificate[] certificateChain)throws CertificateVerifyException{
251 /* TODO: commons-logging*/
252 return true;
253 /*
254 String result = verifyWebServices(certificateChain);
255 return WSResponseIsAVerifiedMessage(result);
256 */
257 }
258
259 /*
260 * Validación mediante Web Services de la cadena de certificación
261 */
262 /*
263 private String verifyWebServices(X509Certificate[] certificateChain) throws CertificateVerifyException{
264 String toReturn = new String();
265 try{
266 URLClassLoader urlClassLoader = (URLClassLoader)ClassLoaderFactory.getFactory().getMasterClassLoader();
267 Class c;
268 c = urlClassLoader.loadClass("es.caib.signatura.cliente.ValidadorCertificadosEmbedded");
269 Constructor constructor = c.getConstructor(new Class [] {});
270 IValidadorCertificados iValidadorCertificados = (IValidadorCertificados)constructor.newInstance(new Object [] {});
271 //ValidadorCertificadosEmbedded validadorCertificados = new ValidadorCertificadosEmbedded();
272 toReturn = iValidadorCertificados.validarCertificadoParaFirma(certificateChain);
273 } catch (Exception e) {
274 throw new CertificateVerifyException(e);
275 }
276 return toReturn;
277 }
278
279 private boolean WSResponseIsAVerifiedMessage(String result) throws CertificateVerifyException {
280 String content = new String();
281 try {
282 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
283 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
284 Document document;
285 StringBufferInputStream inputStream = new StringBufferInputStream(result);
286 document = documentBuilder.parse((InputStream)inputStream);
287 //Element element = document.getDocumentElement();
288 try{
289 NodeList nodeList = document.getElementsByTagName("validado");
290 Node node = nodeList.item(0);
291 Element element = (Element) node;
292 String tagName = element.getTagName();
293 Node textData = null;
294 boolean stop = false;
295 NodeList nodeDataList = element.getChildNodes();
296 for(int i = 0;i < nodeDataList.getLength() && !stop;i++){
297 textData = nodeDataList.item(i);
298 stop = textData.getNodeType() == org.w3c.dom.Node.TEXT_NODE;
299 }
300 content = ((org.w3c.dom.Text)textData).getData();
301 }catch(Exception e){
302 // Para no llevar a malos entendidos
303 // se da la firma como falsa
304 //
305 return false;
306 }
307 if(content == null){
308 throw new Exception("No content in 'validado' TAG. Response message: " + result);
309 }
310 }catch(Exception e){
311 throw new CertificateVerifyException(e);
312 }
313 return content.compareToIgnoreCase("true") == 0;
314 }
315 */
316
317 }