Gizli Anahtarı Dizeye ve Tersine Dönüştürme


102

Bir anahtar oluşturuyorum ve onu DB'de saklamam gerekiyor, bu yüzden onu bir String'e dönüştürüyorum, ancak anahtarı String'den geri almak için. Bunu başarmanın olası yolları nelerdir?

Benim kodum,

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

Anahtarı Telden nasıl geri alabilirim?


1
Anahtarların dizeye dönüştürülmesinin yalnızca kesinlikle gerekli olduğunda gerçekleştirilmesi gerektiğini unutmayın. StringAnahtar nesneler ve bayt dizileri temizlenebilirken Java'da örnekleri yok etmenin açık bir yöntemi yoktur . Bu, anahtarların bellekte daha uzun süre kullanılabileceği anlamına gelir. Bir (parola korumalı) KeyStore, tercihen çalışma zamanı sistemi / işletim sistemi tarafından desteklenen bir veya hatta donanımın kullanılması tercih edilmelidir.
Maarten Bodewes

Yanıtlar:


273

SecretKeyBir bayt dizisine ( byte[]) dönüştürebilir , ardından Base64 bunu bir String. Yeniden a'ya dönüştürmek için SecretKey, Base64 String'in kodunu çözün ve SecretKeySpecorijinalinizi yeniden oluşturmak için bunu a'da kullanın SecretKey.

Java 8 için

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());

Gizli Anahtar'a Dize:

// 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 ve öncesi için (Android dahil):

NOT I: Base64 kodlama / kod çözme kısmını atlayabilir ve sadece byte[]SQLite'de saklayabilirsiniz . Bununla birlikte, Base64 kodlama / kod çözme işlemini gerçekleştirmek pahalı bir işlem değildir ve dizeleri neredeyse tüm veritabanlarında sorunsuz saklayabilirsiniz.

NOT II: Daha önceki Java sürümleri, java.langveya java.utilpaketlerinden birinde Base64 içermez . Bununla birlikte, Apache Commons Codec , Bouncy Castle veya Guava'dan codec bileşenleri kullanmak mümkündür .

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

Gizli Anahtar'a Dize:

// 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" sınıfı için paket nedir
Takas L

@SwapL Android.util.Base64. Şu bağlantıya göz atın: developer.android.com/reference/android/util/Base64.html
Jabari

@ MaartenBodewes-owlstead Çoğu insan henüz Java 8 kullanmıyor. Bunu kesinlikle 8'de olmayan Android'de kullandım (ve muhtemelen bir süre olmayacak). Lütfen bir bağlam varsayımı üzerine birinin cevabını düzenlemeyin.
Jabari

@ MaartenBodewes-owlstead Yorumunuz benim ilk cümleyi tamamen görmezden geliyor: "Çoğu insan henüz Java 8 kullanmıyor". Cevabınız, Android ve Android olmayan Java kullanıcılarının büyük çoğunluğu için istisna hataları verecektir. Bununla birlikte, mevcut cevaba ek olarak bir pasaj ekleme öneriniz daha eksiksiz bir çözüm sağlayacaktır. Bilginize, cevabım konusunda "duygusal" değilim. Aslına bakarsanız, DES'i AES ile değiştirdim çünkü bu, güvenlik açısından kesin bir gelişme (ve orijinal sorudaki kodla daha uyumlu).
Jabari

@ MaartenBodewes-owlstead Yine ... Eklediğiniz şey "NoSuchAlgorithmException" istisna hataları oluşturacak. Lütfen Bakınız: docs.oracle.com/javase/7/docs/api/javax/crypto/… Düzelteceğim ...
Jabari

5

Hızlı başarısız olan bazı işlevler oluşturmanın ne kadar eğlenceli olduğunu göstermek için için aşağıdaki 3 işlevi yazdım.

Biri bir AES anahtarı oluşturur, biri onu kodlar ve diğeri de kodunu çözer. Bu üç yöntem Java 8 ile kullanılabilir (dahili sınıflara veya dış bağımlılıklara bağımlı olmadan):

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
Anahtar deposu bir donanım güvenlik modülündeyse (veya getEncoded()kullanılamayan başka bir konumdaysa) anahtarları depolamanın / almanın çalışmayabileceğini unutmayın.
Maarten Bodewes

1

Aslında Luis'in önerdiği benim için işe yaramadı. Başka bir yol bulmalıydım. Bana yardımcı olan buydu. Sana da yardımcı olabilir. Bağlantılar:

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

  2. Kodlayıcı bilgileri: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Kod çözücü bilgileri: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Kod parçacıkları: Kodlama için:

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

Kod çözmek için:

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

0

Kullanmak istemezsin .toString().

SecretKey'in kendisi Serializable'dan devraldığı java.security.Key'den miras aldığına dikkat edin. Yani buradaki anahtar (punto amaçlanmamıştır) anahtarı bir ByteArrayOutputStream'e serileştirmek, bayt [] dizisini almak ve db'de saklamaktır. Ters işlem, bayt [] dizisini db'den çıkarmak, bayt [] dizisinden bir ByteArrayInputStream oluşturmak ve SecretKey'in serisini kaldırmaktır ...

... veya daha basit, .getEncoded()java.security.Key'den (SecretKey'in üst arabirimi olan) miras alınan yöntemi kullanın . Bu yöntem, veritabanından depolayabileceğiniz veya geri alabileceğiniz Key / SecretKey kapalı kodlanmış bayt [] dizisini döndürür.

Bunların hepsi SecretKey uygulamanızın kodlamayı desteklediğini varsayar. Aksi takdirde getEncoded()null döndürür.

Düzenle:

Key / SecretKey javadocs'a bakmalısınız (google sayfasının hemen başında bulunur):

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

Veya CodeRanch'tan bu (aynı Google aramasında da bulundu):

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


Seri hale getirilebilir, bugünlerde alternatif bir yaklaşımınız olduğunda IMO'nun bir anti modelidir. Base64'ün kodladığı ve kodunu çözdüğü onaylanmış yanıt çok daha iyidir.
user2223059

0

String ve tersi SecretKeySpec dönüştürme: kullanabilirsiniz getEncoded()yöntemi SecretKeySpecverecek olan byteArraykullanabilirsiniz Bunun dışında, encodeToString()elde etmek için stringdeğeri SecretKeySpecolarakBase64 nesne.

Dönüştürme sırasında SecretKeySpechiç StringKullanım decode()içinde Base64verecek byteArray, bundan sizin için örneğini oluşturabilir SecretKeySpecolarak parametreleri içeren byteArrayŞu Verilerinizi çoğaltmak 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

bunu deneyin, Base64 olmadan çalışır (yalnızca JDK 1.8'de bulunur), bu kod önceki java sürümünde de çalışır :)

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;

} 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.