In our previous tutorial, we have explained how to generate CSR which can be sent to CA for generating a signed certificate. In this tutorial, we will explain how to generate the signed certificate from CSR in Java. We will not use an actual CA but a self-signed certificate to act as a CA certificate.
Since the CSR contains the subject information where a certificate needs to be generated and signed for. The key here is to extract the subject information from the CSR and then set it as the subject of the newly generated certificate.
The source code can be found below which has a complete example of generating and signing the certificate from CSR.
package security.signcsr;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Random;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import sun.misc.BASE64Encoder;
import sun.security.pkcs10.PKCS10;
import sun.security.provider.X509Factory;
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.util.DerValue;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.Extension;
import sun.security.x509.KeyUsageExtension;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
public class CSRSigner {
private static final String SIGNATURE_ALGORITHM = "SHA1WITHRSA";
private static final long VALIDITY_DAYS = 365L;
public static byte[] sign(PKCS10 csr, X509CertImpl signerCert, PrivateKey signerPrivKey) throws CertificateException, IOException, InvalidKeyException, SignatureException {
/*
* The code below is partly taken from the KeyTool class in OpenJDK7.
*/
X509CertInfo signerCertInfo = (X509CertInfo) signerCert.get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
X500Name issuer = (X500Name) signerCertInfo.get(X509CertInfo.SUBJECT + "." + CertificateSubjectName.DN_NAME);
/*
* Set the certificate's validity:
* From now and for VALIDITY_DAYS days
*/
Date firstDate = new Date();
Date lastDate = new Date();
lastDate.setTime(firstDate.getTime() + VALIDITY_DAYS * 1000L * 24L * 60L * 60L);
CertificateValidity interval = new CertificateValidity(firstDate, lastDate);
/*
* Initialize the signature object
*/
Signature signature;
try {
signature = Signature.getInstance(SIGNATURE_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
signature.initSign(signerPrivKey);
// create the certificate info
X509CertInfo certInfo = new X509CertInfo();
certInfo.set(X509CertInfo.VALIDITY, interval);
certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(new Random().nextInt() & 0x7fffffff));
certInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
try {
certInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(AlgorithmId.get(SIGNATURE_ALGORITHM)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
certInfo.set(X509CertInfo.ISSUER, issuer);
certInfo.set(X509CertInfo.KEY, new CertificateX509Key(csr.getSubjectPublicKeyInfo()));
certInfo.set(X509CertInfo.SUBJECT, csr.getSubjectName());
/*
* Add x509v3 extensions to the container
*/
CertificateExtensions extensions = new CertificateExtensions();
// Example extension.
// See KeyTool source for more.
boolean[] keyUsagePolicies = new boolean[9];
keyUsagePolicies[0] = true; // Digital Signature
keyUsagePolicies[2] = true; // Key encipherment
KeyUsageExtension kue = new KeyUsageExtension(keyUsagePolicies);
byte[] keyUsageValue = new DerValue(DerValue.tag_OctetString, kue.getExtensionValue()).toByteArray();
extensions.set(KeyUsageExtension.NAME, new Extension(
kue.getExtensionId(),
true, // Critical
keyUsageValue));
/*
* Create the certificate and sign it
*/
X509CertImpl cert = new X509CertImpl(certInfo);
try {
cert.sign(signerPrivKey, SIGNATURE_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
}
/*
* Return the signed certificate as PEM-encoded bytes
*/
ByteOutputStream bos = new ByteOutputStream();
PrintStream out = new PrintStream(bos);
BASE64Encoder encoder = new BASE64Encoder();
out.println(X509Factory.BEGIN_CERT);
encoder.encodeBuffer(cert.getEncoded(), out);
out.println(X509Factory.END_CERT);
out.flush();
return bos.getBytes();
}
public static void main(String[] args) {
try{
CertAndKeyGen keyGen=new CertAndKeyGen("RSA","SHA1WithRSA",null);
keyGen.generate(1024);
PrivateKey privKey = keyGen.getPrivateKey();
//Generate self signed certificate(act as CA)
X509Certificate[] chain=new X509Certificate[1];
chain[0]=keyGen.getSelfCertificate(new X500Name("CN=ROOT"), (long)365*24*3600);
X509CertImpl certImpl = new X509CertImpl(chain[0].getEncoded());
// Generate CSR
CSRGenerator csrGeneration = new CSRGenerator();
KeyPair keyPair = csrGeneration.generateKeyPair("RSA", 1024);
System.out.println("KeyPair generated");
PKCS10 csrData = csrGeneration.generateCSR("SHA256WithRSA", keyPair);
System.out.println("CSR : "+ new String(csrData.getEncoded(), StandardCharsets.UTF_8));
// Get the signed certificate
byte[] outBytes = sign(csrData, certImpl, privKey);
String s = new String(outBytes, StandardCharsets.UTF_8);
System.out.println("Certificate : " + s);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
The code should be self-explained. In main(), we will first generate the CA private and public key and certificate so that they will be used to sign the CSR. And then the step to generate the CSR, the code can be found in previous tutorial.
The output of the generated and signed certificate would be like:
-----BEGIN CERTIFICATE-----
MIIBnDCCAQWgAwIBAgIEGuKmyzANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRST09UMB4XDTIw
MTAyNDExMzYyMFoXDTIxMTAyNDExMzYyMFowFjEUMBIGA1UEAxMLRVhBTVBMRS5DT00wgZ8wDQYJ
KoZIhvcNAQEBBQADgY0AMIGJAoGBAKtN4BPnLSCD5kOObh2w11AhLr+uWMClPMQ8oMXpc63r1ez5
r1V+lCeAyqmL7cu5Oycy0s3nbTgAIpdqt1SMy+6SyuNSLtXHkkGx5Dcb188CNIM+DBvRoOE1UVep
vCFFuBOwqcgFI9yvmKeyZoONP9mDd4c7rMiJk4LkXTjrSuK1AgMBAAEwDQYJKoZIhvcNAQEFBQAD
gYEACPBIwGV5IFqrsbWk2C7cLpr8VW6djt5TzGRDEMAQVksm+V7tHn1ue//Ly19QJIUe78UBB+RA
qTnCou6jEiIYomU+wWxqsesUfLePNKNBKPQFc4jfEDx+r+/iIV06PXAUAj8NnKvaUU7xu3a/Ipms
fjWN9x7LrPUn1eBvGFbM7HU=
-----END CERTIFICATE-----
If checking the certificate information, can find its subject is EXAMPLE.COM and its issuer is ROOT which is expected.
This code has been tested on Java 8. It may need some adjustment on other versions of Java but the general flow should be the same.
Why are we generating the csr here. If we have received a csr from the user then we only need to read that csr and generate the certificate, why there is a need of generating this csr here?.
If I have a GUI(suppose a textfield) in which user has entered the csr then how can I read that csr from there and generate certificate?