blob: 5f21f2c5036bcce76ac1f58616d285b264bbc1aa [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.waveprotocol.wave.crypto;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.security.GeneralSecurityException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* A cert path (aka cert chain) validator that stores validation results in
* a cache. It will also first attempt to look up validation results in the
* cache, before performing a full-blown cert chain verification.
*/
public class CachedCertPathValidator implements WaveCertPathValidator {
private static final String VALIDATOR_TYPE = "PKIX";
private static final String CERTIFICATE_TYPE = "X.509";
// the cache that stores, for a limited amount of time, cert chain
// verification result.
private final VerifiedCertChainCache certPathCache;
// source for current time, so that expiration of certificates can be checked
private final TimeSource timeSource;
// set of trusted Certification Authorities
private final Set<TrustAnchor> trustRoots;
public CachedCertPathValidator(VerifiedCertChainCache certPathCache,
TimeSource timeSource, TrustRootsProvider trustRootsProvider) {
this.certPathCache = certPathCache;
this.timeSource = timeSource;
this.trustRoots = getTrustRoots(trustRootsProvider);
}
@Override
public void validate(List<? extends X509Certificate> certs) throws SignatureException {
if (!certPathCache.contains(certs)) {
validateNoCache(certs);
// we don't get here if certs didn't validate
certPathCache.add(certs);
}
}
private Set<TrustAnchor> getTrustRoots(TrustRootsProvider provider) {
List<TrustAnchor> anchors = Lists.newArrayList();
for (X509Certificate c : provider.getTrustRoots()) {
anchors.add(new TrustAnchor(c, null));
}
return ImmutableSet.copyOf(anchors);
}
private static WaveOidChecker WAVE_OID_CHECKER = new WaveOidChecker();
private void validateNoCache(List<? extends X509Certificate> certs)
throws SignatureException {
try {
CertPathValidator validator = CertPathValidator.getInstance(
VALIDATOR_TYPE);
PKIXParameters params = new PKIXParameters(trustRoots);
params.addCertPathChecker(WAVE_OID_CHECKER);
params.setDate(timeSource.now());
// turn off default revocation-checking mechanism
params.setRevocationEnabled(false);
// TODO: add a way for clients to add certificate revocation checks,
// perhaps by letting them pass in PKIXCertPathCheckers. This can also be
// useful to check for Wave-specific certificate extensions.
CertificateFactory certFactory = CertificateFactory.getInstance(
CERTIFICATE_TYPE);
CertPath certPath = certFactory.generateCertPath(certs);
validator.validate(certPath, params);
} catch (GeneralSecurityException e) {
throw new SignatureException("Certificate validation failure", e);
}
}
private static class WaveOidChecker extends PKIXCertPathChecker {
private static final String WAVE_OID = "1.3.6.1.4.1.11129.2.1.1";
private static final Set<String> SUPPORTED_EXTENSIONS =
ImmutableSet.of(WAVE_OID);
@Override
public void check(Certificate cert, Collection<String> unresolvedCritExts) {
// We know about the WAVE OID - if it's in the unresolved critical
// extensions, that should not cause an error. In fact, in the future
// we might _require_ this extension
unresolvedCritExts.remove(WAVE_OID);
}
@Override
public Set<String> getSupportedExtensions() {
return SUPPORTED_EXTENSIONS;
}
@Override
public void init(boolean forward) {
// nothing to do
}
@Override
public boolean isForwardCheckingSupported() {
return true;
}
}
}