org.globus.gsi.trustmanager.X509ProxyCertPathValidator Maven / Gradle / Ivy
/*
* Copyright 1999-2010 University of Chicago
*
* Licensed 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.globus.gsi.trustmanager;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.TBSCertificateStructure;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.globus.gsi.GSIConstants;
import org.globus.gsi.X509ProxyCertPathParameters;
import org.globus.gsi.X509ProxyCertPathValidatorResult;
import org.globus.gsi.CertificateRevocationLists;
import org.globus.gsi.provider.SigningPolicyStore;
import org.globus.gsi.proxy.ProxyPolicyHandler;
import org.globus.gsi.proxy.ext.ProxyCertInfo;
import org.globus.gsi.proxy.ext.ProxyPolicy;
import org.globus.gsi.util.CertificateUtil;
import org.globus.gsi.util.KeyUsage;
import org.globus.gsi.util.ProxyCertificateUtil;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.cert.CertPath;
import java.security.cert.CertPathParameters;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorResult;
import java.security.cert.CertPathValidatorSpi;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
/**
* Implementation of the CertPathValidatorSpi and the logic for X.509 Proxy Path Validation.
*
* @version ${version}
* @since 1.0
*/
public class X509ProxyCertPathValidator extends CertPathValidatorSpi {
public static final String BASIC_CONSTRAINT_OID = "2.5.29.19";
public static final String KEY_USAGE_OID = "2.5.29.15";
protected KeyStore keyStore;
protected CertStore certStore;
protected SigningPolicyStore policyStore;
private X509Certificate identityCert;
private boolean limited;
private boolean rejectLimitedProxy;
private Map policyHandlers;
/**
* Validates the specified certification path using the specified algorithm parameter set.
*
* The CertPath
specified must be of a type that is supported by the validation algorithm, otherwise
* an InvalidAlgorithmParameterException
will be thrown. For example, a CertPathValidator
* that implements the PKIX algorithm validates CertPath
objects of type X.509.
*
* @param certPath the CertPath
to be validated
* @param params the algorithm parameters
* @return the result of the validation algorithm
* @throws java.security.cert.CertPathValidatorException
* if the CertPath
does not validate
* @throws java.security.InvalidAlgorithmParameterException
* if the specified parameters or the type of the
* specified CertPath
are inappropriate for this CertPathValidator
*/
@SuppressWarnings("unchecked")
public CertPathValidatorResult engineValidate(CertPath certPath, CertPathParameters params)
throws CertPathValidatorException, InvalidAlgorithmParameterException {
if (certPath == null) {
throw new IllegalArgumentException(
"Certificate path cannot be null");
}
List list = certPath.getCertificates();
if (list.size() < 1) {
throw new IllegalArgumentException(
"Certificate path cannot be empty");
}
parseParameters(params);
// find the root trust anchor. Validate signatures and see if the
// chain ends in one of the trust root certificates
CertPath trustedCertPath = TrustedCertPathFinder.findTrustedCertPath(this.keyStore, certPath);
// rest of the validation
return validate(trustedCertPath);
}
/**
* Dispose of the current validation state.
*/
public void clear() {
this.identityCert = null;
this.limited = false;
}
protected void parseParameters(CertPathParameters params) throws InvalidAlgorithmParameterException {
if (!(params instanceof X509ProxyCertPathParameters)) {
throw new IllegalArgumentException("Parameter of type " + X509ProxyCertPathParameters.class.getName()
+ " required");
}
X509ProxyCertPathParameters parameters = (X509ProxyCertPathParameters) params;
this.keyStore = parameters.getTrustStore();
this.certStore = parameters.getCrlStore();
this.policyStore = parameters.getSigningPolicyStore();
this.rejectLimitedProxy = parameters.isRejectLimitedProxy();
this.policyHandlers = parameters.getPolicyHandlers();
}
/**
* Validates the certificate path and does the following for each certificate in the chain: method
* checkCertificate() In addition: a) Validates if the issuer type of each certificate is correct b) CA path
* constraints c) Proxy path constraints
*
* If it is of type proxy, check following: a) proxy constraints b) restricted proxy else if certificate, check the
* following: a) keyisage
*
* @param certPath The CertPath to validate.
* @return The results of the validation.
* @throws CertPathValidatorException If the CertPath is invalid.
*/
protected CertPathValidatorResult validate(CertPath certPath) throws CertPathValidatorException {
List extends Certificate> certificates = certPath.getCertificates();
if (certificates.size() == 0) {
return null;
}
X509Certificate cert;
TBSCertificateStructure tbsCert;
GSIConstants.CertificateType certType;
X509Certificate issuerCert;
TBSCertificateStructure issuerTbsCert;
GSIConstants.CertificateType issuerCertType;
int proxyDepth = 0;
cert = (X509Certificate) certificates.get(0);
try {
tbsCert = getTBSCertificateStructure(cert);
certType = getCertificateType(tbsCert);
// validate the first certificate in chain
checkCertificate(cert, certType);
boolean isProxy = ProxyCertificateUtil.isProxy(certType);
if (isProxy) {
proxyDepth++;
}
} catch (CertPathValidatorException e) {
throw new CertPathValidatorException("Path validation failed for " + cert.getSubjectDN() + ": " + e.getMessage(),
e, certPath, 0);
}
for (int i = 1; i < certificates.size(); i++) {
boolean certIsProxy = ProxyCertificateUtil.isProxy(certType);
issuerCert = (X509Certificate) certificates.get(i);
issuerTbsCert = getTBSCertificateStructure(issuerCert);
issuerCertType = getCertificateType(issuerTbsCert);
proxyDepth = validateCert(cert, certType, issuerCert, issuerTbsCert, issuerCertType, proxyDepth, i,
certIsProxy);
if (certIsProxy) {
try {
checkProxyConstraints(certPath, cert, tbsCert, certType, issuerTbsCert, i);
} catch (CertPathValidatorException e) {
throw new CertPathValidatorException("Path validation failed for " + cert.getSubjectDN() + ": " + e.getMessage(),
e, certPath, i - 1);
}
} else {
try {
checkKeyUsage(issuerTbsCert);
} catch (IOException e) {
throw new CertPathValidatorException("Key usage check failed on " + issuerCert.getSubjectDN() + ": " + e.getMessage(),
e, certPath, i);
} catch (CertPathValidatorException e) {
throw new CertPathValidatorException("Path validation failed for " + issuerCert.getSubjectDN() + ": " + e.getMessage(),
e, certPath, i);
}
}
try {
checkCertificate(issuerCert, issuerCertType);
} catch (CertPathValidatorException e) {
throw new CertPathValidatorException("Path validation failed for " + issuerCert.getSubjectDN() + ": " + e.getMessage(),
e, certPath, i);
}
cert = issuerCert;
certType = issuerCertType;
tbsCert = issuerTbsCert;
}
return new X509ProxyCertPathValidatorResult(this.identityCert,
this.limited);
}
private GSIConstants.CertificateType getCertificateType(TBSCertificateStructure issuerTbsCert) throws CertPathValidatorException {
GSIConstants.CertificateType issuerCertType;
try {
issuerCertType = CertificateUtil.getCertificateType(issuerTbsCert);
} catch (CertificateException e) {
throw new CertPathValidatorException(
"Error obtaining certificate type", e);
} catch (IOException e) {
throw new CertPathValidatorException(
"Error obtaining certificate type", e);
}
return issuerCertType;
}
private TBSCertificateStructure getTBSCertificateStructure(X509Certificate issuerCert) throws CertPathValidatorException {
TBSCertificateStructure issuerTbsCert;
try {
issuerTbsCert = CertificateUtil.getTBSCertificateStructure(issuerCert);
} catch (CertificateException e) {
throw new CertPathValidatorException("Error converting certificate", e);
} catch (IOException e) {
throw new CertPathValidatorException("Error converting certificate", e);
}
return issuerTbsCert;
}
private int validateCert(X509Certificate cert, GSIConstants.CertificateType certType, X509Certificate issuerCert,
TBSCertificateStructure issuerTbsCert, GSIConstants.CertificateType issuerCertType,
int proxyDepth, int i, boolean certIsProxy) throws CertPathValidatorException {
if (issuerCertType == GSIConstants.CertificateType.CA) {
validateCACert(cert, issuerCert, issuerTbsCert, proxyDepth, i, certIsProxy);
} else if (ProxyCertificateUtil.isGsi3Proxy(issuerCertType)
|| ProxyCertificateUtil.isGsi4Proxy(issuerCertType)) {
return validateGsiProxyCert(cert, certType, issuerCert, issuerTbsCert,
issuerCertType, proxyDepth);
} else if (ProxyCertificateUtil.isGsi2Proxy(issuerCertType)) {
return validateGsi2ProxyCert(cert, certType, issuerCert, proxyDepth);
} else if (issuerCertType == GSIConstants.CertificateType.EEC) {
validateEECCert(cert, certType, issuerCert);
} else {
// this should never happen?
throw new CertPathValidatorException("UNknown issuer type " + issuerCertType
+ " for certificate " + issuerCert.getSubjectDN());
}
return proxyDepth;
}
private void checkProxyConstraints(CertPath certPath, X509Certificate cert,
TBSCertificateStructure tbsCert, GSIConstants.CertificateType certType,
TBSCertificateStructure issuerTbsCert, int i)
throws CertPathValidatorException {
// check all the proxy & issuer constraints
if (ProxyCertificateUtil.isGsi3Proxy(certType)
|| ProxyCertificateUtil.isGsi4Proxy(certType)) {
try {
checkProxyConstraints(tbsCert, issuerTbsCert, cert);
} catch (IOException e) {
throw new CertPathValidatorException("Proxy constraint check failed on " + cert.getSubjectDN(), e);
}
if ((certType == GSIConstants.CertificateType.GSI_3_RESTRICTED_PROXY)
|| (certType == GSIConstants.CertificateType.GSI_4_RESTRICTED_PROXY)) {
try {
checkRestrictedProxy(tbsCert, certPath, i);
} catch (IOException e) {
throw new CertPathValidatorException("Restricted proxy check failed on " + cert.getSubjectDN(), e);
}
}
}
}
private void validateEECCert(X509Certificate cert, GSIConstants.CertificateType certType,
X509Certificate issuerCert) throws CertPathValidatorException {
if (!ProxyCertificateUtil.isProxy(certType)) {
throw new CertPathValidatorException("EEC can only sign another proxy certificate. Violated by "
+ issuerCert.getSubjectDN() + " issuing " + cert.getSubjectDN());
}
}
private int validateGsi2ProxyCert(X509Certificate cert, GSIConstants.CertificateType certType,
X509Certificate issuerCert, int proxyDepth) throws CertPathValidatorException {
// PC can sign EEC or another PC only
if (!ProxyCertificateUtil.isGsi2Proxy(certType)) {
throw new CertPathValidatorException(
"Proxy certificate can only sign another proxy certificate of same type. Violated by "
+ issuerCert.getSubjectDN() + " issuing " + cert.getSubjectDN());
}
return proxyDepth + 1;
}
private int validateGsiProxyCert(X509Certificate cert, GSIConstants.CertificateType certType,
X509Certificate issuerCert, TBSCertificateStructure issuerTbsCert,
GSIConstants.CertificateType issuerCertType, int proxyDepth)
throws CertPathValidatorException {
if (ProxyCertificateUtil.isGsi3Proxy(issuerCertType)) {
if (!ProxyCertificateUtil.isGsi3Proxy(certType)) {
throw new CertPathValidatorException(
"Proxy certificate can only sign another proxy certificate of same type. Violated by "
+ issuerCert.getSubjectDN() + " issuing " + cert.getSubjectDN());
}
} else if (ProxyCertificateUtil.isGsi4Proxy(issuerCertType) && !ProxyCertificateUtil.isGsi4Proxy(certType)) {
throw new CertPathValidatorException(
"Proxy certificate can only sign another proxy certificate of same type. Violated by "
+ issuerCert.getSubjectDN() + " issuing " + cert.getSubjectDN());
}
int pathLen;
try {
pathLen = ProxyCertificateUtil.getProxyPathConstraint(issuerTbsCert);
} catch (IOException e) {
throw new CertPathValidatorException("Error obtaining proxy path constraint", e);
}
if (pathLen == 0) {
throw new CertPathValidatorException(
"Proxy path length constraint violated of certificate " + issuerCert.getSubjectDN());
}
if (pathLen < Integer.MAX_VALUE
&& proxyDepth > pathLen) {
throw new CertPathValidatorException(
"Proxy path length constraint violated of certificate " + issuerCert.getSubjectDN());
}
return proxyDepth + 1;
}
private void validateCACert(
X509Certificate cert, X509Certificate issuerCert,
TBSCertificateStructure issuerTbsCert, int proxyDepth, int i,
boolean certIsProxy) throws CertPathValidatorException {
// PC can only be signed by EEC or PC
if (certIsProxy) {
throw new CertPathValidatorException(
"Proxy certificate can be signed only by EEC or Proxy "
+ "Certificate. Certificate " + cert.getSubjectDN() + " violates this.");
}
try {
int pathLen =
CertificateUtil.getCAPathConstraint(issuerTbsCert);
if (pathLen < Integer.MAX_VALUE
&& (i - proxyDepth - 1) > pathLen) {
throw new CertPathValidatorException("Path length constraint of certificate "
+ issuerCert.getSubjectDN() + " violated");
}
} catch (IOException e) {
throw new CertPathValidatorException("Error obtaining CA Path constraint", e);
}
}
// private X509Certificate checkCertificate(List trustedCertPath, X509Certificate x509Certificate,
// Certificate issuerCertificate) throws CertPathValidatorException {
// X509Certificate x509IssuerCertificate = (X509Certificate) issuerCertificate;
//
// // check that the next one is indeed issuer
// Principal issuerDN = x509Certificate.getIssuerDN();
// Principal issuerCertDN = x509IssuerCertificate.getSubjectDN();
// if (!(issuerDN.equals(issuerCertDN))) {
// throw new IllegalArgumentException("Incorrect certificate path, certificate in chain can only "
// + "be issuer of previous certificate");
// }
//
// // validate integrity of signature
// PublicKey publicKey = x509IssuerCertificate.getPublicKey();
// try {
// x509Certificate.verify(publicKey);
// } catch (CertificateException e) {
// throw new CertPathValidatorException(
// "Signature validation on the certificate " + x509Certificate.getSubjectDN(), e);
// } catch (NoSuchAlgorithmException e) {
// throw new CertPathValidatorException(
// "Signature validation on the certificate " + x509Certificate.getSubjectDN(), e);
// } catch (InvalidKeyException e) {
// throw new CertPathValidatorException(
// "Signature validation on the certificate " + x509Certificate.getSubjectDN(), e);
// } catch (NoSuchProviderException e) {
// throw new CertPathValidatorException(
// "Signature validation on the certificate " + x509Certificate.getSubjectDN(), e);
// } catch (SignatureException e) {
// throw new CertPathValidatorException(
// "Signature validation on the certificate " + x509Certificate.getSubjectDN(), e);
// }
//
// trustedCertPath.add(x509Certificate);
// return x509IssuerCertificate;
// }
protected void checkRestrictedProxy(TBSCertificateStructure proxy, CertPath certPath, int index)
throws CertPathValidatorException, IOException {
ProxyCertInfo info = ProxyCertificateUtil.getProxyCertInfo(proxy);
ProxyPolicy policy = info.getProxyPolicy();
String pl = policy.getPolicyLanguage().getId();
ProxyPolicyHandler handler = null;
if (this.policyHandlers != null) {
handler = this.policyHandlers.get(pl);
}
if (handler == null) {
throw new CertPathValidatorException("Unknown policy, no handler registered to validate policy " + pl);
}
handler.validate(info, certPath, index);
}
protected void checkKeyUsage(TBSCertificateStructure issuer)
throws CertPathValidatorException, IOException {
EnumSet issuerKeyUsage = CertificateUtil.getKeyUsage(issuer);
if (issuerKeyUsage != null && !issuerKeyUsage.contains(KeyUsage.KEY_CERTSIGN)) {
throw new CertPathValidatorException("Certificate " + issuer.getSubject() + " violated key usage policy.");
}
}
// COMMENT enable the checkers again when ProxyPathValidator starts working!
protected List getCertificateCheckers() {
List checkers = new ArrayList();
checkers.add(new DateValidityChecker());
checkers.add(new UnsupportedCriticalExtensionChecker());
checkers.add(new IdentityChecker(this));
// NOTE: the (possible) refresh of the CRLs happens when we call getDefault.
// Hence, we must recreate crlsList for each call to checkCertificate
// Sadly, this also means that the amount of work necessary for checkCertificate
// can be arbitrarily large (if the CRL is indeed refreshed).
//
// Note we DO NOT use this.certStore by default! TODO: This differs from the unit test
CertificateRevocationLists crlsList = CertificateRevocationLists.getDefaultCertificateRevocationLists();
checkers.add(new CRLChecker(crlsList, this.keyStore, true));
checkers.add(new SigningPolicyChecker(this.policyStore));
return checkers;
}
/*
* Method to check following for any given certificate
*
* a) Date validity, is it valid for the curent time (see DateValidityChecker)
* b) Any unsupported critical extensions (see UnsupportedCriticalExtensionChecker)
* c) Identity of certificate (see IdentityChecker)
* d) Revocation (see CRLChecker)
* e) Signing policy (see SigningPolicyChecker)
*
*/
private void checkCertificate(X509Certificate cert, GSIConstants.CertificateType certType)
throws CertPathValidatorException {
for (CertificateChecker checker : getCertificateCheckers()) {
checker.invoke(cert, certType);
}
}
@SuppressWarnings("unused")
protected void checkProxyConstraints(TBSCertificateStructure proxy, TBSCertificateStructure issuer,
X509Certificate checkedProxy)
throws CertPathValidatorException, IOException {
X509Extensions extensions;
ASN1ObjectIdentifier oid;
X509Extension proxyExtension;
X509Extension proxyKeyUsage = null;
extensions = proxy.getExtensions();
if (extensions != null) {
Enumeration e = extensions.oids();
while (e.hasMoreElements()) {
oid = (ASN1ObjectIdentifier) e.nextElement();
proxyExtension = extensions.getExtension(oid);
if (oid.equals(X509Extension.subjectAlternativeName)
|| oid.equals(X509Extension.issuerAlternativeName)) {
// No Alt name extensions - 3.2 & 3.5
throw new CertPathValidatorException(
"Proxy violation: no Subject or Issuer Alternative Name");
} else if (oid.equals(X509Extension.basicConstraints)) {
// Basic Constraint must not be true - 3.8
BasicConstraints basicExt =
CertificateUtil.getBasicConstraints(proxyExtension);
if (basicExt.isCA()) {
throw new CertPathValidatorException(
"Proxy violation: Basic Constraint CA is set to true");
}
} else if (oid.equals(X509Extension.keyUsage)) {
proxyKeyUsage = proxyExtension;
checkKeyUsage(issuer, proxyExtension);
}
}
}
extensions = issuer.getExtensions();
if (extensions != null) {
Enumeration e = extensions.oids();
while (e.hasMoreElements()) {
oid = (ASN1ObjectIdentifier) e.nextElement();
proxyExtension = extensions.getExtension(oid);
checkExtension(oid, proxyExtension, proxyKeyUsage);
}
}
}
private void checkKeyUsage(TBSCertificateStructure issuer, X509Extension proxyExtension) throws IOException, CertPathValidatorException {
EnumSet keyUsage = CertificateUtil.getKeyUsage(proxyExtension);
// these must not be asserted
if (keyUsage.contains(KeyUsage.NON_REPUDIATION) || keyUsage.contains(KeyUsage.KEY_CERTSIGN)) {
throw new CertPathValidatorException("Proxy violation: Key usage is asserted.");
}
}
private void checkExtension(ASN1ObjectIdentifier oid, X509Extension proxyExtension, X509Extension proxyKeyUsage) throws CertPathValidatorException {
if (oid.equals(X509Extension.keyUsage)) {
// If issuer has it then proxy must have it also
if (proxyKeyUsage == null) {
throw new CertPathValidatorException(
"Proxy violation: Issuer has key usage, but proxy does not");
}
// If issuer has it as critical so does the proxy
if (proxyExtension.isCritical() && !proxyKeyUsage.isCritical()) {
throw new CertPathValidatorException(
"Proxy voilation: issuer key usage is critical, but proxy certificate's is not");
}
}
}
public X509Certificate getIdentityCertificate() {
return this.identityCert;
}
public void setLimited(boolean limited) {
this.limited = limited;
}
// COMMENT: added a way to get 'limited'
public boolean isLimited() {
return this.limited;
}
public void setIdentityCert(X509Certificate identityCert) {
this.identityCert = identityCert;
}
public boolean isRejectLimitedProxy() {
return this.rejectLimitedProxy;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy