¿Cómo decodificar una cadena codificada con openssl aes-128-cbc usando java?
encryption decode (2)
Estoy usando openssl para codificar una cadena usando el siguiente comando:
openssl enc -aes-128-cbc -a -salt -pass pass:mypassword <<< "stackoverflow"
El resultado me da una cadena codificada:
U2FsdGVkX187CGv6DbEpqh/L6XRKON7uBGluIU0nT3w=
Hasta ahora, solo necesito decodificar esto usando openssl, por lo que el siguiente comando devuelve la cadena previamente codificada:
openssl enc -aes-128-cbc -a -salt -pass pass:mypassword -d <<< "U2FsdGVkX187CGv6DbEpqh/L6XRKON7uBGluIU0nT3w="
Resultado:
stackoverflow
Ahora, necesito decodificar la cadena codificada en una aplicación Java .
Mi pregunta es:
¿Alguien puede proporcionarme una clase java simple para decodificar una cadena codificada con el comando openssl dado anteriormente?
Muchas gracias.
Lo resolvió usando la biblioteca Bouncy Castle.
Aquí está el código:
package example;
import java.util.Arrays;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.BlockCipherPadding;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
public class OpenSSLAesDecrypter
{
private static final int AES_NIVBITS = 128; // CBC Initialization Vector (same as cipher block size) [16 bytes]
private final int keyLenBits;
public OpenSSLAesDecrypter(int nKeyBits)
{
this.keyLenBits = nKeyBits;
}
public byte[] decipher(byte[] pwd, byte[] src)
{
// openssl non-standard extension: salt embedded at start of encrypted file
byte[] salt = Arrays.copyOfRange(src, 8, 16); // 0..7 is "SALTED__", 8..15 is the salt
try
{
// Encryption algorithm. Note that the "strength" (bitsize) is controlled by the key object that is used.
// Note that PKCS5 padding and PKCS7 padding are identical.
BlockCipherPadding padding = new PKCS7Padding();
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), padding);
CipherParameters params = getCipherParameters(pwd, salt);
cipher.reset();
cipher.init(false, params);
int buflen = cipher.getOutputSize(src.length - 16);
byte[] workingBuffer = new byte[buflen];
int len = cipher.processBytes(src, 16, src.length - 16, workingBuffer, 0);
len += cipher.doFinal(workingBuffer, len);
// Note that getOutputSize returns a number which includes space for "padding" bytes to be stored in.
// However we don''t want these padding bytes; the "len" variable contains the length of the *real* data
// (which is always less than the return value of getOutputSize.
byte[] bytesDec = new byte[len];
System.arraycopy(workingBuffer, 0, bytesDec, 0, len);
return bytesDec;
}
catch (InvalidCipherTextException e)
{
System.err.println("Error: Decryption failed");
return null;
}
catch (RuntimeException e)
{
System.err.println("Error: Decryption failed");
return null;
}
}
private CipherParameters getCipherParameters(byte[] pwd, byte[] salt)
{
// Use bouncycastle implementation of openssl non-standard (pwd,salt)->(key,iv) algorithm.
// Note that if a "CBC" cipher is selected, then an IV is required as well as a key. When using a password,
// Openssl
// *derives* the IV from the (pwd,salt) pair at the same time as it derives the key.
//
// * PBE = Password Based Encryption
// * CBC = Cipher Block Chaining (ie IV is needed)
//
// Note also that when the IV is derived from (pwd, salt) the salt **must** be different for each message; this is
// the default for openssl - just make sure to NOT explicitly provide a salt, or encryption security is badly
// affected.
OpenSSLPBEParametersGenerator gen = new OpenSSLPBEParametersGenerator();
gen.init(pwd, salt);
CipherParameters cp = gen.generateDerivedParameters(keyLenBits, AES_NIVBITS);
return cp;
}
public static void main(String[] args)
{
OpenSSLAesDecrypter d = new OpenSSLAesDecrypter(128);
String r = new String(d.decipher("mypassword".getBytes(),
Base64.decodeBase64("U2FsdGVkX187CGv6DbEpqh/L6XRKON7uBGluIU0nT3w=")));
System.out.println(r);
}
}
Use las siguientes dependencias para compilarlo / ejecutarlo:
openssl enc
por defecto utiliza un algoritmo de cifrado basado en contraseña (modestamente) no estándar y un formato de datos personalizado pero simple.
-
Si realmente no necesita PBE, solo
algo de
cifrado openssl,
la pregunta vinculada por @Artjom
tiene una buena respuesta: use la clave "sin procesar" y IV en openssl, y luego use la misma clave y IV en Java.
Y deje que ambos tengan el relleno "PKCS5" (realmente PKCS # 7).
Tenga en cuenta que
openssl enc
toma-K
y-iv
en hexadecimal, mientras que Java crypto los toma como bytes; convertir según sea necesario. Dado que / si base64-ed el texto cifrado, primero de-base64; esto se proporciona en java8, y hay numerosas bibliotecas disponibles para versiones anteriores de java.
De lo contrario, debe descomprimir el formato del archivo. Después de de-base64, descarte los primeros 8 bytes, tome los siguientes 8 bytes como sal y los bytes restantes como texto cifrado.
-
Si necesita PBE para el algoritmo específico AES128-CBC o 192 o 256 y puede usar una biblioteca criptográfica de terceros, a saber, http://www.BouncyCastle.org , implementa PBE openssl para esos tres algoritmos.
SecretKeyFactory
paraPBEWITHMD5AND128BITAES-CBC-OPENSSL
- o 192 o 256 pero solo si está instalada la Política de Fuerza Ilimitada (actualización: o versión Oracle> = 8u161 o OpenJDK) - y dele unaPBEKeySpec
con la clave como caracteres, sal y cuenta 1 y usa el resultado en unCipher
del mismo algoritmo. -
De lo contrario, debe hacer el PBE usted mismo. Afortunadamente (?) Es bastante simple. Ponga el siguiente método donde sea conveniente:
public static /*or as appropriate */ void opensslBytesToKey ( byte[] pass, byte[] salt /*or null*/, // inputs int iter, String hashname, // PBKDF1-ish byte[] key, byte[] iv /*or null*/ // outputs ) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance (hashname); byte[] temp = null, out = new byte[key.length+(iv!=null?iv.length:0)]; int outidx = 0; while(outidx < out.length){ if(temp!=null) md.update(temp); md.update(pass); if(salt!=null) md.update(salt); temp = md.digest(); for(int i=1; i<iter; i++) temp = md.digest (temp); int use = Math.min (out.length-outidx, temp.length); System.arraycopy (temp,0, out,outidx, use); outidx += use; } System.arraycopy (out,0, key,0, key.length); if(iv!=null) System.arraycopy (out,key.length, iv,0, iv.length); }
y llámelo con la contraseña como bytes, la sal, el recuento de iteraciones 1, "MD5" y las matrices de salida que tienen el tamaño correcto para su clave AES (16, 24 o 32 bytes) y un AES IV (siempre 16 bytes) .
Úselos en
SecretKeySpec
e
IvParameterSpec
respectivamente con
Cipher
para (corrección)
AES/CBC/PKCS5Padding
.
Aparte: no puede encriptar una cadena como tal, solo bytes (o más exactamente octetos ). Los programas C, incluido openssl, en prácticamente todos los sistemas traducen cadenas / caracteres en ASCII ay desde bytes implícitamente, pero el uso de cualquier carácter fuera del conjunto ASCII puede producir resultados inconsistentes e inutilizables. Java trata las cadenas / caracteres como Unicode (o más exactamente UTF-16) y los convierte ay desde bytes principalmente de manera explícita; esta conversión es confiable (y consistente con C) para ASCII, pero puede variar para caracteres no ASCII.
ACTUALIZACIÓN
: OpenSSL 1.1.0 (2016-08) cambia el hash predeterminado para
enc
PBE de MD5 a SHA256.
Cambiar la llamada en mi opción 3 dependiendo de qué versión de OpenSSL se usó para cifrar, o si se usó la opción
-md
(previamente no documentada).
Para obtener más detalles, consulte (mi)
https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption/#35614