使用以下命令对文件进行加密:
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打印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
我使用以下代码,但加密文件正在打印:
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);
}
}
发布于 2022-08-23 04: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,然后文件的其余部分作为密文。
取决于您想要做什么(或者您的用户/客户/其他人希望做什么),有三种可能性:
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
.对
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
大写)时,才会指定-iv
。openssl enc
总是默认使用不同名称为pkcs5、pkcs7或pkcs5 5/7的填充,除非不需要填充( RC4或ChaCha之类的流密码或CTR、OFB、CFB等流模式)。
好吧,你好像只读了我说的一半。最根本的是,您仍然拥有没有openssl enc
的-pbkdf2
,但是尝试用PBKDF2解密,这是完全错误的。此外,您正在读取盐,然后将其转换为十六进制,这是错误的,文件中的盐是正确的盐,您正在生成一个完全虚假的随机IV,而不是像我说的那样派生它。
具体而言,如果您(或我)使用-pbkdf2加密文件,如
openssl enc -aes-cbc-256 -pbkdf2 -k 1234567812345678
它只适用于OpenSSL 1.1.1或3.0 (即自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解密。有关此情况,请参见
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。
https://stackoverflow.com/questions/73456313
复制