Перетворення секретного ключа в рядок і віце-верса


102

Я генерую ключ і потрібно зберігати його в БД, тому я перетворюю його в String, але щоб повернути ключ від String. Які можливі способи досягти цього?

Мій код:

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

Як я можу отримати ключ назад від String?


1
Зауважте, що перетворення ключів у рядок повинно здійснюватися лише тоді, коли це абсолютно необхідно. Немає явного методу знищення Stringекземплярів у Java, хоча ключові об'єкти та байтові масиви можуть бути очищені. Це означає, що ключі можуть залишатися доступними в пам'яті довший період часу. Використовуючи (захищений паролем) KeyStore, слід віддати перевагу такому, який підтримується системою / ОС або навіть апаратним забезпеченням.
Maarten Bodewes

Відповіді:


272

Ви можете перетворити SecretKeyмасив у байт ( byte[]), а потім кодувати Base64 в a String. Щоб перетворити назад в a SecretKey, Base64 декодує String і використовувати його SecretKeySpecдля відновлення оригіналу SecretKey.

Для Java 8

SecretKey до рядка:

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

Рядок для 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"); 

Для Java 7 і раніше (включаючи Android):

ПРИМІТКА I: ви можете пропустити частину кодування / декодування Base64 і просто зберегти byte[]в SQLite. Однак, виконання кодування / декодування Base64 не є дорогою операцією, і ви можете зберігати рядки майже в будь-якій БД без проблем.

ПРИМІТКА II: Раніші версії Java не містять Base64 в одному з пакетів java.langабо java.util. Однак можливо використовувати кодеки з Apache Commons Codec , Bouncy Castle або Guava .

SecretKey до рядка:

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

Рядок для 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 Що таке пакет для класу "Base64"
Swap L

@SwapL Це android.util.Base64. Перейдіть за цим посиланням: developer.android.com/reference/android/util/Base64.html
Джабари

@ MaartenBodewes-Owlstead Більшість людей ще не використовують Java 8. Я використовував це в Android, який, безумовно, ще не на 8 (і, мабуть, не буде деякий час). Будь ласка, не редагуйте чиюсь відповідь на припущення про контекст.
Джабари

@ MaartenBodewes-owlstead Ваш коментар повністю ігнорує перше моє речення: "Більшість людей ще не використовують Java 8". Ваша відповідь призведе до помилок винятків для переважної більшості користувачів Java, Android та інших користувачів. Однак, ваша пропозиція додати фрагмент на додаток до поточної відповіді забезпечить більш повне рішення. FYI, я не "сентиментальний" щодо своєї відповіді. Власне кажучи, я поміняв DES на AES, оскільки це певне поліпшення безпеки (а також більше відповідає коду в оригінальному запитанні).
Джабари

@ MaartenBodewes-owlstead Знову ... те, що ви додали, призведе до помилок виключення "NoSuchAlgorithmException". Будь ласка, дивіться: docs.oracle.com/javase/7/docs/api/javax/crypto/… я виправлю ...
Джабарі

5

Щоб показати, як весело створити деякі функції, які швидко провалюються, я написав наступні 3 функції.

Один створює ключ AES, один кодує його, а другий розшифровує його назад. Ці три методи можна використовувати з Java 8 (без залежності внутрішніх класів або зовнішніх залежностей):

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
Зауважте, що зберігання / завантаження ключів може не працювати, якщо зберігання ключів знаходиться в модулі захисту апаратного забезпечення (або в будь-якому іншому місці, де getEncoded()немає).
Maarten Bodewes

1

Насправді те, що запропонував Луїс, не працювало для мене. Мені довелося з'ясувати інший спосіб. Це мені допомогло. Можливо, вам теж допоможе. Посилання:

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

  2. Інформація про кодер: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Інформація про декодер: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Фрагменти коду: для кодування:

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

Для декодування:

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

0

Ви не хочете користуватися .toString().

Зауважте, що SecretKey успадковує від java.security.Key, який сам успадковує програму Serializable. Отже, ключовим тут (жодним каламбуром) не є серіалізація ключа у ByteArrayOutputStream, отримання масиву byte [] та збереження його у db. Зворотним процесом було б дістати байт [] масиву від db, створити ByteArrayInputStream offf байтового [] масиву та десеріалізувати SecretKey від нього ...

... або навіть простіше, просто використовуйте .getEncoded()метод, успадкований від java.security.Key (який є батьківським інтерфейсом SecretKey). Цей метод повертає кодований байтовий масив [] Off Key / SecretKey, який ви можете зберігати чи отримувати з бази даних.

Це все за умови, що ваша програма SecretKey підтримує кодування. Інакше getEncoded()повернеться null.

редагувати:

Ви повинні подивитись на javadocs Key / SecretKey (доступний на початку сторінки Google):

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

Або це з CodeRanch (також знайдений при тому ж пошуку в Google):

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


Серіалізалізація є антимоделією ІМО сьогодні, коли у вас був альтернативний підхід. Затверджена відповідь, яку base64 кодує та декодує, набагато краще.
користувач2223059

0

Перетворення SecretKeySpec в рядок і навпаки: ви можете використовувати getEncoded()метод в SecretKeySpecякому дасть byteArray, від який ви можете використовувати , encodeToString()щоб отримати stringзначення з SecretKeySpecв Base64об'єкті.

Під час переходу SecretKeySpecдо String: use decode()in Base64дасть byteArray, з цього ви можете створити екземпляр для SecretKeySpecз допомогою парамерів як byteArrayдля відтворення вашого 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

спробуйте це, це робота без Base64 (що включено лише в JDK 1.8), цей код працює також у попередній версії 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;

} 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.