java bouncycastle jce pbkdf2

PBKDF2 con bouncycastle en Java



jce (2)

Intento almacenar de forma segura una contraseña en una base de datos y para eso elegí almacenar su hash generado usando la función PBKDF2. Quiero hacer esto usando la biblioteca hinchable del castillo, pero no sé por qué no puedo hacer que funcione usando la interfaz JCE ... El problema es que generar el hash en 3 modos diferentes:
1. utilizando la clave secreta PBKDF2WithHmacSHA1 proporcionada por el sol
2. usando el castillo hinchable api directamente
3. usando el castillo hinchable a través de JCE
da como resultado 2 valores distintos: uno común para los primeros dos y uno para el tercero.

Aquí está mi código:

//Mode 1 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec keyspec = new PBEKeySpec("password".toCharArray(), salt, 1000, 128); Key key = factory.generateSecret(keyspec); System.out.println(key.getClass().getName()); System.out.println(Arrays.toString(key.getEncoded())); //Mode 2 PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(); generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(("password").toCharArray()), salt, 1000); KeyParameter params = (KeyParameter)generator.generateDerivedParameters(128); System.out.println(Arrays.toString(params.getKey())); //Mode 3 SecretKeyFactory factorybc = SecretKeyFactory.getInstance("PBEWITHHMACSHA1", "BC"); KeySpec keyspecbc = new PBEKeySpec("password".toCharArray(), salt, 1000, 128); Key keybc = factorybc.generateSecret(keyspecbc); System.out.println(keybc.getClass().getName()); System.out.println(Arrays.toString(keybc.getEncoded())); System.out.println(keybc.getAlgorithm());

Sé que PBKDF2 se implementa usando HMAC SHA1, por eso escogí como algoritmo en el último método el "PBEWITHHMACSHA1" que tomé del castillo hinchable de Java Docs.

El resultado es el siguiente:

com.sun.crypto.provider.SunJCE_ae [-53, 29, 113, -110, -25, 76, 115, -127, -64, 74, -63, 102, 75, 81, -21, 74] [-53, 29, 113, -110, -25, 76, 115, -127, -64, 74, -63, 102, 75, 81, -21, 74] org.bouncycastle.jce.provider.JCEPBEKey [14, -47, -87, -16, -117, -31, 91, -121, 90, -68, -82, -31, -27, 5, -93, -67, 30, -34, -64, -40] PBEwithHmacSHA

¿Algunas ideas?


En resumen, la razón para la diferencia es que el algoritmo PBKDF2 en los modos # 1 y # 2 usa PKCS # 5 v2 scheme 2 (PKCS5S2) para la generación iterativa de claves, pero el proveedor BouncyCastle para "PBEWITHHMACSHA1" en el modo # 3 usa el PKCS # 12 algoritmo v1 (PKCS12) en su lugar. Estos son algoritmos de generación de claves completamente diferentes, por lo que obtienes resultados diferentes.

Más detalles sobre por qué esto es así y por qué obtiene resultados de diferentes tamaños se explica a continuación.

En primer lugar, cuando está construyendo un JCE KeySpec, el parámetro keyLength solo expresa "una preferencia" al proveedor con el tamaño de clave que desea. De los documentos API :

Nota: esto se usa para indicar la preferencia en la longitud de la clave para cifrado de tamaño de clave variable. El tamaño real de la clave depende de la implementación de cada proveedor.

Los proveedores de Bouncy Castle no parecen respetar este parámetro, a juzgar por el origen de JCEPBEKey , por lo que debería esperar obtener una clave de 160 bits de cualquier proveedor de BC que use SHA-1 cuando use la API de JCE.

Puede confirmar esto accediendo mediante programación al método getKeySize() en la variable keybc devuelta en su código de prueba:

Key keybc = factorybc.generateSecret(keyspecbc); // ... Method getKeySize = JCEPBEKey.class.getDeclaredMethod("getKeySize"); getKeySize.setAccessible(true); System.out.println(getKeySize.invoke(keybc)); // prints ''160''

Ahora, para comprender a qué se corresponde el proveedor "PBEWITHHMACSHA1", puede encontrar lo siguiente en la fuente de BouncyCastleProvider :

put("SecretKeyFactory.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA");

Y la implementación de JCESecretKeyFactory.PBEWithSHA ve así:

public static class PBEWithSHA extends PBEKeyFactory { public PBEWithSHA() { super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0); } }

Puede ver arriba que esta fábrica de claves utiliza el algoritmo PKCS # 12 v1 ( PKCS12 ) para la generación de claves iterativas. Pero el algoritmo PBKDF2 que desea utilizar para el hash de contraseñas utiliza PKCS # 5 v2 scheme 2 ( PKCS5S2 ) en su lugar. Es por eso que obtienes resultados diferentes.

BouncyCastleProvider un vistazo rápido a los proveedores de JCE registrados en BouncyCastleProvider , pero no pude ver ningún algoritmo de generación de claves que utilizara PKCS5S2 en absoluto, y mucho menos uno que también lo utilizara con HMAC-SHA-1.

Supongo que está atrapado con el uso de la implementación de Sun (modo # 1 anterior) y la pérdida de portabilidad en otras JVM, o el uso directo de las clases de Bouncy Castle (modo # 2 anterior) y la biblioteca de BC en tiempo de ejecución.

De cualquier manera, probablemente debería cambiar a claves de 160 bits, por lo que no está truncando innecesariamente el hash SHA-1 generado.


Encontré un método BC Crypto-Only (en realidad, del paquete cms de BC) que funciona para producir una codificación de contraseña basada en UTF-8. De esta forma puedo generar salida KDF que es compatible con

http://packages.python.org/passlib/lib/passlib.hash.cta_pbkdf2_sha1.html#passlib.hash.cta_pbkdf2_sha1

private byte[] calculatePasswordDigest(char[] pass, byte[] salt, int iterations) throws PasswordProtectionException { try { /* JCE Version (does not work as BC uses PKCS12 encoding) SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWITHHMACSHA1","BC"); PBEKeySpec ks = new PBEKeySpec(pass, salt, iterations,160); SecretKey digest = kf.generateSecret(ks); return digest.getEncoded(); */ PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(); gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(pass), salt, iterations); byte[] derivedKey = ((KeyParameter)gen.generateDerivedParameters(160)).getKey(); return derivedKey; } catch(Exception e) { LOG.error("Failed to strengthen the password with PBKDF2.",e); throw new PasswordProtectionException(); } }