Esta es la primera página que se muestra a través de Google y las vulnerabilidades de seguridad en todas las implementaciones me ponen nervioso, así que estoy publicando esto para agregar información sobre el cifrado para otros, ya que han pasado 7 años desde la publicación original. Tengo una Maestría en Ingeniería Informática y pasé mucho tiempo estudiando y aprendiendo Criptografía, así que estoy gastando mis dos centavos para hacer de Internet un lugar más seguro.
Además, tenga en cuenta que mucha implementación puede ser segura para una situación dada, pero ¿por qué usarlos y cometer un error? Utilice las herramientas más potentes que tenga disponibles a menos que tenga una razón específica para no hacerlo. En general, recomiendo usar una biblioteca y mantenerse alejado de los detalles esenciales si es posible.
ACTUALIZACIÓN 4/5/18: Reescribí algunas partes para que sean más fáciles de entender y cambié la biblioteca recomendada de Jasypt a la nueva biblioteca de Google Tink , recomendaría eliminar completamente Jasypt de una configuración existente.
Prefacio
Explicaré los conceptos básicos de la criptografía simétrica segura a continuación y señalaré los errores comunes que veo en línea cuando las personas implementan criptografía por su cuenta con la biblioteca estándar de Java. Si desea omitir todos los detalles, vaya a la nueva biblioteca de Google. Tink importe eso en su proyecto y use el modo AES-GCM para todos sus cifrados y estará seguro.
Ahora, si quieres aprender los detalles esenciales sobre cómo cifrar en Java, sigue leyendo :)
Cifrados de bloque
Lo primero que debe hacer primero es elegir una clave simétrica Block Cipher. Un Block Cipher es una función / programa de computadora utilizado para crear Pseudoaleatoriedad. La pseudoaleatoriedad es una aleatoriedad falsa que ninguna computadora que no sea una Computadora Cuántica podría distinguir entre la aleatoriedad real y la aleatoriedad real. Block Cipher es como el componente básico de la criptografía, y cuando se usa con diferentes modos o esquemas podemos crear cifrados.
Ahora, con respecto a los algoritmos de cifrado de bloque disponibles en la actualidad, asegúrese de NUNCA , repito NUNCA use DES , incluso diría que NUNCA use 3DES . El único Block Cipher que incluso el lanzamiento de la NSA de Snowden pudo verificar que está realmente tan cerca de Pseudo-Random como sea posible es AES 256 . También existe AES 128; la diferencia es que AES 256 funciona en bloques de 256 bits, mientras que AES 128 funciona en bloques de 128. Con todo, AES 128 se considera seguro, aunque se han descubierto algunas debilidades, pero 256 es tan sólido como parece.
Dato curioso El DES fue roto por la NSA cuando se fundó inicialmente y en realidad mantuvo un secreto durante unos años. Aunque algunas personas todavía afirman que 3DES es seguro, hay bastantes trabajos de investigación que han encontrado y analizado las debilidades en 3DES .
Modos de cifrado
El cifrado se crea cuando toma un cifrado de bloque y usa un esquema específico para que la aleatoriedad se combine con una clave para crear algo que sea reversible siempre que conozca la clave. Esto se conoce como modo de cifrado.
Aquí hay un ejemplo de un modo de cifrado y el modo más simple conocido como BCE para que pueda comprender visualmente lo que está sucediendo:
Los modos de cifrado que verá más comúnmente en línea son los siguientes:
BCE CTR, CBC, GCM
Existen otros modos fuera de los enumerados y los investigadores siempre están trabajando hacia nuevos modos para mejorar los problemas existentes.
Ahora pasemos a las implementaciones y lo que es seguro. NUNCA use BCE, esto es malo para ocultar datos repetidos como lo muestra el famoso pingüino de Linux .
Al implementar en Java, tenga en cuenta que si usa el siguiente código, el modo ECB se establece de manera predeterminada:
Cipher cipher = Cipher.getInstance("AES");
... ¡PELIGRO ESTA ES UNA VULNERABILIDAD! y desafortunadamente, esto se ve en todo StackOverflow y en línea en tutoriales y ejemplos.
Nonces y IVs
En respuesta al problema encontrado con el modo BCE, se crearon los sustantivos también conocidos como IV. La idea es que generemos una nueva variable aleatoria y la adjuntemos a cada encriptación para que cuando encriptes dos mensajes que sean iguales salgan diferentes. La belleza detrás de esto es que un IV o nonce es de conocimiento público. Eso significa que un atacante puede tener acceso a esto, pero mientras no tengan su clave, no pueden hacer nada con ese conocimiento.
Los problemas comunes que veré es que las personas establecerán el IV como un valor estático como en el mismo valor fijo en su código. y aquí está el escollo de los IV en el momento en que repite uno, realmente compromete la seguridad completa de su cifrado.
Generando un IV al azar
SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
Nota: SHA1 está roto, pero no pude encontrar cómo implementar SHA256 en este caso de uso correctamente, por lo que si alguien quiere resolver esto y actualizarlo, ¡sería increíble! Además, los ataques SHA1 aún no son convencionales, ya que puede demorar algunos años en que un gran clúster se rompa. Mira los detalles aquí.
Implementación de CTR
No se requiere relleno para el modo CTR.
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
Implementación de CBC
Si elige implementar el modo CBC, hágalo con PKCS7Padding de la siguiente manera:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Vulnerabilidad de CBC y CTR y por qué debería usar GCM
Aunque algunos otros modos, como CBC y CTR, son seguros, se topan con el problema de que un atacante puede voltear los datos cifrados, cambiando su valor cuando se descifra. Entonces, digamos que encripta un mensaje bancario imaginario "Vender 100", su mensaje encriptado se ve así "eu23ng", el atacante cambia un poco a "eu53ng" y, de repente, cuando desencripta su mensaje, se lee como "Vender 900".
Para evitar esto, la mayoría de Internet usa GCM, y cada vez que ve HTTPS probablemente estén usando GCM. GCM firma el mensaje cifrado con un hash y lo verifica para verificar que el mensaje no se haya cambiado con esta firma.
Evitaría implementar GCM debido a su complejidad. Es mejor usar la nueva biblioteca de Google Tink porque aquí nuevamente, si repite accidentalmente un IV, está comprometiendo la clave en el caso de GCM, que es el último defecto de seguridad. Los nuevos investigadores están trabajando hacia modos de encriptación resistentes a la repetición IV, donde incluso si repite la IV, la clave no está en peligro, pero esto aún no se ha generalizado.
Ahora, si desea implementar GCM, aquí hay un enlace a una buena implementación de GCM . Sin embargo, no puedo garantizar la seguridad o si está implementada correctamente, pero baja la base. También tenga en cuenta que con GCM no hay relleno.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
Claves vs Contraseñas
Otra nota muy importante, es que cuando se trata de criptografía, una clave y una contraseña no son lo mismo. Una clave en criptografía debe tener una cierta cantidad de entropía y aleatoriedad para considerarse segura. Es por eso que debe asegurarse de utilizar las bibliotecas criptográficas adecuadas para generar la clave para usted.
Entonces, realmente tiene dos implementaciones que puede hacer aquí, la primera es usar el código que se encuentra en este hilo de StackOverflow para la generación aleatoria de claves . Esta solución utiliza un generador de números aleatorios seguro para crear una clave desde cero que puede utilizar.
La otra opción menos segura es usar la entrada del usuario, como una contraseña. El problema como discutimos es que la contraseña no tiene suficiente entropía, por lo que tendríamos que usar PBKDF2 , un algoritmo que toma la contraseña y la fortalece. Aquí hay una implementación de StackOverflow que me gustó . Sin embargo, la biblioteca Google Tink tiene todo esto incorporado y deberías aprovecharlo.
Desarrolladores de Android
Un punto importante a señalar aquí es saber que su código de Android es de ingeniería inversa y la mayoría de los casos la mayoría del código de Java también lo es. Eso significa que si almacena la contraseña en texto sin formato en su código. Un hacker puede recuperarlo fácilmente. Por lo general, para este tipo de cifrado, desea utilizar la Criptografía asimétrica, etc. Esto está fuera del alcance de esta publicación, así que evitaré sumergirme en él.
Una lectura interesante de 2013 : señala que el 88% de las implementaciones de Crypto en Android se realizaron de forma incorrecta.
Pensamientos finales
Una vez más, sugeriría evitar implementar la biblioteca java para crypto directamente y usar Google Tink , le ahorrará el dolor de cabeza ya que realmente han hecho un buen trabajo al implementar todos los algoritmos correctamente. E incluso entonces, asegúrese de verificar los problemas que aparecen en el github de Tink, las vulnerabilidades emergentes aquí y allá.
Si tiene alguna pregunta o comentario, ¡no dude en comentar! La seguridad siempre está cambiando y debe hacer todo lo posible para mantenerse al día :)