Chuyển đổi Khóa bí mật thành Chuỗi và Phó bản


102

Tôi đang tạo một khóa và cần lưu trữ nó trong DB, vì vậy tôi chuyển nó thành một Chuỗi, nhưng để lấy lại khóa từ Chuỗi. Những cách khả thi để đạt được điều này là gì?

Mã của tôi là,

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

Làm cách nào để lấy lại chìa khóa từ Chuỗi?


1
Lưu ý rằng việc chuyển đổi khóa thành chuỗi chỉ nên được thực hiện khi thực sự cần thiết. Không có phương pháp rõ ràng nào để hủy các Stringthể hiện trong Java trong khi các đối tượng chính và mảng byte có thể bị xóa. Điều này có nghĩa là các khóa có thể ở trong bộ nhớ trong một khoảng thời gian dài hơn. KeyStoreNên ưu tiên sử dụng (được bảo vệ bằng mật khẩu) , tốt nhất là được hỗ trợ bởi hệ thống thời gian chạy / HĐH hoặc thậm chí là phần cứng.
Maarten Bodewes

Câu trả lời:


272

Bạn có thể chuyển đổi SecretKeythành một mảng byte ( byte[]), sau đó mã hóa Base64 thành a String. Để chuyển đổi lại thành a SecretKey, Base64 giải mã chuỗi và sử dụng nó trong a SecretKeySpecđể xây dựng lại bản gốc của bạn SecretKey.

Đối với 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());

Chuỗi đến 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"); 

Đối với Java 7 trở về trước (bao gồm cả Android):

LƯU Ý I: bạn có thể bỏ qua phần mã hóa / giải mã Base64 và chỉ cần lưu trữ byte[]trong SQLite. Điều đó nói rằng, thực hiện mã hóa / giải mã Base64 không phải là một hoạt động tốn kém và bạn có thể lưu trữ các chuỗi trong hầu hết mọi DB mà không gặp vấn đề gì.

LƯU Ý II: Các phiên bản Java trước không bao gồm Base64 trong một trong các gói java.langhoặc java.util. Tuy nhiên, có thể sử dụng codec từ Apache Commons Codec , Bouncy Castle hoặc 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)}

Chuỗi đến 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 Gói cho lớp "Base64" là gì
Swap L

@SwapL Đó là android.util.Base64. Kiểm tra liên kết này: developer.android.com/reference/android/util/Base64.html
Jabari

@ MaartenBodewes-owlstead Hầu hết mọi người chưa sử dụng Java 8. Tôi đã sử dụng điều này trong Android, chắc chắn chưa có trên 8 (và có lẽ sẽ không có trong một thời gian). Vui lòng không chỉnh sửa câu trả lời của ai đó dựa trên ngữ cảnh giả định.
Jabari

@ MaartenBodewes-owlstead Nhận xét của bạn hoàn toàn bỏ qua câu đầu tiên của tôi: "Hầu hết mọi người vẫn chưa sử dụng Java 8". Câu trả lời của bạn sẽ đưa ra các lỗi ngoại lệ cho đại đa số người dùng Java, Android và không phải Android. Điều đó nói rằng, đề xuất của bạn về việc thêm một đoạn mã ngoài câu trả lời hiện tại sẽ cung cấp một giải pháp hoàn chỉnh hơn. FYI, tôi không "đa cảm" về câu trả lời của mình. Trên thực tế, tôi đã đổi DES cho AES vì đó là một cải tiến rõ ràng về bảo mật khôn ngoan (cũng như phù hợp hơn với mã trong câu hỏi ban đầu).
Jabari

@ MaartenBodewes-owlstead Một lần nữa ... những gì bạn đã thêm sẽ tạo ra lỗi ngoại lệ "NoSuchAlgorithmException". Vui lòng xem: docs.oracle.com/javase/7/docs/api/javax/crypto/… Tôi sẽ sửa ...
Jabari 21/12/14

5

Để cho thấy thú vị như thế nào khi tạo một số hàm không nhanh, tôi đã viết 3 hàm sau.

Một người tạo một khóa AES, một người mã hóa nó và một người giải mã nó trở lại. Ba phương thức này có thể được sử dụng với Java 8 (không phụ thuộc vào các lớp bên trong hoặc phụ thuộc bên ngoài):

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
Lưu ý rằng việc lưu trữ / truy xuất khóa có thể không hoạt động nếu kho khóa nằm trên mô-đun bảo mật phần cứng (hoặc bất kỳ vị trí nào khác getEncoded()không khả dụng).
Maarten Bodewes

1

Thực ra những gì Luis đề xuất không có tác dụng với tôi. Tôi đã phải tìm ra một cách khác. Đây là những gì đã giúp tôi. Có thể giúp bạn quá. Liên kết:

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

  2. Thông tin bộ mã hóa: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

  3. Thông tin bộ giải mã: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html

Đoạn mã: Để mã hóa:

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

Để giải mã:

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

0

Bạn không muốn sử dụng .toString().

Lưu ý rằng SecretKey kế thừa từ java.security.Key, mà bản thân nó kế thừa từ Serializable. Vì vậy, chìa khóa ở đây (không có ý định chơi chữ) là tuần tự hóa khóa thành một ByteArrayOutputStream, lấy mảng byte [] và lưu trữ nó vào db. Quá trình ngược lại sẽ là lấy mảng byte [] ra khỏi db, tạo một ByteArrayInputStream của mảng byte [] và giải mã SecretKey khỏi nó ...

... hoặc đơn giản hơn, chỉ cần sử dụng .getEncoded()phương thức được kế thừa từ java.security.Key (là giao diện mẹ của SecretKey). Phương thức này trả về mảng byte [] được mã hóa khỏi Key / SecretKey, mà bạn có thể lưu trữ hoặc truy xuất từ ​​cơ sở dữ liệu.

Tất cả điều này là giả sử việc triển khai SecretKey của bạn hỗ trợ mã hóa. Nếu không, getEncoded()sẽ trả về null.

biên tập:

Bạn nên xem javadocs Key / SecretKey (có sẵn ngay ở đầu trang google):

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

Hoặc điều này từ CodeRanch (cũng được tìm thấy với cùng một tìm kiếm google):

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


Serializable là một mô hình chống lại IMO ngày nay bất cứ khi nào bạn có một cách tiếp cận thay thế. Câu trả lời được chấp thuận mà base64 mã hóa và giải mã nó tốt hơn nhiều.
user2223059 29/09/09

0

Chuyển đổi SecretKeySpec thành String và ngược lại: bạn có thể sử dụng getEncoded()phương thức SecretKeySpecsẽ cung cấp byteArray, từ đó bạn có thể sử dụng encodeToString()để nhận stringgiá trị SecretKeySpectrongBase64 đối tượng.

Trong khi chuyển đổi SecretKeySpecthành String: use decode()in Base64sẽ cho byteArray, từ đó bạn có thể tạo phiên bản cho SecretKeySpecvới các tham số byteArrayđể tái tạo của bạn 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

hãy thử cái này, nó hoạt động mà không cần Base64 (chỉ có trong JDK 1.8), mã này cũng chạy trong phiên bản java trước đó :)

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;

} 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.