/**
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* https://glassfish.dev.java.net/public/CDDLv1.0.html or
* glassfish/bootstrap/legal/CDDLv1.0.txt.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at glassfish/bootstrap/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
*/
package org.jvnet.glassfish.comms.security.auth.impl;
import com.sun.enterprise.security.SSLUtils;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.CertPath;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import static org.jvnet.glassfish.comms.security.util.Constants.securityLogger;
/**
*
* @author K.Venugopal@sun.com
*/
public class CertValidator implements CertificateValidator {
private static KeyStore trustStore;
private static CertificateValidator validator = null;
private static boolean revocationCheck = false;
private CertValidator() {
}
public static synchronized void init(Properties props) throws Exception {
if (validator == null) {
if (props != null && !props.isEmpty()) {
String value = props.getProperty("enableRevocationCheck");
if (value != null && value.length() > 0) {
revocationCheck = Boolean.parseBoolean(value);
}
String certVC = (String) props.getProperty("certificateValidator");
if (certVC != null && certVC.length() >0) {
Class cvClass;
try {
cvClass = Class.forName(certVC);
validator = (CertificateValidator) cvClass.newInstance();
} catch (SecurityException ex) {
securityLogger.log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
securityLogger.log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
securityLogger.log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
securityLogger.log(Level.SEVERE, null, ex);
}
}
}
trustStore = SSLUtils.getTrustStore();
if (validator == null) {
validator = new CertValidator();
}
}
}
public static synchronized CertificateValidator getInstance() throws Exception {
if (validator == null) {
trustStore = SSLUtils.getTrustStore();
validator = new CertValidator();
}
return validator;
}
public boolean validate(X509Certificate certificate) throws CertificateException {
try {
certificate.checkValidity();
// for self-signed certificate
if (certificate.getIssuerX500Principal().equals(certificate.getSubjectX500Principal())) {
try {
if (isTrustedSelfSigned(certificate)) {
throw new CertificateNotYetValidException("Self signed certificates are not permitted");
}
} catch (KeyStoreException ex) {
securityLogger.log(Level.SEVERE, null, ex);
throw new CertificateException(ex);
}
}
X509CertSelector certSelector = new X509CertSelector();
certSelector.setCertificate(certificate);
PKIXBuilderParameters parameters;
CertPathBuilder builder = null;
CertPathValidator certValidator = null;
CertPath certPath = null;
List<Certificate> certChainList = new ArrayList<Certificate>();
boolean caFound = false;
Principal certChainIssuer = null;
int noOfEntriesInTrustStore = 0;
boolean isIssuerCertMatched = false;
parameters = new PKIXBuilderParameters(trustStore, certSelector);
parameters.setRevocationEnabled(revocationCheck);
Certificate[] certChain = null;
String certAlias = trustStore.getCertificateAlias(certificate);
if (certAlias != null) {
certChain = trustStore.getCertificateChain(certAlias);
}
if (certChain == null) {
certChainList.add(certificate);
certChainIssuer = certificate.getIssuerX500Principal();
noOfEntriesInTrustStore = trustStore.size();
} else {
certChainList = Arrays.asList(certChain);
}
while (!caFound && noOfEntriesInTrustStore-- != 0 && certChain == null) {
Enumeration aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
String alias = (String) aliases.nextElement();
Certificate cert = trustStore.getCertificate(alias);
if (cert == null || !"X.509".equals(cert.getType()) || certChainList.contains(cert)) {
continue;
}
X509Certificate x509Cert = (X509Certificate) cert;
if (certChainIssuer.equals(x509Cert.getSubjectX500Principal())) {
certChainList.add(cert);
if (x509Cert.getSubjectX500Principal().equals(x509Cert.getIssuerX500Principal())) {
caFound = true;
break;
} else {
certChainIssuer = x509Cert.getIssuerDN();
if (!isIssuerCertMatched) {
isIssuerCertMatched = true;
}
}
} else {
continue;
}
}
if (!caFound) {
if (!isIssuerCertMatched) {
break;
} else {
isIssuerCertMatched = false;
}
}
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
certPath = cf.generateCertPath(certChainList);
certValidator = CertPathValidator.getInstance("PKIX");
certValidator.validate(certPath, parameters);
} catch (CertPathValidatorException ex) {
securityLogger.log(Level.SEVERE, null, ex);
throw new CertificateException(ex);
} catch (NoSuchAlgorithmException ex) {
securityLogger.log(Level.SEVERE, null, ex);
throw new CertificateException(ex);
} catch (KeyStoreException ex) {
securityLogger.log(Level.SEVERE, null, ex);
throw new CertificateException(ex);
} catch (InvalidAlgorithmParameterException ex) {
securityLogger.log(Level.SEVERE, null, ex);
throw new CertificateException(ex);
}
return true;
}
private boolean isTrustedSelfSigned(X509Certificate cert) throws KeyStoreException {
if (trustStore == null) {
return false;
}
Enumeration aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
String alias = (String) aliases.nextElement();
Certificate certificate = trustStore.getCertificate(alias);
if (certificate == null || !"X.509".equals(certificate.getType())) {
continue;
}
X509Certificate x509Cert = (X509Certificate) certificate;
if (x509Cert != null && x509Cert.equals(cert)) {
return true;
}
}
return false;
}
}