2010-08-09 81 views
10
首選方式

此代碼生成一對公鑰/私鑰:Java的非對稱加密:存儲公/私鑰

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 
keyGen.initialize(1024); 
KeyPair keypair = keyGen.genKeyPair(); 
PrivateKey privateKey = keypair.getPrivate(); 
PublicKey publicKey = keypair.getPublic(); 

我想知道什麼是你通常如何保存的公鑰:

選項1:存儲字節

byte[] privateKeyBytes = privateKey.getEncoded(); 
byte[] publicKeyBytes = publicKey.getEncoded(); 
// ... write to file 

// convert bytes back to public/private keys 
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); 
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); 

EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes); 
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); 

我不喜歡的是將代碼綁定到具體的實現,例如PKCS8EncodedKeySpecX509EncodedKeySpec

選項2:存儲係數和指數

KeyFactory fact = KeyFactory.getInstance("RSA"); 
RSAPublicKeySpec pub = fact.getKeySpec(publicKey, RSAPublicKeySpec.class); 
RSAPrivateKeySpec priv = fact.getKeySpec(privateKey,RSAPrivateKeySpec.class); 

// store modulus and exponent as BigIntegers 
BigInteger modulus = pub.getModulus()); 
BigInteger exponent = pub.getPublicExponent()); 
// ... write to file 

// recreate public key (the same applies to the private key) 
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent); 
KeyFactory fact = KeyFactory.getInstance("RSA"); 
PublicKey pubKey = fact.generatePublic(keySpec); 

第二個選項是更容易實現,但我不知道這是否可能是少高性能。

有沒有建議嗎?

回答

12

在我們的應用程序中,我們存儲DER格式的公鑰和私鑰,以便它們可以更容易地在java外部使用和操作。在我們的例子中,私鑰沒有密碼。

私鑰更容易使用Java中轉換成的東西:

openssl pkcs8 -topk8 -nocrypt -in key.pem -inform PEM -out key.der -outform DER 

然後你就可以直接獲得一個RSA私鑰:

public static RSAPrivateKey getPrivateKey(File privateKeyFile) throws IOException, GeneralSecurityException { 
    byte[] keyBytes = new byte[(int)privateKeyFile.length()]; 
    FileInputStream fis = new FileInputStream(privateKeyFile); 
    fis.read(keyBytes); 
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); 
    KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
    RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(spec); 
    return privKey; 
} 

公鑰是相似的:

openssl rsa -in private.pem -pubout -outform DER -out public.der 

並讀取它:

public static RSAPublicKey getPublicKey(File publicKeyFile) throws IOException, GeneralSecurityException { 
    byte[] keyBytes = new byte[(int)publicKeyFile.length()]; 
    FileInputStream fis = new FileInputStream(publicKeyFile); 
    fis.read(keyBytes); 
    X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes); 
    KeyFactory factory = KeyFactory.getInstance("RSA"); 
    RSAPublicKey pubKey = (RSAPublicKey)factory.generatePublic(publicKeySpec); 
    return pubKey; 
} 

很多人存儲密鑰庫。就我們的目的而言,我們需要使用相同的密鑰以多種不同的語言在多個應用程序之間共享,並且不希望在磁盤上覆制文件。

在這兩種情況下,性能不應該是一個巨大的問題,因爲您可能會將這些密鑰存儲在某種Singleton或緩存中,而不是每次都重新生成它們。

+1

DER是不是一個真正的格式本身,只是編碼ASN.1對象 – 2010-08-09 21:37:21

+0

對於那些希望獲得'公共的方式。直接從'id_rsa.pub'(公鑰),而不是'id_rsa'(私鑰)der' [這個答案提供了一種方法來做到這一點(http://stackoverflow.com/a/18290786/813810)。 – Diego 2014-09-01 10:54:01

1

如果你想定義一個用於存儲密鑰的格式,那麼我會選擇一種非常耗費的格式,以便在你想要改變加密時(例如,當舊的軟件變弱時)它不會中斷。

因此,我會將編碼爲base64的字節與描述格式「rsa」的字符串一起存儲。

2

實際上,無論您是否意識到這兩種情況,實際上都會存儲字節。我想@Brian M. Carr的答案暗示了正確的答案,即以最自然的形式存儲更高級別的對象。在公鑰的情況下,明顯的選擇是作爲DER編碼的PKCS#1 RSAPublicKey ASN.1結構或者DER編碼的X509 SubjectPublicKeyInfo ASN.1結構。後者是Sun提供商給你的,Sun X509EncodedKeySpec支持。同樣,PKCS8EncodedKeySpec支持私鑰格式。這兩種格式都是標準的,並且例如由openssl支持。孫趨向 - 往往:( - 支持現有的標準,而不是自己定義

+0

謝謝你真的很有幫助,我將@Brian M. Carr的答案標記爲因爲代碼示例而被接受。 – 2010-08-10 06:51:06