Konvertieren eines geheimen Schlüssels in einen String und umgekehrt

102

Ich generiere einen Schlüssel und muss ihn in der Datenbank speichern, also konvertiere ich ihn in einen String, aber um den Schlüssel aus dem String zurückzubekommen. Was sind die möglichen Wege, um dies zu erreichen?

Mein Code ist,

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

Wie kann ich den Schlüssel vom String zurückbekommen?

Princeyesuraj
quelle
1
Beachten Sie, dass die Konvertierung von Schlüsseln in Zeichenfolgen nur durchgeführt werden sollte, wenn dies unbedingt erforderlich ist. Es gibt keine explizite Methode zum Zerstören von StringInstanzen in Java, während Schlüsselobjekte und Bytearrays möglicherweise gelöscht werden. Dies bedeutet, dass Schlüssel für einen längeren Zeitraum im Speicher verfügbar bleiben können. Die Verwendung eines (kennwortgeschützten) KeyStore, vorzugsweise eines, das vom Laufzeitsystem / Betriebssystem oder sogar von der Hardware unterstützt wird, sollte bevorzugt werden.
Maarten Bodewes

Antworten:

272

Sie können das SecretKeyin ein Byte-Array ( byte[]) konvertieren und Base64 das dann in ein codieren String. Um wieder in a zu konvertieren SecretKey, dekodiert Base64 den String und verwendet ihn in a SecretKeySpec, um Ihr Original neu zu erstellen SecretKey.

Für Java 8

SecretKey to String:

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

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

Für Java 7 und früher (einschließlich Android):

HINWEIS I: Sie können den Base64-Codierungs- / Decodierungsteil überspringen und den byte[]in SQLite speichern . Das Durchführen der Base64-Codierung / -Decodierung ist jedoch kein teurer Vorgang, und Sie können Zeichenfolgen problemlos in nahezu jeder Datenbank speichern.

HINWEIS II: Frühere Java-Versionen enthalten kein Base64 in einem der Pakete java.langoder java.util. Es ist jedoch möglich, Codecs aus Apache Commons Codec , Bouncy Castle oder Guava zu verwenden .

SecretKey to String:

// 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 zu 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
quelle
@Jabari Was ist das Paket für "Base64" Klasse
Swap L
@ SwapL Es ist android.util.Base64. Schauen Sie sich diesen Link an: developer.android.com/reference/android/util/Base64.html
Jabari
@ MaartenBodewes-owlstead Die meisten Leute verwenden Java 8 noch nicht. Ich habe dies in Android verwendet, das definitiv noch nicht auf 8 ist (und wahrscheinlich für einige Zeit nicht sein wird). Bitte bearbeiten Sie die Antwort einer Person nicht unter der Annahme eines Kontexts.
Jabari
@ MaartenBodewes-owlstead Dein Kommentar ignoriert meinen ersten Satz völlig: "Die meisten Leute verwenden Java 8 noch nicht". Ihre Antwort wird Ausnahmefehler für die überwiegende Mehrheit der Java-Benutzer, Android und Nicht-Android gleichermaßen, auslösen. Ihr Vorschlag, zusätzlich zur aktuellen Antwort ein Snippet hinzuzufügen, bietet jedoch eine vollständigere Lösung. Zu Ihrer Information, ich bin in Bezug auf meine Antwort nicht "sentimental". Tatsächlich habe ich DES gegen AES getauscht, weil dies eine deutliche Verbesserung der Sicherheit darstellt (und mehr mit dem Code in der ursprünglichen Frage übereinstimmt).
Jabari
@ MaartenBodewes-owlstead Nochmals ... was Sie hinzugefügt haben, löst Ausnahmefehler "NoSuchAlgorithmException" aus. Bitte sehen Sie: docs.oracle.com/javase/7/docs/api/javax/crypto/… Ich werde beheben ...
Jabari
5

Um zu zeigen, wie viel Spaß es macht, einige Funktionen zu erstellen, die schnell ausfallen, habe ich die folgenden 3 Funktionen geschrieben.

Man erstellt einen AES-Schlüssel, man codiert ihn und man decodiert ihn zurück. Diese drei Methoden können mit Java 8 verwendet werden (ohne Abhängigkeit von internen Klassen oder externen Abhängigkeiten):

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
quelle
2
Beachten Sie, dass das Speichern / Abrufen von Schlüsseln möglicherweise nicht funktioniert, wenn sich der Schlüsselspeicher auf einem Hardware-Sicherheitsmodul befindet (oder an einem anderen Ort, an dem er getEncoded()nicht verfügbar ist).
Maarten Bodewes
1

Eigentlich hat das, was Luis vorgeschlagen hat, bei mir nicht funktioniert. Ich musste einen anderen Weg finden. Das hat mir geholfen. Könnte dir auch helfen. Links:

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

  2. Encoder-Informationen: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Decoder-Informationen: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Codefragmente: Zum Codieren:

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

Zum Entschlüsseln:

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

Du willst nicht benutzen .toString().

Beachten Sie, dass SecretKey von java.security.Key erbt, das selbst von Serializable erbt. Der Schlüssel hier (kein Wortspiel beabsichtigt) besteht darin, den Schlüssel in einen ByteArrayOutputStream zu serialisieren, das Array byte [] abzurufen und in der Datenbank zu speichern. Der umgekehrte Prozess wäre, das Byte [] -Array aus der Datenbank zu entfernen, einen ByteArrayInputStream aus dem Byte [] -Array zu erstellen und den SecretKey daraus zu deserialisieren ...

... oder noch einfacher, verwenden Sie einfach die .getEncoded()von java.security.Key (einer übergeordneten Schnittstelle von SecretKey) geerbte Methode. Diese Methode gibt das codierte Byte [] -Array von Key / SecretKey zurück, das Sie speichern oder aus der Datenbank abrufen können.

Dies alles setzt voraus, dass Ihre SecretKey-Implementierung die Codierung unterstützt. Andernfalls getEncoded()wird null zurückgegeben.

bearbeiten:

Sie sollten sich die Key / SecretKey-Javadocs ansehen (verfügbar direkt am Anfang einer Google-Seite):

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

Oder dies von CodeRanch (auch mit der gleichen Google-Suche gefunden):

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

luis.espinal
quelle
Serializable ist heutzutage ein Anti-Pattern IMO, wenn Sie einen alternativen Ansatz hatten. Die genehmigte Antwort, die base64 codiert und decodiert, ist weitaus besser.
user2223059
0

Konvertieren von SecretKeySpec in String und umgekehrt: Sie können eine getEncoded()Methode verwenden, mit SecretKeySpecder angegeben wird byteArray, aus der Sie encodeToString()den stringWert von SecretKeySpecin Base64object abrufen können .

Während der Konvertierung SecretKeySpecnach String: use decode()in Base64wird geben byteArray, daraus können Sie eine Instanz SecretKeySpecmit den Parametern erstellen byteArray, um Ihre zu reproduzieren 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
quelle
-1

Versuchen Sie dies, es funktioniert ohne Base64 (das nur in JDK 1.8 enthalten ist), dieser Code wird auch in der vorherigen Java-Version ausgeführt :)

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
quelle