diff options
Diffstat (limited to 'base/tps/src')
75 files changed, 19428 insertions, 0 deletions
diff --git a/base/tps/src/CMakeLists.txt b/base/tps/src/CMakeLists.txt new file mode 100644 index 000000000..b36a01526 --- /dev/null +++ b/base/tps/src/CMakeLists.txt @@ -0,0 +1,170 @@ +project(pki-tps_java NONE) + +find_file(LDAPJDK_JAR + NAMES + ldapjdk.jar + PATHS + /usr/share/java +) + +find_file(JSS_JAR + NAMES + jss4.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} +) + +find_file(COMMONS_CODEC_JAR + NAMES + commons-codec.jar + PATHS + /usr/share/java +) + +find_file(COMMONS_LANG_JAR + NAMES + commons-lang.jar + PATHS + /usr/share/java +) + +find_file(JAXRS_API_JAR + NAMES + jaxrs-api.jar + PATHS + ${RESTEASY_LIB} +) + +find_file(TOMCAT_CATALINA_JAR + NAMES + catalina.jar + PATHS + /usr/share/java/tomcat +) + +find_file(PKI_CERTSRV_JAR + NAMES + pki-certsrv.jar + PATHS + /usr/share/java/pki +) + +find_file(PKI_CMS_JAR + NAMES + pki-cms.jar + PATHS + /usr/share/java/pki +) + +find_file(PKI_CMSCORE_JAR + NAMES + pki-cmscore.jar + PATHS + /usr/share/java/pki +) + +find_file(PKI_CMSUTIL_JAR + NAMES + pki-cmsutil.jar + PATHS + /usr/share/java/pki +) + +find_file(PKI_NSUTIL_JAR + NAMES + pki-nsutil.jar + PATHS + /usr/share/java/pki +) + +find_file(JAXRS_API_JAR + NAMES + jaxrs-api.jar + PATHS + ${RESTEASY_LIB} +) + +find_file(RESTEASY_JAXRS_JAR + NAMES + resteasy-jaxrs.jar + PATHS + ${RESTEASY_LIB} +) + +find_file(RESTEASY_ATOM_PROVIDER_JAR + NAMES + resteasy-atom-provider.jar + PATHS + ${RESTEASY_LIB} +) + +find_file(SERVLET_JAR + NAMES + servlet.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +find_file(SYMKEY_JAR + NAMES + symkey.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +find_file(VELOCITY_JAR + NAMES + velocity.jar + PATHS + ${JAVA_LIB_INSTALL_DIR} + /usr/share/java +) + +# build pki-tps +javac(pki-tps-classes + SOURCES + org/dogtagpki/server/tps/*.java + CLASSPATH + ${COMMONS_CODEC_JAR} ${COMMONS_LANG_JAR} + ${LDAPJDK_JAR} + ${JSS_JAR} ${SYMKEY_JAR} + ${JAXRS_API_JAR} ${RESTEASY_JAXRS_JAR} ${RESTEASY_ATOM_PROVIDER_JAR} + ${SERVLET_JAR} ${TOMCAT_CATALINA_JAR} ${VELOCITY_JAR} + ${PKI_CMSUTIL_JAR} ${PKI_NSUTIL_JAR} + ${PKI_CERTSRV_JAR} ${PKI_CMS_JAR} ${PKI_CMSCORE_JAR} + OUTPUT_DIR + ${CMAKE_BINARY_DIR}/classes + DEPENDS + pki-nsutil-jar pki-cmsutil-jar pki-certsrv-jar pki-cms-jar pki-cmscore-jar +) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/pki-tps.mf + ${CMAKE_CURRENT_BINARY_DIR}/pki-tps.mf +) + +jar(pki-tps-jar + CREATE + ${CMAKE_BINARY_DIR}/dist/pki-tps.jar + OPTIONS + m + PARAMS + ${CMAKE_CURRENT_BINARY_DIR}/pki-tps.mf + INPUT_DIR + ${CMAKE_BINARY_DIR}/classes + FILES + org/dogtagpki/server/tps/*.class + DEPENDS + pki-tps-classes +) + +install( + FILES + ${CMAKE_BINARY_DIR}/dist/pki-tps.jar + DESTINATION + ${JAVA_JAR_INSTALL_DIR}/pki +) + +set(PKI_TPS_JAR ${CMAKE_BINARY_DIR}/dist/pki-tps.jar CACHE INTERNAL "pki-tps jar file") diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSPhoneHome.java b/base/tps/src/org/dogtagpki/server/tps/TPSPhoneHome.java new file mode 100644 index 000000000..e0b3b6b87 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/TPSPhoneHome.java @@ -0,0 +1,95 @@ +package org.dogtagpki.server.tps; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.dogtagpki.tps.main.TPSBuffer; + +import com.netscape.certsrv.apps.CMS; + +public class TPSPhoneHome extends HttpServlet { + + private static final long serialVersionUID = 1864386666927370987L; + private static String phoneHomeName = "phoneHome.xml"; + + public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + //Simply return xml file to the client + //In the future we could get this info from elsewhere such as LDAP + + CMS.debug("TPSPhoneHome entering."); + + renderPhoneHome(request, response); + } + + private void renderPhoneHome(HttpServletRequest request, HttpServletResponse response) throws ServletException, + IOException { + ServletOutputStream stream = null; + BufferedInputStream buf = null; + FileInputStream input = null; + + try { + + stream = response.getOutputStream(); + response.setContentType("application/xml"); + + String confPath = getConfigPath(); + + confPath += File.separator + phoneHomeName; + + input = new FileInputStream(confPath); + // InputStream input = ctx.getResourceAsStream(phoneHomeName); + buf = new BufferedInputStream(input); + + int readBytes = 0; + TPSBuffer readData = new TPSBuffer(); + while ((readBytes = buf.read()) != -1) { + stream.write(readBytes); + readData.add((byte) readBytes); + } + + CMS.debug("TPSPhoneHome.renderPhoneHome: data: " + readData.toHexString()); + + } catch (IOException e) { + CMS.debug("TPSPhoneHome.renderPhoneHome: Error encountered: " + e); + throw new ServletException("TPSPhoneHome.renderPhoneHome: Error encountered: " + e); + } finally { + if (stream != null) + stream.close(); + if (buf != null) + buf.close(); + if (input != null) + input.close(); + } + + } + + private String getConfigPath() { + + String path = null; + String context = getServletContext().getContextPath(); + + // get subsystem name by removing the / prefix from the context + String subsystem = context.startsWith("/") ? context.substring(1) : context; + + // catalina.base points to instance dir + String instanceDir = System.getProperty("catalina.base"); + + //Finish off path of conf directory + path = instanceDir + File.separator + "conf" + File.separator + + subsystem + File.separator; + + CMS.debug("TPSPhoneHome.getConfigPath: returning: " + path); + + return path; + + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSServlet.java b/base/tps/src/org/dogtagpki/server/tps/TPSServlet.java new file mode 100644 index 000000000..87c416b3e --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/TPSServlet.java @@ -0,0 +1,65 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.dogtagpki.tps.TPSConnection; + +import com.netscape.certsrv.apps.CMS; + +/** + * @author Endi S. Dewata <edewata@redhat.com> + */ +public class TPSServlet extends HttpServlet { + + private static final long serialVersionUID = -1092227495262381074L; + + public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { + + String encoding = request.getHeader("Transfer-Encoding"); + + CMS.debug("Encoding: " + encoding); + + if (encoding.equals("chunked") == false) { + throw new IOException("TPSServlet.service: incorrect encoding! "); + } + + response.setHeader("Transfer-Encoding", "chunked"); + + TPSConnection con = new TPSConnection( + request.getInputStream(), response.getOutputStream(), true); + + CMS.debug("TPSConnection created: " + con); + + String ipAddress = request.getRemoteAddr(); + + TPSSession session = new TPSSession(con, ipAddress); + + CMS.debug("TPSSession created: " + session); + + session.process(); + + CMS.debug("After session.process() exiting ..."); + + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSSession.java b/base/tps/src/org/dogtagpki/server/tps/TPSSession.java new file mode 100644 index 000000000..4a175e698 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/TPSSession.java @@ -0,0 +1,174 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps; + +import java.io.IOException; + +import org.dogtagpki.server.tps.dbs.TokenRecord; +import org.dogtagpki.server.tps.processor.TPSEnrollProcessor; +import org.dogtagpki.server.tps.processor.TPSPinResetProcessor; +import org.dogtagpki.server.tps.processor.TPSProcessor; +import org.dogtagpki.tps.TPSConnection; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.msg.BeginOpMsg; +import org.dogtagpki.tps.msg.EndOpMsg; +import org.dogtagpki.tps.msg.TPSMessage; + +import com.netscape.certsrv.apps.CMS; + +public class TPSSession { + + private TPSConnection connection; + private String ipAddress; /* remote IP */ + private TokenRecord tokenRecord; + + public TPSSession(TPSConnection conn, String ip) { + + if (conn == null) { + throw new NullPointerException("TPSSession incoming connection is null!"); + } + + CMS.debug("TPSSession constructor conn: " + conn); + connection = conn; + + if (ip == null) { + // probably unlikely to happen; log it and continue anyway + CMS.debug("TPSSession constructor remote ipAddress null"); + } else { + CMS.debug("TPSSession constructor remote ipAddress: " + getIpAddress()); + } + setIpAddress(ip); + + } + + public TPSConnection getConnection() { + return connection; + } + + public TPSMessage read() throws IOException { + TPSMessage message = null; + + CMS.debug("TPSSession.process() about to call read on connection : " + connection); + + try { + message = connection.read(); + CMS.debug("TPSSession.process() created message " + message); + + } catch (IOException e) { + //Catch here so we can log + CMS.debug("TPSSession.process: Exception reading from the client: " + e.toString()); + throw e; + } + + return message; + } + + public void write(TPSMessage msg) throws IOException { + + try { + connection.write(msg); + } catch (Exception e) { + //Catch here so we can log + CMS.debug("Exception writing to client: " + e.toString()); + throw e; + } + + } + + public void process() throws IOException { + EndOpMsg.TPSStatus status = EndOpMsg.TPSStatus.STATUS_NO_ERROR; + CMS.debug("In TPSSession.process()"); + + TPSMessage firstMsg = read(); + + TPSMessage.MsgType msg_type = firstMsg.getType(); + TPSMessage.OpType op_type = firstMsg.getOpType(); + + if (msg_type != TPSMessage.MsgType.MSG_BEGIN_OP) { + throw new IOException("Wrong first message type read in TPSSession.process!"); + } + + int result = EndOpMsg.RESULT_ERROR; + BeginOpMsg beginOp = (BeginOpMsg) firstMsg; + try { + switch (op_type) { + case OP_FORMAT: + + //Assume success, processor will indicate otherwise + result = EndOpMsg.RESULT_GOOD; + TPSProcessor processor = new TPSProcessor(this); + processor.process(beginOp); + break; + + case OP_ENROLL: + //Assume success, processor will indicate otherwise + result = EndOpMsg.RESULT_GOOD; + TPSEnrollProcessor enrollProcessor = new TPSEnrollProcessor(this); + enrollProcessor.process(beginOp); + break; + case OP_RENEW: + break; + case OP_RESET_PIN: + result = EndOpMsg.RESULT_GOOD; + TPSPinResetProcessor pinResetProcessor = new TPSPinResetProcessor(this); + pinResetProcessor.process(beginOp); + break; + case OP_UNBLOCK: + break; + case OP_UNDEFINED: + break; + default: + break; + + } + } catch (TPSException e) { + //Get the status from the exception and return it to the client. + CMS.debug("TPSSession.process: Message processing failed: " + e); + status = e.getStatus(); + result = EndOpMsg.RESULT_ERROR; + } catch (IOException e) { + CMS.debug("TPSSession.process: IO error happened during processing: " + e); + // We get here we are done. + throw e; + + } + + EndOpMsg endOp = new EndOpMsg(firstMsg.getOpType(), result, status); + write(endOp); + + CMS.debug("TPSSession.process: leaving: result: " + result + " status: " + status); + + } + + public TokenRecord getTokenRecord() { + return tokenRecord; + } + + public void setTokenRecord(TokenRecord tokenRecord) { + this.tokenRecord = tokenRecord; + } + + public String getIpAddress() { + return ipAddress; + } + + private void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSSubsystem.java b/base/tps/src/org/dogtagpki/server/tps/TPSSubsystem.java new file mode 100644 index 000000000..75cdddadb --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/TPSSubsystem.java @@ -0,0 +1,231 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps; + +import org.dogtagpki.server.tps.authentication.AuthenticationManager; +import org.dogtagpki.server.tps.cms.ConnectionManager; +import org.dogtagpki.server.tps.config.AuthenticatorDatabase; +import org.dogtagpki.server.tps.config.ConfigDatabase; +import org.dogtagpki.server.tps.config.ConnectorDatabase; +import org.dogtagpki.server.tps.config.ProfileDatabase; +import org.dogtagpki.server.tps.config.ProfileMappingDatabase; +import org.dogtagpki.server.tps.dbs.ActivityDatabase; +import org.dogtagpki.server.tps.dbs.TPSCertDatabase; +import org.dogtagpki.server.tps.dbs.TokenDatabase; +import org.dogtagpki.server.tps.engine.TPSEngine; +import org.dogtagpki.server.tps.profile.TokenProfileResolverManager; +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.CryptoManager.NotInitializedException; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.TokenException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authority.IAuthority; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.ISubsystem; +import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.request.IRequestListener; +import com.netscape.certsrv.request.IRequestQueue; +import com.netscape.cmscore.dbs.DBSubsystem; + +/** + * @author Endi S. Dewata <edewata@redhat.com> + */ +public class TPSSubsystem implements IAuthority, ISubsystem { + + public final static String ID = "tps"; + + public ILogger logger = CMS.getLogger(); + + public String id; + public String nickname; + public ISubsystem owner; + public IConfigStore config; + + public ActivityDatabase activityDatabase; + public AuthenticatorDatabase authenticatorDatabase; + public TPSCertDatabase certDatabase; + public ConfigDatabase configDatabase; + public ConnectorDatabase connectorDatabase; + public ProfileDatabase profileDatabase; + public ProfileMappingDatabase profileMappingDatabase; + public TokenDatabase tokenDatabase; + public ConnectionManager connManager; + public AuthenticationManager authManager; + public TokenProfileResolverManager profileResolverManager; + public TPSEngine engine; + public TPSTokendb tdb; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) throws EBaseException { + this.id = id; + } + + @Override + public void init(ISubsystem owner, IConfigStore config) throws EBaseException { + this.owner = owner; + this.config = config; + + IDBSubsystem dbSubsystem = DBSubsystem.getInstance(); + IConfigStore cs = CMS.getConfigStore(); + + String activityDatabaseDN = cs.getString("tokendb.activityBaseDN"); + activityDatabase = new ActivityDatabase(dbSubsystem, activityDatabaseDN); + + String certDatabaseDN = cs.getString("tokendb.certBaseDN"); + certDatabase = new TPSCertDatabase(dbSubsystem, certDatabaseDN); + + String tokenDatabaseDN = cs.getString("tokendb.baseDN"); + tokenDatabase = new TokenDatabase(dbSubsystem, tokenDatabaseDN); + + configDatabase = new ConfigDatabase(); + authenticatorDatabase = new AuthenticatorDatabase(); + connectorDatabase = new ConnectorDatabase(); + profileDatabase = new ProfileDatabase(); + profileMappingDatabase = new ProfileMappingDatabase(); + tdb = new TPSTokendb(this); + + engine = new TPSEngine(); + engine.init(); + + } + + @Override + public void startup() throws EBaseException { + CMS.debug("TPSSubsystem: startup() begins"); + connManager = new ConnectionManager(); + connManager.initConnectors(); + authManager = new AuthenticationManager(); + authManager.initAuthInstances(); + profileResolverManager = new TokenProfileResolverManager(); + profileResolverManager.initProfileResolverInstances(); + CMS.debug("TPSSubsystem: startup() ends."); + } + + @Override + public void shutdown() { + } + + @Override + public IConfigStore getConfigStore() { + return config; + } + + @Override + public IRequestQueue getRequestQueue() { + return null; + } + + @Override + public void registerRequestListener(IRequestListener listener) { + } + + @Override + public void registerPendingListener(IRequestListener listener) { + } + + @Override + public void log(int level, String msg) { + logger.log(ILogger.EV_SYSTEM, ILogger.S_TPS, level, msg); + } + + @Override + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + @Override + public String getOfficialName() { + return "tps"; + } + + public ActivityDatabase getActivityDatabase() { + return activityDatabase; + } + + public AuthenticatorDatabase getAuthenticatorDatabase() { + return authenticatorDatabase; + } + + public TPSCertDatabase getCertDatabase() { + return certDatabase; + } + + public ConfigDatabase getConfigDatabase() { + return configDatabase; + } + + public ConnectorDatabase getConnectorDatabase() { + return connectorDatabase; + } + + public ProfileDatabase getProfileDatabase() { + return profileDatabase; + } + + public ProfileMappingDatabase getProfileMappingDatabase() { + return profileMappingDatabase; + } + + public TokenDatabase getTokenDatabase() { + return tokenDatabase; + } + + public ConnectionManager getConnectionManager() { + return connManager; + } + + public AuthenticationManager getAuthenticationManager() { + return authManager; + } + + public TokenProfileResolverManager getProfileResolverManager() { + return profileResolverManager; + } + + public TPSTokendb getTokendb() { + return tdb; + } + + public org.mozilla.jss.crypto.X509Certificate getSubsystemCert() throws EBaseException, NotInitializedException, + ObjectNotFoundException, TokenException { + IConfigStore cs = CMS.getConfigStore(); + String nickname = cs.getString("tps.subsystem.nickname", ""); + String tokenname = cs.getString("tps.subsystem.tokenname", ""); + if (!tokenname.equals("internal") && !tokenname.equals("Internal Key Storage Token")) + nickname = tokenname + ":" + nickname; + + CryptoManager cm = CryptoManager.getInstance(); + return cm.findCertByNickname(nickname); + } + + public TPSEngine getEngine() { + return engine; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSTokenPolicy.java b/base/tps/src/org/dogtagpki/server/tps/TPSTokenPolicy.java new file mode 100644 index 000000000..1a866f737 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/TPSTokenPolicy.java @@ -0,0 +1,158 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps; + +import org.dogtagpki.server.tps.dbs.TokenRecord; +import org.dogtagpki.tps.main.TPSException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.EPropertyNotFound; +import com.netscape.certsrv.base.IConfigStore; + +/* + * TPSTokenPolicy - handles token enrollment related policies + * + * @author cfu + */ +public class TPSTokenPolicy { + private TPSSubsystem tps; + private static final String DEFAULT_POLICY_SET_STRING = + "RE_ENROLL=YES;RENEW=NO;FORCE_FORMAT=NO;PIN_RESET=NO;RESET_PIN_RESET_TO_NO=NO"; + private boolean re_enroll = true; + private boolean renew = false; + private boolean force_format = false; + private boolean pin_reset = true; + private boolean reset_pin_reset_to_no = false; + + public TPSTokenPolicy (TPSSubsystem tps) throws TPSException { + if (tps == null) { + String msg = "TPSTokenPolicy.TPSTokenPolicy: tps cannnot be null"; + CMS.debug(msg); + throw new TPSException(msg); + } + this.tps = tps; + // init from config first + String policySetString = getDefaultPolicySetString(); + parsePolicySetString(policySetString); + + } + + public String getDefaultPolicySetString() { + IConfigStore configStore = CMS.getConfigStore(); + String configName = "tokendb.defaultPolicy"; + String policySetString; + try { + policySetString = configStore.getString(configName); + } catch (EPropertyNotFound e) { + policySetString = DEFAULT_POLICY_SET_STRING; + } catch (EBaseException e) { + policySetString = DEFAULT_POLICY_SET_STRING; + } + + return policySetString; + } + + public void parsePolicySetString (String policySetString) { + if (policySetString == null) + return; // take the default + + String[] policySet = policySetString.split(";"); + for (String policyString : policySet) { + String[] policy = policyString.split("="); + if (policy[0].equalsIgnoreCase("RE_ENROLL")) + re_enroll = getBool(policy[1], true); + else if (policy[0].equalsIgnoreCase("RENEW")) + renew = getBool(policy[1], false); + else if (policy[0].equalsIgnoreCase("FORCE_FORMAT")) + force_format = getBool(policy[1], false); + else if (policy[0].equalsIgnoreCase("PIN_RESET")) + pin_reset = getBool(policy[1], false); + else if (policy[0].equalsIgnoreCase("RESET_PIN_RESET_TO_NO")) + reset_pin_reset_to_no = getBool(policy[1], false); + //else no change, just take the default; + } + } + +/* + * getBool translates string to boolean: + * true: "YES", "yes", "TRUE", "true" + * false: "NO", "no", "FALSE", "false" + * + * if tring is null or Anything othrer than the above, defaultbool is returned + */ + private boolean getBool(String string, boolean defaultBool) { + if (string == null) + return defaultBool; + + if (string.equalsIgnoreCase("YES") || + string.equalsIgnoreCase("true")) { + return true; + } else if (string.equalsIgnoreCase("NO") || + string.equalsIgnoreCase("false")) { + return false; + } + + return defaultBool; + } + + private void getUpdatedPolicy(String cuid) { + // note: default policy already initialized in the constructor + TokenRecord tokenRecord = null; + String policySetString = null; + try { + tokenRecord = tps.tdb.tdbGetTokenEntry(cuid); + } catch (Exception e) { + // just take the default; + return; + } + + policySetString = tokenRecord.getPolicy(); + parsePolicySetString(policySetString); + } + + public boolean isAllowedTokenPinReset(String cuid) { + getUpdatedPolicy(cuid); + + return reset_pin_reset_to_no; + } + + public boolean isAllowedPinReset(String cuid) { + getUpdatedPolicy(cuid); + + return pin_reset; + } + + public boolean isForceTokenFormat(String cuid) { + getUpdatedPolicy(cuid); + + return force_format; + } + + public boolean isAllowdTokenReenroll(String cuid) { + getUpdatedPolicy(cuid); + + return re_enroll; + } + + public boolean isAllowdTokenRenew(String cuid) { + getUpdatedPolicy(cuid); + + return renew; + } +}
\ No newline at end of file diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java new file mode 100644 index 000000000..914706dd9 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java @@ -0,0 +1,425 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +import netscape.security.x509.RevocationReason; + +import org.dogtagpki.server.tps.cms.CARemoteRequestHandler; +import org.dogtagpki.server.tps.cms.CARevokeCertResponse; +import org.dogtagpki.server.tps.dbs.TPSCertRecord; +import org.dogtagpki.server.tps.dbs.TokenRecord; +import org.dogtagpki.tps.main.TPSException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.tps.token.TokenStatus; + +/* + * TPSTokendb class offers a collection of tokendb management convenience routines + */ +public class TPSTokendb { + private TPSSubsystem tps; + private Map<TokenStatus, Collection<TokenStatus>> allowedTransitions = new HashMap<TokenStatus, Collection<TokenStatus>>(); + + public TPSTokendb(TPSSubsystem tps) throws EBaseException { + if (tps == null) { + String msg = "TPStokendb.TPSTokendb: tps cannot be null"; + CMS.debug(msg); + throw new EBaseException(msg); + } + this.tps = tps; + try { + initAllowedTransitions(); + } catch (Exception e) { + CMS.debug("TPSTokendb: initAllowedTransitions() failed:" + e); + throw new EBaseException(e.toString()); + } + } + + void initAllowedTransitions() + throws Exception { + CMS.debug("TPSTokendb.initAllowedTransitions()"); + IConfigStore configStore = CMS.getConfigStore(); + + // load allowed token state transitions + CMS.debug("TPSTokendbs: allowed transitions:"); + + for (String transition : configStore.getString("tokendb.allowedTransitions").split(",")) { + String states[] = transition.split(":"); + TokenStatus fromState = TokenStatus.fromInt(Integer.valueOf(states[0])); + TokenStatus toState = TokenStatus.fromInt(Integer.valueOf(states[1])); + CMS.debug("TPSTokendb: - " + fromState + " to " + toState); + + Collection<TokenStatus> nextStates = allowedTransitions.get(fromState); + if (nextStates == null) { + nextStates = new HashSet<TokenStatus>(); + allowedTransitions.put(fromState, nextStates); + } + nextStates.add(toState); + } + } + + public boolean isTransitionAllowed(TokenRecord tokenRecord, TokenStatus newState) { + boolean result = false; + TokenStatus currentTokenStatus = tokenRecord.getTokenStatus(); + CMS.debug("TokenRecord.isTransitionAllowed(): current status: " + currentTokenStatus); + Collection<TokenStatus> nextStatuses = allowedTransitions.get(currentTokenStatus); + CMS.debug("TokenRecord.isTransitionAllowed(): allowed next statuses: " + nextStatuses); + if (nextStatuses == null || !nextStatuses.contains(newState)) { + CMS.debug("TokenRecord.isTransitionAllowed(): next status not allowed: " + newState); + + result = false; + } else { + //status change allowed + result = true; + } + return result; + } + + /* + * tdbActivity logs token activities; This version is called by non-administrative functions + */ + public void tdbActivity( + String op, TokenRecord tokenRecord, String ip, String msg, String result) { + try { + tps.activityDatabase.log( + ip, + (tokenRecord != null)? tokenRecord.getId():null, + op, + result, + msg, + (tokenRecord != null)? tokenRecord.getUserID():null, + (tokenRecord != null)? tokenRecord.getType():null); + } catch (Exception e) { + msg = msg + ";" + " tokendb activity logging failure: " + e; + } + } + + /* + * tdbActivity logs token activities; This version is called by administrative functions + */ + public void tdbActivity( + String op, TokenRecord tokenRecord, String ip, String msg, String result, String uid) { + try { + tps.activityDatabase.log( + ip, + (tokenRecord != null)? tokenRecord.getId():null, + op, + result, + msg, + uid, + (tokenRecord != null)? tokenRecord.getType():null); + } catch (Exception e) { + msg = msg + ";" + " tokendb activity logging failure: " + e; + } + } + + public boolean isTokenPresent(String cuid) { + boolean present = false; + try { + tps.tokenDatabase.getRecord(cuid); + present = true; + } catch (Exception e) { + CMS.debug("TPSTokendb.isTokenPresent: token entry not found"); + present = false; + } + return present; + } + + public TokenRecord tdbGetTokenEntry(String cuid) + throws Exception { + return tps.tokenDatabase.getRecord(cuid); + } + + /* + * tdbFindTokenRecordsByUID finds and returns token records belong to one user + * @param uid the uid of the owner to the token + * @return ArrayList of the token records + */ + public ArrayList<TokenRecord> tdbFindTokenRecordsByUID(String uid) + throws Exception { + ArrayList<TokenRecord> tokenRecords = new ArrayList<TokenRecord>(); + String filter = uid; + Iterator<TokenRecord> records = null; + records = tps.tokenDatabase.findRecords(filter).iterator(); + + while (records.hasNext()) { + TokenRecord tokenRecord = records.next(); + tokenRecords.add(tokenRecord); + } + + return tokenRecords; + } + + public void tdbHasActiveToken(String userid) + throws Exception { + if (userid == null) + throw new Exception("TPSTokendb.tdbhasActiveToken: uerid null"); + + ArrayList<TokenRecord> tokens = + tdbFindTokenRecordsByUID(userid); + boolean foundActive = false; + for (TokenRecord tokenRecord:tokens) { + if (tokenRecord.getStatus().equals("active")) { + foundActive = true; + } + } + if (!foundActive) { + throw new Exception("TPSTokendb.tdbhasActiveToken: active token not found"); + } + } + + public void tdbAddTokenEntry(TokenRecord tokenRecord, String status) + throws Exception { + tokenRecord.setStatus(status); + + tps.tokenDatabase.addRecord(tokenRecord.getId(), tokenRecord); + } + + public void tdbUpdateTokenEntry(TokenRecord tokenRecord) + throws Exception { + String id = tokenRecord.getId(); + TokenRecord existingTokenRecord; + try { + existingTokenRecord = tps.tokenDatabase.getRecord(id); + } catch (Exception e) { + CMS.debug("TPSTokendb.tdbUpdateTokenEntry: token entry not found; Adding"); + // add and exit + tdbAddTokenEntry(tokenRecord, "uninitialized"); + return; + } + // token found; modify + CMS.debug("TPSTokendb.tdbUpdateTokenEntry: token entry found; Modifying with status: "+ tokenRecord.getStatus()); + // don't change the create time of an existing token record; put it back + tokenRecord.setCreateTimestamp(existingTokenRecord.getCreateTimestamp()); + tps.tokenDatabase.updateRecord(id, tokenRecord); + } + + /* + * tdbAddCertificatesForCUID adds certificates issued for the token CUID + * @param cuid the cuid of the token + * @param certs an ArrayList of TPSCertRecord + */ + public void tdbAddCertificatesForCUID(String cuid, ArrayList<TPSCertRecord> certs) + throws TPSException { + boolean tokenExist = isTokenPresent(cuid); + if (!tokenExist){ + CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: token not found: "+ cuid); + throw new TPSException("TPSTokendb:tdbUpdateCertificates: token "+ cuid + " does not exist"); + } + + CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: found token "+ cuid); + CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: number of certs to update:"+ certs.size()); + try { + for (TPSCertRecord cert: certs) { + // cert.setOrigin(cuid); + + try { + tps.certDatabase.addRecord(cert.getId(), cert); + } catch (Exception e) { + + //If this is due to a dup, try to update the record. + tps.certDatabase.updateRecord(cert.getId(), cert); + } + } + } catch (Exception e) { + CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: "+ e); + // TODO: what if it throws in the middle of the cert list -- some cert records already updated? + throw new TPSException(e.getMessage()); + } + } + + /* + * tdbGetCertificatesByCUID finds and returns certificate records belong to a token cuid + * @param cuid the cuid of the token + * @return ArrayList of the cert records + */ + public ArrayList<TPSCertRecord> tdbGetCertificatesByCUID(String cuid) + throws TPSException { + if (cuid == null) + throw new TPSException("TPSTokendb.tdbGetCertificatesByCUID: cuid null"); + + ArrayList<TPSCertRecord> certRecords = new ArrayList<TPSCertRecord>(); + String filter = cuid; + Iterator<TPSCertRecord> records; + try { + records = tps.certDatabase.findRecords(filter).iterator(); + } catch (Exception e) { + CMS.debug("TPSTokendb.tdbGetCertificatesByCUID:" + e); + throw new TPSException(e.getMessage()); + } + + while (records.hasNext()) { + TPSCertRecord certRecord = records.next(); + certRecords.add(certRecord); + } + + return certRecords; + } + + public void revokeCertsByCUID(String cuid, String tokenReason) throws Exception { + String method = "TPStokendb.revokeCertsByCUID"; + CMS.debug(method + ": called"); + if (cuid == null) + throw new TPSException(method + ": cuid null"); + revokeCertsByCUID(true, cuid, tokenReason); + } + + public void unRevokeCertsByCUID(String cuid) throws Exception { + String method = "TPStokendb.unRevokeCertsByCUID"; + CMS.debug(method + ": called"); + if (cuid == null) + throw new TPSException(method + ": cuid null"); + revokeCertsByCUID(false, cuid, null /* null for unrevoke*/); + } + + /* + * revokeCertsByCUID + * @param isRevoke true if to revoke; false to unrevoke + * @param cuid cuid of token to revoke/unrevoke + * @param onHold true if revocation is to put onHold; false if to really revoke + */ + private void revokeCertsByCUID(boolean isRevoke, String cuid, String tokenReason) throws Exception { + String method = "TPSTokendb.revokeCertsByCUID"; + if (cuid == null) + throw new TPSException(method + ": cuid null"); + String auditMsg; + IConfigStore configStore = CMS.getConfigStore(); + ArrayList<TPSCertRecord> certRecords = tps.getTokendb().tdbGetCertificatesByCUID(cuid); + if (tokenReason != null) { + if (!tokenReason.equalsIgnoreCase("onHold") && + !tokenReason.equalsIgnoreCase("destroyed") && + !tokenReason.equalsIgnoreCase("keyCompromise")) { + auditMsg = "unknown tokenRecord lost reason:" + tokenReason; + CMS.debug(method + ":" + auditMsg); + throw new Exception(method + ":" + auditMsg); + } + + } + for (TPSCertRecord cert : certRecords) { + // get conn id + String config = "op.enroll." + cert.getType() + ".keyGen." + cert.getKeyType() + ".ca.conn"; + String connID = configStore.getString(config); + + RevocationReason revokeReason = RevocationReason.UNSPECIFIED; + + if (isRevoke) { + auditMsg = "called to revoke"; + CMS.debug(method + ":" + auditMsg); + boolean revokeCert = false; + + // get revoke or not + config = "op.enroll." + cert.getType() + ".keyGen." + cert.getKeyType() + + ".recovery." + tokenReason + ".revokeCert"; + //TODO: temporaryToken doesn't have all params; default to false if not found for now + revokeCert = configStore.getBoolean(config, false); // default to false + if (!revokeCert) { + auditMsg = "cert not to be revoked:" + cert.getSerialNumber(); + CMS.debug(method + ":" + auditMsg); + continue; + } + auditMsg = "cert to be revoked:" + cert.getSerialNumber(); + CMS.debug(method + ":" + auditMsg); + + // get revoke reason + config = "op.enroll." + cert.getType() + ".keyGen." + cert.getKeyType() + + ".recovery." + tokenReason + ".revokeCert.reason"; + int reasonInt = configStore.getInteger(config, 0); + revokeReason = RevocationReason.fromInt(reasonInt); + } else { // is unrevoke + auditMsg = "called to unrevoke"; + CMS.debug(method + ":" + auditMsg); + if (!cert.getStatus().equalsIgnoreCase("revoked_on_hold")) { + auditMsg = "cert record current status is not revoked_on_hold; cannot unrevoke"; + CMS.debug(method + ":" + auditMsg); + continue;// TODO: continue or bail? + } + } + + CARemoteRequestHandler caRH = null; + caRH = new CARemoteRequestHandler(connID); + String hexSerial = cert.getSerialNumber(); + if (hexSerial.length() >= 3 && hexSerial.startsWith("0x")) { + String serial = hexSerial.substring(2); // skip over the '0x' + BigInteger bInt = new BigInteger(serial, 16); + String serialStr = bInt.toString(); + CMS.debug(method + ": found cert hex serial: " + serial + + " dec serial:" + serialStr); + CARevokeCertResponse response = + caRH.revokeCertificate(isRevoke, serialStr, cert.getCertificate(), + revokeReason); + CMS.debug(method + ": response status =" + response.getStatus()); + } else { + auditMsg = "mulformed hex serial number :" + hexSerial; + CMS.debug(method + ": " + auditMsg); + throw new Exception(auditMsg); + } + + // update certificate status + if (isRevoke) { + if (revokeReason == RevocationReason.CERTIFICATE_HOLD) { + cert.setStatus("revoked_on_hold"); + } else { + cert.setStatus("revoked"); + } + } else { + cert.setStatus("active"); + } + tps.certDatabase.updateRecord(cert.getId(), cert); + auditMsg = "cert (un)revoked:" + cert.getSerialNumber(); + CMS.debug(method + ":" + auditMsg); + //TODO: tdbActivity + } + } + + public void tdbAddCertEntry(TPSCertRecord certRecord, String status) + throws Exception { + certRecord.setStatus(status); + + tps.certDatabase.addRecord(certRecord.getId(), certRecord); + } + + public void tdbUpdateCertEntry(TPSCertRecord certRecord) + throws Exception { + String method = "TPSTokendb.tdbUpdateCertEntry"; + String id = certRecord.getId(); + TPSCertRecord existingCertRecord; + try { + existingCertRecord = tps.certDatabase.getRecord(id); + } catch (Exception e) { + CMS.debug(method + ": token entry not found; Adding"); + // add and exit + tdbAddCertEntry(certRecord, certRecord.getStatus()); + return; + } + // cert found; modify + CMS.debug(method + ": cert entry found; Modifying with status: "+ certRecord.getStatus()); + // don't change the create time of an existing token record; put it back + certRecord.setCreateTime(existingCertRecord.getCreateTime()); + tps.certDatabase.updateRecord(id, certRecord); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/authentication/AuthUIParameter.java b/base/tps/src/org/dogtagpki/server/tps/authentication/AuthUIParameter.java new file mode 100644 index 000000000..16d57f948 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/authentication/AuthUIParameter.java @@ -0,0 +1,92 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.authentication; + +import java.util.HashMap; + +import com.netscape.certsrv.base.EBaseException; + +/* + * AuthUIParameters is a class for per locale parameter sets + * + * @author cfu + */ +public class AuthUIParameter { + + private String paramId; + /* + * auths.instance.<authInst>.ui.id.<param>.name.<locale>=<name> + * auths.instance.<authInst>.ui.id.<param>.description.<locale>=<description> + * e.g. + * auths.instance.ldap1.ui.id.PASSWORD.description.en=LDAP Password + * auths.instance.ldap1.ui.id.PASSWORD.name.en=LDAP Password + * auths.instance.ldap1.ui.id.UID.description.en=LDAP User ID + * auths.instance.ldap1.ui.id.UID.name.en=LDAP User ID + * + * for each id param <locale, name> + */ + private HashMap<String, String> uiParamIdName; + private HashMap<String, String> uiParamIdDescription; + + public AuthUIParameter(String id) + throws EBaseException { + paramId = id; + uiParamIdName = new HashMap<String, String>(); + uiParamIdDescription = new HashMap<String, String>(); + } + + public void setParamName(String locale, String name) { + uiParamIdName.put(locale, name); + } + + public String getParamName(String locale) { + return uiParamIdName.get(locale); + } + + public void setParamDescription(String locale, String desc) { + uiParamIdDescription.put(locale, desc); + } + + public String getParamDescription(String locale) { + return uiParamIdDescription.get(locale); + } + + public String toString(String locale) { + String name = getParamName(locale); + if (name == null) + name = getParamName("en"); + + String desc = getParamDescription(locale); + if (desc == null) + desc = getParamDescription("en"); + + String typeValue = "string"; + + if(paramId.equals("PASSWORD")){ + typeValue = "password"; + } + + String string = + "id=" + paramId + + "&name=" + name + + "&desc=" + desc + + "&type=" + typeValue + + "&option="; + return string; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/authentication/AuthenticationManager.java b/base/tps/src/org/dogtagpki/server/tps/authentication/AuthenticationManager.java new file mode 100644 index 000000000..e163bf6b1 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/authentication/AuthenticationManager.java @@ -0,0 +1,287 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.authentication; + +import java.util.Enumeration; +import java.util.Hashtable; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; + +/** + * AuthenticationManager is a class for management of authentication + * instances + * + * @author cfu + */ +public class AuthenticationManager +{ + private Hashtable<String, TPSAuthenticator> authInstances; + + public AuthenticationManager() { + } + + /* + * initAuthInstances initializes authentication manager instances + * + * configuration e.g. + * + * auths.instance.ldap1.ui.description.en=This authenticates user against the LDAP directory. + * auths.instance.ldap1.ui.title.en=LDAP Authentication + * auths.instance.ldap1.ui.id.PASSWORD.description.en=LDAP Password + * auths.instance.ldap1.ui.id.PASSWORD.name.en=LDAP Password + * auths.instance.ldap1.ui.id.PASSWORD.credMap.authCred=pwd + * auths.instance.ldap1.ui.id.PASSWORD.credMap.msgCred.extlogin=PASSWORD + * auths.instance.ldap1.ui.id.PASSWORD.credMap.msgCred.login=password + * auths.instance.ldap1.ui.id.UID.description.en=LDAP User ID + * auths.instance.ldap1.ui.id.UID.name.en=LDAP User ID + * auths.instance.ldap1.ui.id.UID.credMap.authCred=uid + * auths.instance.ldap1.ui.id.UID.credMap.msgCred.extlogin=UID + * auths.instance.ldap1.ui.id.UID.credMap.msgCred.login=screen_name + * auths.instance.ldap1.ui.retries=1 + * + * # the following are handled by the IAuthManager itself + * auths.instance.ldap1.dnpattern= + * auths.instance.ldap1.ldap.basedn=dc=idm,dc=lab,dc=bos,dc=redhat,dc=com + * auths.instance.ldap1.ldap.ldapauth.authtype=BasicAuth + * auths.instance.ldap1.ldap.ldapauth.bindDN= + * auths.instance.ldap1.ldap.ldapauth.bindPWPrompt=ldap1 + * auths.instance.ldap1.ldap.ldapauth.clientCertNickname= + * auths.instance.ldap1.ldap.ldapconn.host=vm-060.idm.lab.bos.redhat.com + * auths.instance.ldap1.ldap.ldapconn.port=389 + * auths.instance.ldap1.ldap.ldapconn.secureConn=False + * auths.instance.ldap1.ldap.ldapconn.version=3 + * auths.instance.ldap1.ldap.maxConns=15 + * auths.instance.ldap1.ldap.minConns=3 + * auths.instance.ldap1.ldapByteAttributes= + * auths.instance.ldap1.ldapStringAttributes=mail,cn,uid + * auths.instance.ldap1.pluginName=UidPwdDirAuth + */ + public void initAuthInstances() throws EBaseException { + CMS.debug("AuthenticationManager: initAuthInstances(): begins."); + IConfigStore conf = CMS.getConfigStore(); + IConfigStore authInstSubstore = conf.getSubStore("auths.instance"); + Enumeration<String> auth_enu = authInstSubstore.getSubStoreNames(); + authInstances = new Hashtable<String, TPSAuthenticator>(); + while (auth_enu.hasMoreElements()) { + String authInstID = auth_enu.nextElement(); + CMS.debug("AuthenticationManager: initAuthInstances(): initializing authentication instance " + authInstID); + IConfigStore authInstSub = + authInstSubstore.getSubStore(authInstID); + TPSAuthenticator authInst = + createAuthentication(authInstSub, authInstID); + authInstances.put(authInstID, authInst); + CMS.debug("AuthenticationManager: initAuthInstances(): authentication instance " + + authInstID + + " initialized."); + } + CMS.debug("AuthenticationManager: initAuthInstances(): ends."); + } + + /* + * createAuthentication creates and returns an Authenticaiton + * + * @param conf config store of the authentication instance + * @return Authentication the authentication instance + */ + private TPSAuthenticator createAuthentication(IConfigStore conf, String authInstID) + throws EBaseException { + + CMS.debug("AuthenticationManager: createAuthentication(): begins for " + + authInstID); + + if (conf == null || conf.size() <= 0) { + CMS.debug("AuthenticationManager: createAuthentication(): conf null or empty."); + throw new EBaseException("called with null config store"); + } + + TPSAuthenticator auth = new TPSAuthenticator(authInstID); + + IConfigStore uiSub = conf.getSubStore("ui"); + if (uiSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + conf.getName() + ".ui" + " null or empty."); + throw new EBaseException("config " + conf.getName() + ".ui" + " not found"); + } + + // init ui title + IConfigStore uiTitleSub = uiSub.getSubStore("title"); + if (uiTitleSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + uiSub.getName() + ".title" + " null or empty."); + throw new EBaseException("config " + uiSub.getName() + ".title" + " not found"); + } + + Enumeration<String> uiTitle_enu = uiTitleSub.getPropertyNames(); + + while (uiTitle_enu.hasMoreElements()) { + String locale = uiTitle_enu.nextElement(); + String title = uiTitleSub.getString(locale); + if (title.isEmpty()) { + CMS.debug("AuthenticationManager: createAuthentication(): title for locale " + + locale + " not found"); + continue; + } + auth.setUiTitle(locale, title); + CMS.debug("AuthenticationManager: createAuthentication(): added title=" + + title + ", locale= " + locale); + } + + // init ui description + IConfigStore uiDescSub = uiSub.getSubStore("description"); + if (uiDescSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + uiSub.getName() + ".description" + " null or empty."); + throw new EBaseException("config " + uiSub.getName() + ".description" + " not found"); + } + Enumeration<String> uiDesc_enu = uiDescSub.getPropertyNames(); + + while (uiDesc_enu.hasMoreElements()) { + String locale = uiDesc_enu.nextElement(); + String description = uiDescSub.getString(locale); + if (description.isEmpty()) { + CMS.debug("AuthenticationManager: createAuthentication(): description for locale " + + locale + " not found"); + continue; + } + auth.setUiDescription(locale, description); + CMS.debug("AuthenticationManager: createAuthentication(): added description=" + + description + ", locale= " + locale); + } + + // init ui parameters + IConfigStore uiParamSub = uiSub.getSubStore("id"); + if (uiParamSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + uiSub.getName() + ".id" + " null or empty."); + throw new EBaseException("config " + uiSub.getName() + ".id" + " not found"); + } + Enumeration<String> uiParam_enu = uiParamSub.getSubStoreNames(); + while (uiParam_enu.hasMoreElements()) { + String id = uiParam_enu.nextElement(); + CMS.debug("AuthenticationManager: createAuthentication(): id param=" + + id); + IConfigStore idNameSub = uiParamSub.getSubStore(id + ".name"); + if (idNameSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + uiParamSub.getName() + ".name" + " null or empty."); + continue; + } + + AuthUIParameter up = new AuthUIParameter(id); + Enumeration<String> idName_enu = idNameSub.getPropertyNames(); + while (idName_enu.hasMoreElements()) { + String locale = idName_enu.nextElement(); + String name = idNameSub.getString(locale); + if (name.isEmpty()) { + CMS.debug("AuthenticationManager: createAuthentication(): name for locale " + + locale + " not found"); + continue; + } + CMS.debug("AuthenticationManager: createAuthentication(): name =" + + name + " for locale " + locale); + up.setParamName(locale, name); + } + + IConfigStore idDescSub = uiParamSub.getSubStore(id + ".description"); + if (idDescSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + uiParamSub.getName() + ".description" + " null or empty."); + continue; + } + Enumeration<String> idDesc_enu = idDescSub.getPropertyNames(); + while (idDesc_enu.hasMoreElements()) { + String locale = idDesc_enu.nextElement(); + String desc = idDescSub.getString(locale); + if (desc.isEmpty()) { + CMS.debug("AuthenticationManager: createAuthentication(): description for locale " + + locale + " not found"); + continue; + } + CMS.debug("AuthenticationManager: createAuthentication(): desc =" + + desc); + up.setParamDescription(locale, desc); + } + + auth.setUiParam(id, up); + CMS.debug("AuthenticationManager: createAuthentication(): added param=" + + id); + + // map the auth mgr required cred to cred name in request message + IConfigStore credMapSub = uiParamSub.getSubStore(id + ".credMap"); + if (credMapSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + uiParamSub.getName() + ".credMap" + " null or empty."); + continue; + } + String authCred = credMapSub.getString("authCred"); + if (authCred.isEmpty()) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + credMapSub.getName() + ".authCred" + " null or empty."); + continue; + } + + IConfigStore msgCredSub = credMapSub.getSubStore("msgCred"); + if (msgCredSub == null) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + uiParamSub.getName() + ".msgCred" + " null or empty."); + continue; + } + + String msgCred_login = msgCredSub.getString("login"); + if (msgCred_login.isEmpty()) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + msgCredSub.getName() + ".login" + " null or empty."); + continue; + } + auth.setCredMap(authCred, msgCred_login, + false /* not extendedLogin*/); + CMS.debug("AuthenticationManager: createAuthentication(): added cred map_login=" + + authCred + ":" + msgCred_login); + + String msgCred_extlogin = msgCredSub.getString("extlogin"); + if (msgCred_extlogin.isEmpty()) { + CMS.debug("AuthenticationManager: createAuthentication(): conf " + + msgCredSub.getName() + ".extlogin" + " null or empty."); + continue; + } + + auth.setCredMap(authCred, msgCred_extlogin, + true /* extendedLogin*/); + CMS.debug("AuthenticationManager: createAuthentication(): added cred map_extlogin=" + + authCred + ":" + msgCred_extlogin); + + } + + Integer retries = uiSub.getInteger("retries", 1); + auth.setNumOfRetries(retries.intValue()); + + CMS.debug("AuthenticationManager: createAuthentication(): completed for " + + authInstID); + return auth; + } + + /* + * gets an established Authentication instance + */ + public TPSAuthenticator getAuthInstance(String id) { + return authInstances.get(id); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/authentication/TPSAuthenticator.java b/base/tps/src/org/dogtagpki/server/tps/authentication/TPSAuthenticator.java new file mode 100644 index 000000000..8d84cf5f6 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/authentication/TPSAuthenticator.java @@ -0,0 +1,148 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.authentication; + +import java.util.HashMap; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthManager; +import com.netscape.certsrv.authentication.IAuthSubsystem; +import com.netscape.certsrv.base.EBaseException; + +/** + * Authentication is a class for an authentication instance + * + * @author cfu + */ +public class TPSAuthenticator { + private String id; + private IAuthManager authManager; + + /* + * for auths instance ui <locale, value> + * e.g. + * auths.instance.ldap1.ui.description.en= + * This authenticates user against the LDAP directory. + * auths.instance.ldap1.ui.title.en=LDAP Authentication + */ + private HashMap<String, String> uiTitle; + private HashMap<String, String> uiDescription; + + private HashMap<String, AuthUIParameter> uiParameters; + /* + * credMap is for authentication manager required + * credential names (authCred) mapping to the + * client message credentail names (msgCred) + * e.g. + * auths.instance.ldap1.ui.id.UID.credMap.authCred=uid + * auths.instance.ldap1.ui.id.UID.credMap.msgCred=screen_name + * auths.instance.ldap1.ui.id.PASSWORD.credMap.authCred=pwd + * auths.instance.ldap1.ui.id.PASSWORD.credMap.msgCred=password + */ + private HashMap<String, String> credMap_login; + private HashMap<String, String> credMap_extlogin; + + // retries if the user entered the wrong password/securid + private int maxLoginRetries = 1; + + private String authCredName; + + /* + * Authentication constructor + * @param authId authentication instance id + */ + public TPSAuthenticator(String authId) + throws EBaseException { + id = authId; + // retrieves and set authentication manager + IAuthSubsystem authSub = + (IAuthSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTH); + authManager = authSub.getAuthManager(authId); + uiTitle = new HashMap<String, String>(); + uiDescription = new HashMap<String, String>(); + uiParameters = new HashMap<String, AuthUIParameter>(); + credMap_login = new HashMap<String, String>(); + credMap_extlogin = new HashMap<String, String>(); + } + + public String getID() { + return id; + } + + public IAuthManager getAuthManager() { + return authManager; + } + + public void setUiTitle(String locale, String title) { + uiTitle.put(locale, title); + } + + public String getUiTitle(String locale) { + return uiTitle.get(locale); + } + + public void setUiDescription(String locale, String desc) { + uiDescription.put(locale, desc); + } + + public String getUiDescription(String locale) { + return uiDescription.get(locale); + } + + public void setUiParam(String id, AuthUIParameter up) { + uiParameters.put(id, up); + } + + public AuthUIParameter getUiParam(String id) { + return uiParameters.get(id); + } + + public HashMap<String, AuthUIParameter> getUiParamSet() { + return uiParameters; + } + + public void setCredMap(String authCred, String msgCred, boolean extLogin) { + if (extLogin) + credMap_extlogin.put(authCred, msgCred); + else + credMap_login.put(authCred, msgCred); + } + + public String getCredMap(String authCred, boolean extLogin) { + if (extLogin) + return credMap_extlogin.get(authCred); + else + return credMap_login.get(authCred); + } + + public int getNumOfRetries() { + return maxLoginRetries; + } + + public void setNumOfRetries(int num) { + maxLoginRetries = num; + } + + public String getAuthCredName() { + return authCredName; + } + + public void setAuthCredName(String authCredName) { + this.authCredName = authCredName; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java b/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java new file mode 100644 index 000000000..7c3a01ba7 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java @@ -0,0 +1,1045 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.channel; + +import java.io.IOException; + +import org.dogtagpki.server.tps.processor.TPSProcessor; +import org.dogtagpki.tps.apdu.APDU; +import org.dogtagpki.tps.apdu.APDUResponse; +import org.dogtagpki.tps.apdu.CreateObjectAPDU; +import org.dogtagpki.tps.apdu.CreatePinAPDU; +import org.dogtagpki.tps.apdu.DeleteFileAPDU; +import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDU; +import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDU.SecurityLevel; +import org.dogtagpki.tps.apdu.GenerateKeyAPDU; +import org.dogtagpki.tps.apdu.GenerateKeyECCAPDU; +import org.dogtagpki.tps.apdu.ImportKeyEncAPDU; +import org.dogtagpki.tps.apdu.InstallAppletAPDU; +import org.dogtagpki.tps.apdu.InstallLoadAPDU; +import org.dogtagpki.tps.apdu.LifecycleAPDU; +import org.dogtagpki.tps.apdu.LoadFileAPDU; +import org.dogtagpki.tps.apdu.PutKeyAPDU; +import org.dogtagpki.tps.apdu.ReadObjectAPDU; +import org.dogtagpki.tps.apdu.SetIssuerInfoAPDU; +import org.dogtagpki.tps.apdu.SetPinAPDU; +import org.dogtagpki.tps.apdu.WriteObjectAPDU; +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.main.Util; +import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus; +import org.mozilla.jss.pkcs11.PK11SymKey; + +import sun.security.pkcs11.wrapper.PKCS11Constants; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; + +public class SecureChannel { + + // Have not written all code to use all of these as of yet. + + private TPSProcessor processor; + private PK11SymKey sessionKey; + private PK11SymKey encSessionKey; + private TPSBuffer drmDesKey; + private TPSBuffer kekDesKey; + private TPSBuffer keyCheck; + private TPSBuffer keyDiversificationData; + private TPSBuffer cardChallenge; + private TPSBuffer cardCryptogram; + private TPSBuffer hostChallenge; + private TPSBuffer hostCryptogram; + private TPSBuffer icv; + private TPSBuffer keyInfoData; + private SecurityLevel secLevel; + + public enum TokenKeyType { + KEY_TYPE_ENCRYPTION, + KEY_TYPE_SIGNING, + KEY_TYPE_SIGNING_AND_ENCRYPTION + } + + public SecureChannel(TPSProcessor processor, PK11SymKey sessionKey, PK11SymKey encSessionKey, TPSBuffer drmDesKey, + TPSBuffer kekDesKey, TPSBuffer keyCheck, TPSBuffer keyDiversificationData, TPSBuffer cardChallenge, + TPSBuffer cardCryptogram, TPSBuffer hostChallenge, TPSBuffer hostCryptogram, TPSBuffer keyInfoData) + throws TPSException { + + if (processor == null || sessionKey == null | encSessionKey == null || keyDiversificationData == null + || cardChallenge == null || cardCryptogram == null || hostChallenge == null || hostCryptogram == null + || keyInfoData == null) { + throw new TPSException("SecureChannel.SecureChannel: Invalid data in constructor!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + this.processor = processor; + this.sessionKey = sessionKey; + this.encSessionKey = encSessionKey; + this.drmDesKey = drmDesKey; + this.setKekDesKey(kekDesKey); + this.keyCheck = keyCheck; + this.keyDiversificationData = keyDiversificationData; + this.cardChallenge = cardChallenge; + this.cardCryptogram = cardCryptogram; + this.hostChallenge = hostChallenge; + this.hostCryptogram = hostCryptogram; + this.icv = new TPSBuffer(8); + this.keyInfoData = keyInfoData; + + this.secLevel = SecurityLevel.SECURE_MSG_MAC_ENC; + //ToDo: Write method that reads this from the config + + } + + public static void main(String[] args) { + } + + public void appendPKCS11Attribute(TPSBuffer buffer, long type, TPSBuffer attribute) { + + buffer.addLong4Bytes(type); + + buffer.addInt2Bytes(attribute.size()); + buffer.add(attribute); + } + + public void appendKeyCapabilities(TPSBuffer buffer, String keyTypePrefix, String keyType) throws TPSException { + + if (buffer == null || keyTypePrefix == null || keyType == null) { + throw new TPSException("SecureChannel.appdndKeyCabalities: Invalid input datat.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + IConfigStore configStore = CMS.getConfigStore(); + + final String keyCapabilities = "keyCapabilities"; + + try { + + boolean value = false; + String configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "encrypt"; + + value = configStore.getBoolean(configName); + + TPSBuffer attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_ENCRYPT, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "sign"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_SIGN, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "signRecover"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_SIGN_RECOVER, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "decrypt"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_DECRYPT, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "derive"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_DERIVE, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "unwrap"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_UNWRAP, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "wrap"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_WRAP, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "verifyRecover"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_VERIFY_RECOVER, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "verify"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_VERIFY, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "sensitive"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_SENSITIVE, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "private"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_PRIVATE, attr); + + configName = keyTypePrefix + "." + keyType + "." + keyCapabilities + "." + "token"; + value = configStore.getBoolean(configName); + attr = new TPSBuffer(Util.bool2Byte(value)); + appendPKCS11Attribute(buffer, PKCS11Constants.CKA_TOKEN, attr); + + CMS.debug("SecureChannel.appendKeyCapabilities: returning: " + buffer.toHexString()); + + } catch (EBaseException e) { + throw new TPSException("SecureChannel.appentKeyCapabilities. Can't obtain config value!", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + } + + public void externalAuthenticate() throws TPSException, IOException { + + CMS.debug("SecureChannel.externalAuthenticate: entering."); + + ExternalAuthenticateAPDU externalAuth = new ExternalAuthenticateAPDU(hostCryptogram, + secLevel); + + computeAPDUMac(externalAuth); + + APDUResponse response = processor.handleAPDURequest(externalAuth); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.eternalAuthenticate. Failed to external authenticate to token.", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + CMS.debug("SecureChannel.externalAuthenticate: Successfully completed, exiting ..."); + + } + + //This method computes the mac AND encryption if needed. + private void computeAPDU(APDU apdu) throws TPSException { + + CMS.debug("SecureChannel.computeAPDU: entering.."); + + if (apdu == null) { + throw new TPSException("SecureChannel.computeAPDU: bad input apdu!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + computeAPDUMac(apdu); + + if (secLevel == SecurityLevel.SECURE_MSG_MAC_ENC) { + try { + // CMS.debug("SecureChannel.computeAPDU: Before encryption data value: " + apdu.getData().toHexString()); + apdu.secureMessage(encSessionKey); + // CMS.debug("SecureChannel.computeAPDU: After encryption data value: " + apdu.getData().toHexString()); + } catch (EBaseException e) { + throw new TPSException("SecureChannel.computeAPDU: Can't encrypt outgoing data! " + e); + } + + CMS.debug("SecureChannel.computeAPDU: Successfully encrypted apdu data."); + } + } + + // This method computes MAC only. + private void computeAPDUMac(APDU apdu) throws TPSException { + TPSBuffer newMac = null; + TPSBuffer data = null; + + if (apdu == null) { + throw new TPSException("SecureChannel.computeAPDUMac: bad input apdu!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + data = apdu.getDataToMAC(); + + CMS.debug("SecureChannel.computeAPDUMac: data To MAC: " + data.toHexString()); + + try { + newMac = Util.computeMAC(sessionKey, data, icv); + } catch (EBaseException e) { + CMS.debug("SecureChannel.compuatAPDUMac: Can't compute mac. " + e); + throw new TPSException("SecureChannel.compuatAPDUMac: Can't compute mac.", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + CMS.debug("SecureChannel.computeAPDUMac: computed MAC: " + newMac.toHexString()); + + apdu.setMAC(newMac); + + icv.set(newMac); + } + + public void deleteFileX(TPSBuffer aid) throws TPSException, IOException { + CMS.debug("SecureChannel.deleteFileX: entering..."); + if (aid == null) { + throw new TPSException("SecureChannel.deleteFileX: no input aid!"); + } + + DeleteFileAPDU deleteFile = new DeleteFileAPDU(aid); + + computeAPDU(deleteFile); + + processor.handleAPDURequest(deleteFile); + + } + + // Begin process of loading applet onto token. + public void installLoad(TPSBuffer packageAID, TPSBuffer sdAID, int fileLength) throws TPSException, IOException { + + CMS.debug("SecureChannel.installLoad: entering ..."); + + if (packageAID == null || sdAID == null || fileLength <= 0) { + throw new TPSException("SecureChannel.insallLoad bad input parameters!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + InstallLoadAPDU install = new InstallLoadAPDU(packageAID, sdAID, fileLength); + + CMS.debug("SecureChannel.installLoad: Pre computed apdu: " + install.getEncoding().toHexString()); + + computeAPDU(install); + + APDUResponse response = processor.handleAPDURequest(install); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.installLoad. Failed to perform installLoad operation.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + } + + //Actually load applet file onto the token. + + public void loadFile(TPSBuffer programFile, int blockSize, int startProgress, int endProgress) throws TPSException, + IOException { + CMS.debug("SecureChannel.loadFile entering..."); + + if (programFile == null || blockSize <= 0) { + throw new TPSException("ScureChannel.loadFile. Bad input data.", TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + TPSBuffer length = null; + + TPSBuffer tag = new TPSBuffer(1, (byte) 0xC4); + + int progSize = programFile.size(); + + if (progSize < 128) { + length = new TPSBuffer(1, (byte) progSize); + } else if (progSize <= 255) { + length = new TPSBuffer(1, (byte) 0x81); + length.add((byte) progSize); + } else { + length = new TPSBuffer(1, (byte) 0x82); + length.add((byte) ((progSize >> 8) & 0xff)); + length.add((byte) (progSize & 0xff)); + + } + + TPSBuffer tbsProgramFile = new TPSBuffer(tag); + tbsProgramFile.add(length); + tbsProgramFile.add(programFile); + + int totalLen = tbsProgramFile.size(); + int sizeToSend = totalLen; + + int finalBlockSize = 0; + float progressBlockSize = 0; + + if (secLevel == SecurityLevel.SECURE_MSG_MAC_ENC) { + // need leave room for possible encryption padding + finalBlockSize = blockSize - 0x10; + } else { + finalBlockSize = blockSize - 8; + } + + //rough number is good enough + int numLoops = sizeToSend / blockSize; + + if (numLoops == 0) { // We have bogus data here. Good bye. + throw new TPSException("SecureChannel.loadFile. Bad input data.", TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + progressBlockSize = (float) (endProgress - startProgress) / numLoops; + + int count = 0; + byte refControl = 0x00; + + do { + if (sizeToSend < finalBlockSize) { + finalBlockSize = sizeToSend; + refControl = (byte) 0x80; + + } + + CMS.debug("SecureChannel.loadFile: taking data substring from: " + (totalLen - sizeToSend) + " size: " + + finalBlockSize + " to: " + ((totalLen - sizeToSend) + finalBlockSize)); + + TPSBuffer piece = tbsProgramFile.substr(totalLen - sizeToSend, finalBlockSize); + + CMS.debug("SecureChannel.loadFile: attempting to send piece: " + sizeToSend); + + loadFileSegment(refControl, count, piece); + + if (processor.requiresStatusUpdate()) { + processor.statusUpdate(startProgress + (int) (count * progressBlockSize), "PROGRESS_APPLET_BLOCK"); + } + + sizeToSend -= finalBlockSize; + + count++; + + } while (sizeToSend > 0); + + } + + //Load one piece of the applet file onto the token. + private void loadFileSegment(byte refControl, int count, TPSBuffer piece) throws TPSException, IOException { + + if (piece == null || count < 0) { + throw new TPSException("SecureChannel.loadFileSegment: invalid input data.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + LoadFileAPDU loadFile = new LoadFileAPDU(refControl, (byte) count, piece); + + computeAPDU(loadFile); + + APDUResponse response = processor.handleAPDURequest(loadFile); + + if (!response.checkResult()) { + throw new TPSException( + "SecureChannel.loadFileSegment. Failed to perform loadFileSegmentInstallLoad operation.", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + } + + // Kick off the applet loading process. + public void installApplet(TPSBuffer netkeyPAIDBuff, TPSBuffer netkeyAIDBuff, byte appPrivileges, + int channelInstanceSize, + int channelAppletMemSize) throws TPSException, IOException { + + CMS.debug("SecureChannel.installApplet: entering..."); + + // Would be tough to put a check on the various input sizes, let the applet + // decide if the values are appropriate for channelInstanceSize and channelAppletMemSize + + if (netkeyPAIDBuff == null || netkeyAIDBuff == null || channelInstanceSize < 0 || channelAppletMemSize < 0) { + throw new TPSException("SecureChannel.installApplet. Invalid input parameters!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + + } + + InstallAppletAPDU install = new InstallAppletAPDU(netkeyPAIDBuff, netkeyAIDBuff, appPrivileges, + channelInstanceSize, channelAppletMemSize); + + computeAPDU(install); + + APDUResponse response = processor.handleAPDURequest(install); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.installApplett. Failed installApplet operation.", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + } + + // Burn the phone home URL into the token. + public void setIssuerInfo(TPSBuffer issuerInfoBuff) throws TPSException, IOException { + CMS.debug("SecureChannel.setIssuerInfo entering..."); + + final int finalIssuerLength = 224; + final int approxMinUrlSize = 5; + + if (issuerInfoBuff == null || issuerInfoBuff.size() < approxMinUrlSize) { + throw new TPSException("SecureChannel.setIssuerInfo: Invalid input data.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + int issuerLen = issuerInfoBuff.size(); + + int paddingLen = finalIssuerLength - issuerLen; + + TPSBuffer paddingBuff = new TPSBuffer(paddingLen, (byte) 0x0); + + TPSBuffer finalIssuerBuff = new TPSBuffer(issuerInfoBuff); + + finalIssuerBuff.add(paddingBuff); + + CMS.debug("finalIssuerBuff len: " + finalIssuerBuff.size() + " issuerInfo: " + finalIssuerBuff.toString()); + SetIssuerInfoAPDU setIssuer = new SetIssuerInfoAPDU((byte) 0x0, (byte) 0x0, finalIssuerBuff); + + computeAPDU(setIssuer); + + APDUResponse response = processor.handleAPDURequest(setIssuer); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.setIssuerInfo. Failed to set issuer info!", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + CMS.debug("SecureChannel.setIssuerInfo: leaving..."); + + } + + public TPSBuffer getKeyDiversificationData() { + return keyDiversificationData; + } + + public TPSBuffer getCardChallenge() { + return cardChallenge; + } + + public TPSBuffer getHostChallenge() { + return hostChallenge; + } + + public TPSBuffer getHostCryptogram() { + return hostCryptogram; + } + + public TPSBuffer getCardCryptogram() { + return cardCryptogram; + } + + public TPSBuffer getKeyInfoData() { + return keyInfoData; + } + + public void writeObject(TPSBuffer objectID, TPSBuffer objectData) throws TPSException, IOException { + CMS.debug("SecureChannel.writeObject: entering ..."); + + if (objectID == null || objectData == null) { + throw new TPSException("SecureChannel.writeObject: invalid input data."); + } + + final int MAX_WRITE_SIZE = 0xd0; + + int offset = 0; + int toSend = objectData.size(); + int blockSize = 0; + + boolean moreToGo = true; + do { + + if (toSend > MAX_WRITE_SIZE) { + blockSize = MAX_WRITE_SIZE; + } else { + blockSize = toSend; + } + + TPSBuffer blockToSend = objectData.substr(offset, blockSize); + + WriteObjectAPDU write = new WriteObjectAPDU(objectID.toBytesArray(), offset, blockToSend); + + computeAPDU(write); + + APDUResponse response = processor.handleAPDURequest(write); + + if (!response.checkResult()) { + CMS.debug("SecureChannel.writeObject: bad apdu return!"); + //Throw this return code because this happens during enrollment and we don't have + // a more specific error code. + throw new TPSException("SecureChannel.writeObject. Failed in middle of writeObject.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + offset += blockSize; + toSend -= blockSize; + + if (toSend <= 0) { + moreToGo = false; + } + + } while (moreToGo); + + } + + public TPSBuffer readObject(TPSBuffer objectID, int offset, int len) throws TPSException, IOException { + + CMS.debug("SecureChannel.readObject: entering ..."); + + if (objectID == null || len == 0) { + throw new TPSException("SecureChannel.readObject: invalid input data.", + TPSStatus.STATUS_ERROR_READ_OBJECT_PDU); + } + + final int MAX_READ_BUFFER_SIZE = 0xd0; + + ReadObjectAPDU read = null; + TPSBuffer result = new TPSBuffer(); + + int cur_read = 0; + int cur_offset = 0; + int sum = 0; + + if (len > MAX_READ_BUFFER_SIZE) { + cur_offset = offset; + cur_read = MAX_READ_BUFFER_SIZE; + } else { + cur_offset = offset; + cur_read = len; + } + + while (sum < len) { + + read = new ReadObjectAPDU(objectID.toBytesArray(), cur_offset, cur_read); + computeAPDU(read); + + APDUResponse response = processor.handleAPDURequest(read); + + if (!response.checkResult()) { + CMS.debug("SecureChannel.readObject: bad apdu return!"); + throw new TPSException("SecureChannel.installApplett. Failed in middle of readObject.", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + TPSBuffer resp = response.getResultDataNoCode(); + + result.add(resp); + + sum += resp.size(); + cur_offset += resp.size(); + + if ((len - sum) < MAX_READ_BUFFER_SIZE) { + cur_read = len - sum; + } else { + cur_read = MAX_READ_BUFFER_SIZE; + } + + } + + return result; + } + + public void createObject(TPSBuffer objectID, TPSBuffer permissions, TPSBuffer object) throws TPSException, + IOException { + + CMS.debug("SecureChannel.createObject: with full object. entering..."); + + if (objectID == null || permissions == null || object == null) { + throw new TPSException("SecureChannel.createObject, with full object. Bad input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + + } + + createObject(objectID, permissions, object.size()); + + writeObject(objectID, object); + + } + + public void createCertificate(TPSBuffer objectID, TPSBuffer cert) throws TPSException, IOException { + CMS.debug("SecureChannel.createCertificate: entering..."); + + if (objectID == null || cert == null) { + throw new TPSException("SecureChannel.createCertificate. Bad input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + byte[] perms = { (byte) 0xff, (byte) 0xff, 0x40, 0x00, 0x40, 0x00 }; + + TPSBuffer permissions = new TPSBuffer(perms); + + createObject(objectID, permissions, cert); + + } + + public void createPKCS11CertAttrs(TokenKeyType keyType, String id, String label, TPSBuffer keyid) + throws TPSException, IOException { + + TPSBuffer buffer = createPKCS11CertAttrsBuffer(keyType, id, label, keyid); + + byte[] perms = { (byte) 0xff, (byte) 0xff, 0x40, 0x00, 0x40, 0x00 }; + + TPSBuffer permissions = new TPSBuffer(perms); + + createObject(new TPSBuffer(id), permissions, buffer); + + } + + public TPSBuffer createPKCS11PriKeyAttrsBuffer(String id, String label, TPSBuffer keyid, + TPSBuffer modulus, String keyTypePrefix) throws TPSException { + + TPSBuffer result = new TPSBuffer(); + + CMS.debug("SecureChannel.createPKCS11PriKeyAttrsBuffer: entering..."); + + if (id == null || label == null || keyid == null || modulus == null || keyTypePrefix == null) { + throw new TPSException("SecureChannel.craetePKCS11PriKeyAttrsBuffer: invalid input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("SecureChannel.createPKCS11PriKeyAttrsBuffer: id: " + id + " label: " + label + " keyid: " + + keyid.toHexString()); + + byte keytype[] = { 0, 0, 0, 0 }; + byte p11class[] = { 3, 0, 0, 0 }; + + appendPKCS11Attribute(result, PKCS11Constants.CKA_MODULUS, modulus); + appendPKCS11Attribute(result, PKCS11Constants.CKA_KEY_TYPE, new TPSBuffer(keytype)); + appendPKCS11Attribute(result, PKCS11Constants.CKA_CLASS, new TPSBuffer(p11class)); + appendPKCS11Attribute(result, PKCS11Constants.CKA_ID, keyid); + appendKeyCapabilities(result, keyTypePrefix, "private"); + + finalizeObjectBuffer(result, id); + + CMS.debug("SecureChannel.createPKCS11PriKeyAttrsBuffer: returing: " + result.toHexString()); + + return result; + + } + + public void createPKCS11PriKeyAttrs(String id, String label, TPSBuffer keyid, + TPSBuffer modulus, String keyTypePrefix) throws TPSException, IOException { + + CMS.debug("SecureChannel.createPKCS11PriKeyAttrsBuffer: entering..."); + + if (id == null || label == null || keyid == null || modulus == null || keyTypePrefix == null) { + throw new TPSException("SecureChannel.craetePKCS11PriKeyAttrsBuffer: invalid input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + TPSBuffer buffer = createPKCS11PriKeyAttrsBuffer(id, label, keyid, modulus, keyTypePrefix); + + byte[] perms = { (byte) 0xff, (byte) 0xff, 0x40, 0x00, 0x40, 0x00 }; + + TPSBuffer permissions = new TPSBuffer(perms); + + createObject(new TPSBuffer(id), permissions, buffer); + } + + public TPSBuffer createPKCS11PublicKeyAttrsBuffer(String id, String label, TPSBuffer keyid, + TPSBuffer modulus, TPSBuffer exponent, String keyTypePrefix) throws TPSException { + + TPSBuffer result = new TPSBuffer(); + CMS.debug("SecureChannel.createPKCS11PublicKeyAttrsBuffer: entering..."); + + if (id == null || label == null || keyid == null || modulus == null || exponent == null + || keyTypePrefix == null) { + throw new TPSException("SecureChannel.craetePKCS11PublicKeyAttrsBuffer: invalid input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + byte p11class[] = { 2, 0, 0, 0 }; + + appendPKCS11Attribute(result, PKCS11Constants.CKA_PUBLIC_EXPONENT, exponent); + appendPKCS11Attribute(result, PKCS11Constants.CKA_MODULUS, modulus); + appendPKCS11Attribute(result, PKCS11Constants.CKA_ID, keyid); + appendPKCS11Attribute(result, PKCS11Constants.CKA_CLASS, new TPSBuffer(p11class)); + appendKeyCapabilities(result, keyTypePrefix, "public"); + + finalizeObjectBuffer(result, id); + + CMS.debug("SecureChannel.createPKCS11PublicKeyAttrsBuffer: returing: " + result.toHexString()); + + return result; + + } + + public void createPKCS11PublicKeyAttrs(String id, String label, TPSBuffer keyid, + TPSBuffer modulus, TPSBuffer exponent, String keyTypePrefix) throws TPSException, IOException { + + CMS.debug("SecureChannel.createPKCS11PublicKeyAttrsBuffer: entering..."); + + if (id == null || label == null || keyid == null || modulus == null || exponent == null + || keyTypePrefix == null) { + throw new TPSException("SecureChannel.craetePKCS11PriKeyAttrsBuffer: invalid input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + TPSBuffer buffer = createPKCS11PriKeyAttrsBuffer(id, label, keyid, modulus, keyTypePrefix); + + byte[] perms = { (byte) 0xff, (byte) 0xff, 0x40, 0x00, 0x40, 0x00 }; + + TPSBuffer permissions = new TPSBuffer(perms); + + createObject(new TPSBuffer(id), permissions, buffer); + + } + + public void finalizeObjectBuffer(TPSBuffer buffer, String id) { + + TPSBuffer header = new TPSBuffer(); + + header.add((byte) 0); + header.add((byte) id.charAt(0)); + header.add((byte) id.charAt(1)); + header.add((byte) 0); + header.add((byte) 0); + + header.add((byte) ((buffer.size()) / 256)); + header.add((byte) ((buffer.size()) % 256)); + + buffer.prepend(header); + + } + + public TPSBuffer createPKCS11CertAttrsBuffer(TokenKeyType keyType, String id, String label, TPSBuffer keyid) + throws TPSException { + + CMS.debug("SecureChannel.createPKCS11CertAttrsBuffer: entering... id: " + id); + if (keyType == null || id == null || label == null || keyid == null) { + throw new TPSException("SecureChannel.createPKCS11CertAttrsBuffer. Bad input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + + } + + CMS.debug("SecureChannel.createPKCS11CertAttrsBuffer: ... id: " + id + " label: " + label + " keyid: " + + keyid.toHexString()); + + byte[] type = { 0x0, 0x0, 0x0, 0x0 }; + byte[] p11class = { 0x1, 0x0, 0x0, 0x0 }; + byte[] tokenFlag = { 0x1 }; + + TPSBuffer result = new TPSBuffer(); + + CMS.debug("SecureChannel.createPKCS11CertAttrsBuffer: label: " + label + " label bytes: " + + (new TPSBuffer(label)).toHexString()); + + appendPKCS11Attribute(result, PKCS11Constants.CKA_LABEL, new TPSBuffer(label.getBytes())); + appendPKCS11Attribute(result, PKCS11Constants.CKA_ID, keyid); + appendPKCS11Attribute(result, PKCS11Constants.CKA_CERTIFICATE_TYPE, new TPSBuffer(type)); + appendPKCS11Attribute(result, PKCS11Constants.CKA_CLASS, new TPSBuffer(p11class)); + appendPKCS11Attribute(result, PKCS11Constants.CKA_TOKEN, new TPSBuffer(tokenFlag)); + + finalizeObjectBuffer(result, id); + + CMS.debug("SecureChannel.createPKCS11CertAttrsBuffer: returing: " + result.toHexString()); + + return result; + + } + + public void createObject(TPSBuffer objectID, TPSBuffer permissions, int len) throws TPSException, IOException { + + CMS.debug("SecureChannel.createObject: entering..."); + if (objectID == null || permissions == null || len <= 0) { + throw new TPSException("SecureChannel.createObject. Bad input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CreateObjectAPDU create = new CreateObjectAPDU(objectID.toBytesArray(), permissions.toBytesArray(), len); + + computeAPDU(create); + + APDUResponse response = processor.handleAPDURequest(create); + + //Throw this return code because this happens during enrollment and we don't have + // a more specific error code. + if (!response.checkResult()) { + throw new TPSException("SecureChannel.createObject. Failed to create object on token.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + } + + public int startEnrollment(int pe1, int pe2, TPSBuffer wrappedChallenge, TPSBuffer keyCheck, int algorithm, + int keySize, int option) throws TPSException, IOException { + + if (wrappedChallenge == null) { + throw new TPSException("SecureChannel.startEnrollment. Bad input data.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("SecureChannel.startEnrollment: entering ..."); + + boolean isECC = processor.getTPSEngine().isAlgorithmECC(algorithm); + + GenerateKeyAPDU generate_key_apdu = null; + GenerateKeyECCAPDU generate_ecc_key_apdu = null; + + APDUResponse response = null; + if (isECC) { + + generate_ecc_key_apdu = new GenerateKeyECCAPDU((byte) pe1, (byte) pe2, (byte) algorithm, keySize, + (byte) option, (byte) 0, wrappedChallenge, keyCheck); + + computeAPDU(generate_ecc_key_apdu); + + response = processor.handleAPDURequest(generate_ecc_key_apdu); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.startEnrollment. Failed generate key on token.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + } else { + + generate_key_apdu = new GenerateKeyAPDU((byte) pe1, (byte) pe2, (byte) algorithm, keySize, + (byte) option, (byte) 0, wrappedChallenge, keyCheck); + + computeAPDU(generate_key_apdu); + + response = processor.handleAPDURequest(generate_key_apdu); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.startEnrollment. Failed generate key on token.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + } + + TPSBuffer data = response.getData(); + + int size = data.getIntFrom2Bytes(0); + + CMS.debug("SecureChannel.startEnrollment: returning key size: " + size); + + return size; + + } + + public int tokenTypeToInt(TokenKeyType type) { + + if (type == TokenKeyType.KEY_TYPE_ENCRYPTION) + return 0; + + if (type == TokenKeyType.KEY_TYPE_SIGNING) + return 1; + else + return 2; + } + + public void setLifeycleState(byte flag) throws TPSException, IOException { + + CMS.debug("SecureChannel.setLifecycleState: flage: " + flag); + + LifecycleAPDU life = new LifecycleAPDU(flag); + + computeAPDU(life); + + APDUResponse response = processor.handleAPDURequest(life); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.setLifecycleState. Failed to set Lifecycle State!.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + } + + public void createPin(int pinNumber, int maxRetries, String pin) throws TPSException, IOException { + + CMS.debug("SecureChannel.createPin: entering..."); + + if (pin == null) { + throw new TPSException("SecureChannel.createPin: invalid intput data.", + TPSStatus.STATUS_ERROR_TOKEN_RESET_PIN_FAILED); + } + + TPSBuffer pinBuf = new TPSBuffer(pin.getBytes()); + CreatePinAPDU create = new CreatePinAPDU((byte) pinNumber, (byte) maxRetries, pinBuf); + + computeAPDU(create); + + @SuppressWarnings("unused") + APDUResponse response = processor.handleAPDURequest(create); + + //If the pin already exists we may get an error here, but we go on. + + } + + public void resetPin(int pinNumber, String new_pin) throws TPSException, IOException { + + CMS.debug("SecureChannel.resetPin"); + + if (new_pin == null) { + throw new TPSException("SecureChannel.resetPin: invalid intput data.", + TPSStatus.STATUS_ERROR_TOKEN_RESET_PIN_FAILED); + } + + TPSBuffer newPinBuf = new TPSBuffer(new_pin.getBytes()); + + SetPinAPDU reset = new SetPinAPDU((byte) 0x0, (byte) 0x0, newPinBuf); + + computeAPDU(reset); + + APDUResponse response = processor.handleAPDURequest(reset); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.resetPin: failed to reset pin.", + TPSStatus.STATUS_ERROR_TOKEN_RESET_PIN_FAILED); + } + + } + + public void putKeys(byte curVersion, byte curIndex, TPSBuffer keySetData) throws TPSException, IOException { + + CMS.debug("SecureChannel.putKeys: entering.."); + + if (keySetData == null) { + throw new TPSException("SecureChannel.putKeys: Invalid input data!", TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER); + } + + byte keyVersion = curVersion; + + if (curVersion == 0xff) { + keyVersion = 0x0; + } + + PutKeyAPDU putKey = new PutKeyAPDU(keyVersion, (byte) (0x80 | curIndex), keySetData); + + computeAPDU(putKey); + + APDUResponse response = processor.handleAPDURequest(putKey); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.putKeys: failed to upgrade key set!", + TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER); + } + + } + + public TPSBuffer getDRMWrappedDesKey() { + return drmDesKey; + } + + public void setDRMWrappedDesKey(TPSBuffer drmDesKey) { + this.drmDesKey = drmDesKey; + } + + public TPSBuffer getKeyCheck() { + return keyCheck; + } + + public void setKeyCheck(TPSBuffer theKeyCheck) { + this.keyCheck = theKeyCheck; + } + + public void importKeyEnc(int pe1, int pe2, TPSBuffer data) throws TPSException, IOException { + + CMS.debug("SecureChannel.importKeyEnc entering..."); + + if(data == null) { + throw new TPSException("SecureChannel.importKeyEnc: Invalid input data!",TPSStatus.STATUS_ERROR_MAC_CERT_PDU); + } + + ImportKeyEncAPDU importKeyEnc = new ImportKeyEncAPDU((byte)pe1,(byte) pe2, data); + + computeAPDU(importKeyEnc); + + APDUResponse response = processor.handleAPDURequest(importKeyEnc); + + if (!response.checkResult()) { + throw new TPSException("SecureChannel.importKeyEnc: failed to import private key!", + TPSStatus.STATUS_ERROR_MAC_CERT_PDU); + } + + } + + public TPSBuffer getKekDesKey() { + return kekDesKey; + } + + public void setKekDesKey(TPSBuffer kekDesKey) { + this.kekDesKey = kekDesKey; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/CAEnrollCertResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/CAEnrollCertResponse.java new file mode 100644 index 000000000..9c83e0842 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/CAEnrollCertResponse.java @@ -0,0 +1,52 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import netscape.security.x509.X509CertImpl; +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * CAEnrollCertResponse is the class for the response to + * CA Remote Request: enrollCertificate() + * + */ +public class CAEnrollCertResponse extends RemoteResponse +{ + public CAEnrollCertResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public String getCertB64() { + return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_b64); + } + + public String getCertSerialHex() { + return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_serial); + } + + public String getCertSubjectDN() { + return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN); + } + + public X509CertImpl getCert() { + return (X509CertImpl) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_x509); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java b/base/tps/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java new file mode 100644 index 000000000..5851d2f69 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java @@ -0,0 +1,760 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.CertificateException; +import java.util.Hashtable; +import java.util.List; + +import netscape.security.x509.RevocationReason; +import netscape.security.x509.X509CertImpl; + +import org.dogtagpki.server.connector.IRemoteRequest; +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.engine.TPSEngine; +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.Util; +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.CryptoManager.NotInitializedException; +import org.mozilla.jss.crypto.X509Certificate; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.EPropertyNotFound; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.cmscore.connector.HttpConnector; +import com.netscape.cmsutil.http.HttpResponse; +import com.netscape.cmsutil.util.Utils; +import com.netscape.cmsutil.xml.XMLObject; + +/** + * CARemoteRequestHandler is a class representing remote requests + * offered by the Certificate Authority (CA) + * + * @author cfu + */ +public class CARemoteRequestHandler extends RemoteRequestHandler +{ + public CARemoteRequestHandler(String connID) + throws EBaseException { + if (connID == null) { + throw new EBaseException("CARemoteRequestHandler: CARemoteRequestHandler(): connID null."); + } + connid = connID; + } + + /** + * enrollCertificate enrolls a certificate in the CA + * + * @param pubKeybuf public key for enrollment + * @param uid uid for enrollment + * @param cuid token id + * + * @returns CAEnrollCertResponse + */ + public CAEnrollCertResponse enrollCertificate( + TPSBuffer pubKeybuf, + String uid, + String cuid, + String tokenType, + String keyType) + throws EBaseException { + + CMS.debug("CARemoteRequestHandler: enrollCertificate(): begins."); + if (pubKeybuf == null || uid == null || cuid == null) { + throw new EBaseException("CARemoteRequestHandler: enrollCertificate(): input parameter null."); + } + + IConfigStore conf = CMS.getConfigStore(); + String profileId = + conf.getString(TPSEngine.OP_ENROLL_PREFIX + "." + + tokenType + ".keyGen." + + keyType + ".ca.profileId"); + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("CARemoteRequestHandler: enrollCertificate(): sending request to CA"); + String encodedPubKey = null; + try { + encodedPubKey = Util.uriEncode(CMS.BtoA(pubKeybuf.toBytesArray())); + } catch (Exception e) { + CMS.debug("CARemoteRequestHandler: enrollCertificate(): uriEncode of pubkey failed: " + e); + throw new EBaseException("CARemoteRequestHandler: enrollCertificate(): uriEncode of pubkey failed: " + e); + } + HttpResponse resp = + conn.send("enrollment", + IRemoteRequest.GET_XML + "=" + + true + + "&" + IRemoteRequest.TOKEN_CUID + "=" + + cuid + + "&" + IRemoteRequest.CA_ENROLL_screenname + "=" + + uid + + "&" + IRemoteRequest.CA_ENROLL_publickey + "=" + + encodedPubKey + + "&" + IRemoteRequest.CA_ProfileId + "=" + + profileId); + + String content = resp.getContent(); + + CMS.debug("CARemoteRequestHandler: enrollCertificate(): got content = " + content); + + if (content != null && !content.equals("")) { + XMLObject xmlResponse = + getXMLparser(content); + + Hashtable<String, Object> response = + new Hashtable<String, Object>(); + + CMS.debug("CARemoteRequestHandler: enrollCertificate(): received:" + + content); + + /** + * When a value is not found in response, keep going so we know + * what else is missing + * Note: serverKeygen and !serverKeygen returns different set of + * response values so "missing" might not be bad + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = xmlResponse.getValue(IRemoteRequest.RESPONSE_STATUS_XML); + if (value == null) { + CMS.debug("CARemoteRequestHandler: enrollCertificate(): Status not found."); + CMS.debug("CARemoteRequestHandler: enrollCertificate(): got content = " + content); + } else { + CMS.debug("CARemoteRequestHandler: enrollCertificate(): got Status = " + value); + ist = Integer.parseInt(value); + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = xmlResponse.getValue("SubjectDN"); + if (value == null) { + CMS.debug("CARemoteRequestHandler:: enrollCertificate(): response missing name-value pair for: " + + IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN); + } else { + CMS.debug("CARemoteRequestHandler:: enrollCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN = " + + value); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN, value); + } + + value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_Certificate_serial); + if (value == null) { + CMS.debug("CARemoteRequestHandler:: enrollCertificate(): response missing name-value pair for: " + + IRemoteRequest.CA_RESPONSE_Certificate_serial); + } else { + CMS.debug("CARemoteRequestHandler:: enrollCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_serial = 0x" + + value); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_serial, value); + } + + value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_Certificate_b64); + if (value == null) { + CMS.debug("CARemoteRequestHandler:: enrollCertificate(): response missing name-value pair for: " + + IRemoteRequest.CA_RESPONSE_Certificate_b64); + } else { + try { + CMS.debug("CARemoteRequestHandler:: enrollCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_b64 = " + + value); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_b64, value); + X509CertImpl newCert = new X509CertImpl(Utils.base64decode(value)); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_x509, newCert); + CMS.debug("CARemoteRequestHandler: enrollCertificate(): new cert parsed successfully"); + } catch (Exception e) { + // we don't exit. Keep going. + CMS.debug("CARemoteRequestHandler: enrollCertificate(): exception:" + e); + } + } + + CMS.debug("CARemoteRequestHandler: enrollCertificate(): ends."); + return new CAEnrollCertResponse(response); + } else { + CMS.debug("CARemoteRequestHandler: enrollCertificate(): no response content"); + throw new EBaseException("CARemoteRequestHandler: enrollCertificate(): no response content."); + } + } + + /** + * retrieveCertificate retrieves a certificate by serial number + * + * @param serialno the serial number of the cert to be retrieved + * @return CARetrieveCertResponse + */ + public CARetrieveCertResponse retrieveCertificate( + BigInteger serialno) + throws EBaseException { + + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): begins."); + if (serialno == null) { + throw new EBaseException("CARemoteRequestHandler: retrieveCertificate(): input parameter null."); + } + + //ToDo: I"m not sure why these are not used, let's check this out. + //It's working though. + + /* + IConfigStore conf = CMS.getConfigStore(); + String configName = "tps.connector." + connid + ".uri.getBySerial"; + String servlet = conf.getString(configName, "/ca/ee/ca/displayBySerial"); + */ + + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): sending request to CA"); + HttpResponse resp = + conn.send("getcert", + IRemoteRequest.GET_XML + "=" + true + + "&" + IRemoteRequest.CA_GET_CERT_B64CertOnly + "=" + true + + "&" + IRemoteRequest.CA_GET_CERT_SERIAL + "=" + serialno.toString()); + + String content = resp.getContent(); + if (content != null && !content.equals("")) { + XMLObject xmlResponse = + getXMLparser(content); + + Hashtable<String, Object> response = + new Hashtable<String, Object>(); + + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): received:" + + content); + + /** + * When a value is not found in response, keep going so we know + * what else is missing + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = xmlResponse.getValue(IRemoteRequest.RESPONSE_STATUS_XML); + if (value == null) { + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): Status not found."); + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): got content = " + content); + } else { + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): got Status = " + value); + ist = Integer.parseInt(value); + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_Certificate_chain_b64); + if (value == null) { + CMS.debug("CARemoteRequestHandler:: retrieveCertificate(): response missing name-value pair for: " + + IRemoteRequest.CA_RESPONSE_Certificate_chain_b64); + } else { + CMS.debug("CARemoteRequestHandler:: retrieveCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_chain_b64 = " + + value); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_chain_b64, value); + try { + X509CertImpl newCert = new X509CertImpl(Utils.base64decode(value)); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_x509, newCert); + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): retrieved cert parsed successfully"); + } catch (CertificateException e) { + // we don't exit. Keep going. + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): exception:" + e); + } + } + + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): ends."); + return new CARetrieveCertResponse(response); + } else { + CMS.debug("CARemoteRequestHandler: retrieveCertificate(): no response content"); + throw new EBaseException("CARemoteRequestHandler: retrieveCertificate(): no response content."); + } + } + + /** + * renewCertificate renew a certificate by serial number + * + * @param serialno the serial number of the cert to be renewed + * @return CARenewCertResponse + */ + public CARenewCertResponse renewCertificate( + BigInteger serialno, + String tokenType, + String keyType) + throws EBaseException { + + CMS.debug("CARemoteRequestHandler: renewCertificate(): begins."); + if (serialno == null) { + throw new EBaseException("CARemoteRequestHandler: renewCertificate(): input parameter null."); + } + + IConfigStore conf = CMS.getConfigStore(); + + String profileId = + conf.getString(TPSEngine.OP_ENROLL_PREFIX + "." + + tokenType + ".renewal." + + keyType + ".ca.profileId"); + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("CARemoteRequestHandler: renewCertificate(): sending request to CA"); + HttpResponse resp = + conn.send("renewal", + IRemoteRequest.GET_XML + "=" + true + + "&" + IRemoteRequest.CA_RENEWAL + "=" + true + + "&" + IRemoteRequest.CA_RENEWAL_SerialNum + "=" + serialno.toString() + + "&" + IRemoteRequest.CA_ProfileId + "=" + profileId); + + String content = resp.getContent(); + + if (content != null && !content.equals("")) { + XMLObject xmlResponse = + getXMLparser(content); + + Hashtable<String, Object> response = + new Hashtable<String, Object>(); + + CMS.debug("CARemoteRequestHandler: renewCertificate(): received:" + + content); + + /** + * When a value is not found in response, keep going so we know + * what else is missing + * Note: serverKeygen and !serverKeygen returns different set of + * response values so "missing" might not be bad + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = xmlResponse.getValue(IRemoteRequest.RESPONSE_STATUS_XML); + if (value == null) { + CMS.debug("CARemoteRequestHandler: renewCertificate(): Status not found."); + CMS.debug("CARemoteRequestHandler: renewCertificate(): got content = " + content); + } else { + CMS.debug("CARemoteRequestHandler: renewCertificate(): got Status = " + value); + ist = Integer.parseInt(value); + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = xmlResponse.getValue("SubjectDN"); + if (value == null) { + CMS.debug("CARemoteRequestHandler:: renewCertificate(): response missing name-value pair for: " + + IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN); + } else { + CMS.debug("CARemoteRequestHandler:: renewCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN = " + + value); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN, value); + } + + value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_Certificate_serial); + if (value == null) { + CMS.debug("CARemoteRequestHandler:: renewCertificate(): response missing name-value pair for: " + + IRemoteRequest.CA_RESPONSE_Certificate_serial); + } else { + CMS.debug("CARemoteRequestHandler:: renewCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_serial = 0x" + + value); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_serial, value); + } + + value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_Certificate_b64); + if (value == null) { + CMS.debug("CARemoteRequestHandler:: renewCertificate(): response missing name-value pair for: " + + IRemoteRequest.CA_RESPONSE_Certificate_b64); + } else { + CMS.debug("CARemoteRequestHandler:: renewCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_b64 = " + + value); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_b64, value); + try { + X509CertImpl newCert = new X509CertImpl(Utils.base64decode(value)); + response.put(IRemoteRequest.CA_RESPONSE_Certificate_x509, newCert); + CMS.debug("CARemoteRequestHandler: renewCertificate(): new cert parsed successfully"); + } catch (CertificateException e) { + // we don't exit. Keep going. + CMS.debug("CARemoteRequestHandler: renewCertificate(): exception:" + e); + } + } + + CMS.debug("CARemoteRequestHandler: renewCertificate(): ends."); + return new CARenewCertResponse(response); + } else { + CMS.debug("CARemoteRequestHandler: renewCertificate(): no response content"); + throw new EBaseException("CARemoteRequestHandler: renewCertificate(): no response content."); + } + } + + /** + * revokeCertificate provides the basic revocation of a certificate from + * the CA + * + * @param serialno serial number of the cert to revoke + * @param reason reason to revoke per definition in RevocationReason + * + * @returns CARevokeCertResponse + */ + private CARevokeCertResponse revokeCertificate( + String serialno, + RevocationReason reason) + throws EBaseException { + + CMS.debug("CARemoteRequestHandler: revokeCertificate(): begins on serial#:"+ serialno); + if (serialno == null || reason == null) { + throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter null."); + } + + // IConfigStore conf = CMS.getConfigStore(); + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("CARemoteRequestHandler: revokeCertificate(): sending request to CA"); + HttpResponse resp = + conn.send("revoke", + IRemoteRequest.CA_OP + "=" + IRemoteRequest.CA_REVOKE + + "&" + IRemoteRequest.CA_REVOCATION_REASON + "=" + reason.getCode() + + "&" + IRemoteRequest.CA_REVOKE_ALL + "=(" + + IRemoteRequest.CA_REVOKE_SERIAL + "=" + serialno + ")&" + + IRemoteRequest.CA_REVOKE_COUNT + "=1"); + String content = resp.getContent(); + + CMS.debug("CARemoteRequestHandler: revokeCertificate(): got content = " + content); + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + + /** + * When a value is not found in response, keep going so we know + * what else is missing + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + + CMS.debug("CARemoteRequestHandler: revokeCertificate(): got status = " + value); + ist = Integer.parseInt(value); + if (ist != 0) { + CMS.debug("CARemoteRequestHandler: revokeCertificate(): status not 0, getting error string... "); + value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING); + if (value == null) { + CMS.debug("CARemoteRequestHandler: revokeCertificate(): response missing name-value pair for: " + + IRemoteRequest.RESPONSE_ERROR_STRING); + } else { + CMS.debug("CARemoteRequestHandler: revokeCertificate(): got IRemoteRequest.RESPONSE_ERROR_STRING = " + + value); + response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value); + } + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + CMS.debug("CARemoteRequestHandler: revokeCertificate(): ends."); + return new CARevokeCertResponse(response); + } else { + CMS.debug("CARemoteRequestHandler: revokeCertificate(): no response content."); + throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): no response content."); + } + } + + /** + * unrevokeCertificate provides the basic unrevocation of a certificate from + * the CA + * + * @param serialno serial number of the cert to unrevoke + * + * @returns CARevokeCertResponse + */ + private CARevokeCertResponse unrevokeCertificate( + String serialno) + throws EBaseException { + + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): begins on serial#:"+ serialno); + if (serialno == null) { + throw new EBaseException("CARemoteRequestHandler: unrevokeCertificate(): input parameter null."); + } + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): sending request to CA"); + HttpResponse resp = + conn.send("unrevoke", + IRemoteRequest.CA_UNREVOKE_SERIAL + "=" + serialno); + String content = resp.getContent(); + + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): got content = " + content); + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + + /** + * When a value is not found in response, keep going so we know + * what else is missing + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): got status = " + value); + ist = Integer.parseInt(value); + if (ist != 0) { + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): status not 0, getting error string... "); + value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING); + if (value == null) { + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): response missing name-value pair for: " + + IRemoteRequest.RESPONSE_ERROR_STRING); + } else { + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): got IRemoteRequest.RESPONSE_ERROR_STRING = " + + value); + response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value); + } + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): ends."); + return new CARevokeCertResponse(response); + } else { + CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): no response content."); + throw new EBaseException("CARemoteRequestHandler: unrevokeCertificate(): no response content."); + } + } + + /** + * revokeFromOtherCA searches through all defined ca entries + * to find the cert's signing ca for revocation / unrevocation. + * It is called from revokeCertificate() when the cert's + * AKI does not match that of the current signing ca. + * + * @param revoke true to revoke; false to unrevoke + * @param cert cert to (un)revoke + * @param serialno parameter for the (Un)RevokeCertificate() functions + * @param reason RevocationReason for the base revokeCertificate() function + * @throws IOException + */ + @SuppressWarnings("unused") + private CARevokeCertResponse revokeFromOtherCA( + boolean revoke, // true==revoke; false==unrevoke + X509CertImpl cert, + RevocationReason reason) + throws EBaseException, IOException { + if (cert == null) { + throw new EBaseException("CARemoteRequestHandler: revokeFromOtherCA(): input parameter cert null."); + } + String certAkiString = null; + try { + certAkiString = Util.getCertAkiString(cert); + } catch (Exception e) { + throw new EBaseException("CARemoteRequestHandler: revokeFromOtherCA(): getCertAkiString failed:" + e); + } + return revokeFromOtherCA(revoke, cert.getSerialNumber().toString(), certAkiString, reason); + } + + + private CARevokeCertResponse revokeFromOtherCA( + boolean revoke, // true==revoke; false==unrevoke + String serialno, + String certAkiString, + RevocationReason reason) + throws EBaseException { + + + CMS.debug("CARemoteRequestHandler: revokeFromOtherCA: begins"); + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + List<String> caList = + subsystem.getConnectionManager().getCAList(); + + Exception exception = null; + + for (String ca : caList) { + try { + String caSkiString = getCaSki(ca); + if (certAkiString.equals(caSkiString)) { + CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() cert AKI and caCert SKI matched"); + if (revoke) { + return revokeCertificate(serialno, reason); + } else { + return unrevokeCertificate(serialno); + } + } else { // not a match then iterate to next ca in list + CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() cert AKI and caCert SKI not matched"); + } + } catch (Exception e) { + // any issue then iterate to next ca in list + CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() issue found, iterate to next ca in list. Exception:" + + e); + exception = e; + } + } + if (exception == null) { + throw new EBaseException("revokeFromOtherCA: signing ca not found"); + } else { + throw new EBaseException(exception.toString()); + } + } + + /** + * getCaSki returns the CA's Subject Key Identifier (ski) + * associated with the connector id. + * If the ca's ski has not been calculated, it will do so and + * save to the connector's caSKI config entry + * + * @param conn connector id + * @returns ca's ski associated with conn + */ + private String getCaSki(String conn) + throws EBaseException, IOException { + + String caSkiString = null; + if (conn == null) { + throw new EBaseException("CARemoteRequestHandler: getCaSki(): input parameter conn null."); + } + + IConfigStore conf = CMS.getConfigStore(); + + /* + * first, see if ca Subject Key Identifier (SKI) is in + * config store. If not, put it in, so we don't have to + * calculate that every time. + */ + try { + String configName = "tps.connector." + conn + ".caSKI"; + CMS.debug("CARemoteRequestHandler: getCaSki() retriving configName=" + configName); + return conf.getString(configName); + } catch (EPropertyNotFound e) { + // caSKI not yet calculated; proceed to calculate + CMS.debug("CARemoteRequestHandler: getCaSki() caSKI not yet calculated:" + e); + } catch (EBaseException e) { + throw e; + } + + try { + String caNickname = + conf.getString("tps.connector." + conn + ".caNickname"); + CMS.debug("CARemoteRequestHandler: getCaSki() Calculating caSKI...searching for ca cert in nss db:" + + caNickname); + CryptoManager cm = CryptoManager.getInstance(); + try { + X509Certificate c = cm.findCertByNickname(caNickname); + X509CertImpl caCert = new X509CertImpl(c.getEncoded()); + // now retrieve caSKI and store in config + caSkiString = Util.getCertSkiString(caCert); + CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculated. Saving it."); + conf.putString("tps.connector." + conn + ".caSKI", caSkiString); + conf.commit(false); + } catch (IOException e) { + throw e; + } catch (Exception et) { + /* ca cert not found in nss db; no match needed */ + CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculation failure." + et); + throw new EBaseException("CARemoteRequestHandler: getCaSki(): skip match."); + } + } catch (EBaseException e) { + /* + * if it gets here, that means config is missing both: + * 1. tps.connector.ca<n>.caSKI + * 2. tps.connector.ca<n>.caNickname + * now assume default of just using the issuing ca and + * no search performed + */ + CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculation failure." + e); + throw e; + } catch (NotInitializedException e) { + CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculation failure." + e); + throw new EBaseException("CARemoteRequestHandler: getCaSki(): skip match.:" + e); + } + + return caSkiString; + } + + + + /** + * revokeCertificate() supports revocation routing by providing + * CA discovery. When needed, it searchs through all listed ca + * entries to find the cert's signing ca for revocation. + * + * Note: in the configuration, the ca signing cert of each ca + * id must be imported into the db and have its nickname present. + * e.g. tps.connector.ca1.caNickname=CA1nickname + * + * See design: + * http://pki.fedoraproject.org/wiki/TPS_-_Revocation_Routing + * + * @param revoke true to revoke; false to unrevoke + * @param serialno serial number for the (Un)RevokeCertificate() functions + * @param reason RevocationReason for the base revokeCertificate() function + */ + public CARevokeCertResponse revokeCertificate( + boolean revoke, // true==revoke; false==unrevoke + X509CertImpl cert, + RevocationReason reason) + throws EBaseException { + if (cert == null) { + throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter cert null."); + } + String certAkiString = null; + try { + certAkiString = Util.getCertAkiString(cert); + } catch (IOException e) { + throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): getCertAkiString failed:" + e); + } + + return revokeCertificate(revoke, cert.getSerialNumber().toString(), certAkiString, reason); + } + + public CARevokeCertResponse revokeCertificate( + boolean revoke, // true==revoke; false==unrevoke + String serialno, + String certAkiString, + RevocationReason reason) + throws EBaseException { + + CMS.debug("CARemoteRequestHandler: revokeCertificate() begins with CA discovery"); + + if (revoke == true && reason == null) { + throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter reason null."); + } + + boolean skipMatch = false; + + String caSkiString = null; + + try { + caSkiString = getCaSki(connid); + } catch (Exception e) { + CMS.debug("CARemoteRequestHandler: revokeCertificate() exception:" + e); + skipMatch = true; + } + if (!skipMatch) { + /* now compare cert's AKI to the ca's SKI + * if matched, continue, + * if not, search in the ca list + */ + CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI matching begins"); + if (certAkiString.equals(caSkiString)) { + CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI matched"); + if (revoke) { + return revokeCertificate(serialno, reason); + } else { + return unrevokeCertificate(serialno); + } + } else { + CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI of the designated issuing ca do not match...calling revokeFromOtherCA to search for another ca"); + return revokeFromOtherCA(revoke, serialno, certAkiString, reason); + } + } else { + if (revoke) { + return revokeCertificate(serialno, reason); + } else { + return unrevokeCertificate(serialno); + } + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/CARenewCertResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/CARenewCertResponse.java new file mode 100644 index 000000000..bb9ebbb44 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/CARenewCertResponse.java @@ -0,0 +1,52 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import netscape.security.x509.X509CertImpl; +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * CARenewCertResponse is the class for the response to + * CA Remote Request: renewCertificate() + * + */ +public class CARenewCertResponse extends RemoteResponse +{ + public CARenewCertResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public String getRenewedCertB64() { + return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_b64); + } + + public String getRenewedCertSerialHex() { + return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_serial); + } + + public String getRenewedCertSubjectDN() { + return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_SubjectDN); + } + + public X509CertImpl getRenewedCert() { + return (X509CertImpl) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_x509); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/CARetrieveCertResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/CARetrieveCertResponse.java new file mode 100644 index 000000000..a356907fa --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/CARetrieveCertResponse.java @@ -0,0 +1,45 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import netscape.security.x509.X509CertImpl; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * CARetrieveCertResponse is the class for the response to + * CA Remote Request: retrieveCertificate() + * + */ +public class CARetrieveCertResponse extends RemoteResponse +{ + public CARetrieveCertResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public String getCertB64() { + return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_chain_b64); + } + + public X509CertImpl getCert() { + return (X509CertImpl) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_x509); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/CARevokeCertResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/CARevokeCertResponse.java new file mode 100644 index 000000000..d7db5976c --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/CARevokeCertResponse.java @@ -0,0 +1,39 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * CARevokeCertResponse is the class for the response to + * CA Remote Request: revoteCertificate() + * + */ +public class CARevokeCertResponse extends RemoteResponse +{ + public CARevokeCertResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public String getErrorString() { + return (String) nameValTable.get(IRemoteRequest.RESPONSE_ERROR_STRING); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/ConnectionManager.java b/base/tps/src/org/dogtagpki/server/tps/cms/ConnectionManager.java new file mode 100644 index 000000000..2b5ab2208 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/ConnectionManager.java @@ -0,0 +1,229 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; + +import javax.ws.rs.core.MediaType; + +import org.dogtagpki.server.tps.TPSSubsystem; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.connector.IConnector; +import com.netscape.cmscore.connector.HttpConnector; +import com.netscape.cmscore.connector.RemoteAuthority; + +/** + * ConnectionManager is a class for connection management + * of its Remote Authorities + * + * @author cfu + */ +public class ConnectionManager +{ + private Hashtable<String, IConnector> connectors; + List<String> caList; + + public ConnectionManager() { + // initialize the ca list for revocation routing: + // tps.connCAList=ca1,ca2...ca<n> + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + IConfigStore conf = subsystem.getConfigStore(); + String caListString; + try { + caListString = conf.getString("connCAList"); + CMS.debug("ConnectionManager: ConnectionManager(): Initializing CA routing list"); + } catch (EBaseException e) { + CMS.debug("ConnectionManager: ConnectionManager(): no connCAList for ca discovery. No revocation routing"); + return; + } + + caList = Arrays.asList(caListString.split(",")); + CMS.debug("ConnectionManager: ConnectionManager(): CA routing list initialized."); + } + + public List<String> getCAList() { + return caList; + } + + /* + * connector establishment with multi-uri support + * + * Initialize all connectors + * tps.connector.<connID>.xxx + * + * e.g. (with Failover list under "host", separated by a space) + * + * tps.connector.ca1.enable=true + * tps.connector.ca1.minHttpConns=1 + * tps.connector.ca1.maxHttpConns=15 + * tps.connector.ca1.host=host1.EXAMPLE.com:8445 host2.EXAMPLE.com:8445 + * tps.connector.ca1.port=<port number; unused if for failover case> + * tps.connector.ca1.nickName=subsystemCert cert-pki-tomcat TPS + * tps.connector.ca1.timeout=30 + * # In the example below, + * # "enrollment", "getcert", "renewal", "revoke", and "unrevoke" + * # are what's being referred to as "op" in the multi-uri support code + * tps.connector.ca1.uri.enrollment=/ca/ee/ca/profileSubmitSSLClient + * tps.connector.ca1.uri.renewal=/ca/ee/ca/profileSubmitSSLClient + * tps.connector.ca1.uri.getcert=/ca/ee/ca/displayBySerial + * tps.connector.ca1.uri.revoke=/ca/ee/subsystem/ca/doRevoke + * tps.connector.ca1.uri.unrevoke=/ca/ee/subsystem/ca/doUnrevoke + */ + public void initConnectors() throws EBaseException { + CMS.debug("ConnectionManager: initConnectors(): begins."); + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + IConfigStore conf = subsystem.getConfigStore(); + IConfigStore connectorSubstore = conf.getSubStore("connector"); + Enumeration<String> connector_enu = connectorSubstore.getSubStoreNames(); + connectors = new Hashtable<String, IConnector>(); + while (connector_enu.hasMoreElements()) { + String connectorID = connector_enu.nextElement(); + CMS.debug("ConnectionManager: initConnectors(): initializing connector " + connectorID); + IConfigStore connectorConfig = + connectorSubstore.getSubStore(connectorID); + IConnector conn = null; + boolean enable = connectorConfig.getBoolean("enable", false); + if (!enable) { + CMS.debug("ConnectionManager: initConnectors(): connector disabled."); + continue; + } + CMS.debug("ConnectionManager: initConnectors(): connector enabled."); + conn = createConnector(connectorConfig); + + connectors.put(connectorID, conn); + CMS.debug("ConnectionManager: initConnectors(): connector " + + connectorID + + " initialized."); + } + CMS.debug("ConnectionManager: initConnectors(): ends."); + } + + /* + * Creates and returns a connector + * + * @param conf config store of the connector + * @return IConnector the connector if created successfully; null if not + */ + private IConnector createConnector(IConfigStore conf) + throws EBaseException { + IConnector connector = null; + + CMS.debug("ConnectionManager: createConnector(): begins."); + if (conf == null || conf.size() <= 0) { + CMS.debug("ConnectionManager: createConnector(): conf null or empty."); + throw new EBaseException("called with null config store"); + } + + String host = conf.getString("host"); + if (host == null) { + CMS.debug("ConnectionManager: createConnector(): host not found in config."); + throw new EBaseException("host not found in config"); + } + // port doesn't have to contain anything if failover supplied in host + int port = conf.getInteger("port"); + + Hashtable<String, String> uris = new Hashtable<String, String>(); + IConfigStore uriSubstore = conf.getSubStore("uri"); + if (uriSubstore == null) { + CMS.debug("ConnectionManager: createConnector(): uri(s) not found in config."); + throw new EBaseException("uri(s) not found in config"); + } + CMS.debug("ConnectionManager: createConnector(): uriSubstore name=" + uriSubstore.getName() + " size =" + + uriSubstore.size()); + + Enumeration<String> uri_enu = uriSubstore.getPropertyNames(); + while (uri_enu.hasMoreElements()) { + String op = uri_enu.nextElement(); + if ((op != null) && !op.equals("")) + CMS.debug("ConnectionManager: createConnector(): op name=" + op); + else + continue; + + String uriValue = uriSubstore.getString(op); + if ((uriValue != null) && !uriValue.equals("")) + CMS.debug("ConnectionManager: createConnector(): uri value=" + uriValue); + else + continue; + uris.put(op, uriValue); + } + + String nickname = conf.getString("nickName", null); + if (nickname != null) + CMS.debug("ConnectionManager: createConnector(): nickName=" + nickname); + else { + CMS.debug("ConnectionManager: createConnector(): nickName not found in config"); + throw new EBaseException("nickName not found in config"); + } + + // "resendInterval" is for Request Queue, and not supported in TPS + int resendInterval = -1; + int timeout = conf.getInteger("timeout", 0); + RemoteAuthority remauthority = + new RemoteAuthority(host, port, uris, timeout, MediaType.APPLICATION_FORM_URLENCODED); + + CMS.debug("ConnectionManager: createConnector(): establishing HttpConnector"); + if (timeout == 0) { + connector = + new HttpConnector(null, nickname, remauthority, resendInterval, conf); + } else { + connector = + new HttpConnector(null, nickname, remauthority, resendInterval, conf, timeout); + } + + CMS.debug("ConnectionManager: createConnector(): ends."); + return connector; + } + + /* + * Gets an established connector to be used to send requests + * to a remote Authority (Note that Failover is supported in the + * underlying connector framework. + * + * Example usage (with example config for "ca1" defined above + * in initConnectors(): + * + * TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + * HttpConnector testConn = + * (HttpConnector) subsystem.getConnectionManager().getConnector(connectionID); + * HttpResponse resp = + * testConn.send("renewal", + * "serial_num=6&profileId=caTokenUserEncryptionKeyRenewal&renewal=true"); + * if (resp != null) { + * CMS.debug("Connector test: HttpResponse content:"+ + * resp.getContent()); + * } else { + * CMS.debug("Connector test: HttpResponse content null"); + * } + * + * @param connID connection id per defined in the configuration + * @return IConnector the connector matching the connection id + */ + public IConnector getConnector(String connID) { + return connectors.get(connID); + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/KRARecoverKeyResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/KRARecoverKeyResponse.java new file mode 100644 index 000000000..9d0c5ff5f --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/KRARecoverKeyResponse.java @@ -0,0 +1,51 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * KRARecoverKeyResponse is the class for the response to + * KRA Remote Request: recoverKey() + * + */ +public class KRARecoverKeyResponse extends RemoteResponse +{ + public KRARecoverKeyResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public String getErrorString() { + return (String) nameValTable.get(IRemoteRequest.RESPONSE_ERROR_STRING); + } + + public String getPublicKey() { + return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_PublicKey); + } + + public String getWrappedPrivKey() { + return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey); + } + + public String getIVParam() { + return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_IV_Param); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/KRARemoteRequestHandler.java b/base/tps/src/org/dogtagpki/server/tps/cms/KRARemoteRequestHandler.java new file mode 100644 index 000000000..87388ff99 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/KRARemoteRequestHandler.java @@ -0,0 +1,322 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; +import org.dogtagpki.server.tps.TPSSubsystem; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.cmscore.connector.HttpConnector; +import com.netscape.cmsutil.http.HttpResponse; + +/** + * KRARemoteRequestHandler is a class representing remote requests + * offered by the Key Recovery Authority (KRA) + * On a successful return, name/value pairs are provided in a Hashtable where + * all contents are URL decoded if needed + * + * @author cfu + */ +public class KRARemoteRequestHandler extends RemoteRequestHandler +{ + public KRARemoteRequestHandler(String connID) + throws EBaseException { + if (connID == null) { + throw new EBaseException("KRARemoteRequestHandler: KRARemoteRequestHandler(): connID null."); + } + + connid = connID; + } + + /** + * serverSideKeyGen generates key pairs on the KRA + * + * @param cuid is the token id + * @param userid is the user id + * @param sDesKey is the des key provided by the TKS for key encryption + * @param archive true or false + * + * @returns KRAServerSideKeyGenResponse + */ + public KRAServerSideKeyGenResponse serverSideKeyGen( + boolean isECC, + int keysize, + String cuid, + String userid, + String sDesKey, + boolean archive) + throws EBaseException { + + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): begins."); + if (cuid == null || userid == null || sDesKey == null) { + throw new EBaseException("KRARemoteRequestHandler: serverSideKeyGen(): input parameter null."); + } + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): sending request to CA"); + HttpResponse resp; + String request; + if (isECC) { + String eckeycurve; + if (keysize == 521) { + eckeycurve = "nistp521"; + } else if (keysize == 384) { + eckeycurve = "nistp384"; + } else if (keysize == 256) { + eckeycurve = "nistp256"; + } else { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): unrecognized ECC keysize" + keysize + + ", setting to nistp256"); + keysize = 256; + eckeycurve = "nistp256"; + } + + request = IRemoteRequest.KRA_KEYGEN_Archive + "=" + + archive + + "&" + IRemoteRequest.TOKEN_CUID + "=" + + cuid + + "&" + IRemoteRequest.KRA_UserId + "=" + + userid + + "&" + IRemoteRequest.KRA_KEYGEN_KeyType + "=" + + "EC" + + "&" + IRemoteRequest.KRA_KEYGEN_EC_KeyCurve + "=" + + eckeycurve + + "&" + IRemoteRequest.KRA_Trans_DesKey + "=" + + sDesKey; + + CMS.debug("KRARemoteRequestHandler: outgoing request for ECC: " + request); + + resp = + conn.send("GenerateKeyPair", + request); + } else { // RSA + + request = IRemoteRequest.KRA_KEYGEN_Archive + "=" + + archive + + "&" + IRemoteRequest.TOKEN_CUID + "=" + + cuid + + "&" + IRemoteRequest.KRA_UserId + "=" + + userid + + "&" + IRemoteRequest.KRA_KEYGEN_KeyType + "=" + + "RSA" + + "&" + IRemoteRequest.KRA_KEYGEN_KeySize + "=" + + keysize + + "&" + IRemoteRequest.KRA_Trans_DesKey + "=" + + sDesKey; + + CMS.debug("KRARemoteRequestHandler: outgoing request for RSA: " + request); + + resp = + conn.send("GenerateKeyPair", + request); + } + + //For some reason the send method can return null and not throw an exception. + // Check here; + + if(resp == null) { + throw new EBaseException("KRARemoteRequestHandler: serverSideKeyGen(): No response object returned from connection."); + } + + String content = resp.getContent(); + + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): got content = " + content); + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + + /** + * When a value is not found in response, keep going so we know + * what else is missing + * Note: response values "missing" might not be bad for some cases + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + + if(value == null) { + throw new EBaseException("KRARemoteRequestHandler: serverSideKeyGen(): Invalide status returned!"); + } + + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): got status = " + value); + ist = Integer.parseInt(value); + if (ist != 0) { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): status not 0, getting error string... "); + value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " + + IRemoteRequest.RESPONSE_ERROR_STRING); + } else { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): got IRemoteRequest.RESPONSE_ERROR_STRING = " + + value); + response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value); + } + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = (String) response.get(IRemoteRequest.KRA_RESPONSE_PublicKey); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " + + IRemoteRequest.KRA_RESPONSE_PublicKey); + } else { + CMS.debug("KRARemoteRequestHandler:serverSideKeyGen(): got IRemoteRequest.KRA_RESPONSE_PublicKey= " + + value); + response.put(IRemoteRequest.KRA_RESPONSE_PublicKey, value); + } + + value = (String) response.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " + + IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey); + } else { + CMS.debug("KRARemoteRequestHandler:serverSideKeyGen(): got IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey= " + + value); + response.put(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey, value); + } + + value = (String) response.get(IRemoteRequest.KRA_RESPONSE_IV_Param); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " + + IRemoteRequest.KRA_RESPONSE_IV_Param); + } else { + CMS.debug("KRARemoteRequestHandler:serverSideKeyGen(): got IRemoteRequest.KRA_RESPONSE_IV_Param= " + + value); + response.put(IRemoteRequest.KRA_RESPONSE_IV_Param, value); + } + + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): ends."); + return new KRAServerSideKeyGenResponse(response); + } else { + CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): no response content."); + throw new EBaseException("KRARemoteRequestHandler: serverSideKeyGen(): no response content."); + } + + } + + /** + * recoverKey recovers keys from KRA + * + * @param cuid is the token id + * @param userid is the user id + * @param sDesKey is the des key provided by the TKS for key encryption + * @param b64cert is the Base64 encoding of a certificate used to recover + * + * @returns KRARecoverKeyResponse + */ + public KRARecoverKeyResponse recoverKey( + String cuid, + String userid, + String sDesKey, + String b64cert) + throws EBaseException { + + CMS.debug("KRARemoteRequestHandler: recoverKey(): begins."); + if (cuid == null || userid == null || sDesKey == null || b64cert == null) { + throw new EBaseException("KRARemoteRequestHandler: recoverKey(): input parameter null."); + } + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("KRARemoteRequestHandler: recoverKey(): sending request to CA"); + + HttpResponse resp = + conn.send("TokenKeyRecovery", + "&" + IRemoteRequest.TOKEN_CUID + "=" + + cuid + + "&" + IRemoteRequest.KRA_UserId + "=" + + userid + + "&" + IRemoteRequest.KRA_RECOVERY_CERT + "=" + + b64cert + + "&" + IRemoteRequest.KRA_Trans_DesKey + "=" + + sDesKey); + + String content = resp.getContent(); + + CMS.debug("KRARemoteRequestHandler: recoverKey(): got content = " + content); + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + + /** + * When a value is not found in response, keep going so we know + * what else is missing + * Note: response values "missing" might not be bad for some cases + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + + CMS.debug("KRARemoteRequestHandler: recoverKey(): got status = " + value); + ist = Integer.parseInt(value); + if (ist != 0) { + CMS.debug("KRARemoteRequestHandler: recoverKey(): status not 0, getting error string... "); + value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " + + IRemoteRequest.RESPONSE_ERROR_STRING); + } else { + CMS.debug("KRARemoteRequestHandler: recoverKey(): got IRemoteRequest.RESPONSE_ERROR_STRING = " + + value); + response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value); + } + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = (String) response.get(IRemoteRequest.KRA_RESPONSE_PublicKey); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " + + IRemoteRequest.KRA_RESPONSE_PublicKey); + } else { + CMS.debug("KRARemoteRequestHandler:recoverKey(): got IRemoteRequest.KRA_RESPONSE_PublicKey= " + value); + response.put(IRemoteRequest.KRA_RESPONSE_PublicKey, value); + } + + value = (String) response.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " + + IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey); + } else { + CMS.debug("KRARemoteRequestHandler:recoverKey(): got IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey= " + + value); + response.put(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey, value); + } + + value = (String) response.get(IRemoteRequest.KRA_RESPONSE_IV_Param); + if (value == null) { + CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " + + IRemoteRequest.KRA_RESPONSE_IV_Param); + } else { + CMS.debug("KRARemoteRequestHandler:recoverKey(): got IRemoteRequest.KRA_RESPONSE_IV_Param= " + value); + response.put(IRemoteRequest.KRA_RESPONSE_IV_Param, value); + } + + CMS.debug("KRARemoteRequestHandler: recoverKey(): ends."); + return new KRARecoverKeyResponse(response); + } else { + CMS.debug("KRARemoteRequestHandler: recoverKey(): no response content."); + throw new EBaseException("KRARemoteRequestHandler: recoverKey(): no response content."); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/KRAServerSideKeyGenResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/KRAServerSideKeyGenResponse.java new file mode 100644 index 000000000..1836bcdbd --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/KRAServerSideKeyGenResponse.java @@ -0,0 +1,51 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * KRAServerSideKeyGenResponse is the class for the response to + * KRA Remote Request: serverSideKeyGen() + * + */ +public class KRAServerSideKeyGenResponse extends RemoteResponse +{ + public KRAServerSideKeyGenResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public String getErrorString() { + return (String) nameValTable.get(IRemoteRequest.RESPONSE_ERROR_STRING); + } + + public String getPublicKey() { + return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_PublicKey); + } + + public String getWrappedPrivKey() { + return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey); + } + + public String getIVParam() { + return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_IV_Param); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/RemoteRequestHandler.java b/base/tps/src/org/dogtagpki/server/tps/cms/RemoteRequestHandler.java new file mode 100644 index 000000000..ceed1c11c --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/RemoteRequestHandler.java @@ -0,0 +1,91 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.io.ByteArrayInputStream; +import java.util.Hashtable; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.cmsutil.xml.XMLObject; + +/** + * RemoteRequestHandler is the base class for the remote authorities + * + * @author cfu + */ +public abstract class RemoteRequestHandler +{ + private static final String RESPONSE_SEPARATOR = "\\&"; + private static final String NAME_VALUE_EQUAL = "="; + + protected String connid; + + /** + * parseResponse parses remote responses that take the form of '&'-separated + * name-value pairs + * + * @param content The exact string content in the HTTP response + * @return name-value pairs in a Hashtable + */ + protected Hashtable<String, Object> parseResponse(String content) + throws EBaseException { + CMS.debug("RemoteRequestHandler: parseResponse(): begins:"); + if (content == null) { + throw new EBaseException("RemoteRequestHandler: parserResponse(): no response content."); + } + Hashtable<String, Object> vars = new Hashtable<String, Object>(); + String[] elements = content.split(RESPONSE_SEPARATOR); + CMS.debug("RemoteRequestHandler: parseResponse(): # of elements:" + + elements.length); + for (String nvs : elements) { + String[] nv = nvs.split(NAME_VALUE_EQUAL); + if (nv.length == 2) { + vars.put(nv[0], nv[1]); + } else { + // continue to parse through + CMS.debug("RemoteRequestHandler: parseResponse(): content contains element not conforming to <name>=<value>."); + } + } + return vars; + } + + /** + * Get the XML parser for XML in text + * + * @param text XML in text + * @return XMLObject the parser + */ + protected XMLObject getXMLparser(String text) { + if (text == null) { + return null; + } else { + CMS.debug("RemoteRequestHandler: getXMLparser(): parsing: " + text); + } + try { + ByteArrayInputStream bis = + new ByteArrayInputStream(text.getBytes()); + return new XMLObject(bis); + } catch (Exception e) { + CMS.debug("RemoteRequestHandler: getXMLparser(): failed: " + e); + throw new RuntimeException(e); + } + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/RemoteResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/RemoteResponse.java new file mode 100644 index 000000000..c2c7818b0 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/RemoteResponse.java @@ -0,0 +1,40 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * RemoteResponse is the base class for remote requests + * + */ +public abstract class RemoteResponse +{ + protected Hashtable<String, Object> nameValTable; + + public int getStatus() { + Integer iValue = (Integer) nameValTable.get(IRemoteRequest.RESPONSE_STATUS); + if (iValue == null) + return IRemoteRequest.RESPONSE_STATUS_NOT_FOUND; + else + return iValue.intValue(); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeRandomDataResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeRandomDataResponse.java new file mode 100644 index 000000000..f241c88ad --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeRandomDataResponse.java @@ -0,0 +1,41 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; +import org.dogtagpki.tps.main.TPSBuffer; + +/** + * TKSComputeRandomDataResponse is the class for the response to + * TKS Remote Request: computeRandomData() + * + */ +public class TKSComputeRandomDataResponse extends RemoteResponse +{ + public TKSComputeRandomDataResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public TPSBuffer getRandomData() { + byte [] random = (byte[]) nameValTable.get(IRemoteRequest.TKS_RESPONSE_RandomData); + return new TPSBuffer(random); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java new file mode 100644 index 000000000..2fe382539 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java @@ -0,0 +1,65 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; +import org.dogtagpki.tps.main.TPSBuffer; + +/** + * TKSComputeSessionKeyResponse is the class for the response to + * TKS Remote Request: computeSessionKey() + * + */ +public class TKSComputeSessionKeyResponse extends RemoteResponse +{ + + public TKSComputeSessionKeyResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public TPSBuffer getKeySetData() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_KeySetData); + } + + public TPSBuffer getSessionKey() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_SessionKey); + } + + public TPSBuffer getEncSessionKey() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_EncSessionKey); + } + + public TPSBuffer getDRM_Trans_DesKey() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey); + } + + public TPSBuffer getKeyCheck() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_KeyCheck); + } + + public TPSBuffer getHostCryptogram() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_HostCryptogram); + } + + public TPSBuffer getKekWrappedDesKey() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_KEK_DesKey); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSCreateKeySetDataResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSCreateKeySetDataResponse.java new file mode 100644 index 000000000..f887c63bb --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSCreateKeySetDataResponse.java @@ -0,0 +1,41 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; +import org.dogtagpki.tps.main.TPSBuffer; + +/** + * TKSCreateKeySetDataResponse is the class for the response to + * TKS Remote Request: createKeySetData + * + */ +public class TKSCreateKeySetDataResponse extends RemoteResponse +{ + + public TKSCreateKeySetDataResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public TPSBuffer getKeySetData() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_KeySetData); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSEncryptDataResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSEncryptDataResponse.java new file mode 100644 index 000000000..7dba17148 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSEncryptDataResponse.java @@ -0,0 +1,41 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; +import org.dogtagpki.tps.main.TPSBuffer; + +/** + * TKSEncryptDataResponse is the class for the response to + * TKS Remote Request: encryptData + * + */ +public class TKSEncryptDataResponse extends RemoteResponse +{ + + public TKSEncryptDataResponse(Hashtable<String, Object> ht) { + nameValTable = ht; + } + + public TPSBuffer getEncryptedData() { + return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_EncryptedData); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java new file mode 100644 index 000000000..3c371a45e --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java @@ -0,0 +1,448 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.Util; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.cmscore.connector.HttpConnector; +import com.netscape.cmsutil.http.HttpResponse; + +/** + * TKSRemoteRequestHandler is a class representing remote requests + * offered by the Token Key Service Authority (TKS) + * On a successful return, name/value pairs are provided in a Hashtable where + * all contents are URL decoded if needed + * + * @author cfu + */ +public class TKSRemoteRequestHandler extends RemoteRequestHandler +{ + public TKSRemoteRequestHandler(String connID) + throws EBaseException { + + if (connID == null) { + throw new EBaseException("TKSRemoteRequestHandler: TKSRemoteRequestHandler(): connID null."); + } + + connid = connID; + } + + /* + * computeSessionKey + * + * Usage Example: + * TKSRemoteRequestHandler tksReq = new TKSRemoteRequestHandler("tks1"); + * TKSComputeSessionKeyResponse responseObj = + * tksReq.computeSessionKey( + * cuid, + * keyInfo, + * card_challenge, + * card_cryptogram, + * host_challenge); + * - on success return, one can say + * TPSBuffer value = responseObj.getSessionKey(); + * to get response param value session key + * + * @param cuid token cuid + * @param keyInfo keyInfo + * @param card_challenge card challenge + * @param card_cryptogram card cryptogram + * @param host_challenge host challenge + * @return response TKSComputeSessionKeyResponse class object + */ + public TKSComputeSessionKeyResponse computeSessionKey( + TPSBuffer cuid, + TPSBuffer keyInfo, + TPSBuffer card_challenge, + TPSBuffer card_cryptogram, + TPSBuffer host_challenge, + String tokenType) + throws EBaseException { + + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): begins."); + if (cuid == null || keyInfo == null || card_challenge == null + || card_cryptogram == null || host_challenge == null) { + throw new EBaseException("TKSRemoteRequestHandler: computeSessionKey(): input parameter null."); + } + + IConfigStore conf = CMS.getConfigStore(); + + boolean serverKeygen = + conf.getBoolean("op.enroll." + + tokenType + ".keyGen.encryption.serverKeygen.enable", + false); + String keySet = + conf.getString("connector." + connid + "keySet", "defKeySet"); + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + + String requestString = IRemoteRequest.SERVER_SIDE_KEYGEN + "=" + serverKeygen + + "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) + + "&" + IRemoteRequest.TOKEN_CARD_CHALLENGE + "=" + Util.specialURLEncode(card_challenge) + + "&" + IRemoteRequest.TOKEN_HOST_CHALLENGE + "=" + Util.specialURLEncode(host_challenge) + + "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(keyInfo) + + "&" + IRemoteRequest.TOKEN_CARD_CRYPTOGRAM + "=" + + Util.specialURLEncode(card_cryptogram.toBytesArray()) + + "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet; + + CMS.debug("TKSRemoteRequestHandler.computeSessionKey: outgoing message: " + requestString); + + HttpResponse resp = + conn.send("computeSessionKey", + requestString + ); + + String content = resp.getContent(); + + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + + /* + * When a value is not found in response, keep going so we know + * what else is missing + * Note: serverKeygen and !serverKeygen returns different set of + * response values so "missing" might not be bad + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): status not found."); + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got content = " + content); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got status = " + value); + ist = Integer.parseInt(value); + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_SessionKey); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_SessionKey); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got IRemoteRequest.TKS_RESPONSE_SessionKey = "); + response.put(IRemoteRequest.TKS_RESPONSE_SessionKey, Util.specialDecode(value)); + } + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_EncSessionKey); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_EncSessionKey); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got IRemoteRequest.TKS_RESPONSE_EncSessionKey = "); + response.put(IRemoteRequest.TKS_RESPONSE_EncSessionKey, Util.specialDecode(value)); + } + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey = "); + response.put(IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey, Util.specialDecode(value)); + } + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_KEK_DesKey); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_KEK_DesKey); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got IRemoteRequest.TKS_RESPONSE_KEK_DesKey = "); + response.put(IRemoteRequest.TKS_RESPONSE_KEK_DesKey, Util.specialDecode(value)); + } + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_KeyCheck); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_KeyCheck); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got IRemoteRequest.TKS_RESPONSE_KeyCheck = "); + response.put(IRemoteRequest.TKS_RESPONSE_KeyCheck, Util.specialDecode(value)); + } + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_HostCryptogram); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_HostCryptogram); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got IRemoteRequest.TKS_RESPONSE_HostCryptogram = "); + response.put(IRemoteRequest.TKS_RESPONSE_HostCryptogram, Util.specialDecode(value)); + } + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): ends."); + + return new TKSComputeSessionKeyResponse(response); + } else { + CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): no response content."); + throw new EBaseException("TKSRemoteRequestHandler: computeSessionKey(): no response content."); + } + } + + /* + * createKeySetData + * + * Usage Example: + * TKSRemoteRequestHandler tksReq = new TKSRemoteRequestHandler("tks1"); + * TKSCreateKeySetDataResponse responseObj = + * tksReq.createKeySetData( + * NewMasterVer, + * version, + * cuid) + * - on success return, one can say + * TPSBuffer value = responseObj.getKeySetData(); + * to get response param value keySetData + * + * @param NewMasterVer newKeyInfo + * @param version keyInfo + * @param cuid token cuid + * @return response TKSCreateKeySetDataResponse class object + */ + public TKSCreateKeySetDataResponse createKeySetData( + TPSBuffer NewMasterVer, + TPSBuffer version, + TPSBuffer cuid) + throws EBaseException { + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): begins."); + if (cuid == null || NewMasterVer == null || version == null) { + throw new EBaseException("TKSRemoteRequestHandler: createKeySetData(): input parameter null."); + } + + IConfigStore conf = CMS.getConfigStore(); + String keySet = + conf.getString("connector." + connid + "keySet", "defKeySet"); + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): sending request to tks."); + HttpResponse resp = + conn.send("createKeySetData", + IRemoteRequest.TOKEN_NEW_KEYINFO + "=" + Util.specialURLEncode(NewMasterVer) + + "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) + + "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(version) + + "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet); + + String content = resp.getContent(); + + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + if (response == null) { + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): parseResponse returned null."); + return null; + } + + /* + * When a value is not found in response, keep going so we know + * what else is missing + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): status not found."); + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): got content = " + content); + } else { + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): got status = " + value); + ist = Integer.parseInt(value); + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_KeySetData); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_KeySetData); + } else { + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): got IRemoteRequest.TKS_RESPONSE_KeySetData = "); + response.put(IRemoteRequest.TKS_RESPONSE_KeySetData, Util.specialDecode(value)); + } + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): ends."); + + return new TKSCreateKeySetDataResponse(response); + } else { + CMS.debug("TKSRemoteRequestHandler: createKeySetData(): no response content."); + throw new EBaseException("TKSRemoteRequestHandler: createKeySetData(): no response content."); + } + } + + /* + * computeRandomData + * + * Usage Example: + * TKSRemoteRequestHandler tksReq = new TKSRemoteRequestHandler("tks1"); + * TKSComputeRandomDataResponse responseObj = + * tksReq.computeRandomData( + * dataSize) + * - on success return, one can say + * TPSBuffer value = responseObj.getRandomData(); + * to get response param value random data + * + * @param dataSize size of random data to be generated in number of bytes + * @return response TKSComputeRandomDataResponse class object + */ + public TKSComputeRandomDataResponse computeRandomData(int dataSize) + throws EBaseException { + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): begins."); + /* + * check for absurd dataSize values + */ + if (dataSize <= 0 || dataSize > 1024) { + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): invalid dataSize requested:" + dataSize); + throw new EBaseException("TKSRemoteRequestHandler: computeRandomData(): invalid dataSize requested"); + } + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): sending request to tks."); + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + HttpResponse resp = + conn.send("computeRandomData", + IRemoteRequest.TOKEN_DATA_NUM_BYTES + "=" + dataSize); + + String content = resp.getContent(); + + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + + /* + * When a value is not found in response, keep going so we know + * what else is missing + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): status not found."); + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): got content = " + content); + } else { + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): got status = " + value); + ist = Integer.parseInt(value); + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_RandomData); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_RandomData); + } else { + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): got IRemoteRequest.TKS_RESPONSE_RandomData = " + + value); + response.put(IRemoteRequest.TKS_RESPONSE_RandomData, Util.uriDecodeFromHex(value)); + } + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): ends."); + + return new TKSComputeRandomDataResponse(response); + } else { + CMS.debug("TKSRemoteRequestHandler: computeRandomData(): no response content."); + throw new EBaseException("TKSRemoteRequestHandler: computeRandomData(): no response content."); + } + } + + /* + * encryptData + * + * Usage Example: + * TKSRemoteRequestHandler tksReq = new TKSRemoteRequestHandler("tks1"); + * TKSEncryptDataResponse responseObj = + * tksReq.encryptData( + * dataSize) + * - on success return, one can say + * TPSBuffer value = responseObj.getEncryptedData(); + * to get response param value encrypted data + * + * @param cuid token cuid + * @param version keyInfo + * @param inData data to be encrypted + * @return response TKSEncryptDataResponse class object + */ + public TKSEncryptDataResponse encryptData( + TPSBuffer cuid, + TPSBuffer version, + TPSBuffer inData) + throws EBaseException { + CMS.debug("TKSRemoteRequestHandler: encryptData(): begins."); + if (cuid == null || version == null || inData == null) { + throw new EBaseException("TKSRemoteRequestHandler: encryptData(): input parameter null."); + } + + IConfigStore conf = CMS.getConfigStore(); + + String keySet = + conf.getString("connector." + connid + "keySet", "defKeySet"); + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + HttpConnector conn = + (HttpConnector) subsystem.getConnectionManager().getConnector(connid); + CMS.debug("TKSRemoteRequestHandler: encryptData(): sending request to tks."); + HttpResponse resp = + conn.send("encryptData", + IRemoteRequest.TOKEN_DATA + "=" + Util.specialURLEncode(inData) + + "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) + + "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(version) + + "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet); + + String content = resp.getContent(); + + if (content != null && !content.equals("")) { + Hashtable<String, Object> response = + parseResponse(content); + + /* + * When a value is not found in response, keep going so we know + * what else is missing + */ + Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND); + String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: encryptData(): status not found."); + CMS.debug("TKSRemoteRequestHandler: encryptData(): got content = " + content); + } else { + CMS.debug("TKSRemoteRequestHandler: encryptData(): got status = " + value); + ist = Integer.parseInt(value); + } + response.put(IRemoteRequest.RESPONSE_STATUS, ist); + + value = (String) response.get(IRemoteRequest.TKS_RESPONSE_EncryptedData); + if (value == null) { + CMS.debug("TKSRemoteRequestHandler: encryptData(): response missing name-value pair for: " + + IRemoteRequest.TKS_RESPONSE_EncryptedData); + } else { + CMS.debug("TKSRemoteRequestHandler: encryptData(): got IRemoteRequest.TKS_RESPONSE_EncryptedData = "); + response.put(IRemoteRequest.TKS_RESPONSE_EncryptedData, Util.specialDecode(value)); + } + CMS.debug("TKSRemoteRequestHandler: encryptData(): ends."); + + return new TKSEncryptDataResponse(response); + } else { + CMS.debug("TKSRemoteRequestHandler: encryptData(): no response content."); + throw new EBaseException("TKSRemoteRequestHandler: encryptData(): no response content."); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSResponse.java new file mode 100644 index 000000000..37269c765 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSResponse.java @@ -0,0 +1,40 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.cms; + +import java.util.Hashtable; + +import org.dogtagpki.server.connector.IRemoteRequest; + +/** + * TKSResponse is the base class for TKS remote requests + * + */ +public abstract class TKSResponse +{ + protected Hashtable<String, Object> nameValTable; + + public int getStatus() { + Integer iValue = (Integer) nameValTable.get(IRemoteRequest.RESPONSE_STATUS); + if (iValue == null) + return IRemoteRequest.RESPONSE_STATUS_NOT_FOUND; + else + return iValue.intValue(); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/AuthenticatorDatabase.java b/base/tps/src/org/dogtagpki/server/tps/config/AuthenticatorDatabase.java new file mode 100644 index 000000000..292b80afb --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/AuthenticatorDatabase.java @@ -0,0 +1,143 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.cmscore.dbs.CSCfgDatabase; + +/** + * This class provides access to the authenticators in CS.cfg. + * + * @author Endi S. Dewata + */ +public class AuthenticatorDatabase extends CSCfgDatabase<AuthenticatorRecord> { + + public AuthenticatorDatabase() { + super("Authenticator", "Authentication_Sources"); + } + + public AuthenticatorRecord createAuthenticatorRecord(ConfigDatabase configDatabase, ConfigRecord configRecord, String authenticatorID) throws EBaseException { + AuthenticatorRecord authenticatorRecord = new AuthenticatorRecord(); + authenticatorRecord.setID(authenticatorID); + + String status = getRecordStatus(authenticatorID); + authenticatorRecord.setStatus(status); + + Map<String, String> properties = configDatabase.getProperties(configRecord, authenticatorID); + authenticatorRecord.setProperties(properties); + return authenticatorRecord; + } + + @Override + public Collection<AuthenticatorRecord> findRecords(String filter) throws Exception { + + Collection<AuthenticatorRecord> result = new ArrayList<AuthenticatorRecord>(); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + for (String authenticatorID : configRecord.getKeys()) { + if (filter != null && !authenticatorID.contains(filter)) continue; + AuthenticatorRecord authenticatorRecord = createAuthenticatorRecord(configDatabase, configRecord, authenticatorID); + result.add(authenticatorRecord); + } + + return result; + } + + @Override + public AuthenticatorRecord getRecord(String authenticatorID) throws Exception { + + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + return createAuthenticatorRecord(configDatabase, configRecord, authenticatorID); + } + + @Override + public void addRecord(String authenticatorID, AuthenticatorRecord authenticatorRecord) throws Exception { + + CMS.debug("AuthenticatorDatabase.addRecord(\"" + authenticatorID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = authenticatorRecord.getProperties(); + configDatabase.validateProperties(configRecord, authenticatorID, properties); + + // add new connection + configRecord.addKey(authenticatorID); + configDatabase.updateRecord(substoreName, configRecord); + + // store new properties + configDatabase.addProperties(configRecord, authenticatorID, properties); + + // create status + setRecordStatus(authenticatorID, authenticatorRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void updateRecord(String authenticatorID, AuthenticatorRecord authenticatorRecord) throws Exception { + + CMS.debug("AuthenticatorDatabase.updateRecord(\"" + authenticatorID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = authenticatorRecord.getProperties(); + configDatabase.validateProperties(configRecord, authenticatorID, properties); + + // remove old properties + configDatabase.removeProperties(configRecord, authenticatorID); + + // add new properties + configDatabase.addProperties(configRecord, authenticatorID, properties); + + // update status + setRecordStatus(authenticatorID, authenticatorRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void removeRecord(String authenticatorID) throws Exception { + + CMS.debug("AuthenticatorDatabase.removeRecord(\"" + authenticatorID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // remove properties + configDatabase.removeProperties(configRecord, authenticatorID); + + // remove connection + configRecord.removeKey(authenticatorID); + configDatabase.updateRecord(substoreName, configRecord); + + // remove status + removeRecordStatus(authenticatorID); + + configDatabase.commit(); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/AuthenticatorRecord.java b/base/tps/src/org/dogtagpki/server/tps/config/AuthenticatorRecord.java new file mode 100644 index 000000000..937a25e51 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/AuthenticatorRecord.java @@ -0,0 +1,28 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import com.netscape.cmscore.dbs.CSCfgRecord; + + +/** + * @author Endi S. Dewata + */ +public class AuthenticatorRecord extends CSCfgRecord { +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ConfigDatabase.java b/base/tps/src/org/dogtagpki/server/tps/config/ConfigDatabase.java new file mode 100644 index 000000000..db0fa9aa6 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ConfigDatabase.java @@ -0,0 +1,191 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.commons.lang.StringUtils; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.ResourceNotFoundException; +import com.netscape.cmscore.dbs.Database; + +/** + * This class implements in-memory connection database. In the future this + * will be replaced with LDAP database. + * + * @author Endi S. Dewata + */ +public class ConfigDatabase extends Database<ConfigRecord> { + + IConfigStore configStore = CMS.getConfigStore(); + + public ConfigDatabase() throws EBaseException { + super("Configuration"); + } + + public String createFilter(ConfigRecord record, String key) { + String pattern = record.getPattern(); + if (key == null) return pattern; + return pattern.replace("$name", key); + } + + @Override + public Collection<ConfigRecord> findRecords(String filter) throws Exception { + + CMS.debug("ConfigDatabase.findRecords()"); + + Collection<ConfigRecord> result = new ArrayList<ConfigRecord>(); + + Collection<String> configIDs = new LinkedHashSet<String>(); + configIDs.add("Generals"); + + String list = configStore.get("target.configure.list"); + if (list != null) { + configIDs.addAll(Arrays.asList(list.split(","))); + } + + list = configStore.get("target.agent_approve.list"); + if (list != null) { + configIDs.addAll(Arrays.asList(list.split(","))); + } + + for (String configID : configIDs) { + if (filter != null && !configID.contains(filter)) continue; + ConfigRecord configData = getRecord(configID); + result.add(configData); + } + + return result; + } + + @Override + public ConfigRecord getRecord(String configID) throws Exception { + + CMS.debug("ConfigDatabase.getRecord(\"" + configID + "\")"); + + ConfigRecord record = new ConfigRecord(); + record.setID(configID); + + String displayName = configStore.get("target." + configID + ".displayname"); + if (StringUtils.isEmpty(displayName)) { + throw new ResourceNotFoundException("Configuration " + configID + " not found."); + } + record.setDisplayName(displayName); + + String pattern = configStore.get("target." + configID + ".pattern"); + if (StringUtils.isEmpty(pattern)) { + throw new ResourceNotFoundException("Missing pattern for " + configID + " configuration."); + } + + // replace \| with | + record.setPattern(pattern.replace("\\|", "|")); + + String list = configStore.get("target." + configID + ".list"); + if (!StringUtils.isEmpty(list)) { + record.setKeys(Arrays.asList(list.split(","))); + } + + return record; + } + + + @Override + public void updateRecord(String configID, ConfigRecord newRecord) throws Exception { + + CMS.debug("ConfigDatabase.updateRecord(\"" + configID + "\")"); + + configStore.put("target." + configID + ".displayname", newRecord.getDisplayName()); + configStore.put("target." + configID + ".pattern", newRecord.getPattern()); + configStore.put("target." + configID + ".list", StringUtils.join(newRecord.getKeys(), ",")); + + configStore.commit(true); + } + + public Map<String, String> getProperties(ConfigRecord record, String key) throws EBaseException { + + CMS.debug("ConfigDatabase.getProperties(\"" + record.getID() + "\", \"" + key + "\")"); + + if (key != null && !record.containsKey(key)) { + throw new ResourceNotFoundException("Entry does not exist: " + key); + } + + Map<String, String> properties = new TreeMap<String, String>(); + + // get properties that match the filter + String filter = createFilter(record, key); + Map<String, String> map = configStore.getProperties(); + for (String name : map.keySet()) { + if (!name.matches(filter)) continue; + + String value = map.get(name); + properties.put(name, value); + } + + return properties; + } + + public void validateProperties(ConfigRecord record, String key, Map<String, String> properties) throws Exception { + + CMS.debug("ConfigDatabase.validateProperties(\"" + record.getID() + "\")"); + + String filter = createFilter(record, key); + for (String name : properties.keySet()) { + if (name.matches(filter)) continue; + CMS.debug("Property " + name + " doesn't match filter " + filter + "."); + throw new BadRequestException("Invalid property: " + name); + } + } + + public void addProperties(ConfigRecord record, String key, Map<String, String> properties) throws Exception { + + CMS.debug("ConfigDatabase.addProperties(\"" + record.getID() + "\")"); + + for (String name : properties.keySet()) { + String value = properties.get(name); + configStore.put(name, value); + } + } + + public void removeProperties(ConfigRecord record, String key) throws Exception { + + CMS.debug("ConfigDatabase.removeProperties(\"" + record.getID() + "\")"); + + Map<String, String> oldProperties = getProperties(record, key); + for (String name : oldProperties.keySet()) { + configStore.remove(name); + } + } + + public void commit() throws Exception { + + CMS.debug("ConfigDatabase.commit()"); + + // save configuration + configStore.commit(true); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ConfigRecord.java b/base/tps/src/org/dogtagpki/server/tps/config/ConfigRecord.java new file mode 100644 index 000000000..f1a87ddfe --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ConfigRecord.java @@ -0,0 +1,126 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import java.util.Collection; +import java.util.LinkedHashSet; + +import com.netscape.certsrv.base.ConflictingOperationException; +import com.netscape.certsrv.base.ResourceNotFoundException; + +/** + * @author Endi S. Dewata + */ +public class ConfigRecord { + + String id; + String displayName; + String pattern; + Collection<String> keys = new LinkedHashSet<String>(); + + public String getID() { + return id; + } + + public void setID(String id) { + this.id = id; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public Collection<String> getKeys() { + return keys; + } + + public void setKeys(Collection<String> keys) { + this.keys.clear(); + this.keys.addAll(keys); + } + + public boolean containsKey(String key) { + return keys.contains(key); + } + + public void addKey(String key) { + if (keys.contains(key)) throw new ConflictingOperationException("Entry already exists: " + key); + keys.add(key); + } + + public void removeKey(String key) { + if (!keys.contains(key)) throw new ResourceNotFoundException("Entry does not exist: " + key); + keys.remove(key); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((keys == null) ? 0 : keys.hashCode()); + result = prime * result + ((pattern == null) ? 0 : pattern.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ConfigRecord other = (ConfigRecord) obj; + if (displayName == null) { + if (other.displayName != null) + return false; + } else if (!displayName.equals(other.displayName)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (keys == null) { + if (other.keys != null) + return false; + } else if (!keys.equals(other.keys)) + return false; + if (pattern == null) { + if (other.pattern != null) + return false; + } else if (!pattern.equals(other.pattern)) + return false; + return true; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ConfigService.java b/base/tps/src/org/dogtagpki/server/tps/config/ConfigService.java new file mode 100644 index 000000000..6cd5e9f7d --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ConfigService.java @@ -0,0 +1,135 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.tps.config.ConfigData; +import com.netscape.certsrv.tps.config.ConfigResource; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class ConfigService extends PKIService implements ConfigResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public ConfigService() { + CMS.debug("ConfigService.<init>()"); + } + + public ConfigData createConfigData(Map<String, String> properties) throws UnsupportedEncodingException { + + ConfigData configData = new ConfigData(); + configData.setProperties(properties); + + URI uri = uriInfo.getBaseUriBuilder().path(ConfigResource.class).build(); + configData.setLink(new Link("self", uri)); + + return configData; + } + + @Override + public Response getConfig() { + + CMS.debug("ConfigService.getConfig()"); + + try { + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord("Generals"); + + Map<String, String> properties = configDatabase.getProperties(configRecord, null); + + return createOKResponse(createConfigData(properties)); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response updateConfig(ConfigData configData) { + + if (configData == null) throw new BadRequestException("Config data is null."); + + CMS.debug("ConfigService.updateConfig()"); + + try { + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord("Generals"); + + Map<String, String> properties = configData.getProperties(); + if (properties != null) { + // validate new properties + configDatabase.validateProperties(configRecord, null, properties); + + // remove old properties + configDatabase.removeProperties(configRecord, null); + + // add new properties + configDatabase.addProperties(configRecord, null, properties); + } + + configDatabase.commit(); + + properties = configDatabase.getProperties(configRecord, null); + configData = createConfigData(properties); + + return Response + .ok(configData) + .build(); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ConnectorDatabase.java b/base/tps/src/org/dogtagpki/server/tps/config/ConnectorDatabase.java new file mode 100644 index 000000000..4b1589797 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ConnectorDatabase.java @@ -0,0 +1,242 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.cmscore.dbs.CSCfgDatabase; + +/** + * This class provides access to the connections in CS.cfg. + * + * @author Endi S. Dewata + */ +public class ConnectorDatabase extends CSCfgDatabase<ConnectorRecord> { + + public String prefix = "tps.connector"; + + public ConnectorDatabase() { + super("Connector", "Subsystem_Connections"); + } + + public ConnectorRecord createConnectorRecord(ConfigDatabase configDatabase, ConfigRecord configRecord, String connectorID) throws EBaseException { + ConnectorRecord connectorRecord = new ConnectorRecord(); + connectorRecord.setID(connectorID); + + String status = getRecordStatus(connectorID); + connectorRecord.setStatus(status); + + Map<String, String> properties = configDatabase.getProperties(configRecord, connectorID); + connectorRecord.setProperties(properties); + return connectorRecord; + } + + @Override + public Collection<ConnectorRecord> findRecords(String filter) throws Exception { + + Collection<ConnectorRecord> result = new ArrayList<ConnectorRecord>(); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + for (String connectorID : configRecord.getKeys()) { + if (filter != null && !connectorID.contains(filter)) continue; + ConnectorRecord connectorRecord = createConnectorRecord(configDatabase, configRecord, connectorID); + result.add(connectorRecord); + } + + return result; + } + + @Override + public ConnectorRecord getRecord(String connectionID) throws Exception { + + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + return createConnectorRecord(configDatabase, configRecord, connectionID); + } + + @Override + public void addRecord(String connectorID, ConnectorRecord connectorRecord) throws Exception { + + CMS.debug("ConnectorDatabase.addRecord(\"" + connectorID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = connectorRecord.getProperties(); + configDatabase.validateProperties(configRecord, connectorID, properties); + + // add new connector + configRecord.addKey(connectorID); + configDatabase.updateRecord(substoreName, configRecord); + + // store new properties + configDatabase.addProperties(configRecord, connectorID, properties); + + // create status + setRecordStatus(connectorID, connectorRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void updateRecord(String connectorID, ConnectorRecord connectorRecord) throws Exception { + + CMS.debug("ConnectorDatabase.updateRecord(\"" + connectorID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = connectorRecord.getProperties(); + configDatabase.validateProperties(configRecord, connectorID, properties); + + // remove old properties + configDatabase.removeProperties(configRecord, connectorID); + + // add new properties + configDatabase.addProperties(configRecord, connectorID, properties); + + // update status + setRecordStatus(connectorID, connectorRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void removeRecord(String connectorID) throws Exception { + + CMS.debug("ConnectorDatabase.removeRecord(\"" + connectorID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // remove properties + configDatabase.removeProperties(configRecord, connectorID); + + // remove connector + configRecord.removeKey(connectorID); + configDatabase.updateRecord(substoreName, configRecord); + + // remove status + removeRecordStatus(connectorID); + + configDatabase.commit(); + } + + public String getNextID(String type) throws Exception { + + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + Collection<String> keys = configRecord.getKeys(); + + String id; + int n = 1; + + while (true) { + id = type + n; + + if (keys.contains(id)) { + // ID is already used, find the next one. + n++; + + } else { + // ID is available, use this one. + break; + } + } + + return id; + } + + public void addCAConnector(String hostname, Integer port, String nickname) throws Exception { + + String id = getNextID("ca"); + + ConnectorRecord record = new ConnectorRecord(); + record.setID(id); + record.setStatus("Enabled"); + + record.setProperty(prefix + "." + id + ".enable", "true"); + record.setProperty(prefix + "." + id + ".host", hostname); + record.setProperty(prefix + "." + id + ".port", port.toString()); + record.setProperty(prefix + "." + id + ".minHttpConns", "1"); + record.setProperty(prefix + "." + id + ".maxHttpConns", "15"); + record.setProperty(prefix + "." + id + ".nickName", nickname); + record.setProperty(prefix + "." + id + ".timeout", "30"); + record.setProperty(prefix + "." + id + ".uri.enrollment", "/ca/ee/ca/profileSubmitSSLClient"); + record.setProperty(prefix + "." + id + ".uri.getcert", "/ca/ee/ca/displayBySerial"); + record.setProperty(prefix + "." + id + ".uri.renewal", "/ca/ee/ca/profileSubmitSSLClient"); + record.setProperty(prefix + "." + id + ".uri.revoke", "/ca/ee/subsystem/ca/doRevoke"); + record.setProperty(prefix + "." + id + ".uri.unrevoke", "/ca/ee/subsystem/ca/doUnrevoke"); + + addRecord(id, record); + } + + public void addKRAConnector(String hostname, Integer port, String nickname) throws Exception { + + String id = getNextID("kra"); + + ConnectorRecord record = new ConnectorRecord(); + record.setID(id); + record.setStatus("Enabled"); + + record.setProperty(prefix + "." + id + ".enable", "true"); + record.setProperty(prefix + "." + id + ".host", hostname); + record.setProperty(prefix + "." + id + ".port", port.toString()); + record.setProperty(prefix + "." + id + ".minHttpConns", "1"); + record.setProperty(prefix + "." + id + ".maxHttpConns", "15"); + record.setProperty(prefix + "." + id + ".nickName", nickname); + record.setProperty(prefix + "." + id + ".timeout", "30"); + record.setProperty(prefix + "." + id + ".uri.GenerateKeyPair", "/kra/agent/kra/GenerateKeyPair"); + record.setProperty(prefix + "." + id + ".uri.TokenKeyRecovery", "/kra/agent/kra/TokenKeyRecovery"); + + addRecord(id, record); + } + + public void addTKSConnector(String hostname, Integer port, String nickname, Boolean keygen) throws Exception { + + String id = getNextID("tks"); + + ConnectorRecord record = new ConnectorRecord(); + record.setID(id); + record.setStatus("Enabled"); + + record.setProperty(prefix + "." + id + ".enable", "true"); + record.setProperty(prefix + "." + id + ".host", hostname); + record.setProperty(prefix + "." + id + ".port", port.toString()); + record.setProperty(prefix + "." + id + ".minHttpConns", "1"); + record.setProperty(prefix + "." + id + ".maxHttpConns", "15"); + record.setProperty(prefix + "." + id + ".nickName", nickname); + record.setProperty(prefix + "." + id + ".timeout", "30"); + record.setProperty(prefix + "." + id + ".generateHostChallenge", "true"); + record.setProperty(prefix + "." + id + ".serverKeygen", keygen.toString()); + record.setProperty(prefix + "." + id + ".keySet", "defKeySet"); + record.setProperty(prefix + "." + id + ".tksSharedSymKeyName", "sharedSecret"); + record.setProperty(prefix + "." + id + ".uri.computeRandomData", "/tks/agent/tks/computeRandomData"); + record.setProperty(prefix + "." + id + ".uri.computeSessionKey", "/tks/agent/tks/computeSessionKey"); + record.setProperty(prefix + "." + id + ".uri.createKeySetData", "/tks/agent/tks/createKeySetData"); + record.setProperty(prefix + "." + id + ".uri.encryptData", "/tks/agent/tks/encryptData"); + + addRecord(id, record); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ConnectorRecord.java b/base/tps/src/org/dogtagpki/server/tps/config/ConnectorRecord.java new file mode 100644 index 000000000..3123718b1 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ConnectorRecord.java @@ -0,0 +1,28 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import com.netscape.cmscore.dbs.CSCfgRecord; + + +/** + * @author Endi S. Dewata + */ +public class ConnectorRecord extends CSCfgRecord { +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ProfileDatabase.java b/base/tps/src/org/dogtagpki/server/tps/config/ProfileDatabase.java new file mode 100644 index 000000000..01225d309 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ProfileDatabase.java @@ -0,0 +1,144 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.cmscore.dbs.CSCfgDatabase; + +/** + * This class provides access to the profiles in CS.cfg. + * + * @author Endi S. Dewata + */ +public class ProfileDatabase extends CSCfgDatabase<ProfileRecord> { + + public ProfileDatabase() { + super("Profile", "Profiles"); + } + + public ProfileRecord createProfileRecord(ConfigDatabase configDatabase, ConfigRecord configRecord, String profileID) throws EBaseException { + ProfileRecord profileRecord = new ProfileRecord(); + profileRecord.setID(profileID); + + String status = getRecordStatus(profileID); + profileRecord.setStatus(status); + + Map<String, String> properties = configDatabase.getProperties(configRecord, profileID); + profileRecord.setProperties(properties); + + return profileRecord; + } + + @Override + public Collection<ProfileRecord> findRecords(String filter) throws Exception { + + Collection<ProfileRecord> result = new ArrayList<ProfileRecord>(); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + for (String profileID : configRecord.getKeys()) { + if (filter != null && !profileID.contains(filter)) continue; + ProfileRecord profileRecord = createProfileRecord(configDatabase, configRecord, profileID); + result.add(profileRecord); + } + + return result; + } + + @Override + public ProfileRecord getRecord(String profileID) throws Exception { + + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + return createProfileRecord(configDatabase, configRecord, profileID); + } + + @Override + public void addRecord(String profileID, ProfileRecord profileRecord) throws Exception { + + CMS.debug("ProfileDatabase.addRecord(\"" + profileID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = profileRecord.getProperties(); + configDatabase.validateProperties(configRecord, profileID, properties); + + // add new profile + configRecord.addKey(profileID); + configDatabase.updateRecord(substoreName, configRecord); + + // store new properties + configDatabase.addProperties(configRecord, profileID, properties); + + // create status + setRecordStatus(profileID, profileRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void updateRecord(String profileID, ProfileRecord profileRecord) throws Exception { + + CMS.debug("ProfileDatabase.updateRecord(\"" + profileID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = profileRecord.getProperties(); + configDatabase.validateProperties(configRecord, profileID, properties); + + // remove old properties + configDatabase.removeProperties(configRecord, profileID); + + // add new properties + configDatabase.addProperties(configRecord, profileID, properties); + + // update status + setRecordStatus(profileID, profileRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void removeRecord(String profileID) throws Exception { + + CMS.debug("ProfileDatabase.removeRecord(\"" + profileID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // remove properties + configDatabase.removeProperties(configRecord, profileID); + + // remove profile + configRecord.removeKey(profileID); + configDatabase.updateRecord(substoreName, configRecord); + + // remove status + removeRecordStatus(profileID); + + configDatabase.commit(); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ProfileMappingDatabase.java b/base/tps/src/org/dogtagpki/server/tps/config/ProfileMappingDatabase.java new file mode 100644 index 000000000..67a21d6ac --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ProfileMappingDatabase.java @@ -0,0 +1,144 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.cmscore.dbs.CSCfgDatabase; + +/** + * This class provides access to the profileMappings in CS.cfg. + * + * @author Endi S. Dewata + */ +public class ProfileMappingDatabase extends CSCfgDatabase<ProfileMappingRecord> { + + public ProfileMappingDatabase() { + super("Profile Mapping", "Profile_Mappings"); + } + + public ProfileMappingRecord createProfileMappingRecord(ConfigDatabase configDatabase, ConfigRecord configRecord, String profileMappingID) throws EBaseException { + ProfileMappingRecord profileMappingRecord = new ProfileMappingRecord(); + profileMappingRecord.setID(profileMappingID); + + String status = getRecordStatus(profileMappingID); + profileMappingRecord.setStatus(status); + + Map<String, String> properties = configDatabase.getProperties(configRecord, profileMappingID); + profileMappingRecord.setProperties(properties); + return profileMappingRecord; + } + + @Override + public Collection<ProfileMappingRecord> findRecords(String filter) throws Exception { + + Collection<ProfileMappingRecord> result = new ArrayList<ProfileMappingRecord>(); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + for (String profileMappingID : configRecord.getKeys()) { + if (filter != null && !profileMappingID.contains(filter)) continue; + ProfileMappingRecord profileMappingRecord = createProfileMappingRecord(configDatabase, configRecord, profileMappingID); + result.add(profileMappingRecord); + } + + return result; + } + + @Override + public ProfileMappingRecord getRecord(String profileMappingID) throws Exception { + + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + return createProfileMappingRecord(configDatabase, configRecord, profileMappingID); + } + + + @Override + public void addRecord(String profileMappingID, ProfileMappingRecord profileMappingRecord) throws Exception { + + CMS.debug("ProfileMappingDatabase.addRecord(\"" + profileMappingID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = profileMappingRecord.getProperties(); + configDatabase.validateProperties(configRecord, profileMappingID, properties); + + // add new profileMapping + configRecord.addKey(profileMappingID); + configDatabase.updateRecord(substoreName, configRecord); + + // store new properties + configDatabase.addProperties(configRecord, profileMappingID, properties); + + // create status + setRecordStatus(profileMappingID, profileMappingRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void updateRecord(String profileMappingID, ProfileMappingRecord profileMappingRecord) throws Exception { + + CMS.debug("ProfileMappingDatabase.updateRecord(\"" + profileMappingID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // validate new properties + Map<String, String> properties = profileMappingRecord.getProperties(); + configDatabase.validateProperties(configRecord, profileMappingID, properties); + + // remove old properties + configDatabase.removeProperties(configRecord, profileMappingID); + + // add new properties + configDatabase.addProperties(configRecord, profileMappingID, properties); + + // update status + setRecordStatus(profileMappingID, profileMappingRecord.getStatus()); + + configDatabase.commit(); + } + + @Override + public void removeRecord(String profileMappingID) throws Exception { + + CMS.debug("ProfileMappingDatabase.removeRecord(\"" + profileMappingID + "\")"); + ConfigDatabase configDatabase = new ConfigDatabase(); + ConfigRecord configRecord = configDatabase.getRecord(substoreName); + + // remove properties + configDatabase.removeProperties(configRecord, profileMappingID); + + // remove profileMapping + configRecord.removeKey(profileMappingID); + configDatabase.updateRecord(substoreName, configRecord); + + // remove status + removeRecordStatus(profileMappingID); + + configDatabase.commit(); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ProfileMappingRecord.java b/base/tps/src/org/dogtagpki/server/tps/config/ProfileMappingRecord.java new file mode 100644 index 000000000..92793ce5e --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ProfileMappingRecord.java @@ -0,0 +1,28 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import com.netscape.cmscore.dbs.CSCfgRecord; + + +/** + * @author Endi S. Dewata + */ +public class ProfileMappingRecord extends CSCfgRecord { +} diff --git a/base/tps/src/org/dogtagpki/server/tps/config/ProfileRecord.java b/base/tps/src/org/dogtagpki/server/tps/config/ProfileRecord.java new file mode 100644 index 000000000..a84b7f5ba --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/config/ProfileRecord.java @@ -0,0 +1,28 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.config; + +import com.netscape.cmscore.dbs.CSCfgRecord; + + +/** + * @author Endi S. Dewata + */ +public class ProfileRecord extends CSCfgRecord { +} diff --git a/base/tps/src/org/dogtagpki/server/tps/dbs/ActivityDatabase.java b/base/tps/src/org/dogtagpki/server/tps/dbs/ActivityDatabase.java new file mode 100644 index 000000000..9b4a4b28d --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/dbs/ActivityDatabase.java @@ -0,0 +1,100 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.dbs; + +import java.util.Calendar; +import java.util.Date; + +import org.apache.commons.lang.StringUtils; +import org.dogtagpki.tps.main.Util; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.cmscore.dbs.LDAPDatabase; +import com.netscape.cmsutil.ldap.LDAPUtil; + +/** + * This class implements in-memory activity database. In the future this + * will be replaced with LDAP database. + * + * @author Endi S. Dewata + */ +public class ActivityDatabase extends LDAPDatabase<ActivityRecord> { + public final static String OP_DO_TOKEN = "do_token"; + public final static String OP_ADD = "add"; // add a token + public final static String OP_DELETE = "delete"; // delete a token + //public final static String OP_MODIFY_AUDIT_SIGNING = "modify_audit_signing"; + public final static String OP_ENROLLMENT = "enrollment"; + public final static String OP_RENEWAL = "renewal"; + public final static String OP_PIN_RESET = "pin_reset"; + public final static String OP_FORMAT = "format"; + + public ActivityDatabase(IDBSubsystem dbSubsystem, String baseDN) throws EBaseException { + super("Activity", dbSubsystem, baseDN, ActivityRecord.class); + } + + public ActivityRecord log( + String ip, String tokenID, String operation, String result, + String message, String userID, String tokenType) throws Exception { + Calendar c = Calendar.getInstance(); + + String timeString = Util.getTimeStampString(true); + long threadID = Thread.currentThread().getId(); + String threadIDS = String.format("%x", threadID); + String id = timeString + "." + threadIDS; + + ActivityRecord activityRecord = new ActivityRecord(); + activityRecord.setId(id); + activityRecord.setIP(ip); + activityRecord.setTokenID(tokenID); + activityRecord.setOperation(operation); + activityRecord.setResult(result); + activityRecord.setMessage(message); + activityRecord.setUserID(userID); + activityRecord.setType(tokenType); + activityRecord.setDate(c.getTime()); + + super.addRecord(id, activityRecord); + + return activityRecord; + } + + @Override + public void addRecord(String id, ActivityRecord activityRecord) throws Exception { + activityRecord.setDate(new Date()); + + super.addRecord(id, activityRecord); + } + + @Override + public String createDN(String id) { + return "cn=" + id + "," + baseDN; + } + + @Override + public String createFilter(String filter) { + + if (StringUtils.isEmpty(filter)) { + return "(id=*)"; + } + + filter = LDAPUtil.escapeFilter(filter); + return "(|(tokenID=*" + filter + "*)(userID=*" + filter + "*))"; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/dbs/ActivityRecord.java b/base/tps/src/org/dogtagpki/server/tps/dbs/ActivityRecord.java new file mode 100644 index 000000000..fe5b1f91c --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/dbs/ActivityRecord.java @@ -0,0 +1,214 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.dbs; + +import java.util.Date; + +import com.netscape.cmscore.dbs.DBAttribute; +import com.netscape.cmscore.dbs.DBObjectClasses; +import com.netscape.cmscore.dbs.DBRecord; + +/** + * @author Endi S. Dewata + */ +@DBObjectClasses({ "top", "tokenActivity" }) +public class ActivityRecord extends DBRecord { + + private static final long serialVersionUID = 1L; + + String id; + String tokenID; + String userID; + String ip; + String operation; + String result; + String message; + String extensions; + String type; + Date date; + + @DBAttribute("cn") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @DBAttribute("tokenID") + public String getTokenID() { + return tokenID; + } + + public void setTokenID(String tokenID) { + this.tokenID = tokenID; + } + + @DBAttribute("tokenUserID") + public String getUserID() { + return userID; + } + + public void setUserID(String userID) { + this.userID = userID; + } + + @DBAttribute("tokenIP") + public String getIP() { + return ip; + } + + public void setIP(String ip) { + this.ip = ip; + } + + @DBAttribute("tokenOp") + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + @DBAttribute("tokenResult") + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + @DBAttribute("tokenMsg") + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @DBAttribute("extensions") + public String getExtensions() { + return extensions; + } + + public void setExtensions(String extensions) { + this.extensions = extensions; + } + + @DBAttribute("tokenType") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @DBAttribute("dateOfCreate") + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((date == null) ? 0 : date.hashCode()); + result = prime * result + ((extensions == null) ? 0 : extensions.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((ip == null) ? 0 : ip.hashCode()); + result = prime * result + ((message == null) ? 0 : message.hashCode()); + result = prime * result + ((operation == null) ? 0 : operation.hashCode()); + result = prime * result + ((this.result == null) ? 0 : this.result.hashCode()); + result = prime * result + ((tokenID == null) ? 0 : tokenID.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((userID == null) ? 0 : userID.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ActivityRecord other = (ActivityRecord) obj; + if (date == null) { + if (other.date != null) + return false; + } else if (!date.equals(other.date)) + return false; + if (extensions == null) { + if (other.extensions != null) + return false; + } else if (!extensions.equals(other.extensions)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (ip == null) { + if (other.ip != null) + return false; + } else if (!ip.equals(other.ip)) + return false; + if (message == null) { + if (other.message != null) + return false; + } else if (!message.equals(other.message)) + return false; + if (operation == null) { + if (other.operation != null) + return false; + } else if (!operation.equals(other.operation)) + return false; + if (result == null) { + if (other.result != null) + return false; + } else if (!result.equals(other.result)) + return false; + if (tokenID == null) { + if (other.tokenID != null) + return false; + } else if (!tokenID.equals(other.tokenID)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + if (userID == null) { + if (other.userID != null) + return false; + } else if (!userID.equals(other.userID)) + return false; + return true; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/dbs/TPSCertDatabase.java b/base/tps/src/org/dogtagpki/server/tps/dbs/TPSCertDatabase.java new file mode 100644 index 000000000..7450b0665 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/dbs/TPSCertDatabase.java @@ -0,0 +1,71 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.dbs; + +import java.util.Date; + +import org.apache.commons.lang.StringUtils; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.cmscore.dbs.LDAPDatabase; +import com.netscape.cmsutil.ldap.LDAPUtil; + +/** + * This class implements in-memory activity database. In the future this + * will be replaced with LDAP database. + * + * @author Endi S. Dewata + */ +public class TPSCertDatabase extends LDAPDatabase<TPSCertRecord> { + + public TPSCertDatabase(IDBSubsystem dbSubsystem, String baseDN) throws EBaseException { + super("Certificate", dbSubsystem, baseDN, TPSCertRecord.class); + } + + @Override + public void addRecord(String id, TPSCertRecord certRecord) throws Exception { + certRecord.setCreateTime(new Date()); + + super.addRecord(id, certRecord); + } + + @Override + public void updateRecord(String id, TPSCertRecord certRecord) throws Exception { + certRecord.setModifyTime(new Date()); + + super.updateRecord(id, certRecord); + } + + @Override + public String createDN(String id) { + return "cn=" + id + "," + baseDN; + } + + @Override + public String createFilter(String filter) { + + if (StringUtils.isEmpty(filter)) { + return "(id=*)"; + } + + filter = LDAPUtil.escapeFilter(filter); + return "(|(id=*" + filter + "*)(tokenID=*" + filter + "*)(userID=*" + filter + "*))"; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/dbs/TPSCertRecord.java b/base/tps/src/org/dogtagpki/server/tps/dbs/TPSCertRecord.java new file mode 100644 index 000000000..288f25f53 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/dbs/TPSCertRecord.java @@ -0,0 +1,313 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.dbs; + +import java.util.Date; + +import com.netscape.cmscore.dbs.DBAttribute; +import com.netscape.cmscore.dbs.DBObjectClasses; +import com.netscape.cmscore.dbs.DBRecord; + +/** + * @author Endi S. Dewata + */ +@DBObjectClasses({ "top", "tokenCert" }) +public class TPSCertRecord extends DBRecord { + + private static final long serialVersionUID = 1L; + + String id; + String serialNumber; + String subject; + String tokenID; + String keyType; + String status; + String userID; + String certificate; + String issuedBy; + String origin; + String type; + Date validNotBefore; + Date validNotAfter; + String extensions; + Date createTime; + Date modifyTime; + + @DBAttribute("cn") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + // the serial number is in HEX + @DBAttribute("tokenSerial") + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + @DBAttribute("tokenSubject") + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + @DBAttribute("tokenID") + public String getTokenID() { + return tokenID; + } + + public void setTokenID(String tokenID) { + this.tokenID = tokenID; + } + + @DBAttribute("tokenKeyType") + public String getKeyType() { + return keyType; + } + + public void setKeyType(String keyType) { + this.keyType = keyType; + } + + @DBAttribute("tokenStatus") + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + @DBAttribute("tokenUserID") + public String getUserID() { + return userID; + } + + public void setUserID(String userID) { + this.userID = userID; + } + + @DBAttribute("userCertificate") + // Alternative to the actual certificate -- certificate AKI + public String getCertificate() { + return certificate; + } + + // Alternative to the actual certificate -- certificate AKI + public void setCertificate(String certificate) { + this.certificate = certificate; + } + + @DBAttribute("tokenIssuer") + public String getIssuedBy() { + return issuedBy; + } + + public void setIssuedBy(String issuedBy) { + this.issuedBy = issuedBy; + } + + @DBAttribute("tokenOrigin") + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + @DBAttribute("tokenType") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @DBAttribute("tokenNotBefore") + public Date getValidNotBefore() { + return validNotBefore; + } + + public void setValidNotBefore(Date validNotBefore) { + this.validNotBefore = validNotBefore; + } + + @DBAttribute("tokenNotAfter") + public Date getValidNotAfter() { + return validNotAfter; + } + + public void setValidNotAfter(Date validNotAfter) { + this.validNotAfter = validNotAfter; + } + + @DBAttribute("extensions") + public String getExtensions() { + return extensions; + } + + public void setExtensions(String extensions) { + this.extensions = extensions; + } + + @DBAttribute("dateOfCreate") + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @DBAttribute("dateOfModify") + public Date getModifyTime() { + return modifyTime; + } + + public void setModifyTime(Date modifyTime) { + this.modifyTime = modifyTime; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((certificate == null) ? 0 : certificate.hashCode()); + result = prime * result + ((createTime == null) ? 0 : createTime.hashCode()); + result = prime * result + ((extensions == null) ? 0 : extensions.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((issuedBy == null) ? 0 : issuedBy.hashCode()); + result = prime * result + ((keyType == null) ? 0 : keyType.hashCode()); + result = prime * result + ((modifyTime == null) ? 0 : modifyTime.hashCode()); + result = prime * result + ((origin == null) ? 0 : origin.hashCode()); + result = prime * result + ((serialNumber == null) ? 0 : serialNumber.hashCode()); + result = prime * result + ((status == null) ? 0 : status.hashCode()); + result = prime * result + ((subject == null) ? 0 : subject.hashCode()); + result = prime * result + ((tokenID == null) ? 0 : tokenID.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((userID == null) ? 0 : userID.hashCode()); + result = prime * result + ((validNotAfter == null) ? 0 : validNotAfter.hashCode()); + result = prime * result + ((validNotBefore == null) ? 0 : validNotBefore.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TPSCertRecord other = (TPSCertRecord) obj; + if (certificate == null) { + if (other.certificate != null) + return false; + } else if (!certificate.equals(other.certificate)) + return false; + if (createTime == null) { + if (other.createTime != null) + return false; + } else if (!createTime.equals(other.createTime)) + return false; + if (extensions == null) { + if (other.extensions != null) + return false; + } else if (!extensions.equals(other.extensions)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (issuedBy == null) { + if (other.issuedBy != null) + return false; + } else if (!issuedBy.equals(other.issuedBy)) + return false; + if (keyType == null) { + if (other.keyType != null) + return false; + } else if (!keyType.equals(other.keyType)) + return false; + if (modifyTime == null) { + if (other.modifyTime != null) + return false; + } else if (!modifyTime.equals(other.modifyTime)) + return false; + if (origin == null) { + if (other.origin != null) + return false; + } else if (!origin.equals(other.origin)) + return false; + if (serialNumber == null) { + if (other.serialNumber != null) + return false; + } else if (!serialNumber.equals(other.serialNumber)) + return false; + if (status == null) { + if (other.status != null) + return false; + } else if (!status.equals(other.status)) + return false; + if (subject == null) { + if (other.subject != null) + return false; + } else if (!subject.equals(other.subject)) + return false; + if (tokenID == null) { + if (other.tokenID != null) + return false; + } else if (!tokenID.equals(other.tokenID)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + if (userID == null) { + if (other.userID != null) + return false; + } else if (!userID.equals(other.userID)) + return false; + if (validNotAfter == null) { + if (other.validNotAfter != null) + return false; + } else if (!validNotAfter.equals(other.validNotAfter)) + return false; + if (validNotBefore == null) { + if (other.validNotBefore != null) + return false; + } else if (!validNotBefore.equals(other.validNotBefore)) + return false; + return true; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/dbs/TokenDatabase.java b/base/tps/src/org/dogtagpki/server/tps/dbs/TokenDatabase.java new file mode 100644 index 000000000..f86c6e203 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/dbs/TokenDatabase.java @@ -0,0 +1,68 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.dbs; + +import java.util.Date; + +import org.apache.commons.lang.StringUtils; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.cmscore.dbs.LDAPDatabase; +import com.netscape.cmsutil.ldap.LDAPUtil; + +/** + * @author Endi S. Dewata + */ +public class TokenDatabase extends LDAPDatabase<TokenRecord> { + + public TokenDatabase(IDBSubsystem dbSubsystem, String baseDN) throws EBaseException { + super("Token", dbSubsystem, baseDN, TokenRecord.class); + } + + @Override + public void addRecord(String id, TokenRecord tokenRecord) throws Exception { + tokenRecord.setCreateTimestamp(new Date()); + + super.addRecord(id, tokenRecord); + } + + @Override + public void updateRecord(String id, TokenRecord tokenRecord) throws Exception { + tokenRecord.setModifyTimestamp(new Date()); + + super.updateRecord(id, tokenRecord); + } + + @Override + public String createDN(String id) { + return "cn=" + id + "," + baseDN; + } + + @Override + public String createFilter(String filter) { + + if (StringUtils.isEmpty(filter)) { + return "(id=*)"; + } + + filter = LDAPUtil.escapeFilter(filter); + return "(|(id=*" + filter + "*)(userID=*" + filter + "*))"; + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/dbs/TokenRecord.java b/base/tps/src/org/dogtagpki/server/tps/dbs/TokenRecord.java new file mode 100644 index 000000000..a1aef5720 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/dbs/TokenRecord.java @@ -0,0 +1,271 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.dbs; + +import java.util.Date; + +import com.netscape.certsrv.tps.token.TokenStatus; +import com.netscape.cmscore.dbs.DBAttribute; +import com.netscape.cmscore.dbs.DBObjectClasses; +import com.netscape.cmscore.dbs.DBRecord; + +/** + * @author Endi S. Dewata + */ +@DBObjectClasses({ "top", "tokenRecord" }) +public class TokenRecord extends DBRecord { + + private static final long serialVersionUID = 1L; + + /** + * objectClasses: ( tokenRecord-oid + * NAME 'tokenRecord' + * DESC 'CMS defined class' + * SUP top + * STRUCTURAL + * MUST cn + * MAY ( + * dateOfCreate $ + * dateOfModify $ + * modified $ (unused) + * tokenReason $ + * tokenUserID $ + * tokenStatus $ + * tokenAppletID $ + * keyInfo $ + * tokenPolicy $ + * extensions $ (unused) + * numberOfResets $ (unused) + * numberOfEnrollments $ (unused) + * numberOfRenewals $ (unused) + * numberOfRecoveries $ (unused) + * userCertificate $ (unused) + * tokenType ) + * X-ORIGIN 'user defined' ) + */ + String id; + String userID; + String type; + String status; + String reason; + String appletID; + String keyInfo; + String policy; + Date createTimestamp; + Date modifyTimestamp; + + @DBAttribute("cn") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @DBAttribute("tokenUserID") + public String getUserID() { + return userID; + } + + public void setUserID(String userID) { + this.userID = userID; + } + + @DBAttribute("tokenType") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @DBAttribute("tokenStatus") + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + @DBAttribute("tokenReason") + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + @DBAttribute("tokenAppletID") + public String getAppletID() { + return appletID; + } + + public void setAppletID(String appletID) { + this.appletID = appletID; + } + + @DBAttribute("keyInfo") + public String getKeyInfo() { + return keyInfo; + } + + public void setKeyInfo(String keyInfo) { + this.keyInfo = keyInfo; + } + + @DBAttribute("tokenPolicy") + public String getPolicy() { + return policy; + } + + public void setPolicy(String policy) { + this.policy = policy; + } + + @DBAttribute("dateOfCreate") + public Date getCreateTimestamp() { + return createTimestamp; + } + + public void setCreateTimestamp(Date createTimestamp) { + this.createTimestamp = createTimestamp; + } + + @DBAttribute("dateOfModify") + public Date getModifyTimestamp() { + return modifyTimestamp; + } + + public void setModifyTimestamp(Date modifyTimestamp) { + this.modifyTimestamp = modifyTimestamp; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appletID == null) ? 0 : appletID.hashCode()); + result = prime * result + ((createTimestamp == null) ? 0 : createTimestamp.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((keyInfo == null) ? 0 : keyInfo.hashCode()); + result = prime * result + ((modifyTimestamp == null) ? 0 : modifyTimestamp.hashCode()); + result = prime * result + ((policy == null) ? 0 : policy.hashCode()); + result = prime * result + ((reason == null) ? 0 : reason.hashCode()); + result = prime * result + ((status == null) ? 0 : status.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((userID == null) ? 0 : userID.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TokenRecord other = (TokenRecord) obj; + if (appletID == null) { + if (other.appletID != null) + return false; + } else if (!appletID.equals(other.appletID)) + return false; + if (createTimestamp == null) { + if (other.createTimestamp != null) + return false; + } else if (!createTimestamp.equals(other.createTimestamp)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (keyInfo == null) { + if (other.keyInfo != null) + return false; + } else if (!keyInfo.equals(other.keyInfo)) + return false; + if (modifyTimestamp == null) { + if (other.modifyTimestamp != null) + return false; + } else if (!modifyTimestamp.equals(other.modifyTimestamp)) + return false; + if (policy == null) { + if (other.policy != null) + return false; + } else if (!policy.equals(other.policy)) + return false; + if (reason == null) { + if (other.reason != null) + return false; + } else if (!reason.equals(other.reason)) + return false; + if (status == null) { + if (other.status != null) + return false; + } else if (!status.equals(other.status)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + if (userID == null) { + if (other.userID != null) + return false; + } else if (!userID.equals(other.userID)) + return false; + return true; + } + + public TokenStatus getTokenStatus() { + String status = getStatus(); + + if ("uninitialized".equals(status)) { + return TokenStatus.UNINITIALIZED; + + } else if ("active".equals(status)) { + return TokenStatus.ACTIVE; + + } else if ("lost".equals(status)) { + String reason = getReason(); + + if ("keyCompromise".equals(reason)) { + return TokenStatus.PERM_LOST; + + } else if ("destroyed".equals(reason)) { + return TokenStatus.DAMAGED; + + } else if ("onHold".equals(reason)) { + return TokenStatus.TEMP_LOST; + } + + } else if ("terminated".equals(status)) { + return TokenStatus.TERMINATED; + } + + return TokenStatus.PERM_LOST; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java b/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java new file mode 100644 index 000000000..c4b485c45 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java @@ -0,0 +1,589 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.engine; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; + +import org.dogtagpki.server.tps.cms.CARemoteRequestHandler; +import org.dogtagpki.server.tps.cms.CARenewCertResponse; +import org.dogtagpki.server.tps.cms.CARetrieveCertResponse; +import org.dogtagpki.server.tps.cms.KRARecoverKeyResponse; +import org.dogtagpki.server.tps.cms.KRARemoteRequestHandler; +import org.dogtagpki.server.tps.cms.KRAServerSideKeyGenResponse; +import org.dogtagpki.server.tps.cms.TKSComputeSessionKeyResponse; +import org.dogtagpki.server.tps.cms.TKSCreateKeySetDataResponse; +import org.dogtagpki.server.tps.cms.TKSRemoteRequestHandler; +import org.dogtagpki.server.tps.dbs.TPSCertRecord; +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.main.Util; +import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.tps.token.TokenStatus; + +public class TPSEngine { + + public enum RA_Algs { + ALG_RSA, + ALG_RSA_CRT, + ALG_DSA, + ALG_EC_F2M, + ALG_EC_FP + }; + + public enum ENROLL_MODES { + MODE_ENROLL, + MODE_RECOVERY, + MODE_RENEWAL + } + + public static final String CFG_DEBUG_ENABLE = "logging.debug.enable"; + public static final String CFG_DEBUG_FILENAME = "logging.debug.filename"; + public static final String CFG_DEBUG_LEVEL = "logging.debug.level"; + public static final String CFG_AUDIT_ENABLE = "logging.audit.enable"; + public static final String CFG_AUDIT_FILENAME = "logging.audit.filename"; + public static final String CFG_SIGNED_AUDIT_FILENAME = "logging.audit.signedAuditFilename"; + public static final String CFG_AUDIT_LEVEL = "logging.audit.level"; + public static final String CFG_AUDIT_SIGNED = "logging.audit.logSigning"; + public static final String CFG_AUDIT_SIGNING_CERT_NICK = "logging.audit.signedAuditCertNickname"; + public static final String CFG_ERROR_ENABLE = "logging.error.enable"; + public static final String CFG_ERROR_FILENAME = "logging.error.filename"; + public static final String CFG_ERROR_LEVEL = "logging.error.level"; + public static final String CFG_SELFTEST_ENABLE = "selftests.container.logger.enable"; + public static final String CFG_SELFTEST_FILENAME = "selftests.container.logger.fileName"; + public static final String CFG_SELFTEST_LEVEL = "selftests.container.logger.level"; + public static final String CFG_CHANNEL_SEC_LEVEL = "channel.securityLevel"; + public static final String CFG_CHANNEL_ENCRYPTION = "channel.encryption"; + public static final String CFG_APPLET_CARDMGR_INSTANCE_AID = "applet.aid.cardmgr_instance"; + public static final String CFG_APPLET_NETKEY_INSTANCE_AID = "applet.aid.netkey_instance"; + public static final String CFG_APPLET_NETKEY_FILE_AID = "applet.aid.netkey_file"; + public static final String CFG_APPLET_NETKEY_OLD_INSTANCE_AID = "applet.aid.netkey_old_instance"; + public static final String CFG_APPLET_NETKEY_OLD_FILE_AID = "applet.aid.netkey_old_file"; + public static final String CFG_APPLET_SO_PIN = "applet.so_pin"; + public static final String CFG_APPLET_DELETE_NETKEY_OLD = "applet.delete_old"; + public static final String CFG_AUDIT_SELECTED_EVENTS = "logging.audit.selected.events"; + public static final String CFG_AUDIT_NONSELECTABLE_EVENTS = "logging.audit.nonselectable.events"; + public static final String CFG_AUDIT_SELECTABLE_EVENTS = "logging.audit.selectable.events"; + public static final String CFG_AUDIT_BUFFER_SIZE = "logging.audit.buffer.size"; + public static final String CFG_AUDIT_FLUSH_INTERVAL = "logging.audit.flush.interval"; + public static final String CFG_AUDIT_FILE_TYPE = "logging.audit.file.type"; + public static final String CFG_DEBUG_FILE_TYPE = "logging.debug.file.type"; + public static final String CFG_ERROR_FILE_TYPE = "logging.error.file.type"; + public static final String CFG_SELFTEST_FILE_TYPE = "selftests.container.logger.file.type"; + public static final String CFG_AUDIT_PREFIX = "logging.audit"; + public static final String CFG_ERROR_PREFIX = "logging.error"; + public static final String CFG_DEBUG_PREFIX = "logging.debug"; + public static final String CFG_SELFTEST_PREFIX = "selftests.container.logger"; + public static final String CFG_TOKENDB_ALLOWED_TRANSITIONS = "tokendb.allowedTransitions"; + public static final String CFG_OPERATIONS_ALLOWED_TRANSITIONS = "tps.operations.allowedTransitions"; + + public static final String CFG_PRINTBUF_FULL = "tps.printBufFull"; + public static final String CFG_RECV_BUF_SIZE = "tps.recvBufSize"; + public static final String CFG_AUTHS_ENABLE = "auth.enable"; + public static final String CFG_PROFILE_MAPPING_ORDER = "mapping.order"; + public static final String CFG_ALLOW_UNKNOWN_TOKEN = "allowUnkonwnToken"; + public static final String CFG_ALLOW_NO_APPLET = "update.applet.emptyToken.enable"; + public static final String CFG_APPLET_UPDATE_REQUIRED_VERSION = "update.applet.requiredVersion"; + public static final String CFG_APPLET_DIRECTORY = "update.applet.directory"; + public static final String CFG_APPLET_EXTENSION = "general.applet_ext"; + + public static final String CFG_CHANNEL_BLOCK_SIZE = "channel.blockSize"; + public static final String CFG_CHANNEL_INSTANCE_SIZE = "channel.instanceSize"; + public static final String CFG_CHANNEL_DEFKEY_VERSION = "channel.defKeyVersion"; + public static final String CFG_CHANNEL_APPLET_MEMORY_SIZE = "channel.appletMemorySize"; + public static final String CFG_CHANNEL_DEFKEY_INDEX = "channel.defKeyIndex"; + public static final String CFG_ISSUER_INFO_ENABLE = "issuerinfo.enable"; + public static final String CFG_ISSUER_INFO_VALUE = "issuerinfo.value"; + public static final String CFG_UPDATE_APPLET_ENCRYPTION = "update.applet.encryption"; + public static final String CFG_UPDATE_APPLET_ENABLE = "update.applet.enable"; + public static final String CFG_SYMM_KEY_UPGRADE_ENABLED = "update.symmetricKeys.enable"; + + /* default values */ + public static final String CFG_DEF_CARDMGR_INSTANCE_AID = "A0000000030000"; + public static final String CFG_DEF_NETKEY_INSTANCE_AID = "627601FF000000"; + public static final String CFG_DEF_NETKEY_FILE_AID = "627601FF0000"; + public static final String CFG_DEF_NETKEY_OLD_INSTANCE_AID = "A00000000101"; + public static final String CFG_DEF_NETKEY_OLD_FILE_AID = "A000000001"; + public static final String CFG_DEF_APPLET_SO_PIN = "000000000000"; + public static final String CFG_ENABLED = "Enabled"; + + public static final int CFG_CHANNEL_DEF_BLOCK_SIZE = 242; + public static final int CFG_CHANNEL_DEF_INSTANCE_SIZE = 18000; + public static final int CFG_CHANNEL_DEF_APPLET_MEMORY_SIZE = 5000; + + /* token enrollment values */ + public static final String CFG_SIGNING = "signing"; + public static final String CFG_ENCRYPTION = "encryption"; + public static final String CFG_KEYGEN_ENCRYPTION = "keyGen." + CFG_ENCRYPTION; + public static final String CFG_KEYGEN_SIGNING = "keyGen." + CFG_SIGNING; + public static final String CFG_KEYTYPE_NUM = "keyType.num"; + public static final String CFG_KEYTYPE_VALUE = "keyType.value"; + public static final String CFG_KEYGEN_KEYTYPE_NUM = "keyGen." + CFG_KEYTYPE_NUM; + public static final String CFG_KEYGEN_KEYTYPE_VALUE = "keyGen." + CFG_KEYTYPE_VALUE; + public static final String CFG_SERVER_KEYGEN_ENABLE = "serverKeygen.enable"; + public static final String CFG_SERVER_KEY_ARCHIVAL = "serverKeygen.archive"; + public static final String CFG_DRM_CONNECTOR = "serverKeygen.drm.conn"; + public static final String CFG_KEYGEN = "keyGen"; + public static final String CFG_ALG = "alg"; + + /* token renewal values */ + public static final String CFG_RENEW_KEYTYPE_NUM = "renewal.keyType.num"; + public static final String CFG_RENEW_KEYTYPE_VALUE = "renewal.keyType.value"; + + /* External reg values */ + + public static final String CFG_EXTERNAL_REG = "externalReg"; + + /* misc values */ + + public static final String ENROLL_OP = "enroll"; + public static final String FORMAT_OP = "format"; + public static final String RECOVERY_OP = "recovery"; + public static final String RENEWAL_OP = "renewal"; + + public static final String OP_FORMAT_PREFIX = "op." + FORMAT_OP; + public static final String CFG_PROFILE_RESOLVER = "tokenProfileResolver"; + public static final String CFG_DEF_FORMAT_PROFILE_RESOLVER = "formatMappingResolver"; + public static final String CFG_DEF_ENROLL_PROFILE_RESOLVER = "enrollMappingResolver"; + public static final String CFG_DEF_PIN_RESET_PROFILE_RESOLVER = "pinResetMappingResolver"; + public static final String OP_ENROLL_PREFIX = "op." + ENROLL_OP; + public static final String OP_RECOVERY_PREFIX = "op." + RECOVERY_OP; + + public static final String OP_PIN_RESET_PREFIX = "op.pinReset"; + public static final String CFG_PIN_RESET_ENABLE = "pinReset.enable"; + public static final String CFG_PIN_RESET_MIN_LEN = "pinReset.pin.minLen"; + public static final String CFG_PIN_RESET_MAX_LEN = "pinReset.pin.maxLen"; + public static final String CFG_PIN_RESET_MAX_RETRIES = "pinReset.pin.maxRetries"; + public static final String CFG_PIN_RESET_STRING = "create_pin.string"; + + public static final String CFG_SCHEME = "scheme"; + public static final String RECOVERY_SCHEME_GENERATE_NEW_KEY_AND_RECOVER_LAST = "GenerateNewKeyandRecoverLast"; + public static final Object RECOVERY_GENERATE_NEW_KEY = "GenerateNewKey"; + public static final Object RECOVERY_RECOVER_LAST = "RecoverLast"; + + public static final String CFG_OVERWRITE = "overwrite"; + public static final String PIN_RESET_OP = "pin_reset"; + public static final String ENROLL_MODE_ENROLLMENT = ENROLL_OP; + public static final String ENROLL_MODE_RECOVERY = RECOVERY_OP; + public static final String ERNOLL_MODE_RENEWAL = RENEWAL_OP; + private static final String CFG_OPERATIONS_TRANSITIONS = "tps.operations.allowedTransitions"; + + private static String transitionList; + + public void init() { + //ToDo + } + + public TPSEngine() { + } + + public int initialize(String cfg_path) { + + int rc = -1; + + return rc; + } + + public TKSComputeSessionKeyResponse computeSessionKey(TPSBuffer cuid, + TPSBuffer keyInfo, + TPSBuffer card_challenge, + TPSBuffer host_challenge, + TPSBuffer card_cryptogram, + String connId, + String tokenType) throws TPSException { + + if (cuid == null || keyInfo == null || card_challenge == null || host_challenge == null + || card_cryptogram == null || connId == null || tokenType == null) { + + throw new TPSException("TPSEngine.computeSessionKey: Invalid input data!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + + } + + CMS.debug("TPSEngine.computeSessionKey"); + + TKSRemoteRequestHandler tks = null; + + TKSComputeSessionKeyResponse resp = null; + try { + tks = new TKSRemoteRequestHandler(connId); + resp = tks.computeSessionKey(cuid, keyInfo, card_challenge, card_cryptogram, host_challenge, tokenType); + } catch (EBaseException e) { + throw new TPSException("TPSEngine.computeSessionKey: Error computing session key!" + e, + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + int status = resp.getStatus(); + if (status != 0) { + CMS.debug("TPSEngine.computeSessionKey: Non zero status result: " + status); + throw new TPSException("TPSEngine.computeSessionKey: invalid returned status: " + status); + + } + + return resp; + + } + + public CARetrieveCertResponse recoverCertificate(TPSCertRecord cert, String serialS, String keyType, String caConnID) + throws TPSException { + + String method = "TPSEngine.recoverCertificate"; + + CMS.debug(method + ": serial # =" + serialS); + + if (cert == null || serialS == null || keyType == null || caConnID == null) { + throw new TPSException(method + " Invalid input data!", TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + String serialhex = serialS.substring(2); // strip off the "0x" + BigInteger serialBI = new BigInteger(serialhex, 16); + + CARetrieveCertResponse retrieveResponse = null; + + String retrievedCertB64 = null; + + try { + + CARemoteRequestHandler caRH = new CARemoteRequestHandler(caConnID); + + retrieveResponse = caRH.retrieveCertificate(serialBI); + retrievedCertB64 = retrieveResponse.getCertB64(); + CMS.debug(method + ": retrieved cert: " + retrievedCertB64); + // test ends - remove up to here + + } catch (EBaseException e) { + CMS.debug(method + ":" + e); + throw new TPSException(method + ": Exception thrown: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + if (retrievedCertB64 == null) { + throw new TPSException(method + " Unable to get valid cert blob.", TPSStatus.STATUS_ERROR_CA_RESPONSE); + } + + return retrieveResponse; + + } + + public CARenewCertResponse renewCertificate(TPSCertRecord cert, String serialS, String tokenType, String keyType, + String caConnID) throws TPSException { + + String method = "TPSEngine.renewCertificate"; + + CMS.debug(method + " entering..."); + + if (cert == null || serialS == null || keyType == null || tokenType == null || caConnID == null) { + throw new TPSException(method + " Invalid input data!", TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + CMS.debug(method + ": serial # =" + serialS); + String serialhex = serialS.substring(2); // strip off the "0x" + BigInteger serialBI = new BigInteger(serialhex, 16); + + CARenewCertResponse renewResponse = null; + + String retrievedCertB64 = null; + + try { + + CARemoteRequestHandler caRH = new CARemoteRequestHandler(caConnID); + + /* + * testing retrieveCertificate() to retrieve the old cert (to be used by Recovery) + * TODO: remove + */ + renewResponse = caRH.renewCertificate(serialBI, tokenType, keyType); + retrievedCertB64 = renewResponse.getRenewedCertB64(); + CMS.debug(method + ": retrieved cert: " + retrievedCertB64); + + } catch (EBaseException e) { + CMS.debug(method + ":" + e); + throw new TPSException(method + ": Exception thrown: " + e, + TPSStatus.STATUS_ERROR_RENEWAL_FAILED); + } + + if (retrievedCertB64 == null) { + throw new TPSException(method + " Unable to get valid cert blob.", TPSStatus.STATUS_ERROR_CA_RESPONSE); + } + + return renewResponse; + + } + + public TPSBuffer createKeySetData(TPSBuffer newMasterVersion, TPSBuffer oldVersion, TPSBuffer cuid, String connId) + throws TPSException { + CMS.debug("TPSEngine.createKeySetData. entering..."); + + if (newMasterVersion == null || oldVersion == null || cuid == null || connId == null) { + throw new TPSException("TPSEngine.createKeySetData: Invalid input data", + TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER); + } + + TKSRemoteRequestHandler tks = null; + + TKSCreateKeySetDataResponse resp = null; + + try { + tks = new TKSRemoteRequestHandler(connId); + resp = tks.createKeySetData(newMasterVersion, oldVersion, cuid); + } catch (EBaseException e) { + + throw new TPSException("TPSEngine.createKeySetData, failure to get key set data from TKS", + TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER); + } + + int status = resp.getStatus(); + if (status != 0) { + CMS.debug("TPSEngine.createKeySetData: Non zero status result: " + status); + throw new TPSException("TPSEngine.computeSessionKey: invalid returned status: " + status, + TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER); + + } + + TPSBuffer keySetData = resp.getKeySetData(); + + if (keySetData == null) { + CMS.debug("TPSEngine.createKeySetData: No valid key set data returned."); + throw new TPSException("TPSEngine.createKeySetData: No valid key set data returned.", + TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER); + + } + + return keySetData; + } + + public static void main(String[] args) { + + } + + public boolean raForceTokenFormat(String cuid) { + // TODO Auto-generated method stub + return false; + } + + public boolean isAlgorithmECC(int algorithm) { + + RA_Algs algEnum = intToRAAlgs(algorithm); + + boolean isECC = false; + + if (algEnum == RA_Algs.ALG_EC_F2M || algEnum == RA_Algs.ALG_EC_FP) { + isECC = true; + } + + CMS.debug("TPSEngine.isAlgorithmECC: result: " + isECC); + return isECC; + + } + + public static RA_Algs intToRAAlgs(int alg) { + + RA_Algs def = RA_Algs.ALG_RSA; + + switch (alg) { + + case 1: + return RA_Algs.ALG_RSA; + + case 2: + return RA_Algs.ALG_RSA_CRT; + case 3: + return RA_Algs.ALG_DSA; + case 4: + return RA_Algs.ALG_EC_F2M; + case 5: + return RA_Algs.ALG_EC_FP; + + default: + return def; + + } + + } + + public KRARecoverKeyResponse recoverKey(String cuid, + String userid, + TPSBuffer sDesKey, + String b64cert, String drmConnId) throws TPSException { + String method = "TPSEngine.recoverKey"; + CMS.debug("TPSEngine.recoverKey"); + if (cuid == null) + CMS.debug(method + ": cuid null"); + else if (userid == null) + CMS.debug(method + ": userid null"); + else if (sDesKey == null) + CMS.debug(method + ": isDesKey null"); + else if (b64cert == null) + CMS.debug(method + ": b64cert null"); + else if (drmConnId == null) + CMS.debug(method + ": drmConnId null"); + + if (cuid == null || userid == null || sDesKey == null || b64cert == null || drmConnId == null) { + throw new TPSException("TPSEngine.recoverKey: invalid input data!", TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + KRARecoverKeyResponse resp = null; + KRARemoteRequestHandler kra = null; + + try { + kra = new KRARemoteRequestHandler(drmConnId); + + resp = kra.recoverKey(cuid, userid, Util.specialURLEncode(sDesKey), Util.uriEncode(b64cert)); + } catch (EBaseException e) { + throw new TPSException("TPSEngine.recoverKey: Problem creating or using KRARemoteRequestHandler! " + + e.toString(), TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + + } catch (UnsupportedEncodingException e) { + throw new TPSException("TPSEngine.recoverKey: Problem creating or using KRARemoteRequestHandler! " + + e.toString(), TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + int status = resp.getStatus(); + + if (status != 0) { + throw new TPSException("TPSEngine.recoverKey: Bad status from server: " + status, + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + if (resp.getPublicKey() == null) { + throw new TPSException("TPSEngine.recoverKey: invalid public key from server! ", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + if (resp.getWrappedPrivKey() == null) { + throw new TPSException("TPSEngine.recoverKey: invalid private key from server! ", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + + } + + if (resp.getIVParam() == null) { + throw new TPSException("TPSEngine.recoverKey: invalid iv vector from server!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + return resp; + + } + + public KRAServerSideKeyGenResponse serverSideKeyGen(int keySize, String cuid, String userid, String drmConnId, + TPSBuffer wrappedDesKey, + boolean archive, + boolean isECC) throws TPSException { + + CMS.debug("TPSEngine.serverSideKeyGen entering... keySize: " + keySize + " cuid: " + cuid + " userid: " + + userid + " drConnId: " + drmConnId + " wrappedDesKey: " + wrappedDesKey + " archive: " + archive + + " isECC: " + isECC); + + if (cuid == null || userid == null || drmConnId == null || wrappedDesKey == null) { + throw new TPSException("TPSEngine.serverSideKeyGen: Invalid input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + KRARemoteRequestHandler kra = null; + KRAServerSideKeyGenResponse resp = null; + + try { + kra = new KRARemoteRequestHandler(drmConnId); + + resp = kra.serverSideKeyGen(isECC, keySize, cuid, userid, + Util.specialURLEncode(wrappedDesKey), archive); + + } catch (EBaseException e) { + throw new TPSException("TPSEngine.serverSideKeyGen: Problem creating or using KRARemoteRequestHandler! " + + e.toString()); + } + + int status = resp.getStatus(); + + if (status != 0) { + throw new TPSException("TPSEngine.serverSideKeyGen: Bad status from server: " + status, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + if (resp.getPublicKey() == null) { + throw new TPSException("TPSEngine.serverSideKeyGen: invalid public key from server! ", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + if (resp.getWrappedPrivKey() == null) { + throw new TPSException("TPSEngine.serverSideKeyGen: invalid private key from server! ", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + + } + + if (resp.getIVParam() == null) { + throw new TPSException("TPSEngine.serverSideKeyGen: invalid iv vector from server!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + //We return this resonse we know that all the data is present and can be accessed + return resp; + + } + + //Check to see if special operations transition is allowed + + public boolean isOperationTransitionAllowed(TokenStatus oldState, TokenStatus newState) throws TPSException { + boolean allowed = true; + + if(transitionList == null) { + + IConfigStore configStore = CMS.getConfigStore(); + + String transConfig = CFG_OPERATIONS_TRANSITIONS; + + CMS.debug("TPSEngine.isOperationTransistionAllowed: getting config: " + transConfig); + try { + transitionList = configStore.getString(transConfig,null); + } catch (EBaseException e) { + throw new TPSException( + "TPSProcessor.isOperationTransitionAllowed: Internal error getting config value for operations transition list!", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + + if(transitionList == null) { + throw new TPSException( + "TPSProcessor.isOperationTransitionAllowed: Can't find non null config value for operations transition list!", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + + CMS.debug("TPSEngine.isOperationTransistionAllowed: transitionList is: " + transitionList); + + + } + + String transition = oldState.toInt() + ":" + newState.toInt(); + + CMS.debug("TPSEngine.isOperationTransistionAllowed: checking for transition: " + transition); + + if(transitionList.indexOf(transition) == -1) { + CMS.debug("TPSEngine.isOperationTransistionAllowed: checking for transition: " + transition); + allowed = false; + } + + CMS.debug("TPSEngine.isOperationTransistionAllowed: checking for transition: " + transition + " allowed: " + allowed); + + return allowed; + + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/installer/CAInfoPanel.java b/base/tps/src/org/dogtagpki/server/tps/installer/CAInfoPanel.java new file mode 100644 index 000000000..4c5371f20 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/installer/CAInfoPanel.java @@ -0,0 +1,171 @@ +package org.dogtagpki.server.tps.installer; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.cert.CertificateException; +import java.util.Vector; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.lang.StringUtils; +import org.apache.velocity.context.Context; +import org.mozilla.jss.CryptoManager.NotInitializedException; +import org.mozilla.jss.crypto.TokenException; +import org.xml.sax.SAXException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.property.PropertySet; +import com.netscape.certsrv.util.HttpInput; +import com.netscape.cms.servlet.csadmin.ConfigurationUtils; +import com.netscape.cms.servlet.csadmin.WizardPanelBase; +import com.netscape.cms.servlet.wizard.WizardServlet; + +public class CAInfoPanel extends WizardPanelBase { + + public CAInfoPanel() { + } + + public void init(ServletConfig config, int panelno) + throws ServletException { + setPanelNo(panelno); + setName("CA Information"); + } + + public void init(WizardServlet servlet, ServletConfig config, int panelno, String id) + throws ServletException { + setPanelNo(panelno); + setName("CA Information"); + setId(id); + } + + public boolean shouldSkip() { + return false; + } + + public void cleanUp() throws IOException { + IConfigStore cs = CMS.getConfigStore(); + cs.putString("preop.cainfo.select", ""); + } + + public boolean isPanelDone() { + IConfigStore cs = CMS.getConfigStore(); + try { + String s = cs.getString("preop.cainfo.select", ""); + if (s != null && !s.isEmpty()) { + return true; + } + } catch (EBaseException e) { + } + return false; + } + + public PropertySet getUsage() { + return new PropertySet(); + } + + public void display(HttpServletRequest request, + HttpServletResponse response, + Context context) { + String errorString = ""; + context.put("title", "CA Information"); + context.put("panel", "admin/console/config/cainfopanel.vm"); + IConfigStore config = CMS.getConfigStore(); + + if (isPanelDone()) { + // TODO - put the selected URL in selection box. + // String s = config.getString("preop.cainfo.select"); + } + + // get CA URLs + Vector<String> v; + try { + v = null; + v = ConfigurationUtils.getUrlListFromSecurityDomain(config, "CA", "SecurePort"); + if (v == null) { + errorString = "No CA found. CA, TKS and optionally DRM " + + " must be installed prior to TPS installation"; + context.put("errorString", errorString); + context.put("preop.cainfo.errorString", errorString); + return; + } + + config.putString("preop.ca.list", StringUtils.join(v,",")); + config.commit(false); + } catch (EBaseException | IOException | SAXException | ParserConfigurationException e) { + e.printStackTrace(); + errorString = "Failed to get CA information from security domain. " + e; + context.put("errorString", errorString); + context.put("preop.cainfo.errorString", errorString); + return; + } + + context.put("urls", v); + context.put("urls_size", v.size()); + context.put("errorString", ""); + context.put("preop.cainfo.errorString", ""); + } + + public void validate(HttpServletRequest request, + HttpServletResponse response, + Context context) throws IOException { + } + + public void update(HttpServletRequest request, + HttpServletResponse response, + Context context) throws IOException { + IConfigStore config = CMS.getConfigStore(); + String subsystemNick; + try { + subsystemNick = config.getString("preop.cert.subsystem.nickname"); + } catch (EBaseException e1) { + e1.printStackTrace(); + throw new IOException("Failed to get subsystem certificate nickname"); + } + + String url = HttpInput.getString(request, "urls"); + URI caUri = null; + String parsedURI = url.substring(url.lastIndexOf("http")); + try { + caUri = new URI(parsedURI); + } catch (URISyntaxException e) { + throw new IOException("Invalid URI " + parsedURI); + } + + // TODO: get installer from session + TPSInstaller installer = new TPSInstaller(); + installer.configureCAConnector(caUri, subsystemNick); + + String host = caUri.getHost(); + int port = caUri.getPort(); + + // Note - + // list contains EE port. If admin port is different, it needs to + // be obtained from security domain and used to get the cert chain + + /* int admin_port = ConfigurationUtils.getPortFromSecurityDomain(domainXML, + host, port, "CA", "SecurePort", "SecureAdminPort"); + */ + + try { + ConfigurationUtils.importCertChain(host, port, "/ca/admin/ca/getCertChain", "ca"); + } catch (CertificateException | SAXException | ParserConfigurationException + | NotInitializedException | TokenException | EBaseException e) { + e.printStackTrace(); + throw new IOException("Failed to import certificate chain from CA"); + } + + context.put("updateStatus", "success"); + } + + public void displayError(HttpServletRequest request, + HttpServletResponse response, + Context context) { + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/installer/DRMInfoPanel.java b/base/tps/src/org/dogtagpki/server/tps/installer/DRMInfoPanel.java new file mode 100644 index 000000000..2237f9e53 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/installer/DRMInfoPanel.java @@ -0,0 +1,154 @@ +package org.dogtagpki.server.tps.installer; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Vector; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.lang.StringUtils; +import org.apache.velocity.context.Context; +import org.xml.sax.SAXException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.property.PropertySet; +import com.netscape.certsrv.util.HttpInput; +import com.netscape.cms.servlet.csadmin.ConfigurationUtils; +import com.netscape.cms.servlet.csadmin.WizardPanelBase; +import com.netscape.cms.servlet.wizard.WizardServlet; + +public class DRMInfoPanel extends WizardPanelBase { + + public DRMInfoPanel() { + } + + public void init(ServletConfig config, int panelno) + throws ServletException { + setPanelNo(panelno); + setName("DRM Information"); + } + + public void init(WizardServlet servlet, ServletConfig config, int panelno, String id) + throws ServletException { + setPanelNo(panelno); + setName("DRM Information"); + setId(id); + } + + public boolean shouldSkip() { + return false; + } + + public void cleanUp() throws IOException { + IConfigStore cs = CMS.getConfigStore(); + cs.putString("preop.krainfo.select", ""); + } + + public boolean isPanelDone() { + IConfigStore cs = CMS.getConfigStore(); + try { + String s = cs.getString("preop.krainfo.select", ""); + if (s != null && ! s.isEmpty()) { + return true; + } + } catch (EBaseException e) { + } + return false; + } + + public PropertySet getUsage() { + return new PropertySet(); + } + + public void display(HttpServletRequest request, + HttpServletResponse response, + Context context) { + String errorString = ""; + context.put("title", "DRM Information"); + context.put("panel", "admin/console/config/drminfopanel.vm"); + IConfigStore config = CMS.getConfigStore(); + + if (isPanelDone()) { + //TODO - put selected entry in selection box. + //String s = config.getString("preop.krainfo.select"); + } + + // get KRA URLs + Vector<String> v = null; + try { + v = ConfigurationUtils.getUrlListFromSecurityDomain(config, "KRA", "SecurePort"); + if (v == null) { + errorString = "No DRM found. CA, TKS and optionally DRM " + + " must be installed prior to TPS installation"; + context.put("errorString", errorString); + context.put("preop.krainfo.errorString", errorString); + return; + } + + config.putString("preop.kra.list", StringUtils.join(v,",")); + config.commit(false); + } catch (EBaseException | IOException | SAXException | ParserConfigurationException e1) { + e1.printStackTrace(); + errorString = "Failed to get DRM information from security domain. " + e1; + context.put("errorString", errorString); + context.put("preop.krainfo.errorString", errorString); + return; + } + + context.put("urls", v); + context.put("urls_size", v.size()); + context.put("errorString", ""); + context.put("preop.krainfo.errorString", ""); + } + + public void validate(HttpServletRequest request, + HttpServletResponse response, + Context context) throws IOException { + } + + public void update(HttpServletRequest request, + HttpServletResponse response, + Context context) throws IOException { + IConfigStore config = CMS.getConfigStore(); + String subsystemNick; + try { + subsystemNick = config.getString("preop.cert.subsystem.nickname"); + } catch (EBaseException e1) { + e1.printStackTrace(); + throw new IOException("Failed to get subsystem certificate nickname"); + } + + String url = HttpInput.getString(request, "urls"); + String parsedURI = url.substring(url.lastIndexOf("http")); + URI kraUri = null; + try { + kraUri = new URI(parsedURI); + } catch (URISyntaxException e) { + throw new IOException("Invalid URI " + parsedURI); + } + + String choice = HttpInput.getString(request, "choice"); + boolean keyGen = choice.equalsIgnoreCase("keygen"); + + // TODO: get installer from session + TPSInstaller installer = new TPSInstaller(); + installer.configureKRAConnector(keyGen, kraUri, subsystemNick); + + context.put("updateStatus", "success"); + } + + /** + * If validate() returns false, this method will be called. + */ + public void displayError(HttpServletRequest request, + HttpServletResponse response, + Context context) { + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/installer/TKSInfoPanel.java b/base/tps/src/org/dogtagpki/server/tps/installer/TKSInfoPanel.java new file mode 100644 index 000000000..0c0cf62a7 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/installer/TKSInfoPanel.java @@ -0,0 +1,150 @@ +package org.dogtagpki.server.tps.installer; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Vector; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.lang.StringUtils; +import org.apache.velocity.context.Context; +import org.xml.sax.SAXException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.property.PropertySet; +import com.netscape.certsrv.util.HttpInput; +import com.netscape.cms.servlet.csadmin.ConfigurationUtils; +import com.netscape.cms.servlet.csadmin.WizardPanelBase; +import com.netscape.cms.servlet.wizard.WizardServlet; + +public class TKSInfoPanel extends WizardPanelBase { + + public TKSInfoPanel() { + } + + public void init(ServletConfig config, int panelno) + throws ServletException { + setPanelNo(panelno); + setName("TKS Information"); + } + + public void init(WizardServlet servlet, ServletConfig config, int panelno, String id) + throws ServletException { + setPanelNo(panelno); + setName("TKS Information"); + setId(id); + } + + public boolean shouldSkip() { + return false; + } + + public void cleanUp() throws IOException { + IConfigStore cs = CMS.getConfigStore(); + cs.putString("preop.tksinfo.select", ""); + } + + public boolean isPanelDone() { + IConfigStore cs = CMS.getConfigStore(); + try { + String s = cs.getString("preop.tksinfo.select", ""); + if (s != null && !s.isEmpty()) { + return true; + } + } catch (EBaseException e) { + } + return false; + } + + public PropertySet getUsage() { + return new PropertySet(); + } + + + public void display(HttpServletRequest request, + HttpServletResponse response, + Context context) { + String errorString = ""; + context.put("title", "TKS Information"); + context.put("panel", "admin/console/config/tksinfopanel.vm"); + IConfigStore config = CMS.getConfigStore(); + + if (isPanelDone()) { + // TODO - put selected URL in selection box + // String s = config.getString("preop.tksinfo.select"); + } + + // get TKS URLs + Vector<String> v = null; + try { + v = ConfigurationUtils.getUrlListFromSecurityDomain(config, "TKS", "SecureAdminPort"); + if (v == null) { + errorString = "No TKS found. CA, TKS and optionally DRM " + + " must be installed prior to TPS installation"; + context.put("errorString", errorString); + context.put("preop.tksinfo.errorString", errorString); + return; + } + + config.putString("preop.tks.list", StringUtils.join(v,",")); + config.commit(false); + } catch (EBaseException | IOException | SAXException | ParserConfigurationException e) { + e.printStackTrace(); + errorString = "Failed to get TKS information from security domain. " + e; + context.put("errorString", errorString); + context.put("preop.tksinfo.errorString", errorString); + return; + } + + context.put("urls", v); + context.put("urls_size", v.size()); + context.put("errorString", ""); + context.put("preop.tksinfo.errorString", ""); + } + + public void validate(HttpServletRequest request, + HttpServletResponse response, + Context context) throws IOException { + } + + public void update(HttpServletRequest request, + HttpServletResponse response, + Context context) throws IOException { + IConfigStore config = CMS.getConfigStore(); + String subsystemNick; + try { + subsystemNick = config.getString("preop.cert.subsystem.nickname"); + } catch (EBaseException e1) { + e1.printStackTrace(); + throw new IOException("Failed to get subsystem certificate nickname"); + } + + String url = HttpInput.getString(request, "urls"); + String parsedURI = url.substring(url.lastIndexOf("http")); + URI tksUri = null; + try { + tksUri = new URI(parsedURI); + } catch (URISyntaxException e) { + throw new IOException("Invalid URI " + parsedURI); + } + + // TODO: get installer from session + TPSInstaller installer = new TPSInstaller(); + installer.configureTKSConnector(tksUri, subsystemNick); + + context.put("updateStatus", "success"); + } + + public void displayError(HttpServletRequest request, + HttpServletResponse response, + Context context) { + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/installer/TPSInstaller.java b/base/tps/src/org/dogtagpki/server/tps/installer/TPSInstaller.java new file mode 100644 index 000000000..2779e5b98 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/installer/TPSInstaller.java @@ -0,0 +1,124 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.installer; + +import java.net.URI; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.config.ConnectorDatabase; +import org.dogtagpki.server.tps.config.ConnectorRecord; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.PKIException; + +/** + * Utility class for TPS installation to be used both by the RESTful installer + * and the UI Panels. + * + * @author alee + * + */ + +public class TPSInstaller { + + public TPSInstaller() { + } + + public void configureCAConnector(URI uri, String nickname) { + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + IConfigStore cs = CMS.getConfigStore(); + + // TODO: see if this is only needed by wizard-based installation + cs.putString("preop.cainfo.select", uri.toString()); + + try { + database.addCAConnector(uri.getHost(), uri.getPort(), nickname); + + } catch (Exception e) { + throw new PKIException("Unable to create CA connector", e); + } + } + + public void configureTKSConnector(URI uri, String nickname) { + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + IConfigStore cs = CMS.getConfigStore(); + + // TODO: see if this is only needed by wizard-based installation + cs.putString("preop.tksinfo.select", uri.toString()); + + try { + database.addTKSConnector(uri.getHost(), uri.getPort(), nickname, false); + + } catch (Exception e) { + throw new PKIException("Unable to create TKS connector", e); + } + } + + public void configureKRAConnector(Boolean keygen, URI uri, String nickname) { + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + IConfigStore cs = CMS.getConfigStore(); + + if (keygen) { + // TODO: see if this is only needed by wizard-based installation + cs.putString("preop.krainfo.select", uri.toString()); + + // TODO: see if there are other profiles need to be configured + cs.putString("op.enroll.userKey.keyGen.encryption.serverKeygen.enable", "true"); + cs.putString("op.enroll.userKeyTemporary.keyGen.encryption.serverKeygen.enable", "true"); + cs.putString("op.enroll.soKey.keyGen.encryption.serverKeygen.enable", "true"); + cs.putString("op.enroll.soKeyTemporary.keyGen.encryption.serverKeygen.enable", "true"); + + try { + database.addKRAConnector(uri.getHost(), uri.getPort(), nickname); + + } catch (Exception e) { + throw new PKIException("Unable to create KRA connector", e); + } + + } else { // no keygen + // TODO: see if there are other profiles need to be configured + cs.putString("op.enroll.userKey.keyGen.encryption.serverKeygen.enable", "false"); + cs.putString("op.enroll.userKeyTemporary.keyGen.encryption.serverKeygen.enable", "false"); + cs.putString("op.enroll.userKey.keyGen.encryption.recovery.destroyed.scheme", "GenerateNewKey"); + cs.putString("op.enroll.userKeyTemporary.keyGen.encryption.recovery.onHold.scheme", "GenerateNewKey"); + cs.putString("op.enroll.soKey.keyGen.encryption.serverKeygen.enable", "false"); + cs.putString("op.enroll.soKeyTemporary.keyGen.encryption.serverKeygen.enable", "false"); + cs.putString("op.enroll.soKey.keyGen.encryption.recovery.destroyed.scheme", "GenerateNewKey"); + cs.putString("op.enroll.soKeyTemporary.keyGen.encryption.recovery.onHold.scheme", "GenerateNewKey"); + } + + try { + String id = "tks1"; // there is only one default TKS connector + + // update keygen in TKS connector + ConnectorRecord record = database.getRecord(id); + record.setProperty(database.prefix + "." + id + ".serverKeygen", keygen.toString()); + database.updateRecord(id, record); + + } catch (Exception e) { + throw new PKIException("Unable to update TKS connector", e); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/main/AttributeSpec.java b/base/tps/src/org/dogtagpki/server/tps/main/AttributeSpec.java new file mode 100644 index 000000000..444f22c4b --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/main/AttributeSpec.java @@ -0,0 +1,71 @@ +package org.dogtagpki.server.tps.main; + +import org.dogtagpki.tps.main.TPSBuffer; + +public class AttributeSpec { + + public AttributeSpec() { + } + + private long id; + private byte type; + private TPSBuffer data; + + public void setAttributeID(long attribute_id) { + id = attribute_id; + } + + public long getAttributeID() { + return id; + } + + public void setType(byte type) { + this.type = type; + } + + public byte getType() { + return type; + } + + public void setData(TPSBuffer data) { + this.data = data; + + } + + public TPSBuffer getData() { + TPSBuffer theData = new TPSBuffer(); + theData.addLong4Bytes(id); + theData.add(type); + + if (type == 0) { /* String */ + theData.addInt2Bytes(data.size()); + } + theData.add(data); + return theData; + } + + public TPSBuffer getValue() { + return data; + } + + public static AttributeSpec parse(TPSBuffer b, int offset) { + AttributeSpec o = new AttributeSpec(); + + long id = b.getLongFrom4Bytes(offset); + + o.setAttributeID(id); + + o.setType(b.at(offset + 4)); + // DatatypeString contains two bytes for AttributeLen of AttributeData + TPSBuffer theData; + if (o.getType() == (byte) 0) + theData = b.substr(offset + 5 + 2, b.size() - 5 - 2); + else + theData = b.substr(offset + 5, b.size() - 5); + + o.setData(theData); + return o; + + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/main/ObjectSpec.java b/base/tps/src/org/dogtagpki/server/tps/main/ObjectSpec.java new file mode 100644 index 000000000..a8dbdb1bc --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/main/ObjectSpec.java @@ -0,0 +1,456 @@ +package org.dogtagpki.server.tps.main; + +import java.util.ArrayList; + +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.TPSException; + +import sun.security.pkcs11.wrapper.PKCS11Constants; + +import com.netscape.certsrv.apps.CMS; + +public class ObjectSpec { + + public ObjectSpec() + { + attributeSpecs = new ArrayList<AttributeSpec>(); + } + + final static int DATATYPE_STRING = 0; + final static int DATATYPE_INTEGER = 1; + final static int DATATYPE_BOOL_FALSE = 2; + final static int DATATYPE_BOOL_TRUE = 3; + + private long objectID; + private long fixedAttributes; + private ArrayList<AttributeSpec> attributeSpecs; + private int parseRead; + + public int getParseReadSize() { + return parseRead; + } + + /** + * Parse 'c' object. + */ + public static void parseAttributes(String objectID, ObjectSpec objectSpec, TPSBuffer b) + { + int curpos = 7; + long fixedAttrs = 0; + int xclass = 0; + int id = 0; + + /* skip first 7 bytes */ + + while (curpos < ((b.size()))) { + long attribute_id = b.getLongFrom4Bytes(curpos); + int attribute_size = b.getIntFrom2Bytes(curpos + 4); + + byte type = 0; + TPSBuffer data = new TPSBuffer(); + boolean found = false; + /* modify fixed attributes */ + + switch ((int) attribute_id) { + case (int) PKCS11Constants.CKA_TOKEN: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00000080; + } + break; + case (int) PKCS11Constants.CKA_PRIVATE: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00000100; + } else { + } + break; + case (int) PKCS11Constants.CKA_MODIFIABLE: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00000200; + } + break; + case (int) PKCS11Constants.CKA_DERIVE: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00000400; + } + break; + case (int) PKCS11Constants.CKA_LOCAL: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00000800; + } + break; + case (int) PKCS11Constants.CKA_ENCRYPT: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00001000; + } + break; + case (int) PKCS11Constants.CKA_DECRYPT: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00002000; + } + break; + case (int) PKCS11Constants.CKA_WRAP: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00004000; + } + break; + case (int) PKCS11Constants.CKA_UNWRAP: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00008000; + } + break; + case (int) PKCS11Constants.CKA_SIGN: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00010000; + } + break; + case (int) PKCS11Constants.CKA_SIGN_RECOVER: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00020000; + } + break; + case (int) PKCS11Constants.CKA_VERIFY: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00040000; + } + break; + case (int) PKCS11Constants.CKA_VERIFY_RECOVER: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00080000; + } + break; + case (int) PKCS11Constants.CKA_SENSITIVE: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00100000; + } + break; + case (int) PKCS11Constants.CKA_ALWAYS_SENSITIVE: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00200000; + } + break; + case (int) PKCS11Constants.CKA_EXTRACTABLE: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00400000; + } + break; + case (int) PKCS11Constants.CKA_NEVER_EXTRACTABLE: + if (b.at(curpos + 6) != 0) { + fixedAttrs |= 0x00800000; + } + break; + case (int) PKCS11Constants.CKA_SUBJECT: + type = DATATYPE_STRING; + data = b.substr(curpos + 6, attribute_size); + /* build by PKCS11 */ + break; + case (int) PKCS11Constants.CKA_LABEL: + type = DATATYPE_STRING; + data = b.substr(curpos + 6, attribute_size); + found = true; + break; + case (int) PKCS11Constants.CKA_MODULUS: + type = DATATYPE_STRING; + data = b.substr(curpos + 6, attribute_size); + /* build by PKCS11 */ + break; + case (int) PKCS11Constants.CKA_ID: + type = DATATYPE_STRING; + data = b.substr(curpos + 6, attribute_size); + /* build by PKCS11 */ + break; + case (int) PKCS11Constants.CKA_KEY_TYPE: + type = DATATYPE_INTEGER; + data = b.substr(curpos + 6, 4); + found = true; + /* build by PKCS11 */ + break; + case (int) PKCS11Constants.CKA_CLASS: + type = DATATYPE_INTEGER; + data = b.substr(curpos + 6, 4); + xclass = data.at(0); + /* build by PKCS11 */ + break; + case (int) PKCS11Constants.CKA_PUBLIC_EXPONENT: + type = DATATYPE_STRING; + data = b.substr(curpos + 6, attribute_size); + /* build by PKCS11 */ + break; + case (int) PKCS11Constants.CKA_CERTIFICATE_TYPE: + type = DATATYPE_INTEGER; + data = b.substr(curpos + 6, 4); + /* build by PKCS11 */ + break; + + case (int) PKCS11Constants.CKA_EC_PARAMS: + type = DATATYPE_STRING; + data = b.substr(curpos + 6, attribute_size); + found = true; + break; + + case (int) PKCS11Constants.CKA_EC_POINT: + type = DATATYPE_STRING; + data = b.substr(curpos + 6, attribute_size); + found = true; + break; + default: + CMS.debug("ObjectSpec.parseKeyBlob" + + "skipped attribute_id = " + + attribute_id); + break; + } + + if (found) { + /* add attribute spec */ + AttributeSpec attrSpec = new AttributeSpec(); + attrSpec.setAttributeID(attribute_id); + attrSpec.setType(type); + + switch (type) { + case DATATYPE_STRING: + attrSpec.setData(data); + break; + case DATATYPE_INTEGER: + attrSpec.setData(data); + break; + case DATATYPE_BOOL_FALSE: + break; + case DATATYPE_BOOL_TRUE: + break; + default: + break; + } + + objectSpec.addAttributeSpec(attrSpec); + } + + curpos += 4 + 2 + attribute_size; + } + + //Here the objectID fixed attribute gets massaged. Here's how: + // The objectID becomes the cert container id, ex: 01 + // Each key pair associated with the cert must have the same ID. + // This is done by math using the following formula: + // Given a cert id of "2", the keyAttrIds of the keys are originally + // configured as k4 and k5. Note that one is twice the cert id, and + // the other is twice the cert id plus 1. In order to map the key ids + // down to the cert's id, the code below changes both "4" and "5" back + // to "2". + + int val = (objectID.charAt(1) - '0'); + switch (objectID.charAt(0)) { + case 'c': + + id = val; + + break; + case 'k': + if ((val % 2) != 0) { + id = (val - 1) / 2; + } else { + id = (val / 2); + + } + + break; + } + + objectSpec.setFixedAttributes(fixedAttrs | (xclass << 4) | id); + } + + /** + * Parse 'c' object. + */ + public static void parseCertificateAttributes(String objectID, ObjectSpec objectSpec, TPSBuffer b) + { + parseAttributes(objectID, objectSpec, b); + } + + /** + * Parse 'k' object. + */ + public static void parseKeyAttributes(String objectID, ObjectSpec objectSpec, TPSBuffer b) + { + parseAttributes(objectID, objectSpec, b); + } + + /** + * Parse 'C' object. + */ + public static void parseCertificateBlob(String objectID, ObjectSpec objectSpec, TPSBuffer b) + { + long fixedAttrs = 0; + int xclass = 0; + int id = 0; + + AttributeSpec value = new AttributeSpec(); + value.setAttributeID((int) PKCS11Constants.CKA_VALUE); + value.setType((byte) DATATYPE_STRING); + value.setData(b); + objectSpec.addAttributeSpec(value); + + fixedAttrs = 0x00000080; /* CKA_TOKEN */ + xclass = (int) PKCS11Constants.CKO_CERTIFICATE; + id = objectID.charAt(1) - '0'; + + objectSpec.setFixedAttributes(fixedAttrs | (xclass << 4) | id); + } + + /** + * Convert object from token into object spec. + * + * Reference: + * http://netkey/design/applet_readable_object_spec-0.1.txt + * http://netkey/design/pkcs11obj.txt + * + * @throws TPSException + */ + public static ObjectSpec parseFromTokenData(long objid, TPSBuffer b) throws TPSException + { + String objectID = null; + + StringBuilder idBuilder = new StringBuilder(); + + ObjectSpec o = new ObjectSpec(); + o.setObjectID(objid); + + char[] b1 = new char[4]; + b1[0] = (char) ((objid >> 24) & 0xff); + b1[1] = (char) ((objid >> 16) & 0xff); + b1[2] = (char) ((objid >> 8) & 0xff); + b1[3] = (char) (objid & 0xff); + + idBuilder.append(b1[0]); + idBuilder.append(b1[1]); + idBuilder.append(b1[2]); + idBuilder.append(b1[3]); + + objectID = idBuilder.toString(); + switch (b1[0]) { + case 'c': /* certificate attributes */ + parseCertificateAttributes(objectID, o, b); + break; + case 'k': /* public key or private key attributes */ + parseKeyAttributes(objectID, o, b); + break; + case 'C': /* certificate in DER */ + parseCertificateBlob(objectID, o, b); + break; + default: + CMS.debug("ObjectSpec::ParseKeyBlob" + + "unknown objectID = " + objectID.charAt(0)); + throw new TPSException("ObjectSpec parseFromToken data: Invalid object type, aborting.."); + } + + return o; + } + + public static ObjectSpec parse(TPSBuffer b, int offset) throws TPSException + { + int sum = 0; + + if ((b.size() - offset) < 10) + return null; + + ObjectSpec o = new ObjectSpec(); + long id = b.getLongFrom4Bytes(offset); + + o.setObjectID(id); + long attribute = b.getLongFrom4Bytes(offset + 4); + + o.setFixedAttributes(attribute); + int count = b.getIntFrom2Bytes(offset + 8); + sum += 10; + int curpos = offset + 10; + for (int i = 0; i < count; i++) { + int len = 0; + switch (b.at(curpos + 4)) { + case DATATYPE_STRING: + len = 4 + 1 + 2 + b.getIntFrom2Bytes(curpos + 5); + break; + case DATATYPE_INTEGER: + len = 4 + 1 + 4; + break; + case DATATYPE_BOOL_FALSE: + len = 4 + 1; + break; + case DATATYPE_BOOL_TRUE: + len = 4 + 1; + break; + default: + CMS.debug("ObjectSpec::parse" + + "unknown DataType = " + b.at(curpos + 4)); + throw new TPSException("ObjectSpec parse: Invalid data type, aborting.."); + } + TPSBuffer attr = b.substr(curpos, len); + AttributeSpec attrSpec = AttributeSpec.parse(attr, 0); + o.addAttributeSpec(attrSpec); + curpos += len; + sum += len; + } + o.setParseRead(sum); + return o; + } + + private void setParseRead(int nread) { + parseRead = nread; + } + + void setObjectID(long v) + { + objectID = v; + } + + public long getObjectID() + { + return objectID; + } + + public void setFixedAttributes(long v) + { + fixedAttributes = v; + } + + public long getFixedAttributes() + { + return fixedAttributes; + } + + public int getAttributeSpecCount() + { + return attributeSpecs.size(); + } + + public AttributeSpec getAttributeSpec(int p) + { + return attributeSpecs.get(p); + } + + public void addAttributeSpec(AttributeSpec p) + { + attributeSpecs.add(p); + } + + public void removeAttributeSpec(int p) + { + attributeSpecs.remove(p); + + } + + TPSBuffer getData() + { + TPSBuffer data = new TPSBuffer(); + + data.addLong4Bytes(objectID); + data.addLong4Bytes(fixedAttributes); + + int attributeCount = getAttributeSpecCount(); + data.addInt2Bytes(attributeCount); + for (int i = 0; i < attributeCount; i++) { + AttributeSpec spec = getAttributeSpec(i); + data.add(spec.getData()); + } + + return data; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/main/PKCS11Obj.java b/base/tps/src/org/dogtagpki/server/tps/main/PKCS11Obj.java new file mode 100644 index 000000000..6224c3f81 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/main/PKCS11Obj.java @@ -0,0 +1,689 @@ +package org.dogtagpki.server.tps.main; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.main.Util; + +import sun.security.pkcs11.wrapper.PKCS11Constants; + +import com.netscape.certsrv.apps.CMS; + +public class PKCS11Obj { + + private ArrayList<ObjectSpec> objectSpecs; + + public PKCS11Obj() { + objectSpecs = new ArrayList<ObjectSpec>(); + } + + private int oldFormatVersion; + private int oldObjectVersion; + + private int formatVersion; + private int objectVersion; + + private TPSBuffer tokenName; + private TPSBuffer cuid; + + public static PKCS11Obj parse(TPSBuffer b, int offset) throws TPSException, DataFormatException, IOException + { + PKCS11Obj o = new PKCS11Obj(); + + int formatVersion = b.getIntFrom2Bytes(0); + + CMS.debug("PKCS11Obj.parse: formatVersion read from blob: " + formatVersion); + + o.setFormatVersion(formatVersion); + int objectVersion = b.getIntFrom2Bytes(2); + + CMS.debug("PKCS11Obj.parse: objectVersion read from blob: " + objectVersion); + + o.setObjectVersion(objectVersion); + o.setCUID(b.substr(offset + 4, 10)); + + int compressionType = b.getIntFrom2Bytes(14); + int dataSize = b.getIntFrom2Bytes(16); + + int dataOffset = b.getIntFrom2Bytes(18); + + CMS.debug("PKCS11Obj.parse: commpressionType: " + compressionType + " DataSize:" + + dataSize + "DataOffset: " + dataOffset + " data: " + b.toHexString()); + + TPSBuffer data = new TPSBuffer(); + + if (compressionType == 0) { /* no compression */ + data.add(b.substr(offset + dataOffset, dataSize)); + } else if (compressionType == 1) { /* zlib */ + TPSBuffer compressedData = b.substr(offset + dataOffset, dataSize); + + TPSBuffer uncompressedData = uncompress( + compressedData); + + data = new TPSBuffer(uncompressedData); + } else { + throw new TPSException("PKCS11Obj.parse: error parsing object data!"); + } + + CMS.debug("PKCS11Obj.parse: uncompressed data: " + data.toHexString()); + + int objOffset = data.getIntFrom2Bytes(0); + int objCount = data.getIntFrom2Bytes(2); + + //Check for absurd number of objects + if (objCount < 0 || objCount > 100) { + throw new TPSException("PKCS11Obj.parse: error parsing object data!"); + } + + TPSBuffer tokenName = data.substr(5, data.at(4)); + o.setTokenName(tokenName); + + if (tokenName != null) + System.out.println("tokenName: " + tokenName.toHexString()); + System.out.println("uncompressed data size: " + data.size()); + + CMS.debug("PKCS11Obj.parse" + "objcount = " + objCount); + + int curpos = objOffset; + int nread = 0; + for (int i = 0; i < objCount; i++) { + CMS.debug("PKCS11Ob.parse: working on object " + i); + ObjectSpec objSpec = ObjectSpec.parse(data, curpos); + + if (objSpec == null) + continue; + + nread = objSpec.getParseReadSize(); + o.addObjectSpec(objSpec); + + long oid = objSpec.getObjectID(); + char[] b1 = new char[2]; + + b1[0] = (char) ((oid >> 24) & 0xff); + b1[1] = (char) ((oid >> 16) & 0xff); + + CMS.debug("PKCS11Obj.parse " + "About to parse = " + b1[0] + ":" + b1[1]); + System.out.println("PKCS11Obj.parse " + "About to parse = " + b1[0] + ":" + b1[1]); + + // add corresponding 'C' object for 'c' + if (b1[0] == 'c') { + for (int j = 0; j < objSpec.getAttributeSpecCount(); j++) { + AttributeSpec as = objSpec.getAttributeSpec(j); + if (as.getAttributeID() == PKCS11Constants.CKA_VALUE) { + if (as.getType() == (byte) 0) { + TPSBuffer cert = as.getValue(); + + long l1 = 0x43; // 'C' + long l2 = b1[1]; + + l1 = (l1 & 0xff) << 24; + l2 = (l2 & 0xff) << 16; + long certid = l1 + l2; + + ObjectSpec certSpec = + ObjectSpec.parseFromTokenData( + certid, cert); + o.addObjectSpec(certSpec); + + objSpec.removeAttributeSpec(j); + break; + } + } + } + + } + + curpos += nread; + } + return o; + } + + public boolean doesCertIdExist(String certId) { + + boolean foundObj = false; + char[] certChars = certId.toCharArray(); + + for (ObjectSpec objSpec : objectSpecs) { + + long oid = objSpec.getObjectID(); + + char[] b1 = new char[2]; + + b1[0] = (char) ((oid >> 24) & 0xff); + b1[1] = (char) ((oid >> 16) & 0xff); + + if (Arrays.equals(b1, certChars)) { + foundObj = true; + CMS.debug("PKCD11Obj.doesCertIdExist: match found!"); + break; + } + + } + + return foundObj; + } + + public void setFormatVersion(int v) + { + formatVersion = v; + } + + public void setObjectVersion(int v) + { + CMS.debug("PKCS11Obj.setObjectVersion: setting to: " + v); + objectVersion = v; + } + + public int getFormatVersion() + { + return formatVersion; + } + + public int getObjectVersion() + { + return objectVersion; + } + + public void setCUID(TPSBuffer cuid) + { + this.cuid = cuid; + ; + } + + public TPSBuffer getCUID() + { + return cuid; + } + + public void setTokenName(TPSBuffer tokenName) + { + this.tokenName = tokenName; + } + + public TPSBuffer getTokenName() + { + return tokenName; + } + + int getObjectSpecCount() + { + return objectSpecs.size(); + } + + public ObjectSpec getObjectSpec(int p) + { + return objectSpecs.get(p); + } + + public void addObjectSpec(ObjectSpec p) + { + CMS.debug("PKCS11Obj.adObjectSpec entering.. " + p); + for (ObjectSpec objSpec : objectSpecs) { + + long oid = objSpec.getObjectID(); + + if (oid == p.getObjectID()) { + objectSpecs.remove(objSpec); + char[] b1 = new char[2]; + + b1[0] = (char) ((oid >> 24) & 0xff); + b1[1] = (char) ((oid >> 16) & 0xff); + String oidStr = new String(b1); + CMS.debug("PKCS11Obj.addObjectSpec: found dup, removing...: " + oidStr); + break; + } + } + + objectSpecs.add(p); + } + + public void removeObjectSpec(int p) + { + objectSpecs.remove(p); + + } + + public void removeAllObjectSpecs() { + + objectSpecs.clear(); + } + + private TPSBuffer getRawHeaderData(int compressionType, TPSBuffer data) { + TPSBuffer header = new TPSBuffer(); + + CMS.debug("PKCS11Obj.getRawHeaderData: " + " formatVersion: " + formatVersion + " objectVersion: " + + objectVersion); + header.add((byte) ((formatVersion >> 8) & 0xff)); + header.add((byte) (formatVersion & 0xff)); + header.add((byte) ((objectVersion >> 8) & 0xff)); + header.add((byte) (objectVersion & 0xff)); + header.add(cuid); + // COMP_NONE = 0x00 + // COMP_ZLIB = 0x01 + + header.add((byte) ((compressionType >> 8) & 0xff)); + header.add((byte) (compressionType & 0xff)); + int compressedDataSize = data.size(); + header.add((byte) ((compressedDataSize >> 8) & 0xff)); + header.add((byte) (compressedDataSize & 0xff)); + int compressedDataOffset = 20; + header.add((byte) ((compressedDataOffset >> 8) & 0xff)); + header.add((byte) (compressedDataOffset & 0xff)); + + CMS.debug("PKCS11Obj.getRawHeaderData: returning: " + header.toHexString()); + + return header; + + } + + private TPSBuffer getRawData() { + TPSBuffer data = new TPSBuffer(); + + int objectOffset = tokenName.size() + 2 + 3; + + data.add((byte) ((objectOffset >> 8) & 0xff)); + data.add((byte) (objectOffset & 0xff)); + int objectCount = getObjectSpecCount(); + int objectCountX = objectCount; + if (objectCountX == 0) { + objectCountX = 0; + } else { + objectCountX = objectCountX - (objectCountX / 4); + } + + data.add((byte) ((objectCountX >> 8) & 0xff)); + data.add((byte) (objectCountX & 0xff)); + data.add((byte) (tokenName.size() & 0xff)); + data.add(tokenName); + + CMS.debug("PKCS11Obj:getRawData: objectCount: " + objectCount); + + for (int i = 0; i < objectCount; i++) { + ObjectSpec spec = getObjectSpec(i); + long objectID = spec.getObjectID(); + char c = (char) ((objectID >> 24) & 0xff); + long fixedAttrs = spec.getFixedAttributes(); + int xclass = (int) ((fixedAttrs & 0x70) >> 4); + char cont_id = (char) ((objectID >> 16) & 0xff); + long id = (int) (fixedAttrs & 0x0f); + /* locate all certificate objects */ + if (c == 'c' && xclass == PKCS11Constants.CKO_CERTIFICATE) { + + //We need to use the container id, there may be more than one cert + //with the same CKA_ID byte + + id = cont_id - '0'; + + /* locate the certificate object */ + for (int u = 0; u < objectCount; u++) { + ObjectSpec u_spec = getObjectSpec(u); + long u_objectID = u_spec.getObjectID(); + char u_c = (char) ((u_objectID >> 24) & 0xff); + long u_fixedAttrs = + u_spec.getFixedAttributes(); + int u_xclass = (int) ((u_fixedAttrs & 0x70) >> 4); + int u_id = (int) (u_fixedAttrs & 0x0f); + if (u_c == 'C' && u_xclass == PKCS11Constants.CKO_CERTIFICATE && u_id == id) { + CMS.debug("PKCSObj:getRawData: found cert object: id: " + id + " u_id: " + u_id); + + AttributeSpec u_attr = + u_spec.getAttributeSpec(0); + AttributeSpec n_attr = new AttributeSpec(); + n_attr.setAttributeID(u_attr.getAttributeID()); + n_attr.setType(u_attr.getType()); + n_attr.setData(u_attr.getValue()); + spec.addAttributeSpec(n_attr); + } + } + + data.add(spec.getData()); + + /* locate public object */ + for (int x = 0; x < objectCount; x++) { + ObjectSpec x_spec = getObjectSpec(x); + long x_fixedAttrs = + x_spec.getFixedAttributes(); + int x_xclass = (int) ((x_fixedAttrs & 0x70) >> 4); + int x_id = (int) (x_fixedAttrs & 0x0f); + if (x_xclass == PKCS11Constants.CKO_PUBLIC_KEY && x_id == id) { + CMS.debug("PKCSObj:getRawData: found public key object: id: " + id); + data.add(x_spec.getData()); + } + } + + /* locate private object */ + for (int y = 0; y < objectCount; y++) { + ObjectSpec y_spec = getObjectSpec(y); + long y_fixedAttrs = + y_spec.getFixedAttributes(); + int y_xclass = (int) ((y_fixedAttrs & 0x70) >> 4); + int y_id = (int) (y_fixedAttrs & 0x0f); + if (y_xclass == PKCS11Constants.CKO_PRIVATE_KEY && y_id == id) { + CMS.debug("PKCSObj:getRawData: found private key object: id: " + id); + data.add(y_spec.getData()); + } + } + } + } + + return data; + + } + + public TPSBuffer getData() + { + TPSBuffer data = getRawData(); + TPSBuffer header = getRawHeaderData(0, data); + + TPSBuffer result = new TPSBuffer(header); + result.add(data); + return result; + } + + public TPSBuffer getCompressedData() throws TPSException, IOException + { + TPSBuffer data = getRawData(); // new TPSBuffer(); + + CMS.debug("PKCS11Obj.getCompressedData: " + "before compress length = " + data.size()); + CMS.debug("PKCS11Obj.getCompressedData: " + "before compress data = " + data.toHexString()); + + System.out.println("Raw data before compress length: " + data.size()); + + TPSBuffer src_buffer = new TPSBuffer(data); + + CMS.debug("PKCS11Obj.getCompressedData: " + "sizeof src_buffer = " + src_buffer.size()); + CMS.debug("PKCS11Obj.getCompressedData: " + "data size = " + data.size()); + + TPSBuffer compressed = compress(src_buffer); + TPSBuffer header = getRawHeaderData(0x01, compressed); + + TPSBuffer result = new TPSBuffer(header); + result.add(compressed); + + CMS.debug("PKCS11Obj.getCompressedData: PKCS11 Data: " + result.toHexString()); + + return result; + } + + static private TPSBuffer compress(TPSBuffer uncompressedData) throws TPSException, IOException { + + if (uncompressedData == null) { + throw new TPSException("PKCS11Obj.uncompress: bad input data!"); + } + + byte[] data = uncompressedData.toBytesArray(); + + Deflater deflater = new Deflater(); + + deflater.setInput(data); + + byte[] buffer = new byte[1024]; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); + deflater.finish(); + while (!deflater.finished()) { + int count = deflater.deflate(buffer); + outputStream.write(buffer, 0, count); + } + outputStream.close(); + byte[] output = outputStream.toByteArray(); + CMS.debug("Original: " + data.length); + CMS.debug("Compressed: " + output.length); + + TPSBuffer result = new TPSBuffer(output); + + return result; + + } + + static private TPSBuffer uncompress(TPSBuffer compressedData) throws TPSException, DataFormatException, IOException { + + if (compressedData == null) { + throw new TPSException("PKCS11Obj.uncompress: bad input data!"); + } + byte[] data = compressedData.toBytesArray(); + + Inflater inflater = new Inflater(); + inflater.setInput(data); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); + byte[] buffer = new byte[1024]; + while (!inflater.finished()) { + int count = inflater.inflate(buffer); + outputStream.write(buffer, 0, count); + } + outputStream.close(); + byte[] output = outputStream.toByteArray(); + CMS.debug("Original: " + data.length); + CMS.debug("Uncompressed: " + output.length); + + TPSBuffer result = new TPSBuffer(output); + + return result; + } + + public static void main(String[] args) throws TPSException, DataFormatException, IOException { + + //Sample token data taken from previous server for + // testing this functionality. + + String compressedTokenData = + + "%01%00%38%4c%53%4e%06%50%00%01" + + "%00%00%01%08%00%01%04%03%00%14" + + "%78%9c%63%e0%61%e0%64%cf%ca%4d" + + "%4c%cf%4b%55%48%36%64%00%81%89" + + "%0c%4c%40%92%99%81%41%ac%38%33" + + "%3d%2f%33%2f%5d%21%3b%b5%52%21" + + "%2d%bf%48%01%a2%0e%28%29%c8%c0" + + "%f8%c7%a0%89%f1%07%10%cf%59%c0" + + "%cc%c4%c8%c4%c4%e8%69%c0%c3%c6" + + "%a1%d5%e6%71%ce%96%85%99%89%95" + + "%c1%20%d2%50%c1%40%8e%8d%39%94" + + "%85%4b%58%3c%28%35%45%c1%23%b1" + + "%44%21%38%35%b9%b4%28%b3%a4%52" + + "%c1%25%3f%37%31%33%cf%50%d4%40" + + "%18%a4%82%5b%98%a7%20%3b%53%37" + + "%39%51%37%2b%31%39%db%c8%50%ce" + + "%40%06%24%cc%2c%2c%ea%9c%5a%54" + + "%92%99%96%99%9c%58%92%aa%e0%58" + + "%5a%92%91%0f%d2%6d%20%27%ce%6b" + + "%68%62%60%68%60%61%60%64%68%69" + + "%6a%10%05%e4%5a%02%b9%e6%50%ae" + + "%81%a1%a1%b8%81%28%c4%6a%be%90" + + "%fc%ec%d4%3c%05%6f%a0%07%42%8b" + + "%53%8b%0c%c5%0c%44%d8%b8%38%27" + + "%a9%75%4e%fe%a4%93%c2%c8%28%cc" + + "%06%f1%92%41%24%d0%25%ec%60%d7" + + "%33%31%42%bd%c1%cc%c8%ce%ec%c4" + + "%c0%12%dc%1d%9d%b7%91%ef%a6%dd" + + "%8a%27%01%3f%cf%3e%3d%7e%23%78" + + "%4e%8f%8c%c4%89%f7%ad%4b%1c%92" + + "%0c%2e%1d%5d%b5%46%ad%7a%aa%d9" + + "%7b%13%c7%6d%07%fb%0d%67%f5%45" + + "%ce%3e%52%b6%fc%ed%14%87%8a%04" + + "%29%3d%4e%b5%b5%2e%0f%33%fc%17" + + "%57%1b%54%1a%f0%01%5d%23%cb%cf" + + "%c8%f8%9f%05%18%38%6c%07%c0%fe" + + "%93%15%64%01%fa%bf%51%10%e2%02" + + "%87%a2%d4%94%8c%c4%12%bd%e4%fc" + + "%5c%03%59%90%2c%1f%8b%18%8b%88" + + "%d6%6f%81%1f%09%45%05%16%cf%64" + + "%27%1d%fe%e2%99%df%c7%64%c1%37" + + "%cf%40%1e%24%ad%cc%22%61%20%d6" + + "%20%72%ec%2a%6b%5d%94%e1%bb%c0" + + "%6f%05%33%5a%0e%ec%90%bb%b6%29" + + "%b1%d4%d1%80%13%a4%40%98%85%c9" + + "%80%01%35%4e%98%3d%18%0c%5c%99" + + "%14%19%16%d6%14%64%1c%9b%d2%a4" + + "%90%6e%1b%e3%56%cc%d6%36%7f%5a" + + "%8b%1a%87%f9%79%a7%a8%92%24%c7" + + "%49%4b%59%02%1e%32%29%78%9a%f0" + + "%70%45%1c%57%b9%77%60%b7%82%c6" + + "%fc%fc%09%8b%f7%a8%ec%6e%51%3d" + + "%60%cb%58%b3%e5%5d%af%c0%f9%a6" + + "%c7%d9%c6%c0%44%c0%c3%b0%10%94" + + "%4a%18%81%10%9c%5c%18%1b%18%18" + + "%b8%10%81%07%14%68%64%60%70%a4" + + "%38%00%b3%8d%80%c6%0b%33%6e%04" + + "%a5%4a%5c%76%25%1b%81%53%ee%24" + + "%58%ca%95%4c%cd%4b%2e%aa%2c%28" + + "%c9%cc%cf%c3%9a%78%7f%03%13%ee" + + "%77%a4%c4%eb%35%50%89%d7%10%35" + + "%f1%1a%52%39%f1%06%de%54%65%71" + + "%10%e3%2a%fe%5e%aa%7a%9e%77%f3" + + "%fe%e6%0d%ed%02%0d%37%0e%33%71" + + "%2c%58%65%c4%2f%b9%53%6c%5d%f7" + + "%c4%53%07%d7%eb%5d%79%df%d8%fc" + + "%43%c3%c0%6b%f3%89%ef%47%96%3e" + + "%72%7e%9b%fc%f6%4c%83%77%bf%ce" + + "%5d%7b%35%cc%c4%cb%aa%40%74%e2" + + "%fd%7d%75%09%57%fb%01%4f%cd%ab" + + "%4b%b3%58%5b%f7%9f%bf%a7%be%4a" + + "%2c%85%82%c4%eb%ce%60%e0%c2%a4" + + "%10%1f%7c%75%eb%26%9f%83%2d%8f" + + "%f5%3a%76%b5%f2%d4%cf%3b%de%b7" + + "%bb%e9%56%8a%ef%f4%af%3c%31%3c" + + "%76%77%b9%1a%98%14%32%3e%09%cd" + + "%fc%c8%f9%f3%c9%b2%45%21%f3%b6" + + "%2c%56%0c%39%fb%2d%e5%7f%da%de" + + "%fd%ce%33%17%d4%c8%af%15%c8%dd" + + "%99%6d%0a%4a%2a%01%8b%88%49%bb" + + "%94%86%5f%b6%09%d0%78%81%85%9b" + + "%f0%a6%5d%63%70%da%9d%0c%4b%bb" + + "%32%38%d3%ae%02%58%29%96%b2%d7" + + "%84%be%c9%d7%d8%d0%c8%c8%00%98" + + "%50%4d%8c%c1%65%af%85%a1%11%28" + + "%09%83%b9%d4%4d%be%49%31%e1%27" + + "%cf%4e%93%9b%7a%35%6d%1b%db%e1" + + "%10%3b%ee%c5%1e%b6%1f%23%ee%7c" + + "%9c%b7%6e%ee%aa%57%a2%7f%95%fb" + + "%fd%3a%3b%ab%de%46%cb%ff%62%9e" + + "%ac%90%66%b3%f0%58%b3%dd%ed%38" + + "%e1%16%9f%a4%56%8d%fb%6b%db%96" + + "%ba%ef%aa%a4%28%f9%66%b5%2f%9d" + + "%36%cd%7b%52%6b%4b%68%e5%8d%57" + + "%92%92%5f%78%36%7e%31%a4%b4%ec" + + "%55%a8%95%da%7c%76%42%8a%19%a3" + + "%d9%f1%03%27%af%cb%14%fb%3b%44" + + "%cf%dc%b1%fc%a1%dc%8f%95%31%b9" + + "%5f%ff%2e%aa%b8%00%2c%9d%f7%b9" + + "%1e%bd%c5%2e%78%b4%48%4e%aa%7b" + + "%92%e1%16%5d%af%cd%1f%94%93%96" + + "%3b%27%c5%ed%ab%f6%0c%dd%cb%73" + + "%fd%7a%b6%39%38%fd%2e%26%26%fd" + + "%52%1a%80%d9%66%e0%f4%bb%19%5f" + + "%fa%05%00%21%15%7f%b0"; + + System.out.println("Test PKCS11 Blob data: " + compressedTokenData); + System.out.println("Test Data: Len: " + compressedTokenData.length()); + + // Test getting integer values from a TPSBuffer + + byte[] value = { (byte) 99, (byte) 49, (byte) 0, (byte) 0 }; + + TPSBuffer valBuf = new TPSBuffer(value); + + long l1 = valBuf.getLongFrom4Bytes(0); + + int i1 = valBuf.getIntFrom2Bytes(0); + + int i2 = valBuf.getIntFrom1Byte(0); + + System.out.println("4 bytes long: " + l1 + " 2 bytes int: " + i1 + " 1 byte int: " + i2); + + // Now test the parsing and un-parsing of the data, the result at the end should be + // the same as the original data. + // The data above is an exact copy of a blob taken off of a real token in the + // old TPS. + + byte[] decoded = Util.uriDecodeFromHex(compressedTokenData); + + System.out.println("decoded compressed datat size: " + decoded.length); + + // This is buffer containing sample copressed pkcs#11 blob. + TPSBuffer tokenData = new TPSBuffer(decoded); + + // Parse the given token data into PKCS#11 objects and attributes + + PKCS11Obj object = PKCS11Obj.parse(tokenData, 0); + + String certId = "C1"; + boolean exists = object.doesCertIdExist(certId); + + System.out.println("CertID " + certId + " exists: " + exists); + + // This gets the compressed blob that will go out to token of the parsed data. + TPSBuffer implodedData = object.getCompressedData(); + + System.out.println("imploded token data size: " + implodedData.size()); + + String encodedImplodedData = implodedData.toHexString(); + + System.out.println("encodedImplodedData: " + encodedImplodedData); + + // Now test to see if both blobs are identical + + boolean identical = implodedData.equals(tokenData); + + System.out.println("Before and after comparison result: Are the blobs identical?: " + identical); + + } + + public int getOldFormatVersion() { + return oldFormatVersion; + } + + public void setOldFormatVersion(int oldFormatVersion) { + this.oldFormatVersion = oldFormatVersion; + } + + public int getOldObjectVersion() { + return oldObjectVersion; + } + + public void setOldObjectVersion(int oldObjectVersion) { + this.oldObjectVersion = oldObjectVersion; + } + + public int getNextFreeCertIdNumber() { + + int highest_cert_id = 0; + + int numObjs = getObjectSpecCount(); + + for (int i = 0; i < numObjs; i++) { + ObjectSpec os = getObjectSpec(i); + if (os == null) + continue; + + long objid = os.getObjectID(); + + char[] b1 = new char[2]; + + b1[0] = (char) ((objid >> 24) & 0xff); + b1[1] = (char) ((objid >> 16) & 0xff); + + if (b1[0] == 'C') { //found a certificate + + int id_int = b1[1] - '0'; + + if (id_int > highest_cert_id) { + highest_cert_id = id_int; + } + } + + } + + highest_cert_id++; + CMS.debug("TPSEnrollProcessor.getNextFreeCertIdNumber: returning: " + highest_cert_id); + + return highest_cert_id; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/AppletInfo.java b/base/tps/src/org/dogtagpki/server/tps/processor/AppletInfo.java new file mode 100644 index 000000000..bc46eaba8 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/processor/AppletInfo.java @@ -0,0 +1,115 @@ +package org.dogtagpki.server.tps.processor; + +import org.dogtagpki.tps.main.TPSBuffer; + + +public class AppletInfo { + + private byte majorVersion; + private byte minorVersion; + private byte appMajorVersion; + private byte appMinorVersion; + + private TPSBuffer aid; + private TPSBuffer cuid; + private TPSBuffer msn; + private int totalMem; + private int freeMem; + + public AppletInfo(byte appletMajorVer,byte appletMinorVer,byte appMajorVer,byte appMinorVer) { + + majorVersion = appletMajorVer; + minorVersion = appletMinorVer; + appMajorVersion = appMajorVer; + appMinorVersion = appMinorVer; + + } + + public void setCUID(TPSBuffer theCuid) { + cuid = new TPSBuffer(theCuid); + } + + public TPSBuffer getCUID() { + return cuid; + } + + public void setMSN(TPSBuffer theMsn) { + msn = new TPSBuffer(theMsn); + } + + public TPSBuffer getMSN() { + return msn; + } + + public String getCUIDhexString(){ + if(cuid != null) { + return cuid.toHexString(); + } + + return null; + } + + /* + * getCUIDhexString2 returns hex string without the '%' + */ + public String getCUIDhexStringPlain() { + if(cuid != null) { + return cuid.toHexStringPlain(); + } + + return null; + } + + public String getMSNString() { + if(msn != null) { + return msn.toHexString(); + } + return null; + } + + public byte getMajorVersion() { + return majorVersion; + } + + public byte getMinorVersion() { + return minorVersion; + } + + public byte getAppMinorVersion() { + return appMinorVersion; + } + + public byte getAppMajorVersion() { + return appMajorVersion; + } + + public static void main(String[] args) { + + } + + public void setTotalMem(int total_mem) { + totalMem = total_mem; + + } + + public int getTotalMem() { + return totalMem; + } + + public void setFreeMem(int free_mem) { + freeMem = free_mem; + } + + public int getFreeMem() { + return freeMem; + } + + public TPSBuffer getAid() { + return aid; + } + + public void setAid(TPSBuffer aid) { + this.aid = aid; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/CertEnrollInfo.java b/base/tps/src/org/dogtagpki/server/tps/processor/CertEnrollInfo.java new file mode 100644 index 000000000..9dfb3f114 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/processor/CertEnrollInfo.java @@ -0,0 +1,270 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.processor; + +import org.dogtagpki.server.tps.channel.SecureChannel.TokenKeyType; +import org.dogtagpki.server.tps.cms.CARenewCertResponse; +import org.dogtagpki.server.tps.cms.CARetrieveCertResponse; +import org.dogtagpki.server.tps.cms.KRARecoverKeyResponse; +import org.dogtagpki.server.tps.dbs.TokenRecord; +import org.dogtagpki.server.tps.engine.TPSEngine; + +public class CertEnrollInfo { + + private TokenKeyType keyTypeEnum; + private String profileId; + private String certId; + private String certAttrId; + private String privateKeyAttrId; + private String publicKeyAttrId; + private String publisherId; + private String keyType; + private String keyTypePrefix; + + private CARetrieveCertResponse recoveredCertData; + private KRARecoverKeyResponse recoveredKeyData; + private TokenRecord toBeRecoveredRecord; + + private CARenewCertResponse renewedCertData; + + private int keySize; + private int algorithm; + private int keyUsage; + private int keyUser; + private int privateKeyNumber; + private int publicKeyNumber; + private int startProgress; + private int endProgress; + + private TPSEngine.ENROLL_MODES enrollmentMode = TPSEngine.ENROLL_MODES.MODE_ENROLL; + + public void setEnrollmentMode(TPSEngine.ENROLL_MODES mode) { + enrollmentMode = mode; + } + + public TPSEngine.ENROLL_MODES getEnrollmentMode() { + return enrollmentMode; + } + + public void setRecoveredCertData(CARetrieveCertResponse cData) { + recoveredCertData = cData; + } + + public CARetrieveCertResponse getRecoveredCertData() { + return recoveredCertData; + } + + public void setRecoveredKeyData(KRARecoverKeyResponse kData) { + recoveredKeyData = kData; + } + + public KRARecoverKeyResponse getRecoveredKeyData() { + return recoveredKeyData; + } + + + public void setStartProgressValue(int progress) { + startProgress = progress; + } + + public int getStartProgressValue() { + return startProgress; + } + + public void setEndProgressValue(int progress) { + endProgress = progress; + } + + public int getEndProgressValue() { + return endProgress; + } + + public void setKeyTypeEnum(TokenKeyType keyTypeEnum) { + this.keyTypeEnum = keyTypeEnum; + } + + public TokenKeyType getKeyTypeEnum() { + return keyTypeEnum; + } + + public void setProfileId(String profileId) { + this.profileId = profileId; + } + + public String getProfileId() { + return profileId; + } + + public void setCertId(String certId) { + this.certId = certId; + } + + public String getCertId() { + return certId; + } + + public void setCertAttrId(String certAttrId) { + this.certAttrId = certAttrId; + } + + public String getCertAttrId() { + return certAttrId; + } + + public void setPrivateKeyAttrId(String priKeyAttrId) { + privateKeyAttrId = priKeyAttrId; + } + + public String getPrivateKeyAttrId() { + return privateKeyAttrId; + } + + public void setPublicKeyAttrId(String publicKeyAttrId) { + this.publicKeyAttrId = publicKeyAttrId; + } + + public String getPublicKeyAttrId() { + return publicKeyAttrId; + } + + public void setKeySize(int keySize) { + this.keySize = keySize; + } + + public int getKeySize() { + return keySize; + } + + public void setPublisherId(String publisherId) { + this.publisherId = publisherId; + } + + public String getPublisherId() { + return publisherId; + } + + public void setAlgorithm(int algorithm) { + this.algorithm = algorithm; + } + + public int getAlgorithm() { + return algorithm; + } + + public void setKeyUsage(int keyUsage) { + this.keyUsage = keyUsage; + } + + public int getKeyUsage() { + return keyUsage; + } + + public void setKeyUser(int keyUser) { + this.keyUser = keyUser; + } + + public int getKeyUser() { + return keyUser; + } + + public void setPrivateKeyNumber(int priKeyNumber) { + privateKeyNumber = priKeyNumber; + } + + public int getPrivateKeyNumber() { + return privateKeyNumber; + } + + public void setPublicKeyNumber(int pubKeyNumber) { + publicKeyNumber = pubKeyNumber; + } + + public int getPublicKeyNumber() { + return publicKeyNumber; + } + + public void setKeyType(String keyType) { + this.keyType = keyType; + } + + public String getKeyType() { + return keyType; + } + + public void setKeyTypePrefix(String keyTypePrefix) { + this.keyTypePrefix = keyTypePrefix; + } + + public String getKeyTypePrefix() { + return keyTypePrefix; + } + + public boolean getIsRecoveryMode() { + if (enrollmentMode == TPSEngine.ENROLL_MODES.MODE_RECOVERY) { + return true; + } + + return false; + } + + public boolean getIsRenewalMode() { + if (enrollmentMode == TPSEngine.ENROLL_MODES.MODE_RENEWAL) { + return true; + } + + return false; + } + + public boolean getIsEnrollmentMode() { + if (enrollmentMode == TPSEngine.ENROLL_MODES.MODE_ENROLL) { + return true; + } + + return false; + } + + public void setTokenToBeRecovered(TokenRecord toBeRecovered) { + toBeRecoveredRecord = toBeRecovered; + + } + + public TokenRecord getTokenToBeRecovered() { + return toBeRecoveredRecord; + } + + public void setRenewedCertData(CARenewCertResponse certResponse) { + renewedCertData = certResponse; + } + + public CARenewCertResponse getRenewedCertData() { + return renewedCertData; + } + + public int getCertIdIndex() { + int result = 0; + + if(certId != null && certId.length() == 2) { + result = certId.charAt(1) - '0'; + } + + return result; + } + + + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java b/base/tps/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java new file mode 100644 index 000000000..87b86f7d7 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java @@ -0,0 +1,262 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.processor; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; + +import netscape.security.x509.X509CertImpl; + +import org.dogtagpki.server.tps.dbs.TPSCertRecord; +import org.dogtagpki.server.tps.main.PKCS11Obj; +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.Util; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; + +public class EnrolledCertsInfo { + + EnrolledCertsInfo() { + certificates = new ArrayList<X509CertImpl>(); + ktypes = new ArrayList<String>(); + origins = new ArrayList<String>(); + tokenTypes = new ArrayList<String>(); + } + + EnrolledCertsInfo(PKCS11Obj obj, TPSBuffer wrappedChallenge, TPSBuffer plainChallenge, int keyTypeNum, + int startProgress, int endProgress) { + this(); + this.wrappedChallenge = wrappedChallenge; + plaintextChallenge = plainChallenge; + pkcs11objx = obj; + numCertsToEnroll = keyTypeNum; + this.startProgress = startProgress; + this.endProgress = endProgress; + } + + //Tables that will get set during processing + private ArrayList<String> origins; + private ArrayList<String> ktypes; + private ArrayList<String> tokenTypes; + private ArrayList<X509CertImpl> certificates; + + //Input challenge data + private TPSBuffer wrappedChallenge; + private TPSBuffer plaintextChallenge; + private TPSBuffer keyCheck; + + private int numCertsToEnroll; + private int currentCertIndex; + + private int startProgress; + private int endProgress; + + public int getCurrentCertIndex() { + return currentCertIndex; + } + + public void setCurrentCertIndex(int index) { + currentCertIndex = index; + } + + public void setNumCertsToEnroll(int num) { + numCertsToEnroll = num; + } + + public int getNumCertsToEnroll() { + return numCertsToEnroll; + } + + int getStartProgressValue() { + return startProgress; + } + + int getEndProgressValue() { + return endProgress; + } + + public void setKeyCheck(TPSBuffer keyCheck) { + this.keyCheck = keyCheck; + } + + public TPSBuffer getKeyCheck() { + return keyCheck; + } + + //PKCS11Object that will have values added to it during processing + private PKCS11Obj pkcs11objx; + + public void setWrappedChallenge(TPSBuffer wrappedChallenge) { + this.wrappedChallenge = wrappedChallenge; + } + + public TPSBuffer getWrappedChallenge() { + return wrappedChallenge; + } + + public void setPlaintextChallenge(TPSBuffer plaintextChallenge) { + this.plaintextChallenge = plaintextChallenge; + } + + public TPSBuffer getPlaintextChallenge() { + return plaintextChallenge; + } + + public void setPKCS11Obj(PKCS11Obj pkcs11obj) { + pkcs11objx = pkcs11obj; + } + + public PKCS11Obj getPKCS11Obj() { + return pkcs11objx; + } + + public void addOrigin(String origin) { + + CMS.debug("EnrolledCertsInfo.addOrigin: " + origin); + origins.add(origin); + } + + public void addKType(String ktype) { + ktypes.add(ktype); + } + + public void addTokenType(String tokenType) { + tokenTypes.add(tokenType); + } + + public void addCertificate(X509CertImpl x509Cert) { + certificates.add(x509Cert); + } + + public void setStartProgress(int startP) { + startProgress = startP; + + } + + public void setEndProgress(int endP) { + endProgress = endP; + + } + + public ArrayList<TPSCertRecord> toTPSCertRecords(String cuid, String uid) { + ArrayList<TPSCertRecord> certs = new ArrayList<TPSCertRecord>(); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: starts"); + int index = 0; + for (X509CertImpl cert: certificates) { + TPSCertRecord certRecord = new TPSCertRecord(); + + //serial number + BigInteger serial_BigInt = cert.getSerialNumber(); + + String hexSerial = serial_BigInt.toString(16); + String serialNumber = "0x" + hexSerial; + certRecord.setSerialNumber(serialNumber); + + String uniqueString = Util.getTimeStampString(false); + String id = hexSerial + "." + uniqueString; + + certRecord.setId(id); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: converting cert:"+ certRecord.getId()); + + //token id + certRecord.setTokenID(cuid); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: cuid =" + cuid); + + //origin + if ((!origins.isEmpty()) && index <origins.size() && origins.get(index)!= null) { + certRecord.setOrigin(origins.get(index)); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: origin =" + origins.get(index)); + } else { + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: origin not found for index:"+ index); + } + + //user id + certRecord.setUserID(uid); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: uid =" + uid); + + //KeyType + if ((!ktypes.isEmpty()) && index <ktypes.size() && ktypes.get(index)!= null) { + certRecord.setKeyType(ktypes.get(index)); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: keyType =" + ktypes.get(index)); + } else { + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: keyType not found for index:"+ index); + } + + //token type + if ((!tokenTypes.isEmpty()) && index <tokenTypes.size() && tokenTypes.get(index)!= null) { + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: tokenType=" + tokenTypes.get(index)); + certRecord.setType(tokenTypes.get(index)); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: tokenType set"); + } else { + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: tokenType not found for index:"+ index); + //certRecord.setType(""); + } + + //Issuer + String issuedBy = cert.getIssuerDN().toString(); + certRecord.setIssuedBy(issuedBy); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: issuer ="+ issuedBy); + + //Subject + String subject = cert.getSubjectDN().toString(); + certRecord.setSubject(subject); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: subject ="+ subject); + + //NotBefore + certRecord.setValidNotBefore(cert.getNotBefore()); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: notBefore ="+ cert.getNotBefore().toString()); + + //NotAfter + certRecord.setValidNotAfter(cert.getNotAfter()); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: notAfter ="+ cert.getNotAfter().toString()); + + //status + certRecord.setStatus("active"); + + /* certificate + byte[] certBytes = null; + try { + certBytes = cert.getEncoded(); + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: certBytes ="+ CMS.BtoA(certBytes)); + } catch (CertificateEncodingException e) { + CMS.debug("EnrolledCertsInfo.toTPSCertRecord: "+ e); + //TODO: throw + + } + certRecord.setCertificate(CMS.BtoA(certBytes)); + */ + // Alternative to the actual certificate -- certificate AKI + try { + String aki = Util.getCertAkiString(cert); + certRecord.setCertificate(aki); + } catch (EBaseException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + certs.add(certRecord); + + index++; + } + CMS.debug("EnrolledCertsInfo.toTPSCertRecords: ends"); + return certs; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java new file mode 100644 index 000000000..cff615752 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java @@ -0,0 +1,2789 @@ +package org.dogtagpki.server.tps.processor; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; +import java.util.zip.DataFormatException; + +import netscape.security.provider.RSAPublicKey; +//import org.mozilla.jss.pkcs11.PK11ECPublicKey; +import netscape.security.util.BigInt; +import netscape.security.x509.X509CertImpl; + +import org.dogtagpki.server.tps.TPSSession; +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.TPSTokenPolicy; +import org.dogtagpki.server.tps.authentication.TPSAuthenticator; +import org.dogtagpki.server.tps.channel.SecureChannel; +import org.dogtagpki.server.tps.channel.SecureChannel.TokenKeyType; +import org.dogtagpki.server.tps.cms.CAEnrollCertResponse; +import org.dogtagpki.server.tps.cms.CARemoteRequestHandler; +import org.dogtagpki.server.tps.cms.CARenewCertResponse; +import org.dogtagpki.server.tps.cms.CARetrieveCertResponse; +import org.dogtagpki.server.tps.cms.CARevokeCertResponse; +import org.dogtagpki.server.tps.cms.KRARecoverKeyResponse; +import org.dogtagpki.server.tps.cms.KRAServerSideKeyGenResponse; +import org.dogtagpki.server.tps.dbs.ActivityDatabase; +import org.dogtagpki.server.tps.dbs.TPSCertRecord; +import org.dogtagpki.server.tps.dbs.TokenRecord; +import org.dogtagpki.server.tps.engine.TPSEngine; +import org.dogtagpki.server.tps.engine.TPSEngine.ENROLL_MODES; +import org.dogtagpki.server.tps.main.ObjectSpec; +import org.dogtagpki.server.tps.main.PKCS11Obj; +import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDU.SecurityLevel; +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.main.Util; +import org.dogtagpki.tps.msg.BeginOpMsg; +import org.dogtagpki.tps.msg.EndOpMsg; +import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.crypto.InvalidKeyFormatException; +import org.mozilla.jss.pkcs11.PK11PubKey; +import org.mozilla.jss.pkcs11.PK11RSAPublicKey; +import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthCredentials; +import com.netscape.certsrv.authentication.IAuthToken; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.EPropertyNotFound; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.tps.token.TokenStatus; +import com.netscape.cmsutil.util.Utils; + +public class TPSEnrollProcessor extends TPSProcessor { + + public TPSEnrollProcessor(TPSSession session) { + super(session); + } + + @Override + public void process(BeginOpMsg beginMsg) throws TPSException, IOException { + if (beginMsg == null) { + throw new TPSException("TPSEnrollrocessor.process: invalid input data, not beginMsg provided.", + TPSStatus.STATUS_ERROR_CONTACT_ADMIN); + } + setBeginMessage(beginMsg); + setCurrentTokenOperation("enroll"); + checkIsExternalReg(); + + enroll(); + + } + + private void enroll() throws TPSException, IOException { + CMS.debug("TPSEnrollProcessor enroll: entering..."); + String auditMsg = null; + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + TPSTokenPolicy tokenPolicy = new TPSTokenPolicy(tps); + + AppletInfo appletInfo = null; + TokenRecord tokenRecord = null; + try { + appletInfo = getAppletInfo(); + } catch (TPSException e) { + auditMsg = e.toString(); + tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw e; + } + appletInfo.setAid(getCardManagerAID()); + + CMS.debug("TPSEnrollProcessor.enroll: token cuid: " + appletInfo.getCUIDhexStringPlain()); + boolean isTokenPresent = false; + + tokenRecord = isTokenRecordPresent(appletInfo); + + if (tokenRecord != null) { + CMS.debug("TPSEnrollProcessor.enroll: found token..."); + isTokenPresent = true; + } else { + CMS.debug("TPSEnrollProcessor.enroll: token does not exist in tokendb... create one in memory"); + tokenRecord = new TokenRecord(); + tokenRecord.setId(appletInfo.getCUIDhexStringPlain()); + } + + fillTokenRecord(tokenRecord, appletInfo); + session.setTokenRecord(tokenRecord); + + String resolverInstName = getResolverInstanceName(); + + String tokenType = null; + + tokenType = resolveTokenProfile(resolverInstName, appletInfo.getCUIDhexString(), appletInfo.getMSNString(), + appletInfo.getMajorVersion(), appletInfo.getMinorVersion()); + CMS.debug("TPSEnrollProcessor.enroll: resolved tokenType: " + tokenType); + + checkProfileStateOK(); + String cuid = appletInfo.getCUIDhexStringPlain(); + + boolean do_force_format = false; + if (isTokenPresent) { + CMS.debug("TPSEnrollProcessor.enroll: token exists in tokendb"); + + TokenStatus newState = TokenStatus.ACTIVE; + // Check for transition to ACTIVE status. + + if (!tps.engine.isOperationTransitionAllowed(tokenRecord.getTokenStatus(), newState)) { + CMS.debug("TPSEnrollProcessor.enroll: token transition disallowed " + + tokenRecord.getTokenStatus() + + " to " + newState); + auditMsg = "Operation for CUID " + cuid + + " Disabled, illegal transition attempted " + tokenRecord.getTokenStatus() + + " to " + newState; + tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw new TPSException(auditMsg, + TPSStatus.STATUS_ERROR_DISABLED_TOKEN); + } else { + CMS.debug("TPSPEnrollrocessor.enroll: token transition allowed " + + tokenRecord.getTokenStatus() + + " to " + newState); + } + + do_force_format = tokenPolicy.isForceTokenFormat(cuid); + + if (!tokenPolicy.isAllowdTokenReenroll(cuid) && + !tokenPolicy.isAllowdTokenRenew(cuid)) { + CMS.debug("TPSEnrollProcessor.enroll: token renewal or reEnroll disallowed "); + auditMsg = "Operation renewal or reEnroll for CUID " + cuid + + " Disabled"; + tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw new TPSException(auditMsg, + TPSStatus.STATUS_ERROR_DISABLED_TOKEN); + } + } else { + CMS.debug("TPSEnrollProcessor.enroll: token does not exist"); + tokenRecord.setStatus("uninitialized"); + + checkAllowUnknownToken(TPSEngine.OP_FORMAT_PREFIX); + } + checkAndAuthenticateUser(appletInfo, tokenType); + + if (do_force_format) { + CMS.debug("TPSEnrollProcessor.enroll: About to force format first due to policy."); + //We will skip the auth step inside of format + format(true); + } else { + checkAndUpgradeApplet(appletInfo); + //Get new applet info + appletInfo = getAppletInfo(); + } + + CMS.debug("TPSEnrollProcessor.enroll: Finished updating applet if needed."); + + //Check and upgrade keys if called for + + SecureChannel channel = checkAndUpgradeSymKeys(); + channel.externalAuthenticate(); + + //Reset the token's pin, create one if we don't have one already + + checkAndHandlePinReset(channel); + tokenRecord.setKeyInfo(channel.getKeyInfoData().toHexStringPlain()); + String tksConnId = getTKSConnectorID(); + TPSBuffer plaintextChallenge = computeRandomData(16, tksConnId); + + //These will be used shortly + TPSBuffer wrappedChallenge = encryptData(appletInfo, channel.getKeyInfoData(), plaintextChallenge, tksConnId); + PKCS11Obj pkcs11objx = null; + + try { + pkcs11objx = getCurrentObjectsOnToken(channel); + } catch (DataFormatException e) { + auditMsg = "TPSEnrollProcessor.enroll: Failed to parse original token data: " + e.toString(); + tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw new TPSException(auditMsg); + } + + pkcs11objx.setCUID(appletInfo.getCUID()); + + if (!isTokenPresent) { + try { + tps.tdb.tdbAddTokenEntry(tokenRecord, "uninitialized"); + } catch (Exception e) { + String failMsg = "add token failure"; + auditMsg = failMsg + ":" + e.toString(); + throw new TPSException(auditMsg); + } + } + + statusUpdate(10, "PROGRESS_PROCESS_PROFILE"); + + EnrolledCertsInfo certsInfo = new EnrolledCertsInfo(); + certsInfo.setWrappedChallenge(wrappedChallenge); + certsInfo.setPlaintextChallenge(plaintextChallenge); + certsInfo.setPKCS11Obj(pkcs11objx); + certsInfo.setStartProgress(15); + certsInfo.setEndProgress(90); + + boolean renewed = false; + TPSStatus status = generateCertsAfterRenewalRecoveryPolicy(certsInfo, channel, appletInfo); + //most failed would have thrown an exception + String statusString = "Unknown"; // gives some meaningful debug message + if (status == TPSStatus.STATUS_NO_ERROR) + statusString = "Enrollment to follow"; + else if (status == TPSStatus.STATUS_ERROR_RECOVERY_IS_PROCESSED) + statusString = "Recovery processed"; + else if (status == TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED) + statusString = "Renewal processed"; + else { + auditMsg = " generateCertsAfterRenewalRecoveryPolicy returned status=" + status; + CMS.debug("TPSEnrollProcessor.enroll:" + auditMsg); + throw new TPSException(auditMsg); + } + auditMsg = "generateCertsAfterRenewalRecoveryPolicy returns status:" + + EndOpMsg.statusToInt(status) + " : " + statusString; + CMS.debug("TPSEnrollProcessor.enroll: " + auditMsg); + if (status == TPSStatus.STATUS_NO_ERROR) { + if (!generateCertificates(certsInfo, channel, appletInfo)) { + CMS.debug("TPSEnrollProcessor.enroll:generateCertificates returned false means some certs failed enrollment; clean up (format) the token"); + format(true /*skipAuth*/); + throw new TPSException("generateCertificates failed"); + } else { + CMS.debug("TPSEnrollProcessor.enroll:generateCertificates returned true means cert enrollment successful"); + } + } + // at this point, enrollment, renewal, or recovery have been processed accordingly; + if (status == TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED && + tokenPolicy.isAllowdTokenRenew(cuid)) { + renewed = true; + CMS.debug("TPSEnrollProcessor.enroll: renewal happened.. "); + } + /* + * TODO: + * find the point to do the following... + * when total available memory is exceeded on the token ... + * if(!renewed) //Renewal should leave what they have on the token. + * format(true); + */ + String tokenLabel = buildTokenLabel(certsInfo, appletInfo); + + pkcs11objx.setTokenName(new TPSBuffer(tokenLabel.getBytes())); + + int lastObjVer = pkcs11objx.getOldObjectVersion(); + + CMS.debug("TPSEnrollProcessor.enroll: getOldObjectVersion: returning: " + lastObjVer); + + if (lastObjVer != 0) { + while (lastObjVer == 0xff) { + Random randomGenerator = new Random(); + lastObjVer = randomGenerator.nextInt(1000); + } + + lastObjVer = lastObjVer + 1; + CMS.debug("TPSEnrollProcessor.enroll: Setting objectVersion to: " + lastObjVer); + pkcs11objx.setObjectVersion(lastObjVer); + + } + + pkcs11objx.setFormatVersion(pkcs11objx.getOldFormatVersion()); + + // Make sure we have a good secure channel before writing out the final objects + channel = setupSecureChannel(); + + statusUpdate(92, "PROGRESS_WRITE_OBJECTS"); + + writeFinalPKCS11ObjectToToken(pkcs11objx, appletInfo, channel); + statusUpdate(98, "PROGRESS_ISSUER_INFO"); + writeIssuerInfoToToken(channel); + + statusUpdate(99, "PROGRESS_SET_LIFECYCLE"); + channel.setLifeycleState((byte) 0x0f); + + try { + tokenRecord.setStatus("active"); + tps.tdb.tdbUpdateTokenEntry(tokenRecord); + } catch (Exception e) { + String failMsg = "update token failure"; + auditMsg = failMsg + ":" + e.toString(); + tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), failMsg, + "failure"); + throw new TPSException(auditMsg); + } + //update the tokendb with new certs + CMS.debug("TPSEnrollProcessor.enroll: updating tokendb with certs."); + ArrayList<TPSCertRecord> certRecords = certsInfo.toTPSCertRecords(tokenRecord.getId(), tokenRecord.getUserID()); + tps.tdb.tdbAddCertificatesForCUID(tokenRecord.getId(), certRecords); + + auditMsg = "appletVersion=" + lastObjVer + "; tokenType =" + selectedTokenType + "; userid =" + userid; + if (renewed) { + tps.tdb.tdbActivity(ActivityDatabase.OP_RENEWAL, tokenRecord, session.getIpAddress(), auditMsg, "success"); + } else { + tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg, + "success"); + } + + CMS.debug("TPSEnrollProcessor.enroll: leaving ..."); + + statusUpdate(100, "PROGRESS_DONE_ENROLLMENT"); + } + + private void writeFinalPKCS11ObjectToToken(PKCS11Obj pkcs11objx, AppletInfo ainfo, SecureChannel channel) + throws TPSException, IOException { + if (pkcs11objx == null || ainfo == null || channel == null) { + throw new TPSException("TPSErollProcessor.writeFinalPKCS11ObjectToToken: invalid input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSEnrollProcessor.writeFinalPKCS11ObjectToToken: entering..."); + + IConfigStore configStore = CMS.getConfigStore(); + + String compressConfig = "op." + currentTokenOperation + "." + selectedTokenType + "." + + "pkcs11obj.compress.enable"; + + CMS.debug("TPSEnrollProcessor.writeFinalPKCS11ObjectToToken: config to check: " + compressConfig); + + boolean doCompress = false; + + try { + doCompress = configStore.getBoolean(compressConfig, true); + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.writeFinalPKCS11ObjectToToken: internal error obtaining config value " + e); + } + + CMS.debug("TPSEnrollProcessor.writeFinalPKCS11ObjectToToken: doCompress: " + doCompress); + + TPSBuffer tokenData = null; + + if (doCompress) { + tokenData = pkcs11objx.getCompressedData(); + + } else { + tokenData = pkcs11objx.getData(); + } + + if (tokenData.size() > ainfo.getTotalMem()) { + + throw new TPSException( + "TPSEnrollProcessor.writeFinalPKCS11ObjectToToken: NOt enough memory to write certificates!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + + } + + byte[] zobjectid = { (byte) 'z', (byte) '0', 0, 0 }; + byte[] perms = { (byte) 0xff, (byte) 0xff, 0x40, 0x00, 0x40, 0x00 }; + TPSBuffer zobjidBuf = new TPSBuffer(zobjectid); + + channel.createObject(zobjidBuf, new TPSBuffer(perms), tokenData.size()); + + channel.writeObject(zobjidBuf, tokenData); + + CMS.debug("TPSEnrollProcessor.writeFinalPKCS11ObjectToToken: leaving successfully ..."); + + } + + private void checkAndAuthenticateUser(AppletInfo appletInfo, String tokenType) throws TPSException { + IAuthCredentials userCred; + IAuthToken authToken; + TokenRecord tokenRecord = getTokenRecord(); + if (!isExternalReg) { + // authenticate per profile/tokenType configuration + String configName = TPSEngine.OP_ENROLL_PREFIX + "." + tokenType + ".auth.enable"; + IConfigStore configStore = CMS.getConfigStore(); + + TPSSubsystem tps = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + //TPSSession session = getSession(); + boolean isAuthRequired; + try { + CMS.debug("TPSEnrollProcessor.checkAndAuthenticateUser: getting config: " + configName); + isAuthRequired = configStore.getBoolean(configName, true); + } catch (EBaseException e) { + CMS.debug("TPSEnrollProcessor.checkAndAuthenticateUser: Internal Error obtaining mandatory config values. Error: " + + e); + throw new TPSException("TPS error getting config values from config store.", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + if (isAuthRequired) { + try { + TPSAuthenticator userAuth = + getAuthentication(TPSEngine.OP_ENROLL_PREFIX, tokenType); + userCred = requestUserId(TPSEngine.ENROLL_OP, appletInfo.getCUIDhexString(), userAuth, + beginMsg.getExtensions()); + userid = (String) userCred.get(userAuth.getAuthCredName()); + CMS.debug("TPSEnrollProcessor.checkAndAuthenticateUser: userCred (attempted) userid=" + userid); + // initialize userid first for logging purposes in case authentication fails + tokenRecord.setUserID(userid); + authToken = authenticateUser(TPSEngine.ENROLL_OP, userAuth, userCred); + userid = authToken.getInString("userid"); + tokenRecord.setUserID(userid); + CMS.debug("TPSEnrollProcessor.checkAndAuthenticateUser: auth passed: userid: " + + authToken.get("userid")); + + } catch (Exception e) { + // all exceptions are considered login failure + CMS.debug("TPSEnrollProcessor.checkAndAuthenticateUser:: authentication exception thrown: " + e); + String msg = "TPS error user authentication failed:" + e; + tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), msg, + "failure"); + + throw new TPSException(msg, + TPSStatus.STATUS_ERROR_LOGIN); + } + } else { + throw new TPSException( + "TPSEnrollProcessor.checkAndAuthenticateUser: TPS enrollment must have authentication enabled.", + TPSStatus.STATUS_ERROR_LOGIN); + + } + + } + } + + private void checkAndHandlePinReset(SecureChannel channel) throws TPSException, IOException { + + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset entering..."); + + if (channel == null) { + throw new TPSException("TPSEnrollProcessor.checkAndHandlePinReset: invalid input data!", + TPSStatus.STATUS_ERROR_TOKEN_RESET_PIN_FAILED); + } + + IConfigStore configStore = CMS.getConfigStore(); + + String pinResetEnableConfig = "op." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_PIN_RESET_ENABLE; + + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset config to check: " + pinResetEnableConfig); + + String minLenConfig = "op." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_PIN_RESET_MIN_LEN; + + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset config to check: " + minLenConfig); + + String maxLenConfig = "op." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_PIN_RESET_MAX_LEN; + + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset config to check: " + maxLenConfig); + + String maxRetriesConfig = "op." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_PIN_RESET_MAX_RETRIES; + + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset config to check: " + maxRetriesConfig); + + String pinStringConfig = TPSEngine.CFG_PIN_RESET_STRING; + + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset config to check: " + pinStringConfig); + + boolean enabled = false; + int minLen; + int maxLen; + int maxRetries; + String stringName; + + try { + + enabled = configStore.getBoolean(pinResetEnableConfig, true); + + if (enabled == false) { + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset: Pin Reset not allowed by configuration, exiting..."); + return; + + } + + minLen = configStore.getInteger(minLenConfig, 4); + maxLen = configStore.getInteger(maxLenConfig, 10); + maxRetries = configStore.getInteger(maxRetriesConfig, 0x7f); + stringName = configStore.getString(pinStringConfig, "password"); + + CMS.debug("TPSEnrollProcessor.checkAndHandlePinReset: config vals: enabled: " + enabled + " minLen: " + + minLen + " maxLen: " + maxLen); + + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.checkAndHandlePinReset: internal error in getting value from config."); + } + + String new_pin = requestNewPin(minLen, maxLen); + + channel.createPin(0x0, maxRetries, stringName); + + channel.resetPin(0x0, new_pin); + + } + + private void checkAndUpgradeApplet(AppletInfo appletInfo) throws TPSException, IOException { + // TODO Auto-generated method stub + + CMS.debug("checkAndUpgradeApplet: entering.."); + + SecurityLevel securityLevel = SecurityLevel.SECURE_MSG_MAC; + + boolean useEncryption = checkUpdateAppletEncryption(); + + String tksConnId = getTKSConnectorID(); + if (useEncryption) + securityLevel = SecurityLevel.SECURE_MSG_MAC_ENC; + + if (checkForAppletUpdateEnabled()) { + + String targetAppletVersion = checkForAppletUpgrade("op." + currentTokenOperation); + targetAppletVersion = targetAppletVersion.toLowerCase(); + + String currentAppletVersion = formatCurrentAppletVersion(appletInfo); + + CMS.debug("TPSEnrollProcessor.checkAndUpgradeApplet: currentAppletVersion: " + currentAppletVersion + + " targetAppletVersion: " + targetAppletVersion); + + if (targetAppletVersion.compareTo(currentAppletVersion) != 0) { + + CMS.debug("TPSEnrollProessor.checkAndUpgradeApplet: Upgrading applet to : " + targetAppletVersion); + upgradeApplet("op." + currentTokenOperation, targetAppletVersion, securityLevel, getBeginMessage() + .getExtensions(), + tksConnId, 5, 12); + } else { + CMS.debug("TPSEnrollProcessor.checkAndUpgradeApplet: applet already at correct version."); + } + } + + } + + protected boolean checkUpdateAppletEncryption() throws TPSException { + + CMS.debug("TPSEnrollProcessor.checkUpdateAppletEncryption entering..."); + + IConfigStore configStore = CMS.getConfigStore(); + + String appletEncryptionConfig = "op." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_UPDATE_APPLET_ENCRYPTION; + + CMS.debug("TPSEnrollProcessor.checkUpdateAppletEncryption config to check: " + appletEncryptionConfig); + + boolean appletEncryption = false; + + try { + appletEncryption = configStore.getBoolean(appletEncryptionConfig, false); + } catch (EBaseException e) { + //Default TPSException will return a "contact admin" error code. + throw new TPSException( + "TPSEnrollProcessor.checkUpdateAppletEncryption: internal error in getting value from config."); + } + + CMS.debug("TPSEnrollProcessor.checkUpdateAppletEncryption returning: " + appletEncryption); + return appletEncryption; + + } + + private PKCS11Obj getCurrentObjectsOnToken(SecureChannel channel) throws TPSException, IOException, + DataFormatException { + + byte seq = 0; + + TPSBuffer objects = null; + + int lastFormatVersion = 0x0100; + int lastObjectVersion; + Random randomGenerator = new Random(); + + lastObjectVersion = randomGenerator.nextInt(1000); + + CMS.debug("PKCS11Obj.getCurrentObjectsOnToken: Random lastObjectVersion: " + lastObjectVersion); + + PKCS11Obj pkcs11objx = new PKCS11Obj(); + pkcs11objx.setOldFormatVersion(lastFormatVersion); + pkcs11objx.setOldObjectVersion(lastObjectVersion); + + do { + + objects = listObjects(seq); + + if (objects != null) { + CMS.debug("PKCS11Obj.getCurrentObjectsOnToken: objects: " + objects.toHexString()); + } + + if (objects == null) { + pkcs11objx.setOldObjectVersion(lastObjectVersion); + seq = 0; + } else { + seq = 1; // get next entry + + TPSBuffer objectID = objects.substr(0, 4); + TPSBuffer objectLen = objects.substr(4, 4); + + long objectIDVal = objectID.getLongFrom4Bytes(0); + + long objectLenVal = objectLen.getLongFrom4Bytes(0); + + TPSBuffer obj = channel.readObject(objectID, 0, (int) objectLenVal); + + if (obj != null) { + CMS.debug("PKCS11Obj.getCurrentObjectsOnToken: obj: " + obj.toHexString()); + } + + if ((char) objectID.at(0) == (byte) 'z' && objectID.at(1) == (byte) '0') { + lastFormatVersion = obj.getIntFrom2Bytes(0); + lastObjectVersion = obj.getIntFrom2Bytes(2); + + CMS.debug("PKCS11Obj.getCurrentObjectsOnToken: Versions read from token: lastFormatVersion : " + + lastFormatVersion + + " lastObjectVersion: " + lastObjectVersion); + + pkcs11objx = PKCS11Obj.parse(obj, 0); + + pkcs11objx.setOldFormatVersion(lastFormatVersion); + pkcs11objx.setOldObjectVersion(lastObjectVersion); + seq = 0; + + } else { + ObjectSpec objSpec = ObjectSpec.parseFromTokenData(objectIDVal, obj); + pkcs11objx.addObjectSpec(objSpec); + } + + CMS.debug("TPSEnrollProcessor.getCurrentObjectsOnToken. just read object from token: " + + obj.toHexString()); + } + + } while (seq != 0); + + return pkcs11objx; + } + + /* + * generateCertsAfterRenewalRecoveryPolicy determines whether a renewal or recovery is needed; + * if recovery is needed, it determines which certificates (from which old token) + * to recover onto the new token. + * + * Note: renewal and recovery are invoked in this method; However, if a new enrollment is determined + * to be the proper course of action, it is done after this method. + */ + private TPSStatus generateCertsAfterRenewalRecoveryPolicy(EnrolledCertsInfo certsInfo, SecureChannel channel, + AppletInfo aInfo) + throws TPSException, IOException { + TPSStatus status = TPSStatus.STATUS_NO_ERROR; + String auditMsg; + final String method = "TPSEnrollProcessor.generateCertsAfterRenewalRecoveryPolicy"; + CMS.debug(method + ": begins"); + TPSSubsystem tps = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + TPSTokenPolicy tokenPolicy = new TPSTokenPolicy(tps); + + ArrayList<TokenRecord> tokenRecords = null; + try { + tokenRecords = tps.tdb.tdbFindTokenRecordsByUID(userid); + } catch (Exception e) { + // no existing record, means no "renewal" or "recovery" actions needed + auditMsg = "no token associated with user: " + userid; + CMS.debug(method + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_INACTIVE_TOKEN_NOT_FOUND); + } + CMS.debug(method + " found " + tokenRecords.size() + " tokens for user:" + userid); + boolean isRecover = false; + + TokenRecord lostToken = null; + for (TokenRecord tokenRecord : tokenRecords) { + CMS.debug(method + " token id:" + + tokenRecord.getId() + "; status=" + + tokenRecord.getStatus()); + + //Is this the same token (current token)? + if (tokenRecord.getId().equals(aInfo.getCUIDhexStringPlain())) { + //same token + auditMsg = "found current token entry"; + CMS.debug(method + ":" + auditMsg); + if (tokenRecord.getStatus().equals("uninitialized")) { + // this is the current token + if (tokenRecords.size() == 1) { + // the current token is the only token owned by the user + CMS.debug(method + ": need to do enrollment"); + // need to do enrollment outside + break; + } else { + CMS.debug(method + ": There are multiple token entries for user " + + userid); + try { + // this is assuming that the user can only have one single active token + // TODO: for future, maybe should allow multiple active tokens + tps.tdb.tdbHasActiveToken(userid); + + } catch (Exception e1) { + /* + * user has no active token, need to find a token to recover from + * there are no other active tokens for this user + */ + isRecover = true; + continue; // TODO: or break? + } + auditMsg = method + ": user already has an active token"; + CMS.debug(auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_HAS_AT_LEAST_ONE_ACTIVE_TOKEN); + } + } else if (tokenRecord.getStatus().equals("active")) { + // current token is already active; renew if allowed + if (tokenPolicy.isAllowdTokenRenew(aInfo.getCUIDhexStringPlain())) { + return processRenewal(certsInfo, channel, aInfo, tokenRecord); + } else { + auditMsg = "token is already active; can't renew because renewal is not allowed; will re-enroll if allowed"; + CMS.debug(method + ":" + auditMsg); + } + break; + } else if (tokenRecord.getStatus().equals("terminated")) { + auditMsg = "terminated token cuid=" + + aInfo.getCUIDhexStringPlain() + " cannot be reused"; + CMS.debug(method + ":" + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_CONTACT_ADMIN); + } else if (tokenRecord.getStatus().equals("lost")) { + String reasonStr = tokenRecord.getReason(); + if (reasonStr.equals("keyCompromise")) { + auditMsg = "This token cannot be reused because it has been reported lost"; + CMS.debug(method + ": " + + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_UNUSABLE_TOKEN_KEYCOMPROMISE); + } else if (reasonStr.equals("onHold")) { + try { + tps.tdb.tdbHasActiveToken(userid); + auditMsg = "user already has an active token"; + CMS.debug(method + ": " + + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_HAS_AT_LEAST_ONE_ACTIVE_TOKEN); + } catch (Exception e2) { + auditMsg = "User needs to contact administrator to report lost token (it should be put on Hold)."; + CMS.debug(method + ": " + + auditMsg); + break; + } + } else if (reasonStr.equals("destroyed")) { + auditMsg = "This destroyed lost case should not be executed because the token is so damaged. It should not get here"; + CMS.debug(method + ": " + + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_TOKEN_DISABLED); + } else { + auditMsg = "No such lost reason: " + reasonStr + " for this cuid: " + + aInfo.getCUIDhexStringPlain(); + CMS.debug(method + ":" + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_NO_SUCH_LOST_REASON); + } + + } else { + auditMsg = "No such token status for this cuid=" + aInfo.getCUIDhexStringPlain(); + CMS.debug(method + ":" + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_NO_SUCH_TOKEN_STATE); + } + } else { //cuid != current token + auditMsg = "found token entry different from current token"; + CMS.debug(method + ":" + auditMsg); + if (tokenRecord.getStatus().equals("lost")) { + //lostostToken keeps track of the latest token that's lost + //last one in the look should be the latest + lostToken = tokenRecord; + auditMsg = "found a lost token: cuid = " + tokenRecord.getId(); + CMS.debug(method + ":" + auditMsg); + } + continue; + } + } + + if (isRecover == true) { // this could be set in previous iteration + if (lostToken == null) { + auditMsg = "No lost token to be recovered; do enrollment"; + CMS.debug(method + ":" + auditMsg); + //shouldn't even get here; But if we do, just enroll + } else { + String reasonStr = lostToken.getReason(); + //RevocationReason reason = RevocationReason.valueOf(reasonStr); + auditMsg = "isRecover true; reasonStr =" + reasonStr; + CMS.debug(method + ":" + auditMsg); + + if (reasonStr.equals("keyCompromise")) { + return processRecovery(lostToken, certsInfo, channel, aInfo); + } else if (reasonStr.equals("onHold")) { + /* + * the inactive one becomes the temp token + * No recovery scheme, basically we are going to + * do the brand new enrollment + * + * + */ + + // ToDo: This section has not been tested to work.. Make sure this works. + + IConfigStore configStore = CMS.getConfigStore(); + String configName = TPSEngine.OP_ENROLL_PREFIX + "." + getSelectedTokenType() + + ".temporaryToken.tokenType"; + try { + String tmpTokenType = configStore.getString(configName); + setSelectedTokenType(tmpTokenType); + } catch (EPropertyNotFound e) { + auditMsg = " configuration " + configName + " not found"; + CMS.debug(method + ":" + auditMsg); + throw new TPSException(method + ":" + auditMsg); + } catch (EBaseException e) { + auditMsg = " configuration " + configName + " not found"; + CMS.debug(method + ":" + auditMsg); + throw new TPSException(method + ":" + auditMsg); + } + return processRecovery(lostToken, certsInfo, channel, aInfo); + + } else if (reasonStr.equals("destroyed")) { + return processRecovery(lostToken, certsInfo, channel, aInfo); + } else { + auditMsg = "No such lost reason: " + reasonStr + " for this cuid: " + aInfo.getCUIDhexStringPlain(); + CMS.debug(method + ":" + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_NO_SUCH_LOST_REASON); + } + } + } + + CMS.debug(method + ": ends"); + return status; + } + + /* + * Renewal logic + * 1. Create Optional local TPS grace period per token profile, + * per token type, such as signing or encryption. + * This grace period must match how the CA is configured. Ex: + * op.enroll.userKey.renewal.encryption.enable=true + * op.enroll.userKey.renewal.encryption.gracePeriod.enable=true + * op.enroll.userKey.renewal.encryption.gracePeriod.before=30 + * op.enroll.userKey.renewal.encryption.gracePeriod.after=30 + * 2. In case of a grace period failure the code will go on + * and attempt to renew the next certificate in the list. + * 3. In case of any other code failure, the code will abort + * and leave the token untouched, while informing the user + * with an error message. + * + */ + private TPSStatus processRenewal(EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo, + TokenRecord tokenRecord) + throws TPSException, IOException { + TPSStatus status = TPSStatus.STATUS_ERROR_RENEWAL_FAILED; + String method = "TPSEnrollProcess.processRenewal"; + String auditMsg; + CMS.debug(method + ": begins"); + + boolean noFailedCerts = true; + + if (certsInfo == null || aInfo == null || channel == null) { + throw new TPSException(method + ": Bad Input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + TPSSubsystem tps = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + int keyTypeNum = getNumberCertsToRenew(); + /* + * Get certs from the tokendb for this token to find out about + * renewal possibility + */ + ArrayList<TPSCertRecord> allCerts = tps.tdb.tdbGetCertificatesByCUID(tokenRecord.getId()); + + certsInfo.setNumCertsToEnroll(keyTypeNum); + + CMS.debug(method + ": Number of certs to renew: " + keyTypeNum); + + for (int i = 0; i < keyTypeNum; i++) { + /* + * e.g. op.enroll.userKey.renewal.keyType.value.0=signing + * e.g. op.enroll.userKey.renewal.keyType.value.1=encryption + */ + String keyType = getRenewConfigKeyType(i); + boolean renewEnabled = getRenewEnabled(keyType); + CMS.debug(method + ": key type " + keyType); + if (!renewEnabled) { + CMS.debug(method + ": renew not enabled"); + continue; + } + + CMS.debug(method + ": renew enabled"); + + certsInfo.setCurrentCertIndex(i); + + CertEnrollInfo cEnrollInfo = new CertEnrollInfo(); + IConfigStore configStore = CMS.getConfigStore(); + + // find all config + String configName = null; + boolean graceEnabled = false; + String graceBeforeS = null; + String graceAfterS = null; + try { + String keyTypePrefix = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + ".renewal." + keyType; + + //TODO: profileId is actually gotten in the CARemoteRequestHandler. + configName = keyTypePrefix + ".ca.profileId"; + String profileId; + profileId = configStore.getString(configName); + CMS.debug(method + ": profileId: " + profileId); + + configName = keyTypePrefix + ".gracePeriod.enable"; + graceEnabled = configStore.getBoolean(configName, false); + if (graceEnabled) { + CMS.debug(method + ": grace period check is enabled"); + configName = keyTypePrefix + ".gracePeriod.before"; + graceBeforeS = configStore.getString(configName, ""); + configName = keyTypePrefix + ".gracePeriod.after"; + graceAfterS = configStore.getString(configName, ""); + } else { + CMS.debug(method + ": grace period check is not enabled"); + } + + configName = keyTypePrefix + ".certId"; + String certId = configStore.getString(configName, "C0"); + CMS.debug(method + ": certId: " + certId); + + configName = keyTypePrefix + ".certAttrId"; + String certAttrId = configStore.getString(configName, "c0"); + CMS.debug(method + ": certAttrId: " + certAttrId); + + configName = keyTypePrefix + ".privateKeyAttrId"; + String priKeyAttrId = configStore.getString(configName, "k0"); + CMS.debug(method + ": privateKeyAttrId: " + priKeyAttrId); + + configName = keyTypePrefix + ".publicKeyAttrId"; + String publicKeyAttrId = configStore.getString(configName, "k1"); + CMS.debug(method + ": publicKeyAttrId: " + publicKeyAttrId); + + } catch (EBaseException e) { + throw new TPSException(method + ": Internal error finding config value: " + configName + ":" + + e, + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + + // find the certs that match the keyType to renew + for (TPSCertRecord cert : allCerts) { + if (keyType.equals(cert.getKeyType())) { + try { + CMS.debug(method + ": cert " + cert.getId() + " with status:" + cert.getStatus()); + if (cert.getStatus().equals("revoked") || + cert.getStatus().equals("renewed")) { + CMS.debug(method + ": cert status is not to be renewed"); + continue; + } + + // check if within grace period to save us a trip (note: CA makes the final decision) + if (graceEnabled) { + try { + if (!isCertWithinRenewalGracePeriod(cert, graceBeforeS, graceAfterS)) + continue; + } catch (TPSException ge) { + // error in this will just log and keep going + CMS.debug(method + ":" + ge + "; continue to try renewal"); + } + } + + //Renew and fetch the renewed cert blob. + + CARenewCertResponse certResponse = tps.getEngine().renewCertificate(cert, + cert.getSerialNumber(), selectedTokenType, keyType, getCAConnectorID()); + cEnrollInfo.setRenewedCertData(certResponse); + + generateCertificate(certsInfo, channel, aInfo, keyType, TPSEngine.ENROLL_MODES.MODE_RENEWAL, + 0, cEnrollInfo); + + //renewCertificate(cert, certsInfo, channel, aInfo, keyType); + status = TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED; + } catch (TPSException e) { + CMS.debug(method + "renewCertificate: exception:" + e); + noFailedCerts = false; + break; //need to clean up half-done token later + } + } + } + } + + if (!noFailedCerts) { + // TODO: handle cleanup + auditMsg = "There has been failed cert renewal"; + CMS.debug(method + ":" + auditMsg); + throw new TPSException(auditMsg + TPSStatus.STATUS_ERROR_RENEWAL_FAILED); + } + return status; + } + + /* + * isCertWithinRenewalGracePeriod - check if a cert is within the renewal grace period + * @param cert the cert to be renewed + * @param renewGraceBeforeS string representation of the # of days "before" cert expiration date + * @param renewGraceAfterS string representation of the # of days "after" cert expiration date + */ + private boolean isCertWithinRenewalGracePeriod(TPSCertRecord cert, String renewGraceBeforeS, String renewGraceAfterS) + throws TPSException { + String method = "TPSEnrollProcessor.isCertWithinRenewalGracePeriod"; + int renewGraceBefore = 0; + int renewGraceAfter = 0; + + if (cert == null || renewGraceBeforeS == null || renewGraceAfterS == null) { + CMS.debug(method + ": missing some input"); + throw new TPSException(method + ": Bad Input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + BigInteger renewGraceBeforeBI = new BigInteger(renewGraceBeforeS); + BigInteger renewGraceAfterBI = new BigInteger(renewGraceAfterS); + + // -1 means no limit + if (renewGraceBeforeS == "") + renewGraceBefore = -1; + else + renewGraceBefore = Integer.parseInt(renewGraceBeforeS); + + if (renewGraceAfterS == "") + renewGraceAfter = -1; + else + renewGraceAfter = Integer.parseInt(renewGraceAfterS); + + if (renewGraceBefore > 0) + renewGraceBeforeBI = renewGraceBeforeBI.multiply(BigInteger.valueOf(1000 * 86400)); + if (renewGraceAfter > 0) + renewGraceAfterBI = renewGraceAfterBI.multiply(BigInteger.valueOf(1000 * 86400)); + + Date origExpDate = cert.getValidNotAfter(); + Date current = CMS.getCurrentDate(); + long millisDiff = origExpDate.getTime() - current.getTime(); + CMS.debug(method + ": millisDiff=" + + millisDiff + " origExpDate=" + origExpDate.getTime() + " current=" + current.getTime()); + + /* + * "days", if positive, has to be less than renew_grace_before + * "days", if negative, means already past expiration date, + * (abs value) has to be less than renew_grace_after + * if renew_grace_before or renew_grace_after are negative + * the one with negative value is ignored + */ + if (millisDiff >= 0) { + if ((renewGraceBefore > 0) && (millisDiff > renewGraceBeforeBI.longValue())) { + CMS.debug(method + ": renewal attempted outside of grace period;" + + renewGraceBefore + " days before and " + + renewGraceAfter + " days after original cert expiration date"); + return false; + } + } else { + if ((renewGraceAfter > 0) && ((0 - millisDiff) > renewGraceAfterBI.longValue())) { + CMS.debug(method + ": renewal attempted outside of grace period;" + + renewGraceBefore + " days before and " + + renewGraceAfter + " days after original cert expiration date"); + return false; + } + } + return true; + } + + private boolean getRenewEnabled(String keyType) { + String method = "TPSEnrollProcessor.getRenewEnabled"; + IConfigStore configStore = CMS.getConfigStore(); + boolean enabled = false; + + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + ".renewal." + + keyType + "." + "enable"; + enabled = configStore.getBoolean( + configValue, false); + + } catch (EBaseException e) { + //default to false + } + + CMS.debug(method + ": returning " + enabled); + return enabled; + } + + private String getRenewConfigKeyType(int keyTypeIndex) throws TPSException { + String method = "TPSEnrollProcessor.getRenewConfigKeyType"; + IConfigStore configStore = CMS.getConfigStore(); + String keyType = null; + + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + + TPSEngine.CFG_RENEW_KEYTYPE_VALUE + "." + keyTypeIndex; + keyType = configStore.getString( + configValue, null); + + } catch (EBaseException e) { + throw new TPSException( + method + ": Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + //We would really like one of these to exist + if (keyType == null) { + throw new TPSException( + method + ": Internal error finding config value: ", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug(method + ": returning: " + keyType); + + return keyType; + + } + + private int getNumberCertsToRenew() throws TPSException { + String method = "TPSEnrollProcessor.getNumberCertsToRenew"; + + IConfigStore configStore = CMS.getConfigStore(); + int keyTypeNum = 0; + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + + TPSEngine.CFG_RENEW_KEYTYPE_NUM; + keyTypeNum = configStore.getInteger( + configValue, 0); + + } catch (EBaseException e) { + throw new TPSException(method + ": Internal error finding config value: " + + e, + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + + if (keyTypeNum == 0) { + throw new TPSException( + method + ": invalid number of certificates to renew configured!", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + CMS.debug(method + ": returning: " + keyTypeNum); + + return keyTypeNum; + } + + private TPSStatus processRecovery(TokenRecord toBeRecovered, EnrolledCertsInfo certsInfo, SecureChannel channel, + AppletInfo aInfo) throws TPSException, IOException { + String method = "TPSEnrollProcessor.processRecover"; + String auditMsg; + TPSStatus status = TPSStatus.STATUS_ERROR_RECOVERY_IS_PROCESSED; + + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + IConfigStore configStore = CMS.getConfigStore(); + + CMS.debug("TPSEnrollProcessor.processRecovery: entering:"); + + if (toBeRecovered == null || certsInfo == null || channel == null || aInfo == null) { + throw new TPSException("TPSEnrollProcessor.processRecovery: Invalid reason!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + String reason = toBeRecovered.getReason(); + + int num = getNumberCertsForRecovery(reason); + + int totalNumCerts = 0; + + //We will have to rifle through the configuration to see if there any recovery operations with + //scheme "GenerateNewKeyandRecoverLast" which allows for recovering the old key AND generating a new + // one for the encryption type only. If this scheme is present, the number of certs for bump by + // 1 for each occurrence. + + String keyTypeValue = null; + String scheme = null; + CMS.debug("TPSEnrollProcessor.processRecovery: About to find if we have any GenerateNewAndRecoverLast schemes."); + for (int i = 0; i < num; i++) { + keyTypeValue = getRecoveryKeyTypeValue(reason, i); + scheme = getRecoveryScheme(reason, keyTypeValue); + + if (scheme.equals(TPSEngine.RECOVERY_SCHEME_GENERATE_NEW_KEY_AND_RECOVER_LAST)) { + + //Make sure we are not signing: + if (keyTypeValue.equals(TPSEngine.CFG_SIGNING)) { + throw new TPSException( + "TPSEnrollProcessor.processRecovery: Can't have GenerateNewAndRecoverLast scheme with a signing key!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + totalNumCerts++; + } + totalNumCerts++; + } + + CMS.debug("TPSEnrollProcessor.processRecovery: About to perform actual recoveries: totalNumCerts: " + + totalNumCerts); + + if (!(totalNumCerts > num)) { + totalNumCerts = num; + } + + boolean isGenerateAndRecover = false; + int actualCertIndex = 0; + boolean legalScheme = false; + + //Go through again and do the recoveries/enrollments + + certsInfo.setNumCertsToEnroll(totalNumCerts); + for (int i = 0; i < num; i++) { + + keyTypeValue = getRecoveryKeyTypeValue(reason, i); + scheme = getRecoveryScheme(reason, keyTypeValue); + + if (scheme.equals(TPSEngine.RECOVERY_SCHEME_GENERATE_NEW_KEY_AND_RECOVER_LAST)) { + CMS.debug("TPSEnrollProcessor.processRecovery: scheme GenerateNewKeyAndRecoverLast found."); + isGenerateAndRecover = true; + + } else { + isGenerateAndRecover = false; + } + + if (scheme.equals(TPSEngine.RECOVERY_GENERATE_NEW_KEY) || isGenerateAndRecover) { + legalScheme = true; + CertEnrollInfo cEnrollInfo = new CertEnrollInfo(); + generateCertificate(certsInfo, channel, aInfo, keyTypeValue, TPSEngine.ENROLL_MODES.MODE_ENROLL, + actualCertIndex, cEnrollInfo); + + actualCertIndex = cEnrollInfo.getCertIdIndex(); + CMS.debug("TPSEnrollProcessor.processRecovery: scheme GenerateNewKey found, or isGenerateAndRecove is true: actualCertIndex, after enrollment: " + + actualCertIndex); + + } + + if (scheme.equals(TPSEngine.RECOVERY_RECOVER_LAST) || isGenerateAndRecover) { + legalScheme = true; + CMS.debug("TPSEnrollProcessor.processRecovery: scheme RecoverLast found, or isGenerateAndRecove is true"); + if (isGenerateAndRecover) { + CMS.debug("TPSEnrollProcessor.processRecovery: isGenerateAndRecover is true."); + actualCertIndex++; + } + + ArrayList<TPSCertRecord> certs = tps.tdb.tdbGetCertificatesByCUID(toBeRecovered.getId()); + + String serialToRecover = null; + TPSCertRecord certToRecover = null; + for (TPSCertRecord rec : certs) { + + //Just take the end of the list most recent cert of given type. + CMS.debug("TPSEnrollProcessor.processRecovery: Looking for keyType record: " + keyTypeValue + + " curSererial: " + rec.getSerialNumber()); + + if (rec.getKeyType().equals(keyTypeValue)) { + serialToRecover = rec.getSerialNumber(); + certToRecover = rec; + CMS.debug("TPSCertRecord: serial number: " + serialToRecover); + } + + } + String b64cert = null; + if (serialToRecover != null) { + // get recovery conn id + String caConnId; + String config = "op.enroll." + certToRecover.getType() + ".keyGen." + certToRecover.getKeyType() + ".ca.conn"; + try { + caConnId = configStore.getString(config); + } catch (Exception e) { + auditMsg = "cannot find config:" + config; + CMS.debug(method + ":" + auditMsg); + throw new TPSException( + method + ":" + auditMsg, + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + CMS.debug("TPSEnrollProcessor.processRecovery: Selecting cert to recover: " + serialToRecover); + + CARetrieveCertResponse certResponse = tps.getEngine().recoverCertificate(certToRecover, + serialToRecover, keyTypeValue, caConnId); + + b64cert = certResponse.getCertB64(); + CMS.debug("TPSEnrollProcessor.processRecovery: recoverd cert blob: " + b64cert); + + KRARecoverKeyResponse keyResponse = tps.getEngine().recoverKey(toBeRecovered.getId(), + toBeRecovered.getUserID(), + channel.getDRMWrappedDesKey(), b64cert, getDRMConnectorID()); + + CertEnrollInfo cEnrollInfo = new CertEnrollInfo(); + + cEnrollInfo.setTokenToBeRecovered(toBeRecovered); + cEnrollInfo.setRecoveredCertData(certResponse); + cEnrollInfo.setRecoveredKeyData(keyResponse); + + generateCertificate(certsInfo, channel, aInfo, keyTypeValue, TPSEngine.ENROLL_MODES.MODE_RECOVERY, + actualCertIndex, cEnrollInfo); + + // unrevoke cert if needed + if (certToRecover.getStatus().equalsIgnoreCase("revoked_on_hold")) { + auditMsg = "unrevoking cert..."; + CMS.debug(method + ":" + auditMsg); + + CARemoteRequestHandler caRH = null; + try { + caRH = new CARemoteRequestHandler(caConnId); + + CARevokeCertResponse response = + caRH.revokeCertificate(false /*unrevoke*/, serialToRecover, + certToRecover.getCertificate(), + null); + CMS.debug(method + ": response status =" + response.getStatus()); + + } catch (EBaseException e) { + auditMsg = "failed getting CARemoteRequestHandler"; + CMS.debug(method + ":" + auditMsg); + throw new TPSException(method + ":" + auditMsg, TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + } + + // set cert status to active + certToRecover.setStatus("active"); + try { + tps.tdb.tdbUpdateCertEntry(certToRecover); + } catch (Exception e) { + auditMsg = "failed tdbUpdateCertEntry"; + CMS.debug(method + ":" + auditMsg); + throw new TPSException(method + ":" + auditMsg, TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + } else { + + } + + } + + if (!legalScheme) { + throw new TPSException("TPSEnrollProcessor.processRecovery: Invalid recovery configuration!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + actualCertIndex++; + + } + + return status; + } + + //Stub to generate a certificate, more to come + private boolean generateCertificates(EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo) + throws TPSException, IOException { + + CMS.debug("TPSEnrollProcess.generateCertificates: begins "); + boolean noFailedCerts = true; + + if (certsInfo == null || aInfo == null || channel == null) { + throw new TPSException("TPSEnrollProcessor.generateCertificates: Bad Input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + int keyTypeNum = getNumberCertsToEnroll(); + + certsInfo.setNumCertsToEnroll(keyTypeNum); + + CMS.debug("TPSEnrollProcessor.generateCertificate: Number of certs to enroll: " + keyTypeNum); + + for (int i = 0; i < keyTypeNum; i++) { + String keyType = getConfiguredKeyType(i); + certsInfo.setCurrentCertIndex(i); + try { + generateCertificate(certsInfo, channel, aInfo, keyType, TPSEngine.ENROLL_MODES.MODE_ENROLL, 0, null); + } catch (TPSException e) { + CMS.debug("TPSEnrollProcessor.generateCertificate: exception:" + e); + noFailedCerts = false; + break; //need to clean up half-done token later + } + } + + /* + * In this special case of RE_ENROLL, Revoke current certs for this token + * if so configured + */ + /*TODO: format that follows should do this already based on the returned noFailedCerts value + if (noFailedCerts == true) { + revokeCertificates(aInfo.getCUIDhexStringPlain()); + } + */ + + CMS.debug("TPSEnrollProcessor.generateCertificates: ends "); + return noFailedCerts; + } + + private String buildTokenLabel(EnrolledCertsInfo certsInfo, AppletInfo ainfo) throws TPSException { + String label = null; + + if (certsInfo == null || ainfo == null) { + throw new TPSException("TPSEnrollProcessor.buildTokenLabel: invalide input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSEnrollProcessor.buildTokenLabel: entering..."); + + IConfigStore configStore = CMS.getConfigStore(); + + String configName = TPSEngine.OP_ENROLL_PREFIX + "." + getSelectedTokenType() + ".keyGen.tokenName"; + String pattern = null; + + try { + pattern = configStore.getString(configName, "$cuid$"); + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.buildTokenLabel: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + + } + + CMS.debug("TPSEnrollProcessor.buildTokenLabel: pattern: " + pattern); + + Map<String, String> nv = new LinkedHashMap<String, String>(); + + nv.put("cuid", ainfo.getCUIDhexString()); + nv.put("msn", ainfo.getMSNString()); + nv.put("userid", userid); + nv.put("auth.cn", userid); + nv.put("profileId", getSelectedTokenType()); + + label = mapPattern((LinkedHashMap<String, String>) nv, pattern); + + CMS.debug("TPSEnrollProcessor.buildTokenLabel: returning: " + label); + + return label; + + } + + /* This routine will be able to handle: + * regular enrollment + * recovery enrollment + * renewal enrollment + */ + private void generateCertificate(EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo, + String keyType, TPSEngine.ENROLL_MODES mode, int certIdNumOverride, CertEnrollInfo cEnrollInfo) + throws TPSException, IOException { + + CMS.debug("TPSEnrollProcessor.generateCertificate: entering ... certIdNumOverride: " + certIdNumOverride + + " mode: " + mode); + + if (certsInfo == null || aInfo == null || channel == null || aInfo == null) { + throw new TPSException("TPSEnrollProcessor.generateCertificate: Bad Input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + //get the params needed all at once + + IConfigStore configStore = CMS.getConfigStore(); + + boolean isRenewal = false; + + // This operation modifier allows us to get config entries for either + // regular enrollment and renewal. Allows the re-use of repetitive config processing code below. + String operationModifier = "keyGen"; + if (mode == ENROLL_MODES.MODE_RENEWAL) { + isRenewal = true; + operationModifier = "renewal"; + } + + if (cEnrollInfo == null) + cEnrollInfo = new CertEnrollInfo(); + + try { + + String keyTypePrefix = TPSEngine.OP_ENROLL_PREFIX + "." + getSelectedTokenType() + "." + operationModifier + + "." + keyType; + CMS.debug("TPSEnrollProcessor.generateCertificate: keyTypePrefix: " + keyTypePrefix); + + String configName = keyTypePrefix + ".ca.profileId"; + String profileId = configStore.getString(configName); + CMS.debug("TPSEnrollProcessor.generateCertificate: profileId: " + profileId); + + configName = keyTypePrefix + ".certId"; + String certId = configStore.getString(configName, "C0"); + CMS.debug("TPSEnrollProcessor.generateCertificate: certId: " + certId); + + configName = keyTypePrefix + ".certAttrId"; + String certAttrId = configStore.getString(configName, "c0"); + CMS.debug("TPSEnrollProcessor.generateCertificate: certAttrId: " + certAttrId); + + configName = keyTypePrefix + ".privateKeyAttrId"; + String priKeyAttrId = configStore.getString(configName, "k0"); + CMS.debug("TPSEnrollProcessor.generateCertificate: priKeyAttrId: " + priKeyAttrId); + + configName = keyTypePrefix + ".publicKeyAttrId"; + String publicKeyAttrId = configStore.getString(configName, "k1"); + CMS.debug("TPSEnrollProcessor.generateCertificate: publicKeyAttrId: " + publicKeyAttrId); + + configName = keyTypePrefix + ".keySize"; + int keySize = configStore.getInteger(configName, 1024); + CMS.debug("TPSEnrollProcessor.generateCertificate: keySize: " + keySize); + + //Default RSA_CRT=2 + configName = keyTypePrefix + ".alg"; + int algorithm = configStore.getInteger(configName, 2); + CMS.debug("TPSEnrollProcessor.generateCertificate: algorithm: " + algorithm); + + configName = keyTypePrefix + ".publisherId"; + String publisherId = configStore.getString(configName, ""); + CMS.debug("TPSEnrollProcessor.generateCertificate: publisherId: " + publisherId); + + configName = keyTypePrefix + ".keyUsage"; + int keyUsage = configStore.getInteger(configName, 0); + CMS.debug("TPSEnrollProcessor.generateCertificate: keyUsage: " + keyUsage); + + configName = keyTypePrefix + ".keyUser"; + int keyUser = configStore.getInteger(configName, 0); + CMS.debug("TPSEnrollProcessor.generateCertificate: keyUser: " + keyUser); + + configName = keyTypePrefix + ".privateKeyNumber"; + int priKeyNumber = configStore.getInteger(configName, 0); + CMS.debug("TPSEnrollProcessor.generateCertificate: privateKeyNumber: " + priKeyNumber); + + configName = keyTypePrefix + ".publicKeyNumber"; + int pubKeyNumber = configStore.getInteger(configName, 0); + CMS.debug("TPSEnrollProcessor.generateCertificate: pubKeyNumber: " + pubKeyNumber); + + // get key capabilites to determine if the key type is SIGNING, + // ENCRYPTION, or SIGNING_AND_ENCRYPTION + + configName = keyTypePrefix + ".private.keyCapabilities.sign"; + boolean isSigning = configStore.getBoolean(configName, false); + CMS.debug("TPSEnrollProcessor.generateCertificate: isSigning: " + isSigning); + + configName = keyTypePrefix + ".public.keyCapabilities.encrypt"; + CMS.debug("TPSEnrollProcessor.generateCertificate: encrypt config name: " + configName); + boolean isEncrypt = configStore.getBoolean(configName, true); + CMS.debug("TPSEnrollProcessor.generateCertificate: isEncrypt: " + isEncrypt); + + TokenKeyType keyTypeEnum; + + if (isSigning && isEncrypt) { + keyTypeEnum = TokenKeyType.KEY_TYPE_SIGNING_AND_ENCRYPTION; + } else if (isSigning) { + keyTypeEnum = TokenKeyType.KEY_TYPE_SIGNING; + } else if (isEncrypt) { + keyTypeEnum = TokenKeyType.KEY_TYPE_ENCRYPTION; + } else { + CMS.debug("TPSEnrollProcessor.generateCertificate: Illegal toke key type!"); + throw new TPSException("TPSEnrollProcessor.generateCertificate: Illegal toke key type!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSEnrollProcessor.generateCertificate: keyTypeEnum value: " + keyTypeEnum); + + // The certIdNumOverride allows us to place the certs and keys into a different slot. + // Thus overriding what is found in the config. + // Used in recovery mostly up to this point. + + if (certIdNumOverride > 0) { + CMS.debug("TPSEnrollProcessor.generateCertificate: called with overridden cert id number: " + + certIdNumOverride); + + pubKeyNumber = 2 * certIdNumOverride + 1; + priKeyNumber = 2 * certIdNumOverride; + + certId = "C" + certIdNumOverride; + certAttrId = "c" + certIdNumOverride; + priKeyAttrId = "k" + priKeyNumber; + publicKeyAttrId = "k" + pubKeyNumber; + + CMS.debug("TPSEnrollProcessor.generateCertificate: called with overridden cert no: certId: " + certId + + " certAttrId: " + certAttrId + " priKeyAttrId: " + priKeyAttrId + " publicKeyAttrId: " + + publicKeyAttrId); + + } + + cEnrollInfo.setKeyTypeEnum(keyTypeEnum); + cEnrollInfo.setProfileId(profileId); + cEnrollInfo.setCertId(certId); + cEnrollInfo.setCertAttrId(certAttrId); + + // These setting are key related and have no meaning in renewal. + if (isRenewal == false) { + cEnrollInfo.setPrivateKeyAttrId(priKeyAttrId); + cEnrollInfo.setPublicKeyAttrId(publicKeyAttrId); + cEnrollInfo.setKeySize(keySize); + cEnrollInfo.setAlgorithm(algorithm); + cEnrollInfo.setPublisherId(publisherId); + cEnrollInfo.setKeyUsage(keyUsage); + cEnrollInfo.setKeyUser(keyUser); + cEnrollInfo.setPrivateKeyNumber(priKeyNumber); + cEnrollInfo.setPublicKeyNumber(pubKeyNumber); + cEnrollInfo.setKeyType(keyType); + cEnrollInfo.setKeyTypePrefix(keyTypePrefix); + } + + int certsStartProgress = certsInfo.getStartProgressValue(); + int certsEndProgress = certsInfo.getEndProgressValue(); + int currentCertIndex = certsInfo.getCurrentCertIndex(); + int totalNumCerts = certsInfo.getNumCertsToEnroll(); + + int progressBlock = (certsEndProgress - certsStartProgress) / totalNumCerts; + + int startCertProgValue = certsStartProgress + currentCertIndex * progressBlock; + + int endCertProgValue = startCertProgValue + progressBlock; + + cEnrollInfo.setStartProgressValue(startCertProgValue); + cEnrollInfo.setEndProgressValue(endCertProgValue); + + } catch (EBaseException e) { + + throw new TPSException( + "TPSEnrollProcessor.generateCertificate: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + enrollOneCertificate(certsInfo, cEnrollInfo, aInfo, channel, mode); + + } + + /* Core method handles the following modes: + * Regular enrollment + * Recovery enrollment + * Renewal enrollment + */ + private void enrollOneCertificate(EnrolledCertsInfo certsInfo, CertEnrollInfo cEnrollInfo, AppletInfo aInfo, + SecureChannel channel, TPSEngine.ENROLL_MODES mode) + throws TPSException, IOException { + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: entering ... mode: " + mode); + + if (certsInfo == null || aInfo == null || cEnrollInfo == null || channel == null) { + throw new TPSException("TPSEnrollProcessor.enrollOneCertificate: Bad Input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + statusUpdate(cEnrollInfo.getStartProgressValue(), "PROGRESS_KEY_GENERATION"); + boolean serverSideKeyGen = checkForServerSideKeyGen(cEnrollInfo); + boolean objectOverwrite = checkForObjectOverwrite(cEnrollInfo); + + PKCS11Obj pkcs11obj = certsInfo.getPKCS11Obj(); + + int keyAlg = cEnrollInfo.getAlgorithm(); + + boolean isECC = getTPSEngine().isAlgorithmECC(keyAlg); + + if (objectOverwrite) { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: We are configured to overwrite existing cert objects."); + + } else { + + boolean certIdExists = pkcs11obj.doesCertIdExist(cEnrollInfo.getCertId()); + + //Bomb out if cert exists, we ca't overwrite + + if (certIdExists) { + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate: Overwrite of certificates not allowed!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + } + + TPSBuffer public_key_blob = null; + KRAServerSideKeyGenResponse ssKeyGenResponse = null; + KRARecoverKeyResponse keyResp = null; + RSAPublicKey parsedPubKey = null; + PK11PubKey parsedPK11PubKey = null; + byte[] parsedPubKey_ba = null; + + boolean isRecovery = false; + boolean isRenewal = false; + + if (mode == ENROLL_MODES.MODE_RECOVERY) { + isRecovery = true; + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: detecting recovery mode!"); + if (isRecovery && !serverSideKeyGen) { + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate: Attempting illegal recovery when archival is not enabled!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + } + + if (mode == ENROLL_MODES.MODE_RENEWAL) { + isRenewal = true; + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: detecting renewal mode!"); + } + + if (serverSideKeyGen || isRecovery) { + //Handle server side keyGen/recovery + // The main difference is where the key and cert data is obtained. + // In recovery the cert and key are recovered. + // In server side key gen, cert is enrolled and key is generated and recovered. + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: either generate private key on the server, or preform recovery or perform renewal."); + boolean archive = checkForServerKeyArchival(cEnrollInfo); + String drmConnId = getDRMConnectorID(); + + String publicKeyStr = null; + //Do this for JUST server side keygen + if (isRecovery == false) { + ssKeyGenResponse = getTPSEngine() + .serverSideKeyGen(cEnrollInfo.getKeySize(), + aInfo.getCUIDhexStringPlain(), userid, drmConnId, channel.getDRMWrappedDesKey(), + archive, isECC); + + publicKeyStr = ssKeyGenResponse.getPublicKey(); + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: public key string from server: " + publicKeyStr); + public_key_blob = new TPSBuffer(Utils.base64decode(publicKeyStr)); + + } else { + //Here we have a recovery, get the key data from the CertInfo object + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: Attempt to get key data in recovery mode!"); + keyResp = cEnrollInfo.getRecoveredKeyData(); + + publicKeyStr = keyResp.getPublicKey(); + public_key_blob = new TPSBuffer(Utils.base64decode(publicKeyStr)); + + } + + try { + parsedPK11PubKey = PK11RSAPublicKey.fromSPKI(public_key_blob.toBytesArray()); + + } catch (InvalidKeyFormatException e) { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate, can't create public key object from server side key generated public key blob!"); + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate, can't create public key object from server side key generated public key blob!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + parsedPubKey_ba = parsedPK11PubKey.getEncoded(); + + } else if (isRenewal) { + + CMS.debug("TPSEnrollProcessor: We are in renewal mode, no work to do with the keys, in renewal the keys remain on the token."); + + } else { + //Handle token side keyGen + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: about to generate the private key on the token."); + + int algorithm = 0x80; + + if (certsInfo.getKeyCheck() != null) { + algorithm = 0x81; + } + + if (isECC) { + algorithm = keyAlg; + } + + int pe1 = (cEnrollInfo.getKeyUser() << 4) + cEnrollInfo.getPrivateKeyNumber(); + int pe2 = (cEnrollInfo.getKeyUsage() << 4) + cEnrollInfo.getPublicKeyNumber(); + + int size = channel.startEnrollment(pe1, pe2, certsInfo.getWrappedChallenge(), certsInfo.getKeyCheck(), + algorithm, cEnrollInfo.getKeySize(), 0x0); + + byte[] iobytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; + TPSBuffer iobuf = new TPSBuffer(iobytes); + + public_key_blob = channel.readObject(iobuf, 0, size); + + parsedPubKey = parsePublicKeyBlob(public_key_blob, isECC); + + parsedPubKey_ba = parsedPubKey.getEncoded(); + } + + // enrollment/recovery begins + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: enrollment begins"); + X509CertImpl x509Cert = null; + byte[] cert_bytes = null; + try { + + if (isRecovery == false && isRenewal == false) { + String caConnID = getCAConnectorID(); + CARemoteRequestHandler caRH = new CARemoteRequestHandler(caConnID); + TPSBuffer encodedParsedPubKey = new TPSBuffer(parsedPubKey_ba); + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: userid =" + userid + ", cuid=" + + aInfo.getCUIDhexString()); + CAEnrollCertResponse caEnrollResp = caRH.enrollCertificate(encodedParsedPubKey, userid, + aInfo.getCUIDhexString(), getSelectedTokenType(), + cEnrollInfo.getKeyType()); + String retCertB64 = caEnrollResp.getCertB64(); + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: retCertB64: " + retCertB64); + + cert_bytes = Utils.base64decode(retCertB64); + + TPSBuffer cert_bytes_buf = new TPSBuffer(cert_bytes); + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: retCertB64: " + cert_bytes_buf.toHexString()); + + if (retCertB64 != null) + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: new cert b64 =" + retCertB64); + else { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: new cert b64 not found"); + throw new TPSException("TPSEnrollProcessor.enrollOneCertificate: new cert b64 not found", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + x509Cert = caEnrollResp.getCert(); + if (x509Cert != null) + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: new cert retrieved"); + else { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: new cert not found"); + throw new TPSException("TPSEnrollProcessor.enrollOneCertificate: new cert not found", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + } else { + //Import the cert data from the CertEnrollObject or from Renewal object + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: Attempt to import cert data in recovery mode or renew mode!"); + + if (isRecovery) { + CARetrieveCertResponse certResp = cEnrollInfo.getRecoveredCertData(); + + if (certResp == null) { + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate: In recovery mode, CARetieveCertResponse object not found!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + String retCertB64 = certResp.getCertB64(); + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: recovering: retCertB64: " + retCertB64); + cert_bytes = Utils.base64decode(retCertB64); + + TPSBuffer cert_bytes_buf = new TPSBuffer(cert_bytes); + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: recovering: retCertB64: " + + cert_bytes_buf.toHexString()); + + if (retCertB64 != null) + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: recovering: new cert b64 =" + retCertB64); + else { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: recovering new cert b64 not found"); + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate: recovering: new cert b64 not found", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + x509Cert = certResp.getCert(); + if (x509Cert != null) + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: recovering new cert retrieved"); + else { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: recovering new cert not found"); + throw new TPSException("TPSEnrollProcessor.enrollOneCertificate: new cert not found", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + } + + // If we are here, it has to be one or the other. + + if (isRenewal) { + + CARenewCertResponse certResp = cEnrollInfo.getRenewedCertData(); + if (certResp == null) { + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate: In renewal mode, CARemewCertResponse object not found!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + String retCertB64 = certResp.getRenewedCertB64(); + cert_bytes = Utils.base64decode(retCertB64); + + if (retCertB64 != null) + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: renewing: new cert b64 =" + retCertB64); + else { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: renewing new cert b64 not found"); + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate: remewomg: new cert b64 not found", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + x509Cert = certResp.getRenewedCert(); + + if (x509Cert != null) + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: renewing new cert retrieved"); + else { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: renewing new cert not found"); + throw new TPSException("TPSEnrollProcessor.enrollOneCertificate: new cert not found", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + + TPSBuffer cert_bytes_buf = new TPSBuffer(cert_bytes); + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: renewing: retCertB64: " + + cert_bytes_buf.toHexString()); + + } + + } + + certsInfo.addCertificate(x509Cert); + // In renewal case, we don't need to save this info. + if (!isRenewal) { + certsInfo.addKType(cEnrollInfo.getKeyType()); + } + + //Add origin, special handling for recovery case. + if (isRecovery == true) { + TokenRecord recordToRecover = cEnrollInfo.getTokenToBeRecovered(); + //We need to have this token record otherwise bomb out. + + if (recordToRecover == null) { + throw new TPSException( + "TPSEnrollProcessor.enrollOneCertificate: TokenRecord of token to be recovered not found.", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + certsInfo.addOrigin(recordToRecover.getId()); + + } else { + certsInfo.addOrigin(aInfo.getCUIDhexStringPlain()); + } + + certsInfo.addTokenType(selectedTokenType); + + SubjectPublicKeyInfo publicKeyInfo = null; + + String label = null; + TPSBuffer keyid = null; + TPSBuffer modulus = null; + TPSBuffer exponent = null; + + if (!isRenewal) { + + try { + if (serverSideKeyGen) { + publicKeyInfo = new SubjectPublicKeyInfo(parsedPK11PubKey); + } else { + publicKeyInfo = new SubjectPublicKeyInfo(parsedPubKey); + } + } catch (InvalidBERException e) { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: cant get publicKeyInfo object."); + throw new TPSException("TPSEnrollProcessor.enrollOneCertificate: can't get publcKeyInfo object.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + //Create label ToDo: Do this the correct way later + + label = buildCertificateLabel(cEnrollInfo, aInfo); + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: cert label: " + label); + + keyid = new TPSBuffer(makeKeyIDFromPublicKeyInfo(publicKeyInfo.getEncoded())); + + modulus = null; + exponent = null; + + if (serverSideKeyGen) { + modulus = new TPSBuffer(((PK11RSAPublicKey) parsedPK11PubKey).getModulus().toByteArray()); + exponent = new TPSBuffer(((PK11RSAPublicKey) parsedPK11PubKey).getPublicExponent().toByteArray()); + + } else { + modulus = new TPSBuffer(parsedPubKey.getModulus().toByteArray()); + exponent = new TPSBuffer(parsedPubKey.getPublicExponent().toByteArray()); + } + + } + + //Write cert to the token,do this in all modes + + long l1, l2; + long objid; + PKCS11Obj pkcs11Obj = certsInfo.getPKCS11Obj(); + + String certId = cEnrollInfo.getCertId(); + + l1 = (certId.charAt(0) & 0xff) << 24; + l2 = (certId.charAt(1) & 0xff) << 16; + objid = l1 + l2; + + CMS.debug("TPSEnrollProcess.enrollOneCertificate: cert objid long: " + objid); + + ObjectSpec certObjSpec = ObjectSpec.parseFromTokenData(objid, new TPSBuffer(cert_bytes)); + pkcs11Obj.addObjectSpec(certObjSpec); + + //Do the rest of this stuff only in enrollment or recovery case, in renewal, we need not deal with the keys + + if (isRenewal == false) { + + String certAttrId = cEnrollInfo.getCertAttrId(); + + TPSBuffer certAttrsBuffer = channel.createPKCS11CertAttrsBuffer(cEnrollInfo.getKeyTypeEnum(), + certAttrId, label, keyid); + + l1 = (certAttrId.charAt(0) & 0xff) << 24; + l2 = (certAttrId.charAt(1) & 0xff) << 16; + objid = l1 + l2; + + CMS.debug("TPSEnrollProcess.enrollOneCertificate: cert attr objid long: " + objid); + ObjectSpec certAttrObjSpec = ObjectSpec.parseFromTokenData(objid, certAttrsBuffer); + pkcs11Obj.addObjectSpec(certAttrObjSpec); + + //Add the pri key attrs object + + String priKeyAttrId = cEnrollInfo.getPrivateKeyAttrId(); + + l1 = (priKeyAttrId.charAt(0) & 0xff) << 24; + l2 = (priKeyAttrId.charAt(1) & 0xff) << 16; + + objid = l1 + l2; + + CMS.debug("TPSEnrollProcess.enrollOneCertificate: pri key objid long: " + objid); + + TPSBuffer privKeyAttrsBuffer = channel.createPKCS11PriKeyAttrsBuffer(priKeyAttrId, label, keyid, + modulus, cEnrollInfo.getKeyTypePrefix()); + + ObjectSpec priKeyObjSpec = ObjectSpec.parseFromTokenData(objid, privKeyAttrsBuffer); + pkcs11obj.addObjectSpec(priKeyObjSpec); + + // Now add the public key object + + String pubKeyAttrId = cEnrollInfo.getPublicKeyAttrId(); + + l1 = (pubKeyAttrId.charAt(0) & 0xff) << 24; + l2 = (pubKeyAttrId.charAt(1) & 0xff) << 16; + + objid = l1 + l2; + CMS.debug("TPSEnrollProcess.enrollOneCertificate: pub key objid long: " + objid); + + TPSBuffer pubKeyAttrsBuffer = channel.createPKCS11PublicKeyAttrsBuffer(pubKeyAttrId, label, keyid, + modulus, exponent, cEnrollInfo.getKeyTypePrefix()); + ObjectSpec pubKeyObjSpec = ObjectSpec.parseFromTokenData(objid, pubKeyAttrsBuffer); + pkcs11obj.addObjectSpec(pubKeyObjSpec); + + } + } catch (EBaseException e) { + CMS.debug("TPSEnrollProcessor.enrollOneCertificate::" + e); + throw new TPSException("TPSEnrollProcessor.enrollOneCertificate: Exception thrown: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + if (serverSideKeyGen || isRecovery) { + //Handle injection of private key onto token + CMS.debug("TPSEnrollProcessor.enrollOneCertificate: About to inject private key"); + + if (!isRecovery) { + + // SecureChannel newChannel = setupSecureChannel(); + importPrivateKeyPKCS8(ssKeyGenResponse, cEnrollInfo, channel, isECC); + + } else { + importPrivateKeyPKCS8(keyResp, cEnrollInfo, channel, isECC); + } + + } + + CMS.debug("TPSEnrollProcessor.enrollOneCertificate:: enrollment ends"); + + statusUpdate(cEnrollInfo.getEndProgressValue(), "PROGRESS_ENROLL_CERT"); + CMS.debug("TPSEnrollProcessor.enrollOneCertificate ends"); + + } + + private void importPrivateKeyPKCS8(KRARecoverKeyResponse keyResp, CertEnrollInfo cEnrollInfo, + SecureChannel channel, + boolean isECC) throws TPSException, IOException { + + if (keyResp == null || cEnrollInfo == null || channel == null) { + throw new TPSException("TPSEnrollProcessor.importPrivateKeyPKCS8: invalid input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + importPrivateKeyPKCS8(keyResp.getWrappedPrivKey(), keyResp.getIVParam(), cEnrollInfo, channel, isECC); + + } + + private void importPrivateKeyPKCS8(KRAServerSideKeyGenResponse ssKeyGenResponse, CertEnrollInfo cEnrollInfo, + SecureChannel channel, + boolean isECC) throws TPSException, IOException { + + if (ssKeyGenResponse == null || cEnrollInfo == null || channel == null) { + throw new TPSException("TPSEnrollProcessor.importPrivateKeyPKCS8: invalid input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + importPrivateKeyPKCS8(ssKeyGenResponse.getWrappedPrivKey(), ssKeyGenResponse.getIVParam(), cEnrollInfo, + channel, isECC); + + } + + private void importPrivateKeyPKCS8(String wrappedPrivKeyStr, String ivParams, CertEnrollInfo cEnrollInfo, + SecureChannel channel, + boolean isECC) throws TPSException, IOException { + + CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8 entering.."); + if (wrappedPrivKeyStr == null || ivParams == null || cEnrollInfo == null || channel == null) { + throw new TPSException("TPSEnrollProcessor.importPrivateKeyPKCS8: invalid input data!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + byte[] objid = { + (byte) 0xFF, + 0x00, + (byte) 0xFF, + (byte) 0xF3 }; + + byte keytype = 0x09; //RSAPKCS8Pair + + // String wrappedPrivKeyStr = ssKeyGenResponse.getWrappedPrivKey(); + int keysize = cEnrollInfo.getKeySize(); + + TPSBuffer privKeyBlob = new TPSBuffer(); + + privKeyBlob.add((byte) 0x1); // encryption + privKeyBlob.add(keytype); + privKeyBlob.add((byte) (keysize / 256)); + privKeyBlob.add((byte) (keysize % 256)); + + TPSBuffer privKeyBuff = new TPSBuffer(Util.uriDecodeFromHex(wrappedPrivKeyStr)); + privKeyBlob.add(privKeyBuff); + + CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8 privKeyBlob: " + privKeyBlob.toHexString()); + + byte[] perms = { 0x40, + 0x00, + 0x40, + 0x00, + 0x40, + 0x00 }; + + TPSBuffer objIdBuff = new TPSBuffer(objid); + + channel.createObject(objIdBuff, new TPSBuffer(perms), privKeyBlob.size()); + + channel.writeObject(objIdBuff, privKeyBlob); + + TPSBuffer keyCheck = channel.getKeyCheck(); + + CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8 : keyCheck: " + keyCheck.toHexString()); + + // String ivParams = ssKeyGenResponse.getIVParam(); + CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8: ivParams: " + ivParams); + TPSBuffer ivParamsBuff = new TPSBuffer(Util.uriDecodeFromHex(ivParams)); + + if (ivParamsBuff.size() == 0) { + throw new TPSException("TPSEnrollProcessor.importPrivateKeyPKCS8: invalid iv vector!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + + } + + TPSBuffer kekWrappedDesKey = channel.getKekDesKey(); + + if (kekWrappedDesKey != null) + CMS.debug("TPSEnrollProcessor.importPrivateKeyPKCS8: keyWrappedDesKey: " + kekWrappedDesKey.toHexString()); + else + CMS.debug("TPSEnrollProcessor.iportPrivateKeyPKC8: null kekWrappedDesKey!"); + + byte alg = (byte) 0x80; + if (kekWrappedDesKey == null || kekWrappedDesKey.size() > 0) { + alg = (byte) 0x81; + } + + TPSBuffer data = new TPSBuffer(); + + data.add(objIdBuff); + data.add(alg); + data.add((byte) kekWrappedDesKey.size()); + data.add(kekWrappedDesKey); + data.add((byte) keyCheck.size()); + data.add(keyCheck); + data.add((byte) ivParamsBuff.size()); + data.add(ivParamsBuff); + + int pe1 = (cEnrollInfo.getKeyUser() << 4) + cEnrollInfo.getPrivateKeyNumber(); + int pe2 = (cEnrollInfo.getKeyUsage() << 4) + cEnrollInfo.getPublicKeyNumber(); + + channel.importKeyEnc(pe1, pe2, data); + + CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8 successful, leaving..."); + + } + + private String buildCertificateLabel(CertEnrollInfo cEnrollInfo, AppletInfo ainfo) throws TPSException { + + CMS.debug("TPSEnrollProcessor.buildCertificateLabel"); + + if (cEnrollInfo == null) { + throw new TPSException("TPSErollProcessor.buildCertificateLabel: Invalid input params!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + String label = null; + String pattern = null; + + String defaultLabel = cEnrollInfo.getKeyType() + " key for $userid$"; + + IConfigStore configStore = CMS.getConfigStore(); + + String configValue = "op." + currentTokenOperation + "." + selectedTokenType + ".keyGen." + + cEnrollInfo.getKeyType() + ".label"; + + CMS.debug("TPSEnrollProcessor.buildCertificateLabel: label config: " + configValue); + + try { + pattern = configStore.getString( + configValue, defaultLabel); + + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.buildCertificateLabel: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + Map<String, String> nv = new LinkedHashMap<String, String>(); + + nv.put("cuid", ainfo.getCUIDhexString()); + nv.put("msn", ainfo.getMSNString()); + nv.put("userid", userid); + nv.put("auth.cn", userid); + nv.put("profileId", getSelectedTokenType()); + + label = mapPattern((LinkedHashMap<String, String>) nv, pattern); + + CMS.debug("TPSEnrollProcessor.buildCertificateLabel: returning: " + label); + + return label; + } + + /** + * Extracts information from the public key blob and verify proof. + * + * Muscle Key Blob Format (RSA Public Key) + * --------------------------------------- + * + * The key generation operation places the newly generated key into + * the output buffer encoding in the standard Muscle key blob format. + * For an RSA key the data is as follows: + * + * Byte Encoding (0 for plaintext) + * + * Byte Key Type (1 for RSA public) + * + * Short Key Length (1024 û high byte first) + * + * Short Modulus Length + * + * Byte[] Modulus + * + * Short Exponent Length + * + * Byte[] Exponent + * + * + * ECC KeyBlob Format (ECC Public Key) + * ---------------------------------- + * + * Byte Encoding (0 for plaintext) + * + * Byte Key Type (10 for ECC public) + * + * Short Key Length (256, 384, 521 high byte first) + * + * Byte[] Key (W) + * + * + * Signature Format (Proof) + * --------------------------------------- + * + * The key generation operation creates a proof-of-location for the + * newly generated key. This proof is a signature computed with the + * new private key using the RSA-with-MD5 signature algorithm. The + * signature is computed over the Muscle Key Blob representation of + * the new public key and the challenge sent in the key generation + * request. These two data fields are concatenated together to form + * the input to the signature, without any other data or length fields. + * + * Byte[] Key Blob Data + * + * Byte[] Challenge + * + * + * Key Generation Result + * --------------------------------------- + * + * The key generation command puts the key blob and the signature (proof) + * into the output buffer using the following format: + * + * Short Length of the Key Blob + * + * Byte[] Key Blob Data + * + * Short Length of the Proof + * + * Byte[] Proof (Signature) Data + * + * @param blob the publickey blob to be parsed + * @param challenge the challenge generated by TPS + * + ******/ + private RSAPublicKey parsePublicKeyBlob( + TPSBuffer public_key_blob, + /* TPSBuffer challenge,*/ + boolean isECC) + throws TPSException { + RSAPublicKey parsedPubKey = null; + + if (public_key_blob == null /*|| challenge == null*/) { + throw new TPSException( + "TPSEnrollProcessor.parsePublicKeyBlob: Bad input data! Missing public_key_blob or challenge", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: public key blob from token to parse: " + + public_key_blob.toHexString()); + /* + * decode blob into structures + */ + + // offset to the beginning of the public key length. should be 0 + int pkeyb_len_offset = 0; + + /* + * now, convert lengths + */ + // 1st, keyblob length + /* + byte len0 = public_key_blob.at(pkeyb_len_offset); + byte len1 = public_key_blob.at(pkeyb_len_offset + 1); + int pkeyb_len = (len0 << 8) | (len1 & 0xFF); + */ + int pkeyb_len = public_key_blob.getIntFrom2Bytes(pkeyb_len_offset); + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: pkeyb_len = " + + pkeyb_len + ", isECC: " + isECC); + // public key blob + TPSBuffer pkeyb = public_key_blob.substr(pkeyb_len_offset + 2, pkeyb_len); + if (pkeyb == null) { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: pkeyb null "); + throw new TPSException("TPSEnrollProcessor.parsePublicKeyBlob: Bad input data! pkeyb null", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: pkeyb = " + + pkeyb.toHexString()); + + // 2nd, proof blob length + int proofb_len_offset = pkeyb_len_offset + 2 + pkeyb_len; + /* + len0 = public_key_blob.at(proofb_len_offset); + len1 = public_key_blob.at(proofb_len_offset + 1); + int proofb_len = (len0 << 8 | len1 & 0xFF); + */ + int proofb_len = public_key_blob.getIntFrom2Bytes(proofb_len_offset); + // proof blob + TPSBuffer proofb = public_key_blob.substr(proofb_len_offset + 2, proofb_len); + if (proofb == null) { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: proofb null "); + throw new TPSException("TPSEnrollProcessor.parsePublicKeyBlob: Bad input data! proofb null", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: proofb = " + + proofb.toHexString()); + + // convert pkeyb to pkey + // 1 byte encoding, 1 byte key type, 2 bytes key length, then the key + int pkey_offset = 4; + /* + len0 = pkeyb.at(pkey_offset); + len1 = pkeyb.at(pkey_offset + 1); + */ + if (!isECC) { + // int mod_len = len0 << 8 | len1 & 0xFF; + int mod_len = pkeyb.getIntFrom2Bytes(pkey_offset); + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: mod_len= " + mod_len); + /* + len0 = pkeyb.at(pkey_offset + 2 + mod_len); + len1 = pkeyb.at(pkey_offset + 2 + mod_len + 1); + int exp_len = len0 << 8 | len1 & 0xFF; + */ + int exp_len = pkeyb.getIntFrom2Bytes(pkey_offset + 2 + mod_len); + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: exp_len= " + exp_len); + + TPSBuffer modb = pkeyb.substr(pkey_offset + 2, mod_len); + if (modb == null) { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: modb null "); + throw new TPSException("TPSEnrollProcessor.parsePublicKeyBlob: Bad input data! modb null", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: modb= " + + modb.toHexString()); + TPSBuffer expb = pkeyb.substr(pkey_offset + 2 + mod_len + 2, exp_len); + + if (expb == null) { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: expb null "); + throw new TPSException("TPSEnrollProcessor.parsePublicKeyBlob: Bad input data! expb null", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: expb= " + + expb.toHexString()); + BigInt modb_bi = new BigInt(modb.toBytesArray()); + BigInt expb_bi = new BigInt(expb.toBytesArray()); + try { + RSAPublicKey rsa_pub_key = new RSAPublicKey(modb_bi, expb_bi); + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: public key blob converted to RSAPublicKey"); + if (rsa_pub_key != null) { + parsedPubKey = rsa_pub_key; + } + } catch (InvalidKeyException e) { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob:InvalidKeyException thrown"); + throw new TPSException("TPSEnrollProcessor.parsePublicKeyBlob: Exception thrown: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + } else { + // TODO: handle ECC + } + + // TODO: challenge verification + + // sanity-check parsedPubKey before return + if (parsedPubKey == null) { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: parsedPubKey null"); + throw new TPSException( + "TPSEnrollProcessor.parsePublicKeyBlob: parsedPubKey null.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } else { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: parsedPubKey not null"); + } + byte[] parsedPubKey_ba = parsedPubKey.getEncoded(); + if (parsedPubKey_ba == null) { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: parsedPubKey_ba null"); + throw new TPSException( + "TPSEnrollProcessor.parsePublicKeyBlob: parsedPubKey encoding failure.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } else { + CMS.debug("TPSEnrollProcessor.parsePublicKeyBlob: parsedPubKey getEncoded not null"); + } + + return parsedPubKey; + } + + private boolean checkForServerSideKeyGen(CertEnrollInfo cInfo) throws TPSException { + + if (cInfo == null) { + throw new TPSException("TPSEnrollProcessor.checkForServerSideKeyGen: invalid cert info.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + IConfigStore configStore = CMS.getConfigStore(); + boolean serverSideKeygen = false; + + try { + String configValue = cInfo.getKeyTypePrefix() + "." + TPSEngine.CFG_SERVER_KEYGEN_ENABLE; + CMS.debug("TPSEnrollProcessor.checkForServerSideKeyGen: config: " + configValue); + serverSideKeygen = configStore.getBoolean( + configValue, false); + + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.checkForServerSideKeyGen: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSProcess.checkForServerSideKeyGen: returning: " + serverSideKeygen); + + return serverSideKeygen; + + } + + private boolean checkForServerKeyArchival(CertEnrollInfo cInfo) throws TPSException { + + if (cInfo == null) { + throw new TPSException("TPSEnrollProcessor.checkForServerKeyArchival: invalid cert info.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + IConfigStore configStore = CMS.getConfigStore(); + boolean serverKeyArchival = false; + + try { + String configValue = cInfo.getKeyTypePrefix() + "." + TPSEngine.CFG_SERVER_KEY_ARCHIVAL; + CMS.debug("TPSEnrollProcessor.checkForServerKeyArchival: config: " + configValue); + serverKeyArchival = configStore.getBoolean( + configValue, false); + + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.checkForServerKeyArchival: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSProcess.checkForServerKeyArchival: returning: " + serverKeyArchival); + + return serverKeyArchival; + + } + + private boolean checkForObjectOverwrite(CertEnrollInfo cInfo) throws TPSException { + + if (cInfo == null) { + throw new TPSException("TPSEnrollProcessor.checkForObjectOverwrite: invalid cert info.", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + IConfigStore configStore = CMS.getConfigStore(); + boolean objectOverwrite = false; + + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + getSelectedTokenType() + ".keyGen." + + cInfo.getKeyType() + "." + TPSEngine.CFG_OVERWRITE; + + CMS.debug("TPSProcess.checkForObjectOverwrite: config: " + configValue); + objectOverwrite = configStore.getBoolean( + configValue, true); + + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.checkForServerSideKeyGen: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSProcess.checkForObjectOverwrite: returning: " + objectOverwrite); + + return objectOverwrite; + + } + + private String getConfiguredKeyType(int keyTypeIndex) throws TPSException { + + IConfigStore configStore = CMS.getConfigStore(); + String keyType = null; + + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + + TPSEngine.CFG_KEYGEN_KEYTYPE_VALUE + "." + keyTypeIndex; + keyType = configStore.getString( + configValue, null); + + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.getConfiguredKeyType: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + //We would really like one of these to exist + + if (keyType == null) { + throw new TPSException( + "TPSEnrollProcessor.getConfiguredKeyType: Internal error finding config value: ", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + CMS.debug("TPSProcess.getConfiguredKeyType: returning: " + keyType); + + return keyType; + + } + + private String getDRMConnectorID() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + String id = null; + + String config = "op." + currentTokenOperation + "." + selectedTokenType + "." + TPSEngine.CFG_KEYGEN_ENCRYPTION + + "." + TPSEngine.CFG_DRM_CONNECTOR; + + CMS.debug("TPSEnrollProcessor.getDRMConnectorID: config value: " + config); + try { + id = configStore.getString(config, "kra1"); + } catch (EBaseException e) { + throw new TPSException("TPSEnrollProcessor.getDRMConnectorID: Internal error finding config value."); + + } + + CMS.debug("TPSEnrollProcessor.getDRMConectorID: returning: " + id); + + return id; + } + + protected int getNumberCertsToEnroll() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + int keyTypeNum = 0; + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + + TPSEngine.CFG_KEYGEN_KEYTYPE_NUM; + keyTypeNum = configStore.getInteger( + configValue, 0); + + } catch (EBaseException e) { + throw new TPSException("TPSEnrollProcessor.getNumberCertsToEnroll: Internal error finding config value: " + + e, + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + + } + + if (keyTypeNum == 0) { + throw new TPSException( + "TPSEnrollProcessor.getNumberCertsToEnroll: invalid number of certificates configured!", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + CMS.debug("TPSProcess.getNumberCertsToEnroll: returning: " + keyTypeNum); + + return keyTypeNum; + } + + protected int getEnrollmentAlg() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + int enrollmentAlg; + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + + TPSEngine.CFG_KEYGEN_ENCRYPTION + "." + TPSEngine.CFG_ALG; + + CMS.debug("TPSProcess.getEnrollmentAlg: configValue: " + configValue); + + enrollmentAlg = configStore.getInteger( + configValue, 2); + + } catch (EBaseException e) { + throw new TPSException("TPSEnrollProcessor.getEnrollmentAlg: Internal error finding config value: " + + e, + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + + } + + CMS.debug("TPSProcess.getEnrollmentAlg: returning: " + enrollmentAlg); + + return enrollmentAlg; + } + + protected String getRecoveryKeyTypeValue(String reason, int index) throws TPSException { + + if (reason == null || index < 0) { + throw new TPSException("TPSEnrollProcessor.getRecoveryKeyTypeValue: invalide input data!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + IConfigStore configStore = CMS.getConfigStore(); + String keyTypeValue; + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + TPSEngine.CFG_KEYGEN + + "." + + TPSEngine.RECOVERY_OP + "." + reason + "." + TPSEngine.CFG_KEYTYPE_VALUE + "." + index; + ; + + CMS.debug("TPSProcess.getRecoveryKeyTypeValue: configValue: " + configValue); + + keyTypeValue = configStore.getString( + configValue, null); + + } catch (EBaseException e) { + throw new TPSException("TPSEnrollProcessor.getRecoveryKeyTypeValue: Internal error finding config value: " + + e, + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + + } + + if (keyTypeValue == null) { + throw new TPSException("TPSEnrollProcessor.getRecoveryKeyTypeValue: Invalid keyTypeValue found! ", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + + CMS.debug("TPSProcess.getRecoveryKeyTypeValue: returning: " + keyTypeValue); + + return keyTypeValue; + } + + protected String getRecoveryScheme(String reason, String keyTypeValue) throws TPSException { + + if (reason == null || keyTypeValue == null) { + throw new TPSException("TPSEnrollProcessor.getRecoveryScheme: invalid input data!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + IConfigStore configStore = CMS.getConfigStore(); + String scheme = null; + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + TPSEngine.CFG_KEYGEN + + "." + keyTypeValue + "." + + TPSEngine.RECOVERY_OP + "." + reason + "." + TPSEngine.CFG_SCHEME; + ; + + CMS.debug("TPSProcess.getRecoveryScheme: configValue: " + configValue); + + scheme = configStore.getString( + configValue, null); + + } catch (EBaseException e) { + throw new TPSException("TPSEnrollProcessor.getRecoveryScheme: Internal error finding config value: " + + e, + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + + } + + if (scheme == null) { + throw new TPSException("TPSEnrollProcessor.getRecoverScheme: Invalid scheme found! ", + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + + CMS.debug("TPSProcess.getRecoveryScheme: returning: " + scheme); + + return scheme; + } + + protected int getNumberCertsForRecovery(String reason) throws TPSException { + if (reason == null) { + throw new TPSException("TPSEnrollProcessor.getNumberCertsForRecovery: invlalid input data!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + + IConfigStore configStore = CMS.getConfigStore(); + int keyTypeNum = 0; + try { + String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "." + TPSEngine.CFG_KEYGEN + + "." + TPSEngine.RECOVERY_OP + + "." + reason + "." + TPSEngine.CFG_KEYTYPE_NUM; + + CMS.debug("TPSEnrollProcessor.getNumberCertsForRecovery: configValue: " + configValue); + keyTypeNum = configStore.getInteger( + configValue, 0); + + } catch (EBaseException e) { + throw new TPSException( + "TPSEnrollProcessor.getNumberCertsForRecovery: Internal error finding config value: " + + e, + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + + } + + if (keyTypeNum == 0) { + throw new TPSException( + "TPSEnrollProcessor.getNumberCertsForRecovery: invalid number of certificates configured!", + TPSStatus.STATUS_ERROR_RECOVERY_FAILED); + } + CMS.debug("TPSProcess.getNumberCertsForRecovery: returning: " + keyTypeNum); + + return keyTypeNum; + } + + protected String getCAConnectorID() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + String id = null; + + String config = "op." + currentTokenOperation + "." + selectedTokenType + ".ca.conn"; + + try { + id = configStore.getString(config, "ca1"); + } catch (EBaseException e) { + throw new TPSException("TPSEnrollProcessor.getCAConnectorID: Internal error finding config value."); + + } + + CMS.debug("TPSEnrollProcessor.getCAConectorID: returning: " + id); + + return id; + } + + private TPSBuffer makeKeyIDFromPublicKeyInfo(byte[] publicKeyInfo) throws TPSException { + + final String alg = "SHA1"; + + if (publicKeyInfo == null) { + throw new TPSException("TPSEnrollProcessor.makeKeyIDFromPublicKeyInfo: invalid input data", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + TPSBuffer keyID = null; + + byte[] mozillaDigestOut; + + java.security.MessageDigest mozillaDigest; + try { + mozillaDigest = java.security.MessageDigest.getInstance(alg); + } catch (NoSuchAlgorithmException e) { + throw new TPSException("TPSEnrollProcessor.makeKeyIDFromPublicKeyInfo: " + e, + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + mozillaDigestOut = mozillaDigest.digest(publicKeyInfo); + + if (mozillaDigestOut.length == mozillaDigest.getDigestLength()) { + System.out.println(mozillaDigest.getAlgorithm() + " " + + " digest output size is " + mozillaDigestOut.length); + } else { + throw new TPSException("ERROR: digest output size is " + + mozillaDigestOut.length + ", should be " + + mozillaDigest.getDigestLength(), TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + keyID = new TPSBuffer(mozillaDigestOut); + + CMS.debug("TPSEnrollProcessor.makeKeyIDFromPublicKeyInfo: " + keyID.toHexString()); + + return keyID; + } + + public BigInteger serialNoToBigInt(String serialS) { + if (serialS == null) + return new BigInteger("0", 16); + + CMS.debug("TPSEnrollProcessor.seralNoToBigInt: serial # =" + serialS); + String serialhex = serialS.substring(2); // strip off the "0x" + BigInteger serialBI = new BigInteger(serialhex, 16); + + return serialBI; + } + + public static void main(String[] args) { + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java new file mode 100644 index 000000000..b92cd8b91 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java @@ -0,0 +1,66 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.processor; + +import java.io.IOException; + +import org.dogtagpki.server.tps.TPSSession; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.msg.BeginOpMsg; +import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus; + +import com.netscape.certsrv.apps.CMS; + +public class TPSPinResetProcessor extends TPSProcessor { + + public TPSPinResetProcessor(TPSSession session) { + super(session); + // TODO Auto-generated constructor stub + } + + @Override + public void process(BeginOpMsg beginMsg) throws TPSException, IOException { + if (beginMsg == null) { + throw new TPSException("TPSPinResetProcessor.process: invalid input data, not beginMsg provided.", + TPSStatus.STATUS_ERROR_CONTACT_ADMIN); + } + setBeginMessage(beginMsg); + setCurrentTokenOperation("pinReset"); + + resetPin(); + + } + + private void resetPin() throws TPSException { + + //ToDo: Implement full pin reset processor, the pin reset portion + // of an enrollment works fine. We just need to finish this to perform + // a completely stand alone pin reset of an already enrolled token. + CMS.debug("TPSPinResetProcessor.resetPin: entering..."); + + throw new TPSException("TPSPinResetProcessor.resetPin: Pin Reset standalone operation not yet supported!", + TPSStatus.STATUS_ERROR_MAC_RESET_PIN_PDU); + + } + + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java new file mode 100644 index 000000000..e23cea0ad --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java @@ -0,0 +1,2404 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.processor; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import netscape.security.x509.RevocationReason; + +import org.dogtagpki.server.tps.TPSSession; +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.authentication.AuthUIParameter; +import org.dogtagpki.server.tps.authentication.TPSAuthenticator; +import org.dogtagpki.server.tps.channel.SecureChannel; +import org.dogtagpki.server.tps.cms.CARemoteRequestHandler; +import org.dogtagpki.server.tps.cms.CARevokeCertResponse; +import org.dogtagpki.server.tps.cms.TKSComputeRandomDataResponse; +import org.dogtagpki.server.tps.cms.TKSComputeSessionKeyResponse; +import org.dogtagpki.server.tps.cms.TKSEncryptDataResponse; +import org.dogtagpki.server.tps.cms.TKSRemoteRequestHandler; +import org.dogtagpki.server.tps.dbs.ActivityDatabase; +import org.dogtagpki.server.tps.dbs.TPSCertRecord; +import org.dogtagpki.server.tps.dbs.TokenRecord; +import org.dogtagpki.server.tps.engine.TPSEngine; +import org.dogtagpki.server.tps.profile.BaseTokenProfileResolver; +import org.dogtagpki.server.tps.profile.TokenProfileParams; +import org.dogtagpki.tps.apdu.APDU; +import org.dogtagpki.tps.apdu.APDUResponse; +import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDU.SecurityLevel; +import org.dogtagpki.tps.apdu.GetDataAPDU; +import org.dogtagpki.tps.apdu.GetStatusAPDU; +import org.dogtagpki.tps.apdu.GetVersionAPDU; +import org.dogtagpki.tps.apdu.InitializeUpdateAPDU; +import org.dogtagpki.tps.apdu.ListObjectsAPDU; +import org.dogtagpki.tps.apdu.SelectAPDU; +import org.dogtagpki.tps.main.TPSBuffer; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.msg.BeginOpMsg; +import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus; +import org.dogtagpki.tps.msg.ExtendedLoginRequestMsg; +import org.dogtagpki.tps.msg.ExtendedLoginResponseMsg; +import org.dogtagpki.tps.msg.LoginRequestMsg; +import org.dogtagpki.tps.msg.LoginResponseMsg; +import org.dogtagpki.tps.msg.NewPinRequestMsg; +import org.dogtagpki.tps.msg.NewPinResponseMsg; +import org.dogtagpki.tps.msg.StatusUpdateRequestMsg; +import org.dogtagpki.tps.msg.TPSMessage; +import org.dogtagpki.tps.msg.TokenPDURequestMsg; +import org.dogtagpki.tps.msg.TokenPDUResponseMsg; +import org.mozilla.jss.CryptoManager.NotInitializedException; +import org.mozilla.jss.pkcs11.PK11SymKey; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthCredentials; +import com.netscape.certsrv.authentication.IAuthManager; +import com.netscape.certsrv.authentication.IAuthToken; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.EPropertyNotFound; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.tps.token.TokenStatus; +import com.netscape.symkey.SessionKey; + +public class TPSProcessor { + + public static final int RESULT_NO_ERROR = 0; + public static final int RESULT_ERROR = -1; + + public static final int CPLC_DATA_SIZE = 47; + public static final int CPLC_MSN_INDEX = 41; + public static final int CPLC_MSN_SIZE = 4; + + public static final int INIT_UPDATE_DATA_SIZE = 28; + public static final int DIVERSIFICATION_DATA_SIZE = 10; + public static final int CARD_CRYPTOGRAM_OFFSET = 20; + public static final int CARD_CRYPTOGRAM_SIZE = 8; + public static final int CARD_CHALLENGE_OFFSET = 12; + public static final int CARD_CHALLENGE_SIZE = 8; + + protected boolean isExternalReg; + + protected TPSSession session; + //protected TokenRecord tokenRecord; + protected String selectedTokenType; + + protected String userid = null; + protected String currentTokenOperation; + + protected BeginOpMsg beginMsg; + + public TPSProcessor(TPSSession session) { + setSession(session); + } + + protected void setCurrentTokenOperation(String op) { + currentTokenOperation = op; + } + + protected void setSession(TPSSession session) { + if (session == null) { + throw new NullPointerException("TPS session is null"); + } + this.session = session; + } + + protected TPSSession getSession() { + return session; + } + + protected TokenRecord getTokenRecord() { + TPSSession session = getSession(); + return session.getTokenRecord(); + } + + protected void setBeginMessage(BeginOpMsg msg) { + beginMsg = msg; + } + + public BeginOpMsg getBeginMessage() { + return beginMsg; + } + + protected void setSelectedTokenType(String theTokenType) { + + if (theTokenType == null) { + throw new NullPointerException("TPSProcessor.setSelectedTokenType: Attempt to set invalid null token type!"); + } + CMS.debug("TPS_Processor.setSelectedTokenType: tokenType=" + + theTokenType); + selectedTokenType = theTokenType; + + TokenRecord tokenRecord = getTokenRecord(); + tokenRecord.setType(selectedTokenType); + } + + public String getSelectedTokenType() { + return selectedTokenType; + } + + protected TPSBuffer extractTokenMSN(TPSBuffer cplc_data) throws TPSException { + //Just make sure no one is inputing bogus cplc_data + if (cplc_data == null || cplc_data.size() < CPLC_DATA_SIZE) { + throw new TPSException("TPSProcessor.extractTokenMSN: Can't extract token msn from cplc data!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + TPSBuffer token_msn = cplc_data.substr(CPLC_MSN_INDEX, CPLC_MSN_SIZE); + return token_msn; + + } + + protected TPSBuffer extractTokenCUID(TPSBuffer cplc_data) throws TPSException { + //Just make sure no one is inputing bogus cplc_data + if (cplc_data == null || cplc_data.size() < CPLC_DATA_SIZE) { + CMS.debug("TPS_Processor.extractTokenCUID: cplc_data: invalid length."); + throw new TPSException("TPSProcessor.extractTokenCUID: Can't extract cuid from cplc data!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + TPSBuffer token1 = cplc_data.substr(3, 4); + TPSBuffer token2 = cplc_data.substr(19, 2); + TPSBuffer token3 = cplc_data.substr(15, 4); + + TPSBuffer token_cuid = new TPSBuffer(); + + token_cuid.add(token1); + token_cuid.add(token2); + token_cuid.add(token3); + + return token_cuid; + + } + + /** + * Select applet. + * + * Global Platform Open Platform Card Specification + * Version 2.0.1 Page 9-22 + * + * Sample Data: + * + * _____________ CLA + * | __________ INS + * | | _______ P1 + * | | | ____ P2 + * | | | | _ Len + * | | | | | + * 00 A4 04 00 07 + * 53 4C 42 47 49 4E 41 + * + * @throws IOException + * @throws TPSException + * + */ + + protected APDUResponse selectApplet(byte p1, byte p2, TPSBuffer aid) throws IOException, TPSException { + + CMS.debug("In TPS_Processor.SelectApplet."); + + if (aid == null || aid.size() == 0) { + throw new TPSException("TPSProcessor.selectApplet: Invalid aid value!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + SelectAPDU select_apdu = new SelectAPDU(p1, p2, aid); + + //return the Response because the caller can + //decide what to do, not every failure is fatal. + //For instance the coolkey applet may not yet exist. + return handleAPDURequest(select_apdu); + + } + + protected TPSBuffer getStatus() throws IOException, TPSException { + + CMS.debug("In TPS_Processor.GetStatus."); + + GetStatusAPDU get_status_apdu = new GetStatusAPDU(); + + return handleAPDURequest(get_status_apdu).getData(); + } + + public APDUResponse handleAPDURequest(APDU apdu) throws IOException, TPSException { + + if (apdu == null) { + throw new TPSException("TPSProcessor.handleAPDURequest: invalid incoming apdu!"); + } + + TokenPDURequestMsg request_msg = new TokenPDURequestMsg(apdu); + + try { + session.write(request_msg); + } catch (IOException e) { + CMS.debug("TPS_Processor.HandleAPDURequest failed WriteMsg: " + e.toString()); + throw e; + + } + + TokenPDUResponseMsg response_msg = null; + + try { + response_msg = (TokenPDUResponseMsg) session.read(); + } catch (IOException e) { + CMS.debug("TPS_Processor.HandleAPDURequest failed ReadMsg: " + e.toString()); + throw e; + + } + + return response_msg.getResponseAPDU(); + } + + protected TPSBuffer getCplcData() throws IOException, TPSException { + CMS.debug("In TPS_Processor.GetData"); + + GetDataAPDU get_data_apdu = new GetDataAPDU(); + + APDUResponse respApdu = handleAPDURequest(get_data_apdu); + + if (!respApdu.checkResult()) { + throw new TPSException("TPSProcessor.getCplcData: Can't get data!", TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + TPSBuffer cplcData = respApdu.getData(); + + if (cplcData.size() != CPLC_DATA_SIZE) { + throw new TPSException("TPSProcessor.cplcData: Data invalid size!", TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + return respApdu.getData(); + } + + protected TPSBuffer getAppletVersion() throws IOException, TPSException { + //We return null if no applet present + // This is not an error, the token can be blank. + + CMS.debug("In TPSProcessor.getAppletVersion"); + + GetVersionAPDU get_version_apdu = new GetVersionAPDU(); + + APDUResponse respApdu = handleAPDURequest(get_version_apdu); + + if (!respApdu.checkResult()) { + CMS.debug("TPSProcessor.getAppletVersion: No applet version found on card!"); + return null; + } + + TPSBuffer apdu_data = respApdu.getData(); + + if (apdu_data.size() != 6) { + CMS.debug("TPSProcessor.getAppletVersion: incorrect return data size!"); + throw new TPSException("TPSProcessor.getAppletVersion: invalid applet version string returned!"); + } + + TPSBuffer build_id = apdu_data.substr(0, 4); + + CMS.debug("TPSProcessor.getAppletVersion: returning: " + build_id.toHexString()); + + return build_id; + + } + + protected TPSBuffer encryptData(AppletInfo appletInfo, TPSBuffer keyInfo, TPSBuffer plaintextChallenge, + String connId) throws TPSException { + + TKSRemoteRequestHandler tks = null; + + TKSEncryptDataResponse data = null; + + try { + tks = new TKSRemoteRequestHandler(connId); + data = tks.encryptData(appletInfo.getCUID(), keyInfo, plaintextChallenge); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.encryptData: Erorr getting wrapped data from TKS!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + int status = data.getStatus(); + + if (status != 0) { + throw new TPSException("TPSProcessor.computeRandomData: Erorr getting wrapped data from TKS!", + TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU); + } + + return data.getEncryptedData(); + } + + TPSBuffer computeRandomData(int dataSize, String connId) throws TPSException { + + TKSRemoteRequestHandler tks = null; + + TKSComputeRandomDataResponse data = null; + + try { + tks = new TKSRemoteRequestHandler(connId); + data = tks.computeRandomData(dataSize); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.computeRandomData: Erorr getting random data from TKS!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + int status = data.getStatus(); + + if (status != 0) { + throw new TPSException("TPSProcessor.computeRandomData: Erorr getting random data from TKS!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + return data.getRandomData(); + } + + protected TPSBuffer initializeUpdate(byte keyVersion, byte keyIndex, TPSBuffer randomData) throws IOException, + TPSException { + + CMS.debug("In TPS_Processor.initializeUpdate."); + InitializeUpdateAPDU initUpdate = new InitializeUpdateAPDU(keyVersion, keyIndex, randomData); + + APDUResponse resp = handleAPDURequest(initUpdate); + + if (!resp.checkResult()) { + CMS.debug("TPSProcessor.initializeUpdate: Failed intializeUpdate!"); + throw new TPSException("TPSBuffer.initializeUpdate: Failed initializeUpdate!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + + } + + TPSBuffer data = resp.getResultDataNoCode(); + + if (data.size() != INIT_UPDATE_DATA_SIZE) { + throw new TPSException("TPSBuffer.initializeUpdate: Invalid response from token!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + return data; + + } + + protected SecureChannel setupSecureChannel() throws TPSException, IOException { + SecureChannel channel = null; + + //Create a standard secure channel with current key set. + CMS.debug("TPSProcessor.setupSecureChannel: No arguments entering..."); + + int defKeyVersion = getChannelDefKeyVersion(); + int defKeyIndex = getChannelDefKeyIndex(); + + channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, SecurityLevel.SECURE_MSG_MAC_ENC, + getTKSConnectorID()); + + channel.externalAuthenticate(); + + return channel; + } + + protected SecureChannel setupSecureChannel(byte keyVersion, byte keyIndex, SecurityLevel securityLevel, + String connId) + throws IOException, TPSException { + + //Assume generating host challenge on TKS, we no longer support not involving the TKS. + + TPSBuffer randomData = computeRandomData(8, connId); + CMS.debug("TPSProcessor.setupSecureChannel: obtained randomData: " + randomData.toHexString()); + + TPSBuffer initUpdateResp = initializeUpdate(keyVersion, keyIndex, randomData); + + TPSBuffer key_diversification_data = initUpdateResp.substr(0, DIVERSIFICATION_DATA_SIZE); + CMS.debug("TPSProcessor.setupSecureChannel: diversification data: " + key_diversification_data.toHexString()); + + TPSBuffer key_info_data = initUpdateResp.substr(DIVERSIFICATION_DATA_SIZE, 2); + CMS.debug("TPSProcessor.setupSecureChannel: key info data: " + key_info_data.toHexString()); + + TokenRecord tokenRecord = getTokenRecord(); + tokenRecord.setKeyInfo(key_info_data.toHexStringPlain()); + + TPSBuffer card_cryptogram = initUpdateResp.substr(CARD_CRYPTOGRAM_OFFSET, CARD_CRYPTOGRAM_SIZE); + CMS.debug("TPSProcessor.setupSecureChannel: card cryptogram: " + card_cryptogram.toHexString()); + + TPSBuffer card_challenge = initUpdateResp.substr(CARD_CHALLENGE_OFFSET, CARD_CHALLENGE_SIZE); + CMS.debug("TPSProcessor.setupSecureChannel: card challenge: " + card_challenge.toHexString()); + + SecureChannel channel = null; + + try { + channel = generateSecureChannel(connId, key_diversification_data, key_info_data, card_challenge, + card_cryptogram, + randomData); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.setupSecureChannel: Can't set up secure channel: " + e, + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + return channel; + + } + + protected SecureChannel generateSecureChannel(String connId, TPSBuffer keyDiversificationData, + TPSBuffer keyInfoData, TPSBuffer cardChallenge, TPSBuffer cardCryptogram, TPSBuffer hostChallenge) + throws EBaseException, TPSException { + + CMS.debug("TPSProcessor.generateSecureChannel: entering.."); + + TPSEngine engine = getTPSEngine(); + + SecureChannel channel = null; + TPSBuffer hostCryptogram = null; + + TKSComputeSessionKeyResponse resp = engine.computeSessionKey(keyDiversificationData, keyInfoData, + cardChallenge, hostChallenge, cardCryptogram, + connId, getSelectedTokenType()); + + hostCryptogram = resp.getHostCryptogram(); + + if (hostCryptogram == null) { + new TPSException("TPSProcessor.generateSecureChannel: No host cryptogram returned from token!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + + } + + PK11SymKey sharedSecret = null; + + try { + sharedSecret = getSharedSecretTransportKey(connId); + } catch (Exception e) { + CMS.debug(e); + throw new TPSException("TPSProcessor.generateSecureChannel: Can't get shared secret key!: " + e, + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + PK11SymKey sessionKey = null; + PK11SymKey encSessionKey = null; + String tokenName = "Internal Key Storage Token"; + + try { + TPSBuffer sessionKeyWrapped = resp.getSessionKey(); + TPSBuffer encSessionKeyWrapped = resp.getEncSessionKey(); + + sessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret, + sessionKeyWrapped.toBytesArray()); + + if (sessionKey == null) { + CMS.debug("TPSProcessor.generateSecureChannel: Can't extract session key!"); + throw new TPSException("TPSProcessor.generateSecureChannel: Can't extract session key!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + CMS.debug("TPSProcessor.generateSecureChannel: retrieved session key: " + sessionKey); + + encSessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret, + encSessionKeyWrapped.toBytesArray()); + + if (encSessionKey == null) { + CMS.debug("TPSProcessor.generateSecureChannel: Can't extract enc session key!"); + throw new TPSException("TPSProcessor.generateSecureChannel: Can't extract enc session key!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + CMS.debug("TPSProcessor.generateSecureChannel: retrieved enc session key: " + encSessionKey); + } catch (Exception e) { + CMS.debug(e); + e.printStackTrace(); + throw new TPSException("TPSProcessor.generateSecureChannel: Problem extracting session keys! " + e, + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + TPSBuffer drmDesKey = null; + TPSBuffer kekDesKey = null; + TPSBuffer keyCheck = null; + + drmDesKey = resp.getDRM_Trans_DesKey(); + keyCheck = resp.getKeyCheck(); + kekDesKey = resp.getKekWrappedDesKey(); + + if (checkServerSideKeyGen(connId)) { + + CMS.debug("TPSProcessor.generateSecureChannel: drmDesKey: " + drmDesKey + " kekDesKey : " + kekDesKey + + " keyCheck: " + keyCheck); + //ToDo handle server side keygen. + + } + channel = new SecureChannel(this, sessionKey, encSessionKey, drmDesKey, + kekDesKey, keyCheck, keyDiversificationData, cardChallenge, + cardCryptogram, hostChallenge, hostCryptogram, keyInfoData); + + return channel; + } + + protected void upgradeApplet(String operation, String new_version, SecurityLevel securityLevel, + Map<String, String> extensions, String connId, int startProgress, int endProgress) throws IOException, + TPSException { + + TPSBuffer netkeyAIDBuff = null; + TPSBuffer cardMgrAIDBuff = null; + TPSBuffer netkeyPAIDBuff = null; + + netkeyAIDBuff = getNetkeyAID(); + netkeyPAIDBuff = getNetkeyPAID(); + cardMgrAIDBuff = getCardManagerAID(); + + int channelBlockSize = getChannelBlockSize(); + int channelInstanceSize = getChannelInstanceSize(); + int channelAppletMemSize = getAppletMemorySize(); + int defKeyVersion = getChannelDefKeyVersion(); + int defKeyIndex = getChannelDefKeyIndex(); + + byte[] appletData = null; + + TokenRecord tokenRecord = getTokenRecord(); + + String directory = getAppletDirectory(operation); + + CMS.debug("TPSProcessor.upgradeApplet: applet target directory: " + directory); + + String appletFileExt = getAppletExtension(); + + String appletFilePath = directory + "/" + new_version + "." + appletFileExt; + + CMS.debug("TPSProcessor.upgradeApplet: targe applet file name: " + appletFilePath); + + appletData = getAppletFileData(appletFilePath); + + APDUResponse select = selectApplet((byte) 0x04, (byte) 0x00, cardMgrAIDBuff); + + if (!select.checkResult()) { + throw new TPSException("TPSProcessor.upgradeApplet: Can't selelect the card manager!"); + } + + SecureChannel channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, securityLevel, connId); + + channel.externalAuthenticate(); + channel.deleteFileX(netkeyAIDBuff); + channel.deleteFileX(netkeyPAIDBuff); + + // Next step will be to load the applet file to token. + + TPSBuffer empty = new TPSBuffer(); + + channel.installLoad(netkeyPAIDBuff, empty, appletData.length); + + TPSBuffer appletDataBuff = new TPSBuffer(appletData); + + channel.loadFile(appletDataBuff, channelBlockSize, startProgress, endProgress); + + channel.installApplet(netkeyPAIDBuff, netkeyAIDBuff, (byte) 0, channelInstanceSize, channelAppletMemSize); + + //Now select our new applet + + select = selectApplet((byte) 0x04, (byte) 0x00, netkeyAIDBuff); + + if (!select.checkResult()) { + throw new TPSException("TPSProcessor.upgradeApplet: Cannot select newly created applet!", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + tokenRecord.setAppletID(new_version); + + } + + public void selectCoolKeyApplet() throws TPSException, IOException { + + TPSBuffer netkeyAIDBuff = getNetkeyAID(); + + APDUResponse select = selectApplet((byte) 0x04, (byte) 0x00, netkeyAIDBuff); + + if (!select.checkResult()) { + CMS.debug("TPSProcessor.selectCoolKeyApplet: Can't select coolkey, token may be blank."); + /* throw new TPSException("TPSProcessor.upgradeApplet: Cannot select newly created applet!", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + */ + } + } + + protected byte[] getAppletFileData(String appletFilePath) throws IOException, TPSException { + + if (appletFilePath == null) { + throw new TPSException("TPSProcessor.getAppletFileData: Invalid applet file name.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + byte[] contents = null; + try { + Path path = Paths.get(appletFilePath); + contents = Files.readAllBytes(path); + + } catch (IOException e) { + CMS.debug("TPSProcessor.getAppletFileData: IOException " + e); + throw e; + } catch (Exception e) { + CMS.debug("PSProcessor.getAppletFileData: Exception: " + e); + throw new TPSException("TPSProcessor.getAppletFileData: Exception: " + e, + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + CMS.debug("TPSProcessor.getAppletFileData: data: " + contents); + + return contents; + } + + /** + * getAuthentication gets Authentication per configuration + * + * @param prefix config prefix for tokenType + * @param tokenType the tokenType(profile) + * @return Authentication + */ + public TPSAuthenticator getAuthentication(String prefix, String tokenType) + throws EBaseException { + CMS.debug("TPSProcessor.getAuthentication"); + String auditMsg = null; + + if (prefix.isEmpty() || tokenType.isEmpty()) { + auditMsg = "TPSProcessor.getAuthentication: missing parameters: prefix or tokenType"; + CMS.debug(auditMsg); + throw new EBaseException(auditMsg); + } + IConfigStore configStore = CMS.getConfigStore(); + String configName = prefix + "." + tokenType + ".auth.id"; + String authId; + + CMS.debug("TPSProcessor.getAuthentication: getting config: " + + configName); + authId = configStore.getString(configName); + if (authId == null) { + auditMsg = "TPSProcessor.getAuthentication: config param not found:" + configName; + CMS.debug(auditMsg); + throw new EBaseException(auditMsg); + } + + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + TPSAuthenticator authInst = + subsystem.getAuthenticationManager().getAuthInstance(authId); + String authCredNameConf = "auths.instance." + authId + ".authCredName"; + CMS.debug("TPSProcessor.getAuthentication: getting config: " + + authCredNameConf); + String authCredName = configStore.getString(authCredNameConf); + if (authCredName == null) { + auditMsg = "TPSProcessor.getAuthentication: config param not found:" + authCredNameConf; + CMS.debug(auditMsg); + throw new EBaseException(auditMsg); + } + authInst.setAuthCredName(authCredName); + return authInst; + } + + /** + * authenticateUser authenticates a user using specified authentication + * + * @param op "enrollment", "format", or "pinReset" //TODO: for tokendb activity log + * @param userAuth the authenticator + * @param userCred IAuthCredentials obtained from a successful requestUserId call + * @return IAuthToken information relating to the performed authentication + * -- plugin-specific + */ + public IAuthToken authenticateUser( + String op, + TPSAuthenticator userAuth, + IAuthCredentials userCred) + throws EBaseException, TPSException { + /** + * TODO: isExternalReg is not handled until decision made + */ + String auditMsg = null; + CMS.debug("TPSProcessor.authenticateUser"); + if (op.isEmpty() || userAuth == null || userCred == null) { + auditMsg = "TPSProcessor.authenticateUser: missing parameter(s): op, userAuth, or userCred"; + CMS.debug(auditMsg); + throw new EBaseException(auditMsg); + } + IAuthManager auth = userAuth.getAuthManager(); + + try { + // Authenticate user + IAuthToken aToken = auth.authenticate(userCred); + if (aToken != null) { + CMS.debug("TPSProcessor.authenticateUser: authentication success"); + return aToken; + } else { + CMS.debug("TPSProcessor.authenticateUser: authentication failure with aToken null"); + throw new TPSException("TPS error user authentication failed.", + TPSStatus.STATUS_ERROR_LOGIN); + } + } catch (EBaseException e) { + CMS.debug("TPSProcessor.authenticateUser: authentication failure:" + e); + throw new TPSException("TPS error user authentication failed.", + TPSStatus.STATUS_ERROR_LOGIN); + } + } + + /** + * requestUserId sends message to client to request for user credential + * per authentication plugin + * + * @param op "enrollment", "format", or "pinReset" //TODO: for tokendb activity log + * @param cuid token CUID //TODO: for tokendb activity log + * @param extensions message extensions + * @return IAuthCredentials containing user credential needed for authentication + */ + IAuthCredentials requestUserId(String op, String cuid, TPSAuthenticator auth, Map<String, String> extensions) + throws IOException, TPSException, EBaseException { + CMS.debug("TPSProcessor.requestUserId"); + if (op.isEmpty() || + cuid.isEmpty() || auth == null) { + CMS.debug("TPSProcessor.requestUserId: missing parameter(s): op, cuid, or auth"); + throw new EBaseException("TPSProcessor.requestUserId: missing parameter(s): op, cuid, or auth"); + } + + IAuthCredentials login; + if (extensions != null && + extensions.get("extendedLoginRequest") != null) { + // default locale will be "en" + String locale = extensions.get("locale"); + if (extensions.get("locale") == null) { + locale = "en"; + } + // title + String title = auth.getUiTitle(locale); + if (title.isEmpty()) + title = auth.getUiTitle("en"); + // description + String description = auth.getUiDescription(locale); + if (description.isEmpty()) + description = auth.getUiTitle("en"); + // parameters + HashMap<String, AuthUIParameter> authParamSet = auth.getUiParamSet(); + Set<String> params = new HashSet<String>(); + for (Map.Entry<String, AuthUIParameter> entry : authParamSet.entrySet()) { + params.add(auth.getUiParam(entry.getKey()).toString(locale)); + CMS.debug("TPSProcessor.requestUserId: for extendedLoginRequest, added param: " + + auth.getUiParam(entry.getKey()).toString(locale)); + } + + login = requestExtendedLogin(0 /* invalid_pw */, 0 /* blocked */, + params, title, description, auth); + } else { + login = requestLogin(0 /* invalid_pw */, 0 /* blocked */, auth); + } + + return login; + } + + /** + * mapCredFromMsgResponse fills up authManager required auth credentials + * with mapped values from client + * configuration example: + * + * auths.instance.ldap1.ui.id.UID.credMap.msgCred.extlogin=UID + * auths.instance.ldap1.ui.id.UID.credMap.msgCred.login=screen_name + * auths.instance.ldap1.ui.id.UID.credMap.authCred=uid + * + * auths.instance.ldap1.ui.id.PASSWORD.credMap.msgCred.extlogin=PASSWORD + * auths.instance.ldap1.ui.id.PASSWORD.credMap.msgCred.login=password + * auths.instance.ldap1.ui.id.PASSWORD.credMap.authCred=pwd + * + * @param response the message response to be mapped + * @param auth the authentication for mapping consultation + * @return IAuthCredentials auth credential for auth manager + */ + public IAuthCredentials mapCredFromMsgResponse(TPSMessage response, TPSAuthenticator auth, boolean extendedLogin) + throws EBaseException { + CMS.debug("TPSProcessor.mapCredFromMsgResponse"); + if (response == null || auth == null) { + CMS.debug("TPSProcessor.mapCredFromMsgResponse: missing parameter(s): response or auth"); + throw new EBaseException("TPSProcessor.mapCredFromMsgResponse: missing parameter(s): response or auth"); + } + IAuthCredentials login = + new com.netscape.certsrv.authentication.AuthCredentials(); + + String[] requiredCreds = auth.getAuthManager().getRequiredCreds(); + for (String cred : requiredCreds) { + String name = auth.getCredMap(cred, extendedLogin); + CMS.debug("TPSProcessor.mapCredFromMsgResponse: cred=" + cred + " &name=" + + name); + login.set(cred, response.get(name)); + } + + return login; + } + + /** + * Requests login ID and password from user. + */ + public IAuthCredentials requestExtendedLogin(int invalidPW, int blocked, + Set<String> parameters, + String title, + String description, + TPSAuthenticator auth) + throws IOException, TPSException, EBaseException { + + CMS.debug("TPSProcessor.requestExtendedLogin"); + if (parameters == null || title.isEmpty() || + description.isEmpty() || auth == null) { + CMS.debug("TPSProcessor.requestExtendedLogin: missing parameter(s): parameters, title, description, or auth"); + throw new EBaseException( + "TPSProcessor.requestExtendedLogin: missing parameter(s): parameters, title, description, or auth"); + } + ExtendedLoginRequestMsg loginReq = + new ExtendedLoginRequestMsg(invalidPW, blocked, parameters, title, description); + + try { + session.write(loginReq); + } catch (IOException e) { + CMS.debug("TPSProcessor.requestExtendedLogin failed WriteMsg: " + e.toString()); + throw e; + } + CMS.debug("TPSProcessor.requestExtendedLogin: extendedLoginRequest sent"); + + ExtendedLoginResponseMsg loginResp = null; + try { + loginResp = (ExtendedLoginResponseMsg) session.read(); + } catch (IOException e) { + CMS.debug("TPSProcessor.requestExtendedLogin failed ReadMsg: " + e.toString()); + throw e; + } + + IAuthCredentials login = mapCredFromMsgResponse(loginResp, auth, true /*extendedLogin*/); + + return login; + } + + /** + * Requests login ID and password from user. + */ + public IAuthCredentials requestLogin(int invalidPW, int blocked, + TPSAuthenticator auth) + throws IOException, TPSException, EBaseException { + + CMS.debug("TPSProcessor.requestLogin"); + if (auth == null) { + CMS.debug("TPSProcessor.requestLogin: missing parameter(s): parameters, title, description, or auth"); + throw new EBaseException( + "TPSProcessor.requestLogin: missing parameter(s): parameters, title, description, or auth"); + } + LoginRequestMsg loginReq = new LoginRequestMsg(invalidPW, blocked); + + try { + session.write(loginReq); + } catch (IOException e) { + CMS.debug("TPSProcessor.requestLogin failed WriteMsg: " + e.toString()); + throw e; + } + CMS.debug("TPSProcessor.requestLogin: loginRequest sent"); + + LoginResponseMsg loginResp = null; + try { + loginResp = (LoginResponseMsg) session.read(); + } catch (IOException e) { + CMS.debug("TPSProcessor.requestLogin failed ReadMsg: " + e.toString()); + throw e; + } + + IAuthCredentials login = mapCredFromMsgResponse(loginResp, auth, false /*not extendedLogin*/); + return login; + } + + /* + * fillTokenRecord - + * - retrieves token record from tokendb if it exists, or + * - creates a new token record + * this in-memory copy of tokenRecord is to be set in the TPSSession + */ + protected void fillTokenRecord(TokenRecord tokenRecord, AppletInfo appletInfo) + throws TPSException { + String method = "TPSProcessor.fillTokenRecord"; + CMS.debug(method + ": begins"); + if (tokenRecord == null || appletInfo == null) { + CMS.debug(method + ": params tokenRecord and appletInfo cannot be null"); + throw new TPSException( + method + ": missing parameter(s): parameter appletInfo"); + } + + byte app_major_version = appletInfo.getAppMajorVersion(); + byte app_minor_version = appletInfo.getAppMinorVersion(); + TPSBuffer build_id = null; + try { + build_id = getAppletVersion(); + } catch (IOException e) { + CMS.debug(method + ": failed getting applet version:" + e + " ... continue"); + } + if (build_id != null) { + tokenRecord.setAppletID(Integer.toHexString(app_major_version) + "." + + Integer.toHexString(app_minor_version) + "." + + build_id.toHexStringPlain()); + } + + CMS.debug(method + ": ends"); + + } + + protected TokenRecord isTokenRecordPresent(AppletInfo appletInfo) throws TPSException { + + if (appletInfo == null) { + throw new TPSException("TPSProcessor.isTokenRecordPresent: invalid input data."); + } + + CMS.debug("TPSEnrollProcessor.isTokenRecordPresent: " + appletInfo.getCUIDhexString()); + + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + TokenRecord tokenRecord = null; + try { + tokenRecord = tps.tdb.tdbGetTokenEntry(appletInfo.getCUIDhexStringPlain()); + // now the in memory tokenRecord is replaced by the actual token data + CMS.debug("TPSEnrollProcessor.enroll: found token..."); + } catch (Exception e) { + CMS.debug("TPSEnrollProcessor.enroll: token does not exist in tokendb... create one in memory"); + } + + return tokenRecord; + } + + protected String getCAConnectorID() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + String id = null; + + String config = "op." + currentTokenOperation + "." + selectedTokenType + ".ca.conn"; + + try { + id = configStore.getString(config, "ca1"); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getCAConnectorID: Internal error finding config value."); + + } + + CMS.debug("TPSProcessor.getCAConectorID: returning: " + id); + + return id; + } + + /* + * revokeCertsAtFormat returns a boolean that tells if config wants to revoke certs on the token during format + */ + protected boolean revokeCertsAtFormat() { + String method = "revokeCertsAtFormat"; + String auditMsg; + CMS.debug(method + ": begins"); + + IConfigStore configStore = CMS.getConfigStore(); + String configName = TPSEngine.OP_FORMAT_PREFIX + "." + selectedTokenType + ".revokeCert"; + boolean revokeCert = false; + try { + revokeCert = configStore.getBoolean(configName, false); + } catch (EBaseException e) { + auditMsg = method + ": config not found: " + configName + + "; default to false"; + CMS.debug(auditMsg); + } + if (!revokeCert) { + auditMsg = method + ": revokeCert = false"; + CMS.debug(auditMsg); + } + return revokeCert; + } + + protected RevocationReason getRevocationReasonAtFormat() { + String method = "getRevocationReasonAtFormat"; + String auditMsg; + + IConfigStore configStore = CMS.getConfigStore(); + String configName = TPSEngine.OP_FORMAT_PREFIX + "." + selectedTokenType + ".revokeCert.revokeReason"; + RevocationReason revokeReason = RevocationReason.UNSPECIFIED; + try { + int revokeReasonInt = configStore.getInteger(configName); + revokeReason = RevocationReason.fromInt(revokeReasonInt); + } catch (EBaseException e) { + auditMsg = method + ": config not found: " + configName + + "; default to unspecified"; + CMS.debug(auditMsg); + revokeReason = RevocationReason.UNSPECIFIED; + } + + return revokeReason; + } + + /* + * revokeCertificates revokes certificates on the token specified + * @param cuid the cuid of the token to revoke certificates + * @return auditMsg captures the audit message + * @throws TPSException in case of error + * + * TODO: maybe make this a callback function later + */ + protected void revokeCertificates(String cuid, RevocationReason revokeReason, String caConnId) throws TPSException { + String auditMsg = ""; + final String method = "TPSProcessor.revokeCertificates"; + + if (cuid == null) { + auditMsg = "cuid null"; + CMS.debug(method + ":" + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + CMS.debug(method + ": begins for cuid:" + cuid); + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + boolean isTokenPresent = tps.tdb.isTokenPresent(cuid); + if (!isTokenPresent) { + auditMsg = method + ": token not found: " + cuid; + CMS.debug(auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + + CARemoteRequestHandler caRH = null; + try { + caRH = new CARemoteRequestHandler(caConnId); + } catch (EBaseException e) { + auditMsg = method + ": getting CARemoteRequestHandler failure"; + CMS.debug(auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + //find all certs belonging to the token + ArrayList<TPSCertRecord> certRecords = tps.tdb.tdbGetCertificatesByCUID(cuid); + + CMS.debug(method + ": found " + certRecords.size() + " certs"); + + for (TPSCertRecord cert : certRecords) { + if (cert.getStatus().equals("revoked")) { + // already revoked cert should not be on token any more + CMS.debug(method + ": cert " + cert.getSerialNumber() + + " already revoked; remove from tokendb and move on"); + try { + tps.certDatabase.removeRecord(cert.getId()); + } catch (Exception e) { + auditMsg = method + ": removeRecord failed"; + CMS.debug(auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + continue; + } + + String origin = cert.getOrigin(); + if (origin != null && !origin.equals(cuid)) { + /* + * Raidzilla Bug #57803: + * If the certificate is not originally created for this + * token, we should not revoke the certificate here. + * To figure out if this certificate is originally created + * for this token, we check the tokenOrigin attribute. + */ + CMS.debug(method + ": cert " + cert.getSerialNumber() + + " originally created for this token: " + origin + + " while current token: " + cuid + + "; Remove from tokendb and skip the revoke"); + try { + tps.certDatabase.removeRecord(cert.getId()); + } catch (Exception e) { + auditMsg = method + ": removeRecord failed"; + CMS.debug(auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + continue; + } + if (origin == null) { + // no tokenOrigin, then don't care, keep going + CMS.debug(method + ": tokenOrigin is not present in tokendb cert record"); + } + + // revoke the cert + /* + * if the certificates are revoked_on_hold, don't do anything because the certificates may + * be referenced by more than one token. + */ + if (cert.getStatus().equals("revoked_on_hold")) { + CMS.debug(method + ": cert " + cert.getSerialNumber() + + " has status revoked_on_hold; remove from tokendb and move on"); + try { + tps.certDatabase.removeRecord(cert.getId()); + } catch (Exception e) { + auditMsg = method + ": removeRecord failed"; + CMS.debug(auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + continue; + } + + String hexSerial = cert.getSerialNumber(); + if (hexSerial.length() >= 3 && hexSerial.startsWith("0x")) { + String serial = hexSerial.substring(2); // skip over the '0x' + BigInteger bInt = new BigInteger(serial, 16); + String serialStr = bInt.toString(); + CMS.debug(method + ": found cert hex serial: " + serial + + " dec serial:" + serialStr); + try { + CARevokeCertResponse response = + caRH.revokeCertificate(true, serialStr, cert.getCertificate(), + revokeReason); + CMS.debug(method + ": response status =" + response.getStatus()); + } catch (EBaseException e) { + auditMsg = method + ": revokeCertificate from CA failed:" + e; + CMS.debug(auditMsg); + + if (revokeReason == RevocationReason.CERTIFICATE_HOLD) { + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(), + session.getIpAddress(), auditMsg, + "failure"); + } else { + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(), + session.getIpAddress(), auditMsg, + "failure"); + } + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + } else { + auditMsg = "mulformed hex serial number :" + hexSerial; + CMS.debug(method + ": " + auditMsg); + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(), session.getIpAddress(), + auditMsg, + "failure"); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED); + } + auditMsg = "Certificate " + hexSerial + " revoked"; + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(), session.getIpAddress(), auditMsg, + "success"); + + // delete cert from tokendb + CMS.debug(method + ": cert " + cert.getSerialNumber() + + ": remove from tokendb"); + try { + tps.certDatabase.removeRecord(cert.getId()); + } catch (Exception e) { + auditMsg = "removeRecord failed:" + e; + CMS.debug(method + ": " + auditMsg); + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_UPDATE_TOKENDB_FAILED); + } + continue; + } + CMS.debug(method + ": done for cuid:" + cuid); + } + + protected void format(boolean skipAuth) throws TPSException, IOException { + + String auditMsg = null; + String appletVersion = null; + + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + + boolean isExternalReg = false; + AppletInfo appletInfo = null; + TokenRecord tokenRecord = null; + try { + appletInfo = getAppletInfo(); + } catch (TPSException e) { + auditMsg = e.toString(); + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw e; + } + appletInfo.setAid(getCardManagerAID()); + + CMS.debug("TPSProcessor.format: token cuid: " + appletInfo.getCUIDhexStringPlain()); + boolean isTokenPresent = false; + + tokenRecord = isTokenRecordPresent(appletInfo); + + if (tokenRecord != null) { + CMS.debug("TPSProcessor.format: found token..."); + isTokenPresent = true; + } else { + CMS.debug("TPSProcessor.format: token does not exist in tokendb... create one in memory"); + tokenRecord = new TokenRecord(); + tokenRecord.setId(appletInfo.getCUIDhexStringPlain()); + } + + fillTokenRecord(tokenRecord, appletInfo); + session.setTokenRecord(tokenRecord); + + String cuid = appletInfo.getCUIDhexString(); + CMS.debug("TPSProcessor.format: CUID hex string=" + appletInfo.getCUIDhexStringPlain()); + //tokenRecord.setId(appletInfo.getCUIDhexString(true)); + String msn = appletInfo.getMSNString(); + + byte major_version = appletInfo.getMajorVersion(); + byte minor_version = appletInfo.getMinorVersion(); + byte app_major_version = appletInfo.getAppMajorVersion(); + byte app_minor_version = appletInfo.getAppMinorVersion(); + + CMS.debug("TPSProcessor.format: major_version " + major_version + " minor_version: " + minor_version + + " app_major_version: " + app_major_version + " app_minor_version: " + app_minor_version); + + String tokenType = "tokenType"; + String resolverInstName = getResolverInstanceName(); + + IAuthCredentials userCred = + new com.netscape.certsrv.authentication.AuthCredentials(); + if (isExternalReg) { + //ToDo, do some external Reg stuff along with authentication + tokenType = "externalRegAddToToken"; + } else { + CMS.debug("In TPSProcessor.format isExternalReg: OFF"); + /* + * Note: op.format.tokenProfileResolver=none indicates no resolver + * plugin used (tokenType resolved perhaps via authentication) + */ + + try { + tokenType = resolveTokenProfile(resolverInstName, cuid, msn, major_version, minor_version); + } catch (TPSException e) { + auditMsg = e.toString(); + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + CMS.debug("TPSProcessor.format: calculated tokenType: " + tokenType); + } + + // isExternalReg : user already authenticated earlier + if (!isExternalReg) { + // authenticate per profile/tokenType configuration + String configName = TPSEngine.OP_FORMAT_PREFIX + "." + tokenType + ".auth.enable"; + IConfigStore configStore = CMS.getConfigStore(); + boolean isAuthRequired; + try { + CMS.debug("TPSProcessor.format: getting config: " + configName); + isAuthRequired = configStore.getBoolean(configName, true); + } catch (EBaseException e) { + CMS.debug("TPSProcessor.format: Internal Error obtaining mandatory config values. Error: " + e); + auditMsg = "TPS error getting config values from config store." + e.toString(); + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + + if (isAuthRequired && !skipAuth) { + try { + TPSAuthenticator userAuth = + getAuthentication(TPSEngine.OP_FORMAT_PREFIX, tokenType); + userCred = requestUserId("format", cuid, userAuth, beginMsg.getExtensions()); + userid = (String) userCred.get(userAuth.getAuthCredName()); + CMS.debug("TPSProcessor.format: userCred (attempted) userid=" + userid); + // initialize userid first for logging purposes in case authentication fails + tokenRecord.setUserID(userid); + IAuthToken authToken = authenticateUser("format", userAuth, userCred); + userid = authToken.getInString("userid"); + tokenRecord.setUserID(userid); + CMS.debug("TPSProcessor.format:: auth token userid=" + userid); + // TODO: should check if userid match? + } catch (Exception e) { + // all exceptions are considered login failure + CMS.debug("TPSProcessor.format:: authentication exception thrown: " + e); + auditMsg = "authentication failed, status = STATUS_ERROR_LOGIN"; + + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw new TPSException(auditMsg, + TPSStatus.STATUS_ERROR_LOGIN); + } + } // TODO: if no auth required, should wipe out existing tokenRecord entry data later? + } + + /** + * TODO: + * isExternalReg is not handled beyond this point until decided + */ + + //Now check provided profile + + checkProfileStateOK(); + + if (isTokenPresent) { + CMS.debug("TPSProcessor.format: token exists"); + TokenStatus newState = TokenStatus.UNINITIALIZED; + // Check for transition to 0/UNINITIALIZED status. + + if (!tps.engine.isOperationTransitionAllowed(tokenRecord.getTokenStatus(), newState)) { + CMS.debug("TPSProcessor.format: token transition disallowed " + + tokenRecord.getTokenStatus() + + " to " + newState); + auditMsg = "Operation for CUID " + appletInfo.getCUIDhexStringPlain() + + " Disabled, illegal transition attempted " + tokenRecord.getTokenStatus() + + " to " + newState; + + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, + "failure"); + + throw new TPSException(auditMsg, + TPSStatus.STATUS_ERROR_DISABLED_TOKEN); + } else { + CMS.debug("TPSProcessor.format: token transition allowed " + + tokenRecord.getTokenStatus() + + " to " + newState); + } + } else { + CMS.debug("TPSProcessor.format: token does not exist"); + + checkAllowUnknownToken(TPSEngine.OP_FORMAT_PREFIX); + } + + TPSBuffer build_id = getAppletVersion(); + + if (build_id == null) { + checkAllowNoAppletToken(TPSEngine.OP_FORMAT_PREFIX); + } else { + appletVersion = Integer.toHexString(app_major_version) + "." + Integer.toHexString(app_minor_version) + "." + + build_id.toHexString(); + } + + String appletRequiredVersion = checkForAppletUpgrade(TPSEngine.OP_FORMAT_PREFIX); + + CMS.debug("TPSProcessor.format: appletVersion found: " + appletVersion + " requiredVersion: " + + appletRequiredVersion); + + SecurityLevel secLevel = SecurityLevel.SECURE_MSG_MAC_ENC; + + String tksConnId = getTKSConnectorID(); + + upgradeApplet(TPSEngine.OP_FORMAT_PREFIX, appletRequiredVersion, secLevel, + beginMsg.getExtensions(), tksConnId, + 10, 90); + CMS.debug("TPSProcessor.format: Completed applet upgrade."); + + // Add issuer info to the token + + writeIssuerInfoToToken(null); + + if (requiresStatusUpdate()) { + statusUpdate(100, "PROGRESS_DONE"); + } + + // Upgrade Symm Keys if needed + + SecureChannel channel = checkAndUpgradeSymKeys(); + channel.externalAuthenticate(); + tokenRecord.setKeyInfo(channel.getKeyInfoData().toHexStringPlain()); + + if (isTokenPresent && revokeCertsAtFormat()) { + // Revoke certificates on token, if so configured + RevocationReason reason = getRevocationReasonAtFormat(); + String caConnId = getCAConnectorID(); + + try { + revokeCertificates(tokenRecord.getId(), reason, caConnId); + } catch (TPSException te) { + // failed revocation; capture message and continue + auditMsg = te.getMessage(); + } + } + + // Update Token DB + tokenRecord.setStatus("uninitialized"); + try { + tps.tdb.tdbUpdateTokenEntry(tokenRecord); + String successMsg = "update token success"; + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), successMsg, + "success"); + } catch (Exception e) { + String failMsg = "update token failure"; + auditMsg = failMsg + ":" + e.toString(); + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), failMsg, + "failure"); + + throw new TPSException(auditMsg); + } + + auditMsg = "format operation succeeded"; + + tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, "success"); + + CMS.debug("TPSProcessor.format:: ends"); + + } + + protected void writeIssuerInfoToToken(SecureChannel origChannel) throws TPSException, IOException, + UnsupportedEncodingException { + if (checkIssuerInfoEnabled()) { + + SecurityLevel secLevel = SecurityLevel.SECURE_MSG_MAC_ENC; + + String tksConnId = getTKSConnectorID(); + + int defKeyIndex = getChannelDefKeyIndex(); + int defKeyVersion = getChannelDefKeyVersion(); + + SecureChannel channel = null; + + if (origChannel != null) { + channel = origChannel; + } else { + + channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, secLevel, tksConnId); + channel.externalAuthenticate(); + + } + + String issuer = getIssuerInfoValue(); + + // We know this better be ASCII value URL. + byte[] issuer_bytes = issuer.getBytes("US-ASCII"); + TPSBuffer issuerInfoBuff = new TPSBuffer(issuer_bytes); + + channel.setIssuerInfo(issuerInfoBuff); + + } + } + + protected String getResolverInstanceName() throws TPSException { + + CMS.debug("TPSProcessor.getResolverInstanceName: entering for operaiton : " + currentTokenOperation); + IConfigStore configStore = CMS.getConfigStore(); + String resolverInstName = null; + + String opPrefix = null; + String opDefault = null; + + if (currentTokenOperation.equals(TPSEngine.FORMAT_OP)) { + opPrefix = TPSEngine.OP_FORMAT_PREFIX; + opDefault = TPSEngine.CFG_DEF_FORMAT_PROFILE_RESOLVER; + + } else if (currentTokenOperation.equals(TPSEngine.ENROLL_OP)) { + opDefault = TPSEngine.CFG_DEF_ENROLL_PROFILE_RESOLVER; + opPrefix = TPSEngine.OP_ENROLL_PREFIX; + } else if (currentTokenOperation.equals(TPSEngine.PIN_RESET_OP)) { + + opDefault = TPSEngine.CFG_DEF_PIN_RESET_PROFILE_RESOLVER; + opPrefix = TPSEngine.OP_PIN_RESET_PREFIX; + } + + String config = opPrefix + + "." + TPSEngine.CFG_PROFILE_RESOLVER; + + CMS.debug("TPSProcessor.getResolverInstanceName: config: " + config); + try { + resolverInstName = configStore.getString(config, opDefault); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getResolverInstanceName: Internal error finding config value."); + + } + + CMS.debug("TPSProcessor.getResolverInstanceName: returning: " + resolverInstName); + + return resolverInstName; + } + + /** + * @param resolverInstName + * @param cuid + * @param msn + * @param major_version + * @param minor_version + * @return + */ + protected String resolveTokenProfile( + String resolverInstName, + String cuid, + String msn, + byte major_version, + byte minor_version) + throws TPSException { + String tokenType; + + if (!resolverInstName.equals("none") && (selectedTokenType == null)) { + + try { + TokenProfileParams pParams = new TokenProfileParams(); + CMS.debug("In TPSProcessor.resolveTokenProfile : after new TokenProfileParams"); + pParams.set(TokenProfileParams.PROFILE_PARAM_MAJOR_VERSION, + String.valueOf(major_version)); + pParams.set(TokenProfileParams.PROFILE_PARAM_MINOR_VERSION, + String.valueOf(minor_version)); + pParams.set(TokenProfileParams.PROFILE_PARAM_CUID, cuid); + pParams.set(TokenProfileParams.PROFILE_PARAM_MSN, msn); + if (beginMsg.getExtensions() != null) { + pParams.set(TokenProfileParams.PROFILE_PARAM_EXT_TOKEN_TYPE, + beginMsg.getExtensions().get("tokenType")); + pParams.set(TokenProfileParams.PROFILE_PARAM_EXT_TOKEN_ATR, + beginMsg.getExtensions().get("tokenATR")); + } + CMS.debug("In TPSProcessor.resolveTokenProfile : after setting TokenProfileParams"); + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + BaseTokenProfileResolver resolverInst = + subsystem.getProfileResolverManager().getResolverInstance(resolverInstName); + tokenType = resolverInst.getTokenType(pParams); + CMS.debug("In TPSProcessor.resolveTokenProfile : profile resolver result: " + tokenType); + setSelectedTokenType(tokenType); + } catch (EBaseException et) { + CMS.debug("In TPSProcessor.resolveTokenProfile exception:" + et); + throw new TPSException("TPSProcessor.resolveTokenProfile failed.", + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + } else { + //Already have a token type, return it + tokenType = getSelectedTokenType(); + } + + return tokenType; + } + + protected String getIssuerInfoValue() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + String info = null; + + String config = "op." + currentTokenOperation + "." + selectedTokenType + "." + TPSEngine.CFG_ISSUER_INFO_VALUE; + + CMS.debug("TPSProcessor.getIssuerInfoValue: config: " + config); + try { + info = configStore.getString(config, null); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getIssuerInfoValue: Internal error finding config value."); + + } + + if (info == null) { + throw new TPSException("TPSProcessor.getIssuerInfoValue: Can't find issuer info value in the config.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + CMS.debug("TPSProcessor.getIssuerInfoValue: returning: " + info); + + return info; + } + + void checkProfileStateOK() throws TPSException { + + IConfigStore configStore = CMS.getConfigStore(); + + String profileConfig = "config.Profiles." + selectedTokenType + ".state"; + String profileState = null; + + try { + profileState = configStore.getString(profileConfig, TPSEngine.CFG_ENABLED); + } catch (EBaseException e) { + //Default TPSException will return a "contact admin" error code. + throw new TPSException( + "TPSProcessor.checkProfileStateOK: internal error in getting profile state from config."); + } + + if (!profileState.equals(TPSEngine.CFG_ENABLED)) { + CMS.debug("TPSProcessor.checkProfileStateOK: profile specifically disabled."); + throw new TPSException("TPSProcessor.checkProfileStateOK: profile disabled!"); + } + + } + + protected boolean checkIssuerInfoEnabled() throws TPSException { + + CMS.debug("TPSProcessor.checkIssuerEnabled entering..."); + + IConfigStore configStore = CMS.getConfigStore(); + + String issuerEnabledConfig = "op." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_ISSUER_INFO_ENABLE; + + CMS.debug("TPSProcessor.checkIssuerEnabled config to check: " + issuerEnabledConfig); + + boolean issuerInfoEnabled = false; + + try { + issuerInfoEnabled = configStore.getBoolean(issuerEnabledConfig, false); + } catch (EBaseException e) { + //Default TPSException will return a "contact admin" error code. + throw new TPSException( + "TPSProcessor.checkIssuerInfo: internal error in getting value from config."); + } + + CMS.debug("TPSProcessor.checkIssuerEnabled returning: " + issuerInfoEnabled); + return issuerInfoEnabled; + + } + + //Obtain value and set class property. + protected void checkIsExternalReg() throws TPSException { + + IConfigStore configStore = CMS.getConfigStore(); + String External_Reg_Cfg = TPSEngine.CFG_EXTERNAL_REG + "." + "enable"; + + try { + //These defaults are well known, it is safe to use them. + + CMS.debug("In TPS_Processor.checkIsExternalReg."); + + this.isExternalReg = configStore.getBoolean(External_Reg_Cfg, false); + CMS.debug("In TPS_Processor.checkIsExternalReg. isExternalReg: " + isExternalReg); + } catch (EBaseException e1) { + CMS.debug("TPS_Processor.checkIsExternalReg: Internal Error obtaining mandatory config values. Error: " + + e1); + throw new TPSException("TPS error getting config values from config store."); + } + + } + + boolean checkServerSideKeyGen(String connId) throws TPSException { + + boolean result; + IConfigStore configStore = CMS.getConfigStore(); + + String profileConfig = "conn." + connId + "." + ".serverKeygen"; + + try { + result = configStore.getBoolean(profileConfig, false); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor: checkServerSideKeyGen: Internal error obtaining config value!"); + } + + return result; + } + + void checkAllowNoAppletToken(String operation) throws TPSException { + boolean allow = true; + IConfigStore configStore = CMS.getConfigStore(); + + String noAppletConfig = operation + "." + selectedTokenType + "." + TPSEngine.CFG_ALLOW_NO_APPLET; + + try { + allow = configStore.getBoolean(noAppletConfig, true); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.checkAllowNoAppletToken: Internal error getting config param."); + } + + if (!allow) { + throw new TPSException("TPSProcessor.checkAllowNoAppletToken: token without applet not permitted!", + TPSStatus.STATUS_ERROR_CONTACT_ADMIN); + } + + } + + boolean checkForAppletUpdateEnabled() throws TPSException { + boolean enabled = false; + + IConfigStore configStore = CMS.getConfigStore(); + + String appletUpdate = "op." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_UPDATE_APPLET_ENABLE; + CMS.debug("TPSProcessor.checkForAppletUpdateEnabled: getting config: " + appletUpdate); + try { + enabled = configStore.getBoolean(appletUpdate, false); + } catch (EBaseException e) { + throw new TPSException( + "TPSProcessor.checkForAppleUpdateEnabled: Can't find applet Update Enable. Internal error obtaining value.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + + } + CMS.debug("TPSProcessor.checkForAppletUpdateEnabled: returning " + enabled); + return enabled; + } + + protected String checkForAppletUpgrade(String operation) throws TPSException { + String requiredVersion = null; + IConfigStore configStore = CMS.getConfigStore(); + + String appletRequiredConfig = operation + "." + selectedTokenType + "." + + TPSEngine.CFG_APPLET_UPDATE_REQUIRED_VERSION; + CMS.debug("TPSProcessor.checkForAppletUpgrade: getting config: " + appletRequiredConfig); + try { + requiredVersion = configStore.getString(appletRequiredConfig, null); + } catch (EBaseException e) { + throw new TPSException( + "TPSProcessor.checkForAppletUpgrade: Can't find applet required Version. Internal error obtaining version.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + if (requiredVersion == null) { + throw new TPSException("TPSProcessor.checkForAppletUpgrade: Can't find applet required Version.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + CMS.debug("TPSProcessor.checkForAppletUpgrade: returning: " + requiredVersion); + + return requiredVersion; + } + + protected void checkAllowUnknownToken(String operation) throws TPSException { + boolean allow = true; + + IConfigStore configStore = CMS.getConfigStore(); + + String unknownConfig = "op." + operation + "." + TPSEngine.CFG_ALLOW_UNKNOWN_TOKEN; + + try { + allow = configStore.getBoolean(unknownConfig, true); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.checkAllowUnknownToken: Internal error getting config value."); + } + + if (allow == false) { + throw new TPSException( + "TPSProcessor.checkAllowUnknownToken: Unknown tokens not allowed for this operation!", + TPSStatus.STATUS_ERROR_TOKEN_DISABLED); + } + + } + + protected String getTKSConnectorID() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + String id = null; + + String config = "op." + currentTokenOperation + "." + selectedTokenType + ".tks.conn"; + + try { + id = configStore.getString(config, "tks1"); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getTKSConnectorID: Internal error finding config value."); + + } + + CMS.debug("TPSProcessor.getTKSConectorID: returning: " + id); + + return id; + } + + protected TPSBuffer getNetkeyAID() throws TPSException { + + String NetKeyAID = null; + IConfigStore configStore = CMS.getConfigStore(); + try { + + NetKeyAID = configStore.getString(TPSEngine.CFG_APPLET_NETKEY_INSTANCE_AID, + TPSEngine.CFG_DEF_NETKEY_INSTANCE_AID); + + } catch (EBaseException e1) { + CMS.debug("TPS_Processor.getNetkeyAID: Internal Error obtaining mandatory config values. Error: " + e1); + throw new TPSException("TPS error getting config values from config store."); + } + + TPSBuffer ret = new TPSBuffer(NetKeyAID); + + return ret; + } + + protected TPSBuffer getNetkeyPAID() throws TPSException { + + String NetKeyPAID = null; + IConfigStore configStore = CMS.getConfigStore(); + try { + + NetKeyPAID = configStore.getString( + TPSEngine.CFG_APPLET_NETKEY_FILE_AID, TPSEngine.CFG_DEF_NETKEY_FILE_AID); + + } catch (EBaseException e1) { + CMS.debug("TPS_Processor.getNetkeyAID: Internal Error obtaining mandatory config values. Error: " + e1); + throw new TPSException("TPS error getting config values from config store."); + } + + TPSBuffer ret = new TPSBuffer(NetKeyPAID); + + return ret; + } + + protected TPSBuffer getCardManagerAID() throws TPSException { + + String cardMgrAID = null; + IConfigStore configStore = CMS.getConfigStore(); + try { + + cardMgrAID = configStore.getString(TPSEngine.CFG_DEF_CARDMGR_INSTANCE_AID, + TPSEngine.CFG_DEF_CARDMGR_INSTANCE_AID); + + } catch (EBaseException e1) { + CMS.debug("TPS_Processor.getNetkeyAID: Internal Error obtaining mandatory config values. Error: " + e1); + throw new TPSException("TPS error getting config values from config store."); + } + + TPSBuffer ret = new TPSBuffer(cardMgrAID); + + return ret; + } + + protected String getAppletExtension() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + String extension = null; + String extensionConfig = TPSEngine.CFG_APPLET_EXTENSION; + + try { + extension = configStore.getString(extensionConfig, "ijc"); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getAppletExtension: Internal error finding config value."); + + } + + CMS.debug("TPSProcessor.getAppletExtension: returning: " + extension); + + return extension; + } + + protected String getAppletDirectory(String operation) throws TPSException { + + IConfigStore configStore = CMS.getConfigStore(); + String directory = null; + + String directoryConfig = operation + "." + selectedTokenType + "." + TPSEngine.CFG_APPLET_DIRECTORY; + + //We need a directory + try { + directory = configStore.getString(directoryConfig); + } catch (EPropertyNotFound e) { + throw new TPSException("TPSProcessor.getAppletDirectory: Required config param missing.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getAppletDirectory: Internal error finding config value.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + + CMS.debug("getAppletDirectory: returning: " + directory); + return directory; + } + + protected int getChannelBlockSize() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + int blockSize = 0; + try { + blockSize = configStore.getInteger(TPSEngine.CFG_CHANNEL_BLOCK_SIZE, TPSEngine.CFG_CHANNEL_DEF_BLOCK_SIZE); + + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getChannelBlockSize: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + + } + + CMS.debug("TPSProcess.getChannelBlockSize: returning: " + blockSize); + return blockSize; + + } + + protected int getChannelInstanceSize() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + int instanceSize = 0; + try { + instanceSize = configStore.getInteger(TPSEngine.CFG_CHANNEL_INSTANCE_SIZE, + TPSEngine.CFG_CHANNEL_DEF_INSTANCE_SIZE); + + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getChannelInstanceSize: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + + } + + CMS.debug("TPSProcess.getChannelInstanceSize: returning: " + instanceSize); + + return instanceSize; + + } + + protected int getAppletMemorySize() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + int memSize = 0; + try { + memSize = configStore.getInteger(TPSEngine.CFG_CHANNEL_APPLET_MEMORY_SIZE, + TPSEngine.CFG_CHANNEL_DEF_APPLET_MEMORY_SIZE); + + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getAppletMemorySize: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + + } + CMS.debug("TPSProcess.getAppletMemorySize: returning: " + memSize); + + return memSize; + } + + protected int getChannelDefKeyVersion() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + int ver = 0; + try { + ver = configStore.getInteger(TPSEngine.CFG_CHANNEL_DEFKEY_VERSION, 0x0); + + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getChannelDefKeyVersion: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + + } + + CMS.debug("TPSProcessor.getChannelDefKeyVersion: " + ver); + + return ver; + + } + + protected int getChannelDefKeyIndex() throws TPSException { + IConfigStore configStore = CMS.getConfigStore(); + int index = 0; + try { + index = configStore.getInteger(TPSEngine.CFG_CHANNEL_DEFKEY_INDEX, 0x0); + + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getChannelDefKeyVersion: Internal error finding config value: " + e, + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + + } + + CMS.debug("TPSProcessor.getChannelDefKeyIndex: " + index); + + return index; + + } + + protected PK11SymKey getSharedSecretTransportKey(String connId) throws TPSException, NotInitializedException { + + IConfigStore configStore = CMS.getConfigStore(); + String sharedSecretName = null; + try { + String configName = "conn." + connId + ".tksSharedSymKeyName"; + sharedSecretName = configStore.getString(configName, "sharedSecret"); + + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getSharedSecretTransportKey: Internal error finding config value: " + + e, + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + + } + + CMS.debug("TPSProcessor.getSharedSecretTransportKey: calculated key name: " + sharedSecretName); + + String symmKeys = null; + boolean keyPresent = false; + try { + symmKeys = SessionKey.ListSymmetricKeys("internal"); + CMS.debug("TPSProcessor.getSharedSecretTransportKey: symmKeys List: " + symmKeys); + } catch (Exception e) { + // TODO Auto-generated catch block + CMS.debug(e); + } + + for (String keyName : symmKeys.split(",")) { + if (sharedSecretName.equals(keyName)) { + CMS.debug("TPSProcessor.getSharedSecret: shared secret key found!"); + keyPresent = true; + break; + } + + } + + if (!keyPresent) { + throw new TPSException("TPSProcessor.getSharedSecret: Can't find shared secret!", + TPSStatus.STATUS_ERROR_SECURE_CHANNEL); + } + + // We know for now that shared secret is on this token + String tokenName = "Internal Key Storage Token"; + PK11SymKey sharedSecret = SessionKey.GetSymKeyByName(tokenName, sharedSecretName); + + CMS.debug("TPSProcessor.getSharedSecret: SymKey returns: " + sharedSecret); + + return sharedSecret; + + } + + public boolean getIsExternalReg() { + return isExternalReg; + } + + public void process(BeginOpMsg beginMsg) throws TPSException, IOException { + + if (beginMsg == null) { + throw new TPSException("TPSProcessor.process: invalid input data, not beginMsg provided.", + TPSStatus.STATUS_ERROR_UPGRADE_APPLET); + } + setBeginMessage(beginMsg); + setCurrentTokenOperation("format"); + checkIsExternalReg(); + + format(false); + } + + public void statusUpdate(int status, String info) throws IOException { + + if (!requiresStatusUpdate()) + return; + + CMS.debug("In TPSProcessor.statusUpdate status: " + status + " info: " + info); + + StatusUpdateRequestMsg statusUpdate = new StatusUpdateRequestMsg(status, info); + session.write(statusUpdate); + + //We don't really care about the response, just that we get it. + + session.read(); + + } + + public TPSEngine getTPSEngine() { + TPSSubsystem subsystem = + (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + + return subsystem.getEngine(); + + } + + // Do the incoming extensions support status update? + public boolean requiresStatusUpdate() { + + boolean result = false; + + // We can't get here without a begin message established. + String update = getBeginMessage().getExtension(BeginOpMsg.STATUS_UPDATE_EXTENSION_NAME); + + if (update != null && update.equals("true")) { + result = true; + } + + return result; + + } + + protected AppletInfo getAppletInfo() throws TPSException, IOException { + AppletInfo result = null; + + CMS.debug("TPSProcessor.getAppletInfo, entering ..."); + + selectCardManager(); + + TPSBuffer cplc_data = getCplcData(); + CMS.debug("cplc_data: " + cplc_data.toString()); + + TPSBuffer token_cuid = extractTokenCUID(cplc_data); + TPSBuffer token_msn = extractTokenMSN(cplc_data); + + /** + * Checks if the netkey has the required applet version. + */ + + selectCoolKeyApplet(); + + TPSBuffer token_status = getStatus(); + + byte major_version = 0x0; + byte minor_version = 0x0; + byte app_major_version = 0x0; + byte app_minor_version = 0x0; + + CMS.debug("TPS_Processor.getAppletInfo: status: " + token_status.toHexString()); + if (token_status.size() >= 4) { + major_version = token_status.at(0); + minor_version = token_status.at(1); + app_major_version = token_status.at(2); + app_minor_version = token_status.at(3); + } + + int free_mem = 0; + int total_mem = 0; + + if (token_status.size() >= 12) { + byte tot_high = token_status.at(6); + byte tot_low = token_status.at(7); + + byte free_high = token_status.at(10); + byte free_low = token_status.at(11); + + total_mem = (tot_high << 8) + tot_low; + free_mem = (free_high << 8) + free_low; + + } + + result = new AppletInfo(major_version, minor_version, app_major_version, app_minor_version); + result.setCUID(token_cuid); + result.setMSN(token_msn); + result.setTotalMem(total_mem); + result.setFreeMem(free_mem); + + CMS.debug("TPSProcessor.getAppletInfo: cuid: " + result.getCUIDhexString() + " msn: " + result.getMSNString() + + " major version: " + result.getMinorVersion() + " minor version: " + result.getMinorVersion() + + " App major version: " + result.getAppMajorVersion() + " App minor version: " + + result.getAppMinorVersion()); + + return result; + } + + protected void selectCardManager() throws TPSException, IOException { + CMS.debug("TPSProcessor.selectCardManager: entering.."); + TPSBuffer aidBuf = getCardManagerAID(); + + APDUResponse select = selectApplet((byte) 0x04, (byte) 0x00, aidBuf); + + if (!select.checkResult()) { + throw new TPSException("TPSProcessor.selectCardManager: Can't selelect the card manager applet!"); + } + } + + protected boolean checkSymmetricKeysEnabled() throws TPSException { + boolean result = true; + + IConfigStore configStore = CMS.getConfigStore(); + + String symmConfig = "op" + "." + currentTokenOperation + "." + selectedTokenType + "." + + TPSEngine.CFG_SYMM_KEY_UPGRADE_ENABLED; + + try { + result = configStore.getBoolean(symmConfig, true); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.checkSymmetricKeysEnabled: Internal error getting config value."); + } + + return result; + } + + protected int getSymmetricKeysRequiredVersion() throws TPSException { + int version = 0; + ; + + IConfigStore configStore = CMS.getConfigStore(); + + String requiredVersionConfig = "op" + "." + currentTokenOperation + "." + selectedTokenType + "." + + "update.symmetricKeys.requiredVersion"; + + CMS.debug("TPSProcessor.getSymmetricKeysRequiredVersion: configValue: " + requiredVersionConfig); + try { + version = configStore.getInteger(requiredVersionConfig, 0x0); + } catch (EBaseException e) { + throw new TPSException("TPSProcessor.getSymmetricKeysRequired: Internal error getting config value."); + } + + CMS.debug("TPSProcessor.getSymmetricKeysRequiredVersion: returning version: " + version); + + return version; + } + + protected SecureChannel checkAndUpgradeSymKeys() throws TPSException, IOException { + + /* If the key of the required version is + not found, create them. + + This sends a InitializeUpdate request to the token. + We tell the token to use whatever it thinks is the + default key version (0). It will return the version + of the key it actually used later. (This is accessed + with GetKeyInfoData below) + [ Note: This is not explained very well in the manual + The token can have multiple sets of symmetric keys + Each set is given a version number, which I think is + better thought of as a SLOT. One key slot is populated + with a set of keys when the token is manufactured. + This is then designated as the default key set version. + Later, we will write a new key set with PutKey, and + set it to be the new default] + */ + + SecureChannel channel = null; + + int defKeyVersion = 0; + int defKeyIndex = getChannelDefKeyIndex(); + + if (checkSymmetricKeysEnabled()) { + + CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: Symm key upgrade enabled."); + int requiredVersion = getSymmetricKeysRequiredVersion(); + + // try to make a secure channel with the 'requiredVersion' keys + // If this fails, we know we will have to attempt an upgrade + // of the keys + + boolean failed = false; + try { + + channel = setupSecureChannel((byte) requiredVersion, (byte) defKeyIndex, + SecurityLevel.SECURE_MSG_MAC_ENC, + getTKSConnectorID()); + + } catch (TPSException e) { + + CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: failed to create secure channel with required version, we need to upgrade the keys."); + failed = true; + } + + //If we failed we need to upgrade the keys + if (failed == true) { + + selectCardManager(); + + channel = setupSecureChannel(); + + /* Assemble the Buffer with the version information + The second byte is the key offset, which is always 1 + */ + + byte[] nv = { (byte) requiredVersion, 0x01 }; + TPSBuffer newVersion = new TPSBuffer(nv); + + // GetKeyInfoData will return a buffer which is bytes 11,12 of + // the data structure on page 89 of Cyberflex Access Programmer's + // Guide + // Byte 0 is the key set version. + // Byte 1 is the index into that key set + + String connId = getTKSConnectorID(); + TPSBuffer curKeyInfo = channel.getKeyInfoData(); + TPSEngine engine = getTPSEngine(); + + TPSBuffer keySetData = engine.createKeySetData(newVersion, curKeyInfo, + channel.getKeyDiversificationData(), connId); + + CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: new keySetData from TKS: " + keySetData.toHexString()); + + byte curVersion = curKeyInfo.at(0); + byte curIndex = curKeyInfo.at(1); + + channel.putKeys(curVersion, curIndex, keySetData); + + String curVersionStr = curKeyInfo.toHexString(); + String newVersionStr = newVersion.toHexString(); + TPSSession session = getSession(); + TokenRecord tokenRecord = session.getTokenRecord(); + tokenRecord.setKeyInfo(newVersion.toHexStringPlain()); + + CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: curVersionStr: " + curVersionStr + " newVersionStr: " + + newVersionStr); + + selectCoolKeyApplet(); + + channel = setupSecureChannel((byte) requiredVersion, (byte) defKeyIndex, + SecurityLevel.SECURE_MSG_MAC_ENC, + getTKSConnectorID()); + + } else { + CMS.debug("TPSProcessor.checkAndUpgradeSymeKeys: We are already at the desired key set, returning secure channel."); + } + + } else { + //Create a standard secure channel with current key set. + CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: Key changeover disabled in the configuration."); + + defKeyVersion = getChannelDefKeyVersion(); + + channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, SecurityLevel.SECURE_MSG_MAC_ENC, + getTKSConnectorID()); + + } + + CMS.debug("TPSProcessor.checkAndUpdradeSymKeys: Leaving successfully...."); + return channel; + } + + //List objects that may be on a given token + //Return null if object void of objects + + protected TPSBuffer listObjects(byte seq) throws TPSException, IOException { + TPSBuffer objects = null; + + ListObjectsAPDU listObjects = new ListObjectsAPDU(seq); + + APDUResponse respApdu = handleAPDURequest(listObjects); + + if (!respApdu.checkResult()) { + CMS.debug("TPSProcessor.listObjects: Bad response from ListObjects! Token possibly has no objects"); + return null; + } + + objects = respApdu.getData(); + + return objects; + + } + + // Request new pin from client + protected String requestNewPin(int minLen, int maxLen) throws IOException, TPSException { + + CMS.debug("TPSProcessor.requestNewPin: entering..."); + + String newPin = null; + + NewPinRequestMsg new_pin_req = new NewPinRequestMsg(minLen, maxLen); + + session.write(new_pin_req); + + NewPinResponseMsg new_pin_resp = (NewPinResponseMsg) session.read(); + + newPin = new_pin_resp.get(NewPinResponseMsg.NEW_PIN_NAME); + + if (newPin.length() < minLen || newPin.length() > maxLen) { + throw new TPSException("TPSProcessor.requestNewPin: new pin length outside of length contraints: min: " + + minLen + " max: " + maxLen); + } + + return newPin; + } + + protected String mapPattern(LinkedHashMap<String, String> map, String pattern) throws TPSException { + + //Right now only support one pattern to match within pattern: for instance: + // "encryption key for $userid$ , not only the one "$userid$" pattern. + + String result = null; + + if (pattern == null || map == null) { + throw new TPSException("TPSProcessor.mapPattern: Illegal input paramters!", + TPSStatus.STATUS_ERROR_CONTACT_ADMIN); + } + + final char delim = '$'; + int firstPos = 0; + int nextPos = 0; + String patternToMap = null; + String patternMapped = null; + + firstPos = pattern.indexOf(delim); + nextPos = pattern.indexOf(delim, firstPos + 1); + + if ((nextPos - firstPos) <= 1) { + return pattern; + } + + patternToMap = pattern.substring(firstPos + 1, nextPos); + + CMS.debug("TPSProcessor.mapPattern: patternTo map: " + patternToMap); + + String piece1 = ""; + if (firstPos >= 1) + piece1 = pattern.substring(0, firstPos); + + String piece2 = ""; + if (nextPos < (pattern.length() - 1)) + piece2 = pattern.substring(nextPos + 1); + + for (Map.Entry<String, String> entry : map.entrySet()) { + String key = entry.getKey(); + + String value = entry.getValue(); + CMS.debug("TPSProcessor.mapPattern: Exposed: key: " + key + " Param: " + value); + + if (key.equals(patternToMap)) { + CMS.debug("TPSProcessor.mapPattern: found match: key: " + key + " mapped to: " + value); + patternMapped = value; + break; + } + + } + + result = piece1 + patternMapped + piece2; + + CMS.debug("TPSProcessor.mapPattern: returning: " + result); + return result; + + } + + protected String formatCurrentAppletVersion(AppletInfo aInfo) throws TPSException, IOException { + + if (aInfo == null) { + throw new TPSException("TPSProcessor.formatCurrentAppletVersion: ", TPSStatus.STATUS_ERROR_CONTACT_ADMIN); + } + + TPSBuffer build_id = getAppletVersion(); + String build_idStr = build_id.toHexStringPlain(); + + String finalVersion = aInfo.getAppMajorVersion() + "." + aInfo.getAppMinorVersion() + "." + build_idStr; + + finalVersion = finalVersion.toLowerCase(); + CMS.debug("TPSProcessor.formatCurrentAppletVersion: returing: " + finalVersion); + + return finalVersion; + + } + + public static void main(String[] args) { + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/profile/BaseTokenProfileResolver.java b/base/tps/src/org/dogtagpki/server/tps/profile/BaseTokenProfileResolver.java new file mode 100644 index 000000000..c62d1ed2b --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/profile/BaseTokenProfileResolver.java @@ -0,0 +1,38 @@ +package org.dogtagpki.server.tps.profile; + +import org.dogtagpki.tps.main.TPSException; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.IConfigStore; + +/** + * This class implements the base TPS Profile Resolver instance + * + * @author cfu + */ +public abstract class BaseTokenProfileResolver { + protected IConfigStore configStore = null; + protected String instanceName = ""; + protected String prefix = ""; + + public BaseTokenProfileResolver() { + } + + public void init(String instName) { + instanceName = instName; + prefix = TokenProfileResolverManager.TOKEN_PROFILE_RESOLVER_CFG + + "." + instanceName; + configStore = CMS.getConfigStore(); + } + + public String getName() { + return instanceName; + } + + public String getPrefix() { + return prefix; + } + + public abstract String getTokenType(TokenProfileParams pPram) + throws TPSException; +} diff --git a/base/tps/src/org/dogtagpki/server/tps/profile/MappingTokenProfileResolver.java b/base/tps/src/org/dogtagpki/server/tps/profile/MappingTokenProfileResolver.java new file mode 100644 index 000000000..264fba882 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/profile/MappingTokenProfileResolver.java @@ -0,0 +1,294 @@ +package org.dogtagpki.server.tps.profile; + +import org.dogtagpki.server.tps.engine.TPSEngine; +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.EPropertyNotFound; + +/** + * MappingTokenProfileResolver is a profile resolver plugin that calculates + * token type by sorting through a list of filters in mapping + */ +public class MappingTokenProfileResolver extends BaseTokenProfileResolver { + + public MappingTokenProfileResolver() { + } + + public String getTokenType(TokenProfileParams pParam) + throws TPSException { + + String tokenType = null; + String mappingOrder = null; + int major_version = 0; + int minor_version = 0; + String cuid = null; + // String msn = null; + String eTokenType = null; + String eTokenATR = null; + + CMS.debug("MappingTokenProfileResolver.getTokenType: starts"); + + major_version = pParam.getInt(TokenProfileParams.PROFILE_PARAM_MAJOR_VERSION); + CMS.debug("MappingTokenProfileResolver: param major_version =" + major_version); + + minor_version = pParam.getInt(TokenProfileParams.PROFILE_PARAM_MINOR_VERSION); + CMS.debug("MappingTokenProfileResolver: param minor_version =" + minor_version); + + cuid = pParam.getString(TokenProfileParams.PROFILE_PARAM_CUID); + // msn = (String) pParam.get(TokenProfileParams.PROFILE_PARAM_MSN); + // they don't necessarily have extension + try { + eTokenType = pParam.getString(TokenProfileParams.PROFILE_PARAM_EXT_TOKEN_TYPE); + eTokenATR = pParam.getString(TokenProfileParams.PROFILE_PARAM_EXT_TOKEN_ATR); + } catch (TPSException e) { + CMS.debug("MappingTokenProfileResolver: OK to not have extension. Continue."); + } + + CMS.debug("MappingTokenProfileResolver: params retrieved."); + + String configName = prefix + "." + TPSEngine.CFG_PROFILE_MAPPING_ORDER; + + try { + CMS.debug("MappingTokenProfileResolver: getting mapping order:" + + configName); + mappingOrder = configStore.getString(configName); + } catch (EPropertyNotFound e) { + CMS.debug("MappingTokenProfileResolver: exception:" + e); + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Token Type configuration incorrect! Mising mapping order!", + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + + } catch (EBaseException e1) { + //The whole feature won't work if this is wrong. + CMS.debug("MappingTokenProfileResolver: exception:" + e1); + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value.!", + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + String targetTokenType = null; + + for (String mappingId : mappingOrder.split(",")) { + + CMS.debug("MappingTokenProfileResolver.getTokenType: mapping: " + mappingId); + + String mappingConfigName = prefix + ".mapping." + mappingId + ".target.tokenType"; + + CMS.debug("MappingTokenProfileResolver.getTokenType: mappingConfigName: " + mappingConfigName); + + //We need this to exist. + try { + targetTokenType = configStore.getString(mappingConfigName); + } catch (EPropertyNotFound e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Token Type configuration incorrect! No target token type config value found! Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + + } catch (EBaseException e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value. Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + mappingConfigName = prefix + ".mapping." + mappingId + ".filter.tokenType"; + + CMS.debug("MappingTokenProfileResolver.getTokenType: mappingConfigName: " + mappingConfigName); + + //For this and remaining cases, it is not automatically an error if we don't get anything back + // from the config. + try { + tokenType = configStore.getString(mappingConfigName, null); + } catch (EBaseException e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value. Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + + } + + CMS.debug("MappingTokenProfileResolver.getTokenType: targetTokenType: " + targetTokenType); + + if (tokenType != null && tokenType.length() > 0) { + + if (eTokenType == null) { + continue; + } + + //String eTokenType = extensions.get("tokenType"); + //if (eTokenType == null) { + // continue; + //} + + if (!eTokenType.equals(tokenType)) { + continue; + } + } + + mappingConfigName = prefix + ".mapping." + mappingId + ".filter.tokenATR"; + + CMS.debug("MappingTokenProfileResolver.getTokenType: mappingConfigName: " + mappingConfigName); + + String tokenATR = null; + + try { + tokenATR = configStore.getString(mappingConfigName, null); + } catch (EBaseException e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value. Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + CMS.debug("MappingTokenProfileResolver.getTokenType: tokenATR: " + tokenATR); + + if (tokenATR != null && tokenATR.length() > 0) { + if (eTokenATR == null) { + continue; + } + + //String eTokenATR = extensions.get("tokenATR"); + + //if (eTokenATR == null) { + // continue; + //} + + if (!eTokenATR.equals(tokenATR)) { + continue; + } + + } + + mappingConfigName = prefix + ".mapping." + mappingId + ".filter.tokenCUID.start"; + + CMS.debug("MappingTokenProfileResolver.getTokenType: mappingConfigName: " + mappingConfigName); + + String tokenCUIDStart = null; + + try { + tokenCUIDStart = configStore.getString(mappingConfigName, null); + + } catch (EBaseException e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value. Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + CMS.debug("MappingTokenProfileResolver.getTokenType: tokenCUIDStart: " + tokenCUIDStart); + + if (tokenCUIDStart != null && tokenCUIDStart.length() > 0) { + if (cuid == null) { + continue; + } + + if (tokenCUIDStart.length() != 20) { + continue; + } + + if (cuid.compareTo(tokenCUIDStart) < 0) { + continue; + } + + } + + mappingConfigName = prefix + ".mapping." + mappingId + ".filter.tokenCUID.end"; + + CMS.debug("MappingTokenProfileResolver.getTokenType: mappingConfigName: " + mappingConfigName); + + String tokenCUIDEnd = null; + try { + tokenCUIDEnd = configStore.getString(mappingConfigName, null); + } catch (EBaseException e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value. Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + CMS.debug("MappingTokenProfileResolver.getTokenType: tokenCUIDEnd: " + tokenCUIDEnd); + + if (tokenCUIDEnd != null && tokenCUIDEnd.length() > 0) { + if (cuid == null) { + continue; + } + + if (tokenCUIDEnd.length() != 20) { + continue; + } + + if (cuid.compareTo(tokenCUIDEnd) > 0) { + continue; + } + + } + + mappingConfigName = prefix + ".mapping." + mappingId + ".filter.appletMajorVersion"; + + CMS.debug("MappingTokenProfileResolver.getTokenType: mappingConfigName: " + mappingConfigName); + + String majorVersion = null; + String minorVersion = null; + + try { + majorVersion = configStore.getString(mappingConfigName, null); + } catch (EBaseException e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value. Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + CMS.debug("MappingTokenProfileResolver.getTokenType: majorVersion: " + majorVersion); + if (majorVersion != null && majorVersion.length() > 0) { + + int major = Integer.parseInt(majorVersion); + + if (major != major_version) { + continue; + } + } + + mappingConfigName = prefix + ".mapping." + mappingId + ".filter.appletMinorVersion"; + + CMS.debug("MappingTokenProfileResolver.getTokenType: mappingConfigName: " + mappingConfigName); + + try { + minorVersion = configStore.getString(mappingConfigName, null); + } catch (EBaseException e) { + throw new TPSException( + "MappingTokenProfileResolver.getTokenType: Internal error obtaining config value. Config: " + + mappingConfigName, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + CMS.debug("MappingTokenProfileResolver.getTokenType: minorVersion " + minorVersion); + + if (minorVersion != null && minorVersion.length() > 0) { + + int minor = Integer.parseInt(minorVersion); + + if (minor != minor_version) { + continue; + } + } + + //if we make it this far, we have a token type + CMS.debug("MappingTokenProfileResolver.getTokenType: Selected Token type: " + targetTokenType); + break; + } + + if (targetTokenType == null) { + CMS.debug("MappingTokenProfileResolver.getTokenType: end found: " + targetTokenType); + throw new TPSException("MappingTokenProfileResolver.getTokenType: Can't find token type!", + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_NOT_FOUND); + } + + return targetTokenType; + + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/profile/TokenProfileParams.java b/base/tps/src/org/dogtagpki/server/tps/profile/TokenProfileParams.java new file mode 100644 index 000000000..d04bc9fb8 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/profile/TokenProfileParams.java @@ -0,0 +1,145 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.profile; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.dogtagpki.tps.main.TPSException; +import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus; + +import com.netscape.certsrv.apps.CMS; + +/** + * A class represents profile params information. + * <P> + * + * @version $Revision$, $Date$ + */ +public class TokenProfileParams { + + public static final String PROFILE_PARAM_MAJOR_VERSION = "pp_major_version"; + public static final String PROFILE_PARAM_MINOR_VERSION = "pp_minor_version"; + public static final String PROFILE_PARAM_CUID = "pp_cuid"; + public static final String PROFILE_PARAM_MSN = "pp_msn"; + public static final String PROFILE_PARAM_EXT_TOKEN_TYPE = "pp_ext_tokenType"; + public static final String PROFILE_PARAM_EXT_TOKEN_ATR = "pp_ext_tokenATR"; + + private HashMap<String, String> content = new HashMap<String, String>(); + + /** + * Constructs a meta information. + * <P> + */ + public TokenProfileParams() { + } + + /** + * Returns a short string describing this certificate attribute. + * <P> + * + * @return information about this certificate attribute. + */ + public String toString() { + StringBuffer sb = new StringBuffer("[\n" + " Meta information:\n"); + + for (Map.Entry<String, String> entry : content.entrySet()) { + String key = entry.getKey(); + + sb.append(" " + key + " : " + entry.getValue() + "\n"); + } + sb.append("]\n"); + return sb.toString(); + } + + /** + * Gets a String attribute value. + * <P> + * + * @param name the name of the attribute to return. + */ + public String getString(String name) + throws TPSException { + String val = content.get(name); + if (val == null) { + CMS.debug("TokenProfileParams.getString: param null:"+ name); + throw new TPSException ( + "TokenProfileParams.getString: param null:"+ name, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_PARAMS_NOT_FOUND); + } + return val; + } + + /** + * Gets an int attribute value. + * <P> + * + * @param name the name of the attribute to return. + */ + public int getInt(String name) + throws TPSException { + String val = content.get(name); + if (val == null) { + CMS.debug("TokenProfileParams.getInt: param null:"+ name); + throw new TPSException ( + "TokenProfileParams.getInt: param null:"+ name, + TPSStatus.STATUS_ERROR_DEFAULT_TOKENTYPE_PARAMS_NOT_FOUND); + } + try { + int intVal = Integer.parseInt(val); + return intVal; + } catch (NumberFormatException e) { + CMS.debug("TokenProfileParams.getInt: param "+ name + "=" + val + e); + throw new TPSException ( + "TokenProfileParams.getInt: param major_version:"+ e, + TPSStatus.STATUS_ERROR_MISCONFIGURATION); + } + } + + /** + * Sets an attribute value. + * + * @param name the name of the attribute + * @param val the attribute value. + */ + public void set(String name, String val) { + content.put(name, val); + } + + /** + * Deletes an attribute value from this AttrSet. + * <P> + * + * @param name the name of the attribute to delete. + */ + public void delete(String name) { + content.remove(name); + } + + /** + * Returns an enumeration of the names of the attributes existing within + * this attribute. + * <P> + * + * @return an enumeration of the attribute names. + */ + public Set<String> getElements() { + return content.keySet(); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/profile/TokenProfileResolverManager.java b/base/tps/src/org/dogtagpki/server/tps/profile/TokenProfileResolverManager.java new file mode 100644 index 000000000..b6325fcca --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/profile/TokenProfileResolverManager.java @@ -0,0 +1,114 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.profile; + +import java.util.HashMap; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.registry.IPluginInfo; +import com.netscape.certsrv.registry.IPluginRegistry; + +/** + * TokenProfileResolverManager is a class for profile resolver plugin + * management + * + * @author cfu + */ +public class TokenProfileResolverManager +{ + private static final String TOKEN_PROFILE_RESOLVER_TYPE = "tpsTokenProfileResolver"; + public static final String PROP_RESOLVER_LIST = "list"; + public static final String PROP_RESOLVER_CLASS_ID = "class_id"; + + protected static final String TOKEN_PROFILE_RESOLVER_CFG = "tokenProfileResolver"; + protected IPluginRegistry registry = null; + protected HashMap<String, BaseTokenProfileResolver> tokenProfileResolvers = null; + + public TokenProfileResolverManager() { + tokenProfileResolvers = new HashMap<String, BaseTokenProfileResolver>(); + } + + /** + * initializes all profile resolver plugin instances specified in + * <instance-name>/conf/registry.cfg + * + * configuration e.g. + * + * registry.cfg: + * types=tpsTokenProfileResolver + * tpsProfileResolver.ids=mappingTokenProfileResolverImpl + * tpsProfileResolver.mappingTokenProfileResolverImpl.class=org.dogtagpki.server.tps.profile.MappingTokenProfileResolver + * tpsProfileResolver.mappingTokenProfileResolverImpl.desc=Mapping-based Token profile resolver + * tpsProfileResolver.mappingTokenProfileResolverImpl.name=Mapping-based Token profile resolver + * + * CS.cfg : + * registry.file=/var/lib/pki/pki-tomcat/conf/tps/registry.cfg + * tokenProfileResolver.list=formatMappingResolver,enrollMappingResolver,pinResetMappingResolver + * tokenProfileResolver.formatMappingResolver.class_id=mappingProfileResolverImpl + * tokenProfileResolver.formatMappingResolver.[plugin-specific configuration] + * + * op.format.tokenProfileResolver=formatMappingResolver + * ... + * op.enroll.tokenProfileResolver=enrollMappingResolver + * + * Note: "none" indicates no resolver plugin applied + * op.format.tokenProfileResolver=none + */ + public void initProfileResolverInstances() + throws EBaseException { + + CMS.debug("TokenProfileResolverManager: initProfileResolverInstances(): begins"); + IConfigStore conf = CMS.getConfigStore(); + registry = (IPluginRegistry) CMS.getSubsystem(CMS.SUBSYSTEM_REGISTRY); + if (registry == null) { + CMS.debug("TokenProfileResolverManager: initProfileResolverInstances(): registry null"); + return; + } + + IConfigStore prConf = conf.getSubStore(TOKEN_PROFILE_RESOLVER_CFG); + String profileList = prConf.getString(PROP_RESOLVER_LIST, ""); + + for (String prInst : profileList.split(",")) { + String classID = prConf.getString(prInst + "." + PROP_RESOLVER_CLASS_ID); + CMS.debug("TokenProfileResolverManager: initProfileResolverInstances(): initializing classID=" + classID); + IPluginInfo resolverInfo = + registry.getPluginInfo(TOKEN_PROFILE_RESOLVER_TYPE, classID); + String resolverClass = resolverInfo.getClassName(); + BaseTokenProfileResolver resolver = null; + try { + resolver = (BaseTokenProfileResolver) + Class.forName(resolverClass).newInstance(); + } catch (Exception e) { + // throw Exception + CMS.debug("TokenProfileResolverManager: resolver plugin Class.forName " + + resolverClass + " " + e.toString()); + throw new EBaseException(e.toString()); + } + resolver.init(prInst); + tokenProfileResolvers.put(prInst, resolver); + CMS.debug("TokenProfileResolverManager: initProfileResolverInstances(): resolver instance added: " + prInst); + } + } + + public BaseTokenProfileResolver getResolverInstance(String name) { + return tokenProfileResolvers.get(name); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/ActivityService.java b/base/tps/src/org/dogtagpki/server/tps/rest/ActivityService.java new file mode 100644 index 000000000..6002e7a94 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/ActivityService.java @@ -0,0 +1,177 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.Iterator; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.dbs.ActivityDatabase; +import org.dogtagpki.server.tps.dbs.ActivityRecord; +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.logging.ActivityCollection; +import com.netscape.certsrv.logging.ActivityData; +import com.netscape.certsrv.logging.ActivityResource; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class ActivityService extends PKIService implements ActivityResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public ActivityService() { + CMS.debug("ActivityService.<init>()"); + } + + public ActivityData createActivityData(ActivityRecord activityRecord) { + + ActivityData activityData = new ActivityData(); + activityData.setID(activityRecord.getId()); + activityData.setTokenID(activityRecord.getTokenID()); + activityData.setUserID(activityRecord.getUserID()); + activityData.setIP(activityRecord.getIP()); + activityData.setOperation(activityRecord.getOperation()); + activityData.setResult(activityRecord.getResult()); + activityData.setMessage(activityRecord.getMessage()); + activityData.setDate(activityRecord.getDate()); + + String activityID = activityRecord.getId(); + try { + activityID = URLEncoder.encode(activityID, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + + URI uri = uriInfo.getBaseUriBuilder().path(ActivityResource.class).path("{activityID}").build(activityID); + activityData.setLink(new Link("self", uri)); + + return activityData; + } + + public ActivityRecord createActivityRecord(ActivityData activityData) { + + ActivityRecord activityRecord = new ActivityRecord(); + activityRecord.setId(activityData.getID()); + activityRecord.setTokenID(activityData.getTokenID()); + activityRecord.setUserID(activityData.getUserID()); + activityRecord.setIP(activityData.getIP()); + activityRecord.setOperation(activityData.getOperation()); + activityRecord.setResult(activityData.getResult()); + activityRecord.setMessage(activityData.getMessage()); + activityRecord.setDate(activityData.getDate()); + + return activityRecord; + } + + @Override + public Response findActivities(String filter, Integer start, Integer size) { + + CMS.debug("ActivityService.findActivities()"); + + if (filter != null && filter.length() < MIN_FILTER_LENGTH) { + throw new BadRequestException("Filter is too short."); + } + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ActivityDatabase database = subsystem.getActivityDatabase(); + + Iterator<ActivityRecord> activities = database.findRecords(filter).iterator(); + + ActivityCollection response = new ActivityCollection(); + int i = 0; + + // skip to the start of the page + for ( ; i<start && activities.hasNext(); i++) activities.next(); + + // return entries up to the page size + for ( ; i<start+size && activities.hasNext(); i++) { + response.addEntry(createActivityData(activities.next())); + } + + // count the total entries + for ( ; activities.hasNext(); i++) activities.next(); + response.setTotal(i); + + if (start > 0) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", Math.max(start-size, 0)).build(); + response.addLink(new Link("prev", uri)); + } + + if (start+size < i) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", start+size).build(); + response.addLink(new Link("next", uri)); + } + + return createOKResponse(response); + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response getActivity(String activityID) { + + if (activityID == null) throw new BadRequestException("Activity ID is null."); + + CMS.debug("ActivityService.getActivity(\"" + activityID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ActivityDatabase database = subsystem.getActivityDatabase(); + + return createOKResponse(createActivityData(database.getRecord(activityID))); + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/AuthenticatorService.java b/base/tps/src/org/dogtagpki/server/tps/rest/AuthenticatorService.java new file mode 100644 index 000000000..d862e261d --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/AuthenticatorService.java @@ -0,0 +1,350 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.config.AuthenticatorDatabase; +import org.dogtagpki.server.tps.config.AuthenticatorRecord; +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.ForbiddenException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.tps.authenticator.AuthenticatorCollection; +import com.netscape.certsrv.tps.authenticator.AuthenticatorData; +import com.netscape.certsrv.tps.authenticator.AuthenticatorResource; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class AuthenticatorService extends PKIService implements AuthenticatorResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public AuthenticatorService() { + CMS.debug("AuthenticatorService.<init>()"); + } + + public AuthenticatorData createAuthenticatorData(AuthenticatorRecord authenticatorRecord) throws UnsupportedEncodingException { + + String authenticatorID = authenticatorRecord.getID(); + + AuthenticatorData authenticatorData = new AuthenticatorData(); + authenticatorData.setID(authenticatorID); + authenticatorData.setStatus(authenticatorRecord.getStatus()); + authenticatorData.setProperties(authenticatorRecord.getProperties()); + + authenticatorID = URLEncoder.encode(authenticatorID, "UTF-8"); + URI uri = uriInfo.getBaseUriBuilder().path(AuthenticatorResource.class).path("{authenticatorID}").build(authenticatorID); + authenticatorData.setLink(new Link("self", uri)); + + return authenticatorData; + } + + public AuthenticatorRecord createAuthenticatorRecord(AuthenticatorData authenticatorData) { + + AuthenticatorRecord authenticatorRecord = new AuthenticatorRecord(); + authenticatorRecord.setID(authenticatorData.getID()); + authenticatorRecord.setStatus(authenticatorData.getStatus()); + authenticatorRecord.setProperties(authenticatorData.getProperties()); + + return authenticatorRecord; + } + + @Override + public Response findAuthenticators(String filter, Integer start, Integer size) { + + CMS.debug("AuthenticatorService.findAuthenticators()"); + + if (filter != null && filter.length() < MIN_FILTER_LENGTH) { + throw new BadRequestException("Filter is too short."); + } + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + AuthenticatorDatabase database = subsystem.getAuthenticatorDatabase(); + + Iterator<AuthenticatorRecord> authenticators = database.findRecords(filter).iterator(); + + AuthenticatorCollection response = new AuthenticatorCollection(); + int i = 0; + + // skip to the start of the page + for ( ; i<start && authenticators.hasNext(); i++) authenticators.next(); + + // return entries up to the page size + for ( ; i<start+size && authenticators.hasNext(); i++) { + response.addEntry(createAuthenticatorData(authenticators.next())); + } + + // count the total entries + for ( ; authenticators.hasNext(); i++) authenticators.next(); + response.setTotal(i); + + if (start > 0) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", Math.max(start-size, 0)).build(); + response.addLink(new Link("prev", uri)); + } + + if (start+size < i) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", start+size).build(); + response.addLink(new Link("next", uri)); + } + + return createOKResponse(response); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response getAuthenticator(String authenticatorID) { + + if (authenticatorID == null) throw new BadRequestException("Authenticator ID is null."); + + CMS.debug("AuthenticatorService.getAuthenticator(\"" + authenticatorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + AuthenticatorDatabase database = subsystem.getAuthenticatorDatabase(); + + return createOKResponse(createAuthenticatorData(database.getRecord(authenticatorID))); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response addAuthenticator(AuthenticatorData authenticatorData) { + + if (authenticatorData == null) throw new BadRequestException("Authenticator data is null."); + + CMS.debug("AuthenticatorService.addAuthenticator(\"" + authenticatorData.getID() + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + AuthenticatorDatabase database = subsystem.getAuthenticatorDatabase(); + + String status = authenticatorData.getStatus(); + Principal principal = servletRequest.getUserPrincipal(); + + if (status == null || database.requiresApproval() && !database.canApprove(principal)) { + // if status is unspecified or user doesn't have rights to approve, the entry is disabled + authenticatorData.setStatus("Disabled"); + } + + database.addRecord(authenticatorData.getID(), createAuthenticatorRecord(authenticatorData)); + authenticatorData = createAuthenticatorData(database.getRecord(authenticatorData.getID())); + + return createCreatedResponse(authenticatorData, authenticatorData.getLink().getHref()); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response updateAuthenticator(String authenticatorID, AuthenticatorData authenticatorData) { + + if (authenticatorID == null) throw new BadRequestException("Authenticator ID is null."); + if (authenticatorData == null) throw new BadRequestException("Authenticator data is null."); + + CMS.debug("AuthenticatorService.updateAuthenticator(\"" + authenticatorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + AuthenticatorDatabase database = subsystem.getAuthenticatorDatabase(); + + AuthenticatorRecord record = database.getRecord(authenticatorID); + + // only disabled authenticator can be updated + if (!"Disabled".equals(record.getStatus())) { + throw new ForbiddenException("Unable to update authenticator " + authenticatorID); + } + + // update status if specified + String status = authenticatorData.getStatus(); + if (status != null && !"Disabled".equals(status)) { + if (!"Enabled".equals(status)) { + throw new ForbiddenException("Invalid authenticator status: " + status); + } + + // if user doesn't have rights, set to pending + Principal principal = servletRequest.getUserPrincipal(); + if (database.requiresApproval() && !database.canApprove(principal)) { + status = "Pending_Approval"; + } + + // enable authenticator + record.setStatus(status); + } + + // update properties if specified + Map<String, String> properties = authenticatorData.getProperties(); + if (properties != null) { + record.setProperties(authenticatorData.getProperties()); + } + + database.updateRecord(authenticatorID, record); + + authenticatorData = createAuthenticatorData(database.getRecord(authenticatorID)); + + return createOKResponse(authenticatorData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response changeAuthenticatorStatus(String authenticatorID, String action) { + + if (authenticatorID == null) throw new BadRequestException("Authenticator ID is null."); + if (action == null) throw new BadRequestException("Action is null."); + + CMS.debug("AuthenticatorService.changeAuthenticatorStatus(\"" + authenticatorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + AuthenticatorDatabase database = subsystem.getAuthenticatorDatabase(); + + AuthenticatorRecord record = database.getRecord(authenticatorID); + String status = record.getStatus(); + + if ("Disabled".equals(status)) { + if ("enable".equals(action)) { + status = "Enabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Enabled".equals(status)) { + if ("disable".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Pending_Approval".equals(status)) { + if ("approve".equals(action)) { + status = "Enabled"; + } else if ("reject".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else { + throw new PKIException("Invalid authenticator status: " + status); + } + + record.setStatus(status); + database.updateRecord(authenticatorID, record); + + AuthenticatorData authenticatorData = createAuthenticatorData(database.getRecord(authenticatorID)); + + return createOKResponse(authenticatorData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response removeAuthenticator(String authenticatorID) { + + if (authenticatorID == null) throw new BadRequestException("Authenticator ID is null."); + + CMS.debug("AuthenticatorService.removeAuthenticator(\"" + authenticatorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + AuthenticatorDatabase database = subsystem.getAuthenticatorDatabase(); + + AuthenticatorRecord record = database.getRecord(authenticatorID); + String status = record.getStatus(); + + if (!"Disabled".equals(status)) { + throw new ForbiddenException("Unable to delete authenticator " + authenticatorID); + } + + database.removeRecord(authenticatorID); + + return createNoContentResponse(); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/ConnectorService.java b/base/tps/src/org/dogtagpki/server/tps/rest/ConnectorService.java new file mode 100644 index 000000000..c281265ef --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/ConnectorService.java @@ -0,0 +1,350 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.config.ConnectorDatabase; +import org.dogtagpki.server.tps.config.ConnectorRecord; +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.ForbiddenException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.tps.connector.ConnectorCollection; +import com.netscape.certsrv.tps.connector.ConnectorData; +import com.netscape.certsrv.tps.connector.ConnectorResource; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class ConnectorService extends PKIService implements ConnectorResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public ConnectorService() { + CMS.debug("ConnectorService.<init>()"); + } + + public ConnectorData createConnectorData(ConnectorRecord connectionRecord) throws UnsupportedEncodingException { + + String connectorID = connectionRecord.getID(); + + ConnectorData connectorData = new ConnectorData(); + connectorData.setID(connectorID); + connectorData.setStatus(connectionRecord.getStatus()); + connectorData.setProperties(connectionRecord.getProperties()); + + connectorID = URLEncoder.encode(connectorID, "UTF-8"); + URI uri = uriInfo.getBaseUriBuilder().path(ConnectorResource.class).path("{connectorID}").build(connectorID); + connectorData.setLink(new Link("self", uri)); + + return connectorData; + } + + public ConnectorRecord createConnectorRecord(ConnectorData connectorData) { + + ConnectorRecord connectorRecord = new ConnectorRecord(); + connectorRecord.setID(connectorData.getID()); + connectorRecord.setStatus(connectorData.getStatus()); + connectorRecord.setProperties(connectorData.getProperties()); + + return connectorRecord; + } + + @Override + public Response findConnectors(String filter, Integer start, Integer size) { + + CMS.debug("ConnectorService.findConnectors()"); + + if (filter != null && filter.length() < MIN_FILTER_LENGTH) { + throw new BadRequestException("Filter is too short."); + } + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + + Iterator<ConnectorRecord> connections = database.findRecords(filter).iterator(); + + ConnectorCollection response = new ConnectorCollection(); + int i = 0; + + // skip to the start of the page + for ( ; i<start && connections.hasNext(); i++) connections.next(); + + // return entries up to the page size + for ( ; i<start+size && connections.hasNext(); i++) { + response.addEntry(createConnectorData(connections.next())); + } + + // count the total entries + for ( ; connections.hasNext(); i++) connections.next(); + response.setTotal(i); + + if (start > 0) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", Math.max(start-size, 0)).build(); + response.addLink(new Link("prev", uri)); + } + + if (start+size < i) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", start+size).build(); + response.addLink(new Link("next", uri)); + } + + return createOKResponse(response); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response getConnector(String connectorID) { + + if (connectorID == null) throw new BadRequestException("Connector ID is null."); + + CMS.debug("ConnectorService.getConnector(\"" + connectorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + + return createOKResponse(createConnectorData(database.getRecord(connectorID))); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response addConnector(ConnectorData connectorData) { + + if (connectorData == null) throw new BadRequestException("Connector data is null."); + + CMS.debug("ConnectorService.addConnector(\"" + connectorData.getID() + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + + String status = connectorData.getStatus(); + Principal principal = servletRequest.getUserPrincipal(); + + if (status == null || database.requiresApproval() && !database.canApprove(principal)) { + // if status is unspecified or user doesn't have rights to approve, the entry is disabled + connectorData.setStatus("Disabled"); + } + + database.addRecord(connectorData.getID(), createConnectorRecord(connectorData)); + connectorData = createConnectorData(database.getRecord(connectorData.getID())); + + return createCreatedResponse(connectorData, connectorData.getLink().getHref()); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response updateConnector(String connectorID, ConnectorData connectorData) { + + if (connectorID == null) throw new BadRequestException("Connector ID is null."); + if (connectorData == null) throw new BadRequestException("Connector data is null."); + + CMS.debug("ConnectorService.updateConnector(\"" + connectorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + + ConnectorRecord record = database.getRecord(connectorID); + + // only disabled connector can be updated + if (!"Disabled".equals(record.getStatus())) { + throw new ForbiddenException("Unable to update connector " + connectorID); + } + + // update status if specified + String status = connectorData.getStatus(); + if (status != null && !"Disabled".equals(status)) { + if (!"Enabled".equals(status)) { + throw new ForbiddenException("Invalid connector status: " + status); + } + + // if user doesn't have rights, set to pending + Principal principal = servletRequest.getUserPrincipal(); + if (database.requiresApproval() && !database.canApprove(principal)) { + status = "Pending_Approval"; + } + + // enable connector + record.setStatus(status); + } + + // update properties if specified + Map<String, String> properties = connectorData.getProperties(); + if (properties != null) { + record.setProperties(properties); + } + + database.updateRecord(connectorID, record); + + connectorData = createConnectorData(database.getRecord(connectorID)); + + return createOKResponse(connectorData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response changeConnectorStatus(String connectorID, String action) { + + if (connectorID == null) throw new BadRequestException("Connector ID is null."); + if (action == null) throw new BadRequestException("Action is null."); + + CMS.debug("ConnectorService.changeConnectorStatus(\"" + connectorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + + ConnectorRecord record = database.getRecord(connectorID); + String status = record.getStatus(); + + if ("Disabled".equals(status)) { + if ("enable".equals(action)) { + status = "Enabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Enabled".equals(status)) { + if ("disable".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Pending_Approval".equals(status)) { + if ("approve".equals(action)) { + status = "Enabled"; + } else if ("reject".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else { + throw new PKIException("Invalid connector status: " + status); + } + + record.setStatus(status); + database.updateRecord(connectorID, record); + + ConnectorData connectorData = createConnectorData(database.getRecord(connectorID)); + + return createOKResponse(connectorData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response removeConnector(String connectorID) { + + if (connectorID == null) throw new BadRequestException("Connector ID is null."); + + CMS.debug("ConnectorService.removeConnector(\"" + connectorID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ConnectorDatabase database = subsystem.getConnectorDatabase(); + + ConnectorRecord record = database.getRecord(connectorID); + String status = record.getStatus(); + + if (!"Disabled".equals(status)) { + throw new ForbiddenException("Unable to delete connector " + connectorID); + } + + database.removeRecord(connectorID); + + return createNoContentResponse(); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/ProfileMappingService.java b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileMappingService.java new file mode 100644 index 000000000..f3a6f2e38 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileMappingService.java @@ -0,0 +1,341 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.config.ProfileMappingDatabase; +import org.dogtagpki.server.tps.config.ProfileMappingRecord; +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.ForbiddenException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.tps.profile.ProfileMappingCollection; +import com.netscape.certsrv.tps.profile.ProfileMappingData; +import com.netscape.certsrv.tps.profile.ProfileMappingResource; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class ProfileMappingService extends PKIService implements ProfileMappingResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public ProfileMappingService() { + CMS.debug("ProfileMappingService.<init>()"); + } + + public ProfileMappingData createProfileMappingData(ProfileMappingRecord profileMappingRecord) throws UnsupportedEncodingException { + + String profileMappingID = profileMappingRecord.getID(); + + ProfileMappingData profileMappingData = new ProfileMappingData(); + profileMappingData.setID(profileMappingID); + profileMappingData.setStatus(profileMappingRecord.getStatus()); + profileMappingData.setProperties(profileMappingRecord.getProperties()); + + profileMappingID = URLEncoder.encode(profileMappingID, "UTF-8"); + URI uri = uriInfo.getBaseUriBuilder().path(ProfileMappingResource.class).path("{profileMappingID}").build(profileMappingID); + profileMappingData.setLink(new Link("self", uri)); + + return profileMappingData; + } + + public ProfileMappingRecord createProfileMappingRecord(ProfileMappingData profileMappingData) { + + ProfileMappingRecord profileMappingRecord = new ProfileMappingRecord(); + profileMappingRecord.setID(profileMappingData.getID()); + profileMappingRecord.setStatus(profileMappingData.getStatus()); + profileMappingRecord.setProperties(profileMappingData.getProperties()); + + return profileMappingRecord; + } + + @Override + public Response findProfileMappings(String filter, Integer start, Integer size) { + + CMS.debug("ProfileMappingService.findProfileMappings()"); + + if (filter != null && filter.length() < MIN_FILTER_LENGTH) { + throw new BadRequestException("Filter is too short."); + } + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileMappingDatabase database = subsystem.getProfileMappingDatabase(); + + Iterator<ProfileMappingRecord> profileMappings = database.findRecords(filter).iterator(); + + ProfileMappingCollection response = new ProfileMappingCollection(); + int i = 0; + + // skip to the start of the page + for ( ; i<start && profileMappings.hasNext(); i++) profileMappings.next(); + + // return entries up to the page size + for ( ; i<start+size && profileMappings.hasNext(); i++) { + response.addEntry(createProfileMappingData(profileMappings.next())); + } + + // count the total entries + for ( ; profileMappings.hasNext(); i++) profileMappings.next(); + response.setTotal(i); + + if (start > 0) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", Math.max(start-size, 0)).build(); + response.addLink(new Link("prev", uri)); + } + + if (start+size < i) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", start+size).build(); + response.addLink(new Link("next", uri)); + } + + return createOKResponse(response); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response getProfileMapping(String profileMappingID) { + + CMS.debug("ProfileMappingService.getProfileMapping(\"" + profileMappingID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileMappingDatabase database = subsystem.getProfileMappingDatabase(); + + return createOKResponse(createProfileMappingData(database.getRecord(profileMappingID))); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response addProfileMapping(ProfileMappingData profileMappingData) { + + CMS.debug("ProfileMappingService.addProfileMapping(\"" + profileMappingData.getID() + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileMappingDatabase database = subsystem.getProfileMappingDatabase(); + + String status = profileMappingData.getStatus(); + Principal principal = servletRequest.getUserPrincipal(); + + if (status == null || database.requiresApproval() && !database.canApprove(principal)) { + // if status is unspecified or user doesn't have rights to approve, the entry is disabled + profileMappingData.setStatus("Disabled"); + } + + database.addRecord(profileMappingData.getID(), createProfileMappingRecord(profileMappingData)); + profileMappingData = createProfileMappingData(database.getRecord(profileMappingData.getID())); + + return createCreatedResponse(profileMappingData, profileMappingData.getLink().getHref()); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response updateProfileMapping(String profileMappingID, ProfileMappingData profileMappingData) { + + CMS.debug("ProfileMappingService.updateProfileMapping(\"" + profileMappingID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileMappingDatabase database = subsystem.getProfileMappingDatabase(); + + ProfileMappingRecord record = database.getRecord(profileMappingID); + + // only disabled profile mapping can be updated + if (!"Disabled".equals(record.getStatus())) { + throw new ForbiddenException("Unable to update profile mapping " + profileMappingID); + } + + // update status if specified + String status = profileMappingData.getStatus(); + if (status != null && !"Disabled".equals(status)) { + if (!"Enabled".equals(status)) { + throw new ForbiddenException("Invalid profile mapping status: " + status); + } + + // if user doesn't have rights, set to pending + Principal principal = servletRequest.getUserPrincipal(); + if (database.requiresApproval() && !database.canApprove(principal)) { + status = "Pending_Approval"; + } + + // enable profile mapping + record.setStatus(status); + } + + // update properties if specified + Map<String, String> properties = profileMappingData.getProperties(); + if (properties != null) { + record.setProperties(properties); + } + + database.updateRecord(profileMappingID, record); + + profileMappingData = createProfileMappingData(database.getRecord(profileMappingID)); + + return createOKResponse(profileMappingData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response changeProfileMappingStatus(String profileMappingID, String action) { + + if (profileMappingID == null) throw new BadRequestException("Profile mapping ID is null."); + if (action == null) throw new BadRequestException("Action is null."); + + CMS.debug("ProfileMappingService.changeProfileMappingStatus(\"" + profileMappingID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileMappingDatabase database = subsystem.getProfileMappingDatabase(); + + ProfileMappingRecord record = database.getRecord(profileMappingID); + String status = record.getStatus(); + + if ("Disabled".equals(status)) { + if ("enable".equals(action)) { + status = "Enabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Enabled".equals(status)) { + if ("disable".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Pending_Approval".equals(status)) { + if ("approve".equals(action)) { + status = "Enabled"; + } else if ("reject".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else { + throw new PKIException("Invalid profile mapping status: " + status); + } + + record.setStatus(status); + database.updateRecord(profileMappingID, record); + + ProfileMappingData profileMappingData = createProfileMappingData(database.getRecord(profileMappingID)); + + return createOKResponse(profileMappingData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response removeProfileMapping(String profileMappingID) { + + CMS.debug("ProfileMappingService.removeProfileMapping(\"" + profileMappingID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileMappingDatabase database = subsystem.getProfileMappingDatabase(); + + ProfileMappingRecord record = database.getRecord(profileMappingID); + String status = record.getStatus(); + + if (!"Disabled".equals(status)) { + throw new ForbiddenException("Unable to delete profile mapping " + profileMappingID); + } + + database.removeRecord(profileMappingID); + + return createNoContentResponse(); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java new file mode 100644 index 000000000..e5bfd4663 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java @@ -0,0 +1,351 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.config.ProfileDatabase; +import org.dogtagpki.server.tps.config.ProfileRecord; +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.ForbiddenException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.tps.profile.ProfileCollection; +import com.netscape.certsrv.tps.profile.ProfileData; +import com.netscape.certsrv.tps.profile.ProfileResource; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class ProfileService extends PKIService implements ProfileResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public ProfileService() { + CMS.debug("ProfileService.<init>()"); + } + + public ProfileData createProfileData(ProfileRecord profileRecord) throws UnsupportedEncodingException { + + String profileID = profileRecord.getID(); + + ProfileData profileData = new ProfileData(); + profileData.setID(profileID); + profileData.setStatus(profileRecord.getStatus()); + profileData.setProperties(profileRecord.getProperties()); + + profileID = URLEncoder.encode(profileID, "UTF-8"); + URI uri = uriInfo.getBaseUriBuilder().path(ProfileResource.class).path("{profileID}").build(profileID); + profileData.setLink(new Link("self", uri)); + + return profileData; + } + + public ProfileRecord createProfileRecord(ProfileData profileData) { + + ProfileRecord profileRecord = new ProfileRecord(); + profileRecord.setID(profileData.getID()); + profileRecord.setStatus(profileData.getStatus()); + profileRecord.setProperties(profileData.getProperties()); + + return profileRecord; + } + + @Override + public Response findProfiles(String filter, Integer start, Integer size) { + + CMS.debug("ProfileService.findProfiles()"); + + if (filter != null && filter.length() < MIN_FILTER_LENGTH) { + throw new BadRequestException("Filter is too short."); + } + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileDatabase database = subsystem.getProfileDatabase(); + + Iterator<ProfileRecord> profiles = database.findRecords(filter).iterator(); + + ProfileCollection response = new ProfileCollection(); + int i = 0; + + // skip to the start of the page + for ( ; i<start && profiles.hasNext(); i++) profiles.next(); + + // return entries up to the page size + for ( ; i<start+size && profiles.hasNext(); i++) { + response.addEntry(createProfileData(profiles.next())); + } + + // count the total entries + for ( ; profiles.hasNext(); i++) profiles.next(); + response.setTotal(i); + + if (start > 0) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", Math.max(start-size, 0)).build(); + response.addLink(new Link("prev", uri)); + } + + if (start+size < i) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", start+size).build(); + response.addLink(new Link("next", uri)); + } + + return createOKResponse(response); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response getProfile(String profileID) { + + if (profileID == null) throw new BadRequestException("Profile ID is null."); + + CMS.debug("ProfileService.getProfile(\"" + profileID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileDatabase database = subsystem.getProfileDatabase(); + + return createOKResponse(createProfileData(database.getRecord(profileID))); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response addProfile(ProfileData profileData) { + + if (profileData == null) throw new BadRequestException("Profile data is null."); + + CMS.debug("ProfileService.addProfile(\"" + profileData.getID() + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileDatabase database = subsystem.getProfileDatabase(); + + String status = profileData.getStatus(); + Principal principal = servletRequest.getUserPrincipal(); + + if (status == null || database.requiresApproval() && !database.canApprove(principal)) { + // if status is unspecified or user doesn't have rights to approve, the entry is disabled + profileData.setStatus("Disabled"); + } + + database.addRecord(profileData.getID(), createProfileRecord(profileData)); + + profileData = createProfileData(database.getRecord(profileData.getID())); + + return createCreatedResponse(profileData, profileData.getLink().getHref()); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response updateProfile(String profileID, ProfileData profileData) { + + if (profileID == null) throw new BadRequestException("Profile ID is null."); + if (profileData == null) throw new BadRequestException("Profile data is null."); + + CMS.debug("ProfileService.updateProfile(\"" + profileID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileDatabase database = subsystem.getProfileDatabase(); + + ProfileRecord record = database.getRecord(profileID); + + // only disabled profile can be updated + if (!"Disabled".equals(record.getStatus())) { + throw new ForbiddenException("Unable to update profile " + profileID); + } + + // update status if specified + String status = profileData.getStatus(); + if (status != null && !"Disabled".equals(status)) { + if (!"Enabled".equals(status)) { + throw new ForbiddenException("Invalid profile status: " + status); + } + + // if user doesn't have rights, set to pending + Principal principal = servletRequest.getUserPrincipal(); + if (database.requiresApproval() && !database.canApprove(principal)) { + status = "Pending_Approval"; + } + + // enable profile + record.setStatus(status); + } + + // update properties if specified + Map<String, String> properties = profileData.getProperties(); + if (properties != null) { + record.setProperties(properties); + } + + database.updateRecord(profileID, record); + + profileData = createProfileData(database.getRecord(profileID)); + + return createOKResponse(profileData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response changeProfileStatus(String profileID, String action) { + + if (profileID == null) throw new BadRequestException("Profile ID is null."); + if (action == null) throw new BadRequestException("Action is null."); + + CMS.debug("ProfileService.changeProfileStatus(\"" + profileID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileDatabase database = subsystem.getProfileDatabase(); + + ProfileRecord record = database.getRecord(profileID); + String status = record.getStatus(); + + if ("Disabled".equals(status)) { + if ("enable".equals(action)) { + status = "Enabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Enabled".equals(status)) { + if ("disable".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else if ("Pending_Approval".equals(status)) { + if ("approve".equals(action)) { + status = "Enabled"; + } else if ("reject".equals(action)) { + status = "Disabled"; + } else { + throw new BadRequestException("Invalid action: " + action); + } + + } else { + throw new PKIException("Invalid profile status: " + status); + } + + record.setStatus(status); + database.updateRecord(profileID, record); + + ProfileData profileData = createProfileData(database.getRecord(profileID)); + + return createOKResponse(profileData); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response removeProfile(String profileID) { + + if (profileID == null) throw new BadRequestException("Profile ID is null."); + + CMS.debug("ProfileService.removeProfile(\"" + profileID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + ProfileDatabase database = subsystem.getProfileDatabase(); + + ProfileRecord record = database.getRecord(profileID); + String status = record.getStatus(); + + if (!"Disabled".equals(status)) { + throw new ForbiddenException("Unable to delete profile " + profileID); + } + + database.removeRecord(profileID); + + return createNoContentResponse(); + + } catch (PKIException e) { + throw e; + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/TPSApplication.java b/base/tps/src/org/dogtagpki/server/tps/rest/TPSApplication.java new file mode 100644 index 000000000..70c8afd02 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/TPSApplication.java @@ -0,0 +1,105 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.rest; + +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.ws.rs.core.Application; + +import org.dogtagpki.server.rest.ACLInterceptor; +import org.dogtagpki.server.rest.AccountService; +import org.dogtagpki.server.rest.AuditService; +import org.dogtagpki.server.rest.AuthMethodInterceptor; +import org.dogtagpki.server.rest.GroupService; +import org.dogtagpki.server.rest.PKIExceptionMapper; +import org.dogtagpki.server.rest.MessageFormatInterceptor; +import org.dogtagpki.server.rest.SelfTestService; +import org.dogtagpki.server.rest.SystemCertService; +import org.dogtagpki.server.rest.UserService; +import org.dogtagpki.server.tps.config.ConfigService; + +/** + * @author Endi S. Dewata <edewata@redhat.com> + */ +public class TPSApplication extends Application { + + private Set<Object> singletons = new LinkedHashSet<Object>(); + private Set<Class<?>> classes = new LinkedHashSet<Class<?>>(); + + public TPSApplication() { + + // account + classes.add(AccountService.class); + + // audit + classes.add(AuditService.class); + + // installer + classes.add(TPSInstallerService.class); + + // user and group management + classes.add(GroupService.class); + classes.add(UserService.class); + + // system certs + classes.add(SystemCertService.class); + + // activities + classes.add(ActivityService.class); + + // authenticators + classes.add(AuthenticatorService.class); + + // certificates + classes.add(TPSCertService.class); + + // config + classes.add(ConfigService.class); + + // connections + classes.add(ConnectorService.class); + + // profiles + classes.add(ProfileService.class); + classes.add(ProfileMappingService.class); + + // selftests + classes.add(SelfTestService.class); + + // tokens + classes.add(TokenService.class); + + // exception mapper + classes.add(PKIExceptionMapper.class); + + // interceptors + singletons.add(new AuthMethodInterceptor()); + singletons.add(new ACLInterceptor()); + singletons.add(new MessageFormatInterceptor()); + } + + public Set<Class<?>> getClasses() { + return classes; + } + + public Set<Object> getSingletons() { + return singletons; + } + +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/TPSCertService.java b/base/tps/src/org/dogtagpki/server/tps/rest/TPSCertService.java new file mode 100644 index 000000000..75314cd5d --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/TPSCertService.java @@ -0,0 +1,179 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.Iterator; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.dbs.TPSCertDatabase; +import org.dogtagpki.server.tps.dbs.TPSCertRecord; +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.tps.cert.TPSCertCollection; +import com.netscape.certsrv.tps.cert.TPSCertData; +import com.netscape.certsrv.tps.cert.TPSCertResource; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class TPSCertService extends PKIService implements TPSCertResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public TPSCertService() { + System.out.println("TPSCertService.<init>()"); + } + + public TPSCertData createCertData(TPSCertRecord certRecord) { + + TPSCertData certData = new TPSCertData(); + certData.setID(certRecord.getId()); + certData.setSerialNumber(certRecord.getSerialNumber()); + certData.setSubject(certRecord.getSubject()); + certData.setTokenID(certRecord.getTokenID()); + certData.setKeyType(certRecord.getKeyType()); + certData.setStatus(certRecord.getStatus()); + certData.setUserID(certRecord.getUserID()); + certData.setCreateTime(certRecord.getCreateTime()); + certData.setModifyTime(certRecord.getModifyTime()); + + String certID = certRecord.getId(); + try { + certID = URLEncoder.encode(certID, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + + URI uri = uriInfo.getBaseUriBuilder().path(TPSCertResource.class).path("{certID}").build(certID); + certData.setLink(new Link("self", uri)); + + return certData; + } + + public TPSCertRecord createCertRecord(TPSCertData certData) { + + TPSCertRecord certRecord = new TPSCertRecord(); + certRecord.setId(certData.getID()); + certRecord.setSerialNumber(certData.getSerialNumber()); + certRecord.setSubject(certData.getSubject()); + certRecord.setTokenID(certData.getTokenID()); + certRecord.setKeyType(certData.getKeyType()); + certRecord.setStatus(certData.getStatus()); + certRecord.setUserID(certData.getUserID()); + certRecord.setCreateTime(certData.getCreateTime()); + certRecord.setModifyTime(certData.getModifyTime()); + + return certRecord; + } + + @Override + public Response findCerts(String filter, Integer start, Integer size) { + + System.out.println("TPSCertService.findCerts()"); + + if (filter != null && filter.length() < MIN_FILTER_LENGTH) { + throw new BadRequestException("Filter is too short."); + } + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TPSCertDatabase database = subsystem.getCertDatabase(); + + Iterator<TPSCertRecord> activities = database.findRecords(filter).iterator(); + + TPSCertCollection response = new TPSCertCollection(); + int i = 0; + + // skip to the start of the page + for ( ; i<start && activities.hasNext(); i++) activities.next(); + + // return entries up to the page size + for ( ; i<start+size && activities.hasNext(); i++) { + response.addEntry(createCertData(activities.next())); + } + + // count the total entries + for ( ; activities.hasNext(); i++) activities.next(); + response.setTotal(i); + + if (start > 0) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", Math.max(start-size, 0)).build(); + response.addLink(new Link("prev", uri)); + } + + if (start+size < i) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", start+size).build(); + response.addLink(new Link("next", uri)); + } + + return createOKResponse(response); + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response getCert(String certID) { + + if (certID == null) throw new BadRequestException("Certificate ID is null."); + + System.out.println("TPSCertService.getCert(\"" + certID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TPSCertDatabase database = subsystem.getCertDatabase(); + + return createOKResponse(createCertData(database.getRecord(certID))); + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/TPSInstallerService.java b/base/tps/src/org/dogtagpki/server/tps/rest/TPSInstallerService.java new file mode 100644 index 000000000..9c4943b9f --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/TPSInstallerService.java @@ -0,0 +1,153 @@ +// --- 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) 2014 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package org.dogtagpki.server.tps.rest; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collection; + +import org.dogtagpki.server.rest.SystemConfigService; +import org.dogtagpki.server.tps.installer.TPSInstaller; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.system.ConfigurationRequest; +import com.netscape.certsrv.system.SystemCertData; +import com.netscape.cms.servlet.csadmin.ConfigurationUtils; + +/** + * @author alee + * + */ +public class TPSInstallerService extends SystemConfigService { + + + public TPSInstallerService() throws EBaseException { + } + + @Override + public void configureSubsystem(ConfigurationRequest request, + Collection<String> certList, String token, String domainXML) { + + super.configureSubsystem(request, certList, token, domainXML); + + // get subsystem certificate nickname + String nickname = null; + for (SystemCertData cert : request.getSystemCerts()) { + if (cert.getTag().equals("subsystem")) { + nickname = cert.getNickname(); + break; + } + } + + if (nickname == null || nickname.isEmpty()) { + throw new BadRequestException("No nickname provided for subsystem certificate"); + } + + // CA Info Panel + configureCAConnector(request, nickname); + + // TKS Info Panel + configureTKSConnector(request, nickname); + + //DRM Info Panel + configureKRAConnector(request, nickname); + + //AuthDBPanel + ConfigurationUtils.updateAuthdbInfo(request.getAuthdbBaseDN(), + request.getAuthdbHost(), request.getAuthdbPort(), + request.getAuthdbSecureConn()); + } + + public void configureCAConnector(ConfigurationRequest request, String nickname) { + + // TODO: get installer from session + TPSInstaller installer = new TPSInstaller(); + installer.configureCAConnector(request.getCaUri(), nickname); + } + + public void configureTKSConnector(ConfigurationRequest request, String nickname) { + + // TODO: get installer from session + TPSInstaller installer = new TPSInstaller(); + installer.configureTKSConnector(request.getTksUri(), nickname); + } + + public void configureKRAConnector(ConfigurationRequest request, String nickname) { + + boolean keygen = request.getEnableServerSideKeyGen().equalsIgnoreCase("true"); + + // TODO: get installer from session + TPSInstaller installer = new TPSInstaller(); + installer.configureKRAConnector(keygen, request.getKraUri(), nickname); + } + + @Override + public void configureDatabase(ConfigurationRequest request) { + + super.configureDatabase(request); + + cs.putString("tokendb.activityBaseDN", "ou=Activities," + request.getBaseDN()); + cs.putString("tokendb.baseDN", "ou=Tokens," + request.getBaseDN()); + cs.putString("tokendb.certBaseDN", "ou=Certificates," + request.getBaseDN()); + cs.putString("tokendb.userBaseDN", request.getBaseDN()); + cs.putString("tokendb.hostport", request.getDsHost() + ":" + request.getDsPort()); + } + + @Override + public void finalizeConfiguration(ConfigurationRequest request) { + + super.finalizeConfiguration(request); + + try { + ConfigurationUtils.addProfilesToTPSUser(request.getAdminUID()); + + URI secdomainURI = new URI(request.getSecurityDomainUri()); + + // register TPS with CA + URI caURI = request.getCaUri(); + ConfigurationUtils.registerUser(secdomainURI, caURI, "ca"); + + // register TPS with TKS + URI tksURI = request.getTksUri(); + ConfigurationUtils.registerUser(secdomainURI, tksURI, "tks"); + + if (request.getEnableServerSideKeyGen().equalsIgnoreCase("true")) { + URI kraURI = request.getKraUri(); + ConfigurationUtils.registerUser(secdomainURI, kraURI, "kra"); + String transportCert = ConfigurationUtils.getTransportCert(secdomainURI, kraURI); + ConfigurationUtils.exportTransportCert(secdomainURI, tksURI, transportCert); + } + + // generate shared secret from the tks + ConfigurationUtils.getSharedSecret( + tksURI.getHost(), + tksURI.getPort(), + Boolean.getBoolean(request.getImportSharedSecret())); + + } catch (URISyntaxException e) { + throw new BadRequestException("Invalid URI for CA, TKS or KRA"); + + } catch (Exception e) { + CMS.debug(e); + throw new PKIException("Errors in registering TPS to CA, TKS or KRA: " + e); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java b/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java new file mode 100644 index 000000000..a58447089 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java @@ -0,0 +1,562 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.dogtagpki.server.tps.TPSSubsystem; +import org.dogtagpki.server.tps.dbs.ActivityDatabase; +import org.dogtagpki.server.tps.dbs.TokenDatabase; +import org.dogtagpki.server.tps.dbs.TokenRecord; +import org.jboss.resteasy.plugins.providers.atom.Link; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.BadRequestException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.tps.token.TokenCollection; +import com.netscape.certsrv.tps.token.TokenData; +import com.netscape.certsrv.tps.token.TokenResource; +import com.netscape.certsrv.tps.token.TokenStatus; +import com.netscape.cms.servlet.base.PKIService; + +/** + * @author Endi S. Dewata + */ +public class TokenService extends PKIService implements TokenResource { + + @Context + private UriInfo uriInfo; + + @Context + private HttpHeaders headers; + + @Context + private Request request; + + @Context + private HttpServletRequest servletRequest; + + public Map<TokenStatus, Collection<TokenStatus>> transitions = new HashMap<TokenStatus, Collection<TokenStatus>>(); + + public TokenService() throws Exception { + CMS.debug("TokenService.<init>()"); + IConfigStore configStore = CMS.getConfigStore(); + + // load allowed token state transitions + CMS.debug("TokenService: allowed transitions:"); + + for (String transition : configStore.getString("tokendb.allowedTransitions").split(",")) { + String states[] = transition.split(":"); + TokenStatus fromState = TokenStatus.fromInt(Integer.valueOf(states[0])); + TokenStatus toState = TokenStatus.fromInt(Integer.valueOf(states[1])); + CMS.debug("TokenService: - " + fromState + " to " + toState); + + Collection<TokenStatus> nextStates = transitions.get(fromState); + if (nextStates == null) { + nextStates = new HashSet<TokenStatus>(); + transitions.put(fromState, nextStates); + } + nextStates.add(toState); + } + + } + + public TokenStatus getTokenStatus(TokenRecord tokenRecord) { + String status = tokenRecord.getStatus(); + + if ("uninitialized".equals(status)) { + return TokenStatus.UNINITIALIZED; + + } else if ("active".equals(status)) { + return TokenStatus.ACTIVE; + + } else if ("lost".equals(status)) { + String reason = tokenRecord.getReason(); + + if ("keyCompromise".equals(reason)) { + return TokenStatus.PERM_LOST; + + } else if ("destroyed".equals(reason)) { + return TokenStatus.DAMAGED; + + } else if ("onHold".equals(reason)) { + return TokenStatus.TEMP_LOST; + } + + } else if ("terminated".equals(status)) { + return TokenStatus.TERMINATED; + } + + return TokenStatus.PERM_LOST; + } + + public void setTokenStatus(TokenRecord tokenRecord, TokenStatus tokenState) throws Exception { + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + + switch (tokenState) { + case UNINITIALIZED: + tokenRecord.setStatus("uninitialized"); + tokenRecord.setReason(null); + break; + case ACTIVE: + String origStatus = tokenRecord.getStatus(); + String origReason = tokenRecord.getReason(); + if (origStatus.equalsIgnoreCase("lost") && + origReason.equalsIgnoreCase("onHold")) { + //unrevoke certs + tps.tdb.unRevokeCertsByCUID(tokenRecord.getId()); + } + + tokenRecord.setStatus("active"); + tokenRecord.setReason(null); + break; + case PERM_LOST: + case TEMP_LOST_PERM_LOST: + tokenRecord.setStatus("lost"); + tokenRecord.setReason("keyCompromise"); + + //revoke certs + tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "keyCompromise"); + break; + case DAMAGED: + tokenRecord.setStatus("lost"); + tokenRecord.setReason("destroyed"); + + //revoke certs + tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "destroyed"); + + break; + case TEMP_LOST: + tokenRecord.setStatus("lost"); + tokenRecord.setReason("onHold"); + + // put certs onHold + tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "onHold"); + break; + case TERMINATED: + String reason = "keyCompromise"; + String origStatus2 = tokenRecord.getStatus(); + String origReason2 = tokenRecord.getReason(); + // temp token looks at "onHold" + if (origStatus2.equalsIgnoreCase("lost") && + origReason2.equalsIgnoreCase("onHold")) { + reason = "onHold"; + } + tokenRecord.setStatus("terminated"); + tokenRecord.setReason(reason); + + //revoke certs + tps.tdb.revokeCertsByCUID(tokenRecord.getId(), reason) ; + break; + default: + throw new PKIException("Unsupported token state: " + tokenState); + } + + } + + public TokenData createTokenData(TokenRecord tokenRecord) { + + TokenData tokenData = new TokenData(); + tokenData.setID(tokenRecord.getId()); + tokenData.setTokenID(tokenRecord.getId()); + tokenData.setUserID(tokenRecord.getUserID()); + tokenData.setType(tokenRecord.getType()); + tokenData.setStatus(getTokenStatus(tokenRecord)); + tokenData.setAppletID(tokenRecord.getAppletID()); + tokenData.setKeyInfo(tokenRecord.getKeyInfo()); + tokenData.setPolicy(tokenRecord.getPolicy()); + tokenData.setCreateTimestamp(tokenRecord.getCreateTimestamp()); + tokenData.setModifyTimestamp(tokenRecord.getModifyTimestamp()); + + String tokenID = tokenRecord.getId(); + try { + tokenID = URLEncoder.encode(tokenID, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + + URI uri = uriInfo.getBaseUriBuilder().path(TokenResource.class).path("{tokenID}").build(tokenID); + tokenData.setLink(new Link("self", uri)); + + return tokenData; + } + + public TokenRecord createTokenRecord(TokenData tokenData) throws Exception { + + TokenRecord tokenRecord = new TokenRecord(); + tokenRecord.setId(tokenData.getID()); + tokenRecord.setUserID(tokenData.getUserID()); + tokenRecord.setType(tokenData.getType()); + setTokenStatus(tokenRecord, tokenData.getStatus()); + tokenRecord.setAppletID(tokenData.getAppletID()); + tokenRecord.setKeyInfo(tokenData.getKeyInfo()); + tokenRecord.setPolicy(tokenData.getPolicy()); + tokenRecord.setCreateTimestamp(tokenData.getCreateTimestamp()); + tokenRecord.setModifyTimestamp(tokenData.getModifyTimestamp()); + + return tokenRecord; + } + + @Override + public Response findTokens(String filter, Integer start, Integer size) { + + CMS.debug("TokenService.findTokens()"); + + if (filter != null && filter.length() < MIN_FILTER_LENGTH) { + throw new BadRequestException("Filter is too short."); + } + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TokenDatabase database = subsystem.getTokenDatabase(); + + Iterator<TokenRecord> tokens = database.findRecords(filter).iterator(); + + TokenCollection response = new TokenCollection(); + int i = 0; + + // skip to the start of the page + for ( ; i<start && tokens.hasNext(); i++) tokens.next(); + + // return entries up to the page size + for ( ; i<start+size && tokens.hasNext(); i++) { + response.addEntry(createTokenData(tokens.next())); + } + + // count the total entries + for ( ; tokens.hasNext(); i++) tokens.next(); + response.setTotal(i); + + if (start > 0) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", Math.max(start-size, 0)).build(); + response.addLink(new Link("prev", uri)); + } + + if (start+size < i) { + URI uri = uriInfo.getRequestUriBuilder().replaceQueryParam("start", start+size).build(); + response.addLink(new Link("next", uri)); + } + + return createOKResponse(response); + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response getToken(String tokenID) { + + if (tokenID == null) throw new BadRequestException("Token ID is null."); + + CMS.debug("TokenService.getToken(\"" + tokenID + "\")"); + + try { + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TokenDatabase database = subsystem.getTokenDatabase(); + + return createOKResponse(createTokenData(database.getRecord(tokenID))); + + } catch (Exception e) { + e.printStackTrace(); + throw new PKIException(e.getMessage()); + } + } + + @Override + public Response addToken(TokenData tokenData) { + + if (tokenData == null) throw new BadRequestException("Token data is null."); + + String tokenID = tokenData.getTokenID(); + CMS.debug("TokenService.addToken(\"" + tokenID + "\")"); + + String remoteUser = servletRequest.getRemoteUser(); + String ipAddress = servletRequest.getRemoteAddr(); + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TokenRecord tokenRecord = null; + String msg = "add token"; + + try { + TokenDatabase database = subsystem.getTokenDatabase(); + + // new tokens are uninitialized when created + tokenData.setStatus(TokenStatus.UNINITIALIZED); + + tokenRecord = createTokenRecord(tokenData); + tokenRecord.setId(tokenID); + database.addRecord(tokenID, tokenRecord); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_ADD, tokenRecord, + ipAddress, msg, "success", remoteUser); + tokenData = createTokenData(database.getRecord(tokenID)); + + return createCreatedResponse(tokenData, tokenData.getLink().getHref()); + + } catch (Exception e) { + e.printStackTrace(); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_ADD, tokenRecord, + ipAddress, msg, "failure", remoteUser); + msg = msg + ":" + e; + + throw new PKIException(msg); + } + } + + @Override + public Response replaceToken(String tokenID, TokenData tokenData) { + + if (tokenID == null) throw new BadRequestException("Token ID is null."); + if (tokenData == null) throw new BadRequestException("Token data is null."); + + CMS.debug("TokenService.replaceToken(\"" + tokenID + "\")"); + + String remoteUser = servletRequest.getRemoteUser(); + String ipAddress = servletRequest.getRemoteAddr(); + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TokenRecord tokenRecord = null; + String msg = "replace token"; + try { + TokenDatabase database = subsystem.getTokenDatabase(); + + tokenRecord = database.getRecord(tokenID); + tokenRecord.setUserID(remoteUser); + tokenRecord.setType(tokenData.getType()); + tokenRecord.setAppletID(tokenData.getAppletID()); + tokenRecord.setKeyInfo(tokenData.getKeyInfo()); + tokenRecord.setPolicy(tokenData.getPolicy()); + database.updateRecord(tokenID, tokenRecord); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord, + ipAddress, msg, "success", remoteUser); + + tokenData = createTokenData(database.getRecord(tokenID)); + + return createOKResponse(tokenData); + + } catch (Exception e) { + e.printStackTrace(); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord, + ipAddress, msg, "failure", + remoteUser); + msg = msg + ":" + e; + + throw new PKIException(msg); + } + } + + @Override + public Response modifyToken(String tokenID, TokenData tokenData) { + + if (tokenID == null) throw new BadRequestException("Token ID is null."); + if (tokenData == null) throw new BadRequestException("Token data is null."); + + CMS.debug("TokenService.modifyToken(\"" + tokenID + "\")"); + + String remoteUser = servletRequest.getRemoteUser(); + String ipAddress = servletRequest.getRemoteAddr(); + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TokenRecord tokenRecord = null; + String msg = "modify token"; + try { + TokenDatabase database = subsystem.getTokenDatabase(); + + // get existing record + tokenRecord = database.getRecord(tokenID); + + // update user ID if specified + String userID = tokenData.getUserID(); + if (userID != null) { + if (userID.equals("")) { // remove value if empty + tokenRecord.setUserID(null); + } else { // otherwise replace value + tokenRecord.setUserID(userID); + } + } + + // update type if specified + String type = tokenData.getType(); + if (type != null) { + if (type.equals("")) { // remove value if empty + tokenRecord.setType(null); + } else { // otherwise replace value + tokenRecord.setType(type); + } + } + + // update applet ID if specified + String appletID = tokenData.getAppletID(); + if (appletID != null) { + if (appletID.equals("")) { // remove value if empty + tokenRecord.setAppletID(null); + } else { // otherwise replace value + tokenRecord.setAppletID(appletID); + } + } + + // update key info if specified + String keyInfo = tokenData.getKeyInfo(); + if (keyInfo != null) { + if (keyInfo.equals("")) { // remove value if empty + tokenRecord.setKeyInfo(null); + } else { // otherwise replace value + tokenRecord.setKeyInfo(keyInfo); + } + } + + // update policy if specified + String policy = tokenData.getPolicy(); + if (policy != null) { + if (policy.equals("")) { // remove value if empty + tokenRecord.setPolicy(null); + } else { //otherwise replace value + tokenRecord.setPolicy(policy); + } + } + + database.updateRecord(tokenID, tokenRecord); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord, + ipAddress, msg, "success", remoteUser); + + tokenData = createTokenData(database.getRecord(tokenID)); + + return createOKResponse(tokenData); + + } catch (Exception e) { + e.printStackTrace(); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord, + ipAddress, msg, "failure", + remoteUser); + msg = msg + ":" + e; + + throw new PKIException(msg); + } + } + + @Override + public Response changeTokenStatus(String tokenID, TokenStatus tokenStatus) { + + if (tokenID == null) throw new BadRequestException("Token ID is null."); + if (tokenStatus == null) throw new BadRequestException("Token state is null."); + + CMS.debug("TokenService.changeTokenStatus(\"" + tokenID + "\", \"" + tokenStatus + "\")"); + + String remoteUser = servletRequest.getRemoteUser(); + String ipAddress = servletRequest.getRemoteAddr(); + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TokenRecord tokenRecord = null; + String msg = ""; + try { + TokenDatabase database = subsystem.getTokenDatabase(); + + tokenRecord = database.getRecord(tokenID); + TokenStatus currentTokenStatus = getTokenStatus(tokenRecord); + CMS.debug("TokenService.changeTokenStatus(): current status: " + currentTokenStatus); + msg = "change token status from " + currentTokenStatus + " to " + tokenStatus; + + // make sure transition is allowed + Collection<TokenStatus> nextStatuses = transitions.get(currentTokenStatus); + CMS.debug("TokenService.changeTokenStatus(): allowed next statuses: " + nextStatuses); + if (nextStatuses == null || !nextStatuses.contains(tokenStatus)) { + CMS.debug("TokenService.changeTokenStatus(): next status not allowed: " + tokenStatus); + msg = msg + ": Invalid token status transition"; + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord, + ipAddress, msg, + "failure", + remoteUser); + throw new BadRequestException(msg); + } + + CMS.debug("TokenService.changeTokenStatus(): next status allowed: " + tokenStatus); + setTokenStatus(tokenRecord, tokenStatus); + database.updateRecord(tokenID, tokenRecord); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord, + ipAddress, msg, "success", + remoteUser); + + TokenData tokenData = createTokenData(database.getRecord(tokenID)); + + return createOKResponse(tokenData); + + } catch (Exception e) { + e.printStackTrace(); + msg = msg + e; + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord, + ipAddress, msg, "failure", + remoteUser); + + throw new PKIException(msg); + } + } + + @Override + public Response removeToken(String tokenID) { + + if (tokenID == null) throw new BadRequestException("Token ID is null."); + + CMS.debug("TokenService.removeToken(\"" + tokenID + "\")"); + + String remoteUser = servletRequest.getRemoteUser(); + String ipAddress = servletRequest.getRemoteAddr(); + + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); + TokenRecord tokenRecord = null; + String msg = "remove token"; + try { + TokenDatabase database = subsystem.getTokenDatabase(); + tokenRecord = database.getRecord(tokenID); + database.removeRecord(tokenID); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DELETE, tokenRecord, + ipAddress, msg, "success", remoteUser); + + return createNoContentResponse(); + + } catch (Exception e) { + e.printStackTrace(); + subsystem.tdb.tdbActivity(ActivityDatabase.OP_DELETE, tokenRecord, + ipAddress, msg, "failure", + remoteUser); + msg = msg + ":" + e; + + throw new PKIException(msg); + } + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/selftests/TPSPresence.java b/base/tps/src/org/dogtagpki/server/tps/selftests/TPSPresence.java new file mode 100644 index 000000000..65ac197e7 --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/selftests/TPSPresence.java @@ -0,0 +1,187 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.selftests; + +import java.security.PublicKey; +import java.util.Locale; + +import org.dogtagpki.server.tps.TPSSubsystem; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.logging.ILogEventListener; +import com.netscape.certsrv.selftests.EDuplicateSelfTestException; +import com.netscape.certsrv.selftests.EInvalidSelfTestException; +import com.netscape.certsrv.selftests.EMissingSelfTestException; +import com.netscape.certsrv.selftests.ESelfTestException; +import com.netscape.certsrv.selftests.ISelfTestSubsystem; +import com.netscape.cms.selftests.ASelfTest; + +/** + * This class implements a self test to check for TPS presence. + * <P> + * + * @author alee + * @version $Revision$, $Date$ + */ +public class TPSPresence extends ASelfTest { + + public static final String PROP_TPS_SUB_ID = "TpsSubId"; + private String tpsSubId = null; + + /** + * Initializes this subsystem with the configuration store + * associated with this instance name. + * <P> + * + * @param subsystem the associated subsystem + * @param instanceName the name of this self test instance + * @param parameters configuration store (self test parameters) + * @exception EDuplicateSelfTestException subsystem has duplicate name/value + * @exception EInvalidSelfTestException subsystem has invalid name/value + * @exception EMissingSelfTestException subsystem has missing name/value + */ + public void initSelfTest(ISelfTestSubsystem subsystem, String instanceName, + IConfigStore parameters) throws EDuplicateSelfTestException, EInvalidSelfTestException, + EMissingSelfTestException { + super.initSelfTest(subsystem, instanceName, parameters); + + try { + tpsSubId = mConfig.getString(PROP_TPS_SUB_ID); + if (tpsSubId != null) { + tpsSubId = tpsSubId.trim(); + } else { + mSelfTestSubsystem.log(mSelfTestSubsystem.getSelfTestLogger(), + CMS.getLogMessage("SELFTESTS_MISSING_VALUES", getSelfTestName(), + mPrefix + "." + PROP_TPS_SUB_ID)); + + throw new EMissingSelfTestException(PROP_TPS_SUB_ID); + } + } catch (EBaseException e) { + mSelfTestSubsystem.log(mSelfTestSubsystem.getSelfTestLogger(), + CMS.getLogMessage("SELFTESTS_MISSING_NAME", getSelfTestName(), + mPrefix + "." + PROP_TPS_SUB_ID)); + + throw new EMissingSelfTestException(mPrefix, PROP_TPS_SUB_ID, null); + } + } + + /** + * Notifies this subsystem if it is in execution mode. + * <P> + * + * @exception ESelfTestException failed to start + */ + public void startupSelfTest() throws ESelfTestException { + } + + /** + * Stops this subsystem. The subsystem may call shutdownSelfTest + * anytime after initialization. + * <P> + */ + public void shutdownSelfTest() { + } + + /** + * Returns the name associated with this self test. This method may + * return null if the self test has not been initialized. + * <P> + * + * @return instanceName of this self test + */ + public String getSelfTestName() { + return super.getSelfTestName(); + } + + /** + * Returns the root configuration storage (self test parameters) + * associated with this subsystem. + * <P> + * + * @return configuration store (self test parameters) of this subsystem + */ + public IConfigStore getSelfTestConfigStore() { + return super.getSelfTestConfigStore(); + } + + /** + * Retrieves description associated with an individual self test. + * This method may return null. + * <P> + * + * @param locale locale of the client that requests the description + * @return description of self test + */ + public String getSelfTestDescription(Locale locale) { + return CMS.getUserMessage(locale, + "CMS_SELFTESTS_TPS_PRESENCE_DESCRIPTION"); + } + + /** + * Execute an individual self test. + * <P> + * + * @param logger specifies logging subsystem + * @exception ESelfTestException self test exception + */ + public void runSelfTest(ILogEventListener logger) + throws ESelfTestException { + String logMessage = null; + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(tpsSubId); + if (tps == null) { + // log that the TPS is not installed + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_NOT_PRESENT", getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + throw new ESelfTestException(logMessage); + } + + // Retrieve the TPS certificate + org.mozilla.jss.crypto.X509Certificate tpsCert = null; + try { + tpsCert = tps.getSubsystemCert(); + } catch (Exception e) { + e.printStackTrace(); + // cert does not exist or is not yet configured + // tpsCert will remain null + } + + if (tpsCert == null) { + // log that the TPS is not yet initialized + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_NOT_INITIALIZED", + getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + throw new ESelfTestException(logMessage); + } + + // Retrieve the TPS certificate public key + PublicKey tpsPubKey = tpsCert.getPublicKey(); + if (tpsPubKey == null) { + // log that something is seriously wrong with the TPS + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_CORRUPT", getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + throw new ESelfTestException(logMessage); + } + + // log that the TPS is present + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_PRESENT", getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + } +} diff --git a/base/tps/src/org/dogtagpki/server/tps/selftests/TPSValidity.java b/base/tps/src/org/dogtagpki/server/tps/selftests/TPSValidity.java new file mode 100644 index 000000000..f140d6e7c --- /dev/null +++ b/base/tps/src/org/dogtagpki/server/tps/selftests/TPSValidity.java @@ -0,0 +1,197 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package org.dogtagpki.server.tps.selftests; + +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.util.Locale; + +import netscape.security.x509.X509CertImpl; + +import org.dogtagpki.server.tps.TPSSubsystem; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.logging.ILogEventListener; +import com.netscape.certsrv.selftests.EDuplicateSelfTestException; +import com.netscape.certsrv.selftests.EInvalidSelfTestException; +import com.netscape.certsrv.selftests.EMissingSelfTestException; +import com.netscape.certsrv.selftests.ESelfTestException; +import com.netscape.certsrv.selftests.ISelfTestSubsystem; +import com.netscape.cms.selftests.ASelfTest; + +/** + * This class implements a self test to check the validity of the TPS. + * <P> + * + * @author alee + * @version $Revision$, $Date$ + */ +public class TPSValidity extends ASelfTest { + + // parameter information + public static final String PROP_TPS_SUB_ID = "TpsSubId"; + private String tpsSubId = null; + + /** + * Initializes this subsystem with the configuration store + * associated with this instance name. + * <P> + * + * @param subsystem the associated subsystem + * @param instanceName the name of this self test instance + * @param parameters configuration store (self test parameters) + * @exception EDuplicateSelfTestException subsystem has duplicate name/value + * @exception EInvalidSelfTestException subsystem has invalid name/value + * @exception EMissingSelfTestException subsystem has missing name/value + */ + public void initSelfTest(ISelfTestSubsystem subsystem, String instanceName, + IConfigStore parameters) throws EDuplicateSelfTestException, + EInvalidSelfTestException, EMissingSelfTestException { + super.initSelfTest(subsystem, instanceName, parameters); + + try { + tpsSubId = mConfig.getString(PROP_TPS_SUB_ID); + if (tpsSubId != null) { + tpsSubId = tpsSubId.trim(); + } else { + mSelfTestSubsystem.log(mSelfTestSubsystem.getSelfTestLogger(), + CMS.getLogMessage("SELFTESTS_MISSING_VALUES", getSelfTestName(), + mPrefix + "." + PROP_TPS_SUB_ID)); + + throw new EMissingSelfTestException(PROP_TPS_SUB_ID); + } + } catch (EBaseException e) { + mSelfTestSubsystem.log(mSelfTestSubsystem.getSelfTestLogger(), + CMS.getLogMessage("SELFTESTS_MISSING_NAME", getSelfTestName(), + mPrefix + "." + PROP_TPS_SUB_ID)); + + throw new EMissingSelfTestException(mPrefix, PROP_TPS_SUB_ID, null); + } + } + + /** + * Notifies this subsystem if it is in execution mode. + * <P> + * + * @exception ESelfTestException failed to start + */ + public void startupSelfTest() throws ESelfTestException { + } + + /** + * Stops this subsystem. The subsystem may call shutdownSelfTest + * anytime after initialization. + * <P> + */ + public void shutdownSelfTest() { + } + + /** + * Returns the name associated with this self test. This method may + * return null if the self test has not been intialized. + * <P> + * + * @return instanceName of this self test + */ + public String getSelfTestName() { + return super.getSelfTestName(); + } + + /** + * Returns the root configuration storage (self test parameters) + * associated with this subsystem. + * <P> + * + * @return configuration store (self test parameters) of this subsystem + */ + public IConfigStore getSelfTestConfigStore() { + return super.getSelfTestConfigStore(); + } + + /** + * Retrieves description associated with an individual self test. + * This method may return null. + * <P> + * + * @param locale locale of the client that requests the description + * @return description of self test + */ + public String getSelfTestDescription(Locale locale) { + return CMS.getUserMessage(locale, + "CMS_SELFTESTS_TPS_VALIDITY_DESCRIPTION"); + } + + /** + * Execute an individual self test. + * <P> + * + * @param logger specifies logging subsystem + * @exception ESelfTestException self test exception + */ + public void runSelfTest(ILogEventListener logger) + throws ESelfTestException { + String logMessage = null; + TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(tpsSubId); + + if (tps == null) { + // log that the TPS is not installed + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_NOT_PRESENT", getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + throw new ESelfTestException(logMessage); + } + + // Retrieve the TPS subsystem certificate + X509CertImpl tpsCert = null; + try { + tpsCert = new X509CertImpl(tps.getSubsystemCert().getEncoded()); + } catch (Exception e) { + // certificate is not present or has not been configured + // tpsCert will remain null + } + + if (tpsCert == null) { + // log that the TPS is not yet initialized + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_NOT_INITIALIZED", + getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + throw new ESelfTestException(logMessage); + } + + // Check the TPS validity period + try { + tpsCert.checkValidity(); + } catch (CertificateNotYetValidException e) { + // log that the TPS is not yet valid + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_NOT_YET_VALID", getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + throw new ESelfTestException(logMessage); + } catch (CertificateExpiredException e) { + // log that the TPS is expired + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_EXPIRED", getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + throw new ESelfTestException(logMessage); + } + + // log that the TPS is valid + logMessage = CMS.getLogMessage("SELFTESTS_TPS_IS_VALID", getSelfTestName()); + mSelfTestSubsystem.log(logger, logMessage); + } +} diff --git a/base/tps/src/pki-tps.mf b/base/tps/src/pki-tps.mf new file mode 100644 index 000000000..d77fe8fa9 --- /dev/null +++ b/base/tps/src/pki-tps.mf @@ -0,0 +1,3 @@ +Name: pki-tps +Specification-Version: ${APPLICATION_VERSION} +Implementation-Version: ${VERSION} |