// --- 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.BadRequestException; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.UnauthorizedException; 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; /** * @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 certificates = new ArrayList(); Collection revCertImpls = new ArrayList(); 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 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 = new ArrayList(); 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); } }