/************************************************************************* * * * CESeCore: CE Security Core * * * * This software 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 any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.cesecore.util.provider; import java.security.cert.CertPathValidatorException; import java.security.cert.Certificate; import java.security.cert.CertificateParsingException; import java.security.cert.PKIXCertPathChecker; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.bouncycastle.asn1.x509.Extension; import org.cesecore.util.CertTools; /** * Validate the EKUs of a leaf certificate. * * If a EKU extension exists and is marked as critical, this PKIXCertPathChecker will ensure that * at least the specified extended key usages are present. Additional EKUs will be ignored as long * as the required ones are there. * * @version $Id: EkuPKIXCertPathChecker.java 21415 2015-05-29 14:00:56Z anatom $ */ public class EkuPKIXCertPathChecker extends PKIXCertPathChecker { private static final Logger log = Logger.getLogger(EkuPKIXCertPathChecker.class); private static final List EMPTY = new ArrayList(0); private final List requiredKeyPurposeOids; /** * Create a new instance that will check a critical EKU of the leaf certificate. * * Parameters OIDs could be supplied using org.bouncycastle.asn1.x509.KeyPurposeId.id_kp_*.getId() * * @param requiredKeyPurposeOids a list of EKUs that at a minimum must be present if the EKU exists and is critical. */ public EkuPKIXCertPathChecker(final String...requiredKeyPurposeOids) { super(); if (requiredKeyPurposeOids==null) { this.requiredKeyPurposeOids = EMPTY; } else { this.requiredKeyPurposeOids = Arrays.asList(requiredKeyPurposeOids); } } /** * Create a new instance that will check a critical EKU of the leaf certificate. * * Parameters OIDs could be supplied using org.bouncycastle.asn1.x509.KeyPurposeId.id_kp_*.getId() * * @param requiredKeyPurposeOids a list of EKUs that at a minimum must be present if the EKU exists and is critical. */ public EkuPKIXCertPathChecker(final List requiredKeyPurposeOids) { super(); if (requiredKeyPurposeOids==null) { this.requiredKeyPurposeOids = EMPTY; } else { this.requiredKeyPurposeOids = requiredKeyPurposeOids; } } @Override public void check(final Certificate cert, final Collection unresolvedCritExts) throws CertPathValidatorException { if (!CertTools.isCA(cert) && cert instanceof X509Certificate) { final X509Certificate x509Certificate = (X509Certificate) cert; try { List ekus = x509Certificate.getExtendedKeyUsage(); if (ekus==null) { ekus = EMPTY; } if (ekus.containsAll(requiredKeyPurposeOids)) { // All the required EKUs are present, so mark the EKU extension as processed unresolvedCritExts.remove(Extension.extendedKeyUsage.getId()); } else { final List ekusMissing = new ArrayList(requiredKeyPurposeOids); ekusMissing.removeAll(ekus); if (log.isDebugEnabled()) { log.debug("EKUs in certificate: " +Arrays.toString(ekus.toArray()) + " EKUs required: " +Arrays.toString(requiredKeyPurposeOids.toArray())); } log.info("Validation of certificate with subject " + CertTools.getSubjectDN(cert) + " failed critical EKU validation. The missing EKUs were: " + Arrays.toString(ekusMissing.toArray())); return; } } catch (CertificateParsingException e) { throw new CertPathValidatorException(e); } } } @Override public Set getSupportedExtensions() { return Collections.singleton(Extension.extendedKeyUsage.getId()); } @Override public void init(final boolean forward) throws CertPathValidatorException { // NOOP: We don't care about the order, we handle EKUs for non-CA certs } @Override public boolean isForwardCheckingSupported() { // We don't care about the order, we handle EKUs for non-CA certs return true; } }