2012-03-11 49 views
5

Escribo una pequeña aplicación para transferir archivos, más o menos como una forma de obtener más información sobre los fundamentos del cifrado programático. La idea es generar un par de claves RSA, intercambiar claves públicas y enviar el AES iv y la clave para un descifrado adicional. Quiero cifrar la clave AES con la clave pública receptores RSA, así:Encriptación de la clave AES con la clave pública RSA

// encode the SecretKeySpec 
private byte[] EncryptSecretKey() 
{ 
    Cipher cipher = null; 
    byte[] key = null; 

    try 
    { 
     cipher = Cipher.getInstance("RSA/ECB/NOPADDING"); 
     // contact.getPublicKey returns a public key of type Key 
     cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey()); 
     // skey is the SecretKey used to encrypt the AES data 
     key = cipher.doFinal(skey.getEncoded()); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception encoding key: " + e.getMessage()); 
     e.printStackTrace(); 
    } 
    return key; 
} 

que luego escribir el valor clave para el receptor, y descifrarlo, así:

private SecretKey decryptAESKey(byte[] data) 
{ 
    SecretKey key = null; 
    PrivateKey privKey = null; 
    Cipher cipher = null; 

    System.out.println ("Data as hex: " + utility.asHex(data)); 
    System.out.println ("data length: " + data.length); 
    try 
    { 
     // assume this loads our private key 
     privKey = (PrivateKey)utility.loadLocalKey("private.key", false); 

     cipher = Cipher.getInstance("RSA/ECB/NOPADDING"); 
     cipher.init(Cipher.DECRYPT_MODE, privKey); 
     key = new SecretKeySpec(cipher.doFinal(data), "AES"); 

     System.out.println ("Key decrypted, length is " + key.getEncoded().length); 
     System.out.println ("data: " + utility.asHex(key.getEncoded())); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception decrypting the aes key: " + e.getMessage()); 
     e.printStackTrace(); 
     return null; 
    } 

    return key; 
} 

En la consola , en el otro lado, consigo este como salida:

read_bytes for key: 16 
data length: 16 
Data as hex: <hex string> 
Key decrypted, length is 256 
java.security.InvalidKeyException: Invalid AES key length: 256 bytes 

Además, si se crea una matriz de bytes de tamaño 16 y poner la salida cipher.doFinal (datos) en ella, la matriz es aparentemente cambiar el tamaño a 256 bytes (.leng eso lo dice, al menos). ¿Por qué sería esto, y además, qué estoy haciendo incorrectamente?

edición
He resuelto esto, y pensé que había puesto el tema en caso de que alguien se encuentra con esto. El problema, resultó ser el RSA/ECB/NOPADDING. Por alguna extraña razón, estaba arruinando mi creación de SecretKey cuando lo transferí al cliente. Es podría tener algo que ver con la forma en que estoy generando pares de claves (estoy usando getInstance ("RSA") para eso), pero no estoy del todo seguro.

+1

sombrero Hola, por favor, puesto que su completa ** ** respuesta como una respuesta en lugar de editarlo en su pregunta (que nos dice lo que el el problema era, no la respuesta). Entonces puedes aceptar tu propia respuesta después de un tiempo. O acepte el mío, lo que explica por qué debería usar '" RSA/ECB/PKCS1Padding "' en lugar de '" RSA/ECB/NoPadding "' ... –

+0

Tenga en cuenta que debe usar protección de integridad (por ejemplo, una firma) cuando crea un protocolo en línea que realiza el cifrado. Incluso el cifrado de RSA puede ser aceptable, p. ataques de oráculo de relleno. No tener una protección de integridad es un error común, aunque este problema es limitado si usa PKCS # 1 v1.5 (implícito en '" RSA/ECB/PKCS1Padding "'). –

+0

Como nota al margen, el BCE rara vez es un buen modo para un blockcipher. – CodesInChaos

Respuesta

4

Como se mencionó en owlstead, no se puede usar simplemente el RSA "sin procesar" sin relleno para el cifrado/descifrado. Para uno, es muy inseguro, y para otro, las bibliotecas de Java ni siquiera lo admiten. A continuación se muestra el código de trabajo para el cifrado/descifrado de la clave AES utilizando pares de claves RSA.

private byte[] EncryptSecretKey() 
{ 
    Cipher cipher = null; 
    byte[] key = null; 

    try 
    { 
     // initialize the cipher with the user's public key 
     cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey()); 
     key = cipher.doFinal(skey.getEncoded()); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception encoding key: " + e.getMessage()); 
     e.printStackTrace(); 
    } 
    return key; 
} 

descifrado de los looks clave AES de esta manera:

private SecretKey decryptAESKey(byte[] data) 
{ 
    SecretKey key = null; 
    PrivateKey privKey = null; 
    Cipher cipher = null; 

    try 
    { 
     // this is OUR private key 
     privKey = (PrivateKey)utility.loadLocalKey(
           ConfigFrame.privateKeyLocation, false); 

     // initialize the cipher... 
     cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, privKey); 

     // generate the aes key! 
     key = new SecretKeySpec (cipher.doFinal(data), "AES"); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception decrypting the aes key: " 
               + e.getMessage()); 
     return null; 
    } 

    return key; 
} 
+1

Hola, he creado una pequeña edición para confiar explícitamente en "RSA/ECB/PKCS1Padding" en lugar de usar el valor predeterminado del proveedor (que devuelve el mismo resultado en el proveedor Oracle JCE actual, pero es mucho más seguro especificarlo directamente). –

+0

gracias por la ayuda/aclaración, owlstead! – hat

0

Una cosa para recordar es que con RSA y de hecho muchas otras algoritmos AES incluyendo por lo general, los datos "útiles" que se proporciona no es, literalmente, los datos que se encripta. Por lo general, se deben incluir algunos datos adicionales , por ejemplo, indicando la longitud real de los datos de alguna manera, datos para cualquier comprobación de integridad ... Para el usuario, el número de bytes de entrada no es necesariamente igual al número de bytes después del cifrado.

Comment Source

Para obtener el tamaño de la clave correcta puede utilizar una HashAlgorithm en la llave (SHA), el cual le dará una salida de tamaño fijo. De lo contrario, puede usar los primeros 16 bytes como clave e ignorar el resto? Buena suerte.

+0

Tiene razón, parece que está rellenando la longitud de la clave (claves de 2048 bits, en mi caso). Tampoco veo soporte para NOPADDING con el objeto Cipher de Java 7, pero no me lanza una excepción. Supongo que investigaré analizando los datos reales que necesito. ¡Gracias! – hat

4

No puede simplemente usar RSA "sin formato" para encriptar datos sin ningún tipo de relleno. Necesitas algún tipo de esquema de relleno, aunque solo sea por razones de seguridad. Normalmente se usa "RSA/ECB/PKCS1Padding". Esto puede cifrar datos de hasta 11 bytes menos que el tamaño de la clave. Se asegura de que los datos encajen en el módulo y agrega al menos 8 bytes de datos aleatorios para garantizar que el cifrado, p. la palabra "Sí" dos veces no da como resultado dos textos de cifrado idénticos. Finalmente, se asegura de que pueda averiguar el tamaño en octetos de los datos cifrados, de modo que simplemente puede encriptar los 16, 24 o 32 bytes que componen la clave AES.

+1

Por supuesto, también puede usar el sucesor, OAEP, pero eso no es realmente necesario y puede ser menos compatible que el esquema de relleno PKCS # 1 v1.5 predeterminado. –

Cuestiones relacionadas