13

此代码生成一对公钥/私钥:

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

第二个选项更容易实现,但我不知道它是否会降低性能。

有什么建议吗?

4

3 回答 3

13

在我们的应用程序中,我们以 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;
}

许多人存储然后密钥库。出于我们的目的,我们需要以多种不同语言在多个应用程序之间共享相同的密钥,并且不希望复制磁盘上的文件。

在任何一种情况下,性能都不应该是一个大问题,因为您可能会将这些键存储在某种单例或缓存中,而不是每次都重新生成它们。

于 2010-08-09T15:38:47.723 回答
2

无论您是否意识到,您实际上都在存储字节。我想@Brian M. Carr 答案中暗示了正确的答案,即以最自然的形式存储更高级别的对象。对于公钥,显而易见的选择是 PKCS#1 RSAPublicKey ASN.1 结构,DER 编码,或 X509 SubjectPublicKeyInfo ASN.1 结构,DER 编码。后者是 Sun 提供者为您提供的,Sun 类 X509EncodedKeySpec 支持。同样,PKCS8EncodedKeySpec 支持私钥格式。这两种格式都是标准的,例如都被 openssl 支持。Sun 倾向于 -- 倾向于 :( -- 支持现有标准,而不是定义自己的标准。

于 2010-08-09T21:52:08.293 回答
1

如果您想定义存储密钥的格式,那么我会选择一种可消耗的格式,以便在您想要更改加密时它不会中断(例如,当旧的加密变弱时)。

所以我会将编码为base64的字节与描述格式的字符串一起存储,也许是“rsa”。

于 2010-08-09T15:30:12.450 回答