summaryrefslogtreecommitdiffstats
path: root/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java
diff options
context:
space:
mode:
authorEndi Sukma Dewata <edewata@redhat.com>2012-05-23 14:14:38 -0500
committerEndi Sukma Dewata <edewata@redhat.com>2012-07-11 09:21:09 -0500
commita25705a6fff3525b26a855d03f0c117bfadc1979 (patch)
treef7bd74cd3c9e866e784c6561bcc12a315959c77e /base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java
parent778091c087b072a2e5c56ed1cffbee683d421363 (diff)
downloadpki-a25705a6fff3525b26a855d03f0c117bfadc1979.tar.gz
pki-a25705a6fff3525b26a855d03f0c117bfadc1979.tar.xz
pki-a25705a6fff3525b26a855d03f0c117bfadc1979.zip
Added cert revocation REST service.
The cert revocation REST service is based on DoRevoke and DoUnrevoke servlets. It provides an interface to manage certificate revocation. Ticket #161
Diffstat (limited to 'base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java')
-rw-r--r--base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java501
1 files changed, 501 insertions, 0 deletions
diff --git a/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java b/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java
new file mode 100644
index 000000000..3f0fffbf4
--- /dev/null
+++ b/base/common/src/com/netscape/cms/servlet/cert/RevocationProcessor.java
@@ -0,0 +1,501 @@
+// --- 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) 2012 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cms.servlet.cert;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Locale;
+
+import netscape.security.x509.CRLExtensions;
+import netscape.security.x509.CRLReasonExtension;
+import netscape.security.x509.InvalidityDateExtension;
+import netscape.security.x509.RevocationReason;
+import netscape.security.x509.RevokedCertImpl;
+import netscape.security.x509.X509CertImpl;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.EPropertyNotFound;
+import com.netscape.certsrv.ca.ICertificateAuthority;
+import com.netscape.certsrv.dbs.certdb.CertId;
+import com.netscape.certsrv.dbs.certdb.ICertRecord;
+import com.netscape.certsrv.dbs.certdb.ICertificateRepository;
+import com.netscape.certsrv.logging.AuditFormat;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.publish.IPublisherProcessor;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.IRequestQueue;
+import com.netscape.certsrv.request.RequestId;
+import com.netscape.certsrv.request.RequestStatus;
+import com.netscape.cms.servlet.base.BadRequestException;
+import com.netscape.cms.servlet.base.UnauthorizedException;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class RevocationProcessor extends CertProcessor {
+
+ public final static String REVOKE = "revoke";
+ public final static String ON_HOLD = "on-hold";
+ public final static String OFF_HOLD = "off-hold";
+
+ public final static String LOGGING_SIGNED_AUDIT_CERT_STATUS_CHANGE_REQUEST =
+ "LOGGING_SIGNED_AUDIT_CERT_STATUS_CHANGE_REQUEST_5";
+ public final static String LOGGING_SIGNED_AUDIT_CERT_STATUS_CHANGE_REQUEST_PROCESSED =
+ "LOGGING_SIGNED_AUDIT_CERT_STATUS_CHANGE_REQUEST_PROCESSED_7";
+
+ long startTime;
+
+ ICertificateAuthority authority;
+ ICertificateRepository repo;
+ IRequestQueue requestQueue;
+ IPublisherProcessor publisherProcessor;
+
+ String initiative;
+ RequestId requestID;
+ CertId serialNumber;
+ RevocationReason revocationReason;
+ Date invalidityDate;
+ String comments;
+ String requestType;
+
+ CRLExtensions entryExtn;
+ Collection<X509CertImpl> certificates = new ArrayList<X509CertImpl>();
+ Collection<RevokedCertImpl> revCertImpls = new ArrayList<RevokedCertImpl>();
+ IRequest request;
+ RequestStatus requestStatus;
+
+ public RevocationProcessor(String id, Locale locale) throws EPropertyNotFound, EBaseException {
+ super(id, locale);
+ }
+
+ public ICertificateAuthority getAuthority() {
+ return authority;
+ }
+
+ public void setAuthority(ICertificateAuthority authority) {
+ this.authority = authority;
+ repo = authority.getCertificateRepository();
+ requestQueue = authority.getRequestQueue();
+ publisherProcessor = authority.getPublisherProcessor();
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ public String getInitiative() {
+ return initiative;
+ }
+
+ public void setInitiative(String initiative) {
+ this.initiative = initiative;
+ }
+
+ public RequestId getRequestID() {
+ return requestID;
+ }
+
+ public void setRequestID(RequestId requestID) {
+ this.requestID = requestID;
+ }
+
+ public CertId getSerialNumber() {
+ return serialNumber;
+ }
+
+ public void setSerialNumber(CertId serialNumber) {
+ this.serialNumber = serialNumber;
+ }
+
+ public RevocationReason getRevocationReason() {
+ return revocationReason;
+ }
+
+ public void setRevocationReason(RevocationReason revocationReason) {
+ this.revocationReason = revocationReason;
+ }
+
+ public Date getInvalidityDate() {
+ return invalidityDate;
+ }
+
+ public void setInvalidityDate(Date invalidityDate) {
+ this.invalidityDate = invalidityDate;
+ }
+
+ public String getComments() {
+ return comments;
+ }
+
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+
+ public String getRequestType() {
+ return requestType;
+ }
+
+ public void setRequestType(String requestType) {
+ this.requestType = requestType;
+ }
+
+ public RequestStatus getRequestStatus() {
+ return requestStatus;
+ }
+
+ public void setRequestStatus(RequestStatus requestStatus) {
+ this.requestStatus = requestStatus;
+ }
+
+ public void addCertificate(X509CertImpl cert) {
+ certificates.add(cert);
+ }
+
+ public Collection<X509CertImpl> getCertificates() {
+ return certificates;
+ }
+
+ public IRequest getRequest() {
+ return request;
+ }
+
+ public void validateCertificateToRevoke(String clientSubjectDN, ICertRecord targetRecord, boolean revokingCACert) {
+
+ X509CertImpl targetCert = targetRecord.getCertificate();
+ BigInteger targetSerialNumber = targetCert.getSerialNumber();
+ Principal targetSubjectDN = targetCert.getSubjectDN();
+
+ // Verify client cert's subject DN matches the target cert's subject DN.
+ if (clientSubjectDN != null && !clientSubjectDN.equals(targetSubjectDN.toString())) {
+ throw new UnauthorizedException(
+ "Certificate 0x" + targetSerialNumber.toString(16) + " belongs to different subject.");
+ }
+
+ boolean targetIsCACert = isSystemCertificate(targetCert);
+
+ // If not revoking CA cert verify target cert is not CA cert.
+ if (!revokingCACert && targetIsCACert) {
+ throw new UnauthorizedException(
+ "Certificate 0x" + targetSerialNumber.toString(16) + " is a CA signing certificate");
+ }
+
+ // If revoking CA Cert verify target cert is CA cert.
+ if (revokingCACert && !targetIsCACert) {
+ throw new UnauthorizedException(
+ "Certificate 0x" + targetSerialNumber.toString(16) + " is not a CA signing certificate");
+ }
+
+ // Verify target cert is not already revoked.
+ if (targetRecord.getStatus().equals(ICertRecord.STATUS_REVOKED)) {
+ throw new BadRequestException(
+ CMS.getLogMessage("CA_CERTIFICATE_ALREADY_REVOKED_1", targetSerialNumber.toString(16)));
+ }
+ }
+
+ public void addCertificateToRevoke(X509CertImpl cert) {
+ addCertificate(cert);
+ revCertImpls.add(new RevokedCertImpl(cert.getSerialNumber(), CMS.getCurrentDate(), entryExtn));
+ }
+
+ public void addSerialNumberToUnrevoke(BigInteger serialNumber) throws EBaseException {
+ ICertRecord record = getCertificateRecord(serialNumber);
+ X509CertImpl cert = record.getCertificate();
+ addCertificate(cert);
+ }
+
+ public ICertRecord[] getCertificateRecords(BigInteger[] serialNumbers) throws EBaseException {
+ ICertRecord[] records = new ICertRecord[serialNumbers.length];
+ for (int i=0; i<serialNumbers.length; i++) {
+ records[i] = getCertificateRecord(serialNumbers[i]);
+ }
+ return records;
+ }
+
+ public ICertRecord getCertificateRecord(CertId id) throws EBaseException {
+ return getCertificateRecord(id.toBigInteger());
+ }
+
+ public ICertRecord getCertificateRecord(BigInteger serialNumber) throws EBaseException {
+ return repo.readCertificateRecord(serialNumber);
+ }
+
+ public X509CertImpl[] getCertificates(ICertRecord[] records) throws EBaseException {
+ X509CertImpl[] certs = new X509CertImpl[records.length];
+ for (int i=0; i<records.length; i++) {
+ certs[i] = records[i].getCertificate();
+ }
+ return certs;
+ }
+
+ public void createCRLExtension() throws IOException {
+
+ // Construct a CRL extension for this request.
+ entryExtn = new CRLExtensions();
+
+ // Construct a CRL reason code extension.
+ CRLReasonExtension crlReasonExtn = new CRLReasonExtension(revocationReason);
+ entryExtn.set(crlReasonExtn.getName(), crlReasonExtn);
+
+ // Construct a CRL invalidity date extension.
+ if (invalidityDate != null) {
+ InvalidityDateExtension invalidityDateExtn = new InvalidityDateExtension(invalidityDate);
+ entryExtn.set(invalidityDateExtn.getName(), invalidityDateExtn);
+ }
+ }
+
+ public void createRevocationRequest() throws EBaseException {
+
+ request = requestQueue.newRequest(IRequest.REVOCATION_REQUEST);
+
+ request.setExtData(IRequest.REQ_TYPE, IRequest.REVOCATION_REQUEST);
+
+ request.setExtData(IRequest.OLD_CERTS, certificates.toArray(new X509CertImpl[certificates.size()]));
+ request.setExtData(IRequest.CERT_INFO, revCertImpls.toArray(new RevokedCertImpl[revCertImpls.size()]));
+
+ if (AuditFormat.FROMUSER.equals(initiative)) {
+ request.setExtData(IRequest.REQUESTOR_TYPE, IRequest.REQUESTOR_EE);
+ } else {
+ request.setExtData(IRequest.REQUESTOR_TYPE, IRequest.REQUESTOR_AGENT);
+ }
+
+ if (comments != null) {
+ request.setExtData(IRequest.REQUESTOR_COMMENTS, comments);
+ }
+
+ request.setExtData(IRequest.REVOKED_REASON, revocationReason.toInt());
+ }
+
+ public void processRevocationRequest() throws EBaseException {
+
+ requestQueue.processRequest(request);
+ requestStatus = request.getRequestStatus();
+
+ CMS.debug("revokeCert: status: " + requestStatus);
+
+ String type = request.getRequestType();
+
+ // The SVC_PENDING check has been added for the Cloned CA request
+ // that is meant for the Master CA. From Clone's point of view
+ // the request is complete
+
+ if (requestStatus == RequestStatus.COMPLETE
+ || requestStatus == RequestStatus.SVC_PENDING
+ && type.equals(IRequest.CLA_CERT4CRL_REQUEST)) {
+
+ // audit log the error
+ Integer result = request.getExtDataInInteger(IRequest.RESULT);
+
+ if (result.equals(IRequest.RES_ERROR)) {
+ String[] svcErrors = request.getExtDataInStringArray(IRequest.SVCERRORS);
+
+ if (svcErrors != null) {
+ for (String err : svcErrors) {
+ //cmsReq.setErrorDescription(err);
+ for (X509CertImpl cert : certificates) {
+ logRevoke(
+ request, cert,
+ "completed with error: " + err,
+ revocationReason.toString());
+ }
+ }
+ }
+
+ throw new EBaseException(CMS.getLogMessage("CMSGW_ERROR_MARKING_CERT_REVOKED"));
+ }
+
+ long endTime = CMS.getCurrentDate().getTime();
+
+ // audit log the success.
+ for (X509CertImpl cert : certificates) {
+ logRevoke(request, cert,
+ "completed",
+ revocationReason + " time: " + (endTime - startTime));
+ }
+
+ } else {
+
+ // audit log the pending, revoked and rest
+ for (X509CertImpl cert : certificates) {
+ logRevoke(request, cert,
+ requestStatus.toString(),
+ revocationReason.toString());
+ }
+ }
+ }
+
+ public void createUnrevocationRequest() throws EBaseException {
+
+ request = requestQueue.newRequest(IRequest.UNREVOCATION_REQUEST);
+
+ request.setExtData(IRequest.REQ_TYPE, IRequest.UNREVOCATION_REQUEST);
+
+ Collection<BigInteger> serialNumbers = new ArrayList<BigInteger>();
+ for (X509CertImpl cert : certificates) {
+ serialNumbers.add(cert.getSerialNumber());
+ }
+ request.setExtData(IRequest.OLD_SERIALS, serialNumbers.toArray(new BigInteger[serialNumbers.size()]));
+ request.setExtData(IRequest.REQUESTOR_TYPE, IRequest.REQUESTOR_AGENT);
+ }
+
+ public void processUnrevocationRequest() throws EBaseException {
+
+ requestQueue.processRequest(request);
+ requestStatus = request.getRequestStatus();
+
+ String type = request.getRequestType();
+
+ if (requestStatus == RequestStatus.COMPLETE
+ || requestStatus == RequestStatus.SVC_PENDING && type.equals(IRequest.CLA_UNCERT4CRL_REQUEST)) {
+
+ Integer result = request.getExtDataInInteger(IRequest.RESULT);
+
+ if (result != null && result.equals(IRequest.RES_SUCCESS)) {
+ for (X509CertImpl cert : certificates) {
+ logUnrevoke(request, cert, "completed");
+ }
+
+ } else {
+ String error = request.getExtDataInString(IRequest.ERROR);
+ for (X509CertImpl cert : certificates) {
+ logUnrevoke(request, cert, "completed with error: " + error);
+ }
+ }
+
+ } else {
+ for (X509CertImpl cert : certificates) {
+ logUnrevoke(request, cert, requestStatus.toString());
+ }
+ }
+ }
+
+ /**
+ * A system certificate such as the CA signing certificate
+ * should not be allowed to delete.
+ * The main purpose is to avoid revoking the self signed
+ * CA certificate accidentally.
+ */
+ public boolean isSystemCertificate(X509Certificate cert) {
+
+ X509Certificate caCert = authority.getCACert();
+ if (caCert == null)
+ return false;
+
+ // check whether it's a CA certificate
+ if (!caCert.getSerialNumber().equals(cert.getSerialNumber()))
+ return false;
+
+ // check whether it's a self-signed we certificate
+ return caCert.getSubjectDN().equals(caCert.getIssuerDN());
+ }
+
+ public void logRevoke(IRequest revocationRequest, X509Certificate cert, String status, String message) {
+
+ if (logger == null)
+ return;
+
+ logger.log(
+ ILogger.EV_AUDIT,
+ ILogger.S_OTHER,
+ AuditFormat.LEVEL,
+ AuditFormat.DOREVOKEFORMAT,
+ new Object[] {
+ revocationRequest.getRequestId(),
+ initiative,
+ status,
+ cert.getSubjectDN(),
+ cert.getSerialNumber().toString(16),
+ message
+ });
+ }
+
+ public void logUnrevoke(IRequest unrevocationRequest, X509Certificate cert, String status) {
+
+ if (logger == null)
+ return;
+
+ logger.log(
+ ILogger.EV_AUDIT,
+ ILogger.S_OTHER,
+ AuditFormat.LEVEL,
+ AuditFormat.DOUNREVOKEFORMAT,
+ new Object[] {
+ unrevocationRequest.getRequestId(),
+ initiative,
+ status,
+ cert.getSubjectDN(),
+ cert.getSerialNumber().toString(16),
+ });
+ }
+
+ public void auditChangeRequest(String status) {
+
+ if (auditor == null)
+ return;
+
+ String auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_CERT_STATUS_CHANGE_REQUEST,
+ auditor.getSubjectID(),
+ status,
+ requestID == null ? ILogger.UNIDENTIFIED : requestID.toString(),
+ serialNumber == null ? ILogger.SIGNED_AUDIT_EMPTY_VALUE : serialNumber.toHexString(),
+ requestType);
+
+ auditor.log(auditMessage);
+ }
+
+ public void auditChangeRequestProcessed(String status) {
+
+ if (auditor == null)
+ return;
+
+ // store a message in the signed audit log file
+ // if and only if "requestStatus" is
+ // "complete", "revoked", or "canceled"
+
+ if (!(requestStatus == RequestStatus.COMPLETE
+ || requestStatus == RequestStatus.REJECTED
+ || requestStatus == RequestStatus.CANCELED)) return;
+
+ String auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_CERT_STATUS_CHANGE_REQUEST_PROCESSED,
+ auditor.getSubjectID(),
+ status,
+ requestID == null ? ILogger.UNIDENTIFIED : requestID.toString(),
+ serialNumber == null ? ILogger.SIGNED_AUDIT_EMPTY_VALUE : serialNumber.toHexString(),
+ requestType,
+ String.valueOf(revocationReason.toInt()),
+ requestStatus == null ? ILogger.SIGNED_AUDIT_EMPTY_VALUE : requestStatus.toString());
+
+ auditor.log(auditMessage);
+ }
+
+ public void log(int level, String message) {
+ log(ILogger.S_CA, level, message);
+ }
+}