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

eu.europa.esig.dss.client.crl.JdbcCacheCRLSource Maven / Gradle / Ivy

/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 *
 * This file is part of the "DSS - Digital Signature Services" project.
 *
 * This library 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 (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package eu.europa.esig.dss.client.crl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Date;
import java.util.List;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.europa.esig.dss.DSSASN1Utils;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.SignatureAlgorithm;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.x509.CertificateToken;
import eu.europa.esig.dss.x509.crl.CRLSource;
import eu.europa.esig.dss.x509.crl.CRLToken;
import eu.europa.esig.dss.x509.crl.CRLValidity;

/**
 * CRLSource that retrieve information from a JDBC datasource
 */
public class JdbcCacheCRLSource implements CRLSource {

	private static final Logger LOG = LoggerFactory.getLogger(JdbcCacheCRLSource.class);

	/**
	 * used in the init method to check if the table exists
	 */
	private static final String SQL_INIT_CHECK_EXISTENCE = "SELECT COUNT(*) FROM CACHED_CRL";

	/**
	 * used in the init method to create the table, if not existing: ID (char40 = SHA1 length) and DATA (blob)
	 */
	private static final String SQL_INIT_CREATE_TABLE = "CREATE TABLE CACHED_CRL (ID CHAR(40), DATA LONGVARBINARY, SIGNATURE_ALGORITHM VARCHAR(20), THIS_UPDATE TIMESTAMP, NEXT_UPDATE TIMESTAMP, EXPIRED_CERTS_ON_CRL TIMESTAMP, ISSUER LONGVARBINARY, ISSUER_PRINCIPAL_MATCH BOOLEAN, SIGNATURE_INTACT BOOLEAN, CRL_SIGN_KEY_USAGE BOOLEAN, UNKNOWN_CRITICAL_EXTENSION BOOLEAN, SIGNATURE_INVALID_REASON VARCHAR(256))";

	/**
	 * used in the find method to select the crl via the id
	 */
	private static final String SQL_FIND_QUERY = "SELECT * FROM CACHED_CRL WHERE ID = ?";

	/**
	 * used in the find method when selecting the crl via the id to get the ID (char20) from the resultset
	 */
	private static final String SQL_FIND_QUERY_ID = "ID";

	/**
	 * used in the find method when selecting the crl via the id to get the DATA (blob) from the resultset
	 */
	private static final String SQL_FIND_QUERY_DATA = "DATA";

	/**
	 * used in the find method when selecting the issuer certificate via the id to get the ISSUER (blob) from the
	 * resultset
	 */
	private static final String SQL_FIND_QUERY_ISSUER = "ISSUER";

	private static final String SQL_FIND_QUERY_THIS_UPDATE = "THIS_UPDATE";

	private static final String SQL_FIND_QUERY_NEXT_UPDATE = "NEXT_UPDATE";

	private static final String SQL_FIND_QUERY_EXPIRED_CERTS_ON_CRL = "EXPIRED_CERTS_ON_CRL";

	private static final String SQL_FIND_QUERY_SIGNATURE_ALGO = "SIGNATURE_ALGORITHM";

	private static final String SQL_FIND_QUERY_ISSUER_PRINCIPAL_MATCH = "ISSUER_PRINCIPAL_MATCH";

	private static final String SQL_FIND_QUERY_SIGNATURE_INTACT = "SIGNATURE_INTACT";

	private static final String SQL_FIND_QUERY_CRL_SIGN_KEY_USAGE = "CRL_SIGN_KEY_USAGE";

	private static final String SQL_FIND_QUERY_UNKNOWN_CRITICAL_EXTENSION = "UNKNOWN_CRITICAL_EXTENSION";

	private static final String SQL_FIND_QUERY_SIGNATURE_INVALID_REASON = "SIGNATURE_INVALID_REASON";

	/**
	 * used via the find method to insert a new record
	 */
	private static final String SQL_FIND_INSERT = "INSERT INTO CACHED_CRL (ID, DATA, SIGNATURE_ALGORITHM, THIS_UPDATE, NEXT_UPDATE, EXPIRED_CERTS_ON_CRL, ISSUER, ISSUER_PRINCIPAL_MATCH, SIGNATURE_INTACT, CRL_SIGN_KEY_USAGE, UNKNOWN_CRITICAL_EXTENSION, SIGNATURE_INVALID_REASON) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

	/**
	 * used via the find method to update an existing record via the id
	 */
	private static final String SQL_FIND_UPDATE = "UPDATE CACHED_CRL SET DATA = ?, SIGNATURE_ALGORITHM = ?, THIS_UPDATE = ?, NEXT_UPDATE = ?, EXPIRED_CERTS_ON_CRL = ?, ISSUER = ?, ISSUER_PRINCIPAL_MATCH = ?, SIGNATURE_INTACT = ?, CRL_SIGN_KEY_USAGE = ?, UNKNOWN_CRITICAL_EXTENSION = ?, SIGNATURE_INVALID_REASON = ?  WHERE ID = ?";

	private OnlineCRLSource cachedSource;

	private DataSource dataSource;

	/**
	 * The default constructor for JdbcCRLSource.
	 */
	public JdbcCacheCRLSource() {
	}

	@Override
	public CRLToken findCrl(final CertificateToken certificateToken) throws DSSException {
		if (certificateToken == null) {
			return null;
		}
		final CertificateToken issuerToken = certificateToken.getIssuerToken();
		if (issuerToken == null) {
			return null;
		}
		final List crlUrls = DSSASN1Utils.getCrlUrls(certificateToken);
		if (Utils.isCollectionEmpty(crlUrls)) {
			return null;
		}
		final String crlUrl = crlUrls.get(0);
		LOG.info("CRL's URL for " + certificateToken.getAbbreviation() + " : " + crlUrl);
		try {

			final String key = DSSUtils.getSHA1Digest(crlUrl);
			final CRLValidity storedValidity = findCrlInDB(key);
			if (storedValidity != null) {
				if (storedValidity.getNextUpdate().after(new Date())) {
					LOG.debug("CRL in cache");
					final CRLToken crlToken = new CRLToken(certificateToken, storedValidity);
					crlToken.setSourceURL(crlUrl);
					if (crlToken.isValid()) {
						return crlToken;
					}
				}
			}
			final CRLToken crlToken = cachedSource.findCrl(certificateToken);
			if ((crlToken != null) && crlToken.isValid()) {
				if (storedValidity == null) {
					LOG.info("CRL '{}' not in cache", crlUrl);
					insertCrlInDb(key, crlToken.getCrlValidity());
				} else {
					LOG.debug("CRL '{}' expired", crlUrl);
					updateCrlInDb(key, crlToken.getCrlValidity());
				}
			}
			return crlToken;
		} catch (SQLException e) {
			LOG.info("Error with the cache data store", e);
		}
		return null;
	}

	/**
	 * @param cachedSource
	 *            the cachedSource to set
	 */
	public void setCachedSource(OnlineCRLSource cachedSource) {
		this.cachedSource = cachedSource;
	}

	/**
	 * Initialise the DAO by creating the table if it does not exist.
	 *
	 * @throws Exception
	 */
	private void initDao() throws Exception {
		/* Create the table if it doesn't exist. */
		if (!tableExists()) {
			createTable();
		}
	}

	/**
	 * Create the cache crl table if it does not exist
	 *
	 * @throws java.sql.SQLException
	 */
	private void createTable() throws SQLException {
		Connection c = null;
		Statement s = null;
		try {
			c = getDataSource().getConnection();
			s = c.createStatement();
			s.executeQuery(SQL_INIT_CREATE_TABLE);
			c.commit();
		} finally {
			closeQuietly(c, s, null);
		}
	}

	/**
	 * Check if the cache table exists
	 *
	 * @return true if the table exists.
	 */
	private boolean tableExists() {
		Connection c = null;
		Statement s = null;
		boolean tableExists;
		try {
			c = getDataSource().getConnection();
			s = c.createStatement();
			s.executeQuery(SQL_INIT_CHECK_EXISTENCE);
			tableExists = true;
		} catch (SQLException e) {
			tableExists = false;
		} finally {
			closeQuietly(c, s, null);
		}
		return tableExists;
	}

	/**
	 * Get the cached CRL from the datasource
	 *
	 * @param key
	 *            the key of the CRL
	 * @return the cached crl
	 * @throws java.sql.SQLException
	 */
	private CRLValidity findCrlInDB(String key) throws SQLException {
		Connection c = null;
		PreparedStatement s = null;
		ResultSet rs = null;
		try {
			c = getDataSource().getConnection();
			s = c.prepareStatement(SQL_FIND_QUERY);
			s.setString(1, key);
			rs = s.executeQuery();
			if (rs.next()) {
				CRLValidity cached = new CRLValidity();
				cached.setKey(rs.getString(SQL_FIND_QUERY_ID));
				cached.setCrlEncoded(rs.getBytes(SQL_FIND_QUERY_DATA));
				cached.setSignatureAlgorithm(SignatureAlgorithm.valueOf(rs.getString(SQL_FIND_QUERY_SIGNATURE_ALGO)));
				cached.setThisUpdate(rs.getTimestamp(SQL_FIND_QUERY_THIS_UPDATE));
				cached.setNextUpdate(rs.getTimestamp(SQL_FIND_QUERY_NEXT_UPDATE));
				cached.setExpiredCertsOnCRL(rs.getTimestamp(SQL_FIND_QUERY_EXPIRED_CERTS_ON_CRL));
				cached.setIssuerToken(DSSUtils.loadCertificate(rs.getBytes(SQL_FIND_QUERY_ISSUER)));
				cached.setCrlSignKeyUsage(rs.getBoolean(SQL_FIND_QUERY_CRL_SIGN_KEY_USAGE));
				cached.setUnknownCriticalExtension(rs.getBoolean(SQL_FIND_QUERY_UNKNOWN_CRITICAL_EXTENSION));
				cached.setIssuerX509PrincipalMatches(rs.getBoolean(SQL_FIND_QUERY_ISSUER_PRINCIPAL_MATCH));
				cached.setSignatureIntact(rs.getBoolean(SQL_FIND_QUERY_SIGNATURE_INTACT));
				cached.setSignatureInvalidityReason(rs.getString(SQL_FIND_QUERY_SIGNATURE_INVALID_REASON));
				return cached;
			}
		} finally {
			closeQuietly(c, s, rs);
		}

		return null;
	}

	/**
	 * Insert a new CRL into the cache
	 *
	 * @param key
	 *            the key
	 * @param encoded
	 *            the encoded CRL
	 * @throws java.sql.SQLException
	 */
	private void insertCrlInDb(String key, CRLValidity token) throws SQLException {
		Connection c = null;
		PreparedStatement s = null;
		ResultSet rs = null;
		try {
			c = getDataSource().getConnection();
			s = c.prepareStatement(SQL_FIND_INSERT);

			s.setString(1, key);

			s.setBytes(2, token.getCrlEncoded());

			s.setString(3, token.getSignatureAlgorithm().name());

			if (token.getThisUpdate() != null) {
				s.setTimestamp(4, new Timestamp(token.getThisUpdate().getTime()));
			} else {
				s.setNull(4, Types.TIMESTAMP);
			}

			if (token.getNextUpdate() != null) {
				s.setTimestamp(5, new Timestamp(token.getNextUpdate().getTime()));
			} else {
				s.setNull(5, Types.TIMESTAMP);
			}

			if (token.getExpiredCertsOnCRL() != null) {
				s.setTimestamp(6, new Timestamp(token.getExpiredCertsOnCRL().getTime()));
			} else {
				s.setNull(6, Types.TIMESTAMP);
			}

			s.setBytes(7, token.getIssuerToken().getEncoded());
			s.setBoolean(8, token.isIssuerX509PrincipalMatches());
			s.setBoolean(9, token.isSignatureIntact());
			s.setBoolean(10, token.isCrlSignKeyUsage());
			s.setBoolean(11, token.isUnknownCriticalExtension());
			s.setString(12, token.getSignatureInvalidityReason());
			s.executeUpdate();
		} finally {
			closeQuietly(c, s, rs);
		}
	}

	/**
	 * Update the cache with the CRL
	 *
	 * @param key
	 *            the key
	 * @param encoded
	 *            the encoded CRL
	 * @throws java.sql.SQLException
	 */
	private void updateCrlInDb(String key, CRLValidity token) throws SQLException {
		Connection c = null;
		PreparedStatement s = null;
		ResultSet rs = null;
		try {
			c = getDataSource().getConnection();
			s = c.prepareStatement(SQL_FIND_UPDATE);
			s.setBytes(1, token.getCrlEncoded());

			s.setString(2, token.getSignatureAlgorithm().name());

			if (token.getThisUpdate() != null) {
				s.setTimestamp(3, new Timestamp(token.getThisUpdate().getTime()));
			} else {
				s.setNull(3, Types.TIMESTAMP);
			}

			if (token.getNextUpdate() != null) {
				s.setTimestamp(4, new Timestamp(token.getNextUpdate().getTime()));
			} else {
				s.setNull(4, Types.TIMESTAMP);
			}

			if (token.getExpiredCertsOnCRL() != null) {
				s.setTimestamp(5, new Timestamp(token.getExpiredCertsOnCRL().getTime()));
			} else {
				s.setNull(5, Types.TIMESTAMP);
			}

			s.setBytes(6, token.getIssuerToken().getEncoded());
			s.setBoolean(7, token.isIssuerX509PrincipalMatches());
			s.setBoolean(8, token.isSignatureIntact());
			s.setBoolean(9, token.isCrlSignKeyUsage());
			s.setBoolean(10, token.isUnknownCriticalExtension());
			s.setString(11, token.getSignatureInvalidityReason());

			s.setString(12, key);
			s.executeUpdate();
		} finally {
			closeQuietly(c, s, rs);
		}

	}

	/**
	 * @return the dataSource
	 */
	private DataSource getDataSource() {
		return dataSource;
	}

	/**
	 * @param dataSource
	 *            the dataSource to set
	 * @throws Exception
	 */
	public void setDataSource(DataSource dataSource) throws Exception {
		this.dataSource = dataSource;
		initDao();
	}

	/**
	 * Close the statement and connection and resultset without throwing the exception
	 *
	 * @param c
	 *            the connection
	 * @param s
	 *            the statement
	 * @param rs
	 *            the ResultSet
	 */
	private void closeQuietly(Connection c, Statement s, ResultSet rs) {
		try {
			if (rs != null) {
				rs.close();
			}
			if (s != null) {
				s.close();
			}
			if (c != null) {
				c.close();
			}
		} catch (SQLException e) {
			// purposely empty
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy