解密java中使用openssl加密的文件

2024-04-19

使用以下命令对文件进行加密:

openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -k 1234567812345678

使用以下命令解密该文件:

openssl enc -d -aes-256-cbc -in file_enc.txt -out file.txt -k 1234567812345678

在java中打印盐和密钥后我得到:

密钥=b796fbb416732ce13d39dbb60c0fb234a8f6d70e49df1c7e62e55e81d33a6bff774254ac99268856bf3afe0b95defdad

在 cmd 中我得到:

盐=2D7C7E1C84BD6693 密钥=B796FBB416732CE13D39DBB60C0FB234A8F6D70E49DF1C7E62E55E81D33A6BFF

=774254AC99268856BF3AFE0B95DEFDAD

运行后:

openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -pbkdf2 -k 1234567812345678 -p

我正在使用以下代码,但正在打印加密文件:

public static void main(String args[]) throws InvalidKeySpecException,
                                              NoSuchAlgorithmException,
                                              IllegalBlockSizeException,
                                              InvalidKeyException,
                                              BadPaddingException,
                                              InvalidAlgorithmParameterException,
                                              NoSuchPaddingException,
                                              IOException {
    String password = "1234567812345678";
    String algorithm = "AES/CBC/PKCS5Padding";
    IvParameterSpec ivParameterSpec = AESUtil.generateIv();
    Resource resource = new ClassPathResource("file_enc.txt");
    File inputFile = resource.getFile();
    byte[] salt = new byte[8], data = new byte[1024], tmp; 
        int keylen = 32, ivlen = 16, cnt;
        try( InputStream is = new FileInputStream(inputFile) ){
            if( is.read(salt) != 8 || !Arrays.equals(salt, "Salted__".getBytes() )
                    || is.read(salt) != 8 ) throw new Exception("salt fail");
            byte[] keyandIV = SecretKeyFactory.getInstance("PBKDF2withHmacSHA256") 
                    .generateSecret( new PBEKeySpec(password.toCharArray(), salt, 10000, (keylen+ivlen)*8) 
                    ).getEncoded();
            System.out.println("Key "+ byteArrayToHex(keyandIV));
            Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
            ciph.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyandIV,0,keylen,"AES"), 
                    new IvParameterSpec(keyandIV,keylen,ivlen));
            while( (cnt = is.read(data)) > 0 ){
                if( (tmp = ciph.update(data, 0, cnt)) != null ) System.out.write(tmp);
            }
            tmp = ciph.doFinal(); System.out.write(tmp);
        }
}


Your getKeyFromPassword创建一个SecretkeyFactory对于 PBKDF2(使用 HmacSHA256)但不使用它;相反,您使用密码作为密钥,这是错误的 - 除非您实际上想使用密钥执行 openssl enc (-K大写字母和十六进制,对于 AES-256 应为 64 十六进制/32 字节)而不是密码(-k小写和任何字符或任何长度)。 (低于 IIRC 18,String.getBytes()为您提供了一种依赖于 JVM 的编码,对于任意字符串(如真实密码),该编码可能会在不同的系统或环境中有所不同,这将导致使用它的加密失败,但对于您显示的示例字符串,所有实际编码都会给出相同的结果。)

但即使你确实使用了这个工厂,它也不正确,因为openssl enc默认情况下不使用 PBKDF2,它使用一个名为EVP_BytesToKey它基于但不同于 PBKDF1。在 OpenSSL 1.1.1 及更高版本中,您可以可选地指定-pbkdf2(和/或-iter N) in enc,如果你不这样做,它会给出一条关于“已弃用的密钥派生”的警告消息,你应该注意到这一点,所以我假设你要么使用过时的 OpenSSL,要么试图阻止我们准确理解你的情况,从而使其不太可能得到一个有用的答案。

另外,与either密钥派生类型(旧EVP_BytesToKey或可选的新PBKDF2)openssl enc默认使用salt,加密时随机生成并写入文件中;解密时,您必须从文件中读取盐并使用它。具体来说,跳过或丢弃前 8 个字节(它们是固定字符)Salted__)并将接下来的 8 个字节作为盐,然后将文件的其余部分作为密文

根据您想要(或者您的用户/客户/等)想要做什么,有三种可能性:

  1. 加密用openssl enc -aes-256-cbc -k ...使用默认派生(如现在)并编写 Java 代码以从文件中读取盐,如上所示,实现EVP_BytesToKey使用密码和盐,并使用其输出作为 Java 中的密钥和 IVCipher(对于 aes-256-cbc 生成 48 个字节并使用前 32 个字节作为密钥,最后 16 个字节作为 IV)。EVP_BytesToKey对于 OpenSSL 1.1.0 及以上版本,使用默认为 SHA256 的哈希值,对于较低版本,使用默认为 MD5 的哈希值,因此您需要知道哪个版本进行了加密才能使其工作,否则您可以specify上的哈希值enc命令与-md $hash。十多年前,已经有数百个关于此问题的问题;搜索EVP_BytesToKey找到其中一些。

  2. 加密用openssl enc -aes-256-cbc -pbkdf2 -k ...并编写 Java 代码以从文件中读取盐,如上所示use您创建的用于生成 48 字节“密钥”材料的 keyfactory,您实际上必须将其拆分为 key 和 IV,如上面在 Java 中所示Cipher.

  3. 加密用openssl enc -aes-256-cbc -K 64hexits -iv 32hexits并对 Java 进行编码以使用相应的二进制密钥和 IV 值。

在命令中我既没有指定随机 IV 也没有指定 PKCS5Padding

当您使用旧的或新的密钥派生时openssl enc it derivesIV,而不是单独指定;仅当您使用显式密钥时(-K大写)您还指定吗-iv. openssl enc始终默认为各种称为 pkcs5、pkcs7 或 pkcs5/7 的填充,除非不需要填充(RC4 或 ChaCha 等流密码或 CTR、OFB、CFB 等流模式)。


好吧,你似乎只读了我所说的一半。最根本的是,你仍然有openssl enc没有-pbkdf2但正在尝试使用 PBKDF2 在 Java 中解密,这是完全错误的。此外,您正在读取盐,然后将其转换为十六进制,这是错误的,文件中的盐是正确的盐,并且您正在生成一个完全伪造的随机 IV,而不是像我所说的那样导出它。

具体来说,如果你(或我)加密一个文件与-pbkdf2 like

 openssl enc -aes-cbc-256 -pbkdf2 -k 1234567812345678 

它仅适用于 OpenSSL 1.1.1 以上(自 2018 年起),以下(简约的)Java 代码可以正确解密它:

static void SO73456313OpensslEnc2_Java (String[] args) throws Exception {
    // file pw: decrypt openssl(1.1.1+) enc -aes-256-cbc -pbkdf2 -k $pw
    byte[] salt = new byte[8], data = new byte[1024], tmp; 
    int keylen = 32, ivlen = 16, cnt;
    try( InputStream is = new FileInputStream(args[0]) ){
        if( is.read(salt) != 8 || !Arrays.equals(salt, "Salted__".getBytes() )
                || is.read(salt) != 8 ) throw new Exception("salt fail");
        byte[] keyandIV = SecretKeyFactory.getInstance("PBKDF2withHmacSHA256") 
                .generateSecret( new PBEKeySpec(args[1].toCharArray(), salt, 10000, (keylen+ivlen)*8) 
                ).getEncoded();
        Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
        ciph.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyandIV,0,keylen,"AES"), 
                new IvParameterSpec(keyandIV,keylen,ivlen));
        while( (cnt = is.read(data)) > 0 ){
            if( (tmp = ciph.update(data, 0, cnt)) != null ) System.out.write(tmp);
        }
        tmp = ciph.doFinal(); System.out.write(tmp);
    }
}

请注意,在 PBEKeySpec 中我使用了 itercount=10000 这是enc默认。您可以使用更高的数字,例如 65536,这对于安全性可能是有利的(但这部分在这里是题外话),如果您在加密时指定它,如下所示:

 openssl enc -aes-256-cbc -pbkdf2 -iter 65536 -k ... 

OTOH 如果您使用您发布的命令(必须在 OpenSSL 1.1.0 或更低版本上使用),那么您根本无法使用 PBKDF2 解密。 对于这种情况,请参阅
如何使用AES解密使用openssl命令加密的Java文件? https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
如何使用java解码使用openssl aes-128-cbc编码的字符串? https://stackoverflow.com/questions/31947256/how-to-decode-a-string-encoded-with-openssl-aes-128-cbc-using-java
OpenSSL AES CBC 加密的 Java 等效项 https://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption
使用 BouncyCastle SSL 通过 keyFile 进行 Java AES 解密 https://stackoverflow.com/questions/72944988/java-aes-decryption-with-keyfile-using-bouncycastle-ssl
正在寻找用于解密使用 openssl -aes-256-cbc -a -salt 命令加密的消息的 Java 实现? https://stackoverflow.com/questions/55175864/looking-for-java-implementation-for-decrypting-a-message-encrypted-using-openssl
and CryptoJS AES 加密和 Java AES 解密 https://stackoverflow.com/questions/41432896/cryptojs-aes-encryption-and-java-aes-decryption(cryptojs 有时与 OpenSSL 兼容,包括 Q 中的情况)。 有关背景信息,请参阅我的详细说明:https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption/#35614 https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption/#35614或者熊在https://security.stackexchange.com/questions/29106/openssl-recover-key-and-iv-by-passphrase https://security.stackexchange.com/questions/29106/openssl-recover-key-and-iv-by-passphrase .

请记住,至少正如之前的一些问题中所指出的,您发布的命令在 1.1.0 及更高版本中使用 EVP_BytesToKey 和 SHA256,但在 1.0.2 及更低版本中使用 MD5,因此您需要知道曾经或将使用哪个 OpenSSL。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解密java中使用openssl加密的文件 的相关文章

随机推荐