栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何使用AES解密用openssl命令加密的Java文件?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何使用AES解密用openssl命令加密的Java文件?

OpenSSL通常使用自己的基于密码的密钥派生方法,该方法在中指定EVP_BytesToKey,请参见下面的代码。此外,它会在多行中隐式地将密文编码为base 64,以便在邮件正文中发送密文。

因此,结果是伪代码:

salt = random(8)keyAndIV = BytesToKey(password, salt, 48)key = keyAndIV[0..31]iv = keyAndIV[32..47]ct = AES-256-CBC-encrypt(key, iv, plaintext)res = base64MimeEnpre("Salted__" | salt | ct))

因此解密为:

(salt, ct) = base64MimeDepre(res)key = keyAndIV[0..31]iv = keyAndIV[32..47]pt = AES-256-CBC-decrypt(key, iv, plaintext)

可以像这样在Java中实现:

import java.io.File;import java.io.IOException;import java.nio.charset.Charset;import java.nio.file.Files;import java.security.GeneralSecurityException;import java.security.MessageDigest;import java.util.Arrays;import java.util.List;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import org.bouncycastle.util.enprers.base64;public class OpenSSLDecryptor {    private static final Charset ASCII = Charset.forName("ASCII");    private static final int INDEX_KEY = 0;    private static final int INDEX_IV = 1;    private static final int ITERATIONS = 1;    private static final int ARG_INDEX_FILENAME = 0;    private static final int ARG_INDEX_PASSWORD = 1;    private static final int SALT_OFFSET = 8;    private static final int SALT_SIZE = 8;    private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE;    private static final int KEY_SIZE_BITS = 256;        public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data, int count) {        byte[][] both = new byte[2][];        byte[] key = new byte[key_len];        int key_ix = 0;        byte[] iv = new byte[iv_len];        int iv_ix = 0;        both[0] = key;        both[1] = iv;        byte[] md_buf = null;        int nkey = key_len;        int niv = iv_len;        int i = 0;        if (data == null) { return both;        }        int addmd = 0;        for (;;) { md.reset(); if (addmd++ > 0) {     md.update(md_buf); } md.update(data); if (null != salt) {     md.update(salt, 0, 8); } md_buf = md.digest(); for (i = 1; i < count; i++) {     md.reset();     md.update(md_buf);     md_buf = md.digest(); } i = 0; if (nkey > 0) {     for (;;) {         if (nkey == 0)  break;         if (i == md_buf.length)  break;         key[key_ix++] = md_buf[i];         nkey--;         i++;     } } if (niv > 0 && i != md_buf.length) {     for (;;) {         if (niv == 0)  break;         if (i == md_buf.length)  break;         iv[iv_ix++] = md_buf[i];         niv--;         i++;     } } if (nkey == 0 && niv == 0) {     break; }        }        for (i = 0; i < md_buf.length; i++) { md_buf[i] = 0;        }        return both;    }    public static void main(String[] args) {        try { // --- read base 64 enpred file --- File f = new File(args[ARG_INDEX_FILENAME]); List<String> lines = Files.readAllLines(f.toPath(), ASCII); StringBuilder sb = new StringBuilder(); for (String line : lines) {     sb.append(line.trim()); } String database64 = sb.toString(); byte[] headerSaltAndCipherText = base64.depre(database64); // --- extract salt & encrypted --- // header is "Salted__", ASCII enpred, if salt is being used (the default) byte[] salt = Arrays.copyOfRange(         headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE); byte[] encrypted = Arrays.copyOfRange(         headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length); // --- specify cipher and digest for EVP_BytesToKey method --- Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding"); MessageDigest md5 = MessageDigest.getInstance("MD5"); // --- create key and IV  --- // the IV is useless, OpenSSL might as well have use zero's final byte[][] keyAndIV = EVP_BytesToKey(         KEY_SIZE_BITS / Byte.SIZE,         aesCBC.getBlockSize(),         md5,         salt,         args[ARG_INDEX_PASSWORD].getBytes(ASCII),         ITERATIONS); SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES"); IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]); // --- initialize cipher instance and decrypt --- aesCBC.init(Cipher.DECRYPT_MODE, key, iv); byte[] decrypted = aesCBC.doFinal(encrypted); String answer = new String(decrypted, ASCII); System.out.println(answer);        } catch (BadPaddingException e) { // AKA "something went wrong" throw new IllegalStateException(         "Bad password, algorithm, mode or padding;" +         " no salt, wrong number of iterations or corrupted ciphertext.");        } catch (IllegalBlockSizeException e) { throw new IllegalStateException(         "Bad algorithm, mode or corrupted (resized) ciphertext.");        } catch (GeneralSecurityException e) { throw new IllegalStateException(e);        } catch (IOException e) { throw new IllegalStateException(e);        }    }        }

请注意,该代码将ASCII指定为字符集。对于你的应用程序/终端/操作系统,所使用的字符集可能有所不同。

通常,你应该强制OpenSSL使用NIST批准的PBKDF2算法,因为使用OpenSSL密钥派生方法(迭代计数为1)是不安全的。这可能会迫使你使用与OpenSSL不同的解决方案。请注意,基于密码的加密从本质上讲是不安全的-密码比随机生成的对称密钥安全性低得多。

OpenSSL 1.1.0c更改了某些内部组件中使用的摘要算法。以前使用的是MD5,而1.1.0切换到SHA256。小心的变化不影响你在这两个

EVP_BytesToKey
和命令状
openssl enc

最好在命令行界面中显式指定摘要(例如,-md md5为了向后兼容或sha-256向前兼容),并确保Java代码使用相同的摘要算法(

"MD5"
"SHA-256"
包括破折号)。另请参阅此答案中的信息。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/402370.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号