org.dcache.util.CachingCertificateValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.util.CachingCertificateValidator.java

Source

/* dCache - http://www.dcache.org/
 *
 * Copyright (C) 2015 Deutsches Elektronen-Synchrotron
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.dcache.util;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheStats;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
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.X509CertChainValidatorExt;

import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Collections.singletonList;

/**
 * A Certificate validator that caches validation results for a configurable
 * period of time. The cache is keyed by the SHA256 of the certificate chain.
 */
public class CachingCertificateValidator implements X509CertChainValidatorExt {
    protected final Cache<String, ValidationResult> cache;
    protected final X509CertChainValidatorExt validator;

    public CachingCertificateValidator(X509CertChainValidatorExt val, long maxCacheEntryLifetime) {
        cache = CacheBuilder.newBuilder().expireAfterWrite(maxCacheEntryLifetime, TimeUnit.MILLISECONDS).build();
        validator = val;
    }

    @Override
    public ValidationResult validate(final X509Certificate[] certChain) {
        checkNotNull(certChain, "Cannot validate a null cert chain.");
        checkArgument(certChain.length > 0, "Cannot validate a cert chain of length 0.");

        int pos = 0;
        try {
            /* Check that the chain is still valid; would be nice if we instead could limit the lifetime of the cache
             * entry, but Guava doesn't allow us to do that.
             */
            Hasher hasher = Hashing.sha256().newHasher();
            for (X509Certificate cert : certChain) {
                cert.checkValidity();
                hasher.putBytes(cert.getEncoded());
                pos++;
            }

            String certFingerprint = hasher.hash().toString();

            return cache.get(certFingerprint, () -> validator.validate(certChain));
        } catch (CertificateEncodingException e) {
            return new ValidationResult(false, singletonList(
                    new ValidationError(certChain, pos, ValidationErrorCode.inputError, e.getMessage())));
        } catch (ExecutionException e) {
            return new ValidationResult(false, singletonList(
                    new ValidationError(certChain, pos, ValidationErrorCode.inputError, e.getMessage())));
        } catch (CertificateExpiredException e) {
            return new ValidationResult(false, singletonList(
                    new ValidationError(certChain, pos, ValidationErrorCode.certificateExpired, e.getMessage())));
        } catch (CertificateNotYetValidException e) {
            return new ValidationResult(false, singletonList(new ValidationError(certChain, pos,
                    ValidationErrorCode.certificateNotYetValid, e.getMessage())));
        }
    }

    public CacheStats stats() {
        return cache.stats();
    }

    @Override
    public void dispose() {
        validator.dispose();
    }

    @Override
    public ProxySupport getProxySupport() {
        return validator.getProxySupport();
    }

    @Override
    public ValidationResult validate(CertPath certPath) {
        return validator.validate(certPath);
    }

    @Override
    public RevocationParameters getRevocationCheckingMode() {
        return validator.getRevocationCheckingMode();
    }

    @Override
    public X509Certificate[] getTrustedIssuers() {
        return validator.getTrustedIssuers();
    }

    @Override
    public void addValidationListener(ValidationErrorListener listener) {
        validator.addValidationListener(listener);
    }

    @Override
    public void removeValidationListener(ValidationErrorListener listener) {
        validator.removeValidationListener(listener);
    }

    @Override
    public void addUpdateListener(StoreUpdateListener listener) {
        validator.addUpdateListener(listener);
    }

    @Override
    public void removeUpdateListener(StoreUpdateListener listener) {
        validator.removeUpdateListener(listener);
    }
}