summaryrefslogtreecommitdiffstats
path: root/base/kra/src
diff options
context:
space:
mode:
authorEndi Sukma Dewata <edewata@redhat.com>2012-03-24 02:27:47 -0500
committerEndi Sukma Dewata <edewata@redhat.com>2012-03-26 11:43:54 -0500
commit621d9e5c413e561293d7484b93882d985b3fe15f (patch)
tree638f3d75761c121d9a8fb50b52a12a6686c5ac5c /base/kra/src
parent40d3643b8d91886bf210aa27f711731c81a11e49 (diff)
downloadpki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.gz
pki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.xz
pki-621d9e5c413e561293d7484b93882d985b3fe15f.zip
Removed unnecessary pki folder.
Previously the source code was located inside a pki folder. This folder was created during svn migration and is no longer needed. This folder has now been removed and the contents have been moved up one level. Ticket #131
Diffstat (limited to 'base/kra/src')
-rw-r--r--base/kra/src/CMakeLists.txt109
-rw-r--r--base/kra/src/com/netscape/kra/ArchiveOptions.java154
-rw-r--r--base/kra/src/com/netscape/kra/EncryptionUnit.java741
-rw-r--r--base/kra/src/com/netscape/kra/EnrollmentService.java872
-rw-r--r--base/kra/src/com/netscape/kra/KRANotify.java50
-rw-r--r--base/kra/src/com/netscape/kra/KRAPolicy.java78
-rw-r--r--base/kra/src/com/netscape/kra/KRAService.java101
-rw-r--r--base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java1785
-rw-r--r--base/kra/src/com/netscape/kra/NetkeyKeygenService.java608
-rw-r--r--base/kra/src/com/netscape/kra/RecoveryService.java710
-rw-r--r--base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java388
-rw-r--r--base/kra/src/com/netscape/kra/SecurityDataService.java171
-rw-r--r--base/kra/src/com/netscape/kra/StorageKeyUnit.java978
-rw-r--r--base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java627
-rw-r--r--base/kra/src/com/netscape/kra/TransportKeyUnit.java195
15 files changed, 7567 insertions, 0 deletions
diff --git a/base/kra/src/CMakeLists.txt b/base/kra/src/CMakeLists.txt
new file mode 100644
index 000000000..df7e1929b
--- /dev/null
+++ b/base/kra/src/CMakeLists.txt
@@ -0,0 +1,109 @@
+project(pki-kra_java Java)
+
+# '/usr/share/java/pki' jars
+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
+ ${JAVA_LIB_INSTALL_DIR}
+ /usr/share/java/pki
+)
+
+
+# '/usr/share/java' jars
+find_file(LDAPJDK_JAR
+ NAMES
+ ldapjdk.jar
+ PATHS
+ /usr/share/java
+)
+
+
+# '${JAVA_LIB_INSTALL_DIR}' jars
+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(SYMKEY_JAR
+ NAMES
+ symkey.jar
+ PATHS
+ ${JAVA_LIB_INSTALL_DIR}
+)
+
+
+# identify java sources
+set(pki-kra_java_SRCS
+ com/netscape/kra/KeyRecoveryAuthority.java
+ com/netscape/kra/EnrollmentService.java
+ com/netscape/kra/RecoveryService.java
+ com/netscape/kra/SecurityDataRecoveryService.java
+ com/netscape/kra/TokenKeyRecoveryService.java
+ com/netscape/kra/EncryptionUnit.java
+ com/netscape/kra/KRAService.java
+ com/netscape/kra/NetkeyKeygenService.java
+ com/netscape/kra/SecurityDataService.java
+ com/netscape/kra/KRANotify.java
+ com/netscape/kra/KRAPolicy.java
+ com/netscape/kra/TransportKeyUnit.java
+ com/netscape/kra/StorageKeyUnit.java
+ com/netscape/kra/ArchiveOptions.java
+)
+
+
+# set classpath
+set(CMAKE_JAVA_INCLUDE_PATH
+ ${PKI_CERTSRV_JAR} ${PKI_CMS_JAR} ${PKI_CMSCORE_JAR}
+ ${PKI_CMSUTIL_JAR} ${PKI_NSUTIL_JAR}
+ ${LDAPJDK_JAR}
+ ${JSS_JAR} ${COMMONS_CODEC_JAR} ${SYMKEY_JAR})
+
+
+# set version
+set(CMAKE_JAVA_TARGET_VERSION ${APPLICATION_VERSION})
+
+
+# build pki-kra.jar
+add_jar(pki-kra ${pki-kra_java_SRCS})
+add_dependencies(pki-kra symkey pki-nsutil pki-cmsutil pki-certsrv pki-cms pki-cmscore)
+install_jar(pki-kra ${JAVA_JAR_INSTALL_DIR}/pki)
+set(PKI_KRA_JAR ${pki-kra_JAR_FILE} CACHE INTERNAL "pki-kra jar file")
+
diff --git a/base/kra/src/com/netscape/kra/ArchiveOptions.java b/base/kra/src/com/netscape/kra/ArchiveOptions.java
new file mode 100644
index 000000000..c4650f17e
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/ArchiveOptions.java
@@ -0,0 +1,154 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.mozilla.jss.asn1.ANY;
+import org.mozilla.jss.asn1.BIT_STRING;
+import org.mozilla.jss.asn1.InvalidBERException;
+import org.mozilla.jss.asn1.OCTET_STRING;
+import org.mozilla.jss.asn1.SET;
+import org.mozilla.jss.pkix.cms.EncryptedContentInfo;
+import org.mozilla.jss.pkix.cms.EnvelopedData;
+import org.mozilla.jss.pkix.cms.RecipientInfo;
+import org.mozilla.jss.pkix.crmf.EncryptedKey;
+import org.mozilla.jss.pkix.crmf.EncryptedValue;
+import org.mozilla.jss.pkix.crmf.PKIArchiveOptions;
+import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+
+class ArchiveOptions {
+ private String mSymmAlgOID = null;
+ private byte mSymmAlgParams[] = null;
+ private byte mEncSymmKey[] = null;
+ private byte mEncValue[] = null;
+
+ public ArchiveOptions(PKIArchiveOptions opts) throws EBaseException {
+ try {
+ EncryptedKey key = opts.getEncryptedKey();
+ ANY enveloped_val = null;
+ EncryptedValue val = null;
+ AlgorithmIdentifier symmAlg = null;
+
+ if (key.getType() == org.mozilla.jss.pkix.crmf.EncryptedKey.ENVELOPED_DATA) {
+ CMS.debug("EnrollService: ArchiveOptions() EncryptedKey type= ENVELOPED_DATA");
+ // this is the new RFC4211 EncryptedKey that should
+ // have EnvelopedData to replace the deprecated EncryptedValue
+ enveloped_val = key.getEnvelopedData();
+ byte[] env_b = enveloped_val.getEncoded();
+ EnvelopedData.Template env_template = new EnvelopedData.Template();
+ EnvelopedData env_data =
+ (EnvelopedData) env_template.decode(new ByteArrayInputStream(env_b));
+ EncryptedContentInfo eCI = env_data.getEncryptedContentInfo();
+ symmAlg = eCI.getContentEncryptionAlgorithm();
+ mSymmAlgOID = symmAlg.getOID().toString();
+ mSymmAlgParams =
+ ((OCTET_STRING) ((ANY) symmAlg.getParameters()).decodeWith(OCTET_STRING.getTemplate()))
+ .toByteArray();
+
+ SET recipients = env_data.getRecipientInfos();
+ if (recipients.size() <= 0) {
+ CMS.debug("EnrollService: ArchiveOptions() - missing recipient information ");
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE",
+ "[PKIArchiveOptions] missing recipient information "));
+ }
+ //check recpient - later
+ //we only handle one recipient here anyways. so, either the key
+ //can be decrypted or it can't. No risk here.
+ RecipientInfo ri = (RecipientInfo) recipients.elementAt(0);
+ OCTET_STRING key_o = ri.getEncryptedKey();
+ mEncSymmKey = key_o.toByteArray();
+
+ OCTET_STRING oString = eCI.getEncryptedContent();
+ BIT_STRING encVal = new BIT_STRING(oString.toByteArray(), 0);
+ mEncValue = encVal.getBits();
+ CMS.debug("EnrollService: ArchiveOptions() EncryptedKey type= ENVELOPED_DATA done");
+ } else if (key.getType() == org.mozilla.jss.pkix.crmf.EncryptedKey.ENCRYPTED_VALUE) {
+ CMS.debug("EnrollService: ArchiveOptions() EncryptedKey type= ENCRYPTED_VALUE");
+ // this is deprecated: EncryptedValue
+ val = key.getEncryptedValue();
+ symmAlg = val.getSymmAlg();
+ mSymmAlgOID = symmAlg.getOID().toString();
+ mSymmAlgParams =
+ ((OCTET_STRING) ((ANY) symmAlg.getParameters()).decodeWith(OCTET_STRING.getTemplate()))
+ .toByteArray();
+ BIT_STRING encSymmKey = val.getEncSymmKey();
+
+ mEncSymmKey = encSymmKey.getBits();
+ BIT_STRING encVal = val.getEncValue();
+
+ mEncValue = encVal.getBits();
+ CMS.debug("EnrollService: ArchiveOptions() EncryptedKey type= ENCRYPTED_VALUE done");
+ } else {
+ CMS.debug("EnrollService: ArchiveOptions() invalid EncryptedKey type");
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[PKIArchiveOptions] type "
+ + key.getType()));
+ }
+
+ } catch (InvalidBERException e) {
+ CMS.debug("EnrollService: ArchiveOptions(): " + e.toString());
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE",
+ "[PKIArchiveOptions]" + e.toString()));
+ } catch (IOException e) {
+ CMS.debug("EnrollService: ArchiveOptions(): " + e.toString());
+ throw new EBaseException("ArchiveOptions() exception caught: " +
+ e.toString());
+ } catch (Exception e) {
+ CMS.debug("EnrollService: ArchiveOptions(): " + e.toString());
+ throw new EBaseException("ArchiveOptions() exception caught: " +
+ e.toString());
+ }
+
+ }
+
+ static public ArchiveOptions toArchiveOptions(byte options[]) throws
+ EBaseException {
+ ByteArrayInputStream bis = new ByteArrayInputStream(options);
+ PKIArchiveOptions archOpts = null;
+
+ try {
+ archOpts = (PKIArchiveOptions)
+ (new PKIArchiveOptions.Template()).decode(bis);
+ } catch (Exception e) {
+ throw new EBaseException("Failed to decode input PKIArchiveOptions.");
+ }
+
+ return new ArchiveOptions(archOpts);
+
+ }
+
+ public String getSymmAlgOID() {
+ return mSymmAlgOID;
+ }
+
+ public byte[] getSymmAlgParams() {
+ return mSymmAlgParams;
+ }
+
+ public byte[] getEncSymmKey() {
+ return mEncSymmKey;
+ }
+
+ public byte[] getEncValue() {
+ return mEncValue;
+ }
+}
diff --git a/base/kra/src/com/netscape/kra/EncryptionUnit.java b/base/kra/src/com/netscape/kra/EncryptionUnit.java
new file mode 100644
index 000000000..946f57613
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/EncryptionUnit.java
@@ -0,0 +1,741 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.CharConversionException;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+
+import org.mozilla.jss.crypto.BadPaddingException;
+import org.mozilla.jss.crypto.Cipher;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.EncryptionAlgorithm;
+import org.mozilla.jss.crypto.IVParameterSpec;
+import org.mozilla.jss.crypto.IllegalBlockSizeException;
+import org.mozilla.jss.crypto.KeyGenAlgorithm;
+import org.mozilla.jss.crypto.KeyWrapAlgorithm;
+import org.mozilla.jss.crypto.KeyWrapper;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.TokenException;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.security.IEncryptionUnit;
+import com.netscape.cmscore.util.Debug;
+
+/**
+ * A class represents the transport key pair. This key pair
+ * is used to protected EE's private key in transit.
+ *
+ * @author thomask
+ * @version $Revision$, $Date$
+ */
+@SuppressWarnings("deprecation")
+public abstract class EncryptionUnit implements IEncryptionUnit {
+
+ /* Establish one constant IV for base class, to be used for
+ internal operations. Constant IV acceptable for symmetric keys.
+ */
+ private byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
+ protected IVParameterSpec IV = null;
+
+ public EncryptionUnit() {
+ CMS.debug("EncryptionUnit.EncryptionUnit this: " + this.toString());
+
+ IV = new IVParameterSpec(iv);
+ }
+
+ public abstract CryptoToken getToken();
+
+ public abstract CryptoToken getInternalToken();
+
+ public abstract PublicKey getPublicKey();
+
+ public abstract PrivateKey getPrivateKey();
+
+ /**
+ * Protects the private key so that it can be stored in
+ * internal database.
+ */
+ public byte[] encryptInternalPrivate(byte priKey[])
+ throws EBaseException {
+ try {
+ CMS.debug("EncryptionUnit.encryptInternalPrivate");
+ CryptoToken internalToken = getInternalToken();
+
+ // (1) generate session key
+ org.mozilla.jss.crypto.KeyGenerator kg =
+ internalToken.getKeyGenerator(KeyGenAlgorithm.DES3);
+ SymmetricKey sk = kg.generate();
+
+ // (2) wrap private key with session key
+ Cipher cipher = internalToken.getCipherContext(
+ EncryptionAlgorithm.DES3_CBC_PAD);
+
+ cipher.initEncrypt(sk, IV);
+ byte pri[] = cipher.doFinal(priKey);
+
+ // (3) wrap session with transport public
+ KeyWrapper rsaWrap = internalToken.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initWrap(getPublicKey(), null);
+ byte session[] = rsaWrap.wrap(sk);
+
+ // use MY own structure for now:
+ // SEQUENCE {
+ // encryptedSession OCTET STRING,
+ // encryptedPrivate OCTET STRING
+ // }
+
+ DerOutputStream tmp = new DerOutputStream();
+ DerOutputStream out = new DerOutputStream();
+
+ tmp.putOctetString(session);
+ tmp.putOctetString(pri);
+ out.write(DerValue.tag_Sequence, tmp);
+
+ return out.toByteArray();
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (CharConversionException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (BadPaddingException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (IllegalBlockSizeException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (IOException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ } catch (Exception e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_INTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::encryptInternalPrivate " + e.toString());
+ return null;
+ }
+ }
+
+ public byte[] wrap(PrivateKey privKey) throws EBaseException {
+ return _wrap(privKey,null);
+ }
+
+ public byte[] wrap(SymmetricKey symmKey) throws EBaseException {
+ return _wrap(null,symmKey);
+ }
+ /**
+ * External unwrapping. Unwraps the data using
+ * the transport private key.
+ */
+ public SymmetricKey unwrap_sym(byte encSymmKey[], SymmetricKey.Usage usage) {
+ try {
+ CryptoToken token = getToken();
+
+ // (1) unwrap the session
+ PrivateKey priKey = getPrivateKey();
+ String priKeyAlgo = priKey.getAlgorithm();
+ CMS.debug("EncryptionUnit::unwrap_sym() private key algo: " + priKeyAlgo);
+ KeyWrapper keyWrapper = null;
+ if (priKeyAlgo.equals("EC")) {
+ keyWrapper = token.getKeyWrapper(KeyWrapAlgorithm.AES_ECB);
+ keyWrapper.initUnwrap(priKey, null);
+ } else {
+ keyWrapper = token.getKeyWrapper(KeyWrapAlgorithm.RSA);
+ keyWrapper.initUnwrap(priKey, null);
+ }
+ SymmetricKey sk = keyWrapper.unwrapSymmetric(encSymmKey,
+ SymmetricKey.DES3, usage,
+ 0);
+ CMS.debug("EncryptionUnit::unwrap_sym() unwrapped on slot: "
+ + token.getName());
+ return sk;
+ } catch (Exception e) {
+ CMS.debug("EncryptionUnit::unwrap_sym() error:" +
+ e.toString());
+ return null;
+ }
+ }
+
+ public SymmetricKey unwrap_sym(byte encSymmKey[]) {
+ return unwrap_sym(encSymmKey, SymmetricKey.Usage.WRAP);
+ }
+
+ public SymmetricKey unwrap_encrypt_sym(byte encSymmKey[]) {
+ return unwrap_sym(encSymmKey, SymmetricKey.Usage.ENCRYPT);
+ }
+
+ /**
+ * Decrypts the user private key.
+ */
+ public byte[] decryptExternalPrivate(byte encSymmKey[],
+ String symmAlgOID, byte symmAlgParams[],
+ byte encValue[])
+ throws EBaseException {
+ try {
+
+ CMS.debug("EncryptionUnit.decryptExternalPrivate");
+ CryptoToken token = getToken();
+
+ // (1) unwrap the session
+ KeyWrapper rsaWrap = token.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initUnwrap(getPrivateKey(), null);
+ SymmetricKey sk = rsaWrap.unwrapSymmetric(encSymmKey,
+ SymmetricKey.DES3, SymmetricKey.Usage.DECRYPT,
+ 0);
+
+ // (2) unwrap the pri
+ Cipher cipher = token.getCipherContext(
+ EncryptionAlgorithm.DES3_CBC_PAD // XXX
+ );
+
+ cipher.initDecrypt(sk, new IVParameterSpec(
+ symmAlgParams));
+ return cipher.doFinal(encValue);
+ } catch (IllegalBlockSizeException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString());
+ return null;
+ } catch (BadPaddingException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString());
+ return null;
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString());
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString());
+ return null;
+ } catch (Exception e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_EXTERNAL", e.toString()));
+ Debug.trace("EncryptionUnit::decryptExternalPrivate " + e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * External unwrapping. Unwraps the symmetric key using
+ * the transport private key.
+ */
+ public SymmetricKey unwrap_symmetric(byte encSymmKey[],
+ String symmAlgOID, byte symmAlgParams[],
+ byte encValue[])
+ throws EBaseException {
+ try {
+ CryptoToken token = getToken();
+
+ // (1) unwrap the session
+ KeyWrapper rsaWrap = token.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initUnwrap(getPrivateKey(), null);
+ SymmetricKey sk = rsaWrap.unwrapSymmetric(encSymmKey,
+ SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP,
+ 0);
+
+ // (2) unwrap the sym key
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD // XXX
+ );
+
+ wrapper.initUnwrap(sk, new IVParameterSpec(
+ symmAlgParams));
+
+ SymmetricKey symKey = wrapper.unwrapSymmetric(encValue, SymmetricKey.DES3, SymmetricKey.Usage.DECRYPT, 0);
+
+ return symKey;
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (Exception e) {
+ CMS.debug("EncryptionUnit.unwrap : Exception:" + e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * External unwrapping. Unwraps the data using
+ * the transport private key.
+ */
+ public PrivateKey unwrap(byte encSymmKey[],
+ String symmAlgOID, byte symmAlgParams[],
+ byte encValue[], PublicKey pubKey)
+ throws EBaseException {
+ try {
+ CryptoToken token = getToken();
+
+ // (1) unwrap the session
+ KeyWrapper rsaWrap = token.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initUnwrap(getPrivateKey(), null);
+ SymmetricKey sk = rsaWrap.unwrapSymmetric(encSymmKey,
+ SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP,
+ 0);
+
+ // (2) unwrap the pri
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD // XXX
+ );
+
+ wrapper.initUnwrap(sk, new IVParameterSpec(
+ symmAlgParams));
+
+ PrivateKey.Type keytype = null;
+ String alg = pubKey.getAlgorithm();
+ if (alg.equals("DSA")) {
+ keytype = PrivateKey.DSA;
+ } else if (alg.equals("EC")) {
+ keytype = PrivateKey.EC;
+ } else {
+ keytype = PrivateKey.RSA;
+ }
+ PrivateKey pk = wrapper.unwrapTemporaryPrivate(encValue,
+ keytype , pubKey);
+
+ return pk;
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (Exception e) {
+ CMS.debug("EncryptionUnit.unwrap : Exception:"+e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * External unwrapping. Unwraps the data using
+ * the transport private key.
+ */
+
+ public byte[] decryptInternalPrivate(byte wrappedKeyData[])
+ throws EBaseException {
+ try {
+ CMS.debug("EncryptionUnit.decryptInternalPrivate");
+ DerValue val = new DerValue(wrappedKeyData);
+ // val.tag == DerValue.tag_Sequence
+ DerInputStream in = val.data;
+ DerValue dSession = in.getDerValue();
+ byte session[] = dSession.getOctetString();
+ DerValue dPri = in.getDerValue();
+ byte pri[] = dPri.getOctetString();
+
+ CryptoToken token = getToken();
+
+ // (1) unwrap the session
+ CMS.debug("decryptInternalPrivate(): getting key wrapper on slot:" + token.getName());
+ KeyWrapper rsaWrap = token.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initUnwrap(getPrivateKey(), null);
+ SymmetricKey sk = rsaWrap.unwrapSymmetric(session,
+ SymmetricKey.DES3, SymmetricKey.Usage.DECRYPT, 0);
+
+ // (2) unwrap the pri
+ Cipher cipher = token.getCipherContext(
+ EncryptionAlgorithm.DES3_CBC_PAD);
+
+ cipher.initDecrypt(sk, IV);
+ return cipher.doFinal(pri);
+ } catch (IllegalBlockSizeException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ } catch (BadPaddingException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ } catch (IOException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ } catch (Exception e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_DECRYPT", e.toString()));
+ Debug.trace("EncryptionUnit::decryptInternalPrivate " + e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * External unwrapping of stored symmetric key.
+ */
+ public SymmetricKey unwrap(byte wrappedKeyData[])
+ throws EBaseException {
+ try {
+ DerValue val = new DerValue(wrappedKeyData);
+ // val.tag == DerValue.tag_Sequence
+ DerInputStream in = val.data;
+ DerValue dSession = in.getDerValue();
+ byte session[] = dSession.getOctetString();
+ DerValue dPri = in.getDerValue();
+ byte pri[] = dPri.getOctetString();
+
+ CryptoToken token = getToken();
+ // (1) unwrap the session key
+ KeyWrapper rsaWrap = token.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initUnwrap(getPrivateKey(), null);
+ SymmetricKey sk = rsaWrap.unwrapSymmetric(session,
+ SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, 0);
+
+ // (2) unwrap the symmetric key
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD);
+
+ wrapper.initUnwrap(sk, IV);
+
+ SymmetricKey sk_ret = wrapper.unwrapSymmetric(pri,
+ SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP,
+ 0);
+
+ return sk_ret;
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ CMS.debug(e);
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.printStackTrace(e);
+ return null;
+ } catch (IOException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (Exception e) {
+ Debug.printStackTrace(e);
+ return null;
+ }
+ }
+
+ /**
+ * Internal unwrapping.
+ */
+ public PrivateKey unwrap_temp(byte wrappedKeyData[], PublicKey pubKey)
+ throws EBaseException {
+ return _unwrap(wrappedKeyData, pubKey, true);
+ }
+
+ /**
+ * Internal unwrapping.
+ */
+ public PrivateKey unwrap(byte wrappedKeyData[], PublicKey pubKey)
+ throws EBaseException {
+ return _unwrap(wrappedKeyData, pubKey, false);
+ }
+
+ /**
+ * Internal unwrapping.
+ */
+ private PrivateKey _unwrap(byte wrappedKeyData[], PublicKey
+ pubKey, boolean temporary)
+ throws EBaseException {
+ try {
+ DerValue val = new DerValue(wrappedKeyData);
+ // val.tag == DerValue.tag_Sequence
+ DerInputStream in = val.data;
+ DerValue dSession = in.getDerValue();
+ byte session[] = dSession.getOctetString();
+ DerValue dPri = in.getDerValue();
+ byte pri[] = dPri.getOctetString();
+
+ CryptoToken token = getToken();
+ // (1) unwrap the session
+ KeyWrapper rsaWrap = token.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initUnwrap(getPrivateKey(), null);
+ SymmetricKey sk = rsaWrap.unwrapSymmetric(session,
+ SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, 0);
+
+ // (2) unwrap the pri
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD);
+
+ wrapper.initUnwrap(sk, IV);
+
+ PrivateKey pk = null;
+ if (temporary) {
+ pk = wrapper.unwrapTemporaryPrivate(pri,
+ PrivateKey.RSA, pubKey);
+ } else {
+ pk = wrapper.unwrapPrivate(pri,
+ PrivateKey.RSA, pubKey);
+ }
+ return pk;
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ CMS.debug(e);
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.printStackTrace(e);
+ return null;
+ } catch (IOException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
+ Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ return null;
+ } catch (Exception e) {
+ Debug.printStackTrace(e);
+ return null;
+ }
+ }
+
+ /***
+ * Internal wrap, accounts for either private or symmetric key
+ */
+ private byte[] _wrap(PrivateKey priKey, SymmetricKey symmKey) throws EBaseException {
+ try {
+ if ((priKey == null && symmKey == null) || (priKey != null && symmKey != null)) {
+ return null;
+ }
+ CMS.debug("EncryptionUnit.wrap interal.");
+ CryptoToken token = getToken();
+
+ // (1) generate session key
+ org.mozilla.jss.crypto.KeyGenerator kg =
+ token.getKeyGenerator(KeyGenAlgorithm.DES3);
+ // internalToken.getKeyGenerator(KeyGenAlgorithm.DES3);
+ SymmetricKey.Usage usages[] = new SymmetricKey.Usage[2];
+ usages[0] = SymmetricKey.Usage.WRAP;
+ usages[1] = SymmetricKey.Usage.UNWRAP;
+ kg.setKeyUsages(usages);
+ kg.temporaryKeys(true);
+ SymmetricKey sk = kg.generate();
+ CMS.debug("EncryptionUnit:wrap() session key generated on slot: " + token.getName());
+
+ // (2) wrap private key with session key
+ // KeyWrapper wrapper = internalToken.getKeyWrapper(
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD);
+
+ wrapper.initWrap(sk, IV);
+
+ byte pri[] = null;
+
+ if ( priKey != null) {
+ pri = wrapper.wrap(priKey);
+ } else if ( symmKey != null) {
+ pri = wrapper.wrap(symmKey);
+ }
+ CMS.debug("EncryptionUnit:wrap() privKey wrapped");
+
+ // (3) wrap session with transport public
+ KeyWrapper rsaWrap = token.getKeyWrapper(
+ KeyWrapAlgorithm.RSA);
+
+ rsaWrap.initWrap(getPublicKey(), null);
+ byte session[] = rsaWrap.wrap(sk);
+ CMS.debug("EncryptionUnit:wrap() sessin key wrapped");
+
+ // use MY own structure for now:
+ // SEQUENCE {
+ // encryptedSession OCTET STRING,
+ // encryptedPrivate OCTET STRING
+ // }
+
+ DerOutputStream tmp = new DerOutputStream();
+ DerOutputStream out = new DerOutputStream();
+
+ tmp.putOctetString(session);
+ tmp.putOctetString(pri);
+ out.write(DerValue.tag_Sequence, tmp);
+
+ return out.toByteArray();
+ } catch (TokenException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString()));
+ Debug.trace("EncryptionUnit::wrap " + e.toString());
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString()));
+ Debug.trace("EncryptionUnit::wrap " + e.toString());
+ return null;
+ } catch (CharConversionException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString()));
+ Debug.trace("EncryptionUnit::wrap " + e.toString());
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString()));
+ Debug.trace("EncryptionUnit::wrap " + e.toString());
+ return null;
+ } catch (InvalidKeyException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString()));
+ Debug.trace("EncryptionUnit::wrap " + e.toString());
+ return null;
+ } catch (IOException e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString()));
+ Debug.trace("EncryptionUnit::wrap " + e.toString());
+ return null;
+ } catch (Exception e) {
+ CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_WRAP", e.toString()));
+ Debug.trace("EncryptionUnit::wrap " + e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * Verify the given key pair.
+ */
+ public void verify(PublicKey publicKey, PrivateKey privateKey) throws
+ EBaseException {
+ }
+}
diff --git a/base/kra/src/com/netscape/kra/EnrollmentService.java b/base/kra/src/com/netscape/kra/EnrollmentService.java
new file mode 100644
index 000000000..37d1aea53
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/EnrollmentService.java
@@ -0,0 +1,872 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.cert.CertificateException;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import netscape.security.provider.RSAPublicKey;
+import netscape.security.util.BigInt;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.x509.CertificateSubjectName;
+import netscape.security.x509.CertificateX509Key;
+import netscape.security.x509.X509CertInfo;
+import netscape.security.x509.X509Key;
+
+import org.mozilla.jss.asn1.ASN1Util;
+import org.mozilla.jss.asn1.ASN1Value;
+import org.mozilla.jss.asn1.InvalidBERException;
+import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
+import org.mozilla.jss.asn1.SEQUENCE;
+import org.mozilla.jss.pkix.crmf.CertReqMsg;
+import org.mozilla.jss.pkix.crmf.CertRequest;
+import org.mozilla.jss.pkix.crmf.PKIArchiveOptions;
+import org.mozilla.jss.pkix.primitive.AVA;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.AuthToken;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.certsrv.kra.EKRAException;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.kra.ProofOfArchival;
+import com.netscape.certsrv.logging.AuditFormat;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.profile.IEnrollProfile;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.IService;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.certsrv.security.ITransportKeyUnit;
+import com.netscape.certsrv.util.IStatsSubsystem;
+import com.netscape.cmscore.crmf.CRMFParser;
+import com.netscape.cmscore.crmf.PKIArchiveOptionsContainer;
+import com.netscape.kra.ArchiveOptions;
+import com.netscape.cmscore.dbs.KeyRecord;
+import com.netscape.cmsutil.util.Utils;
+
+/**
+ * A class represents archival request processor. It
+ * passes the request to the policy processor, and
+ * process the request according to the policy decision.
+ * <P>
+ * If policy returns ACCEPTED, the request will be processed immediately.
+ * <P>
+ * Upon processing, the incoming user key is unwrapped with the transport key of KRA, and then wrapped with the storage
+ * key. The encrypted key is stored in the internal database for long term storage.
+ * <P>
+ *
+ * @author thomask (original)
+ * @author cfu (non-RSA keys; private keys secure handling);
+ * @version $Revision$, $Date$
+ */
+public class EnrollmentService implements IService {
+
+ // constants
+ public static final String CRMF_REQUEST = "CRMFRequest";
+ public final static String ATTR_KEY_RECORD = "keyRecord";
+ public final static String ATTR_PROOF_OF_ARCHIVAL =
+ "proofOfArchival";
+
+ // private
+ private IKeyRecoveryAuthority mKRA = null;
+ private ITransportKeyUnit mTransportUnit = null;
+ private IStorageKeyUnit mStorageUnit = null;
+ private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger();
+
+ private final static byte EOL[] = { Character.LINE_SEPARATOR };
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_4";
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED_3";
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_4";
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_4";
+
+ /**
+ * Constructs request processor.
+ * <P>
+ *
+ * @param kra key recovery authority
+ */
+ public EnrollmentService(IKeyRecoveryAuthority kra) {
+ mKRA = kra;
+ mTransportUnit = kra.getTransportKeyUnit();
+ mStorageUnit = kra.getStorageKeyUnit();
+ }
+
+ public PKIArchiveOptions toPKIArchiveOptions(byte options[]) {
+ ByteArrayInputStream bis = new ByteArrayInputStream(options);
+ PKIArchiveOptions archOpts = null;
+
+ try {
+ archOpts = (PKIArchiveOptions)
+ (new PKIArchiveOptions.Template()).decode(bis);
+ } catch (Exception e) {
+ CMS.debug("EnrollProfile: getPKIArchiveOptions " + e.toString());
+ }
+ return archOpts;
+ }
+
+ /**
+ * Services an enrollment/archival request.
+ * <P>
+ *
+ * @param request enrollment request
+ * @return serving successful or not
+ * @exception EBaseException failed to serve
+ */
+ public boolean serviceRequest(IRequest request)
+ throws EBaseException {
+
+ IStatsSubsystem statsSub = (IStatsSubsystem) CMS.getSubsystem("stats");
+ if (statsSub != null) {
+ statsSub.startTiming("archival", true /* main action */);
+ }
+
+ String auditMessage = null;
+ String auditSubjectID = auditSubjectID();
+ String auditRequesterID = auditRequesterID();
+ String auditArchiveID = ILogger.UNIDENTIFIED;
+ String auditPublicKey = ILogger.UNIDENTIFIED;
+
+ String id = request.getRequestId().toString();
+ if (id != null) {
+ auditArchiveID = id.trim();
+ }
+ if (CMS.debugOn())
+ CMS.debug("EnrollmentServlet: KRA services enrollment request");
+
+ SessionContext sContext = SessionContext.getContext();
+ String agentId = (String) sContext.get(SessionContext.USER_ID);
+ AuthToken authToken = (AuthToken) sContext.get(SessionContext.AUTH_TOKEN);
+
+ mKRA.log(ILogger.LL_INFO, "KRA services enrollment request");
+ // unwrap user key with transport
+ byte unwrapped[] = null;
+ PKIArchiveOptionsContainer aOpts[] = null;
+
+ String profileId = request.getExtDataInString("profileId");
+
+ if (profileId == null || profileId.equals("")) {
+ try {
+ aOpts = CRMFParser.getPKIArchiveOptions(
+ request.getExtDataInString(IRequest.HTTP_PARAMS, CRMF_REQUEST));
+ } catch (IOException e) {
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PRIVATE_KEY"));
+ }
+ } else {
+ // profile-based request
+ PKIArchiveOptions options = (PKIArchiveOptions)
+ toPKIArchiveOptions(
+ request.getExtDataInByteArray(IEnrollProfile.REQUEST_ARCHIVE_OPTIONS));
+
+ aOpts = new PKIArchiveOptionsContainer[1];
+ aOpts[0] = new PKIArchiveOptionsContainer(options,
+ 0/* not matter */);
+
+ request.setExtData("dbStatus", "NOT_UPDATED");
+ }
+
+ for (int i = 0; i < aOpts.length; i++) {
+ ArchiveOptions opts = new ArchiveOptions(aOpts[i].mAO);
+
+ if (statsSub != null) {
+ statsSub.startTiming("decrypt_user_key");
+ }
+ mKRA.log(ILogger.LL_INFO, "KRA decrypts external private");
+ if (CMS.debugOn())
+ CMS.debug("EnrollmentService::about to decryptExternalPrivate");
+ unwrapped = mTransportUnit.decryptExternalPrivate(
+ opts.getEncSymmKey(),
+ opts.getSymmAlgOID(),
+ opts.getSymmAlgParams(),
+ opts.getEncValue());
+ if (statsSub != null) {
+ statsSub.endTiming("decrypt_user_key");
+ }
+ if (CMS.debugOn())
+ CMS.debug("EnrollmentService::finished decryptExternalPrivate");
+ if (unwrapped == null) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_UNWRAP_USER_KEY"));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PRIVATE_KEY"));
+ }
+
+ // retrieve pubic key
+ X509Key publicKey = getPublicKey(request, aOpts[i].mReqPos);
+ byte publicKeyData[] = publicKey.getEncoded();
+
+ if (publicKeyData == null) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND"));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY"));
+ }
+
+ /* Bugscape #54948 - verify public and private key before archiving key */
+
+ if (statsSub != null) {
+ statsSub.startTiming("verify_key");
+ }
+ if (verifyKeyPair(publicKeyData, unwrapped) == false) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND"));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY"));
+ }
+ if (statsSub != null) {
+ statsSub.endTiming("verify_key");
+ }
+
+ /**
+ * mTransportKeyUnit.verify(pKey, unwrapped);
+ **/
+ // retrieve owner name
+ String owner = getOwnerName(request, aOpts[i].mReqPos);
+
+ if (owner == null) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_OWNER_NAME_NOT_FOUND"));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_KEYRECORD"));
+ }
+
+ //
+ // privateKeyData ::= SEQUENCE {
+ // sessionKey OCTET_STRING,
+ // encKey OCTET_STRING,
+ // }
+ //
+ mKRA.log(ILogger.LL_INFO, "KRA encrypts internal private");
+ if (statsSub != null) {
+ statsSub.startTiming("encrypt_user_key");
+ }
+ byte privateKeyData[] = mStorageUnit.encryptInternalPrivate(
+ unwrapped);
+ if (statsSub != null) {
+ statsSub.endTiming("encrypt_user_key");
+ }
+
+ if (privateKeyData == null) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_WRAP_USER_KEY"));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PRIVATE_KEY"));
+ }
+
+ // create key record
+ KeyRecord rec = new KeyRecord(null, publicKeyData,
+ privateKeyData, owner,
+ publicKey.getAlgorithmId().getOID().toString(), agentId);
+
+ // we deal with RSA key only
+ try {
+ RSAPublicKey rsaPublicKey = new RSAPublicKey(publicKeyData);
+
+ rec.setKeySize(Integer.valueOf(rsaPublicKey.getKeySize()));
+ } catch (InvalidKeyException e) {
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_KEYRECORD"));
+ }
+
+ // if record alreay has a serial number, yell out.
+ if (rec.getSerialNumber() != null) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_INVALID_SERIAL_NUMBER",
+ rec.getSerialNumber().toString()));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_STATE"));
+ }
+ IKeyRepository storage = mKRA.getKeyRepository();
+ BigInteger serialNo = storage.getNextSerialNumber();
+
+ if (serialNo == null) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_GET_NEXT_SERIAL"));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_STATE"));
+ }
+ if (i == 0) {
+ rec.set(KeyRecord.ATTR_ID, serialNo);
+ request.setExtData(ATTR_KEY_RECORD, serialNo);
+ } else {
+ rec.set(KeyRecord.ATTR_ID + i, serialNo);
+ request.setExtData(ATTR_KEY_RECORD + i, serialNo);
+ }
+
+ mKRA.log(ILogger.LL_INFO, "KRA adding key record " + serialNo);
+ if (statsSub != null) {
+ statsSub.startTiming("store_key");
+ }
+ storage.addKeyRecord(rec);
+ if (statsSub != null) {
+ statsSub.endTiming("store_key");
+ }
+
+ if (CMS.debugOn())
+ CMS.debug("EnrollmentService: key record 0x" + serialNo.toString(16)
+ + " (" + owner + ") archived");
+
+ mKRA.log(ILogger.LL_INFO, "key record 0x" +
+ serialNo.toString(16)
+ + " (" + owner + ") archived");
+
+ // for audit log
+ String authMgr = AuditFormat.NOAUTH;
+
+ if (authToken != null) {
+ authMgr =
+ authToken.getInString(AuthToken.TOKEN_AUTHMGR_INST_NAME);
+ }
+ CMS.getLogger().log(ILogger.EV_AUDIT,
+ ILogger.S_KRA,
+ AuditFormat.LEVEL,
+ AuditFormat.FORMAT,
+ new Object[] {
+ IRequest.KEYARCHIVAL_REQUEST,
+ request.getRequestId(),
+ AuditFormat.FROMAGENT + " agentID: " + agentId,
+ authMgr,
+ "completed",
+ owner,
+ "serial number: 0x" + serialNo.toString(16) }
+ );
+
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+
+ // store a message in the signed audit log file
+ auditPublicKey = auditPublicKey(rec);
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditPublicKey);
+
+ audit(auditMessage);
+
+ // Xxx - should sign this proof of archival
+ ProofOfArchival mProof = new ProofOfArchival(serialNo,
+ owner, mKRA.getX500Name().toString(),
+ rec.getCreateTime());
+
+ DerOutputStream mProofOut = new DerOutputStream();
+ mProof.encode(mProofOut);
+ if (i == 0) {
+ request.setExtData(ATTR_PROOF_OF_ARCHIVAL,
+ mProofOut.toByteArray());
+ } else {
+ request.setExtData(ATTR_PROOF_OF_ARCHIVAL + i,
+ mProofOut.toByteArray());
+ }
+
+ } // for
+
+ /*
+ request.delete(IEnrollProfile.REQUEST_SUBJECT_NAME);
+ request.delete(IEnrollProfile.REQUEST_EXTENSIONS);
+ request.delete(IEnrollProfile.REQUEST_VALIDITY);
+ request.delete(IEnrollProfile.REQUEST_KEY);
+ request.delete(IEnrollProfile.REQUEST_SIGNING_ALGORITHM);
+ request.delete(IEnrollProfile.REQUEST_LOCALE);
+ */
+
+ request.setExtData(IRequest.RESULT, IRequest.RES_SUCCESS);
+
+ // update request
+ mKRA.log(ILogger.LL_INFO, "KRA updating request");
+ mKRA.getRequestQueue().updateRequest(request);
+
+ if (statsSub != null) {
+ statsSub.endTiming("archival");
+ }
+
+ return true;
+ }
+
+ public boolean verifyKeyPair(byte publicKeyData[], byte privateKeyData[]) {
+ try {
+ DerValue publicKeyVal = new DerValue(publicKeyData);
+ DerInputStream publicKeyIn = publicKeyVal.data;
+ publicKeyIn.getSequence(0);
+ DerValue publicKeyDer = new DerValue(publicKeyIn.getBitString());
+ DerInputStream publicKeyDerIn = publicKeyDer.data;
+ BigInt publicKeyModulus = publicKeyDerIn.getInteger();
+ BigInt publicKeyExponent = publicKeyDerIn.getInteger();
+
+ DerValue privateKeyVal = new DerValue(privateKeyData);
+ if (privateKeyVal.tag != DerValue.tag_Sequence)
+ return false;
+ DerInputStream privateKeyIn = privateKeyVal.data;
+ privateKeyIn.getInteger();
+ privateKeyIn.getSequence(0);
+ DerValue privateKeyDer = new DerValue(privateKeyIn.getOctetString());
+ DerInputStream privateKeyDerIn = privateKeyDer.data;
+
+ @SuppressWarnings("unused")
+ BigInt privateKeyVersion = privateKeyDerIn.getInteger();
+ BigInt privateKeyModulus = privateKeyDerIn.getInteger();
+ BigInt privateKeyExponent = privateKeyDerIn.getInteger();
+
+ if (!publicKeyModulus.equals(privateKeyModulus)) {
+ CMS.debug("verifyKeyPair modulus mismatch publicKeyModulus="
+ + publicKeyModulus + " privateKeyModulus=" + privateKeyModulus);
+ return false;
+ }
+
+ if (!publicKeyExponent.equals(privateKeyExponent)) {
+ CMS.debug("verifyKeyPair exponent mismatch publicKeyExponent="
+ + publicKeyExponent + " privateKeyExponent=" + privateKeyExponent);
+ return false;
+ }
+
+ return true;
+ } catch (Exception e) {
+ CMS.debug("verifyKeyPair error " + e);
+ return false;
+ }
+ }
+
+ private static final OBJECT_IDENTIFIER PKIARCHIVEOPTIONS_OID =
+ new OBJECT_IDENTIFIER(new long[] { 1, 3, 6, 1, 5, 5, 7, 5, 1, 4 }
+ );
+
+ /**
+ * Retrieves PKIArchiveOptions from CRMF request.
+ *
+ * @param crmfBlob CRMF request
+ * @return PKIArchiveOptions
+ * @exception EBaseException failed to extrace option
+ */
+ public static PKIArchiveOptionsContainer[] getPKIArchiveOptions(String crmfBlob)
+ throws EBaseException {
+ Vector<PKIArchiveOptionsContainer> options = new Vector<PKIArchiveOptionsContainer>();
+
+ if (CMS.debugOn())
+ CMS.debug("EnrollmentService::getPKIArchiveOptions> crmfBlob=" + crmfBlob);
+ byte[] crmfBerBlob = null;
+
+ crmfBerBlob = Utils.base64decode(crmfBlob);
+ ByteArrayInputStream crmfBerBlobIn = new
+ ByteArrayInputStream(crmfBerBlob);
+ SEQUENCE crmfmsgs = null;
+
+ try {
+ crmfmsgs = (SEQUENCE) new
+ SEQUENCE.OF_Template(new
+ CertReqMsg.Template()).decode(
+ crmfBerBlobIn);
+ } catch (IOException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[crmf msgs]" + e.toString()));
+ } catch (InvalidBERException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "[crmf msgs]" + e.toString()));
+ }
+
+ for (int z = 0; z < crmfmsgs.size(); z++) {
+ CertReqMsg certReqMsg = (CertReqMsg)
+ crmfmsgs.elementAt(z);
+ CertRequest certReq = certReqMsg.getCertReq();
+
+ // try to locate PKIArchiveOption control
+ AVA archAva = null;
+
+ try {
+ for (int i = 0; i < certReq.numControls(); i++) {
+ AVA ava = certReq.controlAt(i);
+ OBJECT_IDENTIFIER oid = ava.getOID();
+
+ if (oid.equals(PKIARCHIVEOPTIONS_OID)) {
+ archAva = ava;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "no PKIArchiveOptions found "
+ + e.toString()));
+ }
+ if (archAva != null) {
+
+ ASN1Value archVal = archAva.getValue();
+ ByteArrayInputStream bis = new ByteArrayInputStream(ASN1Util.encode(archVal));
+ PKIArchiveOptions archOpts = null;
+
+ try {
+ archOpts = (PKIArchiveOptions)
+ (new PKIArchiveOptions.Template()).decode(bis);
+ } catch (IOException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE",
+ "[PKIArchiveOptions]" + e.toString()));
+ } catch (InvalidBERException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE",
+ "[PKIArchiveOptions]" + e.toString()));
+ }
+ options.addElement(new PKIArchiveOptionsContainer(archOpts, z));
+ }
+ }
+ if (options.size() == 0) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "PKIArchiveOptions found"));
+ } else {
+ PKIArchiveOptionsContainer p[] = new PKIArchiveOptionsContainer[options.size()];
+
+ options.copyInto(p);
+ return p;
+ }
+ }
+
+ /**
+ * Retrieves public key from request.
+ *
+ * @param request CRMF request
+ * @return JSS public key
+ * @exception EBaseException failed to retrieve public key
+ */
+ private X509Key getPublicKey(IRequest request, int i) throws EBaseException {
+ String profileId = request.getExtDataInString("profileId");
+
+ if (profileId != null && !profileId.equals("")) {
+ byte[] certKeyData = request.getExtDataInByteArray(IEnrollProfile.REQUEST_KEY);
+ if (certKeyData != null) {
+ try {
+ CertificateX509Key x509key = new CertificateX509Key(
+ new ByteArrayInputStream(certKeyData));
+
+ return (X509Key) x509key.get(CertificateX509Key.KEY);
+ } catch (Exception e1) {
+ CMS.debug("EnrollService: (Archival) getPublicKey " +
+ e1.toString());
+ }
+ }
+ return null;
+ }
+
+ // retrieve x509 Key from request
+ X509CertInfo certInfo[] =
+ request.getExtDataInCertInfoArray(IRequest.CERT_INFO);
+ CertificateX509Key pX509Key = null;
+
+ try {
+ pX509Key = (CertificateX509Key)
+ certInfo[i].get(X509CertInfo.KEY);
+ } catch (IOException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_GET_PUBLIC_KEY", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE",
+ "[" + X509CertInfo.KEY + "]" + e.toString()));
+ } catch (CertificateException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_GET_PUBLIC_KEY", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE",
+ "[" + X509CertInfo.KEY + "]" + e.toString()));
+ }
+ X509Key pKey = null;
+
+ try {
+ pKey = (X509Key) pX509Key.get(
+ CertificateX509Key.KEY);
+ } catch (IOException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_GET_PUBLIC_KEY", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "["
+ + CertificateX509Key.KEY + "]" + e.toString()));
+ }
+ return pKey;
+ }
+
+ /**
+ * Retrieves key's owner name from request.
+ *
+ * @param request CRMF request
+ * @return owner name (subject name)
+ * @exception EBaseException failed to retrieve public key
+ */
+ private String getOwnerName(IRequest request, int i)
+ throws EBaseException {
+
+ String profileId = request.getExtDataInString("profileId");
+
+ if (profileId != null && !profileId.equals("")) {
+ CertificateSubjectName sub = request.getExtDataInCertSubjectName(
+ IEnrollProfile.REQUEST_SUBJECT_NAME);
+ if (sub != null) {
+ return sub.toString();
+ }
+ }
+
+ X509CertInfo certInfo[] =
+ request.getExtDataInCertInfoArray(IRequest.CERT_INFO);
+ CertificateSubjectName pSub = null;
+
+ try {
+ pSub = (CertificateSubjectName)
+ certInfo[0].get(X509CertInfo.SUBJECT);
+ } catch (IOException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_GET_OWNER_NAME", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "["
+ + X509CertInfo.SUBJECT + "]" + e.toString()));
+ } catch (CertificateException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_GET_OWNER_NAME", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "["
+ + X509CertInfo.SUBJECT + "]" + e.toString()));
+ }
+ String owner = pSub.toString();
+
+ return owner;
+ }
+
+ /**
+ * Signed Audit Log Public Key
+ *
+ * This method is called to obtain the public key from the passed in
+ * "KeyRecord" for a signed audit log message.
+ * <P>
+ *
+ * @param rec a Key Record
+ * @return key string containing the certificate's public key
+ */
+ private String auditPublicKey(KeyRecord rec) {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ if (rec == null) {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ }
+
+ byte rawData[] = null;
+
+ try {
+ rawData = rec.getPublicKeyData();
+ } catch (EBaseException e) {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ }
+
+ String key = "";
+
+ // convert "rawData" into "base64Data"
+ if (rawData != null) {
+ String base64Data = null;
+
+ base64Data = CMS.BtoA(rawData).trim();
+
+ // extract all line separators from the "base64Data"
+ StringTokenizer st = new StringTokenizer(base64Data, "\r\n");
+ while (st.hasMoreTokens()) {
+ key += st.nextToken();
+ }
+ }
+
+ key = key.trim();
+
+ if (key.equals("")) {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ } else {
+ return key;
+ }
+ }
+
+ /**
+ * Signed Audit Log Subject ID
+ *
+ * This method is called to obtain the "SubjectID" for
+ * a signed audit log message.
+ * <P>
+ *
+ * @return id string containing the signed audit log message SubjectID
+ */
+
+ private String auditSubjectID() {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ String subjectID = null;
+
+ // Initialize subjectID
+ SessionContext auditContext = SessionContext.getExistingContext();
+
+ if (auditContext != null) {
+ subjectID = (String)
+ auditContext.get(SessionContext.USER_ID);
+
+ if (subjectID != null) {
+ subjectID = subjectID.trim();
+ } else {
+ subjectID = ILogger.NONROLEUSER;
+ }
+ } else {
+ subjectID = ILogger.UNIDENTIFIED;
+ }
+
+ return subjectID;
+ }
+
+ /**
+ * Signed Audit Log Requester ID
+ *
+ * This method is called to obtain the "RequesterID" for
+ * a signed audit log message.
+ * <P>
+ *
+ * @return id string containing the signed audit log message RequesterID
+ */
+ private String auditRequesterID() {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ String requesterID = null;
+
+ // Initialize requesterID
+ SessionContext auditContext = SessionContext.getExistingContext();
+
+ if (auditContext != null) {
+ requesterID = (String)
+ auditContext.get(SessionContext.REQUESTER_ID);
+
+ if (requesterID != null) {
+ requesterID = requesterID.trim();
+ } else {
+ requesterID = ILogger.UNIDENTIFIED;
+ }
+ } else {
+ requesterID = ILogger.UNIDENTIFIED;
+ }
+
+ return requesterID;
+ }
+
+ /**
+ * Signed Audit Log
+ *
+ * This method is called to store messages to the signed audit log.
+ * <P>
+ *
+ * @param msg signed audit log message
+ */
+ private void audit(String msg) {
+ // in this case, do NOT strip preceding/trailing whitespace
+ // from passed-in String parameters
+
+ if (mSignedAuditLogger == null) {
+ return;
+ }
+
+ mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT,
+ null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ msg);
+ }
+}
diff --git a/base/kra/src/com/netscape/kra/KRANotify.java b/base/kra/src/com/netscape/kra/KRANotify.java
new file mode 100644
index 000000000..29eaf477a
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/KRANotify.java
@@ -0,0 +1,50 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.request.ARequestNotifier;
+
+/**
+ * A class represents a KRA request queue notify. This
+ * object will be invoked by the request subsystem
+ * when a request is requested for processing.
+ *
+ * @author thomask
+ * @version $Revision$, $Date$
+ */
+public class KRANotify extends ARequestNotifier {
+ private IKeyRecoveryAuthority mKRA = null;
+
+ /**
+ * default constructor
+ */
+ public KRANotify() {
+ super();
+ }
+
+ /**
+ * Creates KRA notify.
+ */
+ public KRANotify(IKeyRecoveryAuthority kra) {
+ super();
+ mKRA = kra;
+ }
+
+ // XXX may want to do something else with mKRA ?
+}
diff --git a/base/kra/src/com/netscape/kra/KRAPolicy.java b/base/kra/src/com/netscape/kra/KRAPolicy.java
new file mode 100644
index 000000000..aa2b2c2de
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/KRAPolicy.java
@@ -0,0 +1,78 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.ISubsystem;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.policy.IPolicyProcessor;
+import com.netscape.certsrv.request.IPolicy;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.PolicyResult;
+import com.netscape.cmscore.policy.GenericPolicyProcessor;
+import com.netscape.cmscore.util.Debug;
+
+/**
+ * KRA Policy.
+ *
+ * @deprecated
+ * @version $Revision$, $Date$
+ */
+public class KRAPolicy implements IPolicy {
+ IConfigStore mConfig = null;
+ IKeyRecoveryAuthority mKRA = null;
+
+ public GenericPolicyProcessor mPolicies = new GenericPolicyProcessor(false);
+
+ public KRAPolicy() {
+ }
+
+ public void init(ISubsystem owner, IConfigStore config)
+ throws EBaseException {
+ mKRA = (IKeyRecoveryAuthority) owner;
+ mConfig = config;
+ mPolicies.init(mKRA, mConfig);
+ }
+
+ public IPolicyProcessor getPolicyProcessor() {
+ return mPolicies;
+ }
+
+ /**
+ */
+ public PolicyResult apply(IRequest r) {
+ if (Debug.ON)
+ Debug.trace("KRA applies policies");
+ mKRA.log(ILogger.LL_INFO, "KRA applies policies");
+ PolicyResult result = mPolicies.apply(r);
+
+ if (result.equals(PolicyResult.DEFERRED)) {
+ // For KRA request, there is deferred
+ if (Debug.ON)
+ Debug.trace("KRA policies return DEFERRED");
+ return PolicyResult.REJECTED;
+ } else {
+ if (Debug.ON)
+ Debug.trace("KRA policies return ACCEPTED");
+ return mPolicies.apply(r);
+ }
+ }
+
+}
diff --git a/base/kra/src/com/netscape/kra/KRAService.java b/base/kra/src/com/netscape/kra/KRAService.java
new file mode 100644
index 000000000..4858e286a
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/KRAService.java
@@ -0,0 +1,101 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.util.Hashtable;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.IService;
+import com.netscape.cmscore.util.Debug;
+
+/**
+ * A class represents a KRA request queue service. This
+ * is the service object that is registered with
+ * the request queue. And it acts as a broker to
+ * distribute request into different KRA specific
+ * services. This service registration allows us to support
+ * new request easier.
+ * <P>
+ *
+ * @author thomask
+ * @version $Revision$, $Date$
+ */
+public class KRAService implements IService {
+
+ public final static String ENROLLMENT =
+ IRequest.ENROLLMENT_REQUEST;
+ public final static String RECOVERY = IRequest.KEYRECOVERY_REQUEST;
+ public final static String NETKEY_KEYGEN = IRequest.NETKEY_KEYGEN_REQUEST;
+ public final static String NETKEY_KEYRECOVERY = IRequest.NETKEY_KEYRECOVERY_REQUEST;
+ public final static String SECURITY_DATA_ENROLLMENT = IRequest.SECURITY_DATA_ENROLLMENT_REQUEST;
+ public final static String SECURITY_DATA_RECOVERY = IRequest.SECURITY_DATA_RECOVERY_REQUEST;
+
+
+ // private variables
+ private IKeyRecoveryAuthority mKRA = null;
+ private Hashtable<String, IService> mServices = new Hashtable<String, IService>();
+
+ /**
+ * Constructs KRA service.
+ */
+ public KRAService(IKeyRecoveryAuthority kra) {
+ mKRA = kra;
+ mServices.put(ENROLLMENT, new EnrollmentService(kra));
+ mServices.put(RECOVERY, new RecoveryService(kra));
+ mServices.put(NETKEY_KEYGEN, new NetkeyKeygenService(kra));
+ mServices.put(NETKEY_KEYRECOVERY, new TokenKeyRecoveryService(kra));
+ mServices.put(SECURITY_DATA_ENROLLMENT, new SecurityDataService(kra));
+ mServices.put(SECURITY_DATA_RECOVERY, new SecurityDataRecoveryService(kra));
+ }
+
+ /**
+ * Processes a KRA request. This method is invoked by
+ * request subsystem.
+ *
+ * @param r request from request subsystem
+ * @exception EBaseException failed to serve
+ */
+ public boolean serviceRequest(IRequest r) throws EBaseException {
+ if (Debug.ON)
+ Debug.trace("KRA services request " +
+ r.getRequestId().toString());
+ mKRA.log(ILogger.LL_INFO, "KRA services request " +
+ r.getRequestId().toString());
+ IService s = mServices.get(r.getRequestType());
+
+ if (s == null) {
+ r.setExtData(IRequest.RESULT, IRequest.RES_ERROR);
+ r.setExtData(IRequest.ERROR, new EBaseException(
+ CMS.getUserMessage("CMS_BASE_INVALID_OPERATION")));
+ return true;
+ }
+ try {
+ return s.serviceRequest(r);
+ } catch (EBaseException e) {
+ r.setExtData(IRequest.RESULT, IRequest.RES_ERROR);
+ r.setExtData(IRequest.ERROR, e);
+ // return true;
+ // #546508
+ return false;
+ }
+ }
+}
diff --git a/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java b/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
new file mode 100644
index 000000000..8d8cafb84
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
@@ -0,0 +1,1785 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import netscape.security.util.DerOutputStream;
+import netscape.security.x509.CertificateChain;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CertImpl;
+
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.NoSuchTokenException;
+import org.mozilla.jss.crypto.CryptoToken;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authority.IAuthority;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.EPropertyNotFound;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.ISubsystem;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.dbs.IDBSubsystem;
+import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.certsrv.dbs.replicadb.IReplicaIDRepository;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.kra.IKeyService;
+import com.netscape.certsrv.listeners.EListenersException;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.policy.IPolicyProcessor;
+import com.netscape.certsrv.request.ARequestNotifier;
+import com.netscape.certsrv.request.IPolicy;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.IRequestListener;
+import com.netscape.certsrv.request.IRequestNotifier;
+import com.netscape.certsrv.request.IRequestQueue;
+import com.netscape.certsrv.request.IRequestScheduler;
+import com.netscape.certsrv.request.IRequestSubsystem;
+import com.netscape.certsrv.request.IService;
+import com.netscape.certsrv.request.RequestId;
+import com.netscape.certsrv.request.RequestStatus;
+import com.netscape.certsrv.security.Credential;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.certsrv.security.ITransportKeyUnit;
+import com.netscape.certsrv.usrgrp.IUGSubsystem;
+import com.netscape.cmscore.dbs.DBSubsystem;
+import com.netscape.cmscore.dbs.KeyRecord;
+import com.netscape.cmscore.dbs.KeyRepository;
+import com.netscape.cmscore.dbs.ReplicaIDRepository;
+import com.netscape.cmscore.request.RequestSubsystem;
+
+/**
+ * A class represents an key recovery authority (KRA). A KRA
+ * is responsible to maintain key pairs that have been
+ * escrowed. It provides archive and recovery key pairs
+ * functionalities.
+ * <P>
+ *
+ * @author thomask
+ * @version $Revision$, $Date$
+ */
+public class KeyRecoveryAuthority implements IAuthority, IKeyService, IKeyRecoveryAuthority {
+
+ public final static String OFFICIAL_NAME = "Data Recovery Manager";
+
+ /**
+ * Internal Constants
+ */
+
+ private static final String PR_INTERNAL_TOKEN_NAME = "internal";
+ private static final String PARAM_CREDS = "creds";
+ private static final String PARAM_LOCK = "lock";
+ private static final String PARAM_PK12 = "pk12";
+ private static final String PARAM_ERROR = "error";
+ private static final String PARAM_AGENT = "agent";
+
+ private final static String KEY_RESP_NAME = "keyRepository";
+ private static final String PROP_REPLICAID_DN = "dbs.replicadn";
+
+ protected boolean mInitialized = false;
+ protected IConfigStore mConfig = null;
+ protected ILogger mLogger = CMS.getLogger();
+ protected KRAPolicy mPolicy = null;
+ protected X500Name mName = null;
+ protected boolean mQueueRequests = false;
+ protected String mId = null;
+ protected IRequestQueue mRequestQueue = null;
+ protected TransportKeyUnit mTransportKeyUnit = null;
+ protected StorageKeyUnit mStorageKeyUnit = null;
+ protected Hashtable<String, Credential[]> mAutoRecovery = new Hashtable<String, Credential[]>();
+ protected boolean mAutoRecoveryOn = false;
+ protected KeyRepository mKeyDB = null;
+ protected ReplicaIDRepository mReplicaRepot = null;
+ protected IRequestNotifier mNotify = null;
+ protected IRequestNotifier mPNotify = null;
+ protected ISubsystem mOwner = null;
+ protected int mRecoveryIDCounter = 0;
+ protected Hashtable<String, Hashtable<String, Object>> mRecoveryParams =
+ new Hashtable<String, Hashtable<String, Object>>();
+ protected org.mozilla.jss.crypto.X509Certificate mJssCert = null;
+ protected CryptoToken mKeygenToken = null;
+
+ // holds the number of bits of entropy to collect for each keygen
+ private int mEntropyBitsPerKeyPair = 0;
+
+ // the number of milliseconds which it is acceptable to block while
+ // getting entropy - anything longer will cause a warning.
+ // 0 means this warning is disabled
+ private int mEntropyBlockWarnMilliseconds = 0;
+
+ // for the notification listener
+ public IRequestListener mReqInQListener = null;
+
+ private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger();
+ private final static byte EOL[] = { Character.LINE_SEPARATOR };
+ private final static String SIGNED_AUDIT_AGENT_DELIMITER = ", ";
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_4";
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED_3";
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_4";
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_ASYNC =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_ASYNC_4";
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_4";
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_ASYNC =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_ASYNC_4";
+
+ /**
+ * Constructs an escrow authority.
+ * <P>
+ */
+ public KeyRecoveryAuthority() {
+ super();
+ }
+
+ /**
+ * Retrieves subsystem identifier.
+ *
+ * @return subsystem id
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Sets subsystem identifier.
+ *
+ * @param id subsystem id
+ * @exception EBaseException failed to set id
+ */
+ public void setId(String id) throws EBaseException {
+ mId = id;
+ }
+
+ /**
+ * @deprecated
+ */
+ public IPolicyProcessor getPolicyProcessor() {
+ return mPolicy.getPolicyProcessor();
+ }
+
+ // initialize entropy collection parameters
+ private void initEntropy(IConfigStore config) {
+ mEntropyBitsPerKeyPair = 0;
+ mEntropyBlockWarnMilliseconds = 50;
+ // initialize entropy collection
+ IConfigStore ecs = config.getSubStore("entropy");
+ if (ecs != null) {
+ try {
+ mEntropyBitsPerKeyPair = ecs.getInteger("bitsperkeypair", 0);
+ mEntropyBlockWarnMilliseconds = ecs.getInteger("blockwarnms", 50);
+ } catch (EBaseException eb) {
+ // ok - we deal with missing parameters above
+ }
+ }
+ CMS.debug("KeyRecoveryAuthority Entropy bits = " + mEntropyBitsPerKeyPair);
+ if (mEntropyBitsPerKeyPair == 0) {
+ //log(ILogger.LL_INFO,
+ //CMS.getLogMessage("CMSCORE_KRA_ENTROPY_COLLECTION_DISABLED"));
+ } else {
+ //log(ILogger.LL_INFO,
+ //CMS.getLogMessage("CMSCORE_KRA_ENTROPY_COLLECTION_ENABLED"));
+ CMS.debug("KeyRecoveryAuthority about to add Entropy");
+ addEntropy(false);
+ CMS.debug("KeyRecoveryAuthority back from add Entropy");
+ }
+
+ }
+
+ public void addEntropy(boolean logflag) {
+ CMS.debug("KeyRecoveryAuthority addEntropy()");
+ if (mEntropyBitsPerKeyPair == 0) {
+ CMS.debug("KeyRecoveryAuthority returning - disabled()");
+ return;
+ }
+ long start = System.currentTimeMillis();
+ try {
+ com.netscape.cmscore.security.JssSubsystem.getInstance().
+ addEntropy(mEntropyBitsPerKeyPair);
+ } catch (Exception e) {
+ CMS.debug("KeyRecoveryAuthority returning - error - see log file");
+ CMS.debug("exception: " + e.getMessage());
+ CMS.debug(e);
+ if (logflag) {
+ log(ILogger.LL_INFO,
+ CMS.getLogMessage("CMSCORE_KRA_ENTROPY_ERROR",
+ e.getMessage()));
+ }
+ }
+ long end = System.currentTimeMillis();
+ long duration = end - start;
+
+ if (mEntropyBlockWarnMilliseconds > 0 &&
+ duration > mEntropyBlockWarnMilliseconds) {
+
+ CMS.debug("KeyRecoveryAuthority returning - warning - entropy took too long (ms=" +
+ duration + ")");
+ if (logflag) {
+ log(ILogger.LL_INFO,
+ CMS.getLogMessage("CMSCORE_KRA_ENTROPY_BLOCKED_WARNING",
+ "" + (int) duration));
+ }
+ }
+ CMS.debug("KeyRecoveryAuthority returning ");
+ }
+
+ /**
+ * Starts this subsystem. It loads and initializes all
+ * necessary components. This subsystem is started by
+ * KRASubsystem.
+ * <P>
+ *
+ * @param owner owner of this subsystem
+ * @param config configuration store for this subsystem
+ * @exception EBaseException failed to start subsystem
+ */
+ public void init(ISubsystem owner, IConfigStore config)
+ throws EBaseException {
+ CMS.debug("KeyRecoveryAuthority init() begins");
+ if (mInitialized)
+ return;
+
+ mConfig = config;
+ mOwner = owner;
+
+ // initialize policy processor
+ mPolicy = new KRAPolicy();
+ mPolicy.init(this, mConfig.getSubStore(PROP_POLICY));
+
+ // create key repository
+ int keydb_inc = mConfig.getInteger(PROP_KEYDB_INC, 5);
+
+ mKeyDB = new KeyRepository(getDBSubsystem(),
+ keydb_inc,
+ "ou=" + KEY_RESP_NAME + ",ou=" +
+ getId() + "," +
+ getDBSubsystem().getBaseDN());
+
+ // read transport key from internal database
+ mTransportKeyUnit = new TransportKeyUnit();
+ try {
+ mTransportKeyUnit.init(this, mConfig.getSubStore(
+ PROP_TRANSPORT_KEY));
+ } catch (EBaseException e) {
+ CMS.debug("KeyRecoveryAuthority: transport unit exception " + e.toString());
+ //XXX throw e;
+ return;
+ }
+
+ // retrieve the authority name from transport cert
+ try {
+ mJssCert = mTransportKeyUnit.getCertificate();
+ X509CertImpl certImpl = new
+ X509CertImpl(mJssCert.getEncoded());
+
+ mName = (X500Name) certImpl.getSubjectDN();
+ } catch (CertificateEncodingException e) {
+ CMS.debug("KeyRecoveryAuthority: " + e.toString());
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_LOAD_FAILED",
+ "transport cert " + e.toString()));
+ } catch (CertificateException e) {
+ CMS.debug("KeyRecoveryAuthority: " + e.toString());
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_LOAD_FAILED",
+ "transport cert " + e.toString()));
+ }
+
+ // read transport key from storage key
+ mStorageKeyUnit = new StorageKeyUnit();
+ try {
+ mStorageKeyUnit.init(this,
+ mConfig.getSubStore(PROP_STORAGE_KEY));
+ } catch (EBaseException e) {
+ CMS.debug("KeyRecoveryAuthority: storage unit exception " + e.toString());
+ throw e;
+ }
+
+ // setup token for server-side key generation for user enrollments
+ String serverKeygenTokenName = mConfig.getString("serverKeygenTokenName", null);
+ if (serverKeygenTokenName == null) {
+ CMS.debug("serverKeygenTokenName set to nothing");
+ if (mStorageKeyUnit.getToken() != null) {
+ try {
+ String storageToken = mStorageKeyUnit.getToken().getName();
+ if (!storageToken.equals("internal")) {
+ CMS.debug("Auto set serverKeygenTokenName to " + storageToken);
+ serverKeygenTokenName = storageToken;
+ }
+ } catch (Exception e) {
+ }
+ }
+ }
+ if (serverKeygenTokenName == null) {
+ serverKeygenTokenName = "internal";
+ }
+ if (serverKeygenTokenName.equalsIgnoreCase(PR_INTERNAL_TOKEN_NAME))
+ serverKeygenTokenName = PR_INTERNAL_TOKEN_NAME;
+
+ try {
+ if (serverKeygenTokenName.equalsIgnoreCase(PR_INTERNAL_TOKEN_NAME)) {
+ CMS.debug("KeyRecoveryAuthority: getting internal crypto token for serverkeygen");
+ mKeygenToken = CryptoManager.getInstance().getInternalKeyStorageToken();
+ } else {
+ CMS.debug("KeyRecoveryAuthority: getting HSM token for serverkeygen");
+ mKeygenToken = CryptoManager.getInstance().getTokenByName(serverKeygenTokenName);
+ }
+ CMS.debug("KeyRecoveryAuthority: set up keygenToken");
+ } catch (NoSuchTokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_TOKEN_NOT_FOUND", serverKeygenTokenName));
+ } catch (Exception e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CRYPTOMANAGER_UNINITIALIZED"));
+ }
+
+ CMS.debug("KeyRecoveryAuthority: about to init entropy");
+ initEntropy(mConfig);
+ CMS.debug("KeyRecoveryAuthority: completed init of entropy");
+
+ getLogger().log(ILogger.EV_SYSTEM, ILogger.S_KRA,
+ ILogger.LL_INFO, mName.toString() + " is started");
+
+ // setup the KRA request queue
+ IService service = new KRAService(this);
+
+ mNotify = new KRANotify(this);
+ mPNotify = new ARequestNotifier();
+ IRequestSubsystem reqSub = RequestSubsystem.getInstance();
+ int reqdb_inc = mConfig.getInteger("reqdbInc", 5);
+
+ mRequestQueue = reqSub.getRequestQueue(getId(), reqdb_inc,
+ mPolicy, service, mNotify, mPNotify);
+
+ // set KeyStatusUpdateInterval to be 10 minutes if serial management is enabled.
+ mKeyDB.setKeyStatusUpdateInterval(
+ mRequestQueue.getRequestRepository(),
+ mConfig.getInteger("keyStatusUpdateInterval", 10 * 60));
+
+ // init request scheduler if configured
+ String schedulerClass =
+ mConfig.getString("requestSchedulerClass", null);
+
+ if (schedulerClass != null) {
+ try {
+ IRequestScheduler scheduler = (IRequestScheduler)
+ Class.forName(schedulerClass).newInstance();
+
+ mRequestQueue.setRequestScheduler(scheduler);
+ } catch (Exception e) {
+ // do nothing here
+ }
+ }
+ initNotificationListeners();
+
+ String replicaReposDN = mConfig.getString(PROP_REPLICAID_DN, null);
+ if (replicaReposDN == null) {
+ replicaReposDN = "ou=Replica," + getDBSubsystem().getBaseDN();
+ }
+
+ mReplicaRepot = new ReplicaIDRepository(
+ DBSubsystem.getInstance(), 1, replicaReposDN);
+ CMS.debug("Replica Repot inited");
+
+ }
+
+ public CryptoToken getKeygenToken() {
+ return mKeygenToken;
+ }
+
+ public IRequestListener getRequestInQListener() {
+ return mReqInQListener;
+ }
+
+ public org.mozilla.jss.crypto.X509Certificate getTransportCert() {
+ return mJssCert;
+ }
+
+ /**
+ * Clears up system during garbage collection.
+ */
+ public void finalize() {
+ shutdown();
+ }
+
+ /**
+ * Starts this service. When this method is called, all
+ * service
+ *
+ * @exception EBaseException failed to startup this subsystem
+ */
+ public void startup() throws EBaseException {
+ CMS.debug("KeyRecoveryAuthority startup() begins");
+
+ if (mRequestQueue != null) {
+ // setup administration operations if everything else is fine
+ mRequestQueue.recover();
+ CMS.debug("KeyRecoveryAuthority startup() call request Q recover");
+
+ // Note that we use our instance id for registration.
+ // This helps us to support multiple instances
+ // of a subsystem within server.
+
+ // register remote admin interface
+ mInitialized = true;
+ } else {
+ CMS.debug("KeyRecoveryAuthority: mRequestQueue is null, could be in preop mode");
+ }
+ }
+
+ /**
+ * Shutdowns this subsystem.
+ */
+ public void shutdown() {
+ if (!mInitialized)
+ return;
+
+ mTransportKeyUnit.shutdown();
+ mStorageKeyUnit.shutdown();
+ if (mKeyDB != null) {
+ mKeyDB.shutdown();
+ mKeyDB = null;
+ }
+ getLogger().log(ILogger.EV_SYSTEM, ILogger.S_KRA,
+ ILogger.LL_INFO, mName.toString() + " is stopped");
+ mInitialized = false;
+ }
+
+ /**
+ * Retrieves the configuration store of this subsystem.
+ * <P>
+ *
+ * @return configuration store
+ */
+ public IConfigStore getConfigStore() {
+ return mConfig;
+ }
+
+ /**
+ * Changes the auto recovery state.
+ *
+ * @param cs list of recovery agent credentials
+ * @param on turn of auto recovery or not
+ * @return operation success or not
+ */
+ public boolean setAutoRecoveryState(Credential cs[], boolean on) {
+ if (on == true) {
+ // check credential before enabling it
+ try {
+ getStorageKeyUnit().login(cs);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ // maintain in-memory variable; don't store it in config
+ mAutoRecoveryOn = on;
+ return true;
+ }
+
+ /**
+ * Retrieves the current auto recovery state.
+ *
+ * @return enable or not
+ */
+ public boolean getAutoRecoveryState() {
+ // maintain in-memory variable; don't store it in config
+ return mAutoRecoveryOn;
+ }
+
+ /**
+ * Returns a list of users who are in auto
+ * recovery mode.
+ *
+ * @return list of user IDs that are accepted in the
+ * auto recovery mode
+ */
+ public Enumeration<String> getAutoRecoveryIDs() {
+ return mAutoRecovery.keys();
+ }
+
+ /**
+ * Adds auto recovery mode to the given user id.
+ *
+ * @param id new identifier to the auto recovery mode
+ * @param creds list of credentials
+ */
+ public void addAutoRecovery(String id, Credential creds[]) {
+ mAutoRecovery.put(id, creds);
+ }
+
+ /**
+ * Removes auto recovery mode from the given user id.
+ *
+ * @param id id of user to be removed from auto
+ * recovery mode
+ */
+ public void removeAutoRecovery(String id) {
+ mAutoRecovery.remove(id);
+ }
+
+ /**
+ * Retrieves logger from escrow authority.
+ *
+ * @return logger
+ */
+ public ILogger getLogger() {
+ return CMS.getLogger();
+ }
+
+ /**
+ * Retrieves number of required agents for
+ * recovery operation.
+ *
+ * @return number of required agents
+ * @exception EBaseException failed to retrieve info
+ */
+ public int getNoOfRequiredAgents() throws EBaseException {
+ if (mConfig.getBoolean("keySplitting", false)) {
+ return mStorageKeyUnit.getNoOfRequiredAgents();
+ } else {
+ int ret = -1;
+ ret = mConfig.getInteger("noOfRequiredRecoveryAgents", 1);
+ if (ret <= 0) {
+ throw new EBaseException("Invalid parameter noOfRequiredecoveryAgents");
+ }
+ return ret;
+ }
+ }
+
+ /**
+ * Sets number of required agents for
+ * recovery operation
+ *
+ * @return none
+ * @exception EBaseException invalid setting
+ */
+ public void setNoOfRequiredAgents(int number) throws EBaseException {
+ if (mConfig.getBoolean("keySplitting")) {
+ mStorageKeyUnit.setNoOfRequiredAgents(number);
+ } else {
+ mConfig.putInteger("noOfRequiredRecoveryAgents", number);
+ }
+ }
+
+ /**
+ * Distributed recovery.
+ */
+ public String getRecoveryID() {
+ return Integer.toString(mRecoveryIDCounter++);
+ }
+
+ public Hashtable<String, Object> createRecoveryParams(String recoveryID)
+ throws EBaseException {
+ Hashtable<String, Object> h = new Hashtable<String, Object>();
+
+ h.put(PARAM_CREDS, new Vector<Credential>());
+ h.put(PARAM_LOCK, new Object());
+ mRecoveryParams.put(recoveryID, h);
+ return h;
+ }
+
+ public void destroyRecoveryParams(String recoveryID)
+ throws EBaseException {
+ mRecoveryParams.remove(recoveryID);
+ }
+
+ public Hashtable<String, Object> getRecoveryParams(String recoveryID)
+ throws EBaseException {
+ return (Hashtable<String, Object>) mRecoveryParams.get(recoveryID);
+ }
+
+ public void createPk12(String recoveryID, byte[] pk12)
+ throws EBaseException {
+ Hashtable<String, Object> h = getRecoveryParams(recoveryID);
+
+ h.put(PARAM_PK12, pk12);
+ }
+
+ public byte[] getPk12(String recoveryID)
+ throws EBaseException {
+ return (byte[]) getRecoveryParams(recoveryID).get(PARAM_PK12);
+ }
+
+ public void createError(String recoveryID, String error)
+ throws EBaseException {
+ Hashtable<String, Object> h = getRecoveryParams(recoveryID);
+
+ h.put(PARAM_ERROR, error);
+ }
+
+ public String getError(String recoveryID)
+ throws EBaseException {
+ return (String) getRecoveryParams(recoveryID).get(PARAM_ERROR);
+ }
+
+ /**
+ * Retrieve the current approval agents
+ */
+ public Vector<Credential> getAppAgents(
+ String recoveryID) throws EBaseException {
+ Hashtable<String, Object> h = getRecoveryParams(recoveryID);
+ @SuppressWarnings("unchecked")
+ Vector<Credential> dc = (Vector<Credential>) h.get(PARAM_CREDS);
+
+ return dc;
+ }
+
+ /**
+ * Retrieves a list credentials. This puts KRA in a waiting
+ * mode, it never returns until all the necessary passwords
+ * are collected.
+ */
+ public Credential[] getDistributedCredentials(
+ String recoveryID)
+ throws EBaseException {
+ Hashtable<String, Object> h = getRecoveryParams(recoveryID);
+ @SuppressWarnings("unchecked")
+ Vector<Credential> dc = (Vector<Credential>) h.get(PARAM_CREDS);
+ Object lock = (Object) h.get(PARAM_LOCK);
+
+ synchronized (lock) {
+ while (dc.size() < getNoOfRequiredAgents()) {
+ CMS.debug("KeyRecoveryAuthority: cfu in synchronized lock for getDistributedCredentials");
+ try {
+ lock.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ Credential creds[] = new Credential[dc.size()];
+
+ dc.copyInto(creds);
+ return creds;
+ }
+ }
+
+ /**
+ * Verifies credential.
+ */
+ private void verifyCredential(Vector<Credential> creds, String uid,
+ String pwd) throws EBaseException {
+ // see if we have the uid already
+
+ if (!mConfig.getBoolean("keySplitting")) {
+ // check if the uid is in the specified group
+ IUGSubsystem ug = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG);
+ if (!ug.isMemberOf(uid, mConfig.getString("recoveryAgentGroup"))) {
+ // invalid group
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_CREDENTIALS_NOT_EXIST"));
+ }
+ }
+
+ for (int i = 0; i < creds.size(); i++) {
+ Credential c = creds.elementAt(i);
+
+ if (c.getIdentifier().equals(uid)) {
+ // duplicated uid
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_CREDENTIALS_EXIST"));
+ }
+ }
+ if (mConfig.getBoolean("keySplitting")) {
+ mStorageKeyUnit.checkPassword(uid, pwd);
+ }
+ }
+
+ /**
+ * Adds password.
+ */
+ public void addDistributedCredential(String recoveryID,
+ String uid, String pwd) throws EBaseException {
+ Hashtable<String, Object> h = getRecoveryParams(recoveryID);
+ @SuppressWarnings("unchecked")
+ Vector<Credential> dc = (Vector<Credential>) h.get(PARAM_CREDS);
+ Object lock = (Object) h.get(PARAM_LOCK);
+
+ synchronized (lock) {
+ verifyCredential(dc, uid, pwd);
+ // verify password
+ dc.addElement(new Credential(uid, pwd));
+ // modify status object
+ lock.notify();
+ }
+ }
+
+ /**
+ * Archives key. This creates a key record in the key
+ * repository.
+ * <P>
+ *
+ * <ul>
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST used whenever a user private key archive
+ * request is made (this is when the DRM receives the request)
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED used whenever a user private key
+ * archive request is processed (this is when the DRM processes the request)
+ * </ul>
+ *
+ * @param rec key record to be archived
+ * @return executed request
+ * @exception EBaseException failed to archive key
+ * @return the request
+ * <P>
+ */
+ public IRequest archiveKey(KeyRecord rec)
+ throws EBaseException {
+ String auditMessage = null;
+ String auditSubjectID = auditSubjectID();
+ String auditRequesterID = auditRequesterID();
+ String auditPublicKey = auditPublicKey(rec);
+ String auditArchiveID = ILogger.UNIDENTIFIED;
+
+ IRequestQueue queue = null;
+ IRequest r = null;
+ String id = null;
+
+ // ensure that any low-level exceptions are reported
+ // to the signed audit log and stored as failures
+ try {
+ queue = getRequestQueue();
+
+ r = queue.newRequest(KRAService.ENROLLMENT);
+
+ if (r != null) {
+ // overwrite "auditArchiveID" if and only if "id" != null
+ id = r.getRequestId().toString();
+ if (id != null) {
+ auditArchiveID = id.trim();
+ }
+ }
+
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ } catch (EBaseException eAudit1) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+
+ throw eAudit1;
+ }
+
+ // ensure that any low-level exceptions are reported
+ // to the signed audit log and stored as failures
+ try {
+ if (r != null) {
+ r.setExtData(EnrollmentService.ATTR_KEY_RECORD, rec.getSerialNumber());
+ queue.processRequest(r);
+ }
+
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditPublicKey);
+
+ audit(auditMessage);
+ } catch (EBaseException eAudit1) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditPublicKey);
+
+ audit(auditMessage);
+
+ throw eAudit1;
+ }
+
+ return r;
+ }
+
+ /**
+ * async key recovery initiation
+ */
+ public String initAsyncKeyRecovery(BigInteger kid, X509CertImpl cert, String agent)
+ throws EBaseException {
+
+ String auditPublicKey = auditPublicKey(cert);
+ String auditRecoveryID = "undefined";
+ String auditMessage = null;
+ String auditSubjectID = auditSubjectID();
+
+ IRequestQueue queue = null;
+ IRequest r = null;
+
+ try {
+ queue = getRequestQueue();
+ r = queue.newRequest(KRAService.RECOVERY);
+
+ r.setExtData(RecoveryService.ATTR_SERIALNO, kid);
+ r.setExtData(RecoveryService.ATTR_USER_CERT, cert);
+ // first one in the "approvingAgents" list is the initiating agent
+ r.setExtData(RecoveryService.ATTR_APPROVE_AGENTS, agent);
+ r.setRequestStatus(RequestStatus.PENDING);
+ queue.updateRequest(r);
+ auditRecoveryID = r.getRequestId().toString();
+
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_ASYNC,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRecoveryID,
+ auditPublicKey);
+
+ audit(auditMessage);
+ } catch (EBaseException eAudit1) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_ASYNC,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ auditPublicKey);
+
+ audit(auditMessage);
+
+ throw eAudit1;
+ }
+
+ //NO call to queue.processRequest(r) because it is only initiating
+ return r.getRequestId().toString();
+ }
+
+ /**
+ * is async recovery request status APPROVED -
+ * i.e. all required # of recovery agents approved
+ */
+ public boolean isApprovedAsyncKeyRecovery(String reqID)
+ throws EBaseException {
+ IRequestQueue queue = null;
+ IRequest r = null;
+
+ queue = getRequestQueue();
+ r = queue.findRequest(new RequestId(reqID));
+ if ((r.getRequestStatus() == RequestStatus.APPROVED)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * get async recovery request initiating agent
+ */
+ public String getInitAgentAsyncKeyRecovery(String reqID)
+ throws EBaseException {
+ IRequestQueue queue = null;
+ IRequest r = null;
+
+ queue = getRequestQueue();
+ r = queue.findRequest(new RequestId(reqID));
+
+ String agents = r.getExtDataInString(RecoveryService.ATTR_APPROVE_AGENTS);
+ if (agents != null) {
+ int i = agents.indexOf(",");
+ if (i == -1) {
+ return agents;
+ }
+ return agents.substring(0, i);
+ } else { // no approvingAgents existing, can't be async recovery
+ CMS.debug("getInitAgentAsyncKeyRecovery: no approvingAgents in request");
+ }
+
+ return null;
+ }
+
+ /**
+ * add async recovery agent to approving agent list of the recovery request
+ * record
+ * This method will check to see if the agent belongs to the recovery group
+ * first before adding.
+ */
+ public void addAgentAsyncKeyRecovery(String reqID, String agentID)
+ throws EBaseException {
+ IRequestQueue queue = null;
+ IRequest r = null;
+
+ // check if the uid is in the specified group
+ IUGSubsystem ug = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG);
+ if (!ug.isMemberOf(agentID, mConfig.getString("recoveryAgentGroup"))) {
+ // invalid group
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_CREDENTIALS_NOT_EXIST"));
+ }
+
+ queue = getRequestQueue();
+ r = queue.findRequest(new RequestId(reqID));
+
+ String agents = r.getExtDataInString(RecoveryService.ATTR_APPROVE_AGENTS);
+ if (agents != null) {
+ int count = 0;
+ StringTokenizer st = new StringTokenizer(agents, ",");
+ for (; st.hasMoreTokens();) {
+ String a = st.nextToken();
+ // first one is the initiating agent
+ if ((count != 0) && a.equals(agentID)) {
+ // duplicated uid
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_CREDENTIALS_EXIST"));
+ }
+ count++;
+ }
+
+ // note: if count==1 and required agents is 1, it's good to add
+ // and it'd look like "agent1,agent1" - that's the only dup allowed
+ if (count <= getNoOfRequiredAgents()) { //all good, add it
+ r.setExtData(RecoveryService.ATTR_APPROVE_AGENTS,
+ agents + "," + agentID);
+ if (count == getNoOfRequiredAgents()) {
+ r.setRequestStatus(RequestStatus.APPROVED);
+ } else {
+ r.setRequestStatus(RequestStatus.PENDING);
+ }
+ queue.updateRequest(r);
+ }
+ } else { // no approvingAgents existing, can't be async recovery
+ CMS.debug("addAgentAsyncKeyRecovery: no approvingAgents in request. Async recovery request not initiated?");
+ }
+ }
+
+ /**
+ * Recovers key for administrators. This method is
+ * invoked by the agent operation of the key recovery servlet.
+ * <P>
+ *
+ * <ul>
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST used whenever a user private key recovery request is
+ * made (this is when the DRM receives the request)
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED used whenever a user private key recovery
+ * request is processed (this is when the DRM processes the request)
+ * </ul>
+ *
+ * @param kid key identifier
+ * @param creds list of recovery agent credentials
+ * @param password password of the PKCS12 package
+ * @param cert certficate that will be put in PKCS12
+ * @param delivery file, mail or something else
+ * @param nickname string containing the nickname of the id cert for this
+ * subsystem
+ * @exception EBaseException failed to recover key
+ * @return a byte array containing the key
+ */
+ public byte[] doKeyRecovery(BigInteger kid,
+ Credential creds[], String password,
+ X509CertImpl cert,
+ String delivery, String nickname,
+ String agent)
+ throws EBaseException {
+ String auditMessage = null;
+ String auditSubjectID = auditSubjectID();
+ String auditRecoveryID = auditRecoveryID();
+ String auditPublicKey = auditPublicKey(cert);
+ String auditAgents = ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+
+ IRequestQueue queue = null;
+ IRequest r = null;
+ Hashtable<String, Object> params = null;
+
+ CMS.debug("KeyRecoveryAuthority: in synchronous doKeyRecovery()");
+ // ensure that any low-level exceptions are reported
+ // to the signed audit log and stored as failures
+ try {
+ queue = getRequestQueue();
+ r = queue.newRequest(KRAService.RECOVERY);
+
+ // set transient parameters
+ params = createVolatileRequest(r.getRequestId());
+
+ if (mConfig.getBoolean("keySplitting")) {
+ params.put(RecoveryService.ATTR_AGENT_CREDENTIALS, creds);
+ }
+ params.put(RecoveryService.ATTR_TRANSPORT_PWD, password);
+
+ r.setExtData(RecoveryService.ATTR_SERIALNO, kid);
+ r.setExtData(RecoveryService.ATTR_USER_CERT, cert);
+ if (nickname != null) {
+ nickname = nickname.trim();
+ if (!nickname.equals("")) {
+ r.setExtData(RecoveryService.ATTR_NICKNAME, nickname);
+ }
+ }
+ // for both sync and async recovery
+ r.setExtData(RecoveryService.ATTR_APPROVE_AGENTS, agent);
+
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRecoveryID,
+ auditPublicKey);
+
+ audit(auditMessage);
+ } catch (EBaseException eAudit1) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ auditPublicKey);
+
+ audit(auditMessage);
+
+ throw eAudit1;
+ }
+
+ // ensure that any low-level exceptions are reported
+ // to the signed audit log and stored as failures
+ try {
+ queue.processRequest(r);
+
+ if (r.getExtDataInString(IRequest.ERROR) == null) {
+ byte pkcs12[] = (byte[]) params.get(
+ RecoveryService.ATTR_PKCS12);
+
+ auditAgents = auditAgents(creds);
+
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRecoveryID,
+ auditAgents);
+
+ audit(auditMessage);
+
+ destroyVolatileRequest(r.getRequestId());
+
+ return pkcs12;
+ } else {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ auditAgents);
+
+ audit(auditMessage);
+
+ throw new EBaseException(r.getExtDataInString(IRequest.ERROR));
+ }
+ } catch (EBaseException eAudit1) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ auditAgents);
+
+ audit(auditMessage);
+
+ throw eAudit1;
+ }
+ }
+
+ /**
+ * Async Recovers key for administrators. This method is
+ * invoked by the agent operation of the key recovery servlet.
+ * <P>
+ *
+ * <ul>
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST used whenever a user private key recovery request is
+ * made (this is when the DRM receives the request)
+ * <li>signed.audit LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED used whenever a user private key recovery
+ * request is processed (this is when the DRM processes the request)
+ * </ul>
+ *
+ * @param requestID request id
+ * @param password password of the PKCS12 package
+ * subsystem
+ * @exception EBaseException failed to recover key
+ * @return a byte array containing the key
+ */
+ public byte[] doKeyRecovery(
+ String reqID,
+ String password)
+ throws EBaseException {
+ String auditMessage = null;
+ String auditSubjectID = auditSubjectID();
+ String auditRecoveryID = reqID;
+ String auditAgents = ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+
+ IRequestQueue queue = null;
+ IRequest r = null;
+ Hashtable<String, Object> params = null;
+
+ CMS.debug("KeyRecoveryAuthority: in asynchronous doKeyRecovery()");
+ queue = getRequestQueue();
+ r = queue.findRequest(new RequestId(reqID));
+
+ auditAgents =
+ r.getExtDataInString(RecoveryService.ATTR_APPROVE_AGENTS);
+
+ // set transient parameters
+ params = createVolatileRequest(r.getRequestId());
+ params.put(RecoveryService.ATTR_TRANSPORT_PWD, password);
+
+ // ensure that any low-level exceptions are reported
+ // to the signed audit log and stored as failures
+ try {
+ CMS.debug("KeyRecoveryAuthority: in asynchronous doKeyRecovery(), request state ="
+ + r.getRequestStatus().toString());
+ // can only process requests in begin state
+ r.setRequestStatus(RequestStatus.BEGIN);
+ queue.processRequest(r);
+
+ if (r.getExtDataInString(IRequest.ERROR) == null) {
+ byte pkcs12[] = (byte[]) params.get(
+ RecoveryService.ATTR_PKCS12);
+
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRecoveryID,
+ auditAgents);
+
+ audit(auditMessage);
+
+ destroyVolatileRequest(r.getRequestId());
+
+ return pkcs12;
+ } else {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ auditAgents);
+
+ audit(auditMessage);
+
+ throw new EBaseException(r.getExtDataInString(IRequest.ERROR));
+ }
+ } catch (EBaseException eAudit1) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ auditAgents);
+
+ audit(auditMessage);
+ throw eAudit1;
+ }
+ }
+
+ /**
+ * Constructs a recovery request and submits it
+ * to the request subsystem for processing.
+ *
+ * @param kid key identifier
+ * @param creds list of recovery agent credentials
+ * @param password password of the PKCS12 package
+ * @param cert certficate that will be put in PKCS12
+ * @param delivery file, mail or something else
+ * @return executed request
+ * @exception EBaseException failed to recover key
+ */
+ public IRequest recoverKey(BigInteger kid,
+ Credential creds[], String password,
+ X509CertImpl cert,
+ String delivery) throws EBaseException {
+ IRequestQueue queue = getRequestQueue();
+ IRequest r = queue.newRequest("recovery");
+
+ r.setExtData(RecoveryService.ATTR_SERIALNO, kid);
+ r.setExtData(RecoveryService.ATTR_TRANSPORT_PWD, password);
+ r.setExtData(RecoveryService.ATTR_USER_CERT, cert);
+ r.setExtData(RecoveryService.ATTR_DELIVERY, delivery);
+ queue.processRequest(r);
+ return r;
+ }
+
+ /**
+ * Recovers key for end-entities.
+ *
+ * @param creds list of credentials
+ * @param encryptionChain certificate chain
+ * @param signingCert signing cert
+ * @param transportCert certificate to protect in-transit key
+ * @param ownerName owner name
+ * @return executed request
+ * @exception EBaseException failed to recover key
+ */
+ public IRequest recoverKey(Credential creds[], CertificateChain
+ encryptionChain, X509CertImpl signingCert,
+ X509CertImpl transportCert,
+ X500Name ownerName) throws EBaseException {
+ IRequestQueue queue = getRequestQueue();
+ IRequest r = queue.newRequest("recovery");
+
+ ByteArrayOutputStream certChainOut = new ByteArrayOutputStream();
+ try {
+ encryptionChain.encode(certChainOut);
+ r.setExtData(RecoveryService.ATTR_ENCRYPTION_CERTS,
+ certChainOut.toByteArray());
+ } catch (IOException e) {
+ log(ILogger.LL_FAILURE,
+ "Error encoding certificate chain");
+ }
+
+ r.setExtData(RecoveryService.ATTR_SIGNING_CERT, signingCert);
+ r.setExtData(RecoveryService.ATTR_TRANSPORT_CERT, transportCert);
+
+ DerOutputStream ownerNameOut = new DerOutputStream();
+ try {
+ ownerName.encode(ownerNameOut);
+ r.setExtData(RecoveryService.ATTR_OWNER_NAME,
+ ownerNameOut.toByteArray());
+ } catch (IOException e) {
+ log(ILogger.LL_FAILURE,
+ "Error encoding X500Name for owner name");
+ }
+
+ queue.processRequest(r);
+ return r;
+ }
+
+ /**
+ * Retrieves the storage key unit. The storage key
+ * is used to wrap the user key for long term
+ * storage.
+ *
+ * @return storage key unit.
+ */
+ public IStorageKeyUnit getStorageKeyUnit() {
+ return mStorageKeyUnit;
+ }
+
+ /**
+ * Retrieves the transport key unit.
+ *
+ * @return transport key unit
+ */
+ public ITransportKeyUnit getTransportKeyUnit() {
+ return mTransportKeyUnit;
+ }
+
+ /**
+ * Returns the name of this subsystem. This name is
+ * extracted from the transport certificate.
+ *
+ * @return KRA name
+ */
+ public X500Name getX500Name() {
+ return mName;
+ }
+
+ public String getNickName() {
+ return getNickname();
+ }
+
+ /**
+ * Returns the nickname for the id cert of this
+ * subsystem.
+ *
+ * @return nickname of the transport certificate
+ */
+ public String getNickname() {
+ try {
+ return mTransportKeyUnit.getNickName();
+ } catch (EBaseException e) {
+ return null;
+ }
+ }
+
+ public void setNickname(String str) {
+ try {
+ mTransportKeyUnit.setNickName(str);
+ } catch (EBaseException e) {
+ }
+ }
+
+ public String getNewNickName() throws EBaseException {
+ return mConfig.getString(PROP_NEW_NICKNAME, "");
+ }
+
+ public void setNewNickName(String name) {
+ mConfig.putString(PROP_NEW_NICKNAME, name);
+ }
+
+ public IPolicy getPolicy() {
+ return mPolicy;
+ }
+
+ /**
+ * Retrieves KRA request repository.
+ * <P>
+ *
+ * @return request repository
+ */
+ public IRequestQueue getRequestQueue() {
+ return mRequestQueue;
+ }
+
+ /**
+ * Retrieves the key repository. The key repository
+ * stores archived keys.
+ * <P>
+ */
+ public IKeyRepository getKeyRepository() {
+ return mKeyDB;
+ }
+
+ /**
+ * Retrieves replica repository.
+ * <P>
+ *
+ * @return replica repository
+ */
+ public IReplicaIDRepository getReplicaRepository() {
+ return mReplicaRepot;
+ }
+
+ /**
+ * Retrieves the DN of this escrow authority.
+ * <P>
+ *
+ * @return distinguished name
+ */
+ protected String getDN() {
+ return getX500Name().toString();
+ }
+
+ /**
+ * Retrieves database connection.
+ */
+ public IDBSubsystem getDBSubsystem() {
+ return DBSubsystem.getInstance();
+ }
+
+ /**
+ * Logs an event.
+ *
+ * @param level log level
+ * @param msg message to log
+ */
+ public void log(int level, String msg) {
+ mLogger.log(ILogger.EV_SYSTEM, null, ILogger.S_KRA,
+ level, msg);
+ }
+
+ /**
+ * Registers a request listener.
+ *
+ * @param l request listener
+ */
+ public void registerRequestListener(IRequestListener l) {
+ // it's initialized.
+ if (mNotify != null)
+ mNotify.registerListener(l);
+ }
+
+ public void registerPendingListener(IRequestListener l) {
+ mPNotify.registerListener(l);
+ }
+
+ /**
+ * init notification related listeners -
+ * right now only RequestInQueue listener is available for KRA
+ */
+ private void initNotificationListeners() {
+ IConfigStore nc = null;
+
+ try {
+ nc = mConfig.getSubStore(PROP_NOTIFY_SUBSTORE);
+ if (nc != null && nc.size() > 0) {
+ // Initialize Request In Queue notification listener
+ String requestInQListenerClassName =
+ nc.getString("certificateIssuedListenerClassName",
+ "com.netscape.cms.listeners.RequestInQListener");
+
+ try {
+ mReqInQListener = (IRequestListener) Class.forName(requestInQListenerClassName).newInstance();
+ mReqInQListener.init(this, nc);
+ } catch (Exception e1) {
+ log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_REGISTER_LISTENER", requestInQListenerClassName));
+ }
+ } else {
+ log(ILogger.LL_INFO,
+ "No KRA notification Module configuration found");
+ }
+ } catch (EPropertyNotFound e) {
+ log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_NOTIFY_ERROR", e.toString()));
+ } catch (EListenersException e) {
+ log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_NOTIFY_ERROR", e.toString()));
+ } catch (EBaseException e) {
+ log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_NOTIFY_ERROR", e.toString()));
+ }
+ }
+
+ /**
+ * temporary accepted ras.
+ */
+ /* code no longer used
+ public X500Name[] getAcceptedRAs() {
+ // temporary. use usr/grp for real thing.
+ X500Name radn = null;
+ String raname = null;
+
+ try {
+ raname = mConfig.getString("acceptedRA", null);
+ if (raname != null) {
+ radn = new X500Name(raname);
+ }
+ } catch (IOException e) {
+ mLogger.log(ILogger.EV_SYSTEM, ILogger.S_KRA,
+ ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_INVALID_RA_NAME", raname, e.toString()));
+ } catch (EBaseException e) {
+ // ignore - set to null.
+ mLogger.log(ILogger.EV_SYSTEM, ILogger.S_KRA,
+ ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_INVALID_RA_SETUP", e.toString()));
+ }
+ return new X500Name[] { radn };
+ }
+ */
+
+ public Hashtable<String, Hashtable<String, Object>> mVolatileRequests =
+ new Hashtable<String, Hashtable<String, Object>>();
+
+ /**
+ * Creates a request object to store attributes that
+ * will not be serialized. Currently, request queue
+ * framework will try to serialize all the attribute into
+ * persistent storage. Things like passwords are not
+ * desirable to be stored.
+ */
+ public Hashtable<String, Object> createVolatileRequest(RequestId id) {
+ Hashtable<String, Object> params = new Hashtable<String, Object>();
+
+ mVolatileRequests.put(id.toString(), params);
+ return params;
+ }
+
+ public Hashtable<String, Object> getVolatileRequest(RequestId id) {
+ return (Hashtable<String, Object>) mVolatileRequests.get(id.toString());
+ }
+
+ public void destroyVolatileRequest(RequestId id) {
+ mVolatileRequests.remove(id.toString());
+ }
+
+ public String getOfficialName() {
+ return OFFICIAL_NAME;
+ }
+
+ /**
+ * Signed Audit Log
+ *
+ * This method is called to store messages to the signed audit log.
+ * <P>
+ *
+ * @param msg signed audit log message
+ */
+ private void audit(String msg) {
+ // in this case, do NOT strip preceding/trailing whitespace
+ // from passed-in String parameters
+
+ if (mSignedAuditLogger == null) {
+ return;
+ }
+
+ mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT,
+ null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ msg);
+ }
+
+ /**
+ * Signed Audit Log Subject ID
+ *
+ * This method is called to obtain the "SubjectID" for
+ * a signed audit log message.
+ * <P>
+ *
+ * @return id string containing the signed audit log message SubjectID
+ */
+ private String auditSubjectID() {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ String subjectID = null;
+
+ // Initialize subjectID
+ SessionContext auditContext = SessionContext.getExistingContext();
+
+ if (auditContext != null) {
+ subjectID = (String)
+ auditContext.get(SessionContext.USER_ID);
+
+ if (subjectID != null) {
+ subjectID = subjectID.trim();
+ } else {
+ subjectID = ILogger.NONROLEUSER;
+ }
+ } else {
+ subjectID = ILogger.UNIDENTIFIED;
+ }
+
+ return subjectID;
+ }
+
+ /**
+ * Signed Audit Log Requester ID
+ *
+ * This method is called to obtain the "RequesterID" for
+ * a signed audit log message.
+ * <P>
+ *
+ * @return id string containing the signed audit log message RequesterID
+ */
+ private String auditRequesterID() {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ String requesterID = null;
+
+ // Initialize requesterID
+ SessionContext auditContext = SessionContext.getExistingContext();
+
+ if (auditContext != null) {
+ requesterID = (String)
+ auditContext.get(SessionContext.REQUESTER_ID);
+
+ if (requesterID != null) {
+ requesterID = requesterID.trim();
+ } else {
+ requesterID = ILogger.UNIDENTIFIED;
+ }
+ } else {
+ requesterID = ILogger.UNIDENTIFIED;
+ }
+
+ return requesterID;
+ }
+
+ /**
+ * Signed Audit Log Recovery ID
+ *
+ * This method is called to obtain the "RecoveryID" for
+ * a signed audit log message.
+ * <P>
+ *
+ * @return id string containing the signed audit log message RecoveryID
+ */
+ private String auditRecoveryID() {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ String recoveryID = null;
+
+ // Initialize recoveryID
+ SessionContext auditContext = SessionContext.getExistingContext();
+
+ if (auditContext != null) {
+ recoveryID = (String)
+ auditContext.get(SessionContext.RECOVERY_ID);
+
+ if (recoveryID != null) {
+ recoveryID = recoveryID.trim();
+ } else {
+ recoveryID = ILogger.UNIDENTIFIED;
+ }
+ } else {
+ recoveryID = ILogger.UNIDENTIFIED;
+ }
+
+ return recoveryID;
+ }
+
+ /**
+ * Signed Audit Log Public Key
+ *
+ * This method is called to obtain the public key from the passed in
+ * "X509Certificate" for a signed audit log message.
+ * <P>
+ *
+ * @param cert an X509Certificate
+ * @return key string containing the certificate's public key
+ */
+ private String auditPublicKey(X509Certificate cert) {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ if (cert == null) {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ }
+
+ byte rawData[] = cert.getPublicKey().getEncoded();
+
+ // convert "rawData" into "base64Data"
+ if (rawData != null) {
+ String base64Data = CMS.BtoA(rawData).trim();
+ String key = "";
+
+ // extract all line separators from the "base64Data"
+ for (int i = 0; i < base64Data.length(); i++) {
+ if (base64Data.substring(i, i).getBytes() != EOL) {
+ key += base64Data.substring(i, i);
+ }
+ }
+
+ return key;
+ }
+
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ }
+
+ /**
+ * Signed Audit Log Public Key
+ *
+ * This method is called to obtain the public key from the passed in
+ * "KeyRecord" for a signed audit log message.
+ * <P>
+ *
+ * @param rec a Key Record
+ * @return key string containing the certificate's public key
+ */
+ private String auditPublicKey(KeyRecord rec) {
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ if (rec == null) {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ }
+
+ byte rawData[] = null;
+
+ try {
+ rawData = rec.getPublicKeyData();
+ } catch (EBaseException e) {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ }
+
+ String key = null;
+
+ // convert "rawData" into "base64Data"
+ if (rawData != null) {
+ String base64Data = null;
+
+ base64Data = CMS.BtoA(rawData).trim();
+
+ // extract all line separators from the "base64Data"
+ for (int i = 0; i < base64Data.length(); i++) {
+ if (base64Data.substring(i, i).getBytes() != EOL) {
+ key += base64Data.substring(i, i);
+ }
+ }
+ }
+
+ if (key != null) {
+ key = key.trim();
+
+ if (key.equals("")) {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ } else {
+ return key;
+ }
+ } else {
+ return ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+ }
+ }
+
+ /**
+ * Signed Audit Agents
+ *
+ * This method is called to extract agent uids from the passed in
+ * "Credentials[]" and return a string of comma-separated agent uids.
+ * <P>
+ *
+ * @param creds array of credentials
+ * @return a comma-separated string of agent uids
+ */
+ private String auditAgents(Credential creds[]) {
+ if (creds == null)
+ return null;
+
+ // if no signed audit object exists, bail
+ if (mSignedAuditLogger == null) {
+ return null;
+ }
+
+ String agents = ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+
+ String uid = null;
+
+ for (int i = 0; i < creds.length; i++) {
+ uid = creds[i].getIdentifier();
+
+ if (uid != null) {
+ uid = uid.trim();
+ }
+
+ if (uid != null &&
+ !uid.equals("")) {
+
+ if (i == 0) {
+ agents = uid;
+ } else {
+ agents += SIGNED_AUDIT_AGENT_DELIMITER + uid;
+ }
+ }
+ }
+
+ return agents;
+ }
+}
diff --git a/base/kra/src/com/netscape/kra/NetkeyKeygenService.java b/base/kra/src/com/netscape/kra/NetkeyKeygenService.java
new file mode 100644
index 000000000..ecce2eaa9
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/NetkeyKeygenService.java
@@ -0,0 +1,608 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import netscape.security.provider.RSAPublicKey;
+
+import org.mozilla.jss.crypto.Cipher;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.EncryptionAlgorithm;
+import org.mozilla.jss.crypto.IVParameterSpec;
+import org.mozilla.jss.crypto.KeyPairAlgorithm;
+import org.mozilla.jss.crypto.KeyPairGenerator;
+import org.mozilla.jss.crypto.KeyWrapAlgorithm;
+import org.mozilla.jss.crypto.KeyWrapper;
+import org.mozilla.jss.crypto.PQGParamGenException;
+import org.mozilla.jss.crypto.PQGParams;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.TokenException;
+import org.mozilla.jss.pkcs11.PK11SymKey;
+import org.mozilla.jss.pkix.crmf.PKIArchiveOptions;
+import org.mozilla.jss.util.Base64OutputStream;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.IService;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.certsrv.security.ITransportKeyUnit;
+import com.netscape.cmscore.dbs.KeyRecord;
+import com.netscape.cmscore.util.Debug;
+
+/**
+ * A class representing keygen/archival request procesor for requests
+ * from netkey RAs.
+ * the user private key of the encryption cert is wrapped with a
+ * session symmetric key. The session symmetric key is wrapped with the
+ * storage key and stored in the internal database for long term
+ * storage.
+ * The user private key of the encryption cert is to be wrapped with the
+ * DES key which came in in the request wrapped with the KRA
+ * transport cert. The wrapped user private key is then sent back to
+ * the caller (netkey RA) ...netkey RA should already has kek-wrapped
+ * des key from the TKS. They are to be sent together back to
+ * the token.
+ *
+ * @author Christina Fu (cfu)
+ * @version $Revision$, $Date$
+ */
+
+public class NetkeyKeygenService implements IService {
+ public final static String ATTR_KEY_RECORD = "keyRecord";
+ public final static String ATTR_PROOF_OF_ARCHIVAL =
+ "proofOfArchival";
+
+ // private
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_4";
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED_3";
+ // these need to be defined in LogMessages_en.properties later when we do this
+ private final static String LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST =
+ "LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_3";
+ private final static String LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS =
+ "LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS_4";
+ private final static String LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE =
+ "LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE_3";
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS_4";
+ private final static String LOGGING_SIGNED_AUDIT_PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE =
+ "LOGGING_SIGNED_AUDIT_PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE_4";
+ private IKeyRecoveryAuthority mKRA = null;
+ private ITransportKeyUnit mTransportUnit = null;
+ private IStorageKeyUnit mStorageUnit = null;
+ private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger();
+
+ /**
+ * Constructs request processor.
+ * <P>
+ *
+ * @param kra key recovery authority
+ */
+ public NetkeyKeygenService(IKeyRecoveryAuthority kra) {
+ mKRA = kra;
+ mTransportUnit = kra.getTransportKeyUnit();
+ mStorageUnit = kra.getStorageKeyUnit();
+ }
+
+ public PKIArchiveOptions toPKIArchiveOptions(byte options[]) {
+ ByteArrayInputStream bis = new ByteArrayInputStream(options);
+ PKIArchiveOptions archOpts = null;
+
+ try {
+ archOpts = (PKIArchiveOptions)
+ (new PKIArchiveOptions.Template()).decode(bis);
+ } catch (Exception e) {
+ CMS.debug("NetkeyKeygenService: getPKIArchiveOptions " + e.toString());
+ }
+ return archOpts;
+ }
+
+ public KeyPair generateKeyPair(
+ KeyPairAlgorithm kpAlg, int keySize, PQGParams pqg)
+ throws NoSuchAlgorithmException, TokenException, InvalidAlgorithmParameterException,
+ InvalidParameterException, PQGParamGenException {
+
+ CryptoToken token = mKRA.getKeygenToken();
+
+ CMS.debug("NetkeyKeygenService: key pair is to be generated on slot: " + token.getName());
+
+ /*
+ make it temporary so can work with HSM
+ netHSM works with
+ temporary == true
+ sensitive == <do not specify>
+ extractable == <do not specify>
+ LunaSA2 works with
+ temporary == true
+ sensitive == true
+ extractable == true
+ */
+ KeyPairGenerator kpGen = token.getKeyPairGenerator(kpAlg);
+ IConfigStore config = CMS.getConfigStore();
+ IConfigStore kgConfig = config.getSubStore("kra.keygen");
+ boolean tp = false;
+ boolean sp = false;
+ boolean ep = false;
+ if (kgConfig != null) {
+ try {
+ tp = kgConfig.getBoolean("temporaryPairs", false);
+ sp = kgConfig.getBoolean("sensitivePairs", false);
+ ep = kgConfig.getBoolean("extractablePairs", false);
+ // by default, let nethsm work
+ if ((tp == false) && (sp == false) && (ep == false)) {
+ tp = true;
+ }
+ } catch (Exception e) {
+ CMS.debug("NetkeyKeygenService: kgConfig.getBoolean failed");
+ // by default, let nethsm work
+ tp = true;
+ }
+ } else {
+ // by default, let nethsm work
+ CMS.debug("NetkeyKeygenService: cannot find config store: kra.keygen, assume temporaryPairs==true");
+ tp = true;
+ }
+ /* only specified to "true" will it be set */
+ if (tp == true) {
+ CMS.debug("NetkeyKeygenService: setting temporaryPairs to true");
+ kpGen.temporaryPairs(true);
+ }
+ if (sp == true) {
+ CMS.debug("NetkeyKeygenService: setting sensitivePairs to true");
+ kpGen.sensitivePairs(true);
+ }
+ if (ep == true) {
+ CMS.debug("NetkeyKeygenService: setting extractablePairs to true");
+ kpGen.extractablePairs(true);
+ }
+
+ if (kpAlg == KeyPairAlgorithm.DSA) {
+ if (pqg == null) {
+ kpGen.initialize(keySize);
+ } else {
+ kpGen.initialize(pqg);
+ }
+ } else {
+ kpGen.initialize(keySize);
+ }
+
+ if (pqg == null) {
+ KeyPair kp = null;
+ synchronized (new Object()) {
+ CMS.debug("NetkeyKeygenService: key pair generation begins");
+ kp = kpGen.genKeyPair();
+ CMS.debug("NetkeyKeygenService: key pair generation done");
+ mKRA.addEntropy(true);
+ }
+ return kp;
+ } else {
+ // DSA
+ KeyPair kp = null;
+
+ /* no DSA for now... netkey prototype
+ do {
+ // 602548 NSS bug - to overcome it, we use isBadDSAKeyPair
+ kp = kpGen.genKeyPair();
+ }
+ while (isBadDSAKeyPair(kp));
+ */
+ return kp;
+ }
+ }
+
+ public KeyPair generateKeyPair(String alg,
+ int keySize, PQGParams pqg) throws EBaseException {
+
+ KeyPairAlgorithm kpAlg = null;
+
+ if (alg.equals("RSA"))
+ kpAlg = KeyPairAlgorithm.RSA;
+ else
+ kpAlg = KeyPairAlgorithm.DSA;
+
+ try {
+ KeyPair kp = generateKeyPair(kpAlg, keySize, pqg);
+
+ return kp;
+ } catch (InvalidParameterException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEYSIZE_PARAMS",
+ "" + keySize));
+ } catch (PQGParamGenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_PQG_GEN_FAILED"));
+ } catch (NoSuchAlgorithmException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_ALG_NOT_SUPPORTED",
+ kpAlg.toString()));
+ } catch (TokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_TOKEN_ERROR_1", e.toString()));
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_ALG_NOT_SUPPORTED", "DSA"));
+ }
+ }
+
+ private static String base64Encode(byte[] bytes) throws IOException {
+ // All this streaming is lame, but Base64OutputStream needs a
+ // PrintStream
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ Base64OutputStream b64 = new Base64OutputStream(new
+ PrintStream(new
+ FilterOutputStream(output)
+ )
+ );
+
+ b64.write(bytes);
+ b64.flush();
+
+ // This is internationally safe because Base64 chars are
+ // contained within 8859_1
+ return output.toString("8859_1");
+ }
+
+ // this encrypts bytes with a symmetric key
+ public byte[] encryptIt(byte[] toBeEncrypted, SymmetricKey symKey, CryptoToken token,
+ IVParameterSpec IV) {
+ try {
+ Cipher cipher = token.getCipherContext(
+ EncryptionAlgorithm.DES3_CBC_PAD);
+
+ cipher.initEncrypt(symKey, IV);
+ byte pri[] = cipher.doFinal(toBeEncrypted);
+ return pri;
+ } catch (Exception e) {
+ CMS.debug("NetkeyKeygenService:initEncrypt() threw exception: " + e.toString());
+ return null;
+ }
+
+ }
+
+ /**
+ * Services an archival request from netkey.
+ * <P>
+ *
+ * @param request enrollment request
+ * @return serving successful or not
+ * @exception EBaseException failed to serve
+ */
+ public boolean serviceRequest(IRequest request)
+ throws EBaseException {
+ String auditMessage = null;
+ String auditSubjectID = null;
+ String auditArchiveID = ILogger.UNIDENTIFIED;
+ byte[] wrapped_des_key;
+
+ byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
+ String iv_s = "";
+ try {
+ SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+ random.nextBytes(iv);
+ } catch (Exception e) {
+ CMS.debug("NetkeyKeygenService.serviceRequest: " + e.toString());
+ }
+
+ IVParameterSpec algParam = new IVParameterSpec(iv);
+
+ wrapped_des_key = null;
+ boolean archive = true;
+ PK11SymKey sk = null;
+ byte[] publicKeyData = null;
+ ;
+ String PubKey = "";
+
+ String id = request.getRequestId().toString();
+ if (id != null) {
+ auditArchiveID = id.trim();
+ }
+
+ String rArchive = request.getExtDataInString(IRequest.NETKEY_ATTR_ARCHIVE_FLAG);
+ if (rArchive.equals("true")) {
+ archive = true;
+ CMS.debug("NetkeyKeygenService: serviceRequest " + "archival requested for serverSideKeyGen");
+ } else {
+ archive = false;
+ CMS.debug("NetkeyKeygenService: serviceRequest " + "archival not requested for serverSideKeyGen");
+ }
+
+ String rCUID = request.getExtDataInString(IRequest.NETKEY_ATTR_CUID);
+ String rUserid = request.getExtDataInString(IRequest.NETKEY_ATTR_USERID);
+ String rKeysize = request.getExtDataInString(IRequest.NETKEY_ATTR_KEY_SIZE);
+ int keysize = Integer.parseInt(rKeysize);
+ auditSubjectID = rCUID + ":" + rUserid;
+
+ SessionContext sContext = SessionContext.getContext();
+ String agentId = "";
+ if (sContext != null) {
+ agentId =
+ (String) sContext.get(SessionContext.USER_ID);
+ }
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST,
+ agentId,
+ ILogger.SUCCESS,
+ auditSubjectID);
+
+ audit(auditMessage);
+
+ String rWrappedDesKeyString = request.getExtDataInString(IRequest.NETKEY_ATTR_DRMTRANS_DES_KEY);
+ // CMS.debug("NetkeyKeygenService: received DRM-trans-wrapped DES key ="+rWrappedDesKeyString);
+ wrapped_des_key = com.netscape.cmsutil.util.Utils.SpecialDecode(rWrappedDesKeyString);
+ CMS.debug("NetkeyKeygenService: wrapped_des_key specialDecoded");
+
+ // get the token for generating user keys
+ CryptoToken keygenToken = mKRA.getKeygenToken();
+ if (keygenToken == null) {
+ CMS.debug("NetkeyKeygenService: failed getting keygenToken");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(10));
+ return false;
+ } else
+ CMS.debug("NetkeyKeygenService: got keygenToken");
+
+ if ((wrapped_des_key != null) &&
+ (wrapped_des_key.length > 0)) {
+
+ // unwrap the DES key
+ sk = (PK11SymKey) mTransportUnit.unwrap_sym(wrapped_des_key);
+
+ /* XXX could be done in HSM*/
+ KeyPair keypair = null;
+
+ CMS.debug("NetkeyKeygenService: about to generate key pair");
+
+ keypair = generateKeyPair("RSA"/*alg*/,
+ keysize /*Integer.parseInt(len)*/, null /*pqgParams*/);
+
+ if (keypair == null) {
+ CMS.debug("NetkeyKeygenService: failed generating key pair for " + rCUID + ":" + rUserid);
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,
+ agentId,
+ ILogger.FAILURE,
+ auditSubjectID);
+
+ audit(auditMessage);
+
+ return false;
+ }
+ CMS.debug("NetkeyKeygenService: finished generate key pair for " + rCUID + ":" + rUserid);
+
+ try {
+ publicKeyData = keypair.getPublic().getEncoded();
+ if (publicKeyData == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ CMS.debug("NetkeyKeygenService: failed getting publickey encoded");
+ return false;
+ } else {
+ //CMS.debug("NetkeyKeygenService: public key binary length ="+ publicKeyData.length);
+ PubKey = base64Encode(publicKeyData);
+
+ //CMS.debug("NetkeyKeygenService: public key length =" + PubKey.length());
+ request.setExtData("public_key", PubKey);
+ }
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,
+ agentId,
+ ILogger.SUCCESS,
+ auditSubjectID,
+ PubKey);
+
+ audit(auditMessage);
+
+ //...extract the private key handle (not privatekeydata)
+ java.security.PrivateKey privKey =
+ keypair.getPrivate();
+
+ if (privKey == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ CMS.debug("NetkeyKeygenService: failed getting private key");
+ return false;
+ } else {
+ CMS.debug("NetkeyKeygenService: got private key");
+ }
+
+ if (sk == null) {
+ CMS.debug("NetkeyKeygenService: no DES key");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ return false;
+ } else {
+ CMS.debug("NetkeyKeygenService: received DES key");
+ }
+
+ // 3 wrapping should be done in HSM
+ // wrap private key with DES
+ KeyWrapper symWrap =
+ keygenToken.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD);
+ CMS.debug("NetkeyKeygenService: wrapper token=" + keygenToken.getName());
+ CMS.debug("NetkeyKeygenService: got key wrapper");
+
+ CMS.debug("NetkeyKeygenService: key transport key is on slot: " + sk.getOwningToken().getName());
+ symWrap.initWrap((SymmetricKey) sk, algParam);
+ byte wrapped[] = symWrap.wrap((PrivateKey) privKey);
+ /*
+ CMS.debug("NetkeyKeygenService: wrap called");
+ CMS.debug(wrapped);
+ */
+ /* This is for using with my decryption tool and ASN1
+ decoder to see if the private key is indeed PKCS#8 format
+ { // cfu debug
+ String oFilePath = "/tmp/wrappedPrivKey.bin";
+ File file = new File(oFilePath);
+ FileOutputStream ostream = new FileOutputStream(oFilePath);
+ ostream.write(wrapped);
+ ostream.close();
+ }
+ */
+ String wrappedPrivKeyString = /*base64Encode(wrapped);*/
+ com.netscape.cmsutil.util.Utils.SpecialEncode(wrapped);
+ if (wrappedPrivKeyString == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ CMS.debug("NetkeyKeygenService: failed generating wrapped private key");
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,
+ agentId,
+ ILogger.FAILURE,
+ auditSubjectID,
+ PubKey);
+
+ audit(auditMessage);
+ return false;
+ } else {
+ request.setExtData("wrappedUserPrivate", wrappedPrivKeyString);
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,
+ agentId,
+ ILogger.SUCCESS,
+ auditSubjectID,
+ PubKey);
+
+ audit(auditMessage);
+ }
+
+ iv_s = /*base64Encode(iv);*/com.netscape.cmsutil.util.Utils.SpecialEncode(iv);
+ request.setExtData("iv_s", iv_s);
+
+ /*
+ * archival - option flag "archive" controllable by the caller - TPS
+ */
+ if (archive) {
+ //
+ // privateKeyData ::= SEQUENCE {
+ // sessionKey OCTET_STRING,
+ // encKey OCTET_STRING,
+ // }
+ //
+ // mKRA.log(ILogger.LL_INFO, "KRA encrypts internal private");
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ agentId,
+ ILogger.SUCCESS,
+ auditSubjectID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ CMS.debug("KRA encrypts private key to put on internal ldap db");
+ byte privateKeyData[] =
+ mStorageUnit.wrap((org.mozilla.jss.crypto.PrivateKey) privKey);
+
+ if (privateKeyData == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ CMS.debug("NetkeyKeygenService: privatekey encryption by storage unit failed");
+ return false;
+ } else
+ CMS.debug("NetkeyKeygenService: privatekey encryption by storage unit successful");
+
+ // create key record
+ KeyRecord rec = new KeyRecord(null, publicKeyData,
+ privateKeyData, rCUID + ":" + rUserid,
+ keypair.getPublic().getAlgorithm(),
+ agentId);
+
+ CMS.debug("NetkeyKeygenService: got key record");
+
+ // we deal with RSA key only
+ try {
+ RSAPublicKey rsaPublicKey = new RSAPublicKey(publicKeyData);
+
+ rec.setKeySize(Integer.valueOf(rsaPublicKey.getKeySize()));
+ } catch (InvalidKeyException e) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(11));
+ CMS.debug("NetkeyKeygenService: failed:InvalidKeyException");
+ return false;
+ }
+ //??
+ IKeyRepository storage = mKRA.getKeyRepository();
+ BigInteger serialNo = storage.getNextSerialNumber();
+
+ if (serialNo == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(11));
+ CMS.debug("NetkeyKeygenService: serialNo null");
+ return false;
+ }
+ CMS.debug("NetkeyKeygenService: before addKeyRecord");
+ rec.set(KeyRecord.ATTR_ID, serialNo);
+ request.setExtData(ATTR_KEY_RECORD, serialNo);
+ storage.addKeyRecord(rec);
+ CMS.debug("NetkeyKeygenService: key archived for " + rCUID + ":" + rUserid);
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,
+ agentId,
+ ILogger.SUCCESS,
+ PubKey);
+
+ audit(auditMessage);
+
+ } //if archive
+
+ request.setExtData(IRequest.RESULT, Integer.valueOf(1));
+ } catch (Exception e) {
+ CMS.debug("NetKeyKeygenService: " + e.toString());
+ Debug.printStackTrace(e);
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ }
+ } else
+ request.setExtData(IRequest.RESULT, Integer.valueOf(2));
+
+ return true;
+ } //serviceRequest
+
+ /**
+ * Signed Audit Log
+ * y
+ * This method is called to store messages to the signed audit log.
+ * <P>
+ *
+ * @param msg signed audit log message
+ */
+ private void audit(String msg) {
+ // in this case, do NOT strip preceding/trailing whitespace
+ // from passed-in String parameters
+
+ if (mSignedAuditLogger == null) {
+ return;
+ }
+
+ mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT,
+ null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ msg);
+ }
+}
diff --git a/base/kra/src/com/netscape/kra/RecoveryService.java b/base/kra/src/com/netscape/kra/RecoveryService.java
new file mode 100644
index 000000000..c8ecdcf5a
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/RecoveryService.java
@@ -0,0 +1,710 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.ByteArrayOutputStream;
+import java.io.CharConversionException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Hashtable;
+
+import netscape.security.util.BigInt;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerValue;
+import netscape.security.x509.X509CertImpl;
+import netscape.security.x509.X509Key;
+
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.asn1.ASN1Util;
+import org.mozilla.jss.asn1.ASN1Value;
+import org.mozilla.jss.asn1.BMPString;
+import org.mozilla.jss.asn1.OCTET_STRING;
+import org.mozilla.jss.asn1.SEQUENCE;
+import org.mozilla.jss.asn1.SET;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.PBEAlgorithm;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.pkcs12.AuthenticatedSafes;
+import org.mozilla.jss.pkcs12.CertBag;
+import org.mozilla.jss.pkcs12.PFX;
+import org.mozilla.jss.pkcs12.PasswordConverter;
+import org.mozilla.jss.pkcs12.SafeBag;
+import org.mozilla.jss.pkix.primitive.EncryptedPrivateKeyInfo;
+import org.mozilla.jss.pkix.primitive.PrivateKeyInfo;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.AuthToken;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.certsrv.kra.EKRAException;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.logging.AuditFormat;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.IService;
+import com.netscape.certsrv.security.Credential;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.certsrv.util.IStatsSubsystem;
+import com.netscape.cmscore.dbs.KeyRecord;
+import com.netscape.cmscore.util.Debug;
+
+/**
+ * A class represents recovery request processor. There
+ * are 2 types of recovery modes: (1) administrator or
+ * (2) end-entity.
+ * <P>
+ * Administrator recovery will create a PKCS12 file where stores the certificate and the recovered key.
+ * <P>
+ * End Entity recovery will send RA or CA a response where stores the recovered key.
+ *
+ * @author thomask (original)
+ * @author cfu (non-RSA keys; private keys secure handling);
+ * @version $Revision$, $Date$
+ */
+public class RecoveryService implements IService {
+
+ public static final String ATTR_NICKNAME = "nickname";
+ public static final String ATTR_OWNER_NAME = "ownerName";
+ public static final String ATTR_SERIALNO = "serialNumber";
+ public static final String ATTR_PUBLIC_KEY_DATA = "publicKeyData";
+ public static final String ATTR_PRIVATE_KEY_DATA = "privateKeyData";
+ public static final String ATTR_TRANSPORT_CERT = "transportCert";
+ public static final String ATTR_TRANSPORT_PWD = "transportPwd";
+ public static final String ATTR_SIGNING_CERT = "signingCert";
+ public static final String ATTR_PKCS12 = "pkcs12";
+ public static final String ATTR_ENCRYPTION_CERTS =
+ "encryptionCerts";
+ public static final String ATTR_AGENT_CREDENTIALS =
+ "agentCredentials";
+ // same as encryption certs
+ public static final String ATTR_USER_CERT = "cert";
+ public static final String ATTR_DELIVERY = "delivery";
+
+ // for Async Key Recovery
+ public static final String ATTR_APPROVE_AGENTS = "approvingAgents";
+
+ private IKeyRecoveryAuthority mKRA = null;
+ private IKeyRepository mStorage = null;
+ private IStorageKeyUnit mStorageUnit = null;
+
+ /**
+ * Constructs request processor.
+ */
+ public RecoveryService(IKeyRecoveryAuthority kra) {
+ mKRA = kra;
+ mStorage = mKRA.getKeyRepository();
+ mStorageUnit = mKRA.getStorageKeyUnit();
+ }
+
+ /**
+ * Processes a recovery request. Based on the recovery mode
+ * (either Administrator or End-Entity), the method reads
+ * the key record from the database, and tried to recover the
+ * key with the storage key unit.
+ *
+ * @param request recovery request
+ * @return operation success or not
+ * @exception EBaseException failed to serve
+ */
+ public boolean serviceRequest(IRequest request) throws EBaseException {
+
+ CryptoManager cm = null;
+ IConfigStore config = null;
+ String tokName = "";
+ CryptoToken ct = null;
+ Boolean allowEncDecrypt_recovery = false;
+
+ try {
+ cm = CryptoManager.getInstance();
+ config = CMS.getConfigStore();
+ tokName = config.getString("kra.storageUnit.hardware", "internal");
+ if (tokName.equals("internal")) {
+ CMS.debug("RecoveryService: serviceRequest: use internal token ");
+ ct = cm.getInternalCryptoToken();
+ } else {
+ CMS.debug("RecoveryService: serviceRequest: tokenName=" + tokName);
+ ct = cm.getTokenByName(tokName);
+ }
+ allowEncDecrypt_recovery = config.getBoolean("kra.allowEncDecrypt.recovery", false);
+ } catch (Exception e) {
+ CMS.debug("RecoveryService exception: use internal token :"
+ + e.toString());
+ ct = cm.getInternalCryptoToken();
+ }
+ if (ct == null) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR" + "cannot get crypto token"));
+ }
+
+ IStatsSubsystem statsSub = (IStatsSubsystem) CMS.getSubsystem("stats");
+ if (statsSub != null) {
+ statsSub.startTiming("recovery", true /* main action */);
+ }
+
+ if (Debug.ON)
+ Debug.trace("KRA services recovery request");
+ mKRA.log(ILogger.LL_INFO, "KRA services recovery request");
+
+ // byte publicKey[] = (byte[])request.get(ATTR_PUBLIC_KEY_DATA);
+ // X500Name owner = (X500Name)request.get(ATTR_OWNER_NAME);
+
+ Hashtable<String, Object> params = mKRA.getVolatileRequest(
+ request.getRequestId());
+
+ if (params == null) {
+ // possibly we are in recovery mode
+ return true;
+ }
+
+ // retrieve based on serial no
+ BigInteger serialno = request.getExtDataInBigInteger(ATTR_SERIALNO);
+
+ mKRA.log(ILogger.LL_INFO, "KRA reading key record");
+ if (statsSub != null) {
+ statsSub.startTiming("get_key");
+ }
+ KeyRecord keyRecord = (KeyRecord) mStorage.readKeyRecord(serialno);
+ if (statsSub != null) {
+ statsSub.endTiming("get_key");
+ }
+
+ // see if the certificate matches the key
+ byte pubData[] = keyRecord.getPublicKeyData();
+ X509Certificate x509cert =
+ request.getExtDataInCert(ATTR_USER_CERT);
+ byte inputPubData[] = x509cert.getPublicKey().getEncoded();
+
+ if (inputPubData.length != pubData.length) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN"));
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED"));
+ }
+ for (int i = 0; i < pubData.length; i++) {
+ if (pubData[i] != inputPubData[i]) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN"));
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED"));
+ }
+ }
+
+ boolean isRSA = true;
+ String keyAlg = x509cert.getPublicKey().getAlgorithm();
+ if (keyAlg != null) {
+ CMS.debug("RecoveryService: publicKey alg =" + keyAlg);
+ if (!keyAlg.equals("RSA"))
+ isRSA = false;
+ }
+
+ // Unwrap the archived private key
+ byte privateKeyData[] = null;
+ X509Certificate transportCert =
+ request.getExtDataInCert(ATTR_TRANSPORT_CERT);
+
+ if (transportCert == null) {
+ if (statsSub != null) {
+ statsSub.startTiming("recover_key");
+ }
+
+ PrivateKey privKey = null;
+ if (allowEncDecrypt_recovery == true) {
+ privateKeyData = recoverKey(params, keyRecord);
+ } else {
+ privKey = recoverKey(params, keyRecord, isRSA);
+ }
+ if (statsSub != null) {
+ statsSub.endTiming("recover_key");
+ }
+
+ if ((isRSA == true) && (allowEncDecrypt_recovery == true)) {
+ if (statsSub != null) {
+ statsSub.startTiming("verify_key");
+ }
+ // verifyKeyPair() is RSA-centric
+ if (verifyKeyPair(pubData, privateKeyData) == false) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND"));
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY"));
+ }
+ if (statsSub != null) {
+ statsSub.endTiming("verify_key");
+ }
+ }
+
+ if (statsSub != null) {
+ statsSub.startTiming("create_p12");
+ }
+ if (allowEncDecrypt_recovery == true) {
+ createPFX(request, params, privateKeyData);
+ } else {
+ createPFX(request, params, privKey, ct);
+ }
+ if (statsSub != null) {
+ statsSub.endTiming("create_p12");
+ }
+ } else {
+
+ if (CMS.getConfigStore().getBoolean("kra.keySplitting")) {
+ Credential creds[] = (Credential[])
+ params.get(ATTR_AGENT_CREDENTIALS);
+ mKRA.getStorageKeyUnit().login(creds);
+ }
+ if (statsSub != null) {
+ statsSub.startTiming("unwrap_key");
+ }
+ mKRA.getStorageKeyUnit().unwrap(
+ keyRecord.getPrivateKeyData(), null); // throw exception on error
+ if (statsSub != null) {
+ statsSub.endTiming("unwrap_key");
+ }
+
+ if (CMS.getConfigStore().getBoolean("kra.keySplitting")) {
+ mKRA.getStorageKeyUnit().logout();
+ }
+ }
+ mKRA.log(ILogger.LL_INFO, "key " +
+ serialno.toString() +
+ " recovered");
+
+ // for audit log
+ String authMgr = AuditFormat.NOAUTH;
+ String initiative = AuditFormat.FROMUSER;
+ SessionContext sContext = SessionContext.getContext();
+
+ if (sContext != null) {
+ String agentId =
+ (String) sContext.get(SessionContext.USER_ID);
+
+ initiative = AuditFormat.FROMAGENT + " agentID: " + agentId;
+ AuthToken authToken = (AuthToken) sContext.get(SessionContext.AUTH_TOKEN);
+
+ if (authToken != null) {
+ authMgr =
+ authToken.getInString(AuthToken.TOKEN_AUTHMGR_INST_NAME);
+ }
+ }
+ CMS.getLogger().log(ILogger.EV_AUDIT,
+ ILogger.S_KRA,
+ AuditFormat.LEVEL,
+ AuditFormat.FORMAT,
+ new Object[] {
+ IRequest.KEYRECOVERY_REQUEST,
+ request.getRequestId(),
+ initiative,
+ authMgr,
+ "completed",
+ ((X509CertImpl) x509cert).getSubjectDN(),
+ "serial number: 0x" + serialno.toString(16) }
+ );
+
+ if (statsSub != null) {
+ statsSub.endTiming("recovery");
+ }
+
+ return true;
+ }
+
+ /*
+ * verifyKeyPair()- RSA-centric key verification
+ */
+ public boolean verifyKeyPair(byte publicKeyData[], byte privateKeyData[]) {
+ try {
+ DerValue publicKeyVal = new DerValue(publicKeyData);
+ DerInputStream publicKeyIn = publicKeyVal.data;
+ publicKeyIn.getSequence(0);
+ DerValue publicKeyDer = new DerValue(publicKeyIn.getBitString());
+ DerInputStream publicKeyDerIn = publicKeyDer.data;
+ BigInt publicKeyModulus = publicKeyDerIn.getInteger();
+ BigInt publicKeyExponent = publicKeyDerIn.getInteger();
+
+ DerValue privateKeyVal = new DerValue(privateKeyData);
+ if (privateKeyVal.tag != DerValue.tag_Sequence)
+ return false;
+ DerInputStream privateKeyIn = privateKeyVal.data;
+ privateKeyIn.getInteger();
+ privateKeyIn.getSequence(0);
+ DerValue privateKeyDer = new DerValue(privateKeyIn.getOctetString());
+ DerInputStream privateKeyDerIn = privateKeyDer.data;
+
+ @SuppressWarnings("unused")
+ BigInt privateKeyVersion = privateKeyDerIn.getInteger();
+ BigInt privateKeyModulus = privateKeyDerIn.getInteger();
+ BigInt privateKeyExponent = privateKeyDerIn.getInteger();
+
+ if (!publicKeyModulus.equals(privateKeyModulus)) {
+ CMS.debug("verifyKeyPair modulus mismatch publicKeyModulus="
+ + publicKeyModulus + " privateKeyModulus=" + privateKeyModulus);
+ return false;
+ }
+
+ if (!publicKeyExponent.equals(privateKeyExponent)) {
+ CMS.debug("verifyKeyPair exponent mismatch publicKeyExponent="
+ + publicKeyExponent + " privateKeyExponent=" + privateKeyExponent);
+ return false;
+ }
+
+ return true;
+ } catch (Exception e) {
+ CMS.debug("verifyKeyPair error " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Recovers key. (using unwrapping/wrapping on token)
+ * - used when allowEncDecrypt_recovery is false
+ */
+ public synchronized PrivateKey recoverKey(Hashtable<String, Object> request, KeyRecord keyRecord, boolean isRSA)
+ throws EBaseException {
+
+ if (!isRSA) {
+ CMS.debug("RecoverService: recoverKey: currently, non-RSA keys are not supported when allowEncDecrypt_ is false");
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "key type not supported"));
+ }
+ try {
+ if (CMS.getConfigStore().getBoolean("kra.keySplitting")) {
+ Credential creds[] = (Credential[])
+ request.get(ATTR_AGENT_CREDENTIALS);
+
+ mStorageUnit.login(creds);
+ }
+
+ /* wrapped retrieve session key and private key */
+ DerValue val = new DerValue(keyRecord.getPrivateKeyData());
+ DerInputStream in = val.data;
+ DerValue dSession = in.getDerValue();
+ byte session[] = dSession.getOctetString();
+ DerValue dPri = in.getDerValue();
+ byte pri[] = dPri.getOctetString();
+
+ /* debug */
+ byte publicKeyData[] = keyRecord.getPublicKeyData();
+ PublicKey pubkey = null;
+ try {
+ pubkey = X509Key.parsePublicKey(new DerValue(publicKeyData));
+ } catch (Exception e) {
+ CMS.debug("RecoverService: after parsePublicKey:" + e.toString());
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "pubic key parsing failure"));
+ }
+ byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
+ PrivateKey privKey =
+ mStorageUnit.unwrap(
+ session,
+ keyRecord.getAlgorithm(),
+ iv,
+ pri,
+ (PublicKey) pubkey);
+
+ if (privKey == null) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PRIVATE_KEY_NOT_FOUND"));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1",
+ "private key unwrapping failure"));
+ }
+ if (CMS.getConfigStore().getBoolean("kra.keySplitting")) {
+ mStorageUnit.logout();
+ }
+ return privKey;
+ } catch (Exception e) {
+ CMS.debug("RecoverService: recoverKey() failed with allowEncDecrypt_recovery=false:" + e.toString());
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1",
+ "recoverKey() failed with allowEncDecrypt_recovery=false:" + e.toString()));
+ }
+ }
+
+ /**
+ * Creates a PFX (PKCS12) file. (the unwrapping/wrapping way)
+ * - used when allowEncDecrypt_recovery is false
+ *
+ * @param request CRMF recovery request
+ * @param priKey private key handle
+ * @exception EBaseException failed to create P12 file
+ */
+ public void createPFX(IRequest request, Hashtable<String, Object> params,
+ PrivateKey priKey, CryptoToken ct) throws EBaseException {
+ CMS.debug("RecoverService: createPFX() allowEncDecrypt_recovery=false");
+ try {
+ // create p12
+ X509Certificate x509cert =
+ request.getExtDataInCert(ATTR_USER_CERT);
+ String pwd = (String) params.get(ATTR_TRANSPORT_PWD);
+
+ // add certificate
+ mKRA.log(ILogger.LL_INFO, "KRA adds certificate to P12");
+ CMS.debug("RecoverService: createPFX() adds certificate to P12");
+ SEQUENCE encSafeContents = new SEQUENCE();
+ ASN1Value cert = new OCTET_STRING(x509cert.getEncoded());
+ String nickname = request.getExtDataInString(ATTR_NICKNAME);
+
+ if (nickname == null) {
+ nickname = x509cert.getSubjectDN().toString();
+ }
+ byte localKeyId[] = createLocalKeyId(x509cert);
+ SET certAttrs = createBagAttrs(
+ nickname, localKeyId);
+ // attributes: user friendly name, Local Key ID
+ SafeBag certBag = new SafeBag(SafeBag.CERT_BAG,
+ new CertBag(CertBag.X509_CERT_TYPE, cert),
+ certAttrs);
+
+ encSafeContents.addElement(certBag);
+
+ // add key
+ mKRA.log(ILogger.LL_INFO, "KRA adds key to P12");
+ CMS.debug("RecoverService: createPFX() adds key to P12");
+ org.mozilla.jss.util.Password pass = new
+ org.mozilla.jss.util.Password(
+ pwd.toCharArray());
+
+ SEQUENCE safeContents = new SEQUENCE();
+ PasswordConverter passConverter = new
+ PasswordConverter();
+ byte salt[] = { 0x01, 0x01, 0x01, 0x01 };
+
+ ASN1Value key = EncryptedPrivateKeyInfo.createPBE(
+ PBEAlgorithm.PBE_SHA1_DES3_CBC,
+ pass, salt, 1, passConverter, priKey, ct);
+
+ SET keyAttrs = createBagAttrs(
+ x509cert.getSubjectDN().toString(),
+ localKeyId);
+
+ SafeBag keyBag = new SafeBag(
+ SafeBag.PKCS8_SHROUDED_KEY_BAG, key,
+ keyAttrs); // ??
+
+ safeContents.addElement(keyBag);
+
+ // build contents
+ AuthenticatedSafes authSafes = new
+ AuthenticatedSafes();
+
+ authSafes.addSafeContents(
+ safeContents
+ );
+ authSafes.addSafeContents(
+ encSafeContents
+ );
+
+ // authSafes.addEncryptedSafeContents(
+ // authSafes.DEFAULT_KEY_GEN_ALG,
+ // pass, null, 1,
+ // encSafeContents);
+ PFX pfx = new PFX(authSafes);
+
+ pfx.computeMacData(pass, null, 5); // ??
+ ByteArrayOutputStream fos = new
+ ByteArrayOutputStream();
+
+ pfx.encode(fos);
+ pass.clear();
+
+ // put final PKCS12 into volatile request
+ params.put(ATTR_PKCS12, fos.toByteArray());
+ } catch (Exception e) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_CONSTRUCT_P12", e.toString()));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_PKCS12_FAILED_1", e.toString()));
+ }
+
+ // update request
+ mKRA.getRequestQueue().updateRequest(request);
+ }
+
+ /**
+ * Recovers key.
+ * - used when allowEncDecrypt_recovery is true
+ */
+ public synchronized byte[] recoverKey(Hashtable<String, Object> request, KeyRecord keyRecord)
+ throws EBaseException {
+ if (CMS.getConfigStore().getBoolean("kra.keySplitting")) {
+ Credential creds[] = (Credential[])
+ request.get(ATTR_AGENT_CREDENTIALS);
+
+ mStorageUnit.login(creds);
+ }
+ mKRA.log(ILogger.LL_INFO, "KRA decrypts internal private");
+ byte privateKeyData[] =
+ mStorageUnit.decryptInternalPrivate(
+ keyRecord.getPrivateKeyData());
+
+ if (CMS.getConfigStore().getBoolean("kra.keySplitting")) {
+ mStorageUnit.logout();
+ }
+ if (privateKeyData == null) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PRIVATE_KEY_NOT_FOUND"));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "no private key"));
+ }
+ return privateKeyData;
+ }
+
+ /**
+ * Creates a PFX (PKCS12) file.
+ * - used when allowEncDecrypt_recovery is true
+ *
+ * @param request CRMF recovery request
+ * @param priData decrypted private key (PrivateKeyInfo)
+ * @exception EBaseException failed to create P12 file
+ */
+ public void createPFX(IRequest request, Hashtable<String, Object> params,
+ byte priData[]) throws EBaseException {
+ CMS.debug("RecoverService: createPFX() allowEncDecrypt_recovery=true");
+ try {
+ // create p12
+ X509Certificate x509cert =
+ request.getExtDataInCert(ATTR_USER_CERT);
+ String pwd = (String) params.get(ATTR_TRANSPORT_PWD);
+
+ // add certificate
+ mKRA.log(ILogger.LL_INFO, "KRA adds certificate to P12");
+ SEQUENCE encSafeContents = new SEQUENCE();
+ ASN1Value cert = new OCTET_STRING(x509cert.getEncoded());
+ String nickname = request.getExtDataInString(ATTR_NICKNAME);
+
+ if (nickname == null) {
+ nickname = x509cert.getSubjectDN().toString();
+ }
+ byte localKeyId[] = createLocalKeyId(x509cert);
+ SET certAttrs = createBagAttrs(
+ nickname, localKeyId);
+ // attributes: user friendly name, Local Key ID
+ SafeBag certBag = new SafeBag(SafeBag.CERT_BAG,
+ new CertBag(CertBag.X509_CERT_TYPE, cert),
+ certAttrs);
+
+ encSafeContents.addElement(certBag);
+
+ // add key
+ mKRA.log(ILogger.LL_INFO, "KRA adds key to P12");
+ org.mozilla.jss.util.Password pass = new
+ org.mozilla.jss.util.Password(
+ pwd.toCharArray());
+
+ SEQUENCE safeContents = new SEQUENCE();
+ PasswordConverter passConverter = new
+ PasswordConverter();
+ byte salt[] = { 0x01, 0x01, 0x01, 0x01 };
+ PrivateKeyInfo pki = (PrivateKeyInfo)
+ ASN1Util.decode(PrivateKeyInfo.getTemplate(),
+ priData);
+ ASN1Value key = EncryptedPrivateKeyInfo.createPBE(
+ PBEAlgorithm.PBE_SHA1_DES3_CBC,
+ pass, salt, 1, passConverter, pki);
+ SET keyAttrs = createBagAttrs(
+ x509cert.getSubjectDN().toString(),
+ localKeyId);
+ SafeBag keyBag = new SafeBag(
+ SafeBag.PKCS8_SHROUDED_KEY_BAG, key,
+ keyAttrs); // ??
+
+ safeContents.addElement(keyBag);
+
+ // build contents
+ AuthenticatedSafes authSafes = new
+ AuthenticatedSafes();
+
+ authSafes.addSafeContents(
+ safeContents
+ );
+ authSafes.addSafeContents(
+ encSafeContents
+ );
+
+ // authSafes.addEncryptedSafeContents(
+ // authSafes.DEFAULT_KEY_GEN_ALG,
+ // pass, null, 1,
+ // encSafeContents);
+ PFX pfx = new PFX(authSafes);
+
+ pfx.computeMacData(pass, null, 5); // ??
+ ByteArrayOutputStream fos = new
+ ByteArrayOutputStream();
+
+ pfx.encode(fos);
+ pass.clear();
+
+ // put final PKCS12 into volatile request
+ params.put(ATTR_PKCS12, fos.toByteArray());
+ } catch (Exception e) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_CONSTRUCT_P12", e.toString()));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_PKCS12_FAILED_1", e.toString()));
+ }
+
+ // update request
+ mKRA.getRequestQueue().updateRequest(request);
+ }
+
+ /**
+ * Creates local key identifier.
+ */
+ public byte[] createLocalKeyId(X509Certificate cert)
+ throws EBaseException {
+ try {
+ // SHA1 hash of the X509Cert der encoding
+ byte certDer[] = cert.getEncoded();
+
+ // XXX - should use JSS
+ MessageDigest md = MessageDigest.getInstance("SHA");
+
+ md.update(certDer);
+ return md.digest();
+ } catch (CertificateEncodingException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_CREAT_KEY_ID", e.toString()));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_KEYID_FAILED_1", e.toString()));
+ } catch (NoSuchAlgorithmException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_CREAT_KEY_ID", e.toString()));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_KEYID_FAILED_1", e.toString()));
+ }
+ }
+
+ /**
+ * Creates bag attributes.
+ */
+ public SET createBagAttrs(String nickName, byte localKeyId[])
+ throws EBaseException {
+ try {
+ SET attrs = new SET();
+ SEQUENCE nickNameAttr = new SEQUENCE();
+
+ nickNameAttr.addElement(SafeBag.FRIENDLY_NAME);
+ SET nickNameSet = new SET();
+
+ nickNameSet.addElement(new BMPString(nickName));
+ nickNameAttr.addElement(nickNameSet);
+ attrs.addElement(nickNameAttr);
+ SEQUENCE localKeyAttr = new SEQUENCE();
+
+ localKeyAttr.addElement(SafeBag.LOCAL_KEY_ID);
+ SET localKeySet = new SET();
+
+ localKeySet.addElement(new OCTET_STRING(localKeyId));
+ localKeyAttr.addElement(localKeySet);
+ attrs.addElement(localKeyAttr);
+ return attrs;
+ } catch (CharConversionException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_CREAT_KEY_BAG", e.toString()));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_KEYBAG_FAILED_1", e.toString()));
+ }
+ }
+}
diff --git a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
new file mode 100644
index 000000000..f96ece890
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
@@ -0,0 +1,388 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.ByteArrayOutputStream;
+import java.io.CharConversionException;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Hashtable;
+import java.util.Random;
+
+import javax.crypto.spec.RC2ParameterSpec;
+
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.asn1.OCTET_STRING;
+import org.mozilla.jss.crypto.Cipher;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.EncryptionAlgorithm;
+import org.mozilla.jss.crypto.IVParameterSpec;
+import org.mozilla.jss.crypto.KeyGenerator;
+import org.mozilla.jss.crypto.KeyWrapAlgorithm;
+import org.mozilla.jss.crypto.KeyWrapper;
+import org.mozilla.jss.crypto.PBEAlgorithm;
+import org.mozilla.jss.crypto.PBEKeyGenParams;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.TokenException;
+import org.mozilla.jss.pkcs12.PasswordConverter;
+import org.mozilla.jss.pkcs7.ContentInfo;
+import org.mozilla.jss.pkcs7.EncryptedContentInfo;
+import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier;
+import org.mozilla.jss.pkix.primitive.PBEParameter;
+import org.mozilla.jss.util.Password;
+
+import com.netscape.certsrv.kra.EKRAException;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.request.IService;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.certsrv.security.ITransportKeyUnit;
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.dbs.keydb.IKeyRecord;
+import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.cms.servlet.request.KeyRequestResource;
+import com.netscape.cmscore.dbs.KeyRecord;
+import com.netscape.cmsutil.util.Utils;
+
+/**
+ * This implementation services SecurityData Recovery requests.
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+@SuppressWarnings("deprecation")
+public class SecurityDataRecoveryService implements IService {
+
+ private IKeyRecoveryAuthority mKRA = null;
+
+ private IKeyRepository mStorage = null;
+ private IStorageKeyUnit mStorageUnit = null;
+ private ITransportKeyUnit mTransportUnit = null;
+
+ public static final String ATTR_SERIALNO = "serialNumber";
+ public static final String ATTR_KEY_RECORD = "keyRecord";
+
+ public SecurityDataRecoveryService(IKeyRecoveryAuthority kra) {
+ mKRA = kra;
+ mStorage = mKRA.getKeyRepository();
+ mStorageUnit = mKRA.getStorageKeyUnit();
+ mTransportUnit = mKRA.getTransportKeyUnit();
+
+ }
+
+ /**
+ * Performs the service (such as certificate generation)
+ * represented by this request.
+ * <p>
+ *
+ * @param request
+ * The SecurityData recovery request that needs service. The service may use
+ * attributes stored in the request, and may update the
+ * values, or store new ones.
+ * @return
+ * an indication of whether this request is still pending.
+ * 'false' means the request will wait for further notification.
+ * @exception EBaseException indicates major processing failure.
+ */
+ public boolean serviceRequest(IRequest request)
+ throws EBaseException {
+
+ //Pave the way for allowing generated IV vector
+ byte iv[]= null;
+ byte iv_default[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
+ byte iv_in[] = null;
+
+ Hashtable<String, Object> params = mKRA.getVolatileRequest(
+ request.getRequestId());
+
+ if (params == null) {
+ CMS.debug("Can't get volatile params.");
+ throw new EBaseException("Can't obtain volatile params!");
+ }
+
+ BigInteger serialno = request.getExtDataInBigInteger(ATTR_SERIALNO);
+
+ request.setExtData(ATTR_KEY_RECORD, serialno);
+
+ byte[] wrappedPassPhrase = null;
+ byte[] wrappedSessKey = null;
+
+ String transWrappedSessKeyStr = (String) params.get(IRequest.SECURITY_DATA_TRANS_SESS_KEY);
+ if (transWrappedSessKeyStr != null) {
+ wrappedSessKey = Utils.base64decode(transWrappedSessKeyStr);
+ }
+
+ String sessWrappedPassPhraseStr = (String) params.get(IRequest.SECURITY_DATA_SESS_PASS_PHRASE);
+ if (sessWrappedPassPhraseStr != null) {
+ wrappedPassPhrase = Utils.base64decode(sessWrappedPassPhraseStr);
+ }
+
+ String ivInStr = (String) params.get(IRequest.SECURITY_DATA_IV_STRING_IN);
+ if (ivInStr != null) {
+ iv_in = Utils.base64decode(ivInStr);
+ }
+
+ if (transWrappedSessKeyStr == null && sessWrappedPassPhraseStr == null) {
+ //We may be in recovery case where no params were initially submitted.
+ return false;
+ }
+
+ //Create the return IV if needed.
+ iv = new byte[8];
+
+ try {
+ Random rnd = new Random();
+ rnd.nextBytes(iv);
+ } catch (Exception e) {
+ iv = iv_default;
+ }
+
+ String ivStr = Utils.base64encode(iv);
+
+ KeyRecord keyRecord = (KeyRecord) mStorage.readKeyRecord(serialno);
+
+ SymmetricKey unwrappedSess = null;
+
+ String dataType = (String) keyRecord.get(IKeyRecord.ATTR_DATA_TYPE);
+ SymmetricKey symKey = null;
+ byte[] unwrappedSecData = null;
+ if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
+ symKey = recoverSymKey(keyRecord);
+ } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {
+ unwrappedSecData = recoverSecurityData(keyRecord);
+ }
+
+ CryptoToken ct = mTransportUnit.getToken();
+
+ byte[] key_data = null;
+ String pbeWrappedData = null;
+
+ if (sessWrappedPassPhraseStr != null) { //We have a trans wrapped pass phrase, we will be doing PBE packaging
+ byte[] unwrappedPass = null;
+ Password pass = null;
+
+ try {
+ unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.DECRYPT);
+ Cipher decryptor = ct.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD);
+ decryptor.initDecrypt(unwrappedSess, new IVParameterSpec(iv_in));
+ unwrappedPass = decryptor.doFinal(wrappedPassPhrase);
+ String passStr = new String(unwrappedPass, "UTF-8");
+
+ pass = new Password(passStr.toCharArray());
+ passStr = null;
+
+ if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
+ pbeWrappedData = createEncryptedContentInfo(ct, symKey, null,
+ pass);
+ } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {
+ pbeWrappedData = createEncryptedContentInfo(ct, null, unwrappedSecData,
+ pass);
+ }
+
+ params.put(IRequest.SECURITY_DATA_PASS_WRAPPED_DATA, pbeWrappedData);
+
+ } catch (Exception e) {
+ throw new EBaseException("Can't unwrap pass phase! " + e.toString());
+ } finally {
+ if ( pass != null) {
+ pass.clear();
+ }
+
+ if ( unwrappedPass != null) {
+ java.util.Arrays.fill(unwrappedPass, (byte) 0);
+ }
+ }
+
+ } else { // No trans wrapped pass phrase, return session wrapped data.
+ if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
+ //wrap the key with session key
+ try {
+ unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.WRAP);
+ KeyWrapper wrapper = ct.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD);
+ wrapper.initWrap(unwrappedSess, new IVParameterSpec(iv));
+ key_data = wrapper.wrap(symKey);
+ } catch (Exception e) {
+ throw new EBaseException("Can't wrap symmetric key! " + e.toString());
+ }
+
+ } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {
+ try {
+ unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.ENCRYPT);
+ Cipher encryptor = ct.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD);
+ if (encryptor != null) {
+ encryptor.initEncrypt(unwrappedSess, new IVParameterSpec(iv));
+ key_data = encryptor.doFinal(unwrappedSecData);
+ } else {
+ throw new IOException("Failed to create cipher");
+ }
+ } catch (Exception e) {
+ throw new EBaseException("Can't wrap pass phrase!");
+ }
+ }
+
+ String wrappedKeyData = Utils.base64encode(key_data);
+ params.put(IRequest.SECURITY_DATA_SESS_WRAPPED_DATA, wrappedKeyData);
+ params.put(IRequest.SECURITY_DATA_IV_STRING_OUT, ivStr);
+
+ }
+ return false;
+ }
+
+ public SymmetricKey recoverSymKey(KeyRecord keyRecord)
+ throws EBaseException {
+
+ try {
+ SymmetricKey symKey =
+ mStorageUnit.unwrap(
+ keyRecord.getPrivateKeyData());
+
+ if (symKey == null) {
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1",
+ "symmetric key unwrapping failure"));
+ }
+
+ return symKey;
+ } catch (Exception e) {
+
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1",
+ "recoverSymKey() " + e.toString()));
+ }
+ }
+
+ public byte[] recoverSecurityData(KeyRecord keyRecord)
+ throws EBaseException {
+
+ byte[] decodedData = null;
+
+ try {
+ decodedData = mStorageUnit.decryptInternalPrivate(
+ keyRecord.getPrivateKeyData());
+
+ if (decodedData == null) {
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1",
+ "security data unwrapping failure"));
+ }
+
+ return decodedData;
+ } catch (Exception e) {
+
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1",
+ "recoverSecurityData() " + e.toString()));
+ }
+ }
+
+ //ToDo: This might fit in JSS.
+ private static EncryptedContentInfo
+ createEncryptedContentInfoPBEOfSymmKey(PBEAlgorithm keyGenAlg, Password password, byte[] salt,
+ int iterationCount,
+ KeyGenerator.CharToByteConverter charToByteConverter,
+ SymmetricKey symKey, CryptoToken token)
+ throws CryptoManager.NotInitializedException, NoSuchAlgorithmException,
+ InvalidKeyException, InvalidAlgorithmParameterException, TokenException,
+ CharConversionException {
+
+ if (!(keyGenAlg instanceof PBEAlgorithm)) {
+ throw new NoSuchAlgorithmException("Key generation algorithm" +
+ " is not a PBE algorithm");
+ }
+ PBEAlgorithm pbeAlg = (PBEAlgorithm) keyGenAlg;
+
+ KeyGenerator kg = token.getKeyGenerator(keyGenAlg);
+ PBEKeyGenParams pbekgParams = new PBEKeyGenParams(
+ password, salt, iterationCount);
+ if (charToByteConverter != null) {
+ kg.setCharToByteConverter(charToByteConverter);
+ }
+ kg.initialize(pbekgParams);
+ SymmetricKey key = kg.generate();
+
+ EncryptionAlgorithm encAlg = pbeAlg.getEncryptionAlg();
+ AlgorithmParameterSpec params = null;
+ if (encAlg.getParameterClass().equals(IVParameterSpec.class)) {
+ params = new IVParameterSpec(kg.generatePBE_IV());
+ } else if (encAlg.getParameterClass().equals(
+ RC2ParameterSpec.class)) {
+ params = new RC2ParameterSpec(key.getStrength(),
+ kg.generatePBE_IV());
+ }
+
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD);
+ wrapper.initWrap(key, params);
+ byte encrypted[] = wrapper.wrap(symKey);
+
+ PBEParameter pbeParam = new PBEParameter(salt, iterationCount);
+ AlgorithmIdentifier encAlgID = new AlgorithmIdentifier(
+ keyGenAlg.toOID(), pbeParam);
+
+ EncryptedContentInfo encCI = new EncryptedContentInfo(
+ ContentInfo.DATA,
+ encAlgID,
+ new OCTET_STRING(encrypted));
+
+ return encCI;
+
+ }
+
+ private static String createEncryptedContentInfo(CryptoToken ct, SymmetricKey symKey, byte[] securityData,
+ Password password)
+ throws EBaseException {
+
+ EncryptedContentInfo cInfo = null;
+ String retData = null;
+ PBEAlgorithm keyGenAlg = PBEAlgorithm.PBE_SHA1_DES3_CBC;
+
+ byte[] encoded = null;
+ try {
+ PasswordConverter passConverter = new
+ PasswordConverter();
+ byte salt[] = { 0x01, 0x01, 0x01, 0x01 };
+ if (symKey != null) {
+
+ cInfo = createEncryptedContentInfoPBEOfSymmKey(keyGenAlg, password, salt,
+ 1,
+ passConverter,
+ symKey, ct);
+
+ } else if (securityData != null) {
+
+ cInfo = EncryptedContentInfo.createPBE(keyGenAlg, password, salt, 1, passConverter, securityData);
+ }
+
+ if(cInfo == null) {
+ throw new EBaseException("Can't create a PBE wrapped EncryptedContentInfo!");
+ }
+
+ ByteArrayOutputStream oStream = new ByteArrayOutputStream();
+ cInfo.encode(oStream);
+ encoded = oStream.toByteArray();
+ retData = Utils.base64encode(encoded);
+
+ } catch (Exception e) {
+ throw new EBaseException("Can't create a PBE wrapped EncryptedContentInfo! " + e.toString());
+ }
+
+ return retData;
+ }
+
+}
diff --git a/base/kra/src/com/netscape/kra/SecurityDataService.java b/base/kra/src/com/netscape/kra/SecurityDataService.java
new file mode 100644
index 000000000..fa009dac9
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/SecurityDataService.java
@@ -0,0 +1,171 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.math.BigInteger;
+import org.mozilla.jss.crypto.SymmetricKey;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.profile.IEnrollProfile;
+import com.netscape.certsrv.request.IService;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.certsrv.security.ITransportKeyUnit;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.dbs.keydb.IKeyRecord;
+import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.cms.servlet.request.KeyRequestResource;
+import com.netscape.cmscore.dbs.KeyRecord;
+import com.netscape.cmsutil.util.Utils;
+
+/**
+ * This implementation implements SecurityData archival operations.
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+public class SecurityDataService implements IService {
+
+ private final static String DEFAULT_OWNER = "IPA Agent";
+ public final static String ATTR_KEY_RECORD = "keyRecord";
+ private final static String STATUS_ACTIVE = "active";
+
+ private IKeyRecoveryAuthority mKRA = null;
+ private ITransportKeyUnit mTransportUnit = null;
+ private IStorageKeyUnit mStorageUnit = null;
+
+ public SecurityDataService(IKeyRecoveryAuthority kra) {
+ mKRA = kra;
+ mTransportUnit = kra.getTransportKeyUnit();
+ mStorageUnit = kra.getStorageKeyUnit();
+ }
+
+ /**
+ * Performs the service of archiving Security Data.
+ * represented by this request.
+ * <p>
+ *
+ * @param request
+ * The request that needs service. The service may use
+ * attributes stored in the request, and may update the
+ * values, or store new ones.
+ * @return
+ * an indication of whether this request is still pending.
+ * 'false' means the request will wait for further notification.
+ * @exception EBaseException indicates major processing failure.
+ */
+ public boolean serviceRequest(IRequest request)
+ throws EBaseException {
+ String id = request.getRequestId().toString();
+ String clientId = request.getExtDataInString(IRequest.SECURITY_DATA_CLIENT_ID);
+ String wrappedSecurityData = request.getExtDataInString(IEnrollProfile.REQUEST_ARCHIVE_OPTIONS);
+ String dataType = request.getExtDataInString(IRequest.SECURITY_DATA_TYPE);
+
+ CMS.debug("SecurityDataService.serviceRequest. Request id: " + id);
+ CMS.debug("SecurityDataService.serviceRequest wrappedSecurityData: " + wrappedSecurityData);
+
+ String owner = getOwnerName(request);
+
+ //Check here even though restful layer checks for this.
+ if(wrappedSecurityData == null || clientId == null || dataType == null) {
+ throw new EBaseException("Bad data in SecurityDataService.serviceRequest");
+ }
+ //We need some info from the PKIArchiveOptions wrapped security data
+
+ byte[] encoded = Utils.base64decode(wrappedSecurityData);
+
+ ArchiveOptions options = ArchiveOptions.toArchiveOptions(encoded);
+
+ //Check here just in case a null ArchiveOptions makes it this far
+ if(options == null) {
+ throw new EBaseException("Problem decofing PKIArchiveOptions.");
+ }
+
+ String algStr = options.getSymmAlgOID();
+
+ SymmetricKey securitySymKey = null;
+ byte[] securityData = null;
+
+ String keyType = null;
+ if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
+ // Symmetric Key
+ keyType = KeyRequestResource.SYMMETRIC_KEY_TYPE;
+ securitySymKey = mTransportUnit.unwrap_symmetric(options.getEncSymmKey(),
+ options.getSymmAlgOID(),
+ options.getSymmAlgParams(),
+ options.getEncValue());
+
+ } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {
+ keyType = KeyRequestResource.PASS_PHRASE_TYPE;
+ securityData = mTransportUnit.decryptExternalPrivate(options.getEncSymmKey(),
+ options.getSymmAlgOID(),
+ options.getSymmAlgParams(),
+ options.getEncValue());
+
+ }
+
+ byte[] publicKey = null;
+ byte privateSecurityData[] = null;
+
+ if (securitySymKey != null) {
+ privateSecurityData = mStorageUnit.wrap(securitySymKey);
+ } else if (securityData != null) {
+ privateSecurityData = mStorageUnit.encryptInternalPrivate(securityData);
+ } else { // We have no data.
+ throw new EBaseException("Failed to create security data to archive!");
+ }
+ // create key record
+ KeyRecord rec = new KeyRecord(null, publicKey,
+ privateSecurityData, owner,
+ algStr, owner);
+
+ rec.set(IKeyRecord.ATTR_CLIENT_ID, clientId);
+
+ //Now we need a serial number for our new key.
+
+ if (rec.getSerialNumber() != null) {
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE"));
+ }
+
+ IKeyRepository storage = mKRA.getKeyRepository();
+ BigInteger serialNo = storage.getNextSerialNumber();
+
+ if (serialNo == null) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_GET_NEXT_SERIAL"));
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE"));
+ }
+
+ rec.set(KeyRecord.ATTR_ID, serialNo);
+ rec.set(KeyRecord.ATTR_DATA_TYPE, keyType);
+ rec.set(KeyRecord.ATTR_STATUS, STATUS_ACTIVE);
+ request.setExtData(ATTR_KEY_RECORD, serialNo);
+
+ CMS.debug("KRA adding Security Data key record " + serialNo);
+
+ storage.addKeyRecord(rec);
+
+ return true;
+
+ }
+ //ToDo: return real owner with auth
+ private String getOwnerName(IRequest request) {
+ return DEFAULT_OWNER;
+ }
+} \ No newline at end of file
diff --git a/base/kra/src/com/netscape/kra/StorageKeyUnit.java b/base/kra/src/com/netscape/kra/StorageKeyUnit.java
new file mode 100644
index 000000000..c956bf8d8
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/StorageKeyUnit.java
@@ -0,0 +1,978 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.CharConversionException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.crypto.BadPaddingException;
+import org.mozilla.jss.crypto.Cipher;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.EncryptionAlgorithm;
+import org.mozilla.jss.crypto.IllegalBlockSizeException;
+import org.mozilla.jss.crypto.KeyGenerator;
+import org.mozilla.jss.crypto.KeyWrapAlgorithm;
+import org.mozilla.jss.crypto.KeyWrapper;
+import org.mozilla.jss.crypto.ObjectNotFoundException;
+import org.mozilla.jss.crypto.PBEAlgorithm;
+import org.mozilla.jss.crypto.PBEKeyGenParams;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.TokenCertificate;
+import org.mozilla.jss.crypto.TokenException;
+import org.mozilla.jss.crypto.X509Certificate;
+import org.mozilla.jss.util.Password;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.ISubsystem;
+import com.netscape.certsrv.kra.EKRAException;
+import com.netscape.certsrv.kra.IJoinShares;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.kra.IShare;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.security.Credential;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.cmsutil.util.Utils;
+
+/**
+ * A class represents a storage key unit. Currently, this
+ * is implemented with cryptix, the final implementation
+ * should be built on JSS/HCL.
+ *
+ * @author thomask
+ * @version $Revision$, $Date$
+ */
+public class StorageKeyUnit extends EncryptionUnit implements
+ ISubsystem, IStorageKeyUnit {
+
+ private IConfigStore mConfig = null;
+
+ // private RSAPublicKey mPublicKey = null;
+ // private RSAPrivateKey mPrivateKey = null;
+
+ private IConfigStore mStorageConfig = null;
+ private IKeyRecoveryAuthority mKRA = null;
+ private String mTokenFile = null;
+ private X509Certificate mCert = null;
+ private CryptoManager mManager = null;
+ private CryptoToken mToken = null;
+ private PrivateKey mPrivateKey = null;
+ private byte mPrivateKeyData[] = null;
+ private boolean mKeySplitting = false;
+
+ private static final String PROP_N = "n";
+ private static final String PROP_M = "m";
+ private static final String PROP_UID = "uid";
+ private static final String PROP_SHARE = "share";
+ private static final String PROP_HARDWARE = "hardware";
+ private static final String PROP_LOGOUT = "logout";
+ public static final String PROP_NICKNAME = "nickName";
+ public static final String PROP_KEYDB = "keydb";
+ public static final String PROP_CERTDB = "certdb";
+ public static final String PROP_MN = "mn";
+
+ /**
+ * Constructs this token.
+ */
+ public StorageKeyUnit() {
+ super();
+ }
+
+ /**
+ * Retrieves subsystem identifier.
+ */
+ public String getId() {
+ return "storageKeyUnit";
+ }
+
+ /**
+ * Sets subsystem identifier. Once the system is
+ * loaded, system identifier cannot be changed
+ * dynamically.
+ */
+ public void setId(String id) throws EBaseException {
+ throw new EBaseException(CMS.getUserMessage("CMS_INVALID_OPERATION"));
+ }
+
+ /**
+ * return true if byte arrays are equal, false otherwise
+ */
+ private boolean byteArraysMatch(byte a[], byte b[]) {
+ if (a == null || b == null) {
+ return false;
+ }
+ if (a.length != b.length) {
+ return false;
+ }
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Initializes this subsystem.
+ */
+ public void init(ISubsystem owner, IConfigStore config)
+ throws EBaseException {
+ mKRA = (IKeyRecoveryAuthority) owner;
+ mConfig = config;
+
+ mKeySplitting = owner.getConfigStore().getBoolean("keySplitting", false);
+
+ try {
+ mManager = CryptoManager.getInstance();
+ mToken = getToken();
+ } catch (org.mozilla.jss.CryptoManager.NotInitializedException e) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_INIT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ }
+
+ if (mConfig.getString(PROP_HARDWARE, null) != null) {
+ System.setProperty("cms.skip_token", mConfig.getString(PROP_HARDWARE));
+
+ // The strategy here is to read all the certs in the token
+ // and cycle through them until we find one that matches the
+ // kra-cert.db file
+
+ if (mKeySplitting) {
+
+ byte certFileData[] = null;
+ try {
+ File certFile = new File(
+ mConfig.getString(PROP_CERTDB));
+
+ certFileData = new byte[
+ (Long.valueOf(certFile.length())).intValue()];
+ FileInputStream fi = new FileInputStream(certFile);
+
+ fi.read(certFileData);
+ fi.close();
+
+ // pick up cert by nickName
+
+ } catch (IOException e) {
+ mKRA.log(ILogger.LL_INFO,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ }
+
+ try {
+ X509Certificate certs[] =
+ getToken().getCryptoStore().getCertificates();
+ for (int i = 0; i < certs.length; i++) {
+ if (byteArraysMatch(certs[i].getEncoded(), certFileData)) {
+ mCert = certs[i];
+ }
+ }
+ if (mCert == null) {
+ mKRA.log(ILogger.LL_FAILURE,
+ "Storage Cert could not be initialized. No cert in token matched kra-cert file");
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", "mCert == null"));
+ } else {
+ mKRA.log(ILogger.LL_INFO, "Using Storage Cert " + mCert.getSubjectDN());
+ }
+ } catch (CertificateEncodingException e) {
+ mKRA.log(ILogger.LL_FAILURE, "Error encoding cert ");
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ } catch (TokenException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ }
+ }
+
+ } else {
+
+ // read certificate from file
+ byte certData[] = null;
+
+ try {
+ if (mKeySplitting) {
+ File certFile = new File(
+ mConfig.getString(PROP_CERTDB));
+
+ certData = new byte[
+ (Long.valueOf(certFile.length())).intValue()];
+ FileInputStream fi = new FileInputStream(certFile);
+
+ fi.read(certData);
+ fi.close();
+
+ // pick up cert by nickName
+ mCert = mManager.findCertByNickname(
+ config.getString(PROP_NICKNAME));
+
+ } else {
+ mCert = mManager.findCertByNickname(
+ config.getString(PROP_NICKNAME));
+ }
+ } catch (IOException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ } catch (TokenException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ } catch (ObjectNotFoundException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString()));
+ // XXX - this import wont work
+ try {
+ mCert = mManager.importCertPackage(certData,
+ "kraStorageCert");
+ } catch (Exception ex) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_IMPORT_CERT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", ex.toString()));
+ }
+ }
+
+ if (mKeySplitting) {
+ // read private key from the file
+ try {
+ File priFile = new File(mConfig.getString(PROP_KEYDB));
+
+ mPrivateKeyData = new byte[
+ (Long.valueOf(priFile.length())).intValue()];
+ FileInputStream fi = new FileInputStream(priFile);
+
+ fi.read(mPrivateKeyData);
+ fi.close();
+ } catch (IOException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_PRIVATE", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", e.toString()));
+ }
+ }
+
+ }
+
+ if (mKeySplitting) {
+ // open internal data storage configuration
+ mTokenFile = mConfig.getString(PROP_MN);
+ try {
+ // read m, n and no of identifier
+ mStorageConfig = CMS.createFileConfigStore(mTokenFile);
+ } catch (EBaseException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_MN",
+ e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_OPERATION"));
+
+ }
+ }
+
+ try {
+ if (mCert == null) {
+ CMS.debug("mCert is null...retrieving " + config.getString(PROP_NICKNAME));
+ mCert = mManager.findCertByNickname(
+ config.getString(PROP_NICKNAME));
+ CMS.debug("mCert = " + mCert);
+ }
+ } catch (Exception e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_READ_CERT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ }
+
+ }
+
+ /**
+ * Starts up this subsystem.
+ */
+ public void startup() throws EBaseException {
+ }
+
+ /**
+ * Shutdowns this subsystem.
+ */
+ public void shutdown() {
+ }
+
+ /**
+ * Returns the configuration store of this token.
+ */
+ public IConfigStore getConfigStore() {
+ return mConfig;
+ }
+
+ public static SymmetricKey buildSymmetricKeyWithInternalStorage(
+ String pin) throws EBaseException {
+ try {
+ return buildSymmetricKey(CryptoManager.getInstance().getInternalKeyStorageToken(), pin);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Builds symmetric key from the given password.
+ */
+ public static SymmetricKey buildSymmetricKey(CryptoToken token,
+ String pin) throws EBaseException {
+ try {
+
+ Password pass = new Password(pin.toCharArray());
+ KeyGenerator kg = null;
+
+ kg = token.getKeyGenerator(
+ PBEAlgorithm.PBE_SHA1_DES3_CBC);
+ byte salt[] = { 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01 };
+ PBEKeyGenParams kgp = new PBEKeyGenParams(pass,
+ salt, 5);
+
+ pass.clear();
+ kg.initialize(kgp);
+ return kg.generate();
+ } catch (TokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "buildSymmetricKey:" +
+ e.toString()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "buildSymmetricKey:" +
+ e.toString()));
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "buildSymmetricKey:" +
+ e.toString()));
+ } catch (CharConversionException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "buildSymmetricKey:" +
+ e.toString()));
+ }
+ }
+
+ /**
+ * Unwraps the storage key with the given symmetric key.
+ */
+ public PrivateKey unwrapStorageKey(CryptoToken token,
+ SymmetricKey sk, byte wrapped[],
+ PublicKey pubKey)
+ throws EBaseException {
+ try {
+
+ CMS.debug("StorageKeyUnit.unwrapStorageKey.");
+
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD);
+
+ wrapper.initUnwrap(sk, IV);
+
+ // XXX - it does not like the public key that is
+ // not a crypto X509Certificate
+ PrivateKey pk = wrapper.unwrapTemporaryPrivate(wrapped,
+ PrivateKey.RSA, pubKey);
+
+ return pk;
+ } catch (TokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "unwrapStorageKey:" +
+ e.toString()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "unwrapStorageKey:" +
+ e.toString()));
+ } catch (InvalidKeyException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "unwrapStorageKey:" +
+ e.toString()));
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "unwrapStorageKey:" +
+ e.toString()));
+ }
+ }
+
+ /**
+ * Used by config-cert.
+ */
+ public byte[] wrapStorageKey(CryptoToken token,
+ SymmetricKey sk, PrivateKey pri)
+ throws EBaseException {
+ CMS.debug("StorageKeyUnit.wrapStorageKey.");
+ try {
+ // move public & private to config/storage.dat
+ // delete private key
+ KeyWrapper wrapper = token.getKeyWrapper(
+ KeyWrapAlgorithm.DES3_CBC_PAD);
+
+ // next to randomly generate a symmetric
+ // password
+
+ wrapper.initWrap(sk, IV);
+ return wrapper.wrap(pri);
+ } catch (TokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "wrapStorageKey:" +
+ e.toString()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "wrapStorageKey:" +
+ e.toString()));
+ } catch (InvalidKeyException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "wrapStorageKey:" +
+ e.toString()));
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ "wrapStorageKey:" +
+ e.toString()));
+ }
+ }
+
+ /**
+ * Logins to this token.
+ */
+ public void login(String pin) throws EBaseException {
+ if (mConfig.getString(PROP_HARDWARE, null) != null) {
+ try {
+ getToken().login(new Password(pin.toCharArray()));
+ PrivateKey pk[] = getToken().getCryptoStore().getPrivateKeys();
+
+ for (int i = 0; i < pk.length; i++) {
+ if (arraysEqual(pk[i].getUniqueID(),
+ ((TokenCertificate) mCert).getUniqueID())) {
+ mPrivateKey = pk[i];
+ }
+ }
+ } catch (Exception e) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_LOGIN", e.toString()));
+ }
+
+ } else {
+ try {
+ SymmetricKey sk = buildSymmetricKey(mToken, pin);
+
+ mPrivateKey = unwrapStorageKey(mToken, sk,
+ mPrivateKeyData, getPublicKey());
+ } catch (Exception e) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_LOGIN", e.toString()));
+ }
+ if (mPrivateKey == null) {
+ mPrivateKey = getPrivateKey();
+ }
+ }
+ }
+
+ /**
+ * Logins to this token.
+ */
+ public void login(Credential creds[])
+ throws EBaseException {
+ String pwd = constructPassword(creds);
+
+ login(pwd);
+ }
+
+ /**
+ * Logout from this token.
+ */
+ public void logout() {
+ try {
+ if (mConfig.getString(PROP_HARDWARE, null) != null) {
+ if (mConfig.getBoolean(PROP_LOGOUT, false)) {
+ getToken().logout();
+ }
+ }
+ } catch (Exception e) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_STORAGE_LOGOUT", e.toString()));
+
+ }
+ mPrivateKey = null;
+ }
+
+ /**
+ * Returns a list of recovery agent identifiers.
+ */
+ public Enumeration<String> getAgentIdentifiers() {
+ Vector<String> v = new Vector<String>();
+
+ for (int i = 0;; i++) {
+ try {
+ String uid =
+ mStorageConfig.getString(PROP_UID + i);
+
+ if (uid == null)
+ break;
+ v.addElement(uid);
+ } catch (EBaseException e) {
+ break;
+ }
+ }
+ return v.elements();
+ }
+
+ /**
+ * Changes agent password.
+ */
+ public boolean changeAgentPassword(String id, String oldpwd,
+ String newpwd) throws EBaseException {
+ // locate the id(s)
+ for (int i = 0;; i++) {
+ try {
+ String uid =
+ mStorageConfig.getString(PROP_UID + i);
+
+ if (uid == null)
+ break;
+ if (id.equals(uid)) {
+ byte share[] = decryptShareWithInternalStorage(mStorageConfig.getString(PROP_SHARE + i), oldpwd);
+
+ mStorageConfig.putString(PROP_SHARE + i,
+ encryptShareWithInternalStorage(
+ share, newpwd));
+ mStorageConfig.commit(false);
+ return true;
+ }
+ } catch (Exception e) {
+ break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Changes the m out of n recovery schema.
+ */
+ public boolean changeAgentMN(int new_n, int new_m,
+ Credential oldcreds[],
+ Credential newcreds[])
+ throws EBaseException {
+
+ if (new_n != newcreds.length) {
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_N"));
+ }
+
+ // XXX - verify and construct original password
+ String secret = constructPassword(oldcreds);
+
+ // XXX - remove extra configuration
+ for (int j = new_n; j < getNoOfAgents(); j++) {
+ mStorageConfig.remove(PROP_UID + j);
+ mStorageConfig.remove(PROP_SHARE + j);
+ }
+
+ // XXX - split pwd into n pieces
+ byte shares[][] = new byte[newcreds.length][];
+
+ IShare s = null;
+ try {
+ String className = mConfig.getString("share_class",
+ "com.netscape.cms.shares.OldShare");
+ s = (IShare) Class.forName(className).newInstance();
+ } catch (Exception e) {
+ CMS.debug("Loading Shares error " + e);
+ }
+ if (s == null) {
+ CMS.debug("Share plugin is not found");
+ return false;
+ }
+
+ try {
+ s.initialize(secret.getBytes(), new_m);
+ } catch (Exception e) {
+ CMS.debug("Failed to initialize Share plugin");
+ return false;
+ }
+
+ for (int i = 0; i < newcreds.length; i++) {
+ byte share[] = s.createShare(i + 1);
+
+ shares[i] = share;
+ }
+
+ // store the new shares into configuration
+ mStorageConfig.putInteger(PROP_N, new_n);
+ mStorageConfig.putInteger(PROP_M, new_m);
+ for (int i = 0; i < newcreds.length; i++) {
+ mStorageConfig.putString(PROP_UID + i,
+ newcreds[i].getIdentifier());
+ // use password to encrypt shares...
+ mStorageConfig.putString(PROP_SHARE + i,
+ encryptShareWithInternalStorage(shares[i],
+ newcreds[i].getPassword()));
+ }
+
+ try {
+ mStorageConfig.commit(false);
+ return true;
+ } catch (EBaseException e) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_CHANGE_MN", e.toString()));
+ }
+ return false;
+ }
+
+ /**
+ * Returns number of recovery agents.
+ */
+ public int getNoOfAgents() throws EBaseException {
+ return mStorageConfig.getInteger(PROP_N);
+ }
+
+ /**
+ * Returns number of recovery agents required for
+ * recovery operation.
+ */
+ public int getNoOfRequiredAgents() throws EBaseException {
+ return mStorageConfig.getInteger(PROP_M);
+ }
+
+ public void setNoOfRequiredAgents(int number) {
+ mStorageConfig.putInteger(PROP_M, number);
+ }
+
+ public CryptoToken getInternalToken() {
+ try {
+ return CryptoManager.getInstance().getInternalKeyStorageToken();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public CryptoToken getToken() {
+ try {
+ if (mConfig.getString(PROP_HARDWARE, null) != null) {
+ return mManager.getTokenByName(mConfig.getString(PROP_HARDWARE));
+ } else {
+ return CryptoManager.getInstance().getInternalKeyStorageToken();
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the certificate blob.
+ */
+ public PublicKey getPublicKey() {
+ // NEED to move this key into internal storage token.
+ return mCert.getPublicKey();
+ }
+
+ public PrivateKey getPrivateKey() {
+
+ if (!mKeySplitting) {
+ try {
+ PrivateKey pk[] = getToken().getCryptoStore().getPrivateKeys();
+ for (int i = 0; i < pk.length; i++) {
+ if (arraysEqual(pk[i].getUniqueID(),
+ ((TokenCertificate) mCert).getUniqueID())) {
+ return pk[i];
+ }
+ }
+ } catch (TokenException e) {
+ }
+ return null;
+ } else {
+ return mPrivateKey;
+ }
+ }
+
+ /**
+ * Verifies the integrity of the given key pairs.
+ */
+ public void verify(byte publicKey[], PrivateKey privateKey)
+ throws EBaseException {
+ // XXX
+ }
+
+ public String encryptShareWithInternalStorage(
+ byte share[], String pwd)
+ throws EBaseException {
+ try {
+ return encryptShare(CryptoManager.getInstance().getInternalKeyStorageToken(), share, pwd);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Protectes the share with the given password.
+ */
+ public String encryptShare(CryptoToken token,
+ byte share[], String pwd)
+ throws EBaseException {
+ try {
+ CMS.debug("StorageKeyUnit.encryptShare");
+ Cipher cipher = token.getCipherContext(
+ EncryptionAlgorithm.DES3_CBC_PAD);
+ SymmetricKey sk = StorageKeyUnit.buildSymmetricKey(token, pwd);
+
+ cipher.initEncrypt(sk, IV);
+ byte prev[] = preVerify(share);
+ byte enc[] = cipher.doFinal(prev);
+
+ return Utils.base64encode(enc).trim();
+ } catch (NoSuchAlgorithmException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ e.toString()));
+ } catch (TokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ e.toString()));
+ } catch (InvalidKeyException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ e.toString()));
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ e.toString()));
+ } catch (BadPaddingException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ e.toString()));
+ } catch (IllegalBlockSizeException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1",
+ e.toString()));
+ }
+ }
+
+ public static byte[] preVerify(byte share[]) {
+ byte data[] = new byte[share.length + 2];
+
+ data[0] = 0;
+ data[1] = 0;
+ for (int i = 0; i < share.length; i++) {
+ data[2 + i] = share[i];
+ }
+ return data;
+ }
+
+ public static boolean verifyShare(byte share[]) {
+ if (share[0] == 0 && share[1] == 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static byte[] postVerify(byte share[]) {
+ byte data[] = new byte[share.length - 2];
+
+ for (int i = 2; i < share.length; i++) {
+ data[i - 2] = share[i];
+ }
+ return data;
+ }
+
+ public void checkPassword(String userid, String pwd) throws EBaseException {
+ for (int i = 0;; i++) {
+ String uid = null;
+
+ try {
+ uid = mStorageConfig.getString(PROP_UID + i);
+ if (uid == null)
+ break;
+ } catch (Exception e) {
+ break;
+ }
+ if (uid.equals(userid)) {
+ byte data[] = decryptShareWithInternalStorage(
+ mStorageConfig.getString(PROP_SHARE + i),
+ pwd);
+ if (data == null) {
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+ }
+ return;
+ }
+ }
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+
+ }
+
+ public byte[] decryptShareWithInternalStorage(
+ String encoding, String pwd)
+ throws EBaseException {
+ try {
+ return decryptShare(CryptoManager.getInstance().getInternalKeyStorageToken(), encoding, pwd);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Decrypts shares with the given password.
+ */
+ public byte[] decryptShare(CryptoToken token,
+ String encoding, String pwd)
+ throws EBaseException {
+ try {
+ CMS.debug("StorageKeyUnit.decryptShare");
+ byte share[] = CMS.AtoB(encoding);
+ Cipher cipher = token.getCipherContext(
+ EncryptionAlgorithm.DES3_CBC_PAD);
+ SymmetricKey sk = StorageKeyUnit.buildSymmetricKey(
+ token, pwd);
+
+ cipher.initDecrypt(sk, IV);
+ byte dec[] = cipher.doFinal(share);
+
+ if (dec == null || !verifyShare(dec)) {
+ // invalid passwod
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+ }
+ return postVerify(dec);
+ } catch (OutOfMemoryError e) {
+ // XXX - this happens in cipher.doFinal when
+ // the given share is not valid (the password
+ // given from the agent is not correct).
+ // Actulla, cipher.doFinal should return
+ // something better than this!
+ //
+ // e.printStackTrace();
+ //
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ e.toString()));
+ } catch (TokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ e.toString()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ e.toString()));
+ } catch (InvalidKeyException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ e.toString()));
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ e.toString()));
+ } catch (IllegalBlockSizeException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ e.toString()));
+ } catch (BadPaddingException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ e.toString()));
+ }
+ }
+
+ /**
+ * Reconstructs password from recovery agents.
+ */
+ private String constructPassword(Credential creds[])
+ throws EBaseException {
+ // sort the credential according to the order in
+ // configuration file
+ Hashtable<String, byte[]> v = new Hashtable<String, byte[]>();
+
+ for (int i = 0;; i++) {
+ String uid = null;
+
+ try {
+ uid = mStorageConfig.getString(PROP_UID + i);
+ if (uid == null)
+ break;
+ } catch (Exception e) {
+ break;
+ }
+ for (int j = 0; j < creds.length; j++) {
+ if (uid.equals(creds[j].getIdentifier())) {
+ byte pwd[] = decryptShareWithInternalStorage(
+ mStorageConfig.getString(
+ PROP_SHARE + i),
+ creds[j].getPassword());
+ if (pwd == null) {
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+ }
+ v.put(Integer.toString(i), pwd);
+ break;
+ }
+ }
+ }
+
+ if (v.size() < 0) {
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+ }
+
+ if (v.size() != creds.length) {
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+ }
+
+ IJoinShares j = null;
+ try {
+ String className = mConfig.getString("joinshares_class",
+ "com.netscape.cms.shares.OldJoinShares");
+ j = (IJoinShares) Class.forName(className).newInstance();
+ } catch (Exception e) {
+ CMS.debug("JoinShares error " + e);
+ }
+ if (j == null) {
+ CMS.debug("JoinShares plugin is not found");
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+ }
+
+ try {
+ j.initialize(v.size());
+ } catch (Exception e) {
+ CMS.debug("Failed to initialize JoinShares");
+ throw new EBaseException(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+ }
+ Enumeration<String> e = v.keys();
+
+ while (e.hasMoreElements()) {
+ String next = e.nextElement();
+
+ j.addShare(Integer.parseInt(next) + 1,
+ (byte[]) v.get(next));
+ }
+ try {
+ byte secret[] = j.recoverSecret();
+ String pwd = new String(secret);
+
+ return pwd;
+ } catch (Exception ee) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_STORAGE_RECONSTRUCT", e.toString()));
+ throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_PASSWORD",
+ ee.toString()));
+ }
+ }
+
+ public static boolean arraysEqual(byte[] bytes, byte[] ints) {
+ if (bytes == null || ints == null) {
+ return false;
+ }
+
+ if (bytes.length != ints.length) {
+ return false;
+ }
+
+ for (int i = 0; i < bytes.length; i++) {
+ if (bytes[i] != ints[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java b/base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java
new file mode 100644
index 000000000..7575ea9f4
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/TokenKeyRecoveryService.java
@@ -0,0 +1,627 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.SecureRandom;
+import java.util.Hashtable;
+
+import netscape.security.util.BigInt;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerValue;
+
+import org.mozilla.jss.crypto.Cipher;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.EncryptionAlgorithm;
+import org.mozilla.jss.crypto.IVParameterSpec;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.pkcs11.PK11SymKey;
+import org.mozilla.jss.util.Base64OutputStream;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.certsrv.kra.EKRAException;
+import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.IService;
+import com.netscape.certsrv.security.IStorageKeyUnit;
+import com.netscape.certsrv.security.ITransportKeyUnit;
+import com.netscape.cmscore.dbs.KeyRecord;
+import com.netscape.cmsutil.util.Cert;
+
+/**
+ * A class represents recovery request processor.
+ *
+ * @author Christina Fu (cfu)
+ * @version $Revision$, $Date$
+ */
+public class TokenKeyRecoveryService implements IService {
+
+ public static final String ATTR_NICKNAME = "nickname";
+ public static final String ATTR_OWNER_NAME = "ownerName";
+ public static final String ATTR_PUBLIC_KEY_DATA = "publicKeyData";
+ public static final String ATTR_PRIVATE_KEY_DATA = "privateKeyData";
+ public static final String ATTR_TRANSPORT_CERT = "transportCert";
+ public static final String ATTR_TRANSPORT_PWD = "transportPwd";
+ public static final String ATTR_SIGNING_CERT = "signingCert";
+ public static final String ATTR_PKCS12 = "pkcs12";
+ public static final String ATTR_ENCRYPTION_CERTS =
+ "encryptionCerts";
+ public static final String ATTR_AGENT_CREDENTIALS =
+ "agentCredentials";
+ // same as encryption certs
+ public static final String ATTR_USER_CERT = "cert";
+ public static final String ATTR_DELIVERY = "delivery";
+
+ private IKeyRecoveryAuthority mKRA = null;
+ private IKeyRepository mStorage = null;
+ private IStorageKeyUnit mStorageUnit = null;
+ private ITransportKeyUnit mTransportUnit = null;
+
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_4";
+
+ private final static String LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED =
+ "LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED_4";
+ private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger();
+
+ /**
+ * Constructs request processor.
+ */
+ public TokenKeyRecoveryService(IKeyRecoveryAuthority kra) {
+ mKRA = kra;
+ mStorage = mKRA.getKeyRepository();
+ mStorageUnit = mKRA.getStorageKeyUnit();
+ mTransportUnit = kra.getTransportKeyUnit();
+ }
+
+ /**
+ * Process the HTTP request.
+ *
+ * @param s The URL to decode
+ */
+ protected String URLdecode(String s) {
+ if (s == null)
+ return null;
+ ByteArrayOutputStream out = new ByteArrayOutputStream(s.length());
+
+ for (int i = 0; i < s.length(); i++) {
+ int c = (int) s.charAt(i);
+
+ if (c == '+') {
+ out.write(' ');
+ } else if (c == '%') {
+ int c1 = Character.digit(s.charAt(++i), 16);
+ int c2 = Character.digit(s.charAt(++i), 16);
+
+ out.write((char) (c1 * 16 + c2));
+ } else {
+ out.write(c);
+ }
+ } // end for
+ return out.toString();
+ }
+
+ public static String normalizeCertStr(String s) {
+ String val = "";
+
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == '\\') {
+ i++;
+ continue;
+ } else if (s.charAt(i) == '\\') {
+ i++;
+ continue;
+ } else if (s.charAt(i) == '"') {
+ continue;
+ } else if (s.charAt(i) == ' ') {
+ continue;
+ }
+ val += s.charAt(i);
+ }
+ return val;
+ }
+
+ private static String base64Encode(byte[] bytes) throws IOException {
+ // All this streaming is lame, but Base64OutputStream needs a
+ // PrintStream
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ Base64OutputStream b64 = new Base64OutputStream(new
+ PrintStream(new
+ FilterOutputStream(output)
+ )
+ );
+
+ b64.write(bytes);
+ b64.flush();
+
+ // This is internationally safe because Base64 chars are
+ // contained within 8859_1
+ return output.toString("8859_1");
+ }
+
+ // this encrypts bytes with a symmetric key
+ public byte[] encryptIt(byte[] toBeEncrypted, SymmetricKey symKey, CryptoToken token,
+ IVParameterSpec IV) {
+ try {
+ Cipher cipher = token.getCipherContext(
+ EncryptionAlgorithm.DES3_CBC_PAD);
+
+ cipher.initEncrypt(symKey, IV);
+ byte pri[] = cipher.doFinal(toBeEncrypted);
+ return pri;
+ } catch (Exception e) {
+ CMS.debug("initEncrypt() threw exception: " + e.toString());
+ return null;
+ }
+
+ }
+
+ /**
+ * Processes a recovery request. The method reads
+ * the key record from the database, and tries to recover the
+ * key with the storage key unit. Once recovered, it wraps it
+ * with desKey
+ * In the params
+ * - cert is used for recovery record search
+ * - cuid may be used for additional validation check
+ * - userid may be used for additional validation check
+ * - wrappedDesKey is used for wrapping recovered private key
+ *
+ * @param request recovery request
+ * @return operation success or not
+ * @exception EBaseException failed to serve
+ */
+ public boolean serviceRequest(IRequest request) throws EBaseException {
+ String auditMessage = null;
+ String auditSubjectID = null;
+ String auditRecoveryID = ILogger.UNIDENTIFIED;
+ String iv_s = "";
+
+ CMS.debug("KRA services token key recovery request");
+
+ byte[] wrapped_des_key;
+
+ byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
+ try {
+ SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+ random.nextBytes(iv);
+ } catch (Exception e) {
+ CMS.debug("TokenKeyRecoveryService.serviceRequest: " + e.toString());
+ }
+
+ String id = request.getRequestId().toString();
+ if (id != null) {
+ auditRecoveryID = id.trim();
+ }
+ SessionContext sContext = SessionContext.getContext();
+ String agentId = "";
+ if (sContext != null) {
+ agentId =
+ (String) sContext.get(SessionContext.USER_ID);
+ }
+
+ Hashtable<String, Object> params = mKRA.getVolatileRequest(
+ request.getRequestId());
+
+ if (params == null) {
+ // possibly we are in recovery mode
+ CMS.debug("getVolatileRequest params null");
+ // return true;
+ }
+
+ wrapped_des_key = null;
+
+ PK11SymKey sk = null;
+
+ String rCUID = request.getExtDataInString(IRequest.NETKEY_ATTR_CUID);
+ String rUserid = request.getExtDataInString(IRequest.NETKEY_ATTR_USERID);
+ String rWrappedDesKeyString = request.getExtDataInString(IRequest.NETKEY_ATTR_DRMTRANS_DES_KEY);
+ auditSubjectID = rCUID + ":" + rUserid;
+
+ CMS.debug("TokenKeyRecoveryService: received DRM-trans-wrapped des key =" + rWrappedDesKeyString);
+ wrapped_des_key = com.netscape.cmsutil.util.Utils.SpecialDecode(rWrappedDesKeyString);
+ CMS.debug("TokenKeyRecoveryService: wrapped_des_key specialDecoded");
+
+ if ((wrapped_des_key != null) &&
+ (wrapped_des_key.length > 0)) {
+
+ // unwrap the des key
+ sk = (PK11SymKey) mTransportUnit.unwrap_encrypt_sym(wrapped_des_key);
+
+ if (sk == null) {
+ CMS.debug("TokenKeyRecoveryService: no des key");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ } else {
+ CMS.debug("TokenKeyRecoveryService: received des key");
+ }
+ } else {
+ CMS.debug("TokenKeyRecoveryService: not receive des key");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ }
+
+ // retrieve based on Certificate
+ String cert_s = request.getExtDataInString(ATTR_USER_CERT);
+ if (cert_s == null) {
+ CMS.debug("TokenKeyRecoveryService: not receive cert");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(3));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ }
+
+ String cert = normalizeCertStr(cert_s);
+ java.security.cert.X509Certificate x509cert = null;
+ try {
+ x509cert = (java.security.cert.X509Certificate) Cert.mapCert(cert);
+ if (x509cert == null) {
+ CMS.debug("cert mapping failed");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(5));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ }
+ } catch (IOException e) {
+ CMS.debug("TokenKeyRecoveryService: mapCert failed");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(6));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ }
+
+ try {
+ /*
+ CryptoToken internalToken =
+ CryptoManager.getInstance().getInternalKeyStorageToken();
+ */
+ CryptoToken token = mStorageUnit.getToken();
+ CMS.debug("TokenKeyRecoveryService: got token slot:" + token.getName());
+ IVParameterSpec algParam = new IVParameterSpec(iv);
+
+ Cipher cipher = token.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD);
+
+ KeyRecord keyRecord = null;
+ CMS.debug("KRA reading key record");
+ try {
+ keyRecord = (KeyRecord) mStorage.readKeyRecord(cert);
+ if (keyRecord != null)
+ CMS.debug("read key record");
+ else {
+ CMS.debug("key record not found");
+ request.setExtData(IRequest.RESULT, Integer.valueOf(8));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ }
+ } catch (Exception e) {
+ com.netscape.cmscore.util.Debug.printStackTrace(e);
+ request.setExtData(IRequest.RESULT, Integer.valueOf(9));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ }
+
+ // see if the owner name matches (cuid:userid) -XXX need make this optional
+ String owner = keyRecord.getOwnerName();
+ CMS.debug("TokenKeyRecoveryService: owner name on record =" + owner);
+ CMS.debug("TokenKeyRecoveryService: owner name from TPS =" + rCUID + ":" + rUserid);
+ if (owner != null) {
+ if (owner.equals(rCUID + ":" + rUserid)) {
+ CMS.debug("TokenKeyRecoveryService: owner name matches");
+ } else {
+ CMS.debug("TokenKeyRecoveryService: owner name mismatches");
+ }
+ }
+
+ // see if the certificate matches the key
+ byte pubData[] = keyRecord.getPublicKeyData();
+ byte inputPubData[] = x509cert.getPublicKey().getEncoded();
+
+ if (inputPubData.length != pubData.length) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN"));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED"));
+ }
+
+ for (int i = 0; i < pubData.length; i++) {
+ if (pubData[i] != inputPubData[i]) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PUBLIC_KEY_LEN"));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_PUBLIC_KEY_NOT_MATCHED"));
+ }
+ }
+
+ // Unwrap the archived private key
+ byte privateKeyData[] = null;
+ privateKeyData = recoverKey(params, keyRecord);
+ if (privateKeyData == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ CMS.debug("TokenKeyRecoveryService: failed getting private key");
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ }
+ CMS.debug("TokenKeyRecoveryService: got private key...about to verify");
+
+ iv_s = /*base64Encode(iv);*/com.netscape.cmsutil.util.Utils.SpecialEncode(iv);
+ request.setExtData("iv_s", iv_s);
+
+ CMS.debug("request.setExtData: iv_s: " + iv_s);
+
+ /* LunaSA returns data with padding which we need to remove */
+ ByteArrayInputStream dis = new ByteArrayInputStream(privateKeyData);
+ DerValue dv = new DerValue(dis);
+ byte p[] = dv.toByteArray();
+ int l = p.length;
+ CMS.debug("length different data length=" + l +
+ " real length=" + privateKeyData.length);
+ if (l != privateKeyData.length) {
+ privateKeyData = p;
+ }
+
+ if (verifyKeyPair(pubData, privateKeyData) == false) {
+ mKRA.log(ILogger.LL_FAILURE,
+ CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND"));
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY"));
+ } else {
+ CMS.debug("TokenKeyRecoveryService: private key verified with public key");
+ }
+
+ //encrypt and put in private key
+ cipher.initEncrypt(sk, algParam);
+ byte wrapped[] = cipher.doFinal(privateKeyData);
+
+ String wrappedPrivKeyString =
+ com.netscape.cmsutil.util.Utils.SpecialEncode(wrapped);
+ if (wrappedPrivKeyString == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ CMS.debug("TokenKeyRecoveryService: failed generating wrapped private key");
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ } else {
+ CMS.debug("TokenKeyRecoveryService: got private key data wrapped");
+ request.setExtData("wrappedUserPrivate",
+ wrappedPrivKeyString);
+ request.setExtData(IRequest.RESULT, Integer.valueOf(1));
+ CMS.debug("TokenKeyRecoveryService: key for " + rCUID + ":" + rUserid + " recovered");
+ }
+
+ //convert and put in the public key
+ String b64PKey = base64Encode(pubData);
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRecoveryID,
+ b64PKey);
+
+ audit(auditMessage);
+
+ if (b64PKey == null) {
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ CMS.debug("TokenKeyRecoveryService: failed getting publickey encoded");
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+ return false;
+ } else {
+ CMS.debug("TokenKeyRecoveryService: got publicKeyData b64 = " +
+ b64PKey);
+ }
+ request.setExtData("public_key", b64PKey);
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_KEY_RECOVERY_REQUEST_PROCESSED,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ auditRecoveryID,
+ agentId);
+
+ audit(auditMessage);
+
+ return true;
+
+ } catch (Exception e) {
+ CMS.debug("TokenKeyRecoveryService: " + e.toString());
+ request.setExtData(IRequest.RESULT, Integer.valueOf(4));
+ }
+
+ return true;
+ }
+
+ public boolean verifyKeyPair(byte publicKeyData[], byte privateKeyData[]) {
+ try {
+ DerValue publicKeyVal = new DerValue(publicKeyData);
+ DerInputStream publicKeyIn = publicKeyVal.data;
+ publicKeyIn.getSequence(0);
+ DerValue publicKeyDer = new DerValue(publicKeyIn.getBitString());
+ DerInputStream publicKeyDerIn = publicKeyDer.data;
+ BigInt publicKeyModulus = publicKeyDerIn.getInteger();
+ BigInt publicKeyExponent = publicKeyDerIn.getInteger();
+
+ DerValue privateKeyVal = new DerValue(privateKeyData);
+ if (privateKeyVal.tag != DerValue.tag_Sequence)
+ return false;
+ DerInputStream privateKeyIn = privateKeyVal.data;
+ privateKeyIn.getInteger();
+ privateKeyIn.getSequence(0);
+ DerValue privateKeyDer = new DerValue(privateKeyIn.getOctetString());
+ DerInputStream privateKeyDerIn = privateKeyDer.data;
+
+ @SuppressWarnings("unused")
+ BigInt privateKeyVersion = privateKeyDerIn.getInteger(); // consume stream
+ BigInt privateKeyModulus = privateKeyDerIn.getInteger();
+ BigInt privateKeyExponent = privateKeyDerIn.getInteger();
+
+ if (!publicKeyModulus.equals(privateKeyModulus)) {
+ CMS.debug("verifyKeyPair modulus mismatch publicKeyModulus="
+ + publicKeyModulus + " privateKeyModulus=" + privateKeyModulus);
+ return false;
+ }
+
+ if (!publicKeyExponent.equals(privateKeyExponent)) {
+ CMS.debug("verifyKeyPair exponent mismatch publicKeyExponent="
+ + publicKeyExponent + " privateKeyExponent=" + privateKeyExponent);
+ return false;
+ }
+
+ return true;
+ } catch (Exception e) {
+ CMS.debug("verifyKeyPair error " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Recovers key.
+ */
+ public synchronized byte[] recoverKey(Hashtable<String, Object> request, KeyRecord keyRecord)
+ throws EBaseException {
+ /*
+ Credential creds[] = (Credential[])
+ request.get(ATTR_AGENT_CREDENTIALS);
+
+ mStorageUnit.login(creds);
+ */
+ CMS.debug("KRA decrypts internal private");
+ byte privateKeyData[] =
+ mStorageUnit.decryptInternalPrivate(
+ keyRecord.getPrivateKeyData());
+ /*
+ mStorageUnit.logout();
+ */
+ if (privateKeyData == null) {
+ mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_PRIVATE_KEY_NOT_FOUND"));
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "no private key"));
+ }
+ return privateKeyData;
+ }
+
+ /**
+ * Signed Audit Log
+ * y
+ * This method is called to store messages to the signed audit log.
+ * <P>
+ *
+ * @param msg signed audit log message
+ */
+ private void audit(String msg) {
+ // in this case, do NOT strip preceding/trailing whitespace
+ // from passed-in String parameters
+
+ if (mSignedAuditLogger == null) {
+ return;
+ }
+
+ mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT,
+ null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ msg);
+ }
+
+}
diff --git a/base/kra/src/com/netscape/kra/TransportKeyUnit.java b/base/kra/src/com/netscape/kra/TransportKeyUnit.java
new file mode 100644
index 000000000..90ac2120f
--- /dev/null
+++ b/base/kra/src/com/netscape/kra/TransportKeyUnit.java
@@ -0,0 +1,195 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.kra;
+
+import java.security.PublicKey;
+
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.ObjectNotFoundException;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.crypto.Signature;
+import org.mozilla.jss.crypto.SignatureAlgorithm;
+import org.mozilla.jss.crypto.TokenException;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.ISubsystem;
+import com.netscape.certsrv.security.ITransportKeyUnit;
+import com.netscape.cmsutil.util.Cert;
+
+/**
+ * A class represents the transport key pair. This key pair
+ * is used to protected EE's private key in transit.
+ *
+ * @author thomask
+ * @version $Revision$, $Date$
+ */
+public class TransportKeyUnit extends EncryptionUnit implements
+ ISubsystem, ITransportKeyUnit {
+
+ public static final String PROP_NICKNAME = "nickName";
+ public static final String PROP_SIGNING_ALGORITHM = "signingAlgorithm";
+
+ // private RSAPublicKey mPublicKey = null;
+ // private RSAPrivateKey mPrivateKey = null;
+ private IConfigStore mConfig = null;
+ private org.mozilla.jss.crypto.X509Certificate mCert = null;
+ private CryptoManager mManager = null;
+
+ /**
+ * Constructs this token.
+ */
+ public TransportKeyUnit() {
+ super();
+ }
+
+ /**
+ * Retrieves subsystem identifier.
+ */
+ public String getId() {
+ return "transportKeyUnit";
+ }
+
+ /**
+ * Sets subsystem identifier.
+ */
+ public void setId(String id) throws EBaseException {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_OPERATION"));
+ }
+
+ /**
+ * Initializes this subsystem.
+ */
+ public void init(ISubsystem owner, IConfigStore config)
+ throws EBaseException {
+ mConfig = config;
+ try {
+ mManager = CryptoManager.getInstance();
+ mCert = mManager.findCertByNickname(getNickName());
+ String algo = config.getString("signingAlgorithm", "SHA256withRSA");
+
+ // #613795 - initialize this; otherwise JSS is not happy
+ CryptoToken token = getToken();
+ SignatureAlgorithm sigalg = Cert.mapAlgorithmToJss(algo);
+ Signature signer = token.getSignatureContext(sigalg);
+ signer.initSign(getPrivateKey());
+
+ } catch (org.mozilla.jss.CryptoManager.NotInitializedException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
+
+ } catch (TokenException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
+ } catch (ObjectNotFoundException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
+ } catch (Exception e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
+ }
+ }
+
+ public CryptoToken getInternalToken() {
+ try {
+ return CryptoManager.getInstance().getInternalKeyStorageToken();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public CryptoToken getToken() {
+ // 390148: returning the token that owns the private
+ // key.
+ return getPrivateKey().getOwningToken();
+ }
+
+ /**
+ * Starts up this subsystem.
+ */
+ public void startup() throws EBaseException {
+ }
+
+ /**
+ * Shutdowns this subsystem.
+ */
+ public void shutdown() {
+ }
+
+ /**
+ * Returns the configuration store of this token.
+ */
+ public IConfigStore getConfigStore() {
+ return mConfig;
+ }
+
+ public String getNickName() throws EBaseException {
+ return mConfig.getString(PROP_NICKNAME);
+ }
+
+ public void setNickName(String str) throws EBaseException {
+ mConfig.putString(PROP_NICKNAME, str);
+ }
+
+ public String getSigningAlgorithm() throws EBaseException {
+ return mConfig.getString(PROP_SIGNING_ALGORITHM);
+ }
+
+ public void setSigningAlgorithm(String str) throws EBaseException {
+ mConfig.putString(PROP_SIGNING_ALGORITHM, str);
+ }
+
+ /**
+ * Logins to this token.
+ */
+ public void login(String pin) throws EBaseException {
+ }
+
+ /**
+ * Logout from this token.
+ */
+ public void logout() {
+ }
+
+ /**
+ * Retrieves public key.
+ */
+ public org.mozilla.jss.crypto.X509Certificate getCertificate() {
+ return mCert;
+ }
+
+ public PublicKey getPublicKey() {
+ return mCert.getPublicKey();
+ }
+
+ public PrivateKey getPrivateKey() {
+ try {
+ return mManager.findPrivKeyByCert(mCert);
+ } catch (TokenException e) {
+ return null;
+ } catch (ObjectNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Verifies the integrity of the given key pair.
+ */
+ public void verify(byte publicKey[], PrivateKey privateKey)
+ throws EBaseException {
+ // XXX
+ }
+}