// --- 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) 2007 Red Hat, Inc. // All rights reserved. // --- END COPYRIGHT BLOCK --- package com.netscape.cms.servlet.processors; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.cert.CertificateException; import netscape.security.util.ObjectIdentifier; import netscape.security.x509.CertificateExtensions; import netscape.security.x509.CertificateSubjectName; import netscape.security.x509.CertificateValidity; import netscape.security.x509.CertificateVersion; import netscape.security.x509.CertificateX509Key; import netscape.security.x509.Extension; import netscape.security.x509.X500Name; import netscape.security.x509.X509CertInfo; import netscape.security.x509.X509Key; import org.mozilla.jss.asn1.INTEGER; import org.mozilla.jss.asn1.InvalidBERException; import org.mozilla.jss.asn1.SEQUENCE; import org.mozilla.jss.pkix.crmf.CertReqMsg; import org.mozilla.jss.pkix.crmf.CertRequest; import org.mozilla.jss.pkix.crmf.CertTemplate; import org.mozilla.jss.pkix.crmf.ProofOfPossession; import org.mozilla.jss.pkix.primitive.Name; import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.authentication.AuthToken; import com.netscape.certsrv.authentication.IAuthToken; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.IArgBlock; import com.netscape.certsrv.logging.ILogger; import com.netscape.certsrv.request.IRequest; import com.netscape.cms.servlet.base.CMSServlet; import com.netscape.cms.servlet.common.CMSRequest; import com.netscape.cms.servlet.common.ECMSGWException; /** * Process CRMF requests, according to RFC 2511 * See http://www.ietf.org/rfc/rfc2511.txt * * @version $Revision$, $Date$ */ public class CRMFProcessor extends PKIProcessor { private CMSRequest mRequest; private boolean enforcePop = false; private final static String LOGGING_SIGNED_AUDIT_PROOF_OF_POSSESSION = "LOGGING_SIGNED_AUDIT_PROOF_OF_POSSESSION_2"; public CRMFProcessor() { super(); } public CRMFProcessor(CMSRequest cmsReq, CMSServlet servlet, boolean doEnforcePop) { super(cmsReq, servlet); enforcePop = doEnforcePop; mRequest = cmsReq; } public void process(CMSRequest cmsReq) throws EBaseException { } /** * Verify Proof of Possession (POP) *

* *

* * @param certReqMsg the certificate request message * @exception EBaseException an error has occurred */ private void verifyPOP(CertReqMsg certReqMsg) throws EBaseException { String auditMessage = null; String auditSubjectID = auditSubjectID(); try { CMS.debug("CRMFProcessor: verifyPOP"); if (certReqMsg.hasPop()) { ProofOfPossession pop = certReqMsg.getPop(); ProofOfPossession.Type popType = pop.getType(); if (popType == ProofOfPossession.SIGNATURE) { CMS.debug("CRMFProcessor: Request has pop."); try { certReqMsg.verify(); // store a message in the signed audit log file auditMessage = CMS.getLogMessage( LOGGING_SIGNED_AUDIT_PROOF_OF_POSSESSION, auditSubjectID, ILogger.SUCCESS); audit(auditMessage); } catch (Exception e) { CMS.debug("CRMFProcessor: Failed POP verify!"); log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_POP_VERIFY")); // store a message in the signed audit log file auditMessage = CMS.getLogMessage( LOGGING_SIGNED_AUDIT_PROOF_OF_POSSESSION, auditSubjectID, ILogger.FAILURE); audit(auditMessage); throw new ECMSGWException( CMS.getLogMessage("CMSGW_ERROR_POP_VERIFY")); } } } else { if (enforcePop == true) { log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_NO_POP")); // store a message in the signed audit log file auditMessage = CMS.getLogMessage( LOGGING_SIGNED_AUDIT_PROOF_OF_POSSESSION, auditSubjectID, ILogger.FAILURE); audit(auditMessage); throw new ECMSGWException( CMS.getLogMessage("CMSGW_ERROR_NO_POP")); } } } catch (EBaseException eAudit1) { // store a message in the signed audit log file auditMessage = CMS.getLogMessage( LOGGING_SIGNED_AUDIT_PROOF_OF_POSSESSION, auditSubjectID, ILogger.FAILURE); audit(auditMessage); } } public X509CertInfo processIndividualRequest(CertReqMsg certReqMsg, IAuthToken authToken, IArgBlock httpParams) throws EBaseException { CMS.debug("CRMFProcessor::processIndividualRequest!"); try { verifyPOP(certReqMsg); CertRequest certReq = certReqMsg.getCertReq(); CertTemplate certTemplate = certReq.getCertTemplate(); X509CertInfo certInfo = CMS.getDefaultX509CertInfo(); // get key SubjectPublicKeyInfo spki = certTemplate.getPublicKey(); ByteArrayOutputStream keyout = new ByteArrayOutputStream(); spki.encode(keyout); byte[] keybytes = keyout.toByteArray(); X509Key key = new X509Key(); key.decode(keybytes); certInfo.set(X509CertInfo.KEY, new CertificateX509Key(key)); // field suggested notBefore and notAfter in CRMF // Tech Support #383184 if (certTemplate.getNotBefore() != null || certTemplate.getNotAfter() != null) { CertificateValidity certValidity = new CertificateValidity(certTemplate.getNotBefore(), certTemplate.getNotAfter()); certInfo.set(X509CertInfo.VALIDITY, certValidity); } if (certTemplate.hasSubject()) { Name subjectdn = certTemplate.getSubject(); ByteArrayOutputStream subjectEncStream = new ByteArrayOutputStream(); subjectdn.encode(subjectEncStream); byte[] subjectEnc = subjectEncStream.toByteArray(); X500Name subject = new X500Name(subjectEnc); certInfo.set(X509CertInfo.SUBJECT, new CertificateSubjectName(subject)); } else if (authToken == null || authToken.getInString(AuthToken.TOKEN_CERT_SUBJECT) == null) { // No subject name - error! log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_MISSING_SUBJECT_NAME_FROM_AUTHTOKEN")); throw new ECMSGWException( CMS.getUserMessage("CMS_GW_MISSING_SUBJECT_NAME_FROM_AUTHTOKEN")); } // get extensions CertificateExtensions extensions = null; try { extensions = (CertificateExtensions) certInfo.get(X509CertInfo.EXTENSIONS); } catch (CertificateException e) { extensions = null; } catch (IOException e) { extensions = null; } if (certTemplate.hasExtensions()) { // put each extension from CRMF into CertInfo. // index by extension name, consistent with // CertificateExtensions.parseExtension() method. if (extensions == null) extensions = new CertificateExtensions(); int numexts = certTemplate.numExtensions(); for (int j = 0; j < numexts; j++) { org.mozilla.jss.pkix.cert.Extension jssext = certTemplate.extensionAt(j); boolean isCritical = jssext.getCritical(); org.mozilla.jss.asn1.OBJECT_IDENTIFIER jssoid = jssext.getExtnId(); long[] numbers = jssoid.getNumbers(); int[] oidNumbers = new int[numbers.length]; for (int k = numbers.length - 1; k >= 0; k--) { oidNumbers[k] = (int) numbers[k]; } ObjectIdentifier oid = new ObjectIdentifier(oidNumbers); org.mozilla.jss.asn1.OCTET_STRING jssvalue = jssext.getExtnValue(); ByteArrayOutputStream jssvalueout = new ByteArrayOutputStream(); jssvalue.encode(jssvalueout); byte[] extValue = jssvalueout.toByteArray(); Extension ext = new Extension(oid, isCritical, extValue); extensions.parseExtension(ext); } certInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); certInfo.set(X509CertInfo.EXTENSIONS, extensions); } // Added a new configuration parameter // eeGateway.Enrollment.authTokenOverride=[true|false] // By default, it is set to true. In most // of the case, administrator would want // to have the control of the subject name // formulation. // -- CRMFfillCert if (authToken != null && authToken.getInString(AuthToken.TOKEN_CERT_SUBJECT) != null) { // if authenticated override subect name, validity and // extensions if any from authtoken. fillCertInfoFromAuthToken(certInfo, authToken); } // SPECIAL CASE: // if it is adminEnroll servlet, get the validity // from the http parameters. if (mServletId.equals(PKIProcessor.ADMIN_ENROLL_SERVLET_ID)) { fillValidityFromForm(certInfo, httpParams); } return certInfo; } catch (CertificateException e) { log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_CRMF_TO_CERTINFO_1", e.toString())); throw new ECMSGWException( CMS.getUserMessage("CMS_GW_CRMF_TO_CERTINFO_ERROR")); } catch (IOException e) { log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_CRMF_TO_CERTINFO_1", e.toString())); throw new ECMSGWException( CMS.getUserMessage("CMS_GW_CRMF_TO_CERTINFO_ERROR")); } /* catch (InvalidBERException e) { log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_CRMF_TO_CERTINFO_1",e.toString())); throw new ECMSGWException( CMSGWResources.ERROR_CRMF_TO_CERTINFO); } */catch (InvalidKeyException e) { log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_CRMF_TO_CERTINFO_1", e.toString())); throw new ECMSGWException( CMS.getUserMessage("CMS_GW_CRMF_TO_CERTINFO_ERROR")); } } public X509CertInfo[] fillCertInfoArray( String protocolString, IAuthToken authToken, IArgBlock httpParams, IRequest req) throws EBaseException { CMS.debug("CRMFProcessor.fillCertInfoArray!"); String crmf = protocolString; try { byte[] crmfBlob = CMS.AtoB(crmf); ByteArrayInputStream crmfBlobIn = new ByteArrayInputStream(crmfBlob); SEQUENCE crmfMsgs = (SEQUENCE) new SEQUENCE.OF_Template(new CertReqMsg.Template()).decode(crmfBlobIn); int nummsgs = crmfMsgs.size(); X509CertInfo[] certInfoArray = new X509CertInfo[nummsgs]; for (int i = 0; i < nummsgs; i++) { // decode message. CertReqMsg certReqMsg = (CertReqMsg) crmfMsgs.elementAt(i); CertRequest certReq = certReqMsg.getCertReq(); INTEGER certReqId = certReq.getCertReqId(); int srcId = certReqId.intValue(); req.setExtData(IRequest.CRMF_REQID, String.valueOf(srcId)); certInfoArray[i] = processIndividualRequest(certReqMsg, authToken, httpParams); } //do_testbed_hack(nummsgs, certInfoArray, httpParams); return certInfoArray; } catch (IOException e) { log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_CRMF_TO_CERTINFO_1", e.toString())); throw new ECMSGWException( CMS.getUserMessage("CMS_GW_CRMF_TO_CERTINFO_ERROR")); } catch (InvalidBERException e) { log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_CRMF_TO_CERTINFO_1", e.toString())); throw new ECMSGWException( CMS.getUserMessage("CMS_GW_CRMF_TO_CERTINFO_ERROR")); } } }