Saltar al contenido

PBKDF2 con bouncycastle en Java

Solución:

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

A continuación, se explican más detalles sobre por qué es así y por qué obtiene resultados de diferentes tamaños.

Primero, cuando está construyendo una JCE KeySpec, el parámetro keyLength solo expresa “una preferencia” al proveedor sobre el tamaño de clave que desea. De los documentos de la API:

Nota: esto se usa para indicar la preferencia en la longitud de la clave para cifrados 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 la fuente de JCEPBEKey, por lo que debe 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 getKeySize() método en el devuelto keybc variable 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é 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 se 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 usar para el hash de contraseñas usa PKCS # 5 v2 esquema 2 (PKCS5S2) en su lugar. Es por eso que está obteniendo resultados diferentes.

Eché un vistazo rápido a los proveedores de JCE registrados en BouncyCastleProvider, pero no pude ver alguna algoritmos de generación de claves que usaban PKCS5S2, y mucho menos uno que también lo usa con HMAC-SHA-1.

Así que supongo que está atascado con el uso de la implementación de Sun (modo n. ° 1 anterior) y la pérdida de portabilidad en otras JVM, o con el uso de las clases de Bouncy Castle directamente (modo n. ° 2 anterior) y requiriendo la biblioteca 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 manera puedo generar una salida KDF que sea 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();
    }
}

PBKDF2WithHmacSHA1 ya es compatible con BouncyCastle 1.60

https://www.bouncycastle.org/specifications.html Hash de contraseñas y PBE

Prueba aprobada con OpenJDK Runtime Environment 18.9 (compilación 11.0.1 + 13):

    Security.addProvider(new BouncyCastleProvider());

    String password = "xrS7AJk+V6L8J?B%";
    SecureRandom rnd = new SecureRandom();
    int saltLength = 16;
    int keyLength = 128;
    int iterationCount = 10000;

    byte[] salt = new byte[saltLength];
    rnd.nextBytes(salt);

//SunJCE
    SecretKeyFactory factorySun = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1", "SunJCE");
    KeySpec keyspecSun = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength);
    SecretKey keySun = factorySun.generateSecret(keyspecSun);
    System.out.println(keySun.getClass().getName());
    System.out.println(Hex.toHexString(keySun.getEncoded()));

//BouncyCastle  
    SecretKeyFactory factoryBC = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1", "BC");
    KeySpec keyspecBC = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength);
    SecretKey keyBC = factoryBC.generateSecret(keyspecBC);
    System.out.println(keyBC.getClass().getName());
    System.out.println(Hex.toHexString(keyBC.getEncoded()));

    Assert.assertArrayEquals(keySun.getEncoded(), keyBC.getEncoded());

La salida es:

com.sun.crypto.provider.PBKDF2KeyImpl
e9b01389fa91a6172ed6e95e1e1a2611
org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey
e9b01389fa91a6172ed6e95e1e1a2611
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *