การแปลงรหัสลับเป็นสตริงและในทางกลับกัน


102

ฉันกำลังสร้างคีย์และต้องการเก็บไว้ใน DB ดังนั้นฉันจึงแปลงเป็น String แต่เพื่อเรียกคืนคีย์จาก String วิธีที่เป็นไปได้ในการทำสิ่งนี้ให้สำเร็จคืออะไร?

รหัสของฉันคือ

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

ฉันจะเอาคีย์คืนจาก String ได้อย่างไร?


1
โปรดทราบว่าการแปลงคีย์เป็นสตริงควรดำเนินการเมื่อจำเป็นจริงๆเท่านั้น ไม่มีวิธีการที่ชัดเจนในการทำลายStringอินสแตนซ์ใน Java ในขณะที่อ็อบเจ็กต์หลักและไบต์อาเรย์อาจถูกล้าง ซึ่งหมายความว่าคีย์สามารถใช้งานได้ภายในหน่วยความจำเป็นระยะเวลานานขึ้น ควรใช้ (ป้องกันด้วยรหัสผ่าน) KeyStoreโดยควรใช้ระบบรันไทม์ / OS หรือแม้แต่ฮาร์ดแวร์
Maarten Bodewes

คำตอบ:


272

คุณสามารถแปลงSecretKeyเป็นอาร์เรย์ไบต์ ( byte[]) จากนั้น Base64 เข้ารหัสเป็นStringไฟล์. ในการแปลงกลับเป็น a SecretKeyให้ Base64 ถอดรหัส String และใช้ในการSecretKeySpecสร้างต้นฉบับของคุณSecretKeyใหม่

สำหรับ 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());

สตริงเป็น 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):

หมายเหตุฉัน:คุณสามารถข้ามส่วนการเข้ารหัส / ถอดรหัส Base64 และเก็บไว้byte[]ใน SQLite ที่กล่าวว่าการเข้ารหัส / ถอดรหัส Base64 ไม่ใช่การดำเนินการที่มีราคาแพงและคุณสามารถจัดเก็บสตริงในเกือบทุกฐานข้อมูลได้โดยไม่มีปัญหา

หมายเหตุ II: Java เวอร์ชันก่อนหน้านี้ไม่มี Base64 ในหนึ่งในjava.langหรือjava.utilแพ็คเกจ มันเป็นไปได้อย่างไรที่จะใช้ตัวแปลงสัญญาณจากApache Commons Codec , ปราสาท Bouncyหรือฝรั่ง

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

สตริงเป็น 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
Jabari

@ MaartenBodewes-owlstead คนส่วนใหญ่ยังไม่ได้ใช้ Java 8 ฉันใช้สิ่งนี้ใน Android ซึ่งยังไม่ถึง 8 แน่นอน (และอาจจะใช้ไม่ได้ในบางครั้ง) โปรดอย่าแก้ไขคำตอบของใครบางคนโดยใช้บริบทสมมติ
Jabari

@ MaartenBodewes-owlstead ความคิดเห็นของคุณไม่สนใจประโยคแรกของฉันโดยสิ้นเชิง: "คนส่วนใหญ่ยังไม่ได้ใช้ Java 8" คำตอบของคุณจะทำให้เกิดข้อผิดพลาดข้อยกเว้นสำหรับผู้ใช้ Java, Android และไม่ใช่ Android ส่วนใหญ่ กล่าวได้ว่าคำแนะนำของคุณในการเพิ่มข้อมูลโค้ดนอกเหนือจากคำตอบปัจจุบันจะช่วยให้คุณได้รับโซลูชันที่สมบูรณ์ยิ่งขึ้น FYI ฉันไม่ "ซาบซึ้ง" เกี่ยวกับคำตอบของฉัน ตามความเป็นจริงฉันเปลี่ยน DES สำหรับ AES เนื่องจากเป็นการปรับปรุงความปลอดภัยที่ชาญฉลาดอย่างชัดเจน (และสอดคล้องกับรหัสในคำถามเดิมมากขึ้น)
Jabari

@ MaartenBodewes-owlstead อีกครั้ง ... สิ่งที่คุณเพิ่มจะทำให้เกิดข้อผิดพลาดข้อยกเว้น "NoSuchAlgorithmException" โปรดดู: docs.oracle.com/javase/7/docs/api/javax/crypto/… ฉันจะแก้ไข ...
Jabari

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

จริงๆแล้วสิ่งที่ Luis เสนอไม่ได้ผลสำหรับฉัน ฉันต้องหาวิธีอื่น นี่คือสิ่งที่ช่วยฉัน อาจช่วยคุณได้เช่นกัน ลิงค์:

  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 รับอาร์เรย์ไบต์ [] และเก็บไว้ในฐานข้อมูล กระบวนการย้อนกลับคือการทำให้ไบต์ [] อาร์เรย์ออกจากฐานข้อมูลสร้าง ByteArrayInputStream ของอาร์เรย์ไบต์ [] และยกเลิกการกำหนดค่าลับของ SecretKey ออก ...

... หรือง่ายกว่านั้นเพียงใช้.getEncoded()วิธีการที่สืบทอดมาจาก java.security.Key (ซึ่งเป็นส่วนต่อประสานหลักของ SecretKey) วิธีนี้จะส่งคืนอาร์เรย์ที่เข้ารหัสไบต์ [] จาก Key / SecretKey ซึ่งคุณสามารถจัดเก็บหรือดึงข้อมูลจากฐานข้อมูลได้

ทั้งหมดนี้ถือว่าการใช้งาน SecretKey ของคุณรองรับการเข้ารหัส มิฉะนั้นgetEncoded()จะคืนค่าว่าง

แก้ไข:

คุณควรดูที่ Key / SecretKey javadocs (มีให้ที่จุดเริ่มต้นของหน้า 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- หรือ


ต่อเนื่องเป็นรูปแบบการต่อต้าน IMO ในปัจจุบันเมื่อใดก็ตามที่คุณมีแนวทางอื่น คำตอบที่ได้รับอนุมัติซึ่ง base64 เข้ารหัสและถอดรหัสมันดีกว่ามาก
user2223059

0

การแปลง SecretKeySpec เป็น String และในทางกลับกัน: คุณสามารถใช้getEncoded()วิธีการSecretKeySpecที่จะให้byteArrayจากที่คุณสามารถใช้encodeToString()เพื่อรับstringค่าSecretKeySpecในBase64วัตถุ

ในขณะที่แปลงSecretKeySpecเป็นString: use decode()in Base64will give byteArrayจากนั้นคุณสามารถสร้างอินสแตนซ์สำหรับSecretKeySpecparams 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;

} 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.