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

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

有什么建议吗?

在我们的应用程序中,我们以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或缓存中,而不是每次都重新生成它们。

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

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

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