Как использовать облегченный API Bouncy Castle с AES и PBE

У меня есть блок зашифрованного текста, который был создан с использованием алгоритма JCE «PBEWithSHA256And256BitAES-CBC-BC». Провайдер - BouncyCastle. Что бы я хотел сделать, это расшифровать этот зашифрованный текст с помощью облегченного API BouncyCastle. Я не хочу использовать JCE, потому что для этого требуется установить файлы политики юрисдикции неограниченной силы.

Когда дело доходит до использования BC с PBE и AES, документации кажется немного.

Вот что у меня есть на данный момент. Код расшифровки запускается без исключения, но возвращает мусор.

Код шифрования,

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());
}

Код расшифровки,

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;
}

person Adrian    schedule 02.06.2010    source источник


Ответы (4)


Я попробовал это, и, похоже, это сработало. В значительной степени заимствовано из класса BC 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;
}
person President James K. Polk    schedule 02.06.2010
comment
строка pGen.generateDerivedParameters (256, 128); это установка длины ключа? - person george_h; 15.01.2014
comment
@george_h: 256 - длина ключа; 128 - длина IV. - person President James K. Polk; 16.01.2014

С вашим методом дешифрования возникло несколько проблем:

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;
}

Основные проблемы заключались в том, как вы выполняли дешифрование без использования блочного шифра, и в отсутствующем размере IV для метода generateDerivedParameters. Первую проблему я увидел довольно быстро, вторая была гораздо менее очевидной. Я обнаружил это только при просмотре теста Bouncy Castle под названием PBETest.

person laz    schedule 03.06.2010
comment
Спасибо, лаз. Ваше решение работает отлично, но, поскольку Грег ответил первым, это справедливо, я принимаю его ответ. - person Adrian; 03.06.2010
comment
Спасибо за ответ. Мне почему-то не хватало того, что GregS дал ответ, когда он это сделал. Мне интересно узнать, почему размер этого вектора инициализации должен быть 128 и как кто-то должен знать, что это требуется. Это была та часть, которая заставила меня повесить трубку. - person laz; 03.06.2010
comment
Великие умы думают одинаково :) Я знал, что AES - это 128-битный блочный шифр, поэтому IV для AES всегда будет 128 бит. Я мог бы использовать BlockCipher.getBlockSize () * 8, чтобы быть более универсальным. - person President James K. Polk; 03.06.2010

Сгенерировать ключ точно так же, как его аналоги JCE, нетривиально. Я только что бегло просмотрел ваш код. Нашел хотя бы одно несоответствие. JCE использует генератор PKCS12, но вы используете PKCS5S1.

Не удивляюсь, если есть другие отличия. Вам нужно сравнить свой код с исходным кодом BC.

person ZZ Coder    schedule 02.06.2010
comment
Спасибо за это ZZ. Я также пробовал использовать PKCS12, но это не имело никакого значения. - person Adrian; 02.06.2010

Я заметил, что ваш метод шифрования принимает пароль в виде массива символов, но дешифрование принимает пароль в виде байтов. В Java символы 16-битные, а байты 8-битные. Это может привести к получению разных ключей для шифрования / дешифрования и, возможно, объяснить проблемы с бессмысленными результатами расшифровки?

person paradoxpenguin    schedule 12.09.2012