3

I need to generate a public X509 Certificate but I have very little experience with Security in Java. I just want to make sure that what I am currently doing will work. First, I have already generated a public and private key with the following code:

public static void generateKeys() {
        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            SecureRandom random = SecureRandom.getInstanceStrong();
            keyGen.initialize(2048, random);

            KeyPair pair = keyGen.generateKeyPair();
            PrivateKey priv = pair.getPrivate();
            PublicKey pub = pair.getPublic();
            byte[] encPriv = priv.getEncoded();
            FileOutputStream privfos = new FileOutputStream(PRIVATE_KEY_FILENAME);
            privfos.write(encPriv);
            privfos.close();

            byte[] encPub = pub.getEncoded();
            FileOutputStream pubfos = new FileOutputStream(PUBLIC_KEY_FILENAME);
            pubfos.write(encPub);
            pubfos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

And here is the code used for generating the X509Certificate:

public static PrivateKey getPrivateKey() throws Exception {
    byte[] pkcs8EncodedBytes = Files.readAllBytes(PRIVATE_KEY_FILENAME);
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePrivate(keySpec);
}

public static PublicKey getPublicKey() throws Exception {
    byte[] publicEncodedBytes = Files.readAllBytes(PUBLIC_KEY_FILENAME);
    X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicEncodedBytes);
    KeyFactory factory = KeyFactory.getInstance("RSA");
    return factory.generatePublic(publicKeySpec);
}

public static X509Certificate generateX509Certificate() throws Exception {
    PrivateKey privateKey = getPrivateKey();
    PublicKey publicKey = getPublicKey();
    KeyPair keyPair = new KeyPair(publicKey, privateKey);
    final Instant now = Instant.now();
    final Date notBefore = Date.from(now);
    final Date until = LocalDate.now().plusYears(100).toDate();
    final ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WITHRSA").build(keyPair.getPrivate());
    final X500Name x500Name = new X500Name("CN=Common Name,O=Organization,L=City,ST=State");
    final X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(x500Name,
            BigInteger.valueOf(now.toEpochMilli()), notBefore, until, x500Name, keyPair.getPublic());
    return new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider())
            .getCertificate(certificateBuilder.build(contentSigner));
}

Can someone clarify if this is the correct way to create a X509Certificate in Java? Thanks!

Davidm113021
  • 81
  • 1
  • 6

1 Answers1

3

I haven't seen your loading a Key Store. Your method of retrieving the private/public key doesn't seem right.
A working example looks like this:

    public static Certificate generateCertificate(KeyPair keyPair) throws CertificateException, OperatorCreationException 
    {
        X500Name x500Name = new X500Name("CN=***.com, OU=Security&Defense, O=*** Crypto., L=Ottawa, ST=Ontario, C=CA");
        SubjectPublicKeyInfo pubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
        final Date start = new Date();
        final Date until = Date.from(LocalDate.now().plus(365, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC));
        final X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(x500Name,
                new BigInteger(10, new SecureRandom()), start,   until,  x500Name,  pubKeyInfo
        );
        ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").build(keyPair.getPrivate());

        Certificate certificate = new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider()).getCertificate(certificateBuilder.build(contentSigner));
        
        System.out.println("x.509 certificate is successfully generated!");
        
        return certificate;
           
    }

Generate certificate logging:

Generating certificate in KeyStore /security/keystore/Andante-x509.jks

Encapsulating KeyPair with password in KeyStore... 

x.509 certificate is successfully generated!

Sitzung beendet wird. 

Extract and validate certificate from KeyStore:

If certificate doesn’t exist:

Certificate API Exception: The system cannot find the path specified. 

If KeyStore alias doesn’t match:

KeyStore Exception: Key cannot be retrieved. 

If KeyStore password doesn’t match:

KeyStore Exception: Keystore was tampered with, or password was incorrect. 

Fiona Chen
  • 1,358
  • 5
  • 16
  • While KeyStore is (intentionally) suitable and convenient for storing keys in most cases, it is not the only way; the write(key.getEncoded())) and readAllBytes+KeyFactory method in the Q works -- although keeping the unencrypted privatekey in a file is often insecure – dave_thompson_085 Jul 28 '20 at 02:26
  • Yes, the mainspring of Java Security fabric is to lend both veracity and validity to the certificate. If ever the hacker intercepts or replaces data, key pairs or signature, the encoded keys render useless. After the certificate is encrypted in KeyStore, an `alias` permits of genuine certificate existence. While the `encryption` permits of no keystore tampering…That is my understanding… – Fiona Chen Jul 28 '20 at 04:09
  • Substituting a public key and compromising a private key are different threats (but both important). The private key is encrypted in file-based keystores (JKS, JCEKS, PKCS12) and expected to have equivalent protection in others (like PKCS11/HSM); the _certificate_ does not need to be and usually isn't encrypted, but is protected by integrity measures like a PBMAC on the file-based stores. – dave_thompson_085 Jul 30 '20 at 23:27
  • I added an `Extraction` output in `Answer`... What might have safeguarded the certificate better based on that `Extraction` and validation logic? I'm all ears. – Fiona Chen Jul 31 '20 at 19:50
  • I don't know what you're asking. If you have a certificate in a usual file-based Java keystore, it is protected against substitution (or alteration) by the (PB)MAC; the alias helps _organize_ the keystore but does not protect it (an attacker can easily enumerate aliases, even in PKCS12). For some formats the cert is not encrypted and thus not protected against exposure, but that's not a problem because certs (and publickeys generally) are defined to be public. OTOH the _privatekey_ is always encrypted (as well as included in the MAC). – dave_thompson_085 Aug 04 '20 at 09:16
  • I am looking for a password-protected wallet | repository which contains and manages the authentication credentials, private keys, certificates. In this repository, users can generate | retrieve user | trusted certificates within centralised CA, upload|download to|from LDAP, interoperate other 3rd-party PKI applications. This wallet can be deployed over Java API. There could be many key pairs in a single repository serve the same certificate usage ( digital signature, key encipher, decipher…etc) . It is like a mapping: a single certificate(key) is mapped to a certificate usage(value). – Fiona Chen Aug 04 '20 at 14:34
  • That's a very different question than the one asked here, and a Java KeyStore is certainly a solution, probably a good solution, to _parts_ of it. – dave_thompson_085 Aug 07 '20 at 05:35