2009-12-17 79 views
14

解密字符串對於加密我用這樣的:方便的保存/恢復加密密鑰在Java

SecretKey aesKey = KeyGenerator.getInstance("AES").generateKey(); 
StringEncrypter aesEncrypt = new StringEncrypter(aesKey, aesKey.getAlgorithm()); 
String aesEncrypted= aesEncrypt.encrypt(StringContent); 

如果我打印出來aesKey我得到:「[email protected]」 。

所以對於加密我想問問用戶的密鑰,但不知道如何以及它應該是什麼格式。 我的計劃是這樣的:

SecretKey aesKey = [email protected]; 
StringEncrypter aesEncrypt = new StringEncrypter(aesKey, aesKey.getAlgorithm()); 
String aesDecrypt = aesEncrypt.decrypt(aesEncrypted); 

,但似乎它不工作。是否有一些簡單的方法可以將加密後的密鑰打印出來,以便用戶保存(或記住它),然後用於解密?

整個代碼在這裏:Cannot decrypt cyphertext from text file, symmetric key implement. in java 所以我很抱歉再次發佈,但我不知道如果代碼是可讀的(我是新手)。

+0

請參閱我的更新答案。 – erickson

回答

0

僅供參考,您所看到的輸出是默認toString方法的結果和最終有趣的數字是一個散列碼。請參閱here。哈希代碼在設計上是不可逆的,並且toString未必能保證爲您提供足夠的信息來重建原始對象(儘管它對於某些類而言)。

12

大多數Java Key實例表示爲由它們的getEncoded()方法產生的一串字節。這是需要存儲以便稍後重建密鑰的內容。

但是,要安全地以電子形式存儲密鑰,應該對其進行加密。當然,加密密鑰需要另一個密鑰(或密碼)......所以你有一個無限的倒退。 Java KeyStore可用於以這種方式存儲SecretKey對象,並且當您有許多密鑰都受到單個「主」密碼的保護時,這很有用。但爲了保護一個單一的密鑰,這並沒有多大意義。

一種替代方法是以一種可以以某種安全方式存儲的形式(在許多應用程序中,可能位於錢包中的紙條上)向用戶呈現密鑰。這可以像顯示以十六進制,Base-64或其他文本編碼編碼的密鑰的字節一樣簡單,並要求用戶將其寫下。

另一種方法是允許用戶選擇一個令人難忘的密碼,並使用像PBKDF2這樣的算法生成密鑰。用於密鑰派生的鹽(也可能是迭代計數)需要記錄在某處。另一個缺點是人們傾向於從可用總數中選擇相對有限數量的密碼。因此,從密碼派生的密鑰可能比密鑰大小暗示的猜測更容易。


下面是持久化和重構密鑰的基本技巧的說明。

byte[] encoded = aesKey.getEncoded(); 
/* Now store "encoded" somewhere. For example, display the key and 
    ask the user to write it down. */ 
String output = Base64.getEncoder().withoutPadding().encodeToString(encoded); 
System.out.println("Keep it secret, keep it safe! " + output); 

... 

/* At some point, you need to reconstitute the key. Let's say the user 
    enters it as a base-64 number that you convert to bytes. */ 
String input = ... ; 
byte[] encoded = Base64.getDecoder().decode(input); 
SecretKey aesKey = new SecretKeySpec(encoded, "AES"); 
+0

感謝您的解釋。但是,我怎樣才能調用密鑰而不是基因組(KeyGenerator.getInstance(「AES」)。generateKey();)?現在我可以使用getEncoded()打印出關鍵字,用戶將把它寫下來。但是我怎麼把鑰匙叫回來? – dave91

+0

我看到與此代碼的新BigInteger(輸入,16).toByteArray()部分不一致的讀取。當我嘗試使用它時,它往往讀不到17bytes的數據,並給我一個'invalid aes key length:17 bytes'的錯誤。什麼可能導致這個? – sapatos

+2

@sapatos如果密鑰的高位被設置,'BigInteger'將在字節數組的開頭添加一個零字節以防止它被解釋爲負數。也許不是'新的BigInteger(1,編碼)',你應該允許字符串是一個負數:'new BigInteger(encoded)' – erickson

20

最近我不得不親自這樣做。雖然這裏的其他答案讓我朝着正確的方向發展,但本來可能會更容易。所以這裏是我今天的「份額」,一些簡單的AES鍵操作的輔助方法。(請注意:Apache的百科全書和編解碼器的依賴性。)

這一切在現在混帳回購協議:github.com/stuinzuri/SimpleJavaKeyStore

import static org.apache.commons.codec.binary.Hex.*; 
import static org.apache.commons.io.FileUtils.*; 
import java.io.*; 
import java.security.NoSuchAlgorithmException; 
import javax.crypto.*; 
import org.apache.commons.codec.DecoderException; 

public static SecretKey generateKey() throws NoSuchAlgorithmException 
{ 
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); 
    keyGenerator.init(256); // 128 default; 192 and 256 also possible 
    return keyGenerator.generateKey(); 
} 

public static void saveKey(SecretKey key, File file) throws IOException 
{ 
    char[] hex = encodeHex(key.getEncoded()); 
    writeStringToFile(file, String.valueOf(hex)); 
} 

public static SecretKey loadKey(File file) throws IOException 
{ 
    String data = new String(readFileToByteArray(file)); 
    byte[] encoded; 
    try { 
     encoded = decodeHex(data.toCharArray()); 
    } catch (DecoderException e) { 
     e.printStackTrace(); 
     return null; 
    } 
    return new SecretKeySpec(encoded, "AES"); 
} 
+2

如果由'getEncoded()'返回的數組具有前導空字節,或者第一個字節的最高位已設置,那麼這不會失敗嗎? –

+0

@NiklasB。修正,謝謝指出。 –

+1

_我是靜態效用函數和靜態導入的粉絲_ :) –