2010-06-02 23 views
9

Tôi có một khối bản mã được tạo bằng thuật toán JCE "PBEWithSHA256And256BitAES-CBC-BC". Nhà cung cấp là BouncyCastle. Điều tôi muốn làm là giải mã bản mã này bằng cách sử dụng API nhẹ BouncyCastle. Tôi không muốn sử dụng JCE vì điều đó đòi hỏi phải cài đặt các tập tin chính sách quyền hạn không giới hạn quyền lực.Cách sử dụng API nhẹ Bouncy Castle với AES và PBE

Tài liệu có vẻ mỏng trên mặt đất khi nói đến việc sử dụng BC với PBE và AES.

Đây là những gì tôi có cho đến nay. Mã giải mã chạy không có ngoại lệ nhưng trả về rác.

Mã mã hóa,

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

Mã giải mã,

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

Trả lời

10

Tôi đã thử cách này và có vẻ như nó hoạt động. Vay mượn rất nhiều từ lớp 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; 
} 
+0

Cảm ơn Greg. Hoạt động tuyệt vời. – Adrian

+0

dòng pGen.generateDerivedParameters (256, 128); là thiết lập độ dài khóa? –

+1

@george_h: 256 là độ dài khóa; 128 là chiều dài IV. –

1

Nó không phải tầm thường để tạo ra chìa khóa chính xác như các đối tác JCE. Tôi chỉ duyệt qua mã của bạn một thời gian ngắn. Đã tìm thấy ít nhất một sự khác biệt. JCE sử dụng trình tạo PKCS12 nhưng bạn sử dụng PKCS5S1.

Tôi không ngạc nhiên nếu có những khác biệt khác. Bạn cần phải so sánh mã của bạn với nguồn BC.

+0

Cảm ơn ZZ đó. Tôi đã thử sử dụng PKCS12 là tốt, nhưng nó đã không làm cho bất kỳ sự khác biệt. – Adrian

8

Có một vài vấn đề với phương pháp giải mã của bạn:

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

Những vấn đề chính là cách bạn đã thực hiện giải mã mà không sử dụng một thuật toán mã hóa khối và thiếu kích thước IV theo phương pháp generateDerivedParameters. Tôi thấy vấn đề đầu tiên khá nhanh chóng, vấn đề thứ hai ít rõ ràng hơn nhiều. Tôi chỉ phát hiện ra rằng một trong những thông qua nhìn vào một bài kiểm tra Castle Bouncy gọi là PBETest.

+0

Cảm ơn laz. Giải pháp của bạn hoạt động hoàn hảo nhưng kể từ khi Greg trả lời đầu tiên nó chỉ công bằng tôi chấp nhận câu trả lời của mình. – Adrian

+0

Cảm ơn bạn đã phản hồi. Tôi bằng cách nào đó đã bỏ lỡ rằng GregS cung cấp một câu trả lời khi anh ấy đã làm. Tôi quan tâm đến việc tìm hiểu lý do tại sao kích thước cho vector khởi tạo đó cần phải là 128 và cách ai đó được cho là biết điều đó là bắt buộc. Đó là phần khiến tôi bị treo cổ. – laz

+1

Tâm trí tuyệt vời nghĩ như nhau :) Tôi biết rằng AES là một mật mã khối 128-bit, vì vậy IV cho AES sẽ luôn luôn là 128 bit. Tôi có thể đã sử dụng BlockCipher.getBlockSize() * 8 để được chung chung hơn. –

0

Tôi nhận thấy rằng phương thức mã hóa của bạn chấp nhận mật khẩu là mảng char nhưng giải mã chấp nhận mật khẩu dưới dạng byte. Trong các ký tự Java là 16-bit trong khi byte là 8-bit. Điều này có thể dẫn đến các khóa khác nhau để mã hóa/giải mã và có thể giải thích các vấn đề với kết quả giải mã vô nghĩa?

Các vấn đề liên quan