// In that case it is updated once we have read the certificate status of the certificate searched for.
long maxAge = OcspConfiguration.getMaxAge(SecConst.CERTPROFILE_NO_PROFILE);
long nextUpdate = OcspConfiguration.getUntilNextUpdate(SecConst.CERTPROFILE_NO_PROFILE);
OCSPResp ocspresp = null;
OCSPRespGenerator res = new OCSPRespGenerator();
X509Certificate cacert = null; // CA-certificate used to sign response
try {
byte[] reqBytes = checkAndGetRequestBytes(request);
// Start logging process time after we have received the request
transactionLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
auditLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
auditLogger.paramPut(IAuditLogger.OCSPREQUEST, new String (Hex.encode(reqBytes)));
OCSPReq req = null;
try {
req = new OCSPReq(reqBytes);
} catch (Exception e) {
// When not being able to parse the request, we want to send a MalformedRequest back
throw new MalformedRequestException(e);
}
if (req.getRequestorName() == null) {
m_log.debug("Requestorname is null");
} else {
if (m_log.isDebugEnabled()) {
m_log.debug("Requestorname is: "+req.getRequestorName().toString());
}
transactionLogger.paramPut(ITransactionLogger.REQ_NAME, req.getRequestorName().toString());
}
// Make sure our signature keys are updated
loadPrivateKeys(this.data.m_adm, null);
/**
* check the signature if contained in request.
* if the request does not contain a signature
* and the servlet is configured in the way
* the a signature is required we send back
* 'sigRequired' response.
*/
if (m_log.isDebugEnabled()) {
m_log.debug("Incoming OCSP request is signed : " + req.isSigned());
}
if (req.isSigned()) {
X509Certificate signercert = OCSPUtil.checkRequestSignature(request.getRemoteAddr(), req, this.data.m_caCertCache);
String signercertIssuerName = CertTools.getIssuerDN(signercert);
BigInteger signercertSerNo = CertTools.getSerialNumber(signercert);
String signercertSubjectName = CertTools.getSubjectDN(signercert);
transactionLogger.paramPut(ITransactionLogger.SIGN_ISSUER_NAME_DN, signercertIssuerName);
transactionLogger.paramPut(ITransactionLogger.SIGN_SERIAL_NO, signercert.getSerialNumber().toByteArray());
transactionLogger.paramPut(ITransactionLogger.SIGN_SUBJECT_NAME, signercertSubjectName);
transactionLogger.paramPut(IPatternLogger.REPLY_TIME, ITransactionLogger.REPLY_TIME);
if (OcspConfiguration.getEnforceRequestSigning()) {
// If it verifies OK, check if it is revoked
final CertificateStatus status = this.data.certificateStoreSession.getStatus(CertTools.getIssuerDN(signercert), CertTools.getSerialNumber(signercert));
// If rci == null it means the certificate does not exist in database, we then treat it as ok,
// because it may be so that only revoked certificates is in the (external) OCSP database.
if ( status.equals(CertificateStatus.REVOKED) ) {
String serno = signercertSerNo.toString(16);
String infoMsg = intres.getLocalizedMessage("ocsp.infosigner.revoked", signercertSubjectName, signercertIssuerName, serno);
m_log.info(infoMsg);
throw new SignRequestSignatureException(infoMsg);
}
if (m_reqRestrictSignatures) {
loadTrustDir();
if ( m_reqRestrictMethod == OcspConfiguration.RESTRICTONSIGNER) {
if (!OCSPUtil.checkCertInList(signercert, mTrustedReqSigSigners)) {
String infoMsg = intres.getLocalizedMessage("ocsp.infosigner.notallowed", signercertSubjectName, signercertIssuerName, signercertSerNo.toString(16));
m_log.info(infoMsg);
throw new SignRequestSignatureException(infoMsg);
}
} else if (m_reqRestrictMethod == OcspConfiguration.RESTRICTONISSUER) {
X509Certificate signerca = this.data.m_caCertCache.findLatestBySubjectDN(HashID.getFromDN(signercertIssuerName));
if ((signerca == null) || (!OCSPUtil.checkCertInList(signerca, mTrustedReqSigIssuers)) ) {
String infoMsg = intres.getLocalizedMessage("ocsp.infosigner.notallowed", signercertSubjectName, signercertIssuerName, signercertSerNo.toString(16));
m_log.info(infoMsg);
throw new SignRequestSignatureException(infoMsg);
}
} else {
throw new Exception("m_reqRestrictMethod="+m_reqRestrictMethod); // there must be an internal error. We do not want to send a response, just to be safe.
}
}
}
} else {
if (OcspConfiguration.getEnforceRequestSigning()) {
// Signature required
throw new SignRequestException("Signature required");
}
}
// Get the certificate status requests that are inside this OCSP req
Req[] requests = req.getRequestList();
transactionLogger.paramPut(ITransactionLogger.NUM_CERT_ID, requests.length);
if (requests.length <= 0) {
String infoMsg = intres.getLocalizedMessage("ocsp.errornoreqentities");
m_log.info(infoMsg);
{
// All this just so we can create an error response
cacert = this.data.m_caCertCache.findLatestBySubjectDN(HashID.getFromDN(this.data.m_defaultResponderId));
}
throw new MalformedRequestException(infoMsg);
}
int maxRequests = 100;
if (requests.length > maxRequests) {
String infoMsg = intres.getLocalizedMessage("ocsp.errortoomanyreqentities", maxRequests);
m_log.info(infoMsg);
{
// All this just so we can create an error response
cacert = this.data.m_caCertCache.findLatestBySubjectDN(HashID.getFromDN(this.data.m_defaultResponderId));
}
throw new MalformedRequestException(infoMsg);
}
if (m_log.isDebugEnabled()) {
m_log.debug("The OCSP request contains " + requests.length + " simpleRequests.");
}
// Add standard response extensions
Hashtable responseExtensions = OCSPUtil.getStandardResponseExtensions(req);
transactionLogger.paramPut(ITransactionLogger.STATUS, OCSPRespGenerator.SUCCESSFUL);
auditLogger.paramPut(IAuditLogger.STATUS, OCSPRespGenerator.SUCCESSFUL);
// Look over the status requests
ArrayList responseList = new ArrayList();
for (int i = 0; i < requests.length; i++) {
CertificateID certId = requests[i].getCertID();
// now some Logging
transactionLogger.paramPut(ITransactionLogger.SERIAL_NOHEX, certId.getSerialNumber().toByteArray());
transactionLogger.paramPut(ITransactionLogger.DIGEST_ALGOR, certId.getHashAlgOID()); //todo, find text version of this or find out if it should be something else
transactionLogger.paramPut(ITransactionLogger.ISSUER_NAME_HASH, certId.getIssuerNameHash());
transactionLogger.paramPut(ITransactionLogger.ISSUER_KEY, certId.getIssuerKeyHash());
auditLogger.paramPut(IAuditLogger.ISSUER_KEY, certId.getIssuerKeyHash());
auditLogger.paramPut(IAuditLogger.SERIAL_NOHEX, certId.getSerialNumber().toByteArray());
auditLogger.paramPut(IAuditLogger.ISSUER_NAME_HASH, certId.getIssuerNameHash());
byte[] hashbytes = certId.getIssuerNameHash();
String hash = null;
if (hashbytes != null) {
hash = new String(Hex.encode(hashbytes));
}
String infoMsg = intres.getLocalizedMessage("ocsp.inforeceivedrequest", certId.getSerialNumber().toString(16), hash, request.getRemoteAddr());
m_log.info(infoMsg);
boolean unknownCA = false;
// if the certId was issued by an unknown CA
// The algorithm here:
// We will sign the response with the CA that issued the first
// certificate(certId) in the request. If the issuing CA is not available
// on this server, we sign the response with the default responderId (from params in web.xml).
// We have to look up the ca-certificate for each certId in the request though, as we will check
// for revocation on the ca-cert as well when checking for revocation on the certId.
cacert = this.data.m_caCertCache.findByOcspHash(certId); // Get the issuer of certId
if (cacert == null) {
// We could not find certificate for this request so get certificate for default responder
cacert = this.data.m_caCertCache.findLatestBySubjectDN(HashID.getFromDN(this.data.m_defaultResponderId));
unknownCA = true;
}
if (cacert == null) {
String errMsg = intres.getLocalizedMessage("ocsp.errorfindcacert", new String(Hex.encode(certId.getIssuerNameHash())), this.data.m_defaultResponderId);
m_log.error(errMsg);
continue;
}
if (unknownCA == true) {
String errMsg = intres.getLocalizedMessage("ocsp.errorfindcacertusedefault", new String(Hex.encode(certId.getIssuerNameHash())));
m_log.info(errMsg);
// If we can not find the CA, answer UnknowStatus
responseList.add(new OCSPResponseItem(certId, new UnknownStatus(), nextUpdate));
transactionLogger.paramPut(ITransactionLogger.CERT_STATUS, OCSPUnidResponse.OCSP_UNKNOWN);
transactionLogger.writeln();
continue;
} else {
transactionLogger.paramPut(ITransactionLogger.ISSUER_NAME_DN, cacert.getSubjectDN().getName());
}
/*
* Implement logic according to
* chapter 2.7 in RFC2560
*
* 2.7 CA Key Compromise
* If an OCSP responder knows that a particular CA's private key has
* been compromised, it MAY return the revoked state for all
* certificates issued by that CA.
*/
final org.bouncycastle.ocsp.CertificateStatus certStatus;
transactionLogger.paramPut(ITransactionLogger.CERT_STATUS, OCSPUnidResponse.OCSP_GOOD); // it seems to be correct
// Check if the cacert (or the default responderid) is revoked
final CertificateStatus cacertStatus = this.data.certificateStoreSession.getStatus(CertTools.getIssuerDN(cacert), CertTools.getSerialNumber(cacert));
if ( !cacertStatus.equals(CertificateStatus.REVOKED) ) {
// Check if cert is revoked
final CertificateStatus status = this.data.certificateStoreSession.getStatus(cacert.getSubjectDN().getName(), certId.getSerialNumber());
// If we have different maxAge and untilNextUpdate for different certificate profiles, we have to fetch these
// values now that we have fetched the certificate status, that includes certificate profile.
nextUpdate = OcspConfiguration.getUntilNextUpdate(status.certificateProfileId);
maxAge = OcspConfiguration.getMaxAge(status.certificateProfileId);
if (m_log.isDebugEnabled()) {
m_log.debug("Set nextUpdate="+nextUpdate+", and maxAge="+maxAge+" for certificateProfileId="+status.certificateProfileId);
}
final String sStatus;
if (status.equals(CertificateStatus.NOT_AVAILABLE)) {
// No revocation info available for this cert, handle it
if (m_log.isDebugEnabled()) {
m_log.debug("Unable to find revocation information for certificate with serial '"
+ certId.getSerialNumber().toString(16) + "'"
+ " from issuer '" + cacert.getSubjectDN().getName() + "'");
}
// If we do not treat non existing certificates as good
// OR
// we don't actually handle requests for the CA issuing the certificate asked about
// then we return unknown
if ( (!m_nonExistingIsGood) || (this.data.m_caCertCache.findByOcspHash(certId) == null) ) {
sStatus = "unknown";
certStatus = new UnknownStatus();
transactionLogger.paramPut(ITransactionLogger.CERT_STATUS, OCSPUnidResponse.OCSP_UNKNOWN);
} else {
sStatus = "good";
certStatus = null; // null means "good" in OCSP
transactionLogger.paramPut(ITransactionLogger.CERT_STATUS, OCSPUnidResponse.OCSP_GOOD);
}
} else if ( status.equals(CertificateStatus.REVOKED) ) {
// Revocation info available for this cert, handle it
sStatus ="revoked";
certStatus = new RevokedStatus(new RevokedInfo(new DERGeneralizedTime(status.revocationDate),
new CRLReason(status.revocationReason)));
transactionLogger.paramPut(ITransactionLogger.CERT_STATUS, OCSPUnidResponse.OCSP_REVOKED); //1 = revoked
} else {
sStatus = "good";
certStatus = null;
transactionLogger.paramPut(ITransactionLogger.CERT_STATUS, OCSPUnidResponse.OCSP_GOOD);
}
infoMsg = intres.getLocalizedMessage("ocsp.infoaddedstatusinfo", sStatus, certId.getSerialNumber().toString(16), cacert.getSubjectDN().getName());
m_log.info(infoMsg);
responseList.add(new OCSPResponseItem(certId, certStatus, nextUpdate));
transactionLogger.writeln();
} else {
certStatus = new RevokedStatus(new RevokedInfo(new DERGeneralizedTime(cacertStatus.revocationDate),
new CRLReason(cacertStatus.revocationReason)));
infoMsg = intres.getLocalizedMessage("ocsp.infoaddedstatusinfo", "revoked", certId.getSerialNumber().toString(16), cacert.getSubjectDN().getName());
m_log.info(infoMsg);
responseList.add(new OCSPResponseItem(certId, certStatus, nextUpdate));
transactionLogger.paramPut(ITransactionLogger.CERT_STATUS, OCSPUnidResponse.OCSP_REVOKED);
transactionLogger.writeln();
}
// Look for extension OIDs
Iterator iter = m_extensionOids.iterator();
while (iter.hasNext()) {
String oidstr = (String)iter.next();
DERObjectIdentifier oid = new DERObjectIdentifier(oidstr);
X509Extensions reqexts = req.getRequestExtensions();
if (reqexts != null) {
X509Extension ext = reqexts.getExtension(oid);
if (null != ext) {
// We found an extension, call the extenstion class
if (m_log.isDebugEnabled()) {
m_log.debug("Found OCSP extension oid: "+oidstr);
}
IOCSPExtension extObj = (IOCSPExtension)m_extensionMap.get(oidstr);
if (extObj != null) {
// Find the certificate from the certId
X509Certificate cert = null;
cert = (X509Certificate)this.data.certificateStoreSession.findCertificateByIssuerAndSerno(this.data.m_adm, cacert.getSubjectDN().getName(), certId.getSerialNumber());
if (cert != null) {
// Call the OCSP extension
Hashtable retext = extObj.process(request, cert, certStatus);
if (retext != null) {
// Add the returned X509Extensions to the responseExtension we will add to the basic OCSP response
responseExtensions.putAll(retext);
} else {
String errMsg = intres.getLocalizedMessage("ocsp.errorprocessextension", extObj.getClass().getName(), new Integer(extObj.getLastErrorCode()));
m_log.error(errMsg);
}
}
}
}
}
}
} // end of huge for loop
if (cacert != null) {
// Add responseExtensions
X509Extensions exts = new X509Extensions(responseExtensions);
// generate the signed response object
BasicOCSPResp basicresp = signOCSPResponse(req, responseList, exts, cacert);
ocspresp = res.generate(OCSPRespGenerator.SUCCESSFUL, basicresp);
auditLogger.paramPut(IAuditLogger.STATUS, OCSPRespGenerator.SUCCESSFUL);
transactionLogger.paramPut(ITransactionLogger.STATUS, OCSPRespGenerator.SUCCESSFUL);
} else {
// Only unknown CAs in requests and no default reponders cert
String errMsg = intres.getLocalizedMessage("ocsp.errornocacreateresp");
m_log.error(errMsg);
throw new ServletException(errMsg);
}
} catch (MalformedRequestException e) {
transactionLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
auditLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
String errMsg = intres.getLocalizedMessage("ocsp.errorprocessreq", e.getMessage());
m_log.info(errMsg);
if (m_log.isDebugEnabled()) {
m_log.debug(errMsg, e);
}
ocspresp = res.generate(OCSPRespGenerator.MALFORMED_REQUEST, null); // RFC 2560: responseBytes are not set on error.
transactionLogger.paramPut(ITransactionLogger.STATUS, OCSPRespGenerator.MALFORMED_REQUEST);
transactionLogger.writeln();
auditLogger.paramPut(IAuditLogger.STATUS, OCSPRespGenerator.MALFORMED_REQUEST);
} catch (SignRequestException e) {
transactionLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
auditLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
String errMsg = intres.getLocalizedMessage("ocsp.errorprocessreq", e.getMessage());
m_log.info(errMsg); // No need to log the full exception here
ocspresp = res.generate(OCSPRespGenerator.SIG_REQUIRED, null); // RFC 2560: responseBytes are not set on error.
transactionLogger.paramPut(ITransactionLogger.STATUS, OCSPRespGenerator.SIG_REQUIRED);
transactionLogger.writeln();
auditLogger.paramPut(IAuditLogger.STATUS, OCSPRespGenerator.SIG_REQUIRED);
} catch (SignRequestSignatureException e) {
transactionLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
auditLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
String errMsg = intres.getLocalizedMessage("ocsp.errorprocessreq", e.getMessage());
m_log.info(errMsg); // No need to log the full exception here
ocspresp = res.generate(OCSPRespGenerator.UNAUTHORIZED, null); // RFC 2560: responseBytes are not set on error.
transactionLogger.paramPut(ITransactionLogger.STATUS, OCSPRespGenerator.UNAUTHORIZED);
transactionLogger.writeln();
auditLogger.paramPut(IAuditLogger.STATUS, OCSPRespGenerator.UNAUTHORIZED);
} catch (InvalidKeyException e) {
transactionLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
auditLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
String errMsg = intres.getLocalizedMessage("ocsp.errorprocessreq", e.getMessage());
m_log.info(errMsg, e);
ocspresp = res.generate(OCSPRespGenerator.UNAUTHORIZED, null); // RFC 2560: responseBytes are not set on error.
transactionLogger.paramPut(ITransactionLogger.STATUS, OCSPRespGenerator.UNAUTHORIZED);
transactionLogger.writeln();
auditLogger.paramPut(IAuditLogger.STATUS, OCSPRespGenerator.UNAUTHORIZED);
} catch (Throwable e) {
transactionLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
auditLogger.paramPut(IPatternLogger.PROCESS_TIME, IPatternLogger.PROCESS_TIME);
String errMsg = intres.getLocalizedMessage("ocsp.errorprocessreq", e.getMessage());
m_log.error(errMsg, e);
ocspresp = res.generate(OCSPRespGenerator.INTERNAL_ERROR, null); // RFC 2560: responseBytes are not set on error.
transactionLogger.paramPut(ITransactionLogger.STATUS, OCSPRespGenerator.INTERNAL_ERROR);
transactionLogger.writeln();
auditLogger.paramPut(IAuditLogger.STATUS, OCSPRespGenerator.INTERNAL_ERROR);
}
byte[] respBytes = ocspresp.getEncoded();
auditLogger.paramPut(IAuditLogger.OCSPRESPONSE, new String (Hex.encode(respBytes)));
auditLogger.writeln();
auditLogger.flush();
transactionLogger.flush();
if (mDoSaferLogging){
// See if the Errorhandler has found any problems
if (hasErrorHandlerFailedSince(startTime)) {
m_log.info("ProbableErrorhandler reported error, cannot answer request");
ocspresp = res.generate(OCSPRespGenerator.INTERNAL_ERROR, null); // RFC 2560: responseBytes are not set on error.
respBytes = ocspresp.getEncoded();
}
// See if the Appender has reported any problems
if (!canlog) {
m_log.info("SaferDailyRollingFileAppender reported error, cannot answer request");
ocspresp = res.generate(OCSPRespGenerator.INTERNAL_ERROR, null); // RFC 2560: responseBytes are not set on error.
respBytes = ocspresp.getEncoded();
}
}
response.setContentType("application/ocsp-response");
//response.setHeader("Content-transfer-encoding", "binary");