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.base.algorithm;
22  
23  import java.security.NoSuchAlgorithmException;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import es.mityc.firmaJava.libreria.utilidades.UtilidadFirmaElectronica;
28  
29  /**
30   * Clase con constantes que identifican a algoritmos de obtención del hash de un documento.
31   * 
32   * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
33   */
34  public class HashingAlgorithm {
35  
36  	/**
37  	 * Algoritmo de digest MD2 definido en la RFC 1319.
38  	 */
39  	public final static String MD2	= "MD2";
40  	
41  	/**
42  	 * Algoritmo de digest MD5 definido en la RFC 1321.
43  	 */
44  	public final static String MD5	= "MD5";
45  	
46  	/**
47  	 * Algoritmo seguro de hash definido en el estándar de hash seguro NIST FIPS 180-1.
48  	 */
49  	public final static String SHA1	= "SHA-1";
50  	
51  	/**
52  	 * Algoritmo de hash definido por <a href="http://csrc.nist.gov/encryption/shs/dfips-180-2.pdf">
53  	 * Federal Information Processing Standard 180-2, Secure Hash Standard (SHS) </a>. 
54  	 * SHA-256 es una función de hash de 256 bits desarrollada para proporcionar 128 bits 
55  	 * de seguridad contra ataques por colisión.
56  	 */
57  	public final static String SHA256	= "SHA-256";
58  	
59  	/**
60  	 * Algoritmo de hash definido por <a href="http://csrc.nist.gov/encryption/shs/dfips-180-2.pdf">
61  	 * Federal Information Processing Standard 180-2, Secure Hash Standard (SHS) </a>. 
62  	 * SHA-512 es una función de hash de 512 bits desarrollada para proporcionar 256 bits 
63  	 * de seguridad contra ataques por colisión.
64  	 */
65  	public final static String SHA512	= "SHA-512";
66  	
67  	/**
68  	 * Algoritmo de hash definido por <a href="http://csrc.nist.gov/encryption/shs/dfips-180-2.pdf">
69  	 * Federal Information Processing Standard 180-2, Secure Hash Standard (SHS) </a>. 
70  	 * Se obtiene un hash de 384 bits truncando la salida del algoritmo SHA-512. 
71  	 */
72  	public final static String SHA384	= "SHA-384";
73  	
74  	/**
75  	 * Devuelve el algoritmo por defecto para digest (más utilizado)
76  	 * 
77  	 * @return SHA1
78  	 */
79  	public static final String getDefault() {
80  		return SHA1;
81  	}
82  	
83  	/*
84  	 * Mapa de para pasar de texto a OID
85  	 */
86  	private static Map<String,String> mapOIDs = new HashMap<String,String> ();
87  	
88  	/*
89  	 * Mapa de para pasar de OID a text
90  	 */
91  	private static Map<String,String> mapReverseOIDs = new HashMap<String,String> ();
92  	
93  	/*
94  	 * Mapa de para pasar de texto a tamaño del hash en bytes
95  	 */
96  	private static Map<String,Integer> mapBytesLengths = new HashMap<String,Integer> ();
97  	
98  	/*
99  	 * Mapa para pasar del algoritmo a la URI empleada en firmas XML
100 	 */
101 	private static Map<String,String> mapURIXMLSignatures = new HashMap<String,String> ();
102 	
103 	/*
104 	 * Mapa para pasar de la URI empleada en firmas XML al algoritmo
105 	 */
106 	private static Map<String,String> mapReverseURIXMLSignatures = new HashMap<String,String> ();
107 	
108 	static {
109 		mapOIDs.put(MD2, "1.3.14.7.2.2.1");
110 		mapOIDs.put(MD5, "1.2.840.113549.2.5");
111 		mapOIDs.put(SHA1, "1.3.14.3.2.26");
112 		mapOIDs.put(SHA256, "2.16.840.1.101.3.4.2.1");
113 		mapOIDs.put(SHA384, "2.16.840.1.101.3.4.2.2");
114 		mapOIDs.put(SHA512, "2.16.840.1.101.3.4.2.3");
115 		
116 		mapReverseOIDs.put("1.3.14.7.2.2.1", MD2);
117 		mapReverseOIDs.put("1.2.840.113549.2.5", MD5);
118 		mapReverseOIDs.put("1.3.14.3.2.26", SHA1);
119 		mapReverseOIDs.put("2.16.840.1.101.3.4.2.1", SHA256);
120 		mapReverseOIDs.put("2.16.840.1.101.3.4.2.2", SHA384);
121 		mapReverseOIDs.put("2.16.840.1.101.3.4.2.3", SHA512);
122 		
123 		mapBytesLengths.put(MD2, 16);
124 		mapBytesLengths.put(MD5, 16);
125 		mapBytesLengths.put(SHA1, 20);
126 		mapBytesLengths.put(SHA256, 32);
127 		mapBytesLengths.put(SHA384, 48);
128 		mapBytesLengths.put(SHA512, 64);
129 		
130 		mapURIXMLSignatures.put(MD2, UtilidadFirmaElectronica.DIGEST_ALG_MD2);
131 		mapURIXMLSignatures.put(MD5, UtilidadFirmaElectronica.DIGEST_ALG_MD5);
132 		mapURIXMLSignatures.put(SHA1, UtilidadFirmaElectronica.DIGEST_ALG_SHA1);
133 		mapURIXMLSignatures.put(SHA256, UtilidadFirmaElectronica.DIGEST_ALG_SHA256);
134 		mapURIXMLSignatures.put(SHA384, UtilidadFirmaElectronica.DIGEST_ALG_SHA384);
135 		mapURIXMLSignatures.put(SHA512, UtilidadFirmaElectronica.DIGEST_ALG_SHA512);
136 
137 		mapReverseURIXMLSignatures.put(UtilidadFirmaElectronica.DIGEST_ALG_MD2, MD2);
138 		mapReverseURIXMLSignatures.put(UtilidadFirmaElectronica.DIGEST_ALG_MD5, MD5);
139 		mapReverseURIXMLSignatures.put(UtilidadFirmaElectronica.DIGEST_ALG_SHA1, SHA1);
140 		mapReverseURIXMLSignatures.put(UtilidadFirmaElectronica.DIGEST_ALG_SHA256, SHA256);
141 		mapReverseURIXMLSignatures.put(UtilidadFirmaElectronica.DIGEST_ALG_SHA384, SHA384);
142 		mapReverseURIXMLSignatures.put(UtilidadFirmaElectronica.DIGEST_ALG_SHA512, SHA512);
143 
144 	}
145 
146 
147 	/**
148 	 * Devuelve el OID del algoritmo pasado como parámetro. Este algoritmo debe ser uno de
149 	 * los definidos como constantes en esta clase.
150 	 * 
151 	 * @param hashingAlgorithm Nombre del algoritmo
152 	 * @return OID del algoritmo o null si el algoritmo no existe
153 	 * @throws NoSuchAlgorithmException El algoritmo no existe
154 	 */
155 	public static String getOID(String hashingAlgorithm) throws NoSuchAlgorithmException {
156 		String oid = (String) mapOIDs.get(hashingAlgorithm);
157 		if (oid == null) {
158 			throw new NoSuchAlgorithmException ("El algoritmo de hashing '" + hashingAlgorithm + "' no existe dentro de Arangi");
159 		}
160 		
161 		return oid;
162 	}
163 	
164 	/**
165 	 * Devuelve el OID del algoritmo por defecto para digest (más utilizado)
166 	 * 
167 	 * @return SHA1 OID
168 	 */
169 	public static final String getDefaultOID() {
170 		try {
171 			return getOID(getDefault());
172 		} catch (NoSuchAlgorithmException e) {
173 			// No se va a dar
174 			return null;
175 		}
176 	}
177 	
178 	/**
179 	 * Devuelve el nombre del algoritmo en base a su OID. Este algoritmo debe ser uno de
180 	 * los definidos como constantes en esta clase.
181 	 * 
182 	 * @param oid OID del algoritmo
183 	 * @return Nombre del algoritmo
184 	 * @throws NoSuchAlgorithmException El algoritmo no existe
185 	 */
186 	public static String getAlgorithmName(String oid) throws NoSuchAlgorithmException {
187 		String nombre = (String) mapReverseOIDs.get(oid);
188 		if (nombre == null) {
189 			throw new NoSuchAlgorithmException ("El oid '" + oid + "' no existe dentro de Arangi");
190 		}
191 		
192 		return nombre;
193 	}
194 
195 	/**
196 	 * Devuelve el nombre del algoritmo en base a su nombre en algún sistema externo
197 	 * 
198 	 * @param oid OID del algoritmo
199 	 * @return Nombre del algoritmo
200 	 * @throws NoSuchAlgorithmException El algoritmo no existe
201 	 */
202 	public static String getAlgorithmFromExternalName(String externalName) throws NoSuchAlgorithmException {
203 		
204 		//-- Ver si existe sin mas o haciendo un upper case
205 		if (mapOIDs.containsKey(externalName)) {
206 			return externalName;
207 		}
208 		if (mapOIDs.containsKey(externalName.toUpperCase())) {
209 			return externalName.toUpperCase();
210 		}
211 		
212 		//-- Lo más habitual es que no tengan guión siendo sha
213 		if (externalName.toLowerCase().startsWith("sha") && externalName.indexOf("-") == -1) {
214 			String alg = "SHA-" + externalName.substring(3);
215 			if (mapOIDs.containsKey(alg)) {
216 				return alg;
217 			}
218 		}
219 		
220 		throw new NoSuchAlgorithmException ("No se reconoce el algoritmo de hash: " + externalName);
221 	}
222 
223 	/**
224 	 * Devuelve el tamaño del hash generado por el algoritmo
225 	 * 
226 	 * @param hashingAlgorithm Nombre del algoritmo
227 	 * @return Tamaño de los hash generados por el algoritmo o -1 si el
228 	 * 	algoritmo no existe en Arangí
229 	 */
230 	public static int getHashBytesLength(String hashingAlgorithm) {
231 		Integer length = (Integer) mapBytesLengths.get(hashingAlgorithm);
232 		if (length == null) {
233 			return -1;
234 		}
235 		
236 		return length;
237 	}
238 	
239 	/**
240 	 * Obtiene el nombre del algoritmo dependiendo del tamaño del hash
241 	 * 
242 	 * @param hash Hash
243 	 * @return Nombre del algoritmo con el que fue generado el hash
244 	 */
245 	public static String getAlgorithmNameFromHash (byte[] hash) {
246 		for(String key : mapBytesLengths.keySet()) {
247 			if (mapBytesLengths.get(key) == hash.length) {
248 				return key;
249 			}
250 		}
251 		//-- No se ha encontrado el algoritmo
252 		return getDefault();
253 	}
254 	
255 	/**
256 	 * Devuelve la URI a utilizar dentro de las firmas XML
257 	 * 
258 	 * @param hashingAlgorithm Nombre del algoritmo
259 	 * @return URI o null si el algoritmo no existe
260 	 * @throws NoSuchAlgorithmException El algoritmo no existe
261 	 */
262 	public static String getURIXMLSignatures(String hashingAlgorithm) throws NoSuchAlgorithmException {
263 		String uri = (String) mapURIXMLSignatures.get(hashingAlgorithm);
264 		if (uri == null) {
265 			throw new NoSuchAlgorithmException ("El algoritmo de hashing '" + hashingAlgorithm + "' no existe dentro de Arangi");
266 		}
267 		
268 		return uri;
269 	}
270 	
271 	/**
272 	 * Devuelve el nombre del algoritmo a partir de la URI empleada en firmas XML
273 	 * 
274 	 * @param uriURI empleada en firmas XML
275 	 * @return Nombre del algoritmo
276 	 * @throws NoSuchAlgorithmException El algoritmo no existe
277 	 */
278 	public static String getAlgorithmNameFromURIXMLSignatures(String uri) throws NoSuchAlgorithmException {
279 		String nombre = (String) mapReverseURIXMLSignatures.get(uri);
280 		if (nombre == null) {
281 			throw new NoSuchAlgorithmException ("La URI '" + uri + "' no existe dentro de Arangi");
282 		}
283 		
284 		return nombre;
285 	}
286 
287 	/**
288 	 * Compara dos algoritmos para ver cuál tiene como resultado un hash mayor
289 	 * 
290 	 * @param algorithm1 Algoritmo 1
291 	 * @param algorithm2 Algoritmo 2
292 	 * @return Cierto si el algoritmo 1 es mayor que el algoritmo 2
293 	 */
294 	public static boolean isGreater (String algorithm1, String algorithm2) {
295 		// alguno es nulo
296 		if (algorithm1 == null && algorithm2 != null) {
297 			return false;
298 		} else if (algorithm1 != null && algorithm2 == null) {
299 			return true;
300 		}
301 		
302 		// algoritmos de tipos diferentes
303 		if (algorithm1.toLowerCase().startsWith("sha") && !algorithm1.toLowerCase().startsWith("sha")) {
304 			return true;
305 		} else if(!algorithm1.toLowerCase().startsWith("sha") && algorithm1.toLowerCase().startsWith("sha")) {
306 			return false;
307 		}
308 
309 		// algoritmos del mismo tipo
310 		try {
311 			int nivelAlgoritmo1 = getNivelAlgoritmo(algorithm1);
312 			int nivelAlgoritmo2 = getNivelAlgoritmo(algorithm2);
313 			if (nivelAlgoritmo1 > nivelAlgoritmo2) {
314 				return true;
315 			} else {
316 				return false;
317 			}
318 		} catch (Exception e) {
319 			// error de parseo
320 			return false;
321 		}
322 	}
323 	
324 	//-- Métodos privados
325 
326 	private static int getNivelAlgoritmo (String algoritmo) {
327 		if (algoritmo.toLowerCase().startsWith("sha")) {
328 			// sha
329 			if (algoritmo.indexOf("-") > -1) {
330 				return Integer.parseInt((algoritmo.substring(4)));
331 			} else {
332 				return Integer.parseInt((algoritmo.substring(3)));
333 			}
334 		} else {
335 			// md
336 			if (algoritmo.indexOf("-") > -1) {
337 				return Integer.parseInt((algoritmo.substring(3)));
338 			} else {
339 				return Integer.parseInt((algoritmo.substring(2)));
340 			}
341 		}
342 	}
343 }