/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE.txt file for licensing information. */ package eu.emi.security.authn.x509.helpers.pkipath; import java.security.cert.CertPath; import java.security.cert.CertStore; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import eu.emi.security.authn.x509.ProxySupport; import eu.emi.security.authn.x509.RevocationParameters; import eu.emi.security.authn.x509.StoreUpdateListener; import eu.emi.security.authn.x509.ValidationError; import eu.emi.security.authn.x509.ValidationErrorCode; import eu.emi.security.authn.x509.ValidationErrorListener; import eu.emi.security.authn.x509.ValidationResult; import eu.emi.security.authn.x509.X509CertChainValidator; import eu.emi.security.authn.x509.X509CertChainValidatorExt; import eu.emi.security.authn.x509.helpers.ObserversHandler; import eu.emi.security.authn.x509.helpers.crl.AbstractCRLStoreSPI; import eu.emi.security.authn.x509.helpers.crl.SimpleCRLStore; import eu.emi.security.authn.x509.helpers.trust.TrustAnchorStore; import eu.emi.security.authn.x509.impl.CertificateUtils; /** * Base implementation of {@link X509CertChainValidator}. * It is configured with {@link CertStore} providing CRLs and {@link TrustAnchorStore} * providing trusted CAs. The implementation validates certificates using * the {@link BCCertPathValidator}. * <p> * This class is thread safe and its extensions should also guarantee this. * * @author K. Benedyczak */ public abstract class AbstractValidator implements X509CertChainValidatorExt { static { CertificateUtils.configureSecProvider(); } protected Set<ValidationErrorListener> listeners; protected final ObserversHandler observers; private TrustAnchorStore caStore; private AbstractCRLStoreSPI crlStore; protected BCCertPathValidator validator; private ProxySupport proxySupport; private RevocationParameters revocationMode; protected boolean disposed; /** * Default constructor is available, the subclass must initialize the parent * with the init() method. Note that it is strongly suggested to call the init() method * from the child class constructor. * <p> * This is not a cleanest design possible but it is required as arguments to the init() * method require some code to be created in subclasses. Therefore we have a trade off: * a bit unclean design inside the library and a clean external API without factory methods. * @param initialListeners initial listeners */ public AbstractValidator(Collection<? extends StoreUpdateListener> initialListeners) { observers = new ObserversHandler(initialListeners); listeners = new LinkedHashSet<ValidationErrorListener>(); } /** * Use this method to initialize the parent from the extension class, if not using * the non-default constructor. * @param caStore CA store * @param crlStore CRL store * @param proxySupport proxy support * @param revocationCheckingMode revocation checking mode */ protected synchronized void init(TrustAnchorStore caStore, AbstractCRLStoreSPI crlStore, ProxySupport proxySupport, RevocationParameters revocationCheckingMode) { disposed = false; if (caStore != null) this.caStore = caStore; if (crlStore != null) this.crlStore = crlStore; this.validator = new BCCertPathValidator(); this.proxySupport = proxySupport; this.revocationMode = revocationCheckingMode; } /** * {@inheritDoc} */ @Override public ValidationResult validate(CertPath certPath) { List<? extends Certificate> certs = certPath.getCertificates(); X509Certificate[] certsA = new X509Certificate[certs.size()]; for (int i=0; i<certsA.length; i++) { Certificate c = certs.get(i); if (!(c instanceof X509Certificate)) throw new IllegalArgumentException("Can validate only " + "X509Certificate chains. Found instance of: " + c.getClass().getName()); certsA[i] = (X509Certificate) c; } return validate(certsA); } /** * {@inheritDoc} */ @Override public ValidationResult validate(X509Certificate[] certChain) { return validate(certChain, caStore.getTrustAnchors()); } protected ValidationResult validate(X509Certificate[] certChain, Set<TrustAnchor> anchors) { if (isDisposed()) throw new IllegalStateException("The validator instance was disposed"); ValidationResult result; try { result = validator.validate(certChain, getProxySupport() == ProxySupport.ALLOW, anchors, new SimpleCRLStore(crlStore), revocationMode, observers); } catch (CertificateException e) { e.printStackTrace(); ValidationError error = new ValidationError(certChain, -1, ValidationErrorCode.inputError, e.toString()); result = new ValidationResult(false, Collections.singletonList(error)); } if (!result.isValid()) { List<ValidationError> errors = result.getErrors(); processErrorList(errors); result.setErrors(errors); if (result.getErrors().size() == 0 && result.getUnresolvedCriticalExtensions().size() == 0) return new ValidationResult(true); } return result; } protected void processErrorList(List<ValidationError> errors) { for (int i=0; i<errors.size(); i++) { boolean res = notifyListeners(errors.get(i)); if (res) { errors.remove(i); i--; } } } /** * {@inheritDoc} */ @Override public synchronized X509Certificate[] getTrustedIssuers() { return caStore.getTrustedCertificates(); } /** * Notifies all registered listeners. * @param error validation error * @return true if the error should be ignored false otherwise. */ protected boolean notifyListeners(ValidationError error) { synchronized (listeners) { for (ValidationErrorListener listener: listeners) if (listener.onValidationError(error)) return true; } return false; } /** * {@inheritDoc} */ @Override public void addValidationListener(ValidationErrorListener listener) { synchronized (listeners) { listeners.add(listener); } } /** * {@inheritDoc} */ @Override public void removeValidationListener(ValidationErrorListener listener) { synchronized (listeners) { listeners.remove(listener); } } /** * {@inheritDoc} */ @Override public synchronized ProxySupport getProxySupport() { return proxySupport; } /** * {@inheritDoc} */ @Override public synchronized RevocationParameters getRevocationCheckingMode() { return revocationMode; } /** * {@inheritDoc} */ @Override public synchronized void dispose() { disposed = true; observers.removeAllObservers(); crlStore.dispose(); caStore.dispose(); } protected synchronized boolean isDisposed() { return disposed; } /** * {@inheritDoc} */ @Override public void addUpdateListener(StoreUpdateListener listener) { observers.addObserver(listener); } /** * {@inheritDoc} */ @Override public void removeUpdateListener(StoreUpdateListener listener) { observers.addObserver(listener); } }