簡體   English   中英

使用 BouncyCastle SSL 使用 keyFile 進行 Java AES 加密

[英]Java AES encryption with keyFile using BouncyCastle SSL

我正在嘗試將 SSL 中的以下命令轉換為 Java

openssl enc -in <inputfilename> -out <file_to_encrypt> -e -aes256 -k s_key

s_key 是提供的文件,其中包含將用於加密和解密的密鑰

要完成的步驟: 1 - 讀取密鑰文件 2 - 使用它進行 AES 加密以加密文件 inputfilename 3 - 使用密鑰解密相同。

我是加密新手,下面是我迄今為止編寫的加密代碼,但我遇到了問題。

Path path = Paths.get("/home/debashishd/Downloads/s_key");
String content = new String(Files.readAllBytes(Paths.get("/home/debashishd/Downloads/s_key")));
    
String Test_message = "Hello this is Roxane";
    
byte[] keyValue = Files.readAllBytes(path);
ByteArrayInputStream byteIS = new ByteArrayInputStream(keyValue);
    
OpenSSLPBEParametersGenerator gen = new OpenSSLPBEParametersGenerator();
OpenSSLPBEParametersGenerator gen1 = gen;
byte[] saltBytes = Hex.decode(salt.getBytes());
gen1.init(keyValue);
CipherParameters cp = gen1.generateDerivedParameters(256);

byte[] keyBytes = ((KeyParameter)cp);           
SecretKeySpec secretKey = new SecretKeySpec(keyBytes,"AES");
System.out.println(secretKey);
    
Cipher cipher;
Cipher decryptCipher;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));

String encrypt_value = getEncoder().encodeToString(cipher.doFinal(Test_message.getBytes(StandardCharsets.UTF_8)));
    
System.out.println("Encrypted value: " + encrypt_value);
    
decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));
String Decrypt_result = new String(decryptCipher.doFinal(getDecoder().decode(encrypt_value)));
System.out.println("Decrypted value: " + Decrypt_result);

是否需要進行任何更改才能實現上述加密和解密

預期輸出:

Encrypted value: jvggHDPa58+/zQ+HyGUEk/ypndXbatE+b+hBBqiinABOIwxJ7FXqnDb5j813fPwwm/D6d2Y2uh+k4qD77QMqOg==
Decrypted value: Hello this is Roxane

為了與 OpenSSL 語句兼容:

  • 必須生成隨機的 8 字節鹽
  • 必須使用EVP_BytesToKey()和 salt 導出 32 字節密鑰和 16 字節 IV
  • 結果必須以 OpenSSL 格式給出:
    <ASCII Encoding of Salted__>|<salt>|<ciphertext>

對於EVP_BytesToKey()您可以應用您已經建議的OpenSSLPBEParametersGenerator類。

EVP_BytesToKey()使用摘要。 從 v1.1.0 SHA256 開始,在早期版本的 OpenSSL 中默認應用 MD5。 可以使用-md5選項設置摘要。 代碼和 OpenSSL 語句必須都使用相同的摘要才能兼容。 OpenSSLPBEParametersGenerator允許在構造函數中指定摘要,默認為 MD5。

以下代碼基於您的代碼,即對EVP_BytesToKey()使用OpenSSLPBEParametersGenerator但還考慮了以上幾點。 不是加密整個數據,而是應用流並且數據被逐塊加密,因此即使是大文件也可以處理:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.io.CipherOutputStream;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;

...

String inputPath = "...";
String outputPath = "...";
String passwordStr = "...";

// Generate random 8 bytes salt
SecureRandom random = new SecureRandom();
byte salt[] = new byte[8];
random.nextBytes(salt);

// Derive 32 bytes key (AES_256) and 16 bytes IV
byte[] password = passwordStr.getBytes(StandardCharsets.UTF_8);
OpenSSLPBEParametersGenerator pbeGenerator = new OpenSSLPBEParametersGenerator(new MD5Digest()); // SHA256 as of v1.1.0 (if in OpenSSL the default digest is applied)
pbeGenerator.init(password, salt);
ParametersWithIV parameters = (ParametersWithIV) pbeGenerator.generateDerivedParameters(256, 128); // keySize, ivSize in bits

// Encrypt with AES-256, CBC using streams
try (FileOutputStream fos = new FileOutputStream(outputPath)) {

    // Apply OpenSSL format
    fos.write("Salted__".getBytes(StandardCharsets.UTF_8));
    fos.write(salt);

    // Encrypt chunkwise (for large data)
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
    cipher.init(true, parameters);
    try (FileInputStream fis = new FileInputStream(inputPath);
         CipherOutputStream cos = new CipherOutputStream(fos, cipher)) {
        int bytesRead = -1;
        byte[] buffer = new byte[64 * 1024 * 1024]; // chunksize, e.g. 64 MiB
        while ((bytesRead = fis.read(buffer)) != -1) {
            cos.write(buffer, 0, bytesRead);
        }    
    }
}

使用此代碼加密的文件可以使用 OpenSSL 解密,如下所示:

openssl enc -d -aes256 -k <passpharse> -in <enc file> -out <dec file>

因此,該代碼是您問題開頭發布的 OpenSSL 語句的編程模擬(因此仍然必須考慮關於摘要的歧義,即對於 v1.1.0 的 OpenSSL 版本,必須使用 SHA256 而不是 MD5 )。

注意因為隨機鹽,每次加密都會生成不同的key/IV對,所以沒有復用,這也去掉了評論中提到的漏洞。

我能夠用下面的代碼實現我的結果。 確實可以用更好的方式編寫代碼並刪除不必要的初始化。 我將在我的實際實施中做同樣的事情

public class test2 {
    public static void main(String[] args) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {

        Path path = Paths.get("/home/debashishd/Downloads/key");
        String Test_message = FileUtils.readFileToString(new File("/home/debashishd/Downloads/sample.txt"), StandardCharsets.UTF_8);

        /**
         * Read the keyfile provided to encrypt
         */
        byte[] keyValue = Files.readAllBytes(path);
        OpenSSLPBEParametersGenerator gen = new OpenSSLPBEParametersGenerator();
        OpenSSLPBEParametersGenerator gen1 = gen;
        final String salt = "";
        byte[] saltBytes = Hex.decode(salt.getBytes());
        gen1.init(keyValue,saltBytes);
        CipherParameters cp = gen1.generateDerivedParameters(256);
        byte[] keyBytes = ((KeyParameter)cp).getKey();
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes,"AES");

        /**
         * Initialize the Encrypt Ciphers and encrypt the data from input file
         */
        Cipher encryptcipher;
        Cipher decryptCipher;
        encryptcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        encryptcipher.init(Cipher.ENCRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));
        String encrypt_value = getEncoder().encodeToString(encryptcipher.doFinal(Test_message.getBytes(StandardCharsets.UTF_8)));
        System.out.println("Encrypted value: " + encrypt_value);

        /**
         * Initialize the Decrypt Ciphers and decrypt the encrypted data
         */
        decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        decryptCipher.init(Cipher.DECRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));
        String Decrypt_result = new String(decryptCipher.doFinal(getDecoder().decode(encrypt_value)));
        System.out.println("Decrypted value: " + Decrypt_result);

    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM