Commit e62e7bd6 authored by Jay Kline's avatar Jay Kline Committed by jay

* Implemented OCSP support

* Use of CRLs is optional
* Improved PKIX certificate path checking logic
* Fixed IndexOutOfBoundsException in SASLAuthentication when no principals are found in a certificate



git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@10467 b35dd754-fafc-0310-a699-88a17e54d16e
parent 77633afa
......@@ -58,6 +58,11 @@ public class ClientTrustManager implements X509TrustManager {
*/
private long crlLastUpdated = 0;
/**
* Should CRL checking be done
*/
private boolean useCRLs = false;
public ClientTrustManager(KeyStore trustTrust) {
super();
......@@ -85,15 +90,19 @@ public class ClientTrustManager implements X509TrustManager {
File crlFile = new File(JiveGlobals.getProperty("xmpp.client.certificate.crl",
"resources" + File.separator + "security" + File.separator + "crl.pem"));
if (!crlFile.isFile()) {
//dosnt exist or is something weird, skip it
Log.debug("ClientTrustmanager: crl file not found "+crlFile.toString());
useCRLs = false;
return;
}
long modified = crlFile.lastModified();
if (modified > crlLastUpdated) {
crlLastUpdated = modified;
Log.debug("ClientTrustManager: Updating CRLs");
useCRLs = false;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");;
......@@ -108,6 +117,7 @@ public class ClientTrustManager implements X509TrustManager {
Log.debug("ClientTrustManager: adding CRL for "+crl.getIssuerDN());
crls.add(crl);
}
useCRLs = true;
}
catch(FileNotFoundException e) {
// Its ok if the file wasnt found- maybe we dont have any CRL's
......@@ -239,13 +249,22 @@ public class ClientTrustManager implements X509TrustManager {
CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
X509CertSelector certSelector = new X509CertSelector();
certSelector.setSubject(x509Certificates[0].getIssuerX500Principal());
PKIXBuilderParameters params = new PKIXBuilderParameters(trustStore,new X509CertSelector());
if(crlStore != null)
certSelector.setCertificate(x509Certificates[0]);
PKIXBuilderParameters params = new PKIXBuilderParameters(trustStore,certSelector);
if(useCRLs) {
params.addCertStore(crlStore);
} else {
Log.debug("ClientTrustManager: no CRL's found, so setRevocationEnabled(false)");
params.setRevocationEnabled(false);
}
CertPathBuilderResult cpbr = cpb.build(params);
CertPath cp = cpbr.getCertPath();
if(JiveGlobals.getBooleanProperty("ocsp.enable",false)) {
Log.debug("ClientTrustManager: OCSP requested");
OCSPChecker ocspChecker = new OCSPChecker(cp,params);
params.addCertPathChecker(ocspChecker);
}
PKIXCertPathValidatorResult cpvResult = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
X509Certificate trustedCert = (X509Certificate) cpvResult.getTrustAnchor().getTrustedCert();
if(trustedCert == null) {
......@@ -255,42 +274,18 @@ public class ClientTrustManager implements X509TrustManager {
}
}
catch(CertPathBuilderException e) {
Log.debug("ClientTrustManager:",e);
throw new CertificateException("certificate path failed: "+e.getMessage());
}
catch(CertPathValidatorException e) {
Log.debug("ClientTrustManager:",e);
throw new CertificateException("certificate path failed: "+e.getMessage());
}
catch(KeyStoreException e) {
Log.debug("ClientTrustManager: ",e);
}
catch(InvalidAlgorithmParameterException e) {
Log.debug("ClientTrustManager: ",e);
}
catch(NoSuchAlgorithmException e) {
Log.debug("ClientTrustManager: ",e);
catch(Exception e) {
Log.debug("ClientTrustManager:",e);
throw new CertificateException("unexpected error: "+e.getMessage());
}
//If we did not get any CRL's, we have nothing more to do.
if(crlStore == null)
return;
try {
X509CRLSelector crlSelector = new X509CRLSelector();
crlSelector.addIssuerName(x509Certificates[0].getIssuerDN().getName());
crlSelector.setDateAndTime(new Date()); //right now
Collection<X509CRL> selectedCrls = (Collection<X509CRL>) crlStore.getCRLs(crlSelector);
for(X509CRL crl : selectedCrls) {
if(crl.isRevoked(x509Certificates[0])) {
throw new CertificateException("certificate is revoked: "+peerIdentities);
}
}
}
catch(CertStoreException e) {
Log.error("ClientTrustManager: ",e);
}
catch(IOException e) {
Log.error("ClientTrustManager: ",e);
}
}
}
......
/**
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution, or a commercial license
* agreement with Jive.
*/
package org.jivesoftware.openfire.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.NoSuchProviderException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.ocsp.BasicOCSPResp;
import org.bouncycastle.ocsp.CertificateID;
import org.bouncycastle.ocsp.CertificateStatus;
import org.bouncycastle.ocsp.OCSPReq;
import org.bouncycastle.ocsp.OCSPReqGenerator;
import org.bouncycastle.ocsp.OCSPResp;
import org.bouncycastle.ocsp.SingleResp;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
/**
* A <code>PKIXCertPathChecker</code> that uses
* Online Certificate Status Protocol (OCSP)
*
* See <a href="http://www.ietf.org/rfc/rfc2560.txt">RFC 2560</a>.
*
* @author Jay Kline
*/
public class OCSPChecker extends PKIXCertPathChecker {
private static String ocspServerUrl = JiveGlobals.getProperty("ocsp.responderURL");
private static String ocspServerSubject = JiveGlobals.getProperty("ocsp.responderCertSubjectName");
private static final boolean dump = true;
private int certIndex;
private X509Certificate[] certs;
private CertPath cp;
private PKIXParameters pkixParams;
OCSPChecker(CertPath certPath, PKIXParameters pkixParams)
throws CertPathValidatorException {
this.cp = certPath;
this.pkixParams = pkixParams;
List<? extends Certificate> tmp = cp.getCertificates();
certs =
(X509Certificate[]) tmp.toArray(new X509Certificate[tmp.size()]);
init(false);
}
public void init(boolean forward) throws CertPathValidatorException {
if (!forward) {
certIndex = certs.length - 1;
} else {
throw new CertPathValidatorException(
"Forward checking not supported");
}
}
public boolean isForwardCheckingSupported() {
return false;
}
public Set<String> getSupportedExtensions() {
return Collections.<String>emptySet();
}
public void check(Certificate cert, Collection<String> unresolvedCritExts)
throws CertPathValidatorException {
Log.debug("OCSPChecker: check called");
InputStream in = null;
OutputStream out = null;
try {
// Examine OCSP properties
X509Certificate responderCert = null;
boolean haveResponderCert = true; //defaults to issuers cert
X500Principal responderSubjectName = null;
boolean haveIssuerCert = false;
// If we set the subject name, we need to find the certificate
if (ocspServerSubject != null) {
haveResponderCert = false;
responderSubjectName = new X500Principal(ocspServerSubject);
}
X509Certificate issuerCert = null;
X509Certificate currCert = (X509Certificate) cert;
// Set the issuer certificate if we were passed a chain
if (certIndex != 0) {
issuerCert = (X509Certificate) (certs[certIndex]);
haveIssuerCert = true;
if (haveResponderCert) {
responderCert = certs[certIndex];
}
}
if (!haveIssuerCert || !haveResponderCert) {
if (!haveResponderCert) {
Log.debug("OCSPChecker: Looking for responder's certificate");
}
if (!haveIssuerCert) {
Log.debug("OCSPChecker: Looking for issuer's certificate");
}
// Extract the anchor certs
Iterator anchors = pkixParams.getTrustAnchors().iterator();
if (!anchors.hasNext()) {
throw new CertPathValidatorException(
"Must specify at least one trust anchor");
}
X500Principal certIssuerName =
currCert.getIssuerX500Principal();
while (anchors.hasNext() &&
(!haveIssuerCert || !haveResponderCert)) {
TrustAnchor anchor = (TrustAnchor) anchors.next();
X509Certificate anchorCert = anchor.getTrustedCert();
X500Principal anchorSubjectName =
anchorCert.getSubjectX500Principal();
// Check if this anchor cert is the issuer cert
if (!haveIssuerCert && certIssuerName.equals(anchorSubjectName)) {
issuerCert = anchorCert;
haveIssuerCert = true;
//If we have not set the responderCert at this point, set it to the issuer
if (haveResponderCert && responderCert == null) {
responderCert = anchorCert;
Log.debug("OCSPChecker: Responder's certificate = issuer certificate");
}
}
// Check if this anchor cert is the responder cert
if (!haveResponderCert) {
if (responderSubjectName != null &&
responderSubjectName.equals(anchorSubjectName)) {
responderCert = anchorCert;
haveResponderCert = true;
}
}
}
if (issuerCert == null) {
//No trust anchor was found matching the issuer
throw new CertPathValidatorException("No trusted certificate for " + currCert.getIssuerDN());
}
// Check cert stores if responder cert has not yet been found
if (!haveResponderCert) {
Log.debug("OCSPChecker: Searching cert stores for responder's certificate");
if (responderSubjectName != null) {
X509CertSelector filter = new X509CertSelector();
filter.setSubject(responderSubjectName.getName());
List<CertStore> certStores = pkixParams.getCertStores();
for (CertStore certStore : certStores) {
Iterator i = certStore.getCertificates(filter).iterator();
if (i.hasNext()) {
responderCert = (X509Certificate) i.next();
haveResponderCert = true;
break;
}
}
}
}
}
// Could not find the responder cert
if (!haveResponderCert) {
throw new CertPathValidatorException("Cannot find the responder's certificate.");
}
// Construct an OCSP Request
OCSPReqGenerator gen = new OCSPReqGenerator();
CertificateID certID = new CertificateID(CertificateID.HASH_SHA1, issuerCert, currCert.getSerialNumber());
gen.addRequest(certID);
OCSPReq ocspRequest = gen.generate();
URL url;
if (ocspServerUrl != null) {
try {
url = new URL(ocspServerUrl);
} catch (MalformedURLException e) {
throw new CertPathValidatorException(e);
}
} else {
throw new CertPathValidatorException("Must set OCSP Server URL");
}
HttpURLConnection con = (HttpURLConnection) url.openConnection();
Log.debug("OCSPChecker: connecting to OCSP service at: " + url);
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-type", "application/ocsp-request");
con.setRequestProperty("Accept","application/ocsp-response");
byte[] bytes = ocspRequest.getEncoded();
con.setRequestProperty("Content-length", String.valueOf(bytes.length));
out = con.getOutputStream();
out.write(bytes);
out.flush();
// Check the response
if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.debug("OCSPChecker: Received HTTP error: " + con.getResponseCode() +
" - " + con.getResponseMessage());
}
in = con.getInputStream();
OCSPResp ocspResponse = new OCSPResp(in);
BigInteger serialNumber = currCert.getSerialNumber();
BasicOCSPResp brep = (BasicOCSPResp) ocspResponse.getResponseObject();
try {
if( ! brep.verify(responderCert.getPublicKey(),"BC")) {
throw new CertPathValidatorException("OCSP response is not verified");
}
} catch (NoSuchProviderException e) {
throw new CertPathValidatorException("OCSP response could not be verified ("+e.getMessage()+")" ,null, cp, certIndex);
}
SingleResp[] singleResp = brep.getResponses();
boolean foundResponse = false;
for (SingleResp resp : singleResp) {
CertificateID respCertID = resp.getCertID();
if (respCertID.equals(certID)) {
Object status = resp.getCertStatus();
if (status == CertificateStatus.GOOD) {
Log.debug("OCSPChecker: Status of certificate (with serial number " +
serialNumber.toString() + ") is: good");
foundResponse = true;
break;
} else if (status instanceof org.bouncycastle.ocsp.RevokedStatus) {
Log.debug("OCSPChecker: Status of certificate (with serial number " +
serialNumber.toString() + ") is: revoked");
throw new CertPathValidatorException("Certificate has been revoked", null, cp, certIndex);
} else if (status instanceof org.bouncycastle.ocsp.UnknownStatus) {
Log.debug("OCSPChecker: Status of certificate (with serial number " +
serialNumber.toString() + ") is: unknown");
throw new CertPathValidatorException("Certificate's revocation status is unknown", null, cp, certIndex);
} else {
Log.debug("Status of certificate (with serial number " +
serialNumber.toString() + ") is: not recognized");
throw new CertPathValidatorException("Unknown OCSP response for certificate", null, cp, certIndex);
}
}
}
// Check that response applies to the cert that was supplied
if (!foundResponse) {
throw new CertPathValidatorException(
"No certificates in the OCSP response match the " +
"certificate supplied in the OCSP request.");
}
} catch (CertPathValidatorException cpve) {
throw cpve;
} catch (Exception e) {
throw new CertPathValidatorException(e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
throw new CertPathValidatorException(ioe);
}
}
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
throw new CertPathValidatorException(ioe);
}
}
}
}
}
......@@ -469,8 +469,14 @@ public class SASLAuthentication {
Log.warn("Error retrieving client certificates of: " + session, e);
}
principal = principals.get(0);
if(principals.size() == 1) {
principal = principals.get(0);
} else if(principals.size() > 1) {
Log.debug("SASLAuthentication: EXTERNAL authentication: more than one principal found, using first.");
principal = principals.get(0);
} else {
Log.debug("SASLAuthentication: EXTERNAL authentication: No principals found.");
}
if (username == null || username.length() == 0) {
// No username was provided, according to XEP-0178 we need to:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment