Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ocsp crl check method c #4545

Merged
merged 14 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions base/ocsp/src/main/java/com/netscape/cms/ocsp/CRLLdapValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2023 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package com.netscape.cms.ocsp;

import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRLEntry;
import java.util.Arrays;
import java.util.Enumeration;

import org.mozilla.jss.crypto.X509Certificate;
import org.mozilla.jss.netscape.security.x509.AuthorityKeyIdentifierExtension;
import org.mozilla.jss.netscape.security.x509.KeyIdentifier;
import org.mozilla.jss.netscape.security.x509.PKIXExtensions;
import org.mozilla.jss.netscape.security.x509.SubjectKeyIdentifierExtension;
import org.mozilla.jss.netscape.security.x509.X509CRLImpl;
import org.mozilla.jss.netscape.security.x509.X509CertImpl;
import org.mozilla.jss.ssl.SSLCertificateApprovalCallback;

import com.netscape.certsrv.base.EBaseException;
import com.netscape.cmscore.dbs.CRLIssuingPointRecord;

public class CRLLdapValidator implements SSLCertificateApprovalCallback {

public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CRLLdapValidator.class);

private LDAPStore crlStore;



public CRLLdapValidator(LDAPStore crlStore) {
super();
this.crlStore = crlStore;
}


@Override
public boolean approve(X509Certificate certificate, ValidityStatus currentStatus) {
logger.info("CRLLdapValidator: validate of peer's certificate for the connection " + certificate.getSubjectDN());
CRLIssuingPointRecord pt = null;
try {
X509CertImpl peerCert = new X509CertImpl(certificate.getEncoded());
Enumeration<CRLIssuingPointRecord> eCRL = crlStore.searchAllCRLIssuingPointRecord(-1);
AuthorityKeyIdentifierExtension peerAKIExt = (AuthorityKeyIdentifierExtension) peerCert.getExtension(PKIXExtensions.AuthorityKey_Id.toString());
if(peerAKIExt == null) {
logger.error("CRLLdapValidator: the certificate has not Authority Key Identifier Extension. CRL verification cannot be done.");
return false;
}
while (eCRL.hasMoreElements() && pt == null) {
CRLIssuingPointRecord tPt = eCRL.nextElement();
logger.debug("CRLLdapValidator: CRL check issuer " + tPt.getId());
X509CertImpl caCert = new X509CertImpl(tPt.getCACert());
try {
SubjectKeyIdentifierExtension caAKIExt = (SubjectKeyIdentifierExtension) caCert.getExtension(PKIXExtensions.SubjectKey_Id.toString());
if(caAKIExt == null) {
logger.error("CRLLdapValidator: signing certificate missing Subject Key Identifier. Skip CA " + caCert.getName());
continue;
}

KeyIdentifier caSKIId = (KeyIdentifier) caAKIExt.get(SubjectKeyIdentifierExtension.KEY_ID);
KeyIdentifier peerAKIId = (KeyIdentifier) peerAKIExt.get(AuthorityKeyIdentifierExtension.KEY_ID);
if(Arrays.equals(caSKIId.getIdentifier(), peerAKIId.getIdentifier())) {
pt = tPt;
}
} catch (IOException e) {
logger.error("CRLLdapValidator: problem extracting key from SKI/AKI: " + e.getMessage(), e);
}
}
} catch (EBaseException | CertificateException e) {
logger.error("CRLLdapValidator: problem find CRL issuing point. " + e.getMessage(), e);
return false;
}
if (pt == null) {
logger.error("CRLLdapValidator: CRL issuing point not found for " + certificate.getIssuerDN());
return false;
}
try {
X509CRLImpl crl = new X509CRLImpl(pt.getCRL());
X509CRLEntry crlentry = crl.getRevokedCertificate(certificate.getSerialNumber());

if (crlentry == null) {
if (crlStore.isNotFoundGood()) {
return true;
}
}
} catch (Exception e) {
logger.error("CRLLdapValidator: crl check error. " + e.getMessage(), e);
}
logger.error("CRLLdapValidator: peer certificate not valid");
return false;
}

}
17 changes: 17 additions & 0 deletions base/ocsp/src/main/java/com/netscape/cms/ocsp/LDAPStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Locale;
import java.util.Vector;

import org.dogtagpki.server.ocsp.OCSPEngine;
import org.mozilla.jss.asn1.GeneralizedTime;
import org.mozilla.jss.asn1.INTEGER;
import org.mozilla.jss.netscape.security.x509.RevokedCertificate;
Expand Down Expand Up @@ -82,6 +83,11 @@ public class LDAPStore implements IDefStore, IExtendedPluginInfo {
private static final String PROP_HOST = "host";
private static final String PROP_PORT = "port";

// This option enables the revocation verification of peer certificates using the CRL stored in the LDAP.
// Peer certificate of all the outcome connections from the OCSP subsystem are verified with the CRL.
// If also auths.revocationChecking.is set to true the peer certificate og all the income connections to the OCSP subsystem are verified with the CRL.
private static final String PROP_VALIDATE_CONNECTION_WITH_CRL = "validateConnCertWithCRL";

private final static String PROP_NOT_FOUND_GOOD = "notFoundAsGood";
private final static String PROP_INCLUDE_NEXT_UPDATE =
"includeNextUpdate";
Expand All @@ -93,6 +99,8 @@ public class LDAPStore implements IDefStore, IExtendedPluginInfo {
private String mCACertAttr = null;
protected Hashtable<String, Long> mReqCounts = new Hashtable<>();
private Hashtable<X509CertImpl, X509CRLImpl> mCRLs = new Hashtable<>();
private boolean mValidateConnection = true;


/**
* Constructs the default store.
Expand Down Expand Up @@ -140,6 +148,7 @@ public void init(ConfigStore config, DBSubsystem dbSubsystem) throws EBaseExcept
DEF_CA_CERT_ATTR);
mByName = mConfig.getBoolean(PROP_BY_NAME, true);

mValidateConnection = mConfig.getBoolean(PROP_VALIDATE_CONNECTION_WITH_CRL, true);
}

/**
Expand Down Expand Up @@ -277,6 +286,9 @@ public void startup() throws EBaseException {

updater.start();
}
if(mValidateConnection && OCSPEngine.getInstance() != null) {
OCSPEngine.getInstance().setApprovalCallback(new CRLLdapValidator(this));
}
}

@Override
Expand Down Expand Up @@ -531,6 +543,11 @@ public void setConfigParameters(NameValuePairs pairs)
mConfig.put(key, pairs.get(key));
}
}

public boolean isCRLCheckAvailable() {
return mValidateConnection;
}

}

class CRLUpdater extends Thread {
Expand Down
173 changes: 173 additions & 0 deletions base/ocsp/src/main/java/org/dogtagpki/server/ocsp/OCSPEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,33 @@

package org.dogtagpki.server.ocsp;

import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRLEntry;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;

import org.mozilla.jss.netscape.security.x509.AuthorityKeyIdentifierExtension;
import org.mozilla.jss.netscape.security.x509.KeyIdentifier;
import org.mozilla.jss.netscape.security.x509.PKIXExtensions;
import org.mozilla.jss.netscape.security.x509.SubjectKeyIdentifierExtension;
import org.mozilla.jss.netscape.security.x509.X509CRLImpl;
import org.mozilla.jss.netscape.security.x509.X509CertImpl;
import org.mozilla.jss.ssl.SSLCertificateApprovalCallback.ValidityStatus;

import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.Subsystem;
import com.netscape.cms.ocsp.LDAPStore;
import com.netscape.cmscore.apps.CMSEngine;
import com.netscape.cmscore.apps.DatabaseConfig;
import com.netscape.cmscore.base.ConfigStorage;
import com.netscape.cmscore.base.ConfigStore;
import com.netscape.cmscore.dbs.CRLIssuingPointRecord;
import com.netscape.cmscore.dbs.DBSubsystem;
import com.netscape.cmscore.ldapconn.LDAPConfig;
import com.netscape.cmscore.ldapconn.PKISocketConfig;
import com.netscape.cmsutil.password.PasswordStore;
import com.netscape.ocsp.OCSPAuthority;

public class OCSPEngine extends CMSEngine {
Expand Down Expand Up @@ -51,6 +74,13 @@ public OCSPAuthority getOCSP() {
return (OCSPAuthority) getSubsystem(OCSPAuthority.ID);
}

@Override
public void initDBSubsystem() throws Exception {

dbSubsystem = new DBSubsystem();
dbSubsystem.setCMSEngine(this);
dbSubsystem.setEngineConfig(config);
}
@Override
public void initSubsystem(Subsystem subsystem, ConfigStore subsystemConfig) throws Exception {

Expand All @@ -60,5 +90,148 @@ public void initSubsystem(Subsystem subsystem, ConfigStore subsystemConfig) thro
}

super.initSubsystem(subsystem, subsystemConfig);
if (subsystem instanceof OCSPAuthority) {
subsystem.startup();
}
}


protected void startupSubsystems() throws Exception {

for (Subsystem subsystem : subsystems.values()) {
logger.info("CMSEngine: Starting " + subsystem.getId() + " subsystem");
if (!(subsystem instanceof OCSPAuthority)) {
DatabaseConfig dbConfig = config.getDatabaseConfig();
LDAPConfig ldapConfig = dbConfig.getLDAPConfig();
PKISocketConfig socketConfig = config.getSocketConfig();
PasswordStore passwordStore = getPasswordStore();
dbSubsystem.init(dbConfig, ldapConfig, socketConfig, passwordStore);
subsystem.startup();
}
}

// global admin servlet. (anywhere else more fit for this ?)
}
@Override
protected void initSequence() throws Exception {


initDebug();
initAuditor();
initDBSubsystem();
init();
initPasswordStore();
initSubsystemListeners();
initSecurityProvider();
initPluginRegistry();
initLogSubsystem();

initClientSocketListener();
initServerSocketListener();

testLDAPConnections();
initDatabase();

initJssSubsystem();
initUGSubsystem();
initOIDLoaderSubsystem();
initX500NameSubsystem();
// skip TP subsystem;
// problem in needing dbsubsystem in constructor. and it's not used.
initRequestSubsystem();


startupSubsystems();

initAuthSubsystem();
initAuthzSubsystem();
initCMSGateway();
initJobsScheduler();

configureAutoShutdown();
configureServerCertNickname();

initSecurityDomain();
}

@Override
public boolean isRevoked(X509Certificate[] certificates) {
LDAPStore crlStore = null;
for (Subsystem subsystem : subsystems.values()) {
if (subsystem instanceof OCSPAuthority) {
OCSPAuthority ocsp = (OCSPAuthority) subsystem;
if (ocsp.getDefaultStore() instanceof LDAPStore) {
crlStore = (LDAPStore) ocsp.getDefaultStore();
}
break;
}
}

if (crlStore == null || !crlStore.isCRLCheckAvailable()) {
return super.isRevoked(certificates);
}

for (X509Certificate cert: certificates) {
if(!crlCertValid(crlStore, cert, null)) {
return true;
}
}
return false;

}


private boolean crlCertValid(LDAPStore crlStore, X509Certificate certificate, ValidityStatus currentStatus) {
logger.info("OCSPEngine: validate of peer's certificate for the connection " + certificate.getSubjectX500Principal());
CRLIssuingPointRecord pt = null;
try {
X509CertImpl peerCert = new X509CertImpl(certificate.getEncoded());
Enumeration<CRLIssuingPointRecord> eCRL = crlStore.searchAllCRLIssuingPointRecord(-1);
AuthorityKeyIdentifierExtension peerAKIExt = (AuthorityKeyIdentifierExtension) peerCert.getExtension(PKIXExtensions.AuthorityKey_Id.toString());
if(peerAKIExt == null) {
logger.error("OCSPEngine: the certificate has not Authority Key Identifier Extension. CRL verification cannot be done.");
return false;
}
while (eCRL.hasMoreElements() && pt == null) {
CRLIssuingPointRecord tPt = eCRL.nextElement();
logger.debug("OCSPEngine: CRL check issuer " + tPt.getId());
X509CertImpl caCert = new X509CertImpl(tPt.getCACert());

try {
SubjectKeyIdentifierExtension caSKIExt = (SubjectKeyIdentifierExtension) caCert.getExtension(PKIXExtensions.SubjectKey_Id.toString());
if(caSKIExt == null) {
logger.error("OCSPEngine: signing certificate missing Subject Key Identifier. Skip CA " + caCert.getName());
continue;
}

KeyIdentifier caSKIId = (KeyIdentifier) caSKIExt.get(SubjectKeyIdentifierExtension.KEY_ID);
KeyIdentifier peerAKIId = (KeyIdentifier) peerAKIExt.get(AuthorityKeyIdentifierExtension.KEY_ID);
if(Arrays.equals(caSKIId.getIdentifier(), peerAKIId.getIdentifier())) {
pt = tPt;
}
} catch (IOException e) {
logger.error("OCSPEngine: problem extracting key from SKI/AKI: " + e.getMessage(), e);
}
}
} catch (EBaseException | CertificateException e) {
logger.error("OCSPEngine: problem find CRL issuing point for " + certificate.getIssuerX500Principal().toString());
return false;
}
if (pt == null) {
logger.error("OCSPEngine: CRL issuing point not found for " + certificate.getIssuerX500Principal().toString());
return false;
}
try {
X509CRLImpl crl = new X509CRLImpl(pt.getCRL());
X509CRLEntry crlentry = crl.getRevokedCertificate(certificate.getSerialNumber());

if (crlentry == null && crlStore.isNotFoundGood()) {
return true;
}
} catch (Exception e) {
logger.error("OCSPEngine: crl check error. " + e.getMessage(), e);
}
logger.info("OCSPEngine: peer certificate not valid");
return false;
}
}
Loading
Loading