18

I want to create a X509 certificate using Java language and then extract public key from it.

I have searched the internet and found many code examples, but all of them have errors (unknown variable or unknown type) or have many warnings that say something like : "the method ... from type ... is deprecated " etc.

For example, why the following code doesn't work:

PublicKey pk;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
String PKstr = pk.toString();
InputStream PKstream = new ByteArrayInputStream(PKstr.getBytes());
X509Certificate pkcert = (X509Certificate)cf.generateCertificate(PKstream);


Can anyone show me how to create a certificate using pure Java or Bouncy Castle and then get a public key from that?

Thanks all.

informatik01
  • 16,038
  • 10
  • 74
  • 104
leyla moazami
  • 217
  • 1
  • 2
  • 10

3 Answers3

28

For JDK versions <17, you can also generate a certificate using only JDK classes. The disadvantage is that you have to use two classes from the sun.security.x509 package. The code would be:

KeyStore keyStore = ... // your keystore

// generate the certificate
// first parameter  = Algorithm
// second parameter = signrature algorithm
// third parameter  = the provider to use to generate the keys (may be null or
//                    use the constructor without provider)
CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA256WithRSA", null);
// generate it with 2048 bits
certGen.generate(2048);

// prepare the validity of the certificate
long validSecs = (long) 365 * 24 * 60 * 60; // valid for one year
// add the certificate information, currently only valid for one year.
X509Certificate cert = certGen.getSelfCertificate(
   // enter your details according to your application
   new X500Name("CN=My Application,O=My Organisation,L=My City,C=DE"), validSecs);

// set the certificate and the key in the keystore
keyStore.setKeyEntry(certAlias, certGen.getPrivateKey(), null, 
                        new X509Certificate[] { cert });

Retrieve the private key from the key store to encrypt or decrypt data. Based on the code is from http://www.pixelstech.net/article/1408524957-Generate-cetrificate-in-Java----3

Bastian Voigt
  • 5,311
  • 6
  • 47
  • 65
iwan.z
  • 552
  • 5
  • 8
  • 1
    Unfortunately this does not work with Java 17 any more: annot access class sun.security.tools.keytool.CertAndKeyGen (in module java.base) because module java.base does not export sun.security.tools.keytool to unnamed module @0x1a5b6f42 – Bastian Voigt Dec 13 '22 at 07:58
  • Voting this down not because I don't like the solution, but because I guess it becomes less relevant as people upgrade their JDKs – Bastian Voigt Dec 13 '22 at 08:05
7

Yes, with BouncyCastle, creating an X509 cert from 2 public keys (the key for the cert and the one for the CA) is done here.

I turn the resulting certificate into PEM here.

Pierre Carrier
  • 396
  • 2
  • 9
  • I have to add that the performance of BouncyCastle is pretty amazing. I had put this code being Jetty and could generate 8000 different certificates per second, from SSH public keys and (cached) LDAP information. – Pierre Carrier Jul 08 '12 at 14:47
  • oh, no. I think that bad to say my question. I want to a simple code for create usual certificate. – leyla moazami Jul 08 '12 at 18:56
  • Wow, I don't understand what you're looking for at all. You don't want to create a certificate from a key? Can you explain better? What's the input, what's the output? – Pierre Carrier Jul 08 '12 at 21:19
  • the input is public key of the specified entity and the output is a certificate that certify that public key. for this purpose, why the above code doesn't work? – leyla moazami Jul 09 '12 at 15:12
  • `CertificateFactory.generateCertificate(x)` reads an existing certificate in PKCS#7 format from `x` and returns a `Certificate`. That's documented on http://docs.oracle.com/javase/6/docs/api/java/security/cert/CertificateFactory.html#generateCertificate(java.io.InputStream) . Creating one from a public key is harder and what my code does. – Pierre Carrier Jul 09 '12 at 23:37
  • Nit: PKCS7 is a container for (among other things) _multiple_ certs and is read by `.generateCertificates`, see the top of the Javadoc page. `.generateCertificate` without s reads only a single cert, not PKCS7. But you're right both create internal object(s) for existing cert(s), not create wholly new cert(s). – dave_thompson_085 Nov 12 '19 at 16:57
2

For JDK >= 17, this is how to do it with BouncyCastle. The code example is in Kotlin, but it works exactly the same way in Java:

val keyPair = KeyPairGenerator.getInstance("RSA").genKeyPair()
val subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.public.encoded)
val now = Instant.now()
val validFrom = Date.from(now)
val validTo = Date.from(now.plusSeconds(60L * 60 * 24 * 365))
val certBuilder = X509v3CertificateBuilder(
    X500Name("CN=My Application,O=My Organisation,L=My City,C=DE"),
    BigInteger.ONE,
    validFrom,
    validTo,
    X500Name("CN=My Application,O=My Organisation,L=My City,C=DE"),
    subPubKeyInfo
)
val signer = JcaContentSignerBuilder("SHA256WithRSA")
    .setProvider(BouncyCastleProvider())
    .build(keyPair.private)
val certificate = certBuilder.build(signer)

This is the gradledependency you need to pull:

implementation("org.bouncycastle:bcpkix-jdk18on:1.72")
Bastian Voigt
  • 5,311
  • 6
  • 47
  • 65