如何在Java中存储和重用密钥对?

我想要生成一次密钥对并重用它。

public static KeyPair generateKeyPair() throws Exception { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(2048, new SecureRandom()); KeyPair pair = generator.generateKeyPair(); return pair; } 

我该怎么做?

这里有一个问题:Java的重点几乎完全在于TLS和实现TLS所需的加密。 对于TLS,需要私钥和证书。 所以你会遇到这样的情况:

  1. 必须生成(伪造)自签名证书以使用您的公钥或;
  2. 必须找到另一种存储没有证书的私钥的方法。

但是,使用(2.),您很快就会得到一种不兼容的方法。 如果你想这样做,你可以创建一个使用PB​​E / CBC加密的PKCS#8加密私钥。

所以这里有一些代码来创建自签名证书并使用它来存储密钥。 注意到期日期,您可以将其设置为100年以保证安全(或者您实际上可以进行一些密钥管理)。

 import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStore.Entry; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.UnrecoverableEntryException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.Calendar; import java.util.Date; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; public class StoreKeyPair { public static KeyPair generateKeyPair() throws Exception { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(2048, new SecureRandom()); KeyPair pair = generator.generateKeyPair(); return pair; } public static Certificate selfSign(KeyPair keyPair, String subjectDN) throws OperatorCreationException, CertificateException, IOException { Provider bcProvider = new BouncyCastleProvider(); Security.addProvider(bcProvider); long now = System.currentTimeMillis(); Date startDate = new Date(now); X500Name dnName = new X500Name(subjectDN); // Using the current timestamp as the certificate serial number BigInteger certSerialNumber = new BigInteger(Long.toString(now)); Calendar calendar = Calendar.getInstance(); calendar.setTime(startDate); // 1 Yr validity calendar.add(Calendar.YEAR, 1); Date endDate = calendar.getTime(); // Use appropriate signature algorithm based on your keyPair algorithm. String signatureAlgorithm = "SHA256WithRSA"; SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair .getPublic().getEncoded()); X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate, dnName, subjectPublicKeyInfo); ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).setProvider( bcProvider).build(keyPair.getPrivate()); X509CertificateHolder certificateHolder = certificateBuilder.build(contentSigner); Certificate selfSignedCert = new JcaX509CertificateConverter() .getCertificate(certificateHolder); return selfSignedCert; } public static void main(String[] args) throws Exception { KeyPair generatedKeyPair = generateKeyPair(); String filename = "test_gen_self_signed.pkcs12"; char[] password = "test".toCharArray(); storeToPKCS12(filename, password, generatedKeyPair); KeyPair retrievedKeyPair = loadFromPKCS12(filename, password); // you can validate by generating a signature and verifying it or by // comparing the moduli by first casting to RSAPublicKey, eg: RSAPublicKey pubKey = (RSAPublicKey) generatedKeyPair.getPublic(); RSAPrivateKey privKey = (RSAPrivateKey) retrievedKeyPair.getPrivate(); System.out.println(pubKey.getModulus().equals(privKey.getModulus())); } private static KeyPair loadFromPKCS12(String filename, char[] password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableEntryException { KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12"); try (FileInputStream fis = new FileInputStream(filename);) { pkcs12KeyStore.load(fis, password); } KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(password); Entry entry = pkcs12KeyStore.getEntry("owlstead", param); if (!(entry instanceof PrivateKeyEntry)) { throw new KeyStoreException("That's not a private key!"); } PrivateKeyEntry privKeyEntry = (PrivateKeyEntry) entry; PublicKey publicKey = privKeyEntry.getCertificate().getPublicKey(); PrivateKey privateKey = privKeyEntry.getPrivateKey(); return new KeyPair(publicKey, privateKey); } private static void storeToPKCS12( String filename, char[] password, KeyPair generatedKeyPair) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, OperatorCreationException { Certificate selfSignedCertificate = selfSign(generatedKeyPair, "CN=owlstead"); KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12"); pkcs12KeyStore.load(null, null); KeyStore.Entry entry = new PrivateKeyEntry(generatedKeyPair.getPrivate(), new Certificate[] { selfSignedCertificate }); KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(password); pkcs12KeyStore.setEntry("owlstead", entry, param); try (FileOutputStream fos = new FileOutputStream(filename)) { pkcs12KeyStore.store(fos, password); } } } 

请注意,我太懒了,无法正确处理exception。

这段代码使用了这个答案的略微修改版本,请参阅我的注释,了解为什么我更改了代码。


公钥当然也可以单独存储。 只需调用getEncoded并存储生成的SubjectPublicKeyInfo结构。