se.tillvaxtverket.tsltrust.webservice.daemon.ca.CertificationAuthority.java Source code

Java tutorial

Introduction

Here is the source code for se.tillvaxtverket.tsltrust.webservice.daemon.ca.CertificationAuthority.java

Source

/*
 * Copyright 2017 Swedish E-identification Board (E-legitimationsnmnden)
 *         
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package se.tillvaxtverket.tsltrust.webservice.daemon.ca;

import com.aaasec.lib.aaacert.AaaCRL;
import com.aaasec.lib.aaacert.AaaCertificate;
import com.aaasec.lib.aaacert.CertFactory;
import com.aaasec.lib.aaacert.data.CRLEntryData;
import com.aaasec.lib.aaacert.data.CertRequestModel;
import com.aaasec.lib.aaacert.enums.OidName;
import com.aaasec.lib.aaacert.enums.SupportedExtension;
import com.aaasec.lib.aaacert.extension.ExtensionInfo;
import com.aaasec.lib.aaacert.utils.CertUtils;
import se.tillvaxtverket.tsltrust.common.utils.core.FnvHash;
import se.tillvaxtverket.tsltrust.common.utils.general.FileOps;
import se.tillvaxtverket.tsltrust.weblogic.data.DbCALog;
import se.tillvaxtverket.tsltrust.weblogic.data.DbCAParam;
import se.tillvaxtverket.tsltrust.weblogic.data.DbCert;
import se.tillvaxtverket.tsltrust.weblogic.db.CaSQLiteUtil;
import se.tillvaxtverket.tsltrust.weblogic.models.TslTrustModel;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Date;
import java.util.Iterator;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.CRLNumber;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.asn1.x509.CertificatePolicies;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.bouncycastle.asn1.x509.PolicyQualifierInfo;
import org.bouncycastle.cert.X509ExtensionUtils;
import org.bouncycastle.cert.X509CRLEntryHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import se.tillvaxtverket.tsltrust.weblogic.models.TslTrustConfig;

/**
 * This class implements the Certification Authority logic of TSL Trust.
 * Certificates compliant with defined policies are certified under a defined
 * policy Root created by TSL Trust.
 */
public class CertificationAuthority implements CaKeyStoreConstants {

    private static final Logger LOG = Logger.getLogger(CertificationAuthority.class.getName());
    private KeyStore key_store;
    private final File keyStoreFile;
    private final String caName;
    private final String caID;
    private AaaCertificate caRoot = null;
    private X500Principal rootIssuer;
    private boolean initialized = false;
    private final String caDir;
    private long nextSerial;
    private final File crlFile;
    private final File exportCrlFile;
    private final String crlDpUrl;
    private X509CRLHolder latestCrl = null;
    long crlValPeriod;

    public CertificationAuthority(String cAName, String caDir, TslTrustModel model) {
        this.caName = cAName;
        this.caID = FnvHash.getFNV1aToHex(caName);
        this.caDir = caDir;
        TslTrustConfig conf = (TslTrustConfig) model.getConf();
        crlValPeriod = model.getTslRefreshDelay() * 2 + (1000 * 60 * 60); //Make CRLs last the update period x 2 + 1 hour;
        keyStoreFile = new File(this.caDir, "ca.keystore");
        crlFile = new File(caDir, caID + ".crl");
        exportCrlFile = new File(FileOps.getfileNameString(conf.getCaFileStorageLocation(), "crl"), caID + ".crl");
        crlDpUrl = FileOps.getfileNameString(conf.getCaDistributionURL(), "crl/" + caID + ".crl");

    }

    public boolean initKeyStore() {
        try {
            if (keyStoreFile.canRead()) {
                key_store = KeyStore.getInstance("JKS");
                //                key_store = KeyStore.getInstance("IAIKKeyStore", "IAIK");
                key_store.load(new FileInputStream(keyStoreFile), KS_PASSWORD);
                if (crlFile.canRead()) {
                    latestCrl = new AaaCRL(crlFile).getCrl();
                }
                AaaCertificate root = getSelfSignedCert();
                if (root != null) {
                    initialized = true;
                }
            }
        } catch (Exception ex) {
            LOG.log(Level.WARNING, null, ex);
        }
        return initialized;
    }

    public AaaCertificate getSelfSignedCert() {
        try {
            AaaCertificate cert = new AaaCertificate(key_store.getCertificate(ROOT).getEncoded());
            caRoot = cert;
        } catch (Exception ex) {
            LOG.warning(ex.getMessage());
            caRoot = null;
        }
        return caRoot;
    }

    public String getCaName() {
        return caName;
    }

    public String getCaDir() {
        return caDir;
    }

    public String getCaID() {
        return caID;
    }

    public boolean isInitialized() {
        return initialized;
    }

    public X509CRLHolder getLatestCrl() {
        return latestCrl;
    }

    public File getExportCrlFile() {
        return exportCrlFile;
    }

    public AaaCertificate issueXCert(AaaCertificate orgCert) throws IOException {

        DbCAParam cp = CaSQLiteUtil.getParameter(caDir, CERT_SERIAL_KEY);
        if (cp == null) {
            return null;
        }
        nextSerial = cp.getIntValue();

        BigInteger certSerial = BigInteger.valueOf(nextSerial);
        List<Extension> extList = new ArrayList<>();
        Iterator<ExtensionInfo> e = orgCert.getExtensionInfoList().iterator();

        //System.out.println("Original cert extensions:");
        //Get extensions form orgCert
        boolean policy = false;
        if (e != null) {
            while (e.hasNext()) {
                ExtensionInfo ext = e.next();
                //System.out.println(ext.getObjectID().getNameAndID() + " " + ext.toString());
                //Replace policy with AnyPolicy
                if (ext.getExtensionType().equals(SupportedExtension.certificatePolicies)) {
                    CertificatePolicies cpe = getAnyCertificatePolicies();
                    ext.setExtDataASN1(cpe.toASN1Primitive());
                    ext.setExtData(cpe.getEncoded());
                    policy = true;
                }

                switch (ext.getExtensionType()) {
                case cRLDistributionPoints:
                case basicConstraints:
                case authorityInfoAccess:
                case authorityKeyIdentifier:
                case policyConstraints:
                case policyMappings:
                case qCStatements:
                    break;
                default:
                    if (ext.getOid().getId().equalsIgnoreCase("1.3.6.1.4.1.8301.3.5")) {
                        // German signature law validation rules
                        break;
                    }
                    extList.add(new Extension(ext.getOid(), ext.isCritical(), ext.getExtData()));

                }

            }
        } else {
            extList.add(
                    new Extension(Extension.basicConstraints, false, new BasicConstraints(true).getEncoded("DER")));
            policy = false;
        }
        // If no policy in orgCert then add AnyPolicy to list
        if (!policy) {
            CertificatePolicies cpe = getAnyCertificatePolicies();
            extList.add(new Extension(Extension.certificatePolicies, false, cpe.getEncoded("DER")));
        }

        //Copy to extension list
        //        V3Extension[] extensions = new V3Extension[extList.size()];
        //        for (int i = 0; i < extList.size(); i++) {
        //            V3Extension ext = extList.get(i);
        //            extensions[i] = ext;
        //        }
        AaaCertificate xCert = createCertificate(orgCert, certSerial, caRoot, CertFactory.SHA256WITHRSA, extList);
        //System.out.println((char) 10 + "Issued XCert" + (char) 10 + xCert.toString(true));
        CaSQLiteUtil.addCertificate(xCert, caDir);

        //update log 
        DbCALog caLog = new DbCALog();
        caLog.setLogCode(ISSUE_EVENT);
        caLog.setEventString("Certificate issued");
        caLog.setLogParameter(nextSerial);
        caLog.setLogTime(System.currentTimeMillis());
        CaSQLiteUtil.addCertLog(caLog, caDir);

        //Store next serial number
        cp.setIntValue(nextSerial + 1);
        CaSQLiteUtil.storeParameter(cp, caDir);
        return xCert;
    }

    public AaaCertificate createCertificate(AaaCertificate orgCert, BigInteger certSerial,
            AaaCertificate issuerCert, String algorithm, List<Extension> extensions) {

        AaaCertificate cert = null;
        // create a new certificate
        try {
            CertRequestModel reqModel = new CertRequestModel();
            reqModel.setIssuerDN(issuerCert.getSubject());
            reqModel.setPublicKey(orgCert.getCert().getPublicKey());
            reqModel.setSerialNumber(certSerial);
            reqModel.setSubjectDN(orgCert.getSubject());
            reqModel.setNotBefore(orgCert.getNotBefore());
            if (issuerCert.getNotAfter().after(orgCert.getNotAfter())) {
                reqModel.setNotAfter(orgCert.getNotAfter());
            } else {
                reqModel.setNotAfter(issuerCert.getNotAfter());
            }

            // Add AKI
            X509ExtensionUtils extUtil = CertUtils.getX509ExtensionUtils();
            AuthorityKeyIdentifier aki = extUtil.createAuthorityKeyIdentifier(issuerCert);
            extensions.add(new Extension(Extension.authorityKeyIdentifier, false, aki.getEncoded("DER")));

            DistributionPoint dp = new DistributionPoint(
                    new DistributionPointName(
                            new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, crlDpUrl))),
                    null, null);
            CRLDistPoint cdp = new CRLDistPoint(new DistributionPoint[] { dp });
            extensions.add(new Extension(Extension.cRLDistributionPoints, false, cdp.getEncoded("DER")));

            reqModel.setExtensionList(extensions);
            reqModel.setSigner(
                    new JcaContentSignerBuilder(algorithm).build((PrivateKey) key_store.getKey(ROOT, KS_PASSWORD)));

            cert = new AaaCertificate(reqModel);
        } catch (Exception ex) {
            cert = null;
            LOG.warning("Error creating the certificate: " + ex.getMessage());
        }

        return cert;
    }

    /**
     * Add the private key and the certificate chain to the key store.
     */
    public void addToKeyStore(KeyPair keyPair, X509Certificate[] chain, String alias) throws KeyStoreException {
        key_store.setKeyEntry(alias, keyPair.getPrivate(), KS_PASSWORD, chain);
    }

    private void saveKeyStore() {
        try {
            // write the KeyStore to disk
            FileOutputStream os = new FileOutputStream(keyStoreFile);
            key_store.store(os, KS_PASSWORD);
            os.close();
        } catch (Exception ex) {
            LOG.warning("Error saving KeyStore! " + ex.getMessage());
        }
    }

    private CertificatePolicies getDefCertificatePolicies() {
        PolicyQualifierInfo policyQualifierInfo = CertUtils
                .getUserNotice("This certificate may be used for demonstration purposes only.");
        ASN1EncodableVector pqSeq = new ASN1EncodableVector();
        pqSeq.add(policyQualifierInfo);
        PolicyInformation policyInformation = new PolicyInformation(
                new ASN1ObjectIdentifier("1.3.6.1.4.1.2706.2.2.1.1.1.1.1"), new DERSequence(pqSeq));
        CertificatePolicies certificatePolicies = new CertificatePolicies(
                new PolicyInformation[] { policyInformation });
        return certificatePolicies;
    }

    private CertificatePolicies getAnyCertificatePolicies() {
        PolicyInformation policyInformation = new PolicyInformation(
                new ASN1ObjectIdentifier(OidName.cp_anyPolicy.getOid()), null);
        CertificatePolicies certificatePolicies = new CertificatePolicies(
                new PolicyInformation[] { policyInformation });
        return certificatePolicies;
    }

    public X509CRLHolder revokeCertificates() {
        long currentTime = System.currentTimeMillis();
        long nextUpdateTime = currentTime + crlValPeriod;
        List<DbCert> certList = CaSQLiteUtil.getCertificates(caDir, true);

        DbCAParam cp = CaSQLiteUtil.getParameter(caDir, CRL_SERIAL_KEY);
        if (cp == null) {
            return null;
        }
        long nextCrlSerial = cp.getIntValue();

        try {

            AaaCRL crl = new AaaCRL(new Date(currentTime), new Date(nextUpdateTime), caRoot,
                    (PrivateKey) key_store.getKey(ROOT, KS_PASSWORD), CertFactory.SHA256WITHRSA, crlFile);

            List<Extension> extList = new ArrayList<Extension>();
            // Add AKI
            X509ExtensionUtils extu = CertUtils.getX509ExtensionUtils();
            AuthorityKeyIdentifier aki = extu.createAuthorityKeyIdentifier(caRoot);
            extList.add(new Extension(Extension.authorityKeyIdentifier, false, aki.getEncoded("DER")));

            // CRLNumber to be adjusted to an incremental number
            CRLNumber crlNumber = new CRLNumber(BigInteger.valueOf(nextCrlSerial));
            extList.add(new Extension(Extension.cRLNumber, false, crlNumber.getEncoded("DER")));

            GeneralNames distributionPointName = new GeneralNames(
                    new GeneralName(GeneralName.uniformResourceIdentifier, crlDpUrl));
            DistributionPointName dpn = new DistributionPointName(distributionPointName);
            IssuingDistributionPoint idp = new IssuingDistributionPoint(dpn, false, false);
            extList.add(new Extension(Extension.issuingDistributionPoint, true, idp.getEncoded("DER")));

            // IssuingDistributionPoint
            List<CRLEntryData> crlEdList = new ArrayList<>();

            certList.forEach((dbCert) -> {
                Date revTime = new Date();
                BigInteger serialNumber = dbCert.getCertificate().getSerialNumber();
                crlEdList.add(new CRLEntryData(serialNumber, new Date(dbCert.getRevDate()),
                        CRLReason.privilegeWithdrawn));
            });

            crl.updateCrl(new Date(currentTime), new Date(nextUpdateTime), crlEdList, extList);

            logRevocation(certList);

            // receive CRL
            latestCrl = crl.getCrl();
            cp.setIntValue(nextCrlSerial + 1);
            CaSQLiteUtil.storeParameter(cp, caDir);
            // Store CRL
            FileOps.saveByteFile(FileOps.readBinaryFile(crlFile), exportCrlFile);
            return latestCrl;

        } catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException
                | CRLException | CertificateException | OperatorCreationException ex) {
            LOG.warning(ex.getMessage());
            return null;
        }
    }

    public void logRevocation(List<DbCert> revCertList) {
        List<Long> crlList = new LinkedList<Long>();
        if (latestCrl != null) {
            X509CRLEntryHolder revokedCertificate = latestCrl.getRevokedCertificate(BigInteger.ZERO);
            latestCrl.getRevokedCertificates().iterator().forEachRemaining((crlEntryObj) -> {
                X509CRLEntryHolder crlEntry = (X509CRLEntryHolder) crlEntryObj;
                long revokedSerial = crlEntry.getSerialNumber().longValue();
                crlList.add(revokedSerial);
            });
        }

        for (DbCert dbCert : revCertList) {
            if (!crlList.contains((long) dbCert.getSerial())) {
                //update log 
                DbCALog caLog = new DbCALog();
                caLog.setLogCode(REVOKE_EVENT);
                caLog.setEventString("Certificate revoked");
                caLog.setLogParameter(dbCert.getSerial() * 256 + CRLReason.privilegeWithdrawn);
                caLog.setLogTime(dbCert.getRevDate());
                CaSQLiteUtil.addCertLog(caLog, caDir);
            }
        }
    }

    public List<DbCert> getAllCertificates() {
        return CaSQLiteUtil.getCertificates(caDir);
    }

    public List<DbCert> getAllCertificates(boolean revoked) {
        return CaSQLiteUtil.getCertificates(caDir, revoked);
    }

    public DbCert getCertificateBySerial(long serial) {
        return CaSQLiteUtil.getCertificates(caDir, serial);
    }

    public void replaceCertificateData(DbCert certData) {
        CaSQLiteUtil.replaceCertificate(certData, caDir);
    }

    public List<DbCALog> getCertLogs() {
        return CaSQLiteUtil.getCertLogs(caDir);
    }

    public List<DbCALog> getCertLogs(int eventType) {
        return CaSQLiteUtil.getCertLogs(caDir, eventType);
    }

    public String getFormatedLogList() {
        return getFormattedLogList(getCertLogs());
    }

    public String getFormattedLogList(int eventType) {
        return getFormattedLogList(getCertLogs(eventType));
    }

    public String getFormattedLogList(List<DbCALog> logs) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd   HH:mm:ss");
        GregorianCalendar gc = new GregorianCalendar();
        StringBuilder b = new StringBuilder();
        for (DbCALog log : logs) {
            gc.setTimeInMillis(log.getLogTime());
            b.append(dateFormat.format(gc.getTime()));
            b.append("    ");
            b.append(log.getEventString());
            b.append(" -- ");
            if (log.getLogCode() == ISSUE_EVENT) {
                b.append("Certificate Serial Number=").append(log.getLogParameter());
            }
            if (log.getLogCode() == REVOKE_EVENT) {
                b.append("Certificate Serial Number=").append(log.getLogParameter() / 256);
                b.append(", Revocation Reason=");
                long rc = log.getLogParameter() % 256;
                b.append((rc < 11) ? REV_REASON[(int) rc] : String.valueOf(rc));
            }
            b.append((char) 10);
        }
        return b.toString();
    }
}