Conversione della chiave segreta in una stringa e viceversa


102

Sto generando una chiave e devo memorizzarla nel DB, quindi la converto in una stringa, ma per recuperare la chiave dalla stringa. Quali sono i modi possibili per ottenere ciò?

Il mio codice è,

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

Come posso riavere la chiave dalla stringa?


1
Si noti che la conversione delle chiavi in ​​stringa deve essere eseguita solo quando assolutamente necessario. Non esiste un metodo esplicito per distruggere le Stringistanze in Java mentre gli oggetti chiave e gli array di byte possono essere cancellati. Ciò significa che le chiavi potrebbero rimanere disponibili nella memoria per un periodo di tempo più lungo. L'uso di un (protetto da password) KeyStore, preferibilmente uno supportato dal sistema di runtime / OS o anche dall'hardware dovrebbe essere preferito.
Maarten Bodewes

Risposte:


272

Puoi convertire il SecretKeyin un array di byte ( byte[]), quindi codificarlo in Base64 in un file String. Per riconvertire in a SecretKey, Base64 decodifica la stringa e usala in a SecretKeySpecper ricostruire l'originale SecretKey.

Per Java 8

SecretKey alla stringa:

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

String to 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"); 

Per Java 7 e versioni precedenti (incluso Android):

NOTA I: puoi saltare la parte di codifica / decodifica Base64 e memorizzare semplicemente il file byte[]in SQLite. Detto questo, eseguire la codifica / decodifica Base64 non è un'operazione costosa e puoi memorizzare le stringhe in quasi tutti i DB senza problemi.

NOTA II: le versioni precedenti di Java non includono un Base64 in uno dei pacchetti java.lango java.util. È comunque possibile utilizzare codec da Apache Commons Codec , Bouncy Castle o Guava .

SecretKey alla stringa:

// 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)}

String to 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 Qual è il pacchetto per la classe "Base64"
Swap L

@SwapL È android.util.Base64. Dai un'occhiata a questo link: developer.android.com/reference/android/util/Base64.html
Jabari

@ MaartenBodewes-owlstead La maggior parte delle persone non utilizza ancora Java 8. L'ho usato su Android, che sicuramente non è ancora su 8 (e probabilmente non lo sarà per un po 'di tempo). Per favore, non modificare la risposta di qualcuno sulla base del contesto.
Jabari

@ MaartenBodewes-owlstead Il tuo commento ignora completamente la mia prima frase: "La maggior parte delle persone non usa ancora Java 8". La tua risposta genererà errori di eccezione per la stragrande maggioranza degli utenti Java, Android e non Android. Detto questo, il tuo suggerimento di aggiungere uno snippet oltre alla risposta attuale fornirebbe una soluzione più completa. Cordiali saluti, non sono "sentimentale" per quanto riguarda la mia risposta. È un dato di fatto, ho scambiato DES con AES perché è un netto miglioramento della sicurezza (oltre ad essere più in linea con il codice nella domanda originale).
Jabari

@ MaartenBodewes-owlstead Di nuovo ... ciò che hai aggiunto genererà errori di eccezione "NoSuchAlgorithmException". Si prega di consultare: docs.oracle.com/javase/7/docs/api/javax/crypto/… Sistemerò ...
Jabari

5

Per mostrare quanto è divertente creare alcune funzioni che non funzionano velocemente, ho scritto le seguenti 3 funzioni.

Uno crea una chiave AES, una la codifica e l'altra la decodifica. Questi tre metodi possono essere utilizzati con Java 8 (senza dipendenza da classi interne o dipendenze esterne):

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;
}

2
Si noti che l'archiviazione / recupero delle chiavi potrebbe non funzionare se l'archivio chiavi si trova su un modulo di protezione hardware (o qualsiasi altra posizione in cui getEncoded()non è disponibile).
Maarten Bodewes

1

In realtà ciò che Luis ha proposto non ha funzionato per me. Ho dovuto trovare un altro modo. Questo è ciò che mi ha aiutato. Potrebbe aiutare anche te. link:

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

  2. Informazioni sul codificatore: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Informazioni sul decoder: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Snippet di codice: per la codifica:

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

Per la decodifica:

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

0

Non vuoi usare .toString().

Si noti che SecretKey eredita da java.security.Key, che a sua volta eredita da Serializable. Quindi la chiave qui (nessun gioco di parole) è serializzare la chiave in un ByteArrayOutputStream, ottenere l'array byte [] e memorizzarlo nel db. Il processo inverso sarebbe quello di estrarre l'array byte [] dal db, creare un ByteArrayInputStream dell'array byte [] e deserializzare SecretKey da esso ...

... o anche più semplice, usa semplicemente il .getEncoded()metodo ereditato da java.security.Key (che è un'interfaccia genitore di SecretKey). Questo metodo restituisce l'array di byte [] codificato fuori da Key / SecretKey, che è possibile memorizzare o recuperare dal database.

Tutto questo presuppone che l'implementazione di SecretKey supporti la codifica. In caso contrario, getEncoded()restituirà null.

modificare:

Dovresti guardare i javadoc Key / SecretKey (disponibili proprio all'inizio di una pagina Google):

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

O questo da CodeRanch (trovato anche con la stessa ricerca su Google):

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


Serializable è un anti pattern in questi giorni IMO ogni volta che hai un approccio alternativo. La risposta approvata che base64 codifica e decodifica è di gran lunga migliore.
user2223059

0

Conversione di SecretKeySpec in String e viceversa: puoi usare il getEncoded()metodo in SecretKeySpeccui darà byteArray, da quello che puoi usare encodeToString()per ottenere il stringvalore di SecretKeySpecin Base64object.

Durante la conversione SecretKeySpecin String: use decode()in Base64darà byteArray, da ciò puoi creare istanze per SecretKeySpeccon i parametri come byteArrayper riprodurre il tuo file 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");

-1

prova questo, funziona senza Base64 (che è incluso solo in JDK 1.8), questo codice funziona anche nella precedente versione 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;

} 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.