From 621d9e5c413e561293d7484b93882d985b3fe15f Mon Sep 17 00:00:00 2001 From: Endi Sukma Dewata Date: Sat, 24 Mar 2012 02:27:47 -0500 Subject: Removed unnecessary pki folder. Previously the source code was located inside a pki folder. This folder was created during svn migration and is no longer needed. This folder has now been removed and the contents have been moved up one level. Ticket #131 --- .../cms/servlet/cert/scep/CRSEnrollment.java | 2135 ++++++++++++++++++++ .../cms/servlet/cert/scep/ChallengePassword.java | 141 ++ .../cms/servlet/cert/scep/ExtensionsRequested.java | 176 ++ 3 files changed, 2452 insertions(+) create mode 100644 base/common/src/com/netscape/cms/servlet/cert/scep/CRSEnrollment.java create mode 100644 base/common/src/com/netscape/cms/servlet/cert/scep/ChallengePassword.java create mode 100644 base/common/src/com/netscape/cms/servlet/cert/scep/ExtensionsRequested.java (limited to 'base/common/src/com/netscape/cms/servlet/cert/scep') diff --git a/base/common/src/com/netscape/cms/servlet/cert/scep/CRSEnrollment.java b/base/common/src/com/netscape/cms/servlet/cert/scep/CRSEnrollment.java new file mode 100644 index 000000000..90a48cb4e --- /dev/null +++ b/base/common/src/com/netscape/cms/servlet/cert/scep/CRSEnrollment.java @@ -0,0 +1,2135 @@ +// --- 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.cert.scep; + +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Random; +import java.util.Vector; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.security.pkcs.PKCS10; +import netscape.security.pkcs.PKCS10Attribute; +import netscape.security.pkcs.PKCS10Attributes; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.AVA; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.CertificateChain; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.CertificateSubjectName; +import netscape.security.x509.CertificateVersion; +import netscape.security.x509.CertificateX509Key; +import netscape.security.x509.DNSName; +import netscape.security.x509.Extension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.IPAddressName; +import netscape.security.x509.KeyUsageExtension; +import netscape.security.x509.OIDMap; +import netscape.security.x509.RDN; +import netscape.security.x509.SubjectAlternativeNameExtension; +import netscape.security.x509.X500Name; +import netscape.security.x509.X500NameAttrMap; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; +import netscape.security.x509.X509Key; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.NoSuchTokenException; +import org.mozilla.jss.asn1.ANY; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.BIT_STRING; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.crypto.Cipher; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.EncryptionAlgorithm; +import org.mozilla.jss.crypto.IVParameterSpec; +import org.mozilla.jss.crypto.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyGenerator; +import org.mozilla.jss.crypto.KeyWrapAlgorithm; +import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.TokenException; +import org.mozilla.jss.pkcs7.IssuerAndSerialNumber; +import org.mozilla.jss.pkix.cert.Certificate; +import org.mozilla.jss.util.IncorrectPasswordException; +import org.mozilla.jss.util.PasswordCallback; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.AuthCredentials; +import com.netscape.certsrv.authentication.AuthToken; +import com.netscape.certsrv.authentication.EInvalidCredentials; +import com.netscape.certsrv.authentication.EMissingCredential; +import com.netscape.certsrv.authentication.IAuthSubsystem; +import com.netscape.certsrv.authentication.IAuthToken; +import com.netscape.certsrv.authority.ICertAuthority; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IArgBlock; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.ISubsystem; +import com.netscape.certsrv.base.SessionContext; +import com.netscape.certsrv.ca.ICertificateAuthority; +import com.netscape.certsrv.common.Constants; +import com.netscape.certsrv.ldap.ILdapConnFactory; +import com.netscape.certsrv.logging.AuditFormat; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.profile.EProfileException; +import com.netscape.certsrv.profile.IEnrollProfile; +import com.netscape.certsrv.profile.IProfile; +import com.netscape.certsrv.profile.IProfileAuthenticator; +import com.netscape.certsrv.profile.IProfileContext; +import com.netscape.certsrv.profile.IProfileSubsystem; +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.profile.SSLClientCertProvider; +import com.netscape.cmsutil.scep.CRSPKIMessage; +import com.netscape.cmsutil.util.Utils; + +/** + * This servlet deals with PKCS#10-based certificate requests from + * CRS, now called SCEP, and defined at: + * http://search.ietf.org/internet-drafts/draft-nourse-scep-02.txt + * + * The router is hardcoded to look for the http://host:80/cgi-bin/pkiclient.exe + * + * The HTTP parameters are 'operation' and 'message' + * operation can be either 'GetCACert' or 'PKIOperation' + * + * @version $Revision$, $Date$ + */ +public class CRSEnrollment extends HttpServlet { + /** + * + */ + private static final long serialVersionUID = 8483002540957382369L; + protected IProfileSubsystem mProfileSubsystem = null; + protected String mProfileId = null; + protected ICertAuthority mAuthority; + protected IConfigStore mConfig = null; + protected IAuthSubsystem mAuthSubsystem; + protected String mAppendDN = null; + protected String mEntryObjectclass = null; + protected boolean mCreateEntry = false; + protected boolean mFlattenDN = false; + + private String mAuthManagerName; + private String mSubstoreName; + private boolean mEnabled = false; + private boolean mUseCA = true; + private String mNickname = null; + private String mTokenName = ""; + private String mHashAlgorithm = "SHA1"; + private String mHashAlgorithmList = null; + private String[] mAllowedHashAlgorithm; + private String mConfiguredEncryptionAlgorithm = "DES3"; + private String mEncryptionAlgorithm = "DES3"; + private String mEncryptionAlgorithmList = null; + private String[] mAllowedEncryptionAlgorithm; + private Random mRandom = null; + private int mNonceSizeLimit = 0; + protected ILogger mLogger = CMS.getLogger(); + private ICertificateAuthority ca; + /* for hashing challenge password */ + protected MessageDigest mSHADigest = null; + + private static final String PROP_SUBSTORENAME = "substorename"; + private static final String PROP_AUTHORITY = "authority"; + private static final String PROP_CRS = "crs"; + private static final String PROP_CRSCA = "casubsystem"; + private static final String PROP_CRSAUTHMGR = "authName"; + private static final String PROP_APPENDDN = "appendDN"; + private static final String PROP_CREATEENTRY = "createEntry"; + private static final String PROP_FLATTENDN = "flattenDN"; + private static final String PROP_ENTRYOC = "entryObjectclass"; + + // URL parameters + private static final String URL_OPERATION = "operation"; + private static final String URL_MESSAGE = "message"; + + // possible values for 'operation' + private static final String OP_GETCACERT = "GetCACert"; + private static final String OP_PKIOPERATION = "PKIOperation"; + + public static final String AUTH_PASSWORD = "pwd"; + + public static final String AUTH_CREDS = "AuthCreds"; + public static final String AUTH_TOKEN = "AuthToken"; + public static final String AUTH_FAILED = "AuthFailed"; + + public static final String SANE_DNSNAME = "DNSName"; + public static final String SANE_IPADDRESS = "IPAddress"; + + public static final String CERTINFO = "CertInfo"; + public static final String SUBJECTNAME = "SubjectName"; + + public static ObjectIdentifier OID_UNSTRUCTUREDNAME = null; + public static ObjectIdentifier OID_UNSTRUCTUREDADDRESS = null; + public static ObjectIdentifier OID_SERIALNUMBER = null; + + public CRSEnrollment() { + } + + public static Hashtable toHashtable(HttpServletRequest req) { + Hashtable httpReqHash = new Hashtable(); + @SuppressWarnings("unchecked") + Enumeration names = req.getParameterNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + httpReqHash.put(name, req.getParameter(name)); + } + return httpReqHash; + } + + public void init(ServletConfig sc) { + // Find the CertificateAuthority we should use for CRS. + String crsCA = sc.getInitParameter(PROP_AUTHORITY); + if (crsCA == null) + crsCA = "ca"; + mAuthority = (ICertAuthority) CMS.getSubsystem(crsCA); + ca = (ICertificateAuthority) mAuthority; + + if (mAuthority == null) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_CANT_FIND_AUTHORITY", crsCA)); + } + + try { + if (mAuthority instanceof ISubsystem) { + IConfigStore authorityConfig = ((ISubsystem) mAuthority).getConfigStore(); + IConfigStore scepConfig = authorityConfig.getSubStore("scep"); + mEnabled = scepConfig.getBoolean("enable", false); + mHashAlgorithm = scepConfig.getString("hashAlgorithm", "SHA1"); + mConfiguredEncryptionAlgorithm = scepConfig.getString("encryptionAlgorithm", "DES3"); + mNonceSizeLimit = scepConfig.getInteger("nonceSizeLimit", 0); + mHashAlgorithmList = scepConfig.getString("allowedHashAlgorithms", "SHA1,SHA256,SHA512"); + mAllowedHashAlgorithm = mHashAlgorithmList.split(","); + mEncryptionAlgorithmList = scepConfig.getString("allowedEncryptionAlgorithms", "DES3"); + mAllowedEncryptionAlgorithm = mEncryptionAlgorithmList.split(","); + mNickname = scepConfig.getString("nickname", ca.getNickname()); + if (mNickname.equals(ca.getNickname())) { + mTokenName = ca.getSigningUnit().getTokenName(); + } else { + mTokenName = scepConfig.getString("tokenname", ""); + mUseCA = false; + } + if (!(mTokenName.equalsIgnoreCase(Constants.PR_INTERNAL_TOKEN) || + mTokenName.equalsIgnoreCase("Internal Key Storage Token") || mTokenName.length() == 0)) { + int i = mNickname.indexOf(':'); + if (!((i > -1) && (mTokenName.length() == i) && (mNickname.startsWith(mTokenName)))) { + mNickname = mTokenName + ":" + mNickname; + } + } + } + } catch (EBaseException e) { + CMS.debug("CRSEnrollment: init: EBaseException: " + e); + } + mEncryptionAlgorithm = mConfiguredEncryptionAlgorithm; + CMS.debug("CRSEnrollment: init: SCEP support is " + ((mEnabled) ? "enabled" : "disabled") + "."); + CMS.debug("CRSEnrollment: init: SCEP nickname: " + mNickname); + CMS.debug("CRSEnrollment: init: CA nickname: " + ca.getNickname()); + CMS.debug("CRSEnrollment: init: Token name: " + mTokenName); + CMS.debug("CRSEnrollment: init: Is SCEP using CA keys: " + mUseCA); + CMS.debug("CRSEnrollment: init: mNonceSizeLimit: " + mNonceSizeLimit); + CMS.debug("CRSEnrollment: init: mHashAlgorithm: " + mHashAlgorithm); + CMS.debug("CRSEnrollment: init: mHashAlgorithmList: " + mHashAlgorithmList); + for (int i = 0; i < mAllowedHashAlgorithm.length; i++) { + mAllowedHashAlgorithm[i] = mAllowedHashAlgorithm[i].trim(); + CMS.debug("CRSEnrollment: init: mAllowedHashAlgorithm[" + i + "]=" + mAllowedHashAlgorithm[i]); + } + CMS.debug("CRSEnrollment: init: mEncryptionAlgorithm: " + mEncryptionAlgorithm); + CMS.debug("CRSEnrollment: init: mEncryptionAlgorithmList: " + mEncryptionAlgorithmList); + for (int i = 0; i < mAllowedEncryptionAlgorithm.length; i++) { + mAllowedEncryptionAlgorithm[i] = mAllowedEncryptionAlgorithm[i].trim(); + CMS.debug("CRSEnrollment: init: mAllowedEncryptionAlgorithm[" + i + "]=" + mAllowedEncryptionAlgorithm[i]); + } + + try { + mProfileSubsystem = (IProfileSubsystem) CMS.getSubsystem("profile"); + mProfileId = sc.getInitParameter("profileId"); + CMS.debug("CRSEnrollment: init: mProfileId=" + mProfileId); + + mAuthSubsystem = (IAuthSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTH); + mAuthManagerName = sc.getInitParameter(PROP_CRSAUTHMGR); + mAppendDN = sc.getInitParameter(PROP_APPENDDN); + String tmp = sc.getInitParameter(PROP_CREATEENTRY); + if (tmp != null && tmp.trim().equalsIgnoreCase("true")) + mCreateEntry = true; + else + mCreateEntry = false; + tmp = sc.getInitParameter(PROP_FLATTENDN); + if (tmp != null && tmp.trim().equalsIgnoreCase("true")) + mFlattenDN = true; + else + mFlattenDN = false; + mEntryObjectclass = sc.getInitParameter(PROP_ENTRYOC); + if (mEntryObjectclass == null) + mEntryObjectclass = "cep"; + mSubstoreName = sc.getInitParameter(PROP_SUBSTORENAME); + if (mSubstoreName == null) + mSubstoreName = "default"; + } catch (Exception e) { + } + + OID_UNSTRUCTUREDNAME = X500NameAttrMap.getDefault().getOid("UNSTRUCTUREDNAME"); + OID_UNSTRUCTUREDADDRESS = X500NameAttrMap.getDefault().getOid("UNSTRUCTUREDADDRESS"); + OID_SERIALNUMBER = X500NameAttrMap.getDefault().getOid("SERIALNUMBER"); + + try { + mSHADigest = MessageDigest.getInstance("SHA1"); + } catch (NoSuchAlgorithmException e) { + } + + mRandom = new Random(); + } + + /** + * + * Service a CRS Request. It all starts here. This is where the message from the + * router is processed + * + * @param httpReq The HttpServletRequest. + * @param httpResp The HttpServletResponse. + * + */ + public void service(HttpServletRequest httpReq, + HttpServletResponse httpResp) + throws ServletException { + boolean running_state = CMS.isInRunningState(); + if (!running_state) + throw new ServletException( + "CMS server is not ready to serve."); + + String operation = null; + String message = null; + mEncryptionAlgorithm = mConfiguredEncryptionAlgorithm; + + // Parse the URL from the HTTP Request. Split it up into + // a structure which enables us to read the form elements + IArgBlock input = CMS.createArgBlock(toHashtable(httpReq)); + + try { + // Read in two form parameters - the router sets these + operation = (String) input.get(URL_OPERATION); + CMS.debug("operation=" + operation); + message = (String) input.get(URL_MESSAGE); + CMS.debug("message=" + message); + + if (!mEnabled) { + CMS.debug("CRSEnrollment: SCEP support is disabled."); + throw new ServletException("SCEP support is disabled."); + } + if (operation == null) { + // 'operation' is mandatory. + throw new ServletException("Bad request: operation missing from URL"); + } + + /** + * the router can make two kinds of requests + * 1) simple request for CA cert + * 2) encoded, signed, enveloped request for anything else (PKIOperation) + */ + + if (operation.equals(OP_GETCACERT)) { + handleGetCACert(httpReq, httpResp); + } else if (operation.equals(OP_PKIOPERATION)) { + String decodeMode = (String) input.get("decode"); + if (decodeMode == null || decodeMode.equals("false")) { + handlePKIOperation(httpReq, httpResp, message); + } else { + decodePKIMessage(httpReq, httpResp, message); + } + } else { + CMS.debug("Invalid operation " + operation); + throw new ServletException("unknown operation requested: " + operation); + } + + } catch (ServletException e) { + CMS.debug("ServletException " + e); + throw new ServletException(e.getMessage().toString()); + } catch (Exception e) { + CMS.debug("Service exception " + e); + log(ILogger.LL_FAILURE, e.getMessage()); + } + + } + + /** + * Log a message to the system log + */ + + private void log(int level, String msg) { + + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + level, "CEP Enrollment: " + msg); + } + + private boolean isAlgorithmAllowed(String[] allowedAlgorithm, String algorithm) { + boolean allowed = false; + + if (algorithm != null && algorithm.length() > 0) { + for (int i = 0; i < allowedAlgorithm.length; i++) { + if (algorithm.equalsIgnoreCase(allowedAlgorithm[i])) { + allowed = true; + } + } + } + + return allowed; + } + + public IAuthToken authenticate(AuthCredentials credentials, IProfileAuthenticator authenticator, + HttpServletRequest request) throws EBaseException { + + // build credential + Enumeration authNames = authenticator.getValueNames(); + + if (authNames != null) { + while (authNames.hasMoreElements()) { + String authName = (String) authNames.nextElement(); + + credentials.set(authName, request.getParameter(authName)); + } + } + + credentials.set("clientHost", request.getRemoteHost()); + IAuthToken authToken = authenticator.authenticate(credentials); + if (authToken == null) { + return null; + } + SessionContext sc = SessionContext.getContext(); + if (sc != null) { + sc.put(SessionContext.AUTH_MANAGER_ID, authenticator.getName()); + String userid = authToken.getInString(IAuthToken.USER_ID); + if (userid != null) { + sc.put(SessionContext.USER_ID, userid); + } + } + + return authToken; + } + + /** + * Return the CA certificate back to the requestor. + * This needs to be changed so that if the CA has a certificate chain, + * the whole thing should get packaged as a PKIMessage (degnerate PKCS7 - no + * signerInfo) + */ + + public void handleGetCACert(HttpServletRequest httpReq, + HttpServletResponse httpResp) + throws ServletException { + java.security.cert.X509Certificate[] chain = null; + + CertificateChain certChain = mAuthority.getCACertChain(); + + try { + if (certChain == null) { + throw new ServletException("Internal Error: cannot get CA Cert"); + } + + chain = certChain.getChain(); + + byte[] bytes = null; + + int i = 0; + String message = (String) httpReq.getParameter(URL_MESSAGE); + CMS.debug("handleGetCACert message=" + message); + if (message != null) { + try { + int j = Integer.parseInt(message); + if (j < chain.length) { + i = j; + } + } catch (NumberFormatException e1) { + } + } + CMS.debug("handleGetCACert selected chain=" + i); + + if (mUseCA) { + bytes = chain[i].getEncoded(); + } else { + CryptoContext cx = new CryptoContext(); + bytes = cx.getSigningCert().getEncoded(); + } + + httpResp.setContentType("application/x-x509-ca-cert"); + + // The following code may be used one day to encode + // the RA/CA cert chain for RA mode, but it will need some + // work. + + /****** + * SET certs = new SET(); + * for (int i=0; i e = p10atts.getElements(); + + try { + while (e.hasMoreElements()) { + PKCS10Attribute p10a = (PKCS10Attribute) e.nextElement(); + CertAttrSet attr = p10a.getAttributeValue(); + + if (attr.getName().equals(ChallengePassword.NAME)) { + if (attr.get(ChallengePassword.PASSWORD) != null) { + return (String) attr.get(ChallengePassword.PASSWORD); + } + } + } + } catch (Exception e1) { + // do nothing + } + return null; + } + + /** + * If the 'operation' is 'PKIOperation', the 'message' part of the URL is a + * PKIMessage structure. We decode it to see what type message it is. + */ + + /** + * Decodes the PKI message and return information to RA. + */ + public void decodePKIMessage(HttpServletRequest httpReq, + HttpServletResponse httpResp, + String msg) + throws ServletException { + + CryptoContext cx = null; + + CRSPKIMessage req = null; + + byte[] decodedPKIMessage; + byte[] response = null; + String responseData = ""; + + decodedPKIMessage = Utils.base64decode(msg); + + try { + ByteArrayInputStream is = new ByteArrayInputStream(decodedPKIMessage); + + // We make two CRSPKIMessages. One of them, is the request, so we initialize + // it from the DER given to us from the router. + // The second is the response, and we'll fill this in as we go. + + if (decodedPKIMessage.length < 50) { + throw new ServletException("CRS request is too small to be a real request (" + + decodedPKIMessage.length + " bytes)"); + } + try { + req = new CRSPKIMessage(is); + String ea = req.getEncryptionAlgorithm(); + if (!isAlgorithmAllowed(mAllowedEncryptionAlgorithm, ea)) { + CMS.debug("CRSEnrollment: decodePKIMessage: Encryption algorithm '" + ea + + "' is not allowed (" + mEncryptionAlgorithmList + ")."); + throw new ServletException("Encryption algorithm '" + ea + + "' is not allowed (" + mEncryptionAlgorithmList + ")."); + } + String da = req.getDigestAlgorithmName(); + if (!isAlgorithmAllowed(mAllowedHashAlgorithm, da)) { + CMS.debug("CRSEnrollment: decodePKIMessage: Hashing algorithm '" + da + + "' is not allowed (" + mHashAlgorithmList + ")."); + throw new ServletException("Hashing algorithm '" + da + + "' is not allowed (" + mHashAlgorithmList + ")."); + } + if (ea != null) { + mEncryptionAlgorithm = ea; + } + } catch (Exception e) { + CMS.debug(e); + throw new ServletException("Could not decode the request."); + } + + // Create a new crypto context for doing all the crypto operations + cx = new CryptoContext(); + + // Verify Signature on message (throws exception if sig bad) + verifyRequest(req, cx); + unwrapPKCS10(req, cx); + + IProfile profile = mProfileSubsystem.getProfile(mProfileId); + if (profile == null) { + CMS.debug("Profile '" + mProfileId + "' not found."); + throw new ServletException("Profile '" + mProfileId + "' not found."); + } else { + CMS.debug("Found profile '" + mProfileId + "'."); + } + + IProfileAuthenticator authenticator = null; + try { + CMS.debug("Retrieving authenticator"); + authenticator = profile.getAuthenticator(); + if (authenticator == null) { + CMS.debug("Authenticator not found."); + throw new ServletException("Authenticator not found."); + } else { + CMS.debug("Got authenticator=" + authenticator.getClass().getName()); + } + } catch (EProfileException e) { + throw new ServletException("Authenticator not found."); + } + AuthCredentials credentials = new AuthCredentials(); + IAuthToken authToken = null; + // for ssl authentication; pass in servlet for retrieving + // ssl client certificates + SessionContext context = SessionContext.getContext(); + + // insert profile context so that input parameter can be retrieved + context.put("sslClientCertProvider", new SSLClientCertProvider(httpReq)); + + try { + authToken = authenticate(credentials, authenticator, httpReq); + } catch (Exception e) { + CMS.debug("Authentication failure: " + e.getMessage()); + throw new ServletException("Authentication failure: " + e.getMessage()); + } + if (authToken == null) { + CMS.debug("Authentication failure."); + throw new ServletException("Authentication failure."); + } + + // Deal with Transaction ID + String transactionID = req.getTransactionID(); + responseData = responseData + + "" + transactionID + ""; + + // End-User or RA's IP address + responseData = responseData + + "" + httpReq.getRemoteAddr() + ""; + + responseData = responseData + + "" + httpReq.getRemoteHost() + ""; + + // Deal with message type + String mt = req.getMessageType(); + responseData = responseData + + "" + mt + ""; + + PKCS10 p10 = (PKCS10) req.getP10(); + X500Name p10subject = p10.getSubjectName(); + responseData = responseData + + "" + p10subject.toString() + ""; + + String pkcs10Attr = ""; + PKCS10Attributes p10atts = p10.getAttributes(); + Enumeration e = p10atts.getElements(); + + while (e.hasMoreElements()) { + PKCS10Attribute p10a = (PKCS10Attribute) e.nextElement(); + CertAttrSet attr = p10a.getAttributeValue(); + + if (attr.getName().equals(ChallengePassword.NAME)) { + if (attr.get(ChallengePassword.PASSWORD) != null) { + pkcs10Attr = + pkcs10Attr + + + "" + + (String) attr.get(ChallengePassword.PASSWORD) + + ""; + } + + } + String extensionsStr = ""; + if (attr.getName().equals(ExtensionsRequested.NAME)) { + + Enumeration exts = ((ExtensionsRequested) attr).getExtensions().elements(); + while (exts.hasMoreElements()) { + Extension ext = exts.nextElement(); + + if (ext.getExtensionId().equals( + OIDMap.getOID(SubjectAlternativeNameExtension.IDENT))) { + SubjectAlternativeNameExtension sane = new SubjectAlternativeNameExtension( + Boolean.valueOf(false), // noncritical + ext.getExtensionValue()); + + @SuppressWarnings("unchecked") + Vector v = + (Vector) sane + .get(SubjectAlternativeNameExtension.SUBJECT_NAME); + + Enumeration gne = v.elements(); + + StringBuffer subjAltNameStr = new StringBuffer(); + while (gne.hasMoreElements()) { + GeneralNameInterface gni = gne.nextElement(); + if (gni instanceof GeneralName) { + GeneralName genName = (GeneralName) gni; + + String gn = genName.toString(); + int colon = gn.indexOf(':'); + String gnType = gn.substring(0, colon).trim(); + String gnValue = gn.substring(colon + 1).trim(); + + subjAltNameStr.append("<"); + subjAltNameStr.append(gnType); + subjAltNameStr.append(">"); + subjAltNameStr.append(gnValue); + subjAltNameStr.append(""); + } + } // while + extensionsStr = "" + + subjAltNameStr.toString() + ""; + } // if + } // while + pkcs10Attr = pkcs10Attr + + "" + extensionsStr + ""; + } // if extensions + } // while + responseData = responseData + + "" + pkcs10Attr + ""; + + } catch (ServletException e) { + throw new ServletException(e.getMessage().toString()); + } catch (CRSInvalidSignatureException e) { + CMS.debug("handlePKIMessage exception " + e); + CMS.debug(e); + } catch (Exception e) { + CMS.debug("handlePKIMessage exception " + e); + CMS.debug(e); + throw new ServletException("Failed to process message in CEP servlet: " + e.getMessage()); + } + + // We have now processed the request, and need to make the response message + + try { + + responseData = "" + responseData + ""; + // Get the response coding + response = responseData.getBytes(); + + // Encode the httpResp into B64 + httpResp.setContentType("application/xml"); + httpResp.setContentLength(response.length); + httpResp.getOutputStream().write(response); + httpResp.getOutputStream().flush(); + + int i1 = responseData.indexOf(""); + if (i1 > -1) { + i1 += 10; // 10 is a length of "" + int i2 = responseData.indexOf("", i1); + if (i2 > -1) { + responseData = responseData.substring(0, i1) + "********" + + responseData.substring(i2, responseData.length()); + } + } + + CMS.debug("Output (decoding) PKIOperation response:"); + CMS.debug(responseData); + } catch (Exception e) { + throw new ServletException("Failed to create response for CEP message" + e.getMessage()); + } + + } + + /** + * finds a request with this transaction ID. + * If could not find any request - return null + * If could only find 'rejected' or 'cancelled' requests, return null + * If found 'pending' or 'completed' request - return that request + */ + + public void handlePKIOperation(HttpServletRequest httpReq, + HttpServletResponse httpResp, + String msg) + throws ServletException { + + CryptoContext cx = null; + + CRSPKIMessage req = null; + CRSPKIMessage crsResp = null; + + byte[] decodedPKIMessage; + byte[] response = null; + X509CertImpl cert = null; + + decodedPKIMessage = Utils.base64decode(msg); + + try { + ByteArrayInputStream is = new ByteArrayInputStream(decodedPKIMessage); + + // We make two CRSPKIMessages. One of them, is the request, so we initialize + // it from the DER given to us from the router. + // The second is the response, and we'll fill this in as we go. + + if (decodedPKIMessage.length < 50) { + throw new ServletException("CRS request is too small to be a real request (" + + decodedPKIMessage.length + " bytes)"); + } + try { + req = new CRSPKIMessage(is); + String ea = req.getEncryptionAlgorithm(); + if (!isAlgorithmAllowed(mAllowedEncryptionAlgorithm, ea)) { + CMS.debug("CRSEnrollment: handlePKIOperation: Encryption algorithm '" + ea + + "' is not allowed (" + mEncryptionAlgorithmList + ")."); + throw new ServletException("Encryption algorithm '" + ea + + "' is not allowed (" + mEncryptionAlgorithmList + ")."); + } + String da = req.getDigestAlgorithmName(); + if (!isAlgorithmAllowed(mAllowedHashAlgorithm, da)) { + CMS.debug("CRSEnrollment: handlePKIOperation: Hashing algorithm '" + da + + "' is not allowed (" + mHashAlgorithmList + ")."); + throw new ServletException("Hashing algorithm '" + da + + "' is not allowed (" + mHashAlgorithmList + ")."); + } + if (ea != null) { + mEncryptionAlgorithm = ea; + } + crsResp = new CRSPKIMessage(); + } catch (ServletException e) { + throw new ServletException(e.getMessage().toString()); + } catch (Exception e) { + CMS.debug(e); + throw new ServletException("Could not decode the request."); + } + crsResp.setMessageType(CRSPKIMessage.mType_CertRep); + + // Create a new crypto context for doing all the crypto operations + cx = new CryptoContext(); + + // Verify Signature on message (throws exception if sig bad) + verifyRequest(req, cx); + + // Deal with Transaction ID + String transactionID = req.getTransactionID(); + if (transactionID == null) { + throw new ServletException("Error: malformed PKIMessage - missing transactionID"); + } else { + crsResp.setTransactionID(transactionID); + } + + // Deal with Nonces + byte[] sn = req.getSenderNonce(); + if (sn == null) { + throw new ServletException("Error: malformed PKIMessage - missing sendernonce"); + } else { + if (mNonceSizeLimit > 0 && sn.length > mNonceSizeLimit) { + byte[] snLimited = (mNonceSizeLimit > 0) ? new byte[mNonceSizeLimit] : null; + System.arraycopy(sn, 0, snLimited, 0, mNonceSizeLimit); + crsResp.setRecipientNonce(snLimited); + } else { + crsResp.setRecipientNonce(sn); + } + byte[] serverNonce = new byte[16]; + mRandom.nextBytes(serverNonce); + crsResp.setSenderNonce(serverNonce); + // crsResp.setSenderNonce(new byte[] {0}); + } + + // Deal with message type + String mt = req.getMessageType(); + if (mt == null) { + throw new ServletException("Error: malformed PKIMessage - missing messageType"); + } + + // now run appropriate code, depending on message type + if (mt.equals(CRSPKIMessage.mType_PKCSReq)) { + CMS.debug("Processing PKCSReq"); + try { + // Check if there is an existing request. If this returns non-null, + // then the request is 'active' (either pending or completed) in + // which case, we compare the hash of the new request to the hash of the + // one in the queue - if they are the same, I return the state of the + // original request - as if it was 'getCertInitial' message. + // If the hashes are different, then the user attempted to enroll + // for a new request with the same txid, which is not allowed - + // so we return 'failure'. + + IRequest cmsRequest = findRequestByTransactionID(req.getTransactionID(), true); + + // If there was no request (with a cert) with this transaction ID, + // process it as a new request + + cert = handlePKCSReq(httpReq, cmsRequest, req, crsResp, cx); + + } catch (CRSFailureException e) { + throw new ServletException("Couldn't handle CEP request (PKCSReq) - " + e.getMessage()); + } + } else if (mt.equals(CRSPKIMessage.mType_GetCertInitial)) { + CMS.debug("Processing GetCertInitial"); + cert = handleGetCertInitial(req, crsResp); + } else { + CMS.debug("Invalid request type " + mt); + } + } catch (ServletException e) { + throw new ServletException(e.getMessage().toString()); + } catch (CRSInvalidSignatureException e) { + CMS.debug("handlePKIMessage exception " + e); + CMS.debug(e); + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badMessageCheck); + } catch (Exception e) { + CMS.debug("handlePKIMessage exception " + e); + CMS.debug(e); + throw new ServletException("Failed to process message in CEP servlet: " + e.getMessage()); + } + + // We have now processed the request, and need to make the response message + + try { + // make the response + processCertRep(cx, cert, crsResp, req); + + // Get the response coding + response = crsResp.getResponse(); + + // Encode the crsResp into B64 + httpResp.setContentType("application/x-pki-message"); + httpResp.setContentLength(response.length); + httpResp.getOutputStream().write(response); + httpResp.getOutputStream().flush(); + + CMS.debug("Output PKIOperation response:"); + CMS.debug(CMS.BtoA(response)); + } catch (Exception e) { + throw new ServletException("Failed to create response for CEP message" + e.getMessage()); + } + + } + + /** + * finds a request with this transaction ID. + * If could not find any request - return null + * If could only find 'rejected' or 'cancelled' requests, return null + * If found 'pending' or 'completed' request - return that request + */ + + public IRequest findRequestByTransactionID(String txid, boolean ignoreRejected) + throws EBaseException { + + /* Check if certificate request has been completed */ + + IRequestQueue rq = ca.getRequestQueue(); + IRequest foundRequest = null; + + Enumeration rids = rq.findRequestsBySourceId(txid); + if (rids == null) { + return null; + } + + while (rids.hasMoreElements()) { + RequestId rid = rids.nextElement(); + if (rid == null) { + continue; + } + + IRequest request = rq.findRequest(rid); + if (request == null) { + continue; + } + if (!ignoreRejected || + request.getRequestStatus().equals(RequestStatus.PENDING) || + request.getRequestStatus().equals(RequestStatus.COMPLETE)) { + if (foundRequest != null) { + } + foundRequest = request; + } + } + return foundRequest; + } + + /** + * Called if the router is requesting us to send it its certificate + * Examine request queue for a request matching the transaction ID. + * Ignore any rejected or cancelled requests. + * + * If a request is found in the pending state, the response should be + * 'pending' + * + * If a request is found in the completed state, the response should be + * to return the certificate + * + * If no request is found, the response should be to return null + * + */ + + public X509CertImpl handleGetCertInitial(CRSPKIMessage req, CRSPKIMessage resp) { + IRequest foundRequest = null; + + // already done by handlePKIOperation + // resp.setRecipientNonce(req.getSenderNonce()); + // resp.setSenderNonce(null); + + try { + foundRequest = findRequestByTransactionID(req.getTransactionID(), false); + } catch (EBaseException e) { + } + + if (foundRequest == null) { + resp.setFailInfo(CRSPKIMessage.mFailInfo_badCertId); + resp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + return null; + } + + return makeResponseFromRequest(req, resp, foundRequest); + } + + public void verifyRequest(CRSPKIMessage req, CryptoContext cx) + throws CRSInvalidSignatureException { + + // Get Signed Data + + @SuppressWarnings("unused") + byte[] reqAAbytes = req.getAA(); // check for errors + + @SuppressWarnings("unused") + byte[] reqAAsig = req.getAADigest(); // check for errors + + } + + /** + * Create an entry for this user in the publishing directory + * + */ + + private boolean createEntry(String dn) { + boolean result = false; + + IPublisherProcessor ldapPub = mAuthority.getPublisherProcessor(); + if (ldapPub == null || !ldapPub.enabled()) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERROR_CREATE_ENTRY_FROM_CEP")); + + return result; + } + + ILdapConnFactory connFactory = ((IPublisherProcessor) ldapPub).getLdapConnModule().getLdapConnFactory(); + if (connFactory == null) { + return result; + } + + LDAPConnection connection = null; + try { + connection = connFactory.getConn(); + String[] objectclasses = { "top", mEntryObjectclass }; + LDAPAttribute ocAttrs = new LDAPAttribute("objectclass", objectclasses); + + LDAPAttributeSet attrSet = new LDAPAttributeSet(); + attrSet.add(ocAttrs); + + LDAPEntry newEntry = new LDAPEntry(dn, attrSet); + connection.add(newEntry); + result = true; + } catch (Exception e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_FAIL_CREAT_ENTRY_EXISTS", dn)); + } finally { + try { + connFactory.returnConn(connection); + } catch (Exception f) { + } + } + return result; + } + + /** + * Here we decrypt the PKCS10 message from the client + * + */ + + public void unwrapPKCS10(CRSPKIMessage req, CryptoContext cx) + throws ServletException, + CryptoManager.NotInitializedException, + CryptoContext.CryptoContextException, + CRSFailureException { + + byte[] decryptedP10bytes = null; + SymmetricKey sk; + SymmetricKey skinternal; + SymmetricKey.Type skt; + KeyWrapper kw; + Cipher cip; + EncryptionAlgorithm ea; + + // Unwrap the session key with the Cert server key + try { + kw = cx.getKeyWrapper(); + + kw.initUnwrap(cx.getPrivateKey(), null); + + skt = SymmetricKey.Type.DES; + ea = EncryptionAlgorithm.DES_CBC; + if (mEncryptionAlgorithm != null && mEncryptionAlgorithm.equals("DES3")) { + skt = SymmetricKey.Type.DES3; + ea = EncryptionAlgorithm.DES3_CBC; + } + + sk = kw.unwrapSymmetric(req.getWrappedKey(), + skt, + SymmetricKey.Usage.DECRYPT, + 0); // keylength is ignored + + skinternal = cx.getDESKeyGenerator().clone(sk); + + cip = skinternal.getOwningToken().getCipherContext(ea); + + cip.initDecrypt(skinternal, (new IVParameterSpec(req.getIV()))); + + decryptedP10bytes = cip.doFinal(req.getEncryptedPkcs10()); + CMS.debug("decryptedP10bytes:"); + CMS.debug(decryptedP10bytes); + + req.setP10(new PKCS10(decryptedP10bytes)); + } catch (Exception e) { + CMS.debug("failed to unwrap PKCS10 " + e); + throw new CRSFailureException("Could not unwrap PKCS10 blob: " + e.getMessage()); + } + + } + + private void getDetailFromRequest(CRSPKIMessage req, CRSPKIMessage crsResp) + throws CRSFailureException { + + SubjectAlternativeNameExtension sane = null; + + try { + PKCS10 p10 = req.getP10(); + + if (p10 == null) { + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badMessageCheck); + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + throw new CRSFailureException("Failed to decode pkcs10 from CEP request"); + } + + AuthCredentials authCreds = new AuthCredentials(); + + // Here, we make a new CertInfo - it's a new start for a certificate + + X509CertInfo certInfo = CMS.getDefaultX509CertInfo(); + + // get some stuff out of the request + X509Key key = p10.getSubjectPublicKeyInfo(); + X500Name p10subject = p10.getSubjectName(); + + X500Name subject = null; + + // The following code will copy all the attributes + // into the AuthCredentials so they can be used for + // authentication + // + // Optionally, you can re-map the subject name from: + // one RDN, with many AVA's to + // many RDN's with one AVA in each. + + Enumeration rdne = p10subject.getRDNs(); + Vector rdnv = new Vector(); + + Hashtable sanehash = new Hashtable(); + + X500NameAttrMap xnap = X500NameAttrMap.getDefault(); + while (rdne.hasMoreElements()) { + RDN rdn = (RDN) rdne.nextElement(); + int i = 0; + AVA[] oldavas = rdn.getAssertion(); + for (i = 0; i < rdn.getAssertionLength(); i++) { + AVA[] newavas = new AVA[1]; + newavas[0] = oldavas[i]; + + authCreds.set(xnap.getName(oldavas[i].getOid()), + oldavas[i].getValue().getAsString()); + + if (oldavas[i].getOid().equals(OID_UNSTRUCTUREDNAME)) { + + sanehash.put(SANE_DNSNAME, oldavas[i].getValue().getAsString()); + } + if (oldavas[i].getOid().equals(OID_UNSTRUCTUREDADDRESS)) { + sanehash.put(SANE_IPADDRESS, oldavas[i].getValue().getAsString()); + } + + RDN newrdn = new RDN(newavas); + if (mFlattenDN) { + rdnv.addElement(newrdn); + } + } + } + + if (mFlattenDN) + subject = new X500Name(rdnv); + else + subject = p10subject; + + // create default key usage extension + KeyUsageExtension kue = new KeyUsageExtension(); + kue.set(KeyUsageExtension.DIGITAL_SIGNATURE, Boolean.valueOf(true)); + kue.set(KeyUsageExtension.KEY_ENCIPHERMENT, Boolean.valueOf(true)); + + PKCS10Attributes p10atts = p10.getAttributes(); + Enumeration e = p10atts.getElements(); + + while (e.hasMoreElements()) { + PKCS10Attribute p10a = (PKCS10Attribute) e.nextElement(); + CertAttrSet attr = p10a.getAttributeValue(); + + if (attr.getName().equals(ChallengePassword.NAME)) { + if (attr.get(ChallengePassword.PASSWORD) != null) { + req.put(AUTH_PASSWORD, + (String) attr.get(ChallengePassword.PASSWORD)); + req.put(ChallengePassword.NAME, + hashPassword( + (String) attr.get(ChallengePassword.PASSWORD))); + } + } + + if (attr.getName().equals(ExtensionsRequested.NAME)) { + + Enumeration exts = ((ExtensionsRequested) attr).getExtensions().elements(); + while (exts.hasMoreElements()) { + Extension ext = exts.nextElement(); + + if (ext.getExtensionId().equals( + OIDMap.getOID(KeyUsageExtension.IDENT))) { + + kue = new KeyUsageExtension( + new Boolean(false), // noncritical + ext.getExtensionValue()); + } + + if (ext.getExtensionId().equals( + OIDMap.getOID(SubjectAlternativeNameExtension.IDENT))) { + sane = new SubjectAlternativeNameExtension( + new Boolean(false), // noncritical + ext.getExtensionValue()); + + @SuppressWarnings("unchecked") + Vector v = + (Vector) sane + .get(SubjectAlternativeNameExtension.SUBJECT_NAME); + + Enumeration gne = v.elements(); + + while (gne.hasMoreElements()) { + GeneralNameInterface gni = (GeneralNameInterface) gne.nextElement(); + if (gni instanceof GeneralName) { + GeneralName genName = (GeneralName) gni; + + String gn = genName.toString(); + int colon = gn.indexOf(':'); + String gnType = gn.substring(0, colon).trim(); + String gnValue = gn.substring(colon + 1).trim(); + + authCreds.set(gnType, gnValue); + } + } + } + } + } + } + + if (authCreds != null) + req.put(AUTH_CREDS, authCreds); + + try { + if (sane == null) + sane = makeDefaultSubjectAltName(sanehash); + } catch (Exception sane_e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ENROLL_FAIL_NO_SUBJ_ALT_NAME", + sane_e.getMessage())); + } + + try { + if (mAppendDN != null && !mAppendDN.equals("")) { + + new X500Name(subject.toString()); // check for errors + + subject = new X500Name(subject.toString().concat("," + mAppendDN)); + } + + } catch (Exception sne) { + log(ILogger.LL_INFO, + "Unable to use appendDN parameter: " + + mAppendDN + ". Error is " + sne.getMessage() + " Using unmodified subjectname"); + } + + if (subject != null) + req.put(SUBJECTNAME, subject); + + if (key == null || subject == null) { + // log + //throw new ERegistrationException(RegistrationResources.ERROR_MALFORMED_P10); + } + + certInfo.set(X509CertInfo.VERSION, + new CertificateVersion(CertificateVersion.V3)); + + certInfo.set(X509CertInfo.SUBJECT, + new CertificateSubjectName(subject)); + + certInfo.set(X509CertInfo.KEY, + new CertificateX509Key(key)); + + CertificateExtensions ext = new CertificateExtensions(); + + if (kue != null) { + ext.set(KeyUsageExtension.NAME, kue); + } + + // add subjectAltName extension, if present + if (sane != null) { + ext.set(SubjectAlternativeNameExtension.NAME, sane); + } + + certInfo.set(X509CertInfo.EXTENSIONS, ext); + + req.put(CERTINFO, certInfo); + } catch (Exception e) { + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badMessageCheck); + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + return; + } // NEED TO FIX + } + + private SubjectAlternativeNameExtension makeDefaultSubjectAltName(Hashtable ht) { + + // if no subjectaltname extension was requested, we try to make it up + // from some of the elements of the subject name + + int itemCount = ht.size(); + GeneralNameInterface[] gn = new GeneralNameInterface[ht.size()]; + + itemCount = 0; + Enumeration en = ht.keys(); + while (en.hasMoreElements()) { + String key = (String) en.nextElement(); + if (key.equals(SANE_DNSNAME)) { + gn[itemCount++] = new DNSName((String) ht.get(key)); + } + if (key.equals(SANE_IPADDRESS)) { + gn[itemCount++] = new IPAddressName((String) ht.get(key)); + } + } + + try { + return new SubjectAlternativeNameExtension(new GeneralNames(gn)); + } catch (Exception e) { + log(ILogger.LL_INFO, CMS.getLogMessage("CMSGW_ENROLL_FAIL_NO_SUBJ_ALT_NAME", + e.getMessage())); + return null; + } + } + + // Perform authentication + + /* + * if the authentication is set up for CEP, and the user provides + * some credential, an attempt is made to authenticate the user + * If this fails, this method will return true + * If it is sucessful, this method will return true and + * an authtoken will be in the request + * + * If authentication is not configured, this method will + * return false. The request will be processed in the usual + * way, but no authtoken will be in the request. + * + * In other word, this method returns true if the request + * should be aborted, false otherwise. + */ + + private boolean authenticateUser(CRSPKIMessage req) { + boolean authenticationFailed = true; + + if (mAuthManagerName == null) { + return false; + } + + String password = (String) req.get(AUTH_PASSWORD); + + AuthCredentials authCreds = (AuthCredentials) req.get(AUTH_CREDS); + + if (authCreds == null) { + authCreds = new AuthCredentials(); + } + + // authtoken starts as null + AuthToken token = null; + + if (password != null && !password.equals("")) { + try { + authCreds.set(AUTH_PASSWORD, password); + } catch (Exception e) { + } + } + + try { + token = (AuthToken) mAuthSubsystem.authenticate(authCreds, mAuthManagerName); + authCreds.delete(AUTH_PASSWORD); + // if we got here, the authenticate call must not have thrown + // an exception + authenticationFailed = false; + } catch (EInvalidCredentials ex) { + // Invalid credentials - we must reject the request + authenticationFailed = true; + } catch (EMissingCredential mc) { + // Misssing credential - we'll log, and process manually + authenticationFailed = false; + } catch (EBaseException ex) { + // If there's some other error, we'll reject + // So, we just continue on, - AUTH_TOKEN will not be set. + } + + if (token != null) { + req.put(AUTH_TOKEN, token); + } + + return authenticationFailed; + } + + private boolean areFingerprintsEqual(IRequest req, Hashtable fingerprints) { + + Hashtable old_fprints = req.getExtDataInHashtable(IRequest.FINGERPRINTS); + if (old_fprints == null) { + return false; + } + + byte[] old_md5 = CMS.AtoB(old_fprints.get("MD5")); + byte[] new_md5 = fingerprints.get("MD5"); + + if (old_md5.length != new_md5.length) + return false; + + for (int i = 0; i < old_md5.length; i++) { + if (old_md5[i] != new_md5[i]) + return false; + } + return true; + } + + public X509CertImpl handlePKCSReq(HttpServletRequest httpReq, + IRequest cmsRequest, CRSPKIMessage req, + CRSPKIMessage crsResp, CryptoContext cx) + throws ServletException, + CryptoManager.NotInitializedException, + CRSFailureException { + + try { + unwrapPKCS10(req, cx); + Hashtable fingerprints = makeFingerPrints(req); + + if (cmsRequest != null) { + if (areFingerprintsEqual(cmsRequest, fingerprints)) { + CMS.debug("created response from request"); + return makeResponseFromRequest(req, crsResp, cmsRequest); + } else { + CMS.debug("duplicated transaction id"); + log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ENROLL_FAIL_DUP_TRANS_ID")); + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badRequest); + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + return null; + } + } + + getDetailFromRequest(req, crsResp); + boolean authFailed = authenticateUser(req); + + if (authFailed) { + CMS.debug("authentication failed"); + log(ILogger.LL_SECURITY, CMS.getLogMessage("CMSGW_ENROLL_FAIL_NO_AUTH")); + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badIdentity); + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + + // perform audit log + String auditMessage = CMS.getLogMessage( + "LOGGING_SIGNED_AUDIT_NON_PROFILE_CERT_REQUEST_5", + httpReq.getRemoteAddr(), + ILogger.FAILURE, + req.getTransactionID(), + "CRSEnrollment", + ILogger.SIGNED_AUDIT_EMPTY_VALUE); + ILogger signedAuditLogger = CMS.getSignedAuditLogger(); + if (signedAuditLogger != null) { + signedAuditLogger.log(ILogger.EV_SIGNED_AUDIT, + null, ILogger.S_SIGNED_AUDIT, + ILogger.LL_SECURITY, auditMessage); + } + + return null; + } else { + IRequest ireq = postRequest(httpReq, req, crsResp); + + CMS.debug("created response"); + return makeResponseFromRequest(req, crsResp, ireq); + } + } catch (CryptoContext.CryptoContextException e) { + CMS.debug("failed to decrypt the request " + e); + log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ENROLL_FAIL_NO_DECRYPT_PKCS10", + e.getMessage())); + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badMessageCheck); + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + } catch (EBaseException e) { + CMS.debug("operation failure - " + e); + log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_ERNOLL_FAIL_NO_NEW_REQUEST_POSTED", + e.getMessage())); + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_internalCAError); + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + } + return null; + } + + ////// post the request + + /* + needed: + + token (authtoken) + certInfo + fingerprints x + req.transactionID + crsResp + */ + + private IRequest postRequest(HttpServletRequest httpReq, CRSPKIMessage req, CRSPKIMessage crsResp) + throws EBaseException { + X500Name subject = (X500Name) req.get(SUBJECTNAME); + + if (mCreateEntry) { + if (subject == null) { + CMS.debug("CRSEnrollment::postRequest() - subject is null!"); + return null; + } + createEntry(subject.toString()); + } + + // use profile framework to handle SCEP + if (mProfileId != null) { + PKCS10 pkcs10data = req.getP10(); + String pkcs10blob = CMS.BtoA(pkcs10data.toByteArray()); + + // XXX authentication handling + CMS.debug("Found profile=" + mProfileId); + IProfile profile = mProfileSubsystem.getProfile(mProfileId); + if (profile == null) { + CMS.debug("profile " + mProfileId + " not found"); + return null; + } + IProfileContext ctx = profile.createContext(); + + IProfileAuthenticator authenticator = null; + try { + CMS.debug("Retrieving authenticator"); + authenticator = profile.getAuthenticator(); + if (authenticator == null) { + CMS.debug("No authenticator Found"); + } else { + CMS.debug("Got authenticator=" + authenticator.getClass().getName()); + } + } catch (EProfileException e) { + // authenticator not installed correctly + } + + IAuthToken authToken = null; + + // for ssl authentication; pass in servlet for retrieving + // ssl client certificates + SessionContext context = SessionContext.getContext(); + + // insert profile context so that input parameter can be retrieved + context.put("profileContext", ctx); + context.put("sslClientCertProvider", + new SSLClientCertProvider(httpReq)); + + String p10Password = getPasswordFromP10(pkcs10data); + AuthCredentials credentials = new AuthCredentials(); + credentials.set("UID", httpReq.getRemoteAddr()); + credentials.set("PWD", p10Password); + + if (authenticator == null) { + // XXX - to help caRouterCert to work, we need to + // add authentication to caRouterCert + authToken = new AuthToken(null); + } else { + authToken = authenticate(credentials, authenticator, httpReq); + } + + IRequest reqs[] = null; + CMS.debug("CRSEnrollment: Creating profile requests"); + ctx.set(IEnrollProfile.CTX_CERT_REQUEST_TYPE, "pkcs10"); + ctx.set(IEnrollProfile.CTX_CERT_REQUEST, pkcs10blob); + Locale locale = Locale.getDefault(); + reqs = profile.createRequests(ctx, locale); + if (reqs == null) { + CMS.debug("CRSEnrollment: No request has been created"); + return null; + } else { + CMS.debug("CRSEnrollment: Request (" + reqs.length + ") have been created"); + } + // set transaction id + reqs[0].setSourceId(req.getTransactionID()); + reqs[0].setExtData("profile", "true"); + reqs[0].setExtData("profileId", mProfileId); + reqs[0].setExtData(IEnrollProfile.CTX_CERT_REQUEST_TYPE, IEnrollProfile.REQ_TYPE_PKCS10); + reqs[0].setExtData(IEnrollProfile.CTX_CERT_REQUEST, pkcs10blob); + reqs[0].setExtData("requestor_name", ""); + reqs[0].setExtData("requestor_email", ""); + reqs[0].setExtData("requestor_phone", ""); + reqs[0].setExtData("profileRemoteHost", httpReq.getRemoteHost()); + reqs[0].setExtData("profileRemoteAddr", httpReq.getRemoteAddr()); + reqs[0].setExtData("profileApprovedBy", profile.getApprovedBy()); + + CMS.debug("CRSEnrollment: Populating inputs"); + profile.populateInput(ctx, reqs[0]); + CMS.debug("CRSEnrollment: Populating requests"); + profile.populate(reqs[0]); + + CMS.debug("CRSEnrollment: Submitting request"); + profile.submit(authToken, reqs[0]); + CMS.debug("CRSEnrollment: Done submitting request"); + profile.getRequestQueue().markAsServiced(reqs[0]); + CMS.debug("CRSEnrollment: Request marked as serviced"); + + return reqs[0]; + + } + + IRequestQueue rq = ca.getRequestQueue(); + IRequest pkiReq = rq.newRequest(IRequest.ENROLLMENT_REQUEST); + + AuthToken token = (AuthToken) req.get(AUTH_TOKEN); + if (token != null) { + pkiReq.setExtData(IRequest.AUTH_TOKEN, token); + } + + pkiReq.setExtData(IRequest.HTTP_PARAMS, IRequest.CERT_TYPE, IRequest.CEP_CERT); + X509CertInfo certInfo = (X509CertInfo) req.get(CERTINFO); + pkiReq.setExtData(IRequest.CERT_INFO, new X509CertInfo[] { certInfo }); + pkiReq.setExtData("cepsubstore", mSubstoreName); + + try { + String chpwd = (String) req.get(ChallengePassword.NAME); + if (chpwd != null) { + pkiReq.setExtData("challengePhrase", + chpwd); + } + } catch (Exception pwex) { + } + + Hashtable fingerprints = (Hashtable) req.get(IRequest.FINGERPRINTS); + if (fingerprints.size() > 0) { + Hashtable encodedPrints = new Hashtable(fingerprints.size()); + Enumeration e = fingerprints.keys(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + byte[] value = (byte[]) fingerprints.get(key); + encodedPrints.put(key, CMS.BtoA(value)); + } + pkiReq.setExtData(IRequest.FINGERPRINTS, encodedPrints); + } + + pkiReq.setSourceId(req.getTransactionID()); + + rq.processRequest(pkiReq); + + crsResp.setPKIStatus(CRSPKIMessage.mStatus_SUCCESS); + + mLogger.log(ILogger.EV_AUDIT, ILogger.S_OTHER, + AuditFormat.LEVEL, + AuditFormat.ENROLLMENTFORMAT, + new Object[] { + pkiReq.getRequestId(), + AuditFormat.FROMROUTER, + mAuthManagerName == null ? AuditFormat.NOAUTH : mAuthManagerName, + "pending", + subject, + "" } + ); + + return pkiReq; + } + + public Hashtable makeFingerPrints(CRSPKIMessage req) { + Hashtable fingerprints = new Hashtable(); + + MessageDigest md; + String[] hashes = new String[] { "MD2", "MD5", "SHA1", "SHA256", "SHA512" }; + PKCS10 p10 = (PKCS10) req.getP10(); + + for (int i = 0; i < hashes.length; i++) { + try { + md = MessageDigest.getInstance(hashes[i]); + md.update(p10.getCertRequestInfo()); + fingerprints.put(hashes[i], md.digest()); + } catch (NoSuchAlgorithmException nsa) { + } + } + + if (fingerprints != null) { + req.put(IRequest.FINGERPRINTS, fingerprints); + } + return fingerprints; + } + + // Take a look to see if the request was successful, and fill + // in the response message + + private X509CertImpl makeResponseFromRequest(CRSPKIMessage crsReq, CRSPKIMessage crsResp, + IRequest pkiReq) { + + X509CertImpl issuedCert = null; + + RequestStatus status = pkiReq.getRequestStatus(); + + String profileId = pkiReq.getExtDataInString("profileId"); + if (profileId != null) { + CMS.debug("CRSEnrollment: Found profile request"); + X509CertImpl cert = + pkiReq.getExtDataInCert(IEnrollProfile.REQUEST_ISSUED_CERT); + if (cert == null) { + CMS.debug("CRSEnrollment: No certificate has been found"); + } else { + CMS.debug("CRSEnrollment: Found certificate"); + } + crsResp.setPKIStatus(CRSPKIMessage.mStatus_SUCCESS); + return cert; + } + + if (status.equals(RequestStatus.COMPLETE)) { + Integer success = pkiReq.getExtDataInInteger(IRequest.RESULT); + + if (success.equals(IRequest.RES_SUCCESS)) { + // The cert was issued, lets send it back to the router + X509CertImpl[] issuedCertBuf = + pkiReq.getExtDataInCertArray(IRequest.ISSUED_CERTS); + if (issuedCertBuf == null || issuedCertBuf.length == 0) { + // writeError("Internal Error: Bad operation",httpReq,httpResp); + CMS.debug("CRSEnrollment::makeResponseFromRequest() - " + + "Bad operation"); + return null; + } + issuedCert = issuedCertBuf[0]; + crsResp.setPKIStatus(CRSPKIMessage.mStatus_SUCCESS); + + } else { // status is not 'success' - there must've been a problem + + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badAlg); + } + } else if (status.equals(RequestStatus.REJECTED_STRING) || + status.equals(RequestStatus.CANCELED_STRING)) { + crsResp.setPKIStatus(CRSPKIMessage.mStatus_FAILURE); + crsResp.setFailInfo(CRSPKIMessage.mFailInfo_badRequest); + } else { // not complete + crsResp.setPKIStatus(CRSPKIMessage.mStatus_PENDING); + } + + return issuedCert; + } + + protected String hashPassword(String pwd) { + String salt = "lala123"; + byte[] pwdDigest = mSHADigest.digest((salt + pwd).getBytes()); + String b64E = Utils.base64encode(pwdDigest); + return "{SHA}" + b64E; + } + + /** + * Make the CRSPKIMESSAGE response + */ + + private void processCertRep(CryptoContext cx, + X509CertImpl issuedCert, + CRSPKIMessage crsResp, + CRSPKIMessage crsReq) + throws CRSFailureException { + byte[] msgdigest = null; + byte[] encryptedDesKey = null; + + try { + if (issuedCert != null) { + + SymmetricKey sk; + SymmetricKey skinternal; + + KeyGenAlgorithm kga = KeyGenAlgorithm.DES; + EncryptionAlgorithm ea = EncryptionAlgorithm.DES_CBC; + if (mEncryptionAlgorithm != null && mEncryptionAlgorithm.equals("DES3")) { + kga = KeyGenAlgorithm.DES3; + ea = EncryptionAlgorithm.DES3_CBC; + } + + // 1. Make the Degenerated PKCS7 with the recipient's certificate in it + + byte toBeEncrypted[] = + crsResp.makeSignedRep(1, // version + issuedCert.getEncoded() + ); + + // 2. Encrypt the above byte array with a new random DES key + + sk = cx.getDESKeyGenerator().generate(); + + skinternal = cx.getInternalToken().getKeyGenerator(kga).clone(sk); + + byte[] padded = Cipher.pad(toBeEncrypted, ea.getBlockSize()); + + // This should be changed to generate proper DES IV. + + Cipher cipher = cx.getInternalToken().getCipherContext(ea); + IVParameterSpec desIV = + new IVParameterSpec(new byte[] { + (byte) 0xff, (byte) 0x00, + (byte) 0xff, (byte) 0x00, + (byte) 0xff, (byte) 0x00, + (byte) 0xff, (byte) 0x00 }); + + cipher.initEncrypt(sk, desIV); + byte[] encryptedData = cipher.doFinal(padded); + + crsResp.makeEncryptedContentInfo(desIV.getIV(), encryptedData, mEncryptionAlgorithm); + + // 3. Extract the recipient's public key + + PublicKey rcpPK = crsReq.getSignerPublicKey(); + + // 4. Encrypt the DES key with the public key + + // we have to move the key onto the interal token. + //skinternal = cx.getInternalKeyStorageToken().cloneKey(sk); + skinternal = cx.getInternalToken().cloneKey(sk); + + KeyWrapper kw = cx.getInternalKeyWrapper(); + kw.initWrap(rcpPK, null); + encryptedDesKey = kw.wrap(skinternal); + + crsResp.setRcpIssuerAndSerialNumber(crsReq.getSgnIssuerAndSerialNumber()); + crsResp.makeRecipientInfo(0, encryptedDesKey); + + } + + byte[] ed = crsResp.makeEnvelopedData(0); + + // 7. Make Digest of SignedData Content + MessageDigest md = MessageDigest.getInstance(mHashAlgorithm); + msgdigest = md.digest(ed); + + crsResp.setMsgDigest(msgdigest); + + } + + catch (Exception e) { + throw new CRSFailureException("Failed to create inner response to CEP message: " + e.getMessage()); + } + + // 5. Make a RecipientInfo + + // The issuer name & serial number here, should be that of + // the EE's self-signed Certificate + // [I can get it from the req blob, but later, I should + // store the recipient's self-signed certificate with the request + // so I can get at it later. I need to do this to support + // 'PENDING'] + + try { + + // 8. Make Authenticated Attributes + // we can just pull the transaction ID out of the request. + // Later, we will have to put it out of the Request queue, + // so we can support PENDING + crsResp.setTransactionID(crsReq.getTransactionID()); + // recipientNonce and SenderNonce have already been set + + crsResp.makeAuthenticatedAttributes(); + // crsResp.makeAuthenticatedAttributes_old(); + + // now package up the rest of the SignerInfo + { + byte[] signingcertbytes = cx.getSigningCert().getEncoded(); + + Certificate.Template sgncert_t = new Certificate.Template(); + Certificate sgncert = + (Certificate) sgncert_t.decode(new ByteArrayInputStream(signingcertbytes)); + + IssuerAndSerialNumber sgniasn = + new IssuerAndSerialNumber(sgncert.getInfo().getIssuer(), + sgncert.getInfo().getSerialNumber()); + + crsResp.setSgnIssuerAndSerialNumber(sgniasn); + + // 10. Make SignerInfo + crsResp.makeSignerInfo(1, cx.getPrivateKey(), mHashAlgorithm); + + // 11. Make SignedData + crsResp.makeSignedData(1, signingcertbytes, mHashAlgorithm); + + crsResp.debug(); + } + } catch (Exception e) { + throw new CRSFailureException("Failed to create outer response to CEP request: " + e.getMessage()); + } + + // if debugging, dump out the response into a file + + } + + class CryptoContext { + private CryptoManager cm; + private CryptoToken internalToken; + private CryptoToken keyStorageToken; + private CryptoToken internalKeyStorageToken; + private KeyGenerator DESkg; + private Enumeration externalTokens = null; + private org.mozilla.jss.crypto.X509Certificate signingCert; + private org.mozilla.jss.crypto.PrivateKey signingCertPrivKey; + private int signingCertKeySize = 0; + + class CryptoContextException extends Exception { + /** + * + */ + private static final long serialVersionUID = -1124116326126256475L; + + public CryptoContextException() { + super(); + } + + public CryptoContextException(String s) { + super(s); + } + } + + public CryptoContext() + throws CryptoContextException { + try { + KeyGenAlgorithm kga = KeyGenAlgorithm.DES; + if (mEncryptionAlgorithm != null && mEncryptionAlgorithm.equals("DES3")) { + kga = KeyGenAlgorithm.DES3; + } + cm = CryptoManager.getInstance(); + internalToken = cm.getInternalCryptoToken(); + DESkg = internalToken.getKeyGenerator(kga); + if (mTokenName.equalsIgnoreCase(Constants.PR_INTERNAL_TOKEN) || + mTokenName.equalsIgnoreCase("Internal Key Storage Token") || + mTokenName.length() == 0) { + keyStorageToken = cm.getInternalKeyStorageToken(); + internalKeyStorageToken = keyStorageToken; + CMS.debug("CRSEnrollment: CryptoContext: internal token name: '" + mTokenName + "'"); + } else { + keyStorageToken = cm.getTokenByName(mTokenName); + internalKeyStorageToken = null; + } + if (!mUseCA && internalKeyStorageToken == null) { + PasswordCallback cb = CMS.getPasswordCallback(); + keyStorageToken.login(cb); // ONE_TIME by default. + } + signingCert = cm.findCertByNickname(mNickname); + signingCertPrivKey = cm.findPrivKeyByCert(signingCert); + byte[] encPubKeyInfo = signingCert.getPublicKey().getEncoded(); + SEQUENCE.Template outer = SEQUENCE.getTemplate(); + outer.addElement(ANY.getTemplate()); // algid + outer.addElement(BIT_STRING.getTemplate()); + SEQUENCE outerSeq = (SEQUENCE) ASN1Util.decode(outer, encPubKeyInfo); + BIT_STRING bs = (BIT_STRING) outerSeq.elementAt(1); + byte[] encPubKey = bs.getBits(); + if (bs.getPadCount() != 0) { + throw new CryptoContextException( + "Internal error: Invalid Public key. Not an integral number of bytes."); + } + SEQUENCE.Template inner = new SEQUENCE.Template(); + inner.addElement(INTEGER.getTemplate()); + inner.addElement(INTEGER.getTemplate()); + SEQUENCE pubKeySeq = (SEQUENCE) ASN1Util.decode(inner, encPubKey); + INTEGER modulus = (INTEGER) pubKeySeq.elementAt(0); + signingCertKeySize = modulus.bitLength(); + + try { + FileOutputStream fos = new FileOutputStream("pubkey.der"); + fos.write(signingCert.getPublicKey().getEncoded()); + fos.close(); + } catch (Exception e) { + } + + } catch (InvalidBERException e) { + throw new CryptoContextException( + "Internal Error: Bad internal Certificate Representation. Not a valid RSA-signed certificate"); + } catch (CryptoManager.NotInitializedException e) { + throw new CryptoContextException("Crypto Manager not initialized"); + } catch (NoSuchAlgorithmException e) { + throw new CryptoContextException("Cannot create DES key generator"); + } catch (ObjectNotFoundException e) { + throw new CryptoContextException("Certificate not found: " + ca.getNickname()); + } catch (TokenException e) { + throw new CryptoContextException("Problem with Crypto Token: " + e.getMessage()); + } catch (NoSuchTokenException e) { + throw new CryptoContextException("Crypto Token not found: " + e.getMessage()); + } catch (IncorrectPasswordException e) { + throw new CryptoContextException("Incorrect Password."); + } + } + + public KeyGenerator getDESKeyGenerator() { + return DESkg; + } + + public CryptoToken getInternalToken() { + return internalToken; + } + + public void setExternalTokens(Enumeration tokens) { + externalTokens = tokens; + } + + public Enumeration getExternalTokens() { + return externalTokens; + } + + public CryptoToken getInternalKeyStorageToken() { + return internalKeyStorageToken; + } + + public CryptoToken getKeyStorageToken() { + return keyStorageToken; + } + + public CryptoManager getCryptoManager() { + return cm; + } + + public KeyWrapper getKeyWrapper() + throws CryptoContextException { + try { + return signingCertPrivKey.getOwningToken().getKeyWrapper(KeyWrapAlgorithm.RSA); + } catch (TokenException e) { + throw new CryptoContextException("Problem with Crypto Token: " + e.getMessage()); + } catch (NoSuchAlgorithmException e) { + throw new CryptoContextException(e.getMessage()); + } + } + + public KeyWrapper getInternalKeyWrapper() + throws CryptoContextException { + try { + return getInternalToken().getKeyWrapper(KeyWrapAlgorithm.RSA); + } catch (TokenException e) { + throw new CryptoContextException("Problem with Crypto Token: " + e.getMessage()); + } catch (NoSuchAlgorithmException e) { + throw new CryptoContextException(e.getMessage()); + } + } + + public org.mozilla.jss.crypto.PrivateKey getPrivateKey() { + return signingCertPrivKey; + } + + public org.mozilla.jss.crypto.X509Certificate getSigningCert() { + return signingCert; + } + + } + + /* General failure. The request/response cannot be processed. */ + + class CRSFailureException extends Exception { + /** + * + */ + private static final long serialVersionUID = 1962741611501549051L; + + public CRSFailureException() { + super(); + } + + public CRSFailureException(String s) { + super(s); + } + } + + class CRSInvalidSignatureException extends Exception { + /** + * + */ + private static final long serialVersionUID = 9096408193567657944L; + + public CRSInvalidSignatureException() { + super(); + } + + public CRSInvalidSignatureException(String s) { + super(s); + } + } + + class CRSPolicyException extends Exception { + /** + * + */ + private static final long serialVersionUID = 5846593800658787396L; + + public CRSPolicyException() { + super(); + } + + public CRSPolicyException(String s) { + super(s); + } + } + +} diff --git a/base/common/src/com/netscape/cms/servlet/cert/scep/ChallengePassword.java b/base/common/src/com/netscape/cms/servlet/cert/scep/ChallengePassword.java new file mode 100644 index 000000000..ff55dc9ce --- /dev/null +++ b/base/common/src/com/netscape/cms/servlet/cert/scep/ChallengePassword.java @@ -0,0 +1,141 @@ +// --- 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.cert.scep; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerValue; +import netscape.security.x509.CertAttrSet; + +/** + * Class for handling the decoding of a SCEP Challenge Password + * object. Currently this class cannot be used for encoding + * thus some fo the methods are unimplemented + */ +public class ChallengePassword implements CertAttrSet { + + public static final String NAME = "ChallengePassword"; + public static final String PASSWORD = "password"; + + private String cpw; + + /** + * Get the password marshalled in this object + * + * @return the challenge password + */ + public String toString() { + return cpw; + } + + /** + * Create a ChallengePassword object + * + * @param stuff (must be of type byte[]) a DER-encoded by array following + * The ASN.1 template for ChallenegePassword specified in the SCEP + * documentation + * @throws IOException if the DER encoded byt array was malformed, or if it + * did not match the template + */ + + public ChallengePassword(Object stuff) + throws IOException { + + ByteArrayInputStream is = new ByteArrayInputStream((byte[]) stuff); + try { + decode(is); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + + } + + /** + * Currently Unimplemented + */ + public void encode(OutputStream out) + throws CertificateException, IOException { + } + + public void decode(InputStream in) + throws CertificateException, IOException { + DerValue derVal = new DerValue(in); + + construct(derVal); + + } + + private void construct(DerValue derVal) throws IOException { + try { + cpw = derVal.getPrintableString(); + } catch (NullPointerException e) { + cpw = ""; + } + } + + /** + * Currently Unimplemented + */ + public void set(String name, Object obj) + throws CertificateException, IOException { + } + + /** + * Get an attribute of this object. + * + * @param name the name of the attribute of this object to get. The only + * supported attribute is "password" + */ + public Object get(String name) + throws CertificateException, IOException { + if (name.equalsIgnoreCase(PASSWORD)) { + return cpw; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: ChallengePassword"); + } + } + + /** + * Currently Unimplemented + */ + public void delete(String name) + throws CertificateException, IOException { + } + + /** + * @return an empty set of elements + */ + public Enumeration getAttributeNames() { + return (new Vector()).elements(); + } + + /** + * @return the String "ChallengePassword" + */ + public String getName() { + return NAME; + } + +} diff --git a/base/common/src/com/netscape/cms/servlet/cert/scep/ExtensionsRequested.java b/base/common/src/com/netscape/cms/servlet/cert/scep/ExtensionsRequested.java new file mode 100644 index 000000000..85f3938b8 --- /dev/null +++ b/base/common/src/com/netscape/cms/servlet/cert/scep/ExtensionsRequested.java @@ -0,0 +1,176 @@ +// --- 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.cert.scep; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.DerInputStream; +import netscape.security.util.DerValue; +import netscape.security.x509.CertAttrSet; +import netscape.security.x509.Extension; + +public class ExtensionsRequested implements CertAttrSet { + + public static final String NAME = "EXTENSIONS_REQUESTED"; + + public static final String KUE_DIGITAL_SIGNATURE = "kue_digital_signature"; + public static final String KUE_KEY_ENCIPHERMENT = "kue_key_encipherment"; + + private String kue_digital_signature = "false"; + private String kue_key_encipherment = "false"; + + private Vector exts = new Vector(); + + public ExtensionsRequested(Object stuff) throws IOException { + ByteArrayInputStream is = new ByteArrayInputStream((byte[]) stuff); + + try { + decode(is); + } catch (Exception e) { + e.printStackTrace(); + throw new IOException(e.getMessage()); + } + } + + public void encode(OutputStream out) + throws CertificateException, IOException { + } + + public void decode(InputStream in) + throws CertificateException, IOException { + DerValue derVal = new DerValue(in); + + construct(derVal); + } + + public void set(String name, Object obj) + throws CertificateException, IOException { + } + + public Object get(String name) + throws CertificateException, IOException { + if (name.equalsIgnoreCase(KUE_DIGITAL_SIGNATURE)) { + return kue_digital_signature; + } + if (name.equalsIgnoreCase(KUE_KEY_ENCIPHERMENT)) { + return kue_key_encipherment; + } + + throw new IOException("Unsupported attribute queried"); + } + + public void delete(String name) + throws CertificateException, IOException { + } + + public Enumeration getAttributeNames() { + return (new Vector()).elements(); + } + + public String getName() { + return NAME; + } + + /** + * construct - expects this in the inputstream (from the router): + * + * 211 30 31: SEQUENCE { + * 213 06 10: OBJECT IDENTIFIER '2 16 840 1 113733 1 9 8' + * 225 31 17: SET { + * 227 04 15: OCTET STRING, encapsulates { + * 229 30 13: SEQUENCE { + * 231 30 11: SEQUENCE { + * 233 06 3: OBJECT IDENTIFIER keyUsage (2 5 29 15) + * 238 04 4: OCTET STRING + * : 03 02 05 A0 + * : } + * : } + * : } + * + * or this (from IRE client): + * + * 262 30 51: SEQUENCE { + * 264 06 9: OBJECT IDENTIFIER extensionReq (1 2 840 113549 1 9 14) + * 275 31 38: SET { + * 277 30 36: SEQUENCE { + * 279 30 34: SEQUENCE { + * 281 06 3: OBJECT IDENTIFIER subjectAltName (2 5 29 17) + * 286 04 27: OCTET STRING + * : 30 19 87 04 D0 0C 3E 6F 81 03 61 61 61 82 0C 61 + * : 61 61 2E 6D 63 6F 6D 2E 63 6F 6D + * : } + * : } + * : } + * : } + */ + private void construct(DerValue dv) throws IOException { + + DerInputStream stream = null; + + try { // try decoding as sequence first + + stream = dv.toDerInputStream(); + + stream.getDerValue(); // consume stream + stream.reset(); + + stream.getSequence(2); // consume stream + } catch (IOException ioe) { + // if it failed, the outer sequence may be + // encapsulated in an octet string, as in the first + // example above + + byte[] octet_string = dv.getOctetString(); + + // Make a new input stream from the byte array, + // and re-parse it as a sequence. + + dv = new DerValue(octet_string); + + stream = dv.toDerInputStream(); + stream.getSequence(2); // consume stream + } + + // now, the stream will be in the correct format + stream.reset(); + + while (true) { + DerValue ext_dv = null; + try { + ext_dv = stream.getDerValue(); + } catch (IOException ex) { + break; + } + + Extension ext = new Extension(ext_dv); + exts.addElement(ext); + } + + } + + public Vector getExtensions() { + return exts; + } + +} -- cgit