2010-08-09 53 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