diff options
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.java | 501 |
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); + } +} |