首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用openssl加密的java解密文件

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

Stack Overflow用户
提问于 2022-08-23 09:33:21
回答 1查看 302关注 0票数 -1

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

代码语言:javascript
运行
复制
openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -k 1234567812345678

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

代码语言:javascript
运行
复制
openssl enc -d -aes-256-cbc -in file_enc.txt -out file.txt -k 1234567812345678

在用java打印salt和key之后,我得到:

Key=b796fbb416732ce13d39dbb60c0fb234a8f6d70e49df1c7e62e55e81d33a6bff774254ac99268856bf3afe0b95defdad

在cmd,我得到:

salt=2D7C7E1C84BD6693 key=B796FBB416732CE13D39DBB60C0FB234A8F6D70E49DF1C7E62E55E81D33A6BFF

iv =774254AC99268856BF3AFE0B95DEFDAD

跑完后:

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

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

代码语言:javascript
运行
复制
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);
        }
}
EN

回答 1

Stack Overflow用户

发布于 2022-08-23 12:18:13

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

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

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

取决于您想要做什么(或者您的用户/客户/其他人希望做什么),有三种可能性:

  1. 使用默认派生(与现在一样)用openssl enc -aes-256-cbc -k ...加密,并编码Java从上面的文件中读取salt,使用密码和那个salt实现EVP_BytesToKey,并将其输出用于JavaCipher中的密钥和IV (用于aes-256-cbc生成48字节,使用前32字节作为密钥,最后16字节作为IV)。EVP_BytesToKey使用的哈希默认为SHA256 1.1.0up,而MD5用于较低版本,因此您需要知道是哪个版本进行了加密才能工作,否则可以在enc命令中使用-md $hash指定哈希。在过去的十年里,已经有数百个关于这个问题的疑问;寻找EVP_BytesToKey来找到其中一些。

Cipher.对

  1. 进行加密,并将其编码为从上面的文件中读取salt,并使用您创建的密钥工厂生成48字节的“密钥”材料,您必须按照上面的方式将其分割成密钥和IV。

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

命令中的

我既没有指定随机IV也没有指定PKCS5Padding

当您在openssl enc中使用旧的或新的密钥派生时,它派生IV而不是单独指定它;只有当您使用显式键(-K大写)时,才会指定-ivopenssl enc总是默认使用不同名称为pkcs5、pkcs7或pkcs5 5/7的填充,除非不需要填充( RC4或ChaCha之类的流密码或CTR、OFB、CFB等流模式)。

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

具体而言,如果您(或我)使用-pbkdf2加密文件,如

代码语言:javascript
运行
复制
 openssl enc -aes-cbc-256 -pbkdf2 -k 1234567812345678 

它只适用于OpenSSL 1.1.1或3.0 (即自2018年起),以下(极简式) Java代码正确地解密它:

代码语言:javascript
运行
复制
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,这对于安全性来说可能是可取的(但这部分在这里是不讨论主题的),如果您在加密时指定了它,比如:

代码语言:javascript
运行
复制
 openssl enc -aes-256-cbc -pbkdf2 -iter 65536 -k ... 

OTOH如果使用发布的命令(必须在OpenSSL 1.1.0或更低版本上),则根本无法使用PBKDF2解密。有关此情况,请参见

How to decrypt file in Java encrypted with openssl command using AES?

How to decode a string encoded with openssl aes-128-cbc using java?

Java equivalent of an OpenSSL AES CBC encryption

Java AES Decryption with keyFile using BouncyCastle SSL

CryptoJS AES encryption and Java AES decryption (加密is有时与OpenSSL兼容,包括Q中的情况)。

请记住,至少在前面的一些Qs中已经指出,您发布的命令在1.1.0up中使用了EVP_BytesToKey和SHA256,在1.0.2和更低的时候使用了MD5,所以您需要知道使用的是哪个OpenSSL或将要使用哪个OpenSSL。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73456313

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档