Encriptacion con Open SSL
Hola a todos. Estoy buscando informacion de encriptacion con seguridad usando Open SSL, de seguro existe alguna API o Libreria para Java, la cuestion es esta necesito encriptar un archivo de texto plano y de igual manera poder desencriptarlo usando Open SSL, si alguien tiene informacion o sabe de algo se lo agradeceria mucho ademas prometo agregar algun post con un ejemplo.
- javadicto's blog
- Inicie sesión o regístrese para enviar comentarios
Comentarios
JCE
OpenSSL es un software hecho en C. En Java tienes la librería de JCE (Java Cryptography Extensions) y JSSE (Java Secure Socket Extensions) que sirven para manejar cifrado de datos y conexiones seguras basadas en los estándares de SSL. Tanto JCE como JSSE ya vienen incluidas en el runtime y JDK de Java desde la versión 1.4.
Adicionalmente, si quieres manejar algunas cosas a más bajo nivel (aunque la verdad es raro necesitar hacer esto), puedes usar BouncyCastle, una implementación alterna de JCE.
Revisa los paquetes java.security (y sus subpaquetes), javax.crypto y sus subpaquetes, javax.security y sus subpaquetes.
Por ejemplo, para encriptar datos usando AES:
byte[] salt = new byte[16]; //pueden ser 16 bytes aleatorios, pero se tienen que almacenar entonces al principio del archivo, para poder descifrarlo posteriormente
Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec key = new SecretKeySpec(keybytes, "AES");
IvParameterSpec iv = new IvParameterSpec(salt);
aes.init(Cipher.ENCRYPT_MODE, key, iv);
//Ahora vamos a suponer que lees bloques por ejemplo de 1K
byte[] block = new byte[1024];
//Los vas a ir encriptando asi:
while (hayMasDatos) {
byte[] cipher = aes.update(block, 0, largoDatos); //puede que el bloque no este lleno de datos, solamente una parte, depende de tu lectura
//Ahora escribes el buffer 'cipher' a otro lado y obtienes mas datos de entrada
}
//Obtener el ultimo bloque, que trae el padding, etc
byte[] cipher = aes.doFinal();
//Escribir ese ultimo bloque de datos a tu resultado
Para descifrar es exactamente lo mismo pero le pasas Cipher.INIT_DECRYPT al metodo init().
El API es relativamente sencillo de usar, siempre y cuando sepas lo que estás haciendo. Al usar criptografía los problemas más comunes son por falta de entendimiento de los conceptos.
En tu caso, si necesitas cifrar un archivo que pueda ser descifrado usando OpenSSL o viceversa, lo único que necesitas conocer es el formato de los archivos cifrados de OpenSSL, pues segun recuerdo, traen un encabezado indicando el algoritmo usado, la sal, y alguna otra información necesaria para poder descifrar el archivo; luego ya vienen los datos cifrados. Pero dado que los algoritmos son estándar, una vez que conozcas dicho formato, puedes descifrar en Java un archivo de OpenSSL sin problemas.
Que paso ezamudio!
Como siempre muy atinado en tus comentarios. Voy a revisar lo que me indicas y cuando lo termine exponder algun ejemplo para cualquiera que se le presente este problema. Ya habia revisado un codigo usando algo parecido a lo que mencionas, Uso de la API JCE para encriptar y desencriptar una serie de objetos:
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.util.*;
public class EncryptTest {
public static void main(String args[]) {
File desFile = new File("out.des");
// Create data to encrypt
Map map = new TreeMap(System.getProperties());
int number = map.size();
try {
// Create Key
KeyGenerator kg = KeyGenerator.getInstance("DES");
SecretKey secretKey = kg.generateKey();
// Create Cipher
Cipher desCipher =
Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.ENCRYPT_MODE, secretKey);
// Create stream
FileOutputStream fos = new FileOutputStream(desFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
CipherOutputStream cos = new CipherOutputStream(bos,
desCipher);
ObjectOutputStream oos = new ObjectOutputStream(cos);
// Write objects
oos.writeObject(map);
oos.writeInt(number);
oos.flush();
oos.close();
// Change cipher mode
desCipher.init(Cipher.DECRYPT_MODE, secretKey);
// Create stream
FileInputStream fis = new FileInputStream(desFile);
BufferedInputStream bis = new BufferedInputStream(fis);
CipherInputStream cis = new CipherInputStream(bis,
desCipher);
ObjectInputStream ois = new ObjectInputStream(cis);
// Read objects
Map map2 = (Map)ois.readObject();
int number2 = ois.readInt();
ois.close();
// Compare original with what was read back
if (map.equals(map2) && (map.size() == number2)) {
System.out.println("Everything read back out okay.");
} else {
System.out.println("Problems during
encryption/decryption process.");
}
} catch (NoSuchPaddingException e) {
System.err.println("Padding problem: " + e);
} catch (NoSuchAlgorithmException e) {
System.err.println("Invalid algorithm: " + e);
} catch (InvalidKeyException e) {
System.err.println("Invalid key: " + e);
} catch (IOException e) {
System.err.println("I/O Problem: " + e);
} catch (ClassNotFoundException e) {
System.err.println("Class loading Problem: " + e);
} finally {
if (desFile.exists()) {
desFile.delete();
}
}
}
}
Mal ejemplo...
Como ejemplo para los conceptos está bien, sin embargo no deberías usar ese código en ninguna aplicación real por lo siguiente:
En esto otra ves.
Habia estado un poco ocupado y ademas habia pormetido dejar un post sobre openssl, pero me he estado peleando con esto desde hace varios dias y no he logrado decifrar un archivo. La cuestion es la siguiente: tengo un archivo cifrado usando b64 y encriptado usando aes256 en el proceso intervienen dos certificados y una llave, esta misma llave se usara para decifrar. He visto ejemplos usando JCE, JSSE y BouncyCastle pero ninguno se adapta a lo que necesito, por que descifran lo mismo que ellos cifraron usando una llave privada que ellos mismo crearon, y yo necesito descifrarlo usando dos certificados y una llave privada que ya tengo como archivo. Si alguien sabe algo o tiene algun ejemplo me ayudaria de mucho.
Confusión
No entendí bien entonces. El base 64 no es encripción, es simple codificación para guardar datos binarios en formato de texto y puedes encontrar diversas implementaciones (por alguna razón siguen sin incluir una clase para codificar/decodificar base64 en el runtime de Java).
Si los datos vienen encriptados con AES256 entonces es encripción simétrica.
Pero luego mencionas certificados (asumo que son X.509); un certificado X509 es básicamente una llave pública firmada digitalmente por la llave privada de otra entidad.
La encripción asimétrica por lo general funciona así (supongamos que te quiero mandar datos encriptados):
Genero una llave simétrica (por ejemplo de AES256) de manera aleatoria y encripto los datos que te voy a enviar.
Encripto esa llave simétrica con tu llave pública (que viene en tu certificado X509).
Si tengo yo mi propio certificado X509 entonces puedo además sacar una digestión de los datos y encriptarla con mi llave privada y mandarte el resultado junto con mi certificado.
Para descifrar los datos primero usas tu llave privada para obtener la llave simétrica; descifras los datos, les sacas una digestión, descifras la firma digital (la digestión que yo te envié encriptada con mi llave privada) usando mi certificado (o sea mi llave pública) y comparas ambas digestiones, deben ser iguales (si son distintas es que el mensaje fue alterado de alguna forma).
No sé bien qué formato exactamente use OpenSSL para encriptar los datos, eso lo puedes ver en la documentación del proyecto. Pero los certificados X509 se almacenan de forma estándar igual que las llaves privadas. Puedes ver algo de eso en la guía de criptografía de Java.
Los dos certificados a los que te refieres, para qué son? uno es el que cifró y firmó los datos y otro es el de quien los debe descifrar? Si los datos (bueno realmente la llave) fueron encriptados con una llave pública, necesitas la llave privada para descifrar, no un certificado. Los certificados los necesitas para cifrar datos para el dueño del certificado, o para comprobar la firma de datos firmados por el dueño del certificado.
Hola ezamudio, gracias por
Hola ezamudio, gracias por contestar. Creo que no fui muy claro y solo te revolvi, bueno me pasaron unos comandos que ya probe desde la consola (se tubo que instalar el openssl obviamente para que reconociera los comandos), en si son tres procesos para encriptar y tres para desencriptar, se cifra usando base 64, se firma y se encripta, se usan dos archivos .cer (certificado por la CA y otro certificado propio) y un .key (llave privada), estos son los comandos:
---> ENCRIPCION <---
openssl smime -sign -inkey archivo.pem.key -signer OC2009.pem.cer -in prueba.b64 -passin pass:password -out prueba.b64.ce
openssl smime -encrypt -aes256 -in prueba.b64.ce -out prueba.b64.ce.enc cce2006.pem.cer
--->DESENCRIPCION <---
openssl smime -verify -signer OC2009.pem.cer -in salida.ce.b64 -CAfile CAFile.pem.cer -out salida.b64
openssl enc -d -base64 -in salida.b64 -out salida.txt
Estos comandos funcionan adecuadamente, ya los probe. Pero necesito hacerlo desde Java usando JCE o JSSE o BouncyCastle, quise que me aprobaran el lanzamiento de estos comandos desde Java usando el exec() pero no, quieren que se haga todo en Java por que no se quiere depender de que la maquina donde se ejecute este proceso tengo configurado el openssl, bueno seguiere en estal labor pero cualquier informacion o clase de ejemplo me ayudaria, gracias.
Proceso...
Bueno si así lo hacen con OpenSSL pues así lo tendrás que hacer con Java. Pero ese primer paso en encripción y último paso en decripción es completamente superfluo. Base64 no es un algoritmo de encripción, es solamente codificación de datos, para almacenar datos binarios como texto plano.
Y lo que estás haciendo con esos comandos efectivamente es: codificar a base 64 el prueba.txt (algo innecesario), luego firmar digitalmente los datos (ya expliqué lo que esto signfiica en mi comentario anterior) y finalmente encriptar los datos con una llave pública. En el ejemplo que pones, los datos son firmados por el dueño del certificado OC2009.pem.cer y se encriptan para que los pueda ver el usuario dueño del archivo cce2006.pem.cer.
El segundo paso no encripta nada de datos; la firma digital como ya había explicado, realmente es sacar una digestión de los datos y encriptarla con una llave privada (en este caso archivo.pem.key que es la llave privada correspondiente al certificado OC2009.pem.cer).
JCE y BouncyCastle tienen funciones para realizar todo esto pero las vas a tener que manejar a bajo nivel, no vas a invocar 3 métodos y ya.
BouncyCastle tiene codificador para Base 64 y tal vez sea lo único que tengas que usar directamente de BC; para lo demás puede que no necesites esa librería porque el proveedor estándar de JCA te dará lo necesario. Pero sí vas a tener que echarte un clavado a la documentación oficial de JCA para ver cómo se usa (ya te puse una liga a la documentación de JCA en mi comentario anterior, apuntando directamente a cómo leer certificados codificados en base 64).
Lee acerca de la encripción asimétrica porque probablemente no vas a tener que bajarte de nivel tanto como para leer la llave AES sino solamente para usar los certficados (que contienen las llaves públicas) y la llave privada de archivo.pem.key, que probablemente será lo más engorroso de leer con JCA o Bouncy Castle. Pero OpenSSL está usando puros formatos estándar para los datos que almacena así que no debes tener problemas en leerlos desde Java (S/MIME es un estándar basado a su vez en muchos otros estándares de la serie PKCS).
No te puedo poner código porque tendría que tener certificados y llaves privadas y todo eso para probar que el código funcione y estar un buen rato leyendo documentación para refrescar mi memoria y sinceramente no tengo tiempo pero te puedo decir más o menos los pasos a grandes rasgos:
Primero necesitas leer la llave privada del archivo.pem.key; probablemente venga codificada en formato PKCS#8 y vas a tener que primero decodificar manualmente el archivo; si viene en base64 primero decodifica eso; si ya viene en binario, tienes que usar un decodificador de ASN.1 (BC trae clases para ello). Al final debes tener una ASN1Sequence a partir de la cual creas una EncryptedPrivateKeyInfo (busca esas clases en BC). Ese objeto te indica el algoritmo con el que viene encriptada la llave y te devuelve los datos como un arreglo de bytes; los puedes descifrar de manera normal con una instancia de Cipher configurada con el algoritmo y parámetros que indique la EncryptedPrivateKeyInfo. Al final los bytes que tengas con la llave descifrada los puedes pasar a una PrivateKeyFactory para obtener ya la llave privada.
Posteriormente esa llave privada la vas a poder usar para descifrar los datos (en el segundo ejemplo que mencionas) o para firmar los datos (en el primer ejemplo), dependiendo la llave que uses. El proceso que mencionas es para enviar un mensaje de un usuario a otro; la llave privada del remitente se usa para firmar los datos y la llave privada del recipiente para descifrar los datos. Para firmar datos lee la documentación de JCA acerca de la clase Signature. Para decriptar los datos lee ahi mismo acerca de descifrar datos de RSA y encripción asimétrica. Para encriptar con el certificado del recipiente lee acerca de encriptar con RSA y los certificados X509 (el certificado contiene la llave pública que usas para encriptar el mensaje para el destino). Para verificar la firma de los datos es también con la clase Signature y el certificado X509 del remitente.
Es importante que solamente completes el proceso de decripción si puedes validar la firma digital correctamente. Cualquier error en la firma significa que el mensaje ha sido alterado o que no proviene de quien dice venir y por lo tanto debes desechar esos datos (notificando al usuario del problema).