Conversão de chave secreta em string e vice-versa

102

Estou gerando uma chave e preciso armazená-la no banco de dados, então eu a converto em uma String, mas para recuperar a chave da String. Quais são as maneiras possíveis de fazer isso?

Meu código é,

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

Como posso obter a chave de volta da String?

Princeyesuraj
fonte
1
Observe que a conversão de chaves em string só deve ser realizada quando for absolutamente necessário. Não existe um método explícito para destruir Stringinstâncias em Java enquanto objetos-chave e matrizes de bytes podem ser limpos. Isso significa que as chaves podem ficar disponíveis na memória por um longo período de tempo. Usar um (protegido por senha) KeyStore, preferencialmente um apoiado pelo sistema de tempo de execução / SO ou mesmo hardware deve ser preferido.
Maarten Bodewes

Respostas:

272

Você pode converter o SecretKeyem uma matriz de bytes ( byte[]) e, em seguida, em Base64 codificá-lo em um String. Para converter de volta para a SecretKey, Base64 decodifique a String e use-a SecretKeySpecpara reconstruir o original SecretKey.

Para 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 para 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 e anterior (incluindo Android):

NOTA I: você pode pular a parte de codificação / decodificação Base64 e apenas armazená-la byte[]no SQLite. Dito isso, realizar a codificação / decodificação Base64 não é uma operação cara e você pode armazenar strings em quase qualquer banco de dados sem problemas.

NOTA II: As versões anteriores do Java não incluem um Base64 em um dos pacotes java.langou java.util. No entanto, é possível usar codecs do Apache Commons Codec , Bouncy Castle ou Guava .

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 para 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
fonte
@Jabari Qual é o pacote para a classe "Base64"
Troca L
@SwapL É android.util.Base64. Confira este link: developer.android.com/reference/android/util/Base64.html
Jabari
@ MaartenBodewes-owlstead A maioria das pessoas não está usando o Java 8 ainda. Eu usei isso no Android, que definitivamente não está no 8 ainda (e provavelmente não será por algum tempo). Não edite a resposta de alguém com base no contexto.
Jabari
@ MaartenBodewes-owlstead Seu comentário ignora completamente a minha primeira frase: "A maioria das pessoas ainda não está usando o Java 8". Sua resposta gerará erros de exceção para a grande maioria dos usuários de Java, Android e não Android. Dito isso, sua sugestão de adicionar um snippet além da resposta atual forneceria uma solução mais completa. Para sua informação, não sou "sentimental" em relação à minha resposta. Na verdade, troquei o DES pelo AES porque é uma melhoria definitiva em termos de segurança (além de estar mais de acordo com o código da questão original).
Jabari de
@ MaartenBodewes-owlstead Novamente ... o que você adicionou irá gerar erros de exceção "NoSuchAlgorithmException". Consulte: docs.oracle.com/javase/7/docs/api/javax/crypto/… Vou consertar ...
Jabari
5

Para mostrar como é divertido criar algumas funções que falham rapidamente , escrevi as três funções a seguir.

Um cria uma chave AES, outro a codifica e outro a decodifica de volta. Esses três métodos podem ser usados ​​com Java 8 (sem dependência de classes internas ou 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
fonte
2
Observe que o armazenamento / recuperação de chaves pode não funcionar se o armazenamento de chaves estiver em um módulo de segurança de hardware (ou em qualquer outro local onde getEncoded()não esteja disponível).
Maarten Bodewes
1

Na verdade, o que Luis propôs não funcionou para mim. Eu tive que descobrir outra maneira. Isso é o que me ajudou. Pode te ajudar também. Links:

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

  2. Informações do codificador: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Informações do decodificador: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Snippets de código: para codificação:

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
fonte
0

Você não quer usar .toString().

Observe que SecretKey herda de java.security.Key, que por sua vez herda de Serializable. Portanto, a chave aqui (sem trocadilhos) é serializar a chave em um ByteArrayOutputStream, obter o array byte [] e armazená-lo no banco de dados. O processo inverso seria retirar o array byte [] do banco de dados, criar um ByteArrayInputStream offf do array byte [] e desserializar o SecretKey dele ...

... ou ainda mais simples, basta usar o .getEncoded()método herdado de java.security.Key (que é uma interface pai de SecretKey). Este método retorna o array codificado byte [] de Key / SecretKey, que você pode armazenar ou recuperar do banco de dados.

Isso tudo presumindo que sua implementação de SecretKey suporta codificação. Caso contrário, getEncoded()retornará nulo.

editar:

Você deve olhar para os javadocs Key / SecretKey (disponíveis no início de uma página do Google):

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

Ou isto do CodeRanch (também encontrado com a mesma pesquisa do Google):

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

luis.espinal
fonte
Serializable é um anti-padrão atualmente IMO sempre que você tem uma abordagem alternativa. A resposta aprovada que codifica e decodifica base64 é muito melhor.
user2223059
0

Convertendo SecretKeySpec em String e vice-versa: você pode usar o getEncoded()método em SecretKeySpecque dará byteArray, a partir do qual você pode usarencodeToString() para obter stringo valor de SecretKeySpecno Base64objeto.

Ao converter SecretKeySpecpara String: use decode()in Base64dará byteArray, a partir disso você pode criar uma instância para SecretKeySpeccom os parâmetros como o byteArraypara reproduzir o seu 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
fonte
-1

tente fazer isso, ele funciona sem Base64 (que está incluído apenas no JDK 1.8), este código é executado também na versão anterior do 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
fonte