Conversión de la clave secreta en una cadena y viceversa

102

Estoy generando una clave y necesito almacenarla en DB, así que la convierto en una Cadena, pero para recuperar la clave de la Cadena. ¿Cuáles son las posibles formas de lograr esto?

Mi código es

SecretKey key = KeyGenerator.getInstance("AES").generateKey();
String stringKey=key.toString();
System.out.println(stringKey);

¿Cómo puedo recuperar la llave del String?

Princeyesuraj
fuente
1
Tenga en cuenta que la conversión de claves a cadenas solo debe realizarse cuando sea absolutamente necesario. No existe un método explícito para destruir Stringinstancias en Java, mientras que los objetos clave y las matrices de bytes pueden borrarse. Esto significa que las claves podrían permanecer disponibles en la memoria durante un período de tiempo más largo. KeyStoreSe debe preferir el uso de un (protegido por contraseña) , preferiblemente uno respaldado por el sistema de ejecución / SO o incluso el hardware.
Maarten Bodewes

Respuestas:

272

Puede convertir el SecretKeyen una matriz de bytes ( byte[]), luego en Base64 codificarlo en un String. Para volver a convertir a a SecretKey, Base64 decodifica la cadena y úsala en a SecretKeySpecpara reconstruir tu original SecretKey.

Para Java 8

Clave secreta a cadena:

// create new key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
// get base64 encoded version of the key
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());

Cadena a SecretKey:

// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); 

Para Java 7 y versiones anteriores (incluido Android):

NOTA I: puede omitir la parte de codificación / decodificación Base64 y simplemente almacenar el byte[]archivo en SQLite. Dicho esto, realizar la codificación / decodificación Base64 no es una operación costosa y puede almacenar cadenas en casi cualquier base de datos sin problemas.

NOTA II: Las versiones anteriores de Java no incluyen Base64 en uno de los paquetes java.lango java.util. Sin embargo, es posible utilizar códecs de Apache Commons Codec , Bouncy Castle o Guava .

Clave secreta a cadena:

// CREATE NEW KEY
// GET ENCODED VERSION OF KEY (THIS CAN BE STORED IN A DB)

    SecretKey secretKey;
    String stringKey;

    try {secretKey = KeyGenerator.getInstance("AES").generateKey();}
    catch (NoSuchAlgorithmException e) {/* LOG YOUR EXCEPTION */}

    if (secretKey != null) {stringKey = Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT)}

Cadena a SecretKey:

// DECODE YOUR BASE64 STRING
// REBUILD KEY USING SecretKeySpec

    byte[] encodedKey     = Base64.decode(stringKey, Base64.DEFAULT);
    SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
Jabari
fuente
@Jabari ¿Cuál es el paquete para la clase "Base64"
Swap L
@SwapL Es android.util.Base64. Consulte este enlace: developer.android.com/reference/android/util/Base64.html
Jabari
@ MaartenBodewes-owlstead La mayoría de la gente todavía no usa Java 8. Usé esto en Android, que definitivamente no está en 8 todavía (y probablemente no lo estará por algún tiempo). Por favor, no edite la respuesta de alguien asumiendo un contexto.
Jabari
@ MaartenBodewes-owlstead Su comentario ignora por completo la primera oración mía: "La mayoría de la gente no está usando Java 8 todavía". Su respuesta arrojará errores de excepción para la gran mayoría de los usuarios de Java, Android y no Android por igual. Dicho esto, su sugerencia de agregar un fragmento además de la respuesta actual proporcionaría una solución más completa. FYI, no soy "sentimental" en lo que respecta a mi respuesta. De hecho, cambié DES por AES porque es una mejora definitiva en cuanto a seguridad (además de estar más en línea con el código de la pregunta original).
Jabari
@ MaartenBodewes-owlstead Nuevamente ... lo que agregó arrojará errores de excepción "NoSuchAlgorithmException". Consulte: docs.oracle.com/javase/7/docs/api/javax/crypto/ ... Lo arreglaré ...
Jabari
5

Para mostrar lo divertido que es crear algunas funciones que fallan rápido , he escrito las siguientes 3 funciones.

Uno crea una clave AES, uno la codifica y otra la decodifica. Estos tres métodos se pueden usar con Java 8 (sin dependencia de clases internas o dependencias externas):

public static SecretKey generateAESKey(int keysize)
        throws InvalidParameterException {
    try {
        if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
            // this may be an issue if unlimited crypto is not installed
            throw new InvalidParameterException("Key size of " + keysize
                    + " not supported in this runtime");
        }

        final KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(keysize);
        return keyGen.generateKey();
    } catch (final NoSuchAlgorithmException e) {
        // AES functionality is a requirement for any Java SE runtime
        throw new IllegalStateException(
                "AES should always be present in a Java SE runtime", e);
    }
}

public static SecretKey decodeBase64ToAESKey(final String encodedKey)
        throws IllegalArgumentException {
    try {
        // throws IllegalArgumentException - if src is not in valid Base64
        // scheme
        final byte[] keyData = Base64.getDecoder().decode(encodedKey);
        final int keysize = keyData.length * Byte.SIZE;

        // this should be checked by a SecretKeyFactory, but that doesn't exist for AES
        switch (keysize) {
        case 128:
        case 192:
        case 256:
            break;
        default:
            throw new IllegalArgumentException("Invalid key size for AES: " + keysize);
        }

        if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
            // this may be an issue if unlimited crypto is not installed
            throw new IllegalArgumentException("Key size of " + keysize
                    + " not supported in this runtime");
        }

        // throws IllegalArgumentException - if key is empty
        final SecretKeySpec aesKey = new SecretKeySpec(keyData, "AES");
        return aesKey;
    } catch (final NoSuchAlgorithmException e) {
        // AES functionality is a requirement for any Java SE runtime
        throw new IllegalStateException(
                "AES should always be present in a Java SE runtime", e);
    }
}

public static String encodeAESKeyToBase64(final SecretKey aesKey)
        throws IllegalArgumentException {
    if (!aesKey.getAlgorithm().equalsIgnoreCase("AES")) {
        throw new IllegalArgumentException("Not an AES key");
    }

    final byte[] keyData = aesKey.getEncoded();
    final String encodedKey = Base64.getEncoder().encodeToString(keyData);
    return encodedKey;
}
Maarten Bodewes
fuente
2
Tenga en cuenta que es posible que almacenar / recuperar claves no funcione si el almacén de claves está en un módulo de seguridad de hardware (o en cualquier otra ubicación donde getEncoded()no esté disponible).
Maarten Bodewes
1

En realidad lo que me propuso Luis no me funcionó. Tuve que encontrar otra forma. Eso es lo que me ayudó. Podría ayudarte también. Enlaces:

  1. * .getEncoded (): https://docs.oracle.com/javase/7/docs/api/java/security/Key.html

  2. Información del codificador: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Información del decodificador: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Fragmentos de código: para codificar:

String temp = new String(Base64.getEncoder().encode(key.getEncoded()));

Para decodificar:

byte[] encodedKey = Base64.getDecoder().decode(temp);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "DES");
Revanth Kumar
fuente
0

No quieres usar .toString().

Tenga en cuenta que SecretKey hereda de java.security.Key, que a su vez hereda de Serializable. Entonces, la clave aquí (sin juego de palabras) es serializar la clave en un ByteArrayOutputStream, obtener la matriz de bytes [] y almacenarla en la base de datos. El proceso inverso sería sacar la matriz de bytes [] de la base de datos, crear un ByteArrayInputStream de la matriz de bytes [] y deserializar la SecretKey de ella ...

... o incluso más simple, simplemente use el .getEncoded()método heredado de java.security.Key (que es una interfaz principal de SecretKey). Este método devuelve la matriz de bytes codificada [] de Key / SecretKey, que puede almacenar o recuperar de la base de datos.

Todo esto es asumiendo que su implementación de SecretKey admite la codificación. De otra manera,getEncoded() devolverá nulo.

editar:

Debe mirar los javadocs Key / SecretKey (disponibles justo al comienzo de una página de Google):

http://download.oracle.com/javase/6/docs/api/java/security/Key.html

O esto de CodeRanch (también encontrado con la misma búsqueda de Google):

http://www.coderanch.com/t/429127/java/java/Convertion-between-SecretKey-String-or

luis.espinal
fuente
Serializable es un patrón anti en estos días, en mi opinión, siempre que tuviera un enfoque alternativo. La respuesta aprobada que codifica y decodifica base64 es mucho mejor.
user2223059
0

Conversión de SecretKeySpec a String y viceversa: puede usar el getEncoded()método en el SecretKeySpecque dará byteArray, a partir del cual puede usar encodeToString()para obtener el stringvalor de SecretKeySpecenBase64 objeto.

Al convertir SecretKeySpeca String: use decode()in Base64dará byteArray, a partir de eso puede crear una instancia para SecretKeySpeccon los parámetros como byteArraypara reproducir su SecretKeySpec.

String mAesKey_string;
SecretKeySpec mAesKey= new SecretKeySpec(secretKey.getEncoded(), "AES");

//SecretKeySpec to String 
    byte[] byteaes=mAesKey.getEncoded();
    mAesKey_string=Base64.encodeToString(byteaes,Base64.NO_WRAP);

//String to SecretKeySpec
    byte[] aesByte = Base64.decode(mAesKey_string, Base64.NO_WRAP);
    mAesKey= new SecretKeySpec(aesByte, "AES");
anand krish
fuente
-1

intente esto, funciona sin Base64 (que se incluye solo en JDK 1.8), este código se ejecuta también en la versión anterior de Java :)

private static String SK = "Secret Key in HEX";


//  To Encrupt

public static String encrypt( String Message ) throws Exception{

    byte[] KeyByte = hexStringToByteArray( SK);
    SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");

    Cipher c = Cipher.getInstance("DES","SunJCE");
    c.init(1, k);
    byte mes_encrypted[] = cipher.doFinal(Message.getBytes());

    String MessageEncrypted = byteArrayToHexString(mes_encrypted);
    return MessageEncrypted;
}

//  To Decrypt

public static String decrypt( String MessageEncrypted )throws Exception{

    byte[] KeyByte = hexStringToByteArray( SK );
    SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");

    Cipher dcr =  Cipher.getInstance("DES","SunJCE");
    dc.init(Cipher.DECRYPT_MODE, k);
    byte[] MesByte  = hexStringToByteArray( MessageEncrypted );
    byte mes_decrypted[] = dcipher.doFinal( MesByte );
    String MessageDecrypeted = new String(mes_decrypted);

    return MessageDecrypeted;
}

public static String byteArrayToHexString(byte bytes[]){

    StringBuffer hexDump = new StringBuffer();
    for(int i = 0; i < bytes.length; i++){
    if(bytes[i] < 0)
    {   
        hexDump.append(getDoubleHexValue(Integer.toHexString(256 - Math.abs(bytes[i]))).toUpperCase());
    }else
    {
        hexDump.append(getDoubleHexValue(Integer.toHexString(bytes[i])).toUpperCase());
    }
    return hexDump.toString();

}



public static byte[] hexStringToByteArray(String s) {

    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2)
    {   
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
    }
    return data;

} 
daniel
fuente