2010-12-29 18 views
10

Quiero convertir cadena a secretKey¿Cómo puedo convertir una cadena a un SecretKey

public void generateCode(String keyStr){ 
KeyGenerator kgen = KeyGenerator.getInstance("AES"); 
kgen.init(128); // 192 and 256 bits may not be available 
// Generate the secret key specs. 
secretKey skey=keyStr; //How can I make the casting here 
//SecretKey skey = kgen.generateKey(); 
byte[] raw = skey.getEncoded(); 
} 

intento usar BASE64Decoder en lugar de secretKey, pero se enfrentan a un problema que es que no puedo especificar longitud de la clave.

EDIT: Quiero llamar a esta función desde otro lugar

static public String encrypt(String message , String key , int keyLength) throws Exception { 
    // Get the KeyGenerator 
    KeyGenerator kgen = KeyGenerator.getInstance("AES"); 
    kgen.init(keyLength); // 192 and 256 bits may not be available 
    // Generate the secret key specs. 
    SecretKey skey = key; //here is the error 
    byte[] raw = skey.getEncoded(); 
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); 
    // Instantiate the cipher 
    Cipher cipher = Cipher.getInstance("AES"); 

    cipher.init(Cipher.ENCRYPT_MODE, skeySpec); 
    System.out.println("msg is" + message + "\n raw is" + raw); 
    byte[] encrypted = cipher.doFinal(message.getBytes()); 
    String cryptedValue = new String(encrypted); 
    System.out.println("encrypted string: " + cryptedValue); 
    return cryptedValue; 
} 

Si alguien puede ayudar, estaría muy agradecido.

+0

El 'KeyGenerator kgen' no se usa nunca. ¿Qué está haciendo eso allí? Un 'KeyGenerator' se utiliza para elegir una nueva clave aleatoria. Pero parece que desea reutilizar una clave existente que de alguna manera está almacenada en un 'String' (¿Base-64 codificada, quizás?) – erickson

Respuesta

37

No hay comprobaciones de integridad, para estas razones particulares

  1. la necesidad no es evidente a partir del caso de uso.
  2. "AES/GCM/NoPadding" modo solo está disponible desde Java 7 en adelante
  3. Depende del usuario si desea implementar, p. HMAC y/o AESCMAC (recomendado).
  4. Necesitará una clave adicional como mínimo y dos pases completos.

Si tiene una implementación del modo GCM en ambos lados, p. Ej. usando Bouncy Castle en Java 6 - por favor, porque es mucho más seguro (siempre que el "IV" sea realmente único). Debería ser realmente fácil cambiar la implementación.

notas de aplicación en materia de cifrado

  1. Esta aplicación no es segura cuando se utilizan en un papel de cliente/servidor sin restricciones debido a los ataques del oráculo de relleno (se requieren 128 intentos por byte o menor, en promedio, independientemente de algoritmo o tamaño de clave). Tendrá que usar un MAC, HMAC o Signature sobre los datos cifrados, y verificarlo antes de descifrarlo para implementarlo en modo cliente/servidor.
  2. El descifrado devolverá nulo si falla el descifrado. Esto solo puede indicar una excepción de relleno, que debe manejarse adecuadamente (¿advertí sobre los ataques oracle de relleno?)
  3. Las claves inválidas se devolverán como InvalidArgumentException.
  4. Todas las demás excepciones relacionadas con la seguridad están "barridas debajo de la tabla", ya que significa que el entorno de ejecución de Java no es válido. Por ejemplo, admitir "UTF-8" y "AES/CBC/PKCS5Padding" es requiere para cada implementación de Java SE.

Algunas otras notas

  1. favor, no intente lo contrario e insertar bytes directamente en la cadena de entrada del método de cifrar (utilizando new String(byte[]) por ejemplo). ¡El método puede fallar silenciosamente!
  2. Optimizado para facilitar la lectura. Vaya a las implementaciones de transmisión Base64 y CipherStream si prefiere la velocidad y una mejor huella de memoria.
  3. Necesita al menos Java 6 SE o compatible para ejecutar este código.
  4. cifrado/descifrado puede fallar por tamaños de clave AES de 128 bits más a medida que puede necesitar los archivos de política para el cifrado sin restricciones (disponibles en Oracle)
  5. Cuidado con las regulaciones gubernamentales al exportar cifrado.
  6. Esta implementación utiliza llaves hexadecimales en lugar de las teclas base64 ya que son lo suficientemente pequeñas, y el hexadecimal es simplemente más fácil de editar/verificar manualmente.
  7. Codificación/decodificación hexadecimal y base64 utilizadas recuperadas del JDK, no se necesitan bibliotecas externas de ningún tipo.
  8. Uber simple de usar, pero, por supuesto, no muy orientado a objetos, no hay almacenamiento en caché de las instancias de objeto utilizadas en el cifrado/descifrado. Refactor a voluntad.

bien, aquí viene algo de código ...

public static String encrypt(final String plainMessage, 
      final String symKeyHex) { 
     final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex); 

     final byte[] encodedMessage = plainMessage.getBytes(Charset 
       .forName("UTF-8")); 
     try { 
      final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      final int blockSize = cipher.getBlockSize(); 

      // create the key 
      final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES"); 

      // generate random IV using block size (possibly create a method for 
      // this) 
      final byte[] ivData = new byte[blockSize]; 
      final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG"); 
      rnd.nextBytes(ivData); 
      final IvParameterSpec iv = new IvParameterSpec(ivData); 

      cipher.init(Cipher.ENCRYPT_MODE, symKey, iv); 

      final byte[] encryptedMessage = cipher.doFinal(encodedMessage); 

      // concatenate IV and encrypted message 
      final byte[] ivAndEncryptedMessage = new byte[ivData.length 
        + encryptedMessage.length]; 
      System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize); 
      System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage, 
        blockSize, encryptedMessage.length); 

      final String ivAndEncryptedMessageBase64 = DatatypeConverter 
        .printBase64Binary(ivAndEncryptedMessage); 

      return ivAndEncryptedMessageBase64; 
     } catch (InvalidKeyException e) { 
      throw new IllegalArgumentException(
        "key argument does not contain a valid AES key"); 
     } catch (GeneralSecurityException e) { 
      throw new IllegalStateException(
        "Unexpected exception during encryption", e); 
     } 
    } 

    public static String decrypt(final String ivAndEncryptedMessageBase64, 
      final String symKeyHex) { 
     final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex); 

     final byte[] ivAndEncryptedMessage = DatatypeConverter 
       .parseBase64Binary(ivAndEncryptedMessageBase64); 
     try { 
      final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      final int blockSize = cipher.getBlockSize(); 

      // create the key 
      final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES"); 

      // retrieve random IV from start of the received message 
      final byte[] ivData = new byte[blockSize]; 
      System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize); 
      final IvParameterSpec iv = new IvParameterSpec(ivData); 

      // retrieve the encrypted message itself 
      final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length 
        - blockSize]; 
      System.arraycopy(ivAndEncryptedMessage, blockSize, 
        encryptedMessage, 0, encryptedMessage.length); 

      cipher.init(Cipher.DECRYPT_MODE, symKey, iv); 

      final byte[] encodedMessage = cipher.doFinal(encryptedMessage); 

      // concatenate IV and encrypted message 
      final String message = new String(encodedMessage, 
        Charset.forName("UTF-8")); 

      return message; 
     } catch (InvalidKeyException e) { 
      throw new IllegalArgumentException(
        "key argument does not contain a valid AES key"); 
     } catch (BadPaddingException e) { 
      // you'd better know about padding oracle attacks 
      return null; 
     } catch (GeneralSecurityException e) { 
      throw new IllegalStateException(
        "Unexpected exception during decryption", e); 
     } 
    } 

Uso:

String plain = "Zaphod's just zis guy, ya knöw?"; 
    String encrypted = encrypt(plain, "000102030405060708090A0B0C0D0E0F"); 
    System.out.println(encrypted); 
    String decrypted = decrypt(encrypted, "000102030405060708090A0B0C0D0E0F"); 
    if (decrypted != null && decrypted.equals(plain)) { 
     System.out.println("Hey! " + decrypted); 
    } else { 
     System.out.println("Bummer!"); 
    } 
+2

Para usar el cifrado de modo' "AES/GCM/NoPadding" 'en Java 7, reemplace el código para el 'IvParameterSpec' con [ 'GCMParameterSpec params = new GCMParameterSpec (blockSize * Byte.SIZE, ivData);'] (http://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/ GCMParameterSpec.html) y no se olvide de manejar la [ 'AEADBadTagException'] (http://docs.oracle.com/javase/7/docs/api/javax/crypto'/AEADBadTagException.html) para capturar las excepciones de integridad. Bouncy funcionará simplemente usando '" AES/GCM/NoPadding "' y lanzará una BadPaddingException, devolviendo null. –

+0

Ah, AEADBadTagException en realidad extiende BadPaddingException, por lo que el manejo de excepciones aún debería estar bien. –

+0

Tengo que decir que ... ¡fue una publicación maravillosa! :] – Kounavi

3

Aquí está la versión que utiliza la clase base 64 Util en lugar de DatatypeConverter

public static String encrypt(final String plainMessage, 
          final String symKeyHex) { 
    final byte[] symKeyData = Base64.decode(symKeyHex,Base64.DEFAULT); 

    final byte[] encodedMessage = plainMessage.getBytes(Charset 
      .forName("UTF-8")); 
    try { 
     final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     final int blockSize = cipher.getBlockSize(); 

     // create the key 
     final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES"); 

     // generate random IV using block size (possibly create a method for 
     // this) 
     final byte[] ivData = new byte[blockSize]; 
     final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG"); 
     rnd.nextBytes(ivData); 
     final IvParameterSpec iv = new IvParameterSpec(ivData); 

     cipher.init(Cipher.ENCRYPT_MODE, symKey, iv); 

     final byte[] encryptedMessage = cipher.doFinal(encodedMessage); 

     // concatenate IV and encrypted message 
     final byte[] ivAndEncryptedMessage = new byte[ivData.length 
       + encryptedMessage.length]; 
     System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize); 
     System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage, 
       blockSize, encryptedMessage.length); 

     final String ivAndEncryptedMessageBase64 = Base64.encodeToString(ivAndEncryptedMessage,Base64.DEFAULT); 

     return ivAndEncryptedMessageBase64; 
    } catch (InvalidKeyException e) { 
     throw new IllegalArgumentException(
       "key argument does not contain a valid AES key"); 
    } catch (GeneralSecurityException e) { 
     throw new IllegalStateException(
       "Unexpected exception during encryption", e); 
    } 
} 

public static String decrypt(final String ivAndEncryptedMessageBase64, 
          final String symKeyHex) { 
    final byte[] symKeyData = Base64.decode((symKeyHex),Base64.DEFAULT); 

    final byte[] ivAndEncryptedMessage = Base64.decode(ivAndEncryptedMessageBase64,Base64.DEFAULT); 
    try { 
     final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     final int blockSize = cipher.getBlockSize(); 

     // create the key 
     final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES"); 

     // retrieve random IV from start of the received message 
     final byte[] ivData = new byte[blockSize]; 
     System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize); 
     final IvParameterSpec iv = new IvParameterSpec(ivData); 

     // retrieve the encrypted message itself 
     final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length 
       - blockSize]; 
     System.arraycopy(ivAndEncryptedMessage, blockSize, 
       encryptedMessage, 0, encryptedMessage.length); 

     cipher.init(Cipher.DECRYPT_MODE, symKey, iv); 

     final byte[] encodedMessage = cipher.doFinal(encryptedMessage); 

     // concatenate IV and encrypted message 
     final String message = new String(encodedMessage, 
       Charset.forName("UTF-8")); 

     return message; 
    } catch (InvalidKeyException e) { 
     throw new IllegalArgumentException(
       "key argument does not contain a valid AES key"); 
    } catch (BadPaddingException e) { 
     // you'd better know about padding oracle attacks 
     return null; 
    } catch (GeneralSecurityException e) { 
     throw new IllegalStateException(
       "Unexpected exception during decryption", e); 
    } 
} 

Sólo una recordatorio para aquellos que reciben una excepción de Padding. Asegúrese de estar utilizando la longitud correcta de la clave. Un consejo: mira el post de Martín: su hexagonal es exactamente 32;) Eso no es una coincidencia :)

Cuestiones relacionadas