Como descriptografar o arquivo em Java criptografado com o comando openssl usando o AES?

Eu preciso descriptografar em JAVA um arquivo criptografado no UNIX com o seguinte comando:

openssl aes-256-cbc -a -salt -in password.txt -out password.txt.enc mypass mypass 

Eu tenho que descriptografar em java como eu faço aqui eu faço no UNIX

 openssl aes-256-cbc -d -a -in password.txt.enc -out password.txt.new mypass 

Alguém pode me dar um código java para fazer isso?

O OpenSSL geralmente usa seu próprio método de derivação de chave baseado em senha, especificado em EVP_BytesToKey , por favor, veja o código abaixo. Em geral, você deve forçar o OpenSSL a usar o algoritmo PBKDF2 aprovado pelo NIST.

 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.encoders.Base64; /** * Class created for StackOverflow by owlstead. * This is open source, you are free to copy and use for any purpose. */ 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; /** * Thanks go to Ola Bini for releasing this source on his blog. * The source was obtained from here . */ 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 encoded file --- File f = new File(args[ARG_INDEX_FILENAME]); List 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.decode(dataBase64); // --- extract salt & encrypted --- // header is "Salted__", ASCII encoded, 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); } } } 

O OpenSSL 1.1.0c alterou o algoritmo de resumo usado em alguns componentes internos. Antigamente, o MD5 era usado e o 1.1.0 comutado para SHA256. Tenha cuidado para que a mudança não esteja afetando você em EVP_BytesToKey e comandos como o openssl enc .

Abaixo estão OpenSSLPBEInputStream e OpenSSLPBEOutputStream, que podem ser usados ​​para criptografar / descriptografar streams arbitrários de bytes de uma maneira compatível com o OpenSSL.

Exemplo de uso:

  // The original clear text bytes byte[] originalBytes = ... // Encrypt these bytes char[] pwd = "thePassword".toCharArray(); ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); OpenSSLPBEOutputStream encOS = new OpenSSLPBEOutputStream(byteOS, ALGORITHM, 1, pwd); encOS.write(originalBytes); encOS.flush(); byte[] encryptedBytes = byteOS.toByteArray(); // Decrypt the bytes ByteArrayInputStream byteIS = new ByteArrayInputStream(encryptedBytes); OpenSSLPBEInputStream encIS = new OpenSSLPBEInputStream(byteIS, ALGORITHM, 1, pwd); 

Onde ALGORITM (usando apenas classs JDK) pode ser: “PBEWithMD5AndDES”, “PBEWithMD5AndTripleDES”, “PBEWithSHA1AndDESede”, “PBEWithSHA1AndRC2_40”.

Para manipular “openssl aes-256-cbc -a -salt -em senha.txt -out password.txt.enc” do pôster original, adicione o castelo bounce ao classpath e use algorthm = “PBEWITHMD5AND256BITAES-CBC-OPENSSL”.

 /* Add BC provider, and fail fast if BC provider is not in classpath for some reason */ Security.addProvider(new BouncyCastleProvider()); 

A dependência:

   org.bouncycastle bcprov-jdk16 1.44  

O stream de input:

 import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.IOException; import java.io.InputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; public class OpenSSLPBEInputStream extends InputStream { private final static int READ_BLOCK_SIZE = 64 * 1024; private final Cipher cipher; private final InputStream inStream; private final byte[] bufferCipher = new byte[READ_BLOCK_SIZE]; private byte[] bufferClear = null; private int index = Integer.MAX_VALUE; private int maxIndex = 0; public OpenSSLPBEInputStream(final InputStream streamIn, String algIn, int iterationCount, char[] password) throws IOException { this.inStream = streamIn; try { byte[] salt = readSalt(); cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.DECRYPT_MODE, algIn, iterationCount); } catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) { throw new IOException(e); } } @Override public int available() throws IOException { return inStream.available(); } @Override public int read() throws IOException { if (index > maxIndex) { index = 0; int read = inStream.read(bufferCipher); if (read != -1) { bufferClear = cipher.update(bufferCipher, 0, read); } if (read == -1 || bufferClear == null || bufferClear.length == 0) { try { bufferClear = cipher.doFinal(); } catch (IllegalBlockSizeException | BadPaddingException e) { bufferClear = null; } } if (bufferClear == null || bufferClear.length == 0) { return -1; } maxIndex = bufferClear.length - 1; } return bufferClear[index++] & 0xff; } private byte[] readSalt() throws IOException { byte[] headerBytes = new byte[OpenSSLPBECommon.OPENSSL_HEADER_STRING.length()]; inStream.read(headerBytes); String headerString = new String(headerBytes, OpenSSLPBECommon.OPENSSL_HEADER_ENCODE); if (!OpenSSLPBECommon.OPENSSL_HEADER_STRING.equals(headerString)) { throw new IOException("unexpected file header " + headerString); } byte[] salt = new byte[OpenSSLPBECommon.SALT_SIZE_BYTES]; inStream.read(salt); return salt; } } 

O stream de saída:

 import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.IOException; import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; public class OpenSSLPBEOutputStream extends OutputStream { private static final int BUFFER_SIZE = 5 * 1024 * 1024; private final Cipher cipher; private final OutputStream outStream; private final byte[] buffer = new byte[BUFFER_SIZE]; private int bufferIndex = 0; public OpenSSLPBEOutputStream(final OutputStream outputStream, String algIn, int iterationCount, char[] password) throws IOException { outStream = outputStream; try { /* Create and use a random SALT for each instance of this output stream. */ byte[] salt = new byte[PBECommon.SALT_SIZE_BYTES]; new SecureRandom().nextBytes(salt); cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.ENCRYPT_MODE, algIn, iterationCount); /* Write header */ writeHeader(salt); } catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) { throw new IOException(e); } } @Override public void write(int b) throws IOException { buffer[bufferIndex] = (byte) b; bufferIndex++; if (bufferIndex == BUFFER_SIZE) { byte[] result = cipher.update(buffer, 0, bufferIndex); outStream.write(result); bufferIndex = 0; } } @Override public void flush() throws IOException { if (bufferIndex > 0) { byte[] result; try { result = cipher.doFinal(buffer, 0, bufferIndex); outStream.write(result); } catch (IllegalBlockSizeException | BadPaddingException e) { throw new IOException(e); } bufferIndex = 0; } } @Override public void close() throws IOException { flush(); outStream.close(); } private void writeHeader(byte[] salt) throws IOException { outStream.write(OpenSSLPBECommon.OPENSSL_HEADER_STRING.getBytes(OpenSSLPBECommon.OPENSSL_HEADER_ENCODE)); outStream.write(salt); } } 

Classe comum pequena:

 import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; class OpenSSLPBECommon { protected static final int SALT_SIZE_BYTES = 8; protected static final String OPENSSL_HEADER_STRING = "Salted__"; protected static final String OPENSSL_HEADER_ENCODE = "ASCII"; protected static Cipher initializeCipher(char[] password, byte[] salt, int cipherMode, final String algorithm, int iterationCount) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException { PBEKeySpec keySpec = new PBEKeySpec(password); SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm); SecretKey key = factory.generateSecret(keySpec); Cipher cipher = Cipher.getInstance(algorithm); cipher.init(cipherMode, key, new PBEParameterSpec(salt, iterationCount)); return cipher; } } 

Não use ase-128-cbc, use ase-128-ecb.

só pega os primeiros 16 bytes como chave porque a chave é 128 bits

a saída hash é impressa em hexadecimal, que a cada 2 caracteres apresenta um valor de byte

hashpwd = echo -n $password| openssl sha1 | sed 's#.*=\\s*##g' | cut -c 1-32 echo -n $password| openssl sha1 | sed 's#.*=\\s*##g' | cut -c 1-32

openssl enc -aes-128-ecb -salt -em -out -K $ hashpwd

Código Java está aqui:

 import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; //openssl enc -nosalt -aes-128-ecb // -in  // -out  // -K <16 bytes in hex, for example : "abc" can be hashed in SHA-1, the first 16 bytes in hex is a9993e364706816aba3e25717850c26c> private final static String TRANSFORMATION = "AES"; // use aes-128-ecb in openssl public static byte[] encrypt(String passcode, byte[] data) throws CryptographicException { try { Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, genKeySpec(passcode)); return cipher.doFinal(data); } catch (Exception ex) { throw new CryptographicException("Error encrypting", ex); } } public static String encryptWithBase64(String passcode, byte[] data) throws CryptographicException { return new BASE64Encoder().encode(encrypt(passcode, data)); } public static byte[] decrypt(String passcode, byte[] data) throws CryptographicException { try { Cipher dcipher = Cipher.getInstance(TRANSFORMATION); dcipher.init(Cipher.DECRYPT_MODE, genKeySpec(passcode)); return dcipher.doFinal(data); } catch (Exception e) { throw new CryptographicException("Error decrypting", e); } } public static byte[] decryptWithBase64(String passcode, String encrptedStr) throws CryptographicException { try { return decrypt(passcode, new BASE64Decoder().decodeBuffer(encrptedStr)); } catch (Exception e) { throw new CryptographicException("Error decrypting", e); } } public static SecretKeySpec genKeySpec(String passcode) throws UnsupportedEncodingException, NoSuchAlgorithmException { byte[] key = passcode.getBytes("UTF-8"); MessageDigest sha = MessageDigest.getInstance("SHA-1"); key = sha.digest(key); key = Arrays.copyOf(key, 16); // use only first 128 bit return new SecretKeySpec(key, TRANSFORMATION); } 

Testado e passado em jdk6 e jdk8.