com.enioka.jqm.pki.CertificateRequest.java Source code

Java tutorial

Introduction

Here is the source code for com.enioka.jqm.pki.CertificateRequest.java

Source

/**
 * Copyright  2013 enioka. All rights reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.enioka.jqm.pki;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Calendar;
import java.util.Date;

import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509ExtensionUtils;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.BigIntegers;

public class CertificateRequest {
    // Parameter fields
    private String prettyName;
    private Integer size = 2048;
    private X509CertificateHolder authorityCertificate = null;
    private PrivateKey authorityKey = null;
    private String Subject;
    private KeyPurposeId[] EKU;
    private int keyUsage = 0;
    private int validityYear = 10;

    // Result fields
    OutputStream pemPublicFile;
    OutputStream pemPrivateFile;
    X509CertificateHolder holder;
    PublicKey publicKey;
    PrivateKey privateKey;

    // Public API
    public void generateCA(String prettyName) {
        this.prettyName = prettyName;

        Subject = "CN=JQM-CA,OU=ServerProducts,O=Oxymores,C=FR";
        size = 4096;

        EKU = new KeyPurposeId[4];
        EKU[0] = KeyPurposeId.id_kp_codeSigning;
        EKU[1] = KeyPurposeId.id_kp_serverAuth;
        EKU[2] = KeyPurposeId.id_kp_clientAuth;
        EKU[3] = KeyPurposeId.id_kp_emailProtection;

        keyUsage = KeyUsage.cRLSign | KeyUsage.keyCertSign;

        generateAll();
    }

    public void generateClientCert(String prettyName, X509CertificateHolder authority, PrivateKey issuerPrivateKey,
            String subject) {
        this.prettyName = prettyName;

        authorityCertificate = authority;
        authorityKey = issuerPrivateKey;

        this.Subject = subject;

        size = 2048;

        EKU = new KeyPurposeId[1];
        EKU[0] = KeyPurposeId.id_kp_clientAuth;

        keyUsage = KeyUsage.digitalSignature | KeyUsage.keyEncipherment;

        generateAll();
    }

    public void generateServerCert(String prettyName, X509CertificateHolder authority, PrivateKey issuerPrivateKey,
            String subject) {
        this.prettyName = prettyName;

        authorityCertificate = authority;
        authorityKey = issuerPrivateKey;

        this.Subject = subject;

        size = 2048;

        EKU = new KeyPurposeId[1];
        EKU[0] = KeyPurposeId.id_kp_serverAuth;

        keyUsage = KeyUsage.digitalSignature | KeyUsage.keyEncipherment;

        generateAll();
    }

    public void writePemPublicToFile(String path) {
        try {
            File f = new File(path);
            if (!f.getParentFile().isDirectory() && !f.getParentFile().mkdir()) {
                throw new PkiException("couldn't create directory " + f.getParentFile().getAbsolutePath()
                        + " for storing the SSL keystore");
            }
            FileWriter fw = new FileWriter(path);
            PEMWriter wr = new PEMWriter(fw);
            wr.writeObject(holder);
            wr.close();
            fw.close();
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    public String writePemPublicToString() {
        try {
            StringWriter sw = new StringWriter();
            PEMWriter wr = new PEMWriter(sw);
            wr.writeObject(holder);
            wr.close();
            sw.close();
            return sw.toString();
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    public void writePemPrivateToFile(String path) {
        try {
            FileWriter fw = new FileWriter(path);
            PEMWriter wr = new PEMWriter(fw);
            wr.writeObject(privateKey);
            wr.close();
            fw.close();
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    public String writePemPrivateToString() {
        try {
            StringWriter sw = new StringWriter();
            PEMWriter wr = new PEMWriter(sw);
            wr.writeObject(privateKey);
            wr.close();
            sw.close();
            return sw.toString();
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    // Internal methods
    private void generateAll() {
        try {
            generateKeyPair();
            generateX509();
            generatePem();
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    private void generatePem() {
        try {
            // PEM public key
            pemPublicFile = new ByteArrayOutputStream();
            Writer osw = new OutputStreamWriter(pemPublicFile);
            PEMWriter wr = new PEMWriter(osw);
            wr.writeObject(holder);
            wr.close();

            // PEM private key
            pemPrivateFile = new ByteArrayOutputStream();
            osw = new OutputStreamWriter(pemPrivateFile);
            wr = new PEMWriter(osw);
            wr.writeObject(privateKey);
            wr.close();
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    void writePfxToFile(OutputStream out, String password) {
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(null, null);

            Certificate[] chain = null;
            if (authorityCertificate != null) {
                chain = new Certificate[2];
                chain[0] = new JcaX509CertificateConverter().getCertificate(this.holder);
                chain[1] = new JcaX509CertificateConverter().getCertificate(this.authorityCertificate);
            } else {
                chain = new Certificate[1];
                chain[0] = new JcaX509CertificateConverter().setProvider("BC").getCertificate(this.holder);
            }

            ks.setKeyEntry("private key for " + this.prettyName, privateKey, password.toCharArray(), chain);

            ks.store(out, password.toCharArray());
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    public void writeTrustPfxToFile(String path, String password) {
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(null, null);

            Certificate ca = new JcaX509CertificateConverter().getCertificate(this.authorityCertificate);

            ks.setCertificateEntry("JQM-CA", ca);

            FileOutputStream fos = new FileOutputStream(path);
            ks.store(fos, password.toCharArray());
            fos.close();
        } catch (Exception e) {
            throw new PkiException(e);
        }
    }

    private void generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
        Security.addProvider(new BouncyCastleProvider());

        SecureRandom random = new SecureRandom();
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(Constants.KEY_ALGORITHM,
                Constants.JCA_PROVIDER);
        keyPairGenerator.initialize(size, random);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        publicKey = keyPair.getPublic();
        privateKey = keyPair.getPrivate();
    }

    private void generateX509() throws Exception {
        SecureRandom random = new SecureRandom();
        X500Name dnName = new X500Name(Subject);
        Calendar endValidity = Calendar.getInstance();
        endValidity.add(Calendar.YEAR, validityYear);

        SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());

        X509v3CertificateBuilder gen = new X509v3CertificateBuilder(
                authorityCertificate == null ? dnName : authorityCertificate.getSubject(),
                BigIntegers.createRandomInRange(BigInteger.ZERO, BigInteger.valueOf(Long.MAX_VALUE), random),
                new Date(), endValidity.getTime(), dnName, publicKeyInfo);

        // Public key ID
        DigestCalculator digCalc = new BcDigestCalculatorProvider()
                .get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1));
        X509ExtensionUtils x509ExtensionUtils = new X509ExtensionUtils(digCalc);
        gen.addExtension(Extension.subjectKeyIdentifier, false,
                x509ExtensionUtils.createSubjectKeyIdentifier(publicKeyInfo));

        // EKU
        gen.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(EKU));

        // Basic constraints (is CA?)
        if (authorityCertificate == null) {
            gen.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
        }

        // Key usage
        gen.addExtension(Extension.keyUsage, true, new KeyUsage(keyUsage));

        // Subject Alt names ?

        // Authority
        if (authorityCertificate != null) {
            gen.addExtension(Extension.authorityKeyIdentifier, false,
                    new AuthorityKeyIdentifier(authorityCertificate.getSubjectPublicKeyInfo()));
        }

        // Signer
        ContentSigner signer = new JcaContentSignerBuilder("SHA512WithRSAEncryption")
                .setProvider(Constants.JCA_PROVIDER).build(authorityKey == null ? privateKey : authorityKey);

        // Go
        holder = gen.build(signer);
    }

    // Data that can be accessed
    public OutputStream getPemPublicFile() {
        if (pemPublicFile == null) {
            generatePem();
        }
        return pemPublicFile;
    }

    public OutputStream getPemPrivateFile() {
        if (pemPrivateFile == null) {
            generatePem();
        }
        return pemPrivateFile;
    }

    public X509CertificateHolder getHolder() {
        return holder;
    }

    public PublicKey getPublicKey() {
        return publicKey;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }
}