CRLParams.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * 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 org.openspcoop2.utils.certificate.ocsp;

import java.security.cert.CertStore;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.openspcoop2.utils.LoggerBuffer;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.UtilsMultiException;
import org.openspcoop2.utils.certificate.ArchiveLoader;
import org.openspcoop2.utils.certificate.AuthorityInformationAccess;
import org.openspcoop2.utils.certificate.CRLCertstore;
import org.openspcoop2.utils.certificate.CRLDistributionPoint;
import org.openspcoop2.utils.certificate.CRLDistributionPoints;
import org.openspcoop2.utils.certificate.Certificate;
import org.openspcoop2.utils.certificate.CertificateInfo;
import org.openspcoop2.utils.certificate.KeyStore;
import org.openspcoop2.utils.certificate.KeystoreType;
import org.openspcoop2.utils.transport.TransportUtils;

/**
 * OCSPRequestParams
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class CRLParams {

	private KeyStore crlTrustStore;
	private CertStore crlCertstore;
		
	public KeyStore getCrlTrustStore() {
		return this.crlTrustStore;
	}
	public void setCrlTrustStore(KeyStore crlTrustStore) {
		this.crlTrustStore = crlTrustStore;
	}
	public CertStore getCrlCertstore() {
		return this.crlCertstore;
	}
	public void setCrlCertstore(CertStore crlCertstore) {
		this.crlCertstore = crlCertstore;
	}
	
	public static CRLParams build(LoggerBuffer log, X509Certificate certificate, String crlInputConfig, KeyStore trustStore, OCSPConfig config, IOCSPResourceReader reader) throws UtilsException {
		CertificateInfo cer = new CertificateInfo(certificate, "ocspVerifica");
		return build(log, cer, crlInputConfig, trustStore, config, reader);
	}
	public static CRLParams build(LoggerBuffer log, CertificateInfo certificate, String crlInputConfig, KeyStore trustStore, OCSPConfig config, IOCSPResourceReader reader) throws UtilsException {
	
		if(config==null) {
			throw new UtilsException("Param config is null");
		}
		if(certificate==null) {
			throw new UtilsException("Param certificate is null");
		}
		
		
		// comprendo path CRL
		List<String> crlPaths = new ArrayList<>();
		Map<String, byte[]> localResources = new HashMap<>();
		
		if(config.getCrlSource()!=null && !config.getCrlSource().isEmpty()) {
			
			List<Throwable> listExceptions = new ArrayList<>();
						
			for (CertificateSource s : config.getCrlSource()) {
				if(CertificateSource.CONFIG.equals(s)) {
					if(crlInputConfig!=null) {
						// รจ possibile fornire un'unica indicazione di una risorsa remota o in alternativa N file crl
						if(TransportUtils.isRemoteResource(crlInputConfig)){
							readCrlPath(crlInputConfig.trim(),
									crlPaths, localResources,
									listExceptions,
									reader,
									log);
						}
						else {
							List<String> l = CRLCertstore.readCrlPaths(crlInputConfig);
							if(l!=null && !l.isEmpty()) {
								for (String crlPath : l) {
									String p = crlPath.trim();
									if(!crlPaths.contains(p)) {
										crlPaths.add(p);
										log.debug("OCSP-CRL: add config path '"+p+"'");
									}
								}
							}
						}
					}
					if(!crlPaths.isEmpty()) {
						break;
					}
				}
				else if(CertificateSource.ALTERNATIVE_CONFIG.equals(s)) {
					List<String> l = null;
					if(config.getCrlAlternative()!=null) {
						l = config.getCrlAlternative();
					}
					if(l!=null && !l.isEmpty()) {
						for (String crlPath : l) {
							String p = crlPath.trim();
							if(!crlPaths.contains(p)) {
								crlPaths.add(p);
								log.debug("OCSP-CRL: add alternative config path '"+p+"'");
							}
						}
					}
					if(!crlPaths.isEmpty()) {
						break;
					}
				}
				else if(CertificateSource.AUTHORITY_INFORMATION_ACCESS.equals(s)) {
					try {
						CRLDistributionPoints crls = certificate.getCRLDistributionPoints();
						if(crls!=null) {
							List<CRLDistributionPoint> crlsList = crls.getCRLDistributionPoints();
							if(crlsList!=null && !crlsList.isEmpty()) {
								for (CRLDistributionPoint crlDP : crlsList) {
									List<String> l = crlDP.getDistributionPointNames();
									if(l!=null) {
										for (String crlPath : l) {
											readCrlPath(crlPath,
													crlPaths, localResources,
													listExceptions,
													reader,
													log);
										}
									}
								}
							}
						}						
					}catch(Exception t) {
						throw new UtilsException(t.getMessage(),t);
					}
					if(!crlPaths.isEmpty()) {
						break;
					}
				}
			}
			
			if(crlPaths.isEmpty()) {
				boolean reject = false;
				boolean isCA = false;
				try {
					isCA = certificate.isCA();
				}catch(Exception t) {
					throw new UtilsException(t.getMessage(),t);
				}
				if(isCA) {
					reject = config.isRejectsCAWithoutCRL();
				}
				else {
					reject = config.isRejectsCertificateWithoutCRL();
				}
				if(reject) {
					if(!listExceptions.isEmpty()) {
						if(listExceptions.size()==1) {
							Throwable t = listExceptions.get(0);
							if(t!=null) {
								throw new UtilsException("Crl retrieve failed; "+t.getMessage(),t);
							}
						}
						UtilsMultiException multi = new UtilsMultiException("Crl retrieve failed", listExceptions.toArray(new Throwable[1]));
						throw new UtilsException("Crl retrieve failed;\n"+multi.getMultiMessage(),multi);
					}
					else {
						throw new UtilsException("Crl retrieve failed");
					}
				}
			}
			
		}
		
		KeyStore crlTrustStore = null;
		if(!crlPaths.isEmpty() &&
			// comprendo issuer del certificato da validare
			config.getCrlTrustStoreSource()!=null && !config.getCrlTrustStoreSource().isEmpty()) {
			for (CertificateSource s : config.getCrlTrustStoreSource()) {
				if(CertificateSource.CONFIG.equals(s)) {
					if(trustStore!=null) {
						try {
							if(crlTrustStore==null) {
								java.security.KeyStore ks = java.security.KeyStore.getInstance(KeystoreType.JKS.getNome());
								ks.load(null, null);
								crlTrustStore = new KeyStore(ks);
							}
						}catch(Exception t) {
							String msgError = "OCSP-CRL [crlssuer: CONFIG] retrieve failed: "+t.getMessage();
							log.debug(msgError,t);
						}
						if(crlTrustStore!=null) {
							crlTrustStore.putAllCertificate(trustStore, false);
						}
						log.debug("OCSP-CRL: add certificate in truststore config");
					}
				}
				else if(CertificateSource.ALTERNATIVE_CONFIG.equals(s)) {
					KeyStore alternativeTrustStore = null;
					if(config.getAlternativeTrustStoreCRLPath()!=null) {
						alternativeTrustStore = reader.getCrlAlternativeTrustStore();
					}
					if(alternativeTrustStore!=null) {
						try {
							if(crlTrustStore==null) {
								java.security.KeyStore ks = java.security.KeyStore.getInstance(KeystoreType.JKS.getNome());
								ks.load(null, null);
								crlTrustStore = new KeyStore(ks);
							}
						}catch(Exception t) {
							String msgError = "OCSP-CRL [crlssuer: ALTERNATIVE_CONFIG] retrieve failed: "+t.getMessage();
							log.debug(msgError,t);
						}
						if(crlTrustStore!=null) {
							crlTrustStore.putAllCertificate(alternativeTrustStore, false);
						}
						log.debug("OCSP-CRL: add certificate in alternative truststore config");
					}
				}
				else if(CertificateSource.AUTHORITY_INFORMATION_ACCESS.equals(s)) {
					try {
						CRLDistributionPoints crls = certificate.getCRLDistributionPoints();
						if(crls!=null) {
							List<CRLDistributionPoint> crlsList = crls.getCRLDistributionPoints();
							int indexCrl = 0;
							if(crlsList!=null && !crlsList.isEmpty()) {
								for (CRLDistributionPoint crlDP : crlsList) {
									List<String> l = crlDP.getCRLIssuers();
									if(l!=null) {
										for (String crlPath : l) {
											if(!crlPaths.contains(crlPath)) {
												try {
													Map<String, byte[]> map = new HashMap<>();
													reader.readExternalResource(crlPath, map);
													byte [] issuer = null;
													if(!map.isEmpty()) {
														issuer = map.get(crlPath);
													}
													if(issuer==null || issuer.length<=0) {
														throw new UtilsException("empty resource");
													}
													Certificate cer = ArchiveLoader.load(issuer);
													if(cer!=null && cer.getCertificate()!=null) {
														if(crlTrustStore==null) {
															java.security.KeyStore ks = java.security.KeyStore.getInstance(KeystoreType.JKS.getNome());
															ks.load(null, null);
															crlTrustStore = new KeyStore(ks);
														}
														crlTrustStore.putCertificate("crlIssuerCert-"+indexCrl, cer.getCertificate().getCertificate(), false);
														indexCrl++;
														log.debug("OCSP-CRL: add certificate retrieved from '"+crlPath+"'");
													}
												}catch(Exception t) {
													String msgError = "OCSP-CRL [crlssuer: "+crlPath+"] retrieve failed: "+t.getMessage();
													log.debug(msgError,t);
												}
											}
										}
									}
								}
							}
						}						
					}catch(Exception t) {
						throw new UtilsException(t.getMessage(),t);
					}
					try {
						AuthorityInformationAccess aia = certificate.getAuthorityInformationAccess();
						if(aia!=null) {
							List<String> issuer = aia.getCAIssuers();
							int indexAia = 0;
							if(issuer!=null && !issuer.isEmpty()) {
								for (String urlIssuer : issuer) {
									Map<String, byte[]> map = new HashMap<>();
									reader.readExternalResource(urlIssuer, map);
									if(!map.isEmpty()) {
										byte [] issCert = map.get(urlIssuer);
										if(issCert!=null) {
											Certificate cer = ArchiveLoader.load(issCert);
											if(cer!=null && cer.getCertificate()!=null) {
												if(crlTrustStore==null) {
													java.security.KeyStore ks = java.security.KeyStore.getInstance(KeystoreType.JKS.getNome());
													ks.load(null, null);
													crlTrustStore = new KeyStore(ks);
												}
												crlTrustStore.putCertificate("aiaIssuerCert-"+indexAia, cer.getCertificate().getCertificate(), false);
												indexAia++;
												log.debug("OCSP-CRL-AIA: add certificate retrieved from '"+urlIssuer+"'");
											}
										}
									}
								}
							}
						}
					}catch(Exception t) {
						throw new UtilsException(t.getMessage(),t);
					}
				}
			}
			
		}
		
		CRLParams params = new CRLParams();
		if(!crlPaths.isEmpty()) {
			params.crlCertstore = reader.readCRL(crlPaths, localResources).getCertStore();
			
/**			boolean newTrustStore = false;
//			if(crlTrustStore==null) {
//				try {
//					java.security.KeyStore ks = java.security.KeyStore.getInstance(KeystoreType.JKS.getNome());
//					ks.load(null, null);
//					crlTrustStore = new KeyStore(ks);
//					newTrustStore = true;
//				}catch(Throwable t) {
//					throw new UtilsException(t.getMessage(),t);
//				}
//			}
//			
//			try {
//				if(certificate.isCA()) {
//					if(newTrustStore || (!params.crlTrustStore.existsCertificateBySubject(certificate.getCertificate().getSubjectX500Principal()))) {
//						crlTrustStore.putCertificate("ca", certificate.getCertificate(), false);
//					}
//				}
//			}catch(Throwable t) {
//				throw new UtilsException(t.getMessage(),t);
//			}*/
			
			params.crlTrustStore = crlTrustStore;
		}
		
		return params;
		
	}
	
	private static void readCrlPath(String crlPath,
			List<String> crlPaths,Map<String, byte[]> localResources,
			List<Throwable> listExceptions,
			IOCSPResourceReader reader,
			LoggerBuffer log) {
		if(!crlPaths.contains(crlPath)) {
			try {
				Map<String, byte[]> map = new HashMap<>();
				reader.readExternalResource(crlPath, map);
				byte [] crl = null;
				if(!map.isEmpty()) {
					crl = map.get(crlPath);
				}
				if(crl==null || crl.length<=0) {
					throw new UtilsException("empty resource");
				}
				localResources.put(crlPath, crl);
				crlPaths.add(crlPath);
				log.debug("OCSP-CRL: add external resource '"+crlPath+"'");
			}catch(Exception t) {
				String msgError = "[crl: "+crlPath+"] retrieve failed: "+t.getMessage();
				log.debug("OCSP-CRL "+msgError,t);
				listExceptions.add(new Exception(msgError,t));
			}
		}
	}
}