2009-09-17 88 views
16

He leído los siguientes subprocesos y me han ayudado un poco, pero estoy buscando un poco más de información.Cómo implementar cifrado AES Java de 256 bits con CBC

How to write AES/CBC/PKCS5Padding encryption and decryption with Initialization Vector Parameter for BlackBerry

Java 256bit AES Encryption

Básicamente, lo que estoy haciendo está escribiendo un programa que va a cifrar una solicitud que se enviará a través de TCP/IP, y entonces descifrado por un programa de servidor. La encriptación necesitará ser AES, y al hacer una investigación descubrí que necesito usar CBC y PKCS5Padding. Básicamente, necesito una clave secreta y una IV también.

La aplicación que estoy desarrollando es para un teléfono, por lo que quiero utilizar los paquetes de seguridad de Java para mantener el tamaño. Ya terminé el diseño, pero no estoy seguro de la implementación del IV y la clave compartida.

Aquí hay algo de código:

// My user name 
byte[] loginId = "login".getBytes(); 

byte[] preSharedKey128 = "ACME-1234AC".getBytes(); 
byte[] preSharedKey192 = "ACME-1234ACME-1234A".getBytes(); 
// 256 bit key 
byte[] preSharedKey256 = "ACME-1234ACME-1234ACME-1234".getBytes(); 
byte[] preSharedKey = preSharedKey256; 

// Initialization Vector 
// Required for CBC 
byte[] iv ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 
IvParameterSpec ips = new IvParameterSpec(iv); 


byte[] encodedKey = new byte[loginId.length + preSharedKey.length]; 

System.arraycopy(loginId, 0, encodedKey, 0, loginId.length); 
System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length); 

// The SecretKeySpec provides a mechanism for application-specific generation 
// of cryptography keys for consumption by the Java Crypto classes. 

// Create a key specification first, based on our key input. 
SecretKey aesKey = new SecretKeySpec(encodedKey, "AES"); 

// Create a Cipher for encrypting the data using the key we created. 
Cipher encryptCipher; 

encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
// Initialize the Cipher with key and parameters 
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey, ips); 

// Our cleartext 
String clearString = "33,8244000,9999,411,5012022517,0.00,0,1,V330"; 
byte[] cleartext = clearString.getBytes(); 

// Encrypt the cleartext 
byte[] ciphertext = encryptCipher.doFinal(cleartext); 

// Now decrypt back again... 
// Decryption cipher 
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
// Initialize PBE Cipher with key and parameters 
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips); 

// Decrypt the cleartext 
byte[] deciphertext = decryptCipher.doFinal(ciphertext); 

En pocas palabras lo que debe hacer es cifrar algún mensaje que pueda descifrarse por el servidor sin que el servidor necesidad de obtener una clave o IV del teléfono. ¿Hay alguna manera de que yo pueda hacer esto donde pueda asegurar el IV y la llave en el teléfono, y aún así tener la clave y el IV conocidos por el servidor también? Siéntase libre de decirme que deje las cosas más claras si no lo son.

+1

Las claves y contraseñas codificadas aquí son sólo para mi comprensión. No sé mucho sobre encriptación, así que básicamente estoy en modo make-it-compile-and-run. – Stevus

+1

¿Cuál es su modelo de amenaza? Quiero decir, si piensas que se necesita encriptación, debes tener alguna idea sobre quién querría esta información y por qué, cuánto la perjudicaría, la beneficiaría, etc. – erickson

+0

Supongo que mi modelo de amenaza es que tenemos cierta certeza información de cuenta/inicio de sesión necesaria para hacer una transacción bancaria y no queremos que esté expuesta. ¿Es eso lo que estás buscando? – Stevus

Respuesta

11

Hay algunos problemas con el código. En primer lugar, debería usar un generador de claves para generar claves secretas. Usar solo texto directamente podría funcionar para algunos algoritmos, pero otros tienen claves débiles y otros que necesitan ser probados.

Incluso si desea hacer un cifrado basado en contraseña, la contraseña debe ejecutarse a través de un algoritmo de derivación de clave para producir una clave, como se muestra en my answer a la pregunta que ya citó.

Además, no debe utilizar el método no-arg getBytes() de String. Esto depende de la plataforma. Si todas las cadenas que está codificando contienen solo caracteres del conjunto de caracteres US-ASCII, aclárelo especificando esa codificación explícitamente. De lo contrario, si las plataformas del teléfono y del servidor usan codificaciones de caracteres diferentes, la clave y IV no se mostrarán igual.

Para el modo CBC, es mejor utilizar una nueva IV para cada mensaje que envíe. Por lo general, los CBC IV se generan aleatoriamente. Otros modos como CFB y OFB requieren IV únicos para cada mensaje. Por lo general, los IV se envían con el texto cifrado — Los IV no necesitan mantenerse en secreto, pero muchos algoritmos se romperán si se usa un IV predecible.

El servidor no necesita obtener el secreto o IV directamente del teléfono. Usted puede configurar el servidor con la clave secreta (o contraseña, de la que se deriva la clave secreta), pero en muchas aplicaciones, este sería un mal diseño.

Por ejemplo, si la aplicación va a implementarse en los teléfonos de varias personas, no es una buena idea que usen la misma clave secreta. Un usuario malintencionado puede recuperar la clave y romper el sistema para todos.

Un mejor enfoque es generar nuevas claves secretas en el teléfono, y utilizar un algoritmo de acuerdo clave para intercambiar la clave con el servidor. El acuerdo de clave Diffie-Hellman se puede usar para esto, o la clave secreta se puede encriptar con RSA y enviar al servidor.


Actualización:

Diffie-Hellman en el modo "-efímera estática" (y el modo "estático-estática" también, aunque eso es menos deseable) es posible sin un mensaje inicial desde el servidor al el teléfono, siempre que la clave pública del servidor esté incrustada en la aplicación.

La clave pública del servidor no presenta el mismo riesgo que la incrustación de una clave secreta común en el teléfono. Como se trata de una clave pública, la amenaza sería que un atacante tuviera en sus manos (o hackeara de forma remota) el teléfono y reemplazara la clave pública real con una clave falsa que le permita suplantar al servidor.

Puede usarse el modo estático estático, pero en realidad es más complicado y un poco menos seguro. Cada teléfono necesitaría su propio par de claves único, o volverás a caer en el problema de la clave secreta. Al menos no habría necesidad de que el servidor realizara un seguimiento de qué teléfono tiene qué tecla (suponiendo que exista algún mecanismo de autenticación a nivel de aplicación, como una contraseña).

No sé qué tan rápido son los teléfonos. En mi escritorio, generar un par de claves efímeras toma aproximadamente 1/3 de segundo. Generar parámetros Diffie-Hellman es muy lento; definitivamente querrá volver a usar los parámetros de la clave del servidor.

+0

En este momento, el diseño que me dicen usar dice que podemos usar la misma clave para cada teléfono pero no estamos de acuerdo. ¿El enfoque de Diffie-Hellman es fácil de implementar? – Stevus

+0

No, Diffie-Hellman es un poco confuso, al menos para mí. Utiliza la clase KeyAgreement. El cifrado RSA es más fácil. Utiliza un cifrado como el cifrado AES que está utilizando actualmente. Una cosa a tener en cuenta con RSA es que el texto cifrado será de cientos de bytes, lo cual es un desperdicio si el texto plano tiene solo unos pocos bytes; no estoy seguro si eso importa en tu aplicación. Otra opción a considerar es que si la comunicación es de mensajes muy cortos (de 100 a 200 bytes) de un teléfono a otro solo, puede encriptar el mensaje con RSA. – erickson

+0

Sí, creo que el nivel de seguridad que estoy buscando no implica algo realmente complicado. Sin embargo, lo tendrá en cuenta. – Stevus

2

proyectos similares realizados en un MIDlet antes, he siguientes consejos para usted:

  1. No hay manera segura de almacenar secreto compartido en el teléfono. Puede usarlo pero esto cae en una categoría llamada Security through Obscurity. Es como un tipo de seguridad "clave debajo del tapete".
  2. No utilice AES de 256 bits, que no está ampliamente disponible. Es posible que deba instalar otra JCE. AES o TripleDES de 128 bits aún se consideran seguros. Teniendo en cuenta el número 1, no debe preocuparse por esto.
  3. La encriptación usando una contraseña (diferente para cada usuario) es mucho más segura. Pero no debe usar la contraseña como la clave que muestra en el ejemplo. Utilice PBEKeySpec (encriptación basada en contraseña) para generar las claves.
  4. Si solo te preocupan los ataques MITM (man-in-the-middle), usa SSL.
+0

¿Por qué no se puede usar AES de 256 bits? Ya puedo crear una clave de 256 bits en la configuración de prueba, pero se hace manualmente. En su mayoría, a la gerencia le preocupa que alguien intercepte una transmisión y use la información de la transmisión o consiga un teléfono e ingeniería inversa del algoritmo y básicamente sea un impostor en nuestra red. – Stevus

+0

Muchos de los teléfonos que probamos no son compatibles. Mi banco todavía usa 128-bit AES. Es seguro. –

+1

En realidad, se han encontrado algunas debilidades en AES 256. AES-128 es bastante fuerte, y es recomendado por Bruce Schneier sobre AES 256 cuando no es necesario para la interoperabilidad. (http://www.schneier.com/blog/archives/2009/07/another_new_aes.html) Sin embargo, el algoritmo realmente no importa si la clave no es segura. – erickson

Cuestiones relacionadas