-1 java encryption openssl jca
使用以下命令对文件进行加密:
openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -k 1234567812345678
Run Code Online (Sandbox Code Playgroud)
使用以下命令解密该文件:
openssl enc -d -aes-256-cbc -in file_enc.txt -out file.txt -k 1234567812345678
Run Code Online (Sandbox Code Playgroud)
在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);
}
}
Run Code Online (Sandbox Code Playgroud)
Your getKeyFromPassword creates a SecretkeyFactory for PBKDF2 (with HmacSHA256) but doesn't use it; instead you use the password as the key, which is wrong -- unless you actually wanted to do openssl enc with a key (-K uppercase and hex which should be 64 hexits/32 bytes for AES-256) and not a password (-k lowercase and any characters or any length). (Below IIRC 18, String.getBytes() gives you a JVM-dependent encoding which for an arbitrary string like a real password can vary on different systems or environments, which will cause cryptography using it to fail, but for the example string you show all realistic encodings give the same result.)
但即使你确实使用了这个工厂,它也不正确,因为openssl enc by default does not use PBKDF2, it uses a function called EVP_BytesToKey which is based on but different from PBKDF1. In OpenSSL 1.1.1 up, you can optionally specify -pbkdf2 (and/or -iter N) in enc, and if you don't it gives a warning message about 'deprecated key derivation' which you should have noticed, so I assume you're either using obsolete OpenSSL or trying to prevent us accurately understanding your situation and thus making it unlikely to get a useful answer.
Also, with either kind of key derivation (old EVP_BytesToKey or optional new PBKDF2) openssl enc by default uses salt, which is randomly generated and written in the file when encrypting; when decrypting you must read the salt from the file and use it. Specifically, skip or discard the first 8 bytes (which are the fixed characters Salted__) and take the next 8 bytes as salt, then the remainder of the file as ciphertext
Depending on what you want (or maybe your users/customers/etc want) to do there are three possibilities:
使用默认派生(如现在)进行加密openssl enc -aes-256-cbc -k ...,并编写 Java 代码以从上述文件中读取盐,EVP_BytesToKey使用密码和该盐实现,并将其输出用于 Java 中的密钥和 IV Cipher(对于 aes-256 -cbc 生成 48 个字节并使用前 32 个字节作为密钥,后 16 个字节作为 IV)。对于 OpenSSL 1.1.0 及以上版本,使用默认为 SHA256 的哈希值,对于较低版本,使用默认为 MD5 的哈希值,因此您需要知道哪个版本进行了加密才能使其EVP_BytesToKey工作,否则您可以在enc command with -md $hash. There have been hundreds of Qs about this going back over a decade; search for EVP_BytesToKey to find some of them.
使用 Java进行加密openssl enc -aes-256-cbc -pbkdf2 -k ...和编码以从上述文件中读取盐并使用 the keyfactory you created to generate 48 bytes of 'key' material, which you must actually split into key and IV as above in the Java Cipher.
加密用openssl enc -aes-256-cbc -K 64hexits -iv 32hexits and code the Java to use the corresponding binary key and IV values.
In command i didn't specify neither random IV nor PKCS5Padding
当您使用旧的或新的密钥派生时,openssl enc它会派生IV,而不是单独指定它;仅当您使用显式键(-K大写)时,您还指定-iv. openssl enc always defaults to the padding variously called pkcs5, pkcs7, or pkcs5/7, except when no padding is needed (stream ciphers like RC4 or ChaCha or stream modes like CTR, OFB, CFB).
好吧,你似乎只读了我所说的一半。最根本的是,你仍然有openssl enc without -pbkdf2 but are trying to decrypt in Java with PBKDF2, which is flat wrong. In addition you are reading the salt but then converting it to hex, which is wrong, the salt from the file is the correct salt, and you are generating a completely bogus random IV, not deriving it as I said.
具体来说,如果你(或我)使用 -pbkdf2加密文件 like
openssl enc -aes-cbc-256 -pbkdf2 -k 1234567812345678
Run Code Online (Sandbox Code Playgroud)
它仅适用于 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);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,在 PBEKeySpec 中我使用了 itercount=10000 这是enc default. You can use a higher number like 65536, which may be desirable for security (but that part is offtopic here), if you specify it when encrypting like:
openssl enc -aes-256-cbc -pbkdf2 -iter 65536 -k ...
Run Code Online (Sandbox Code Playgroud)
OTOH 如果您使用您发布的命令(必须在 OpenSSL 1.1.0 或更低版本上使用),那么您根本无法使用 PBKDF2 解密。对于这种情况,请参阅
如何使用 AES 解密使用 openssl 命令加密的 Java 文件?
如何使用java解码使用openssl aes-128-cbc编码的字符串?
OpenSSL AES CBC 加密的 Java 等效项
使用 BouncyCastle SSL 通过 keyFile 进行 Java AES 解密
正在寻找用于解密使用 openssl -aes-256-cbc -a -salt 命令加密的消息的 Java 实现?
CryptoJS AES 加密和Java AES 解密(cryptojs 有时与 OpenSSL 兼容,包括 Q 中的情况)。有关背景信息,请参阅我的详细解释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。
请记住,至少正如之前的一些问题中所指出的,您发布的命令在 1.1.0 及更高版本中使用 EVP_BytesToKey 和 SHA256,但在 1.0.2 及更低版本中使用 MD5,因此您需要知道曾经或将使用哪个 OpenSSL。