Conversion de clé secrète en chaîne et vice-versa

102

Je génère une clé et je dois la stocker dans DB, donc je la convertis en une chaîne, mais pour récupérer la clé de la chaîne. Quels sont les moyens possibles pour y parvenir?

Mon code est,

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

Comment puis-je récupérer la clé de la chaîne?

Princeyesuraj
la source
1
Notez que la conversion des clés en chaîne ne doit être effectuée que lorsque cela est absolument nécessaire. Il n'y a pas de méthode explicite pour détruire des Stringinstances en Java tandis que les objets clés et les tableaux d'octets peuvent être effacés. Cela signifie que les clés peuvent rester disponibles dans la mémoire pendant une période de temps plus longue. L'utilisation d'un (protégé par mot de passe) KeyStore, de préférence un supporté par le système d'exécution / OS ou même le matériel doit être préféré.
Maarten Bodewes

Réponses:

272

Vous pouvez convertir le SecretKeyen un tableau d'octets ( byte[]), puis l'encoder en Base64 en un String. Pour reconvertir en a SecretKey, Base64 décode la chaîne et l'utiliser dans a SecretKeySpecpour reconstruire votre original SecretKey.

Pour Java 8

SecretKey à String:

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

Chaîne à 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"); 

Pour Java 7 et versions antérieures (y compris Android):

REMARQUE I: vous pouvez ignorer la partie encodage / décodage Base64 et simplement stocker le byte[]dans SQLite. Cela dit, effectuer un encodage / décodage Base64 n'est pas une opération coûteuse et vous pouvez stocker des chaînes dans presque n'importe quelle base de données sans problèmes.

REMARQUE II: les versions précédentes de Java n'incluent pas de Base64 dans l'un des packages java.langou java.util. Il est cependant possible d'utiliser des codecs d' Apache Commons Codec , Bouncy Castle ou Guava .

SecretKey à 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)}

Chaîne à 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
la source
@Jabari Quel est le package pour la classe "Base64"
Swap L
@SwapL C'est android.util.Base64. Consultez ce lien: developer.android.com/reference/android/util/Base64.html
Jabari
@ MaartenBodewes-owlstead La plupart des gens n'utilisent pas encore Java 8. Je l'ai utilisé dans Android, qui n'est certainement pas encore sur 8 (et ne le sera probablement pas avant un certain temps). Veuillez ne pas modifier la réponse de quelqu'un sur une supposition de contexte.
Jabari
@ MaartenBodewes-owlstead Votre commentaire ignore complètement ma première phrase: "La plupart des gens n'utilisent pas encore Java 8". Votre réponse générera des erreurs d'exception pour la grande majorité des utilisateurs Java, Android et non-Android. Cela dit, votre suggestion d'ajouter un extrait de code en plus de la réponse actuelle fournirait une solution plus complète. Pour info, je ne suis pas "sentimental" en ce qui concerne ma réponse. En fait, j'ai troqué DES pour AES parce que c'est une nette amélioration en termes de sécurité (en plus d'être plus conforme au code de la question d'origine).
Jabari
@ MaartenBodewes-owlstead Encore une fois ... ce que vous avez ajouté lancera des erreurs d'exception "NoSuchAlgorithmException". S'il vous plaît voir: docs.oracle.com/javase/7/docs/api/javax/crypto / ... Je vais réparer ...
Jabari
5

Pour montrer à quel point il est amusant de créer des fonctions qui échouent rapidement, j'ai écrit les 3 fonctions suivantes.

On crée une clé AES, on l'encode et on la décode. Ces trois méthodes peuvent être utilisées avec Java 8 (sans dépendance de classes internes ou de dépendances extérieures):

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
la source
2
Notez que le stockage / la récupération des clés peut ne pas fonctionner si le magasin de clés se trouve sur un module de sécurité matériel (ou tout autre emplacement getEncoded()non disponible).
Maarten Bodewes
1

En fait, ce que Luis a proposé n'a pas fonctionné pour moi. J'ai dû trouver un autre moyen. C'est ce qui m'a aidé. Cela pourrait vous aider aussi. Liens:

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

  2. Informations sur l'encodeur: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Informations sur le décodeur: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Extraits de code: pour l'encodage:

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

Pour le décodage:

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

Vous ne voulez pas utiliser .toString().

Notez que SecretKey hérite de java.security.Key, qui lui-même hérite de Serializable. Donc, la clé ici (sans jeu de mots) est de sérialiser la clé dans un ByteArrayOutputStream, d'obtenir le tableau byte [] et de le stocker dans la base de données. Le processus inverse consisterait à extraire le tableau d'octets [] de la base de données, à créer un ByteArrayInputStream du tableau d'octets [] et à désérialiser le SecretKey hors de celui-ci ...

... ou encore plus simple, il suffit d'utiliser la .getEncoded()méthode héritée de java.security.Key (qui est une interface parent de SecretKey). Cette méthode renvoie le tableau d'octets codés [] off Key / SecretKey, que vous pouvez stocker ou récupérer dans la base de données.

Tout cela suppose que votre implémentation SecretKey prend en charge le codage. Sinon, getEncoded()retournera null.

Éditer:

Vous devriez regarder les javadocs Key / SecretKey (disponibles juste au début d'une page google):

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

Ou ceci de CodeRanch (également trouvé avec la même recherche google):

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

luis.espinal
la source
Serializable est un anti-pattern de nos jours IMO chaque fois que vous aviez une approche alternative. La réponse approuvée qui l'encode et le décode en base64 est bien meilleure.
user2223059
0

Conversion SecretKeySpec à cordes et vice-versa: vous pouvez utiliser la getEncoded()méthode dans ce SecretKeySpecqui donnera byteArray, à partir que vous pouvez utiliser encodeToString()pour obtenir la stringvaleur de SecretKeySpecdans l' Base64objet.

Lors de la conversion SecretKeySpecen String: utiliser decode()dans Base64donnera byteArray, à partir de là, vous pouvez créer une instance pour SecretKeySpecavec les paramètres comme byteArraypour reproduire votre 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
la source
-1

essayez ceci, cela fonctionne sans Base64 (qui n'est inclus que dans JDK 1.8), ce code s'exécute également dans la version java précédente :)

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
la source