All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cesecore.certificates.ca.internal.CaCertificateCache Maven / Gradle / Ivy

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.cesecore.certificates.ca.internal;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.log4j.Logger;
import org.cesecore.certificates.certificate.HashID;
import org.cesecore.config.OcspConfiguration;
import org.cesecore.util.Base64;
import org.cesecore.util.CertTools;


/**
 * A cache for storing CA certificates
 *
 * @version $Id: CaCertificateCache.java 27419 2017-12-05 13:18:18Z anatom $
 *
 */
public enum CaCertificateCache  {
    INSTANCE;
    
	private final Logger log = Logger.getLogger(CaCertificateCache.class);

    /** Mapping from subjectDN to key in the certs HashMap. */
    private Map certsFromSubjectDN = new HashMap();
    /** Mapping from issuerDN to key in the certs HashMap. */
    private Map> certsFromIssuerDN = new HashMap>();
    /** Mapping from subject key identifier to key in the certs HashMap. */
    private Map certsFromSubjectKeyIdentifier = new HashMap();
    /** All root certificates. */
    private Set rootCertificates = new HashSet();

	/** Cache time counter, set and used by loadCertificates */
	private long certValidTo = 0;

    public X509Certificate findLatestBySubjectDN(final HashID id) {
        final X509Certificate ret = certsFromSubjectDN.get(id.getKey());
        if (ret==null && log.isDebugEnabled()) {
            log.debug("Certificate not found from SubjectDN HashId in certsFromSubjectDN map. HashID=" + id.getB64());
        }
        return ret;
	}

	public X509Certificate[] findLatestByIssuerDN(final HashID id) {	    
        final Set sCert = certsFromIssuerDN.get(id.getKey());
        if (sCert == null || sCert.isEmpty()) {
            if (log.isDebugEnabled()) {
                log.debug("Certificate not found from IssuerDN HashId in certsFromIssuerDN map. HashID=" + id.getB64());
            }
            return null;
        }
        return sCert.toArray(new X509Certificate[sCert.size()]);

    }

    public X509Certificate[] getRootCertificates() {
        return rootCertificates.toArray(new X509Certificate[0]);
    }

    public X509Certificate findBySubjectKeyIdentifier(final HashID id) {
        final X509Certificate ret = certsFromSubjectKeyIdentifier.get(id.getKey());
        if (ret==null && log.isDebugEnabled()) {
            log.debug("Certificate not found from SubjectKeyIdentifier HashId in certsFromSubjectKeyIdentifier map. HashID=" + id.getB64());
        }
        return ret;
    }

    public boolean isCacheExpired() {
        return certValidTo < System.currentTimeMillis();
    }

	/** Loads CA certificates but holds a cache so it's reloaded only every five minutes (configurable).
	 *
	 * We keep this method as synchronized, it should not take more than a few microseconds to complete if the cache does not have
	 * to be reloaded. If the cache must be reloaded, we must wait for it anyway to not have ConcurrentModificationException.
	 * We also only want one single thread to do the rebuilding.
	 * 
	 * @return true if the cache was reloaded, false if cache wasn't expired.
	 */
    public synchronized void loadCertificates(final Collection certs) {
        if (log.isDebugEnabled()) {
            log.debug("Loaded " + (certs == null ? "0" : Integer.toString(certs.size())) + " ca certificates");
        }

        Map newCertsFromSubjectDN = new HashMap();
        Map> newCertsFromIssuerDN = new HashMap>();
        Map newCertsFromSubjectKeyIdentifier = new HashMap();
        Set newRootCertificates = new HashSet();
        for (final Certificate tmp : certs) {
            if (!(tmp instanceof X509Certificate)) {
                log.debug("Not adding CA certificate of type: " + tmp.getType());
                continue;
            }
            final X509Certificate cert = (X509Certificate) tmp;
            try { // test if certificate is OK. we have experienced that BC could decode a certificate that later on could not be used.
                final Integer key = HashID.getFromKeyID(cert).getKey();
                final X509Certificate pastCert = newCertsFromSubjectKeyIdentifier.get(key);
                // Add the entry if it's the first, or if it is more recent than the one existing (in that case replace it)
                if ( pastCert == null || (pastCert != null && CertTools.getNotBefore(cert).after(CertTools.getNotBefore(pastCert))) ) {
                    newCertsFromSubjectKeyIdentifier.put(key, cert);
                }
            } catch (Throwable t) { // NOPMD: catch all to not break with an error here.
                if (log.isDebugEnabled()) {
                    final StringWriter sw = new StringWriter();
                    final PrintWriter pw = new PrintWriter(sw);
                    pw.println("Erroneous certificate fetched from database.");
                    pw.println("The public key can not be extracted from the certificate.");
                    pw.println("Here follows a base64 encoding of the certificate:");
                    try {
                        final String b64encoded = new String(Base64.encode(cert.getEncoded()));
                        pw.println(CertTools.BEGIN_CERTIFICATE);
                        pw.println(b64encoded);
                        pw.println(CertTools.END_CERTIFICATE);
                    } catch (CertificateEncodingException e) {
                        pw.println("Not possible to encode certificate.");
                    }
                    pw.flush();
                    log.debug(sw.toString());
                }
                continue;
            }
            final Integer subjectDNKey = HashID.getFromSubjectDN(cert).getKey();
            // Check if we already have a certificate from this issuer in the HashMap.
            // We only want to store the latest cert from each issuer in this map
            final X509Certificate pastCert = newCertsFromSubjectDN.get(subjectDNKey);
            final boolean isLatest;
            if (pastCert != null) {
                if (CertTools.getNotBefore(cert).after(CertTools.getNotBefore(pastCert))) {
                    isLatest = true;
                } else {
                    isLatest = false;
                }
            } else {
                isLatest = true;
            }
            if (isLatest) {
                newCertsFromSubjectDN.put(subjectDNKey, cert);
                final Integer issuerDNKey = HashID.getFromIssuerDN(cert).getKey();
                if (!issuerDNKey.equals(subjectDNKey)) { // don't add roots to themselves
                    Set sIssuer = newCertsFromIssuerDN.get(issuerDNKey);
                    if (sIssuer == null) {
                        sIssuer = new HashSet();
                        newCertsFromIssuerDN.put(issuerDNKey, sIssuer);
                    }
                    sIssuer.add(cert);
                    sIssuer.remove(pastCert);
                } else {
                    newRootCertificates.add(cert);
                    newRootCertificates.remove(pastCert);
                }
            }
        }
        // Log what we have stored in the cache
        if (log.isDebugEnabled()) {
            final StringWriter sw = new StringWriter();
            final PrintWriter pw = new PrintWriter(sw, true);
            pw.println("Found the following CA certificates :");
            for (Entry key : newCertsFromSubjectKeyIdentifier.entrySet()) {
                final Certificate cert = key.getValue();
                pw.print(CertTools.getSubjectDN(cert));
                pw.print(',');
                pw.println(CertTools.getSerialNumberAsString(cert));
            }
            log.debug(sw);
        }
        //Replace the old caches
        certsFromSubjectKeyIdentifier = newCertsFromSubjectKeyIdentifier;
        certsFromIssuerDN = newCertsFromIssuerDN;
        certsFromSubjectDN = newCertsFromSubjectDN;
        rootCertificates = newRootCertificates;
        certValidTo = System.currentTimeMillis() + OcspConfiguration.getSigningCertsValidTimeInMilliseconds();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy