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

时间:2017-05-14 05:46:17

标签: java cryptography keystore

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

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

        return pair;
}

我该怎么做?

1 个答案:

答案 0 :(得分:8)

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

  1. 必须生成(伪造)自签名证书以使用您的公钥或;
  2. 必须找到另一种存储没有证书的私钥的方法。
  3. 但是,使用(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, e.g.:
    
            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);
            }
        }
    }
    

    请注意,我太懒了,无法妥善处理异常。

    此代码使用this answer的略微更改版本,请参阅我对我更改代码的原因的评论。

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