2010-06-02 31 views
9

Tengo un bloque de texto cifrado que se creó utilizando el algoritmo JCE "PBEWithSHA256And256BitAES-CBC-BC". El proveedor es BouncyCastle. Lo que me gustaría hacer es descifrar este texto cifrado usando la API ligera de BouncyCastle. No quiero usar JCE porque eso requiere instalar los archivos de política de jurisdicción de fuerza ilimitada.Cómo utilizar Bouncy Castle ligero API con AES y PBE

La documentación parece ser delgada en el suelo cuando se trata de usar BC con PBE y AES.

Esto es lo que tengo hasta ahora. El código de descifrado se ejecuta sin excepción, pero devuelve basura.

El código cifrado,

String password = "qwerty"; 
String plainText = "hello world"; 

byte[] salt = generateSalt(); 
byte[] cipherText = encrypt(plainText, password.toCharArray(), salt); 

private static byte[] generateSalt() throws NoSuchAlgorithmException { 
    byte salt[] = new byte[8]; 
    SecureRandom saltGen = SecureRandom.getInstance("SHA1PRNG"); 
    saltGen.nextBytes(salt); 
    return salt; 
} 

private static byte[] encrypt(String plainText, char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { 
    Security.addProvider(new BouncyCastleProvider()); 

    PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 20); 

    PBEKeySpec pbeKeySpec = new PBEKeySpec(password); 
    SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC"); 
    SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec); 

    Cipher encryptionCipher = Cipher.getInstance("PBEWithSHA256And256BitAES-CBC-BC"); 
    encryptionCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec); 

    return encryptionCipher.doFinal(plainText.getBytes()); 
} 

El código de descifrado,

byte[] decryptedText = decrypt(cipherText, password.getBytes(), salt); 

private static byte[] decrypt(byte[] cipherText, byte[] password, byte[] salt) throws DataLengthException, IllegalStateException, InvalidCipherTextException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { 
    BlockCipher engine = new AESEngine(); 
    CBCBlockCipher cipher = new CBCBlockCipher(engine); 

    PKCS5S1ParametersGenerator keyGenerator = new PKCS5S1ParametersGenerator(new SHA256Digest()); 
    keyGenerator.init(password, salt, 20); 

    CipherParameters keyParams = keyGenerator.generateDerivedParameters(256); 
    cipher.init(false, keyParams); 

    byte[] decryptedBytes = new byte[cipherText.length]; 
    int numBytesCopied = cipher.processBlock(cipherText, 0, decryptedBytes, 0); 

    return decryptedBytes; 
} 

Respuesta

10

Intenté esto y pareció funcionar. Prestado en gran medida de la clase AC org.bouncycastle.jce.provider.test.PBETest

private byte[] decryptWithLWCrypto(byte[] cipher, String password, byte[] salt, final int iterationCount) 
     throws Exception 
{ 
    PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(new SHA256Digest()); 
    char[] passwordChars = password.toCharArray(); 
    final byte[] pkcs12PasswordBytes = PBEParametersGenerator 
      .PKCS12PasswordToBytes(passwordChars); 
    pGen.init(pkcs12PasswordBytes, salt, iterationCount); 
    CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine()); 
    ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(256, 128); 
    aesCBC.init(false, aesCBCParams); 
    PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC, 
      new PKCS7Padding()); 
    byte[] plainTemp = new byte[aesCipher.getOutputSize(cipher.length)]; 
    int offset = aesCipher.processBytes(cipher, 0, cipher.length, plainTemp, 0); 
    int last = aesCipher.doFinal(plainTemp, offset); 
    final byte[] plain = new byte[offset + last]; 
    System.arraycopy(plainTemp, 0, plain, 0, plain.length); 
    return plain; 
} 
+0

Gracias Greg. Funciona genial. – Adrian

+0

la línea pGen.generateDerivedParameters (256, 128); ¿es eso la configuración de la longitud de la clave? –

+1

@george_h: 256 es la longitud de la clave; 128 es la longitud IV. –

1

No es trivial para generar la clave exactamente como los homólogos de la JCE. Solo busqué brevemente tu código. Encontró al menos una discrepancia. JCE usa el generador PKCS12 pero usted usa PKCS5S1.

No me sorprende si hay otras diferencias. Debes comparar tu código con la fuente BC.

+0

Gracias por eso ZZ. Traté de usar PKCS12 también, pero no hizo ninguna diferencia. – Adrian

8

Hubo algunos problemas con su método de descifrado:

private static byte[] decrypt(final byte[] bytes, final char[] password, final byte[] salt) throws DataLengthException, IllegalStateException, InvalidCipherTextException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { 

    final PBEParametersGenerator keyGenerator = new PKCS12ParametersGenerator(new SHA256Digest()); 
    keyGenerator.init(PKCS12ParametersGenerator.PKCS12PasswordToBytes(password), salt, 20); 
    final CipherParameters keyParams = keyGenerator.generateDerivedParameters(256, 128); 

    final BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); 
    cipher.init(false, keyParams); 

    final byte[] processed = new byte[cipher.getOutputSize(bytes.length)]; 
    int outputLength = cipher.processBytes(bytes, 0, bytes.length, processed, 0); 
    outputLength += cipher.doFinal(processed, outputLength); 

    final byte[] results = new byte[outputLength]; 
    System.arraycopy(processed, 0, results, 0, outputLength); 
    return results; 
} 

Los principales problemas eran la forma en que estaban realizando el descifrado sin usar un cifrado de bloques y la falta el tamaño IV al método generateDerivedParameters. Vi el primer problema bastante rápido, el segundo fue mucho menos obvio. Solo lo descubrí mirando una prueba de Bouncy Castle llamada PBETest.

+0

Gracias laz. Tu solución funciona a la perfección, pero como Greg respondió primero, es justo aceptar tu respuesta. – Adrian

+0

Gracias por los comentarios. De alguna manera me perdí de que GregS proporcionara una respuesta cuando lo hizo. Me interesa averiguar por qué el tamaño de ese vector de inicialización debe ser 128 y cómo se supone que alguien debe saber que es necesario. Esa fue la parte que me colgó. – laz

+1

Las grandes mentes piensan igual :) Sabía que AES es un cifrado de bloque de 128 bits, por lo que el IV para AES siempre será de 128 bits. Podría haber usado BlockCipher.getBlockSize() * 8 para ser más genérico. –

0

Me di cuenta de que su método de encriptación acepta una contraseña como una matriz de caracteres, pero el descifrado acepta la contraseña como bytes. En Java los caracteres son de 16 bits, mientras que los bytes son de 8 bits. Esto podría dar como resultado diferentes claves para cifrar/descifrar y tal vez explicar los problemas con los resultados descifrados de galimatías.

Cuestiones relacionadas