summaryrefslogtreecommitdiffstats
path: root/base/server/cms/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'base/server/cms/src/com')
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java123
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java218
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java1528
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java135
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java140
5 files changed, 2080 insertions, 64 deletions
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java b/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java
new file mode 100644
index 000000000..f8a5b1f6a
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java
@@ -0,0 +1,123 @@
+package com.netscape.cms.servlet.tks;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+
+public class KDF {
+ /* DES KEY Parity conversion table. Takes each byte >> 1 as an index, returns
+ * that byte with the proper parity bit set*/
+ static final int parityTable[] =
+ {
+ /* Even...0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e */
+ /* E */0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e,
+ /* Odd....0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e */
+ /* O */0x10, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x1f,
+ /* Odd....0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e */
+ /* O */0x20, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x2f,
+ /* Even...0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e */
+ /* E */0x31, 0x32, 0x34, 0x37, 0x38, 0x3b, 0x3d, 0x3e,
+ /* Odd....0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e */
+ /* O */0x40, 0x43, 0x45, 0x46, 0x49, 0x4a, 0x4c, 0x4f,
+ /* Even...0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e */
+ /* E */0x51, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, 0x5e,
+ /* Even...0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e */
+ /* E */0x61, 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x6e,
+ /* Odd....0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e */
+ /* O */0x70, 0x73, 0x75, 0x76, 0x79, 0x7a, 0x7c, 0x7f,
+ /* Odd....0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e */
+ /* O */0x80, 0x83, 0x85, 0x86, 0x89, 0x8a, 0x8c, 0x8f,
+ /* Even...0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e */
+ /* E */0x91, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, 0x9e,
+ /* Even...0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae */
+ /* E */0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xae,
+ /* Odd....0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe */
+ /* O */0xb0, 0xb3, 0xb5, 0xb6, 0xb9, 0xba, 0xbc, 0xbf,
+ /* Even...0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce */
+ /* E */0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xcb, 0xcd, 0xce,
+ /* Odd....0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde */
+ /* O */0xd0, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, 0xdf,
+ /* Odd....0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee */
+ /* O */0xe0, 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xef,
+ /* Even...0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe */
+ /* E */0xf1, 0xf2, 0xf4, 0xf7, 0xf8, 0xfb, 0xfd, 0xfe, };
+
+ public static byte[] getDiversificationData(byte[] context, String type) throws EBaseException {
+
+ String method = "KDF.getDiversificationData:";
+
+ CMS.debug(method + " entering ...");
+
+ if (context == null || type == null) {
+ throw new EBaseException(method + "Invalid input parameters!");
+ }
+
+ byte[] KDC = new byte[SecureChannelProtocol.DES2_LENGTH];
+
+ // BYTE *lastTwoBytesOfAID = (BYTE *)cuidValue;
+ // BYTE *ICFabricationDate = (BYTE *)cuidValue + 2;
+ // BYTE *ICSerialNumber = (BYTE *)cuidValue + 4
+ // BYTE *ICBatchIdentifier = (BYTE *)cuidValue + 8;
+
+ // Last 2 bytes of AID
+ KDC[0] = context[0];
+ KDC[1] = context[1];
+ KDC[2] = context[4 + 0];
+ KDC[3] = context[4 + 1];
+ KDC[4] = context[4 + 2];
+ KDC[5] = context[4 + 3];
+ KDC[6] = (byte) 0xF0;
+ KDC[7] = 0x01;
+ KDC[8] = context[0];
+ KDC[9] = context[1];
+ KDC[10] = context[4 + 0];
+ KDC[11] = context[4 + 1];
+ KDC[12] = context[4 + 2];
+ KDC[13] = context[4 + 3];
+ KDC[14] = 0x0F;
+ KDC[15] = 0x01;
+
+ if (type.equals(SecureChannelProtocol.encType))
+ return KDC;
+
+ KDC[6] = (byte) 0xF0;
+ KDC[7] = 0x02;
+ KDC[14] = 0x0F;
+ KDC[15] = 0x02;
+ if (type.equals(SecureChannelProtocol.macType))
+ return KDC;
+
+ KDC[6] = (byte) 0xF0;
+ KDC[7] = 0x03;
+ KDC[14] = 0x0F;
+ KDC[15] = 0x03;
+ if (type.equals(SecureChannelProtocol.kekType))
+ return KDC;
+
+ return KDC;
+
+ }
+
+ static public byte[] getDesParity(byte[] key) throws EBaseException {
+ String method = "KDF.getDesParity";
+ if (key == null || (key.length != SecureChannelProtocol.DES2_LENGTH &&
+ key.length != SecureChannelProtocol.EIGHT_BYTES && key.length != SecureChannelProtocol.DES3_LENGTH)) {
+ throw new EBaseException(method + " Incorrect input key !");
+ }
+
+ byte[] desKey = new byte[key.length];
+
+ for (int i = 0; i < key.length; i++) {
+ int index = key[i] & 0xff;
+ int finalIndex = index >> 1;
+
+ byte val = (byte) parityTable[finalIndex];
+ desKey[i] = val;
+
+ }
+
+ CMS.debug("desKey: len: " + desKey.length);
+
+ return desKey;
+ }
+
+}
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java b/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java
new file mode 100644
index 000000000..e392ce1a3
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java
@@ -0,0 +1,218 @@
+package com.netscape.cms.servlet.tks;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.HMACAlgorithm;
+import org.mozilla.jss.crypto.JSSMessageDigest;
+import org.mozilla.jss.crypto.SymmetricKey;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+
+public class NistSP800_108KDF extends KDF {
+
+ static final int KDF_OUTPUT_SIZE_BITS = 384;
+ static final int KDF_OUTPUT_SIZE_BYTES = KDF_OUTPUT_SIZE_BITS / 8;
+ static final int KEY_DATA_SIZE_BYTES = KDF_OUTPUT_SIZE_BYTES / 3;
+
+ static final int KDD_SIZE_BYTES = 10; // expected KDD field length in bytes
+
+ static final byte KDF_LABEL = 0x04; // arbitra
+
+ static final int SHA256_LENGTH = 32;
+
+ SecureChannelProtocol protocol = null;
+
+ NistSP800_108KDF(SecureChannelProtocol protocol) {
+ this.protocol = protocol;
+ }
+
+ static public boolean useThisKDF(byte nistSP800_108KDFonKeyVersion, byte requestedKeyVersion) {
+ return (requestedKeyVersion >= nistSP800_108KDFonKeyVersion);
+ }
+
+ /*******************************************************************************
+ Generates three PK11SymKey objects using the KDF_CM_SHA256HMAC_L384() function for key data.
+ After calling KDF_CM_SHA256HMAC_L384, the function splits up the output, sets DES parity,
+ and imports the keys into the token.
+
+ Careful: This function currently generates the key data **IN RAM** using calls to NSS sha256.
+ The key data is then "unwrapped" (imported) to the NSS token and then erased from RAM.
+ (This means that a malicious actor on the box could steal the key data.)
+
+ Note: Returned key material from the KDF is converted into keys according to the following:
+ * Bytes 0 - 15 : enc/auth key
+ * Bytes 16 - 31 : mac key
+ * Bytes 32 - 47 : kek key
+ We chose this order to conform with the key order used by the PUT KEY command.
+
+ *******************************************************************************/
+
+ public Map<String, SymmetricKey> computeCardKeys(SymmetricKey masterKey, byte[] context, CryptoToken token)
+ throws EBaseException {
+
+ String method = "NistSP800_108KDF.computeCardKeys:";
+
+ if (masterKey == null || context == null || token == null) {
+ throw new EBaseException(method + " Invlalid input parameters!");
+ }
+
+ Map<String, SymmetricKey> keys = new HashMap<String, SymmetricKey>();
+
+ byte[] kdf_output = null;
+
+ kdf_output = kdf_CM_SHA256_HMAC_L384(masterKey, context, KDF_LABEL, KDF_OUTPUT_SIZE_BYTES, token);
+
+ //Now create the 3 keys from only 48 of the 64 bytes...
+
+ byte[] enc = new byte[16];
+ byte[] mac = new byte[16];
+ byte[] kek = new byte[16];
+
+ System.arraycopy(kdf_output, 0 * SecureChannelProtocol.DES2_LENGTH, enc, 0, SecureChannelProtocol.DES2_LENGTH);
+ System.arraycopy(kdf_output, 1 * SecureChannelProtocol.DES2_LENGTH, mac, 0, SecureChannelProtocol.DES2_LENGTH);
+ System.arraycopy(kdf_output, 2 * SecureChannelProtocol.DES2_LENGTH, kek, 0, SecureChannelProtocol.DES2_LENGTH);
+
+ byte[] encFinal = KDF.getDesParity(enc);
+ byte[] macFinal = KDF.getDesParity(mac);
+ byte[] kekFinal = KDF.getDesParity(kek);
+
+ boolean showKeysOnlyForDebug = false;
+
+ if (showKeysOnlyForDebug) {
+
+ SecureChannelProtocol.debugByteArray(kdf_output, "kdf_CM_SHA256_HMAC_L384 output: ");
+
+ SecureChannelProtocol.debugByteArray(mac, " Nist mac before parity: ");
+ SecureChannelProtocol.debugByteArray(enc, " Nist enc before parity: ");
+ SecureChannelProtocol.debugByteArray(kek, " Nist kek before parityl: ");
+
+
+ SecureChannelProtocol.debugByteArray(macFinal, " Nist macFinal: ");
+ SecureChannelProtocol.debugByteArray(encFinal, " Nist encFinal: ");
+ SecureChannelProtocol.debugByteArray(kekFinal, " Nist kekFinal: ");
+ }
+
+ Arrays.fill(enc, (byte) 0);
+ Arrays.fill(mac, (byte) 0);
+ Arrays.fill(kek, (byte) 0);
+ Arrays.fill(kdf_output, (byte) 0);
+
+ SymmetricKey macKey = protocol.unwrapSymKeyOnToken(token, null, macFinal, false);
+ SymmetricKey encKey = protocol.unwrapSymKeyOnToken(token, null, encFinal, false);
+ SymmetricKey kekKey = protocol.unwrapSymKeyOnToken(token, null, kekFinal, false);
+
+ Arrays.fill(encFinal, (byte) 0);
+ Arrays.fill(macFinal, (byte) 0);
+ Arrays.fill(kekFinal, (byte) 0);
+
+ keys.put(SecureChannelProtocol.macType, macKey);
+ keys.put(SecureChannelProtocol.encType, encKey);
+ keys.put(SecureChannelProtocol.kekType, kekKey);
+
+ return keys;
+
+ }
+
+ /*******************************************************************************
+ Key Derivation Function in Counter Mode using PRF = SHA256HMAC (NIST SP 800-108)
+ Calculates 384 bits of diversified output from the provided master key (K_I)
+ *******************************************************************************/
+
+ private byte[] kdf_CM_SHA256_HMAC_L384(SymmetricKey masterKey, byte[] context, byte kdfLabel,
+ int kdfOutputSizeBytes, CryptoToken token) throws EBaseException {
+
+ String method = "NistSP800_108KDF.kdf_CM_SHA256_HMAC_L384:";
+
+ CMS.debug(method + " entering..");
+ final byte n = 2; // ceil(384 / (SHA256LENGTH * 8)) == 2
+ int L_BYTE_array_length = 2; // 384 = 0x0180 hex; 2 byte long representation
+
+ if (context == null) {
+ throw new EBaseException(method + " Input value context must not be null.");
+ }
+ // sanity check that output buffer is large enough to contain 384 bits
+ if (kdfOutputSizeBytes < KDF_OUTPUT_SIZE_BYTES) {
+ throw new EBaseException(method + " Array \"output\" must be at least 48 bytes in size.");
+ }
+
+ // calculate size of temporary buffer
+ int HMAC_DATA_INPUT_SIZE = context.length + 3 + L_BYTE_array_length; // Don't change without reviewing code below.
+
+ // prevent integer overflow
+ if (HMAC_DATA_INPUT_SIZE < context.length) {
+ throw new EBaseException(method + " Input parameter context size is too large.");
+ }
+
+ byte L_BYTE_array[] = new byte[L_BYTE_array_length]; // Array to store L in BYTES
+ L_BYTE_array[0] = 0x01;
+ L_BYTE_array[1] = (byte) 0x80;
+
+ // Hash Input = context + 5 BYTES
+ byte[] hmac_data_input = new byte[HMAC_DATA_INPUT_SIZE];
+
+ byte[] K = new byte[n * SHA256_LENGTH];
+
+ hmac_data_input[1] = kdfLabel;
+ hmac_data_input[2] = 0x0;
+ System.arraycopy(context, 0, hmac_data_input, 3, context.length);
+ System.arraycopy(L_BYTE_array, 0, hmac_data_input, context.length + 3, 2);
+
+ byte[] outputHMAC256 = null;
+
+ for (byte i = 1; i <= n; i++) {
+ hmac_data_input[0] = i;
+ outputHMAC256 = sha256HMAC(masterKey, hmac_data_input, HMAC_DATA_INPUT_SIZE, token);
+ CMS.debug(method + "outputHMAC256 len: " + outputHMAC256.length);
+ System.arraycopy(outputHMAC256, 0, K, (i - 1) * SHA256_LENGTH, SHA256_LENGTH);
+ Arrays.fill(outputHMAC256, (byte)0);
+ }
+
+ CMS.debug(method + " Full array: " + K.length + " bytes...");
+
+ byte[] finalOutput = new byte[KDF_OUTPUT_SIZE_BYTES];
+
+ System.arraycopy(K, 0, finalOutput, 0, KDF_OUTPUT_SIZE_BYTES);
+
+ Arrays.fill(K, (byte) 0);
+
+ CMS.debug(method + " finalOutput: " + finalOutput.length + " bytes...");
+
+ return finalOutput;
+ }
+
+ private byte[] sha256HMAC(SymmetricKey masterKey, // HMAC Secret Key (K_I)
+ byte[] hmac_data_input, // HMAC Input (i||04||00||context||0180)
+ int hMAC_DATA_INPUT_SIZE, // Input Length
+ CryptoToken token) throws EBaseException {
+
+ String method = "NistSP800_108KDF.sha256HMAC:";
+
+ CMS.debug(method + " Entering...");
+
+ byte[] digestBytes = null;
+
+ if (token == null) {
+ throw new EBaseException(method = " Invalid Crypto Token input!");
+ }
+
+ try {
+ JSSMessageDigest digest = token.getDigestContext(HMACAlgorithm.SHA256);
+ digest.initHMAC(masterKey);
+
+ digestBytes = digest.digest(hmac_data_input);
+ } catch (Exception e) {
+
+ CMS.debug(method + " Failure to HMAC the input data: " + e);
+ throw new EBaseException(method + e);
+ }
+
+ // SecureChannelProtocol.debugByteArray(digestBytes, " output of sha256HMAC: ");
+
+ return digestBytes;
+ }
+
+}
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java b/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java
new file mode 100644
index 000000000..83dd93c6e
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java
@@ -0,0 +1,1528 @@
+package com.netscape.cms.servlet.tks;
+
+import java.io.ByteArrayOutputStream;
+import java.io.CharConversionException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.CryptoManager.NotInitializedException;
+import org.mozilla.jss.NoSuchTokenException;
+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.KeyGenAlgorithm;
+import org.mozilla.jss.crypto.KeyGenerator;
+import org.mozilla.jss.crypto.KeyWrapAlgorithm;
+import org.mozilla.jss.crypto.KeyWrapper;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.SymmetricKey.NotExtractableException;
+import org.mozilla.jss.crypto.SymmetricKeyDeriver;
+import org.mozilla.jss.crypto.TokenException;
+
+import sun.security.pkcs11.wrapper.PKCS11Constants;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+
+public class SecureChannelProtocol {
+
+ static String sharedSecretKeyName = null;
+ static String masterKeyPrefix = null;
+
+ static final int KEYLENGTH = 16;
+ static final int PREFIXLENGHT = 128;
+ static final int DES2_LENGTH = 16;
+ static final int DES3_LENGTH = 24;
+ static final int EIGHT_BYTES = 8;
+ static final int KEYNAMELENGTH = PREFIXLENGHT + 7;
+ static final String TRANSPORT_KEY_NAME = "sharedSecret";
+ static final String DEFKEYSET_NAME = "defKeySet";
+
+ static final String encType = "enc";
+ static final String macType = "mac";
+ static final String kekType = "kek";
+ static final String authType = "auth";
+ static final String dekType = "dek";
+ static final int PROTOCOL_ONE = 1;
+ static final int PROTOCOL_TWO = 2;
+ static final int PROTOCOL_THREE = 3;
+ static final int HOST_CRYPTOGRAM = 0;
+ static final int CARD_CRYPTOGRAM = 1;
+
+ private SymmetricKey transportKey = null;
+ CryptoManager cryptoManager = null;
+
+ public SecureChannelProtocol() {
+ }
+
+ public byte[] computeCryptogram_SCP01(
+ String selectedToken, String keyNickName, byte[] card_challenge,
+ byte[] host_challenge, byte[] keyInfo,
+ byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+ boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+ byte[] xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+ byte[] xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+ int cryptogramType, byte[] authKeyArray, String useSoftToken_s, String keySet, String transportKeyName)
+ throws EBaseException {
+
+ String method = "SecureChannelProtocol.computeCryptogram_SCP01:";
+
+ CMS.debug(method + " Entering: Type: HOST=0 , CARD=1 : TYPE: " + cryptogramType);
+
+ if ((card_challenge == null || card_challenge.length != EIGHT_BYTES)
+ || (host_challenge == null || host_challenge.length != EIGHT_BYTES)) {
+
+ throw new EBaseException(method + " Invalid card challenge or host challenge!");
+
+ }
+
+ if (cryptogramType != HOST_CRYPTOGRAM && cryptogramType != CARD_CRYPTOGRAM) {
+ throw new EBaseException(method + " Invalid cyrptgram type!");
+ }
+
+ byte[] cryptogram = null;
+
+ SymmetricKey authKey = this.computeSessionKey_SCP01(SecureChannelProtocol.encType, selectedToken, keyNickName,
+ card_challenge, host_challenge, keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd,
+ xCUID, xKDD, authKeyArray, useSoftToken_s, keySet, transportKeyName);
+
+ byte[] input = new byte[DES2_LENGTH];
+ byte[] icv = new byte[EIGHT_BYTES];
+
+ if (cryptogramType == HOST_CRYPTOGRAM) // compute host cryptogram
+ {
+ /* copy card and host challenge into input buffer */
+ for (int i = 0; i < EIGHT_BYTES; i++)
+ {
+ input[i] = card_challenge[i];
+ }
+ for (int i = 0; i < EIGHT_BYTES; i++)
+ {
+ input[EIGHT_BYTES + i] = host_challenge[i];
+ }
+ } // compute card cryptogram
+ else if (cryptogramType == CARD_CRYPTOGRAM)
+ {
+ for (int i = 0; i < EIGHT_BYTES; i++)
+ {
+ input[i] = host_challenge[i];
+ }
+ for (int i = 0; i < EIGHT_BYTES; i++)
+ {
+ input[EIGHT_BYTES + i] = card_challenge[i];
+ }
+
+ }
+ cryptogram = computeMAC_SCP01(authKey, input, icv, selectedToken);
+
+ // SecureChannelProtocol.debugByteArray(cryptogram, " Output of computeCrytptogram type: " + cryptogramType);
+
+ return cryptogram;
+ }
+
+ public SymmetricKey computeSessionKey_SCP02(
+ String selectedToken, String keyNickName,
+ byte[] keyInfo,
+ byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+ boolean nistSP800_108KdfUseCuidAsKdd, byte[] xCUID, byte[] xKDD, byte[] macKeyArray,
+ byte[] sequenceCounter, byte[] derivationConstant,
+ String useSoftToken_s, String keySet,
+ String transportKeyName) throws EBaseException {
+
+ String method = "SecureChannelProtocol.computeSessionKey_SCP01:";
+
+ CMS.debug(method + " entering... ");
+
+ throw new EBaseException(method + " Not yet implemented!");
+ }
+
+ public SymmetricKey computeKEKKey_SCP01(
+ String selectedToken, String keyNickName,
+ byte[] keyInfo,
+ byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+ boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+ byte[] xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+ byte[] xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+ byte[] devKeyArray, String useSoftToken_s, String keySet, String transportKeyName) throws EBaseException {
+
+
+ String method = "SecureChannelProtocol.computeKEKKey_SCP01:";
+
+ CMS.debug(method + " entering... ");
+
+ return computeSessionKey_SCP01(SecureChannelProtocol.kekType, selectedToken, keyNickName, null, null, keyInfo,
+ nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, devKeyArray, useSoftToken_s,
+ keySet, transportKeyName);
+
+ }
+
+ public SymmetricKey computeSessionKey_SCP01(String keyType,
+ String selectedToken, String keyNickName, byte[] card_challenge,
+ byte[] host_challenge, byte[] keyInfo,
+ byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+ boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+ byte[] xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+ byte[] xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+ byte[] devKeyArray, String useSoftToken_s, String keySet, String transportKeyName) throws EBaseException {
+
+ String method = "SecureChannelProtocol.computeSessionKey_SCP01:";
+
+ CMS.debug(method + " entering... requested type: " + keyType);
+
+ // This gets set if there is no input card challenge and host challenge
+ // Allows this routine to be used for the "encryptData" routine built on top.
+
+ boolean noDerive = false;
+
+ if (keyType == null || devKeyArray == null || keyInfo == null
+ || keySet == null || transportKeyName == null || (keyInfo == null || keyInfo.length < 2)) {
+ throw new EBaseException(method + "Invalid input!");
+ }
+
+ if (xCUID == null || xCUID.length <= 0) {
+ throw new EBaseException(method + "CUID invalid size!");
+ }
+
+ if (xKDD == null || xKDD.length != NistSP800_108KDF.KDD_SIZE_BYTES) {
+ throw new EBaseException(method + "KDD invalid size!");
+ }
+
+ if (card_challenge == null && host_challenge == null) {
+ noDerive = true;
+ CMS.debug(method + " NoDerive mode: true");
+ } else {
+ if (card_challenge == null || host_challenge == null) {
+ throw new EBaseException(method + " Invalid input!");
+ }
+
+ CMS.debug(method + " NoDerive mode: false");
+ }
+
+ CMS.debug(method + " entering. nickname: " + keyNickName + " selectedToken: " + selectedToken);
+ CMS.debug(method + " nistSP800_108kdfOnKeyVersion: " + nistSP800_108KdfOnKeyVersion);
+
+ CryptoManager cm = null;
+ CryptoToken token = null;
+ CryptoToken internalToken = null;
+ try {
+ cm = CryptoManager.getInstance();
+ token = returnTokenByName(selectedToken, cm);
+ internalToken = returnTokenByName("internal", cm);
+ } catch (NotInitializedException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+
+ } catch (NoSuchTokenException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ sharedSecretKeyName = SecureChannelProtocol.getSharedSecretKeyName(transportKeyName);
+
+ transportKey = getSharedSecretKey(internalToken);
+
+ String keyNameStr = null;
+
+ SymmetricKey sessionKey = null;
+ SymmetricKey masterKey = null;
+
+ if (keyNickName == null) {
+ keyNameStr = this.getKeyName(keyInfo);
+ } else {
+ keyNameStr = keyNickName;
+ }
+
+ byte[] context = null;
+
+ if (nistSP800_108KdfUseCuidAsKdd == true) {
+ context = xCUID;
+ } else {
+ context = xKDD;
+ }
+
+ if ((keyInfo[0] == 0x1 && keyInfo[1] == 0x1 && keyNameStr.equals("#01#01")) ||
+ (keyInfo[0] == -1 && keyNameStr.indexOf("#FF") != -1))
+
+ {
+ /* default manufacturers key */
+
+ String finalKeyType = keyType;
+
+ SymmetricKey devSymKey = returnDeveloperSymKey(token, finalKeyType, keySet, devKeyArray);
+
+ // Create the auth with is the same as enc, might need it later.
+ if (keyType.equals(encType)) {
+ returnDeveloperSymKey(token, authType, keySet, devKeyArray);
+ }
+
+ if (noDerive == true) {
+ sessionKey = devSymKey;
+ } else {
+ sessionKey = deriveKey_SCP01(token, devSymKey, host_challenge, card_challenge);
+ }
+
+ } else {
+
+ SymmetricKey devKey = null;
+ CMS.debug(method + "In master key mode.");
+
+ masterKey = getSymKeyByName(token, keyNameStr);
+
+ if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, keyInfo[1])) {
+ CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using NIST SP800-108 KDF.");
+
+ NistSP800_108KDF nistKDF = new NistSP800_108KDF(this);
+
+ Map<String, SymmetricKey> keys = null;
+ try {
+ keys = nistKDF.computeCardKeys(masterKey, context, token);
+ } catch (EBaseException e) {
+ CMS.debug(method + "Can't compute card keys! " + e);
+ throw e;
+ }
+
+ devKey = keys.get(keyType);
+
+ } else {
+ StandardKDF standardKDF = new StandardKDF(this);
+ CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using original KDF.");
+ byte[] data = KDF.getDiversificationData(context, keyType);
+ devKey = standardKDF.computeCardKey(masterKey, data, token, PROTOCOL_ONE);
+ }
+
+ if (noDerive == true) {
+ sessionKey = devKey;
+ } else {
+ sessionKey = deriveKey_SCP01(token, devKey, host_challenge, card_challenge);
+ }
+ }
+
+ return sessionKey;
+ }
+
+ private SymmetricKey deriveKey_SCP01(CryptoToken token, SymmetricKey cardKey, byte[] host_challenge,
+ byte[] card_challenge)
+ throws EBaseException {
+ String method = "SecureChannelProtocol.deriveKey_SCP01:";
+ CMS.debug(method + "entering..");
+
+ if (cardKey == null || token == null) {
+ throw new EBaseException(method + " Invalid input data!");
+ }
+
+ byte[] derivationData = new byte[KEYLENGTH];
+
+ SymmetricKey derivedKey = null;
+
+ for (int i = 0; i < 4; i++)
+ {
+ derivationData[i] = card_challenge[i + 4];
+ derivationData[i + 4] = host_challenge[i];
+ derivationData[i + 8] = card_challenge[i];
+ derivationData[i + 12] = host_challenge[i + 4];
+ }
+
+ SymmetricKeyDeriver encryptDes3;
+ byte[] encrypted = null;
+ try {
+ encryptDes3 = token.getSymmetricKeyDeriver();
+
+ encryptDes3.initDerive(
+ cardKey, /* PKCS11Constants.CKM_DES3_ECB_ENCRYPT_DATA */4354L, derivationData, null,
+ PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE, 16);
+
+ try {
+ derivedKey = encryptDes3.derive();
+ } catch (TokenException e) {
+ CMS.debug(method + "Unable to derive the key with the proper mechanism!" + e);
+ CMS.debug(method + "Now try this the old fashioned way");
+
+ encrypted = computeDes3EcbEncryption(cardKey, token.getName(), derivationData);
+ byte[] parityEncrypted = KDF.getDesParity(encrypted);
+ CMS.debug(method + "encryption completed");
+
+ derivedKey = this.unwrapSymKeyOnToken(token, null, parityEncrypted, false);
+ }
+
+ } catch (TokenException | InvalidKeyException | EBaseException e) {
+ CMS.debug(method + "Unable to derive the key with the proper mechanism!");
+ throw new EBaseException(e);
+ }
+
+ return derivedKey;
+ }
+
+ public SymmetricKey getSharedSecretKey(CryptoToken token) throws EBaseException {
+
+ String method = "SecureChannelProtocol.getSharedSecretKey:";
+ CMS.debug(method + "entering: transportKey: " + transportKey);
+
+ CryptoToken finalToken = token;
+ CryptoToken internalToken = null;
+ if (token == null) {
+
+ CMS.debug(method + " No token provided assume internal ");
+ CryptoManager cm = null;
+ try {
+ cm = CryptoManager.getInstance();
+ internalToken = returnTokenByName("internal", cm);
+ finalToken = internalToken;
+ } catch (NotInitializedException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+
+ } catch (NoSuchTokenException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+ }
+
+ if (transportKey == null) {
+ transportKey = getSymKeyByName(finalToken, sharedSecretKeyName);
+ }
+
+ if (transportKey == null) {
+ throw new EBaseException(method + "Can't locate shared secret key in token db.");
+ }
+
+ return transportKey;
+ }
+
+ private String getKeyName(byte[] keyVersion) {
+ String method = "SecureChannelProtocol.getKeyName:";
+ CMS.debug(method + " Entering...");
+ String keyName = null;
+
+ if (keyVersion == null || keyVersion.length != 2) {
+ return null;
+ }
+
+ SecureChannelProtocol.debugByteArray(keyVersion, "keyVersion array:");
+ keyName = "#" + String.format("%02X", keyVersion[0]) + "#" + String.format("%02X", keyVersion[1]);
+
+ CMS.debug(method + " returning: " + keyName);
+
+ return keyName;
+ }
+
+ public static String getSharedSecretKeyName(String name) throws EBaseException {
+
+ String method = "SecureChannelProtocol.getSharedSecretKeyName:";
+ CMS.debug(method + " Entering...");
+
+ if (name != null && SecureChannelProtocol.sharedSecretKeyName == null) {
+ SecureChannelProtocol.sharedSecretKeyName = name;
+ }
+
+ if (SecureChannelProtocol.sharedSecretKeyName == null) {
+ throw new EBaseException(method + " Can not find shared secret key name!");
+ }
+
+ return SecureChannelProtocol.sharedSecretKeyName;
+ }
+
+ public static String setSharedSecretKeyName(String name) throws EBaseException {
+ return SecureChannelProtocol.getSharedSecretKeyName(name);
+ }
+
+ /* This routine will attempt to return one of the well known developer symmetric keys from the token.
+ Each key, is merely stored on the token for convenience.
+ If the given key is not found on the token it is put there and left on as a permanent key.
+ From that point it is a simple matter of retrieving the desired key from the token.
+ No security advantage is implied or desired here.
+ */
+ private SymmetricKey returnDeveloperSymKey(CryptoToken token, String keyType, String keySet, byte[] inputKeyArray)
+ throws EBaseException {
+
+ SymmetricKey devKey = null;
+
+ String method = "SecureChannelProtocol.returnDeveloperSymKey:";
+
+ String devKeyName = keySet + "-" + keyType + "Key";
+ CMS.debug(method + " entering.. searching for key: " + devKeyName);
+
+ if (token == null || keyType == null || keySet == null) {
+ throw new EBaseException(method + "Invalid input data!");
+ }
+
+ try {
+ CMS.debug(method + " requested token: " + token.getName());
+ } catch (TokenException e) {
+ throw new EBaseException(method + e);
+ }
+
+ devKey = getSymKeyByName(token, devKeyName);
+
+ if (devKey == null) {
+ //Put the key there and leave it
+
+ byte[] des3InputKey = null;
+
+ if (inputKeyArray == null) {
+ throw new EBaseException(method + "Input key is null and has to be non null when importing...");
+ }
+ int inputLen = inputKeyArray.length;
+
+ CMS.debug(method + " inputKeyArray.length: " + inputLen);
+
+ if (inputLen != DES3_LENGTH && inputLen != DES2_LENGTH) {
+ throw new EBaseException(method + "invalid input key length!");
+ }
+
+ if (inputLen == DES2_LENGTH) {
+ des3InputKey = new byte[DES3_LENGTH];
+ System.arraycopy(inputKeyArray, 0, des3InputKey, 0, DES2_LENGTH);
+ System.arraycopy(inputKeyArray, 0, des3InputKey, DES2_LENGTH, EIGHT_BYTES);
+
+ } else {
+ System.arraycopy(inputKeyArray, 0, des3InputKey, 0, DES3_LENGTH);
+ }
+
+ SecureChannelProtocol.debugByteArray(des3InputKey, "Developer key to import: " + keyType + ": ");
+
+ devKey = unwrapSymKeyOnToken(token, des3InputKey, true);
+ devKey.setNickName(devKeyName);
+ } else {
+ CMS.debug(method + " Found sym key: " + devKeyName);
+ }
+ return devKey;
+ }
+
+ public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray,
+ boolean isPerm)
+ throws EBaseException {
+
+ String method = "SecureChannelProtocol.unwrapSymKeyOnToken:";
+ CMS.debug(method + "Entering...");
+ SymmetricKey unwrapped = null;
+ SymmetricKey tempKey = null;
+
+ if (token == null) {
+ throw new EBaseException(method + "Invalid input!");
+ }
+
+ if (inputKeyArray == null || (inputKeyArray.length != DES3_LENGTH && inputKeyArray.length != DES2_LENGTH)) {
+ throw new EBaseException(method + "No raw array to use to create key!");
+ }
+
+ if (unwrappingKey == null) {
+ try {
+ KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3);
+
+ SymmetricKey.Usage usages[] = new SymmetricKey.Usage[4];
+ usages[0] = SymmetricKey.Usage.WRAP;
+ usages[1] = SymmetricKey.Usage.UNWRAP;
+ usages[2] = SymmetricKey.Usage.ENCRYPT;
+ usages[3] = SymmetricKey.Usage.DECRYPT;
+
+ kg.setKeyUsages(usages);
+ kg.temporaryKeys(true);
+ tempKey = kg.generate();
+ } catch (NoSuchAlgorithmException | TokenException | IllegalStateException | CharConversionException e) {
+ throw new EBaseException(method + "Can't generate temporary key to unwrap the key.");
+ }
+
+ }
+
+ byte[] finalKeyArray = null;
+
+ if (inputKeyArray.length == DES2_LENGTH) {
+ finalKeyArray = SecureChannelProtocol.makeDes3FromDes2(inputKeyArray);
+ }
+
+ Cipher encryptor = null;
+ byte[] wrappedKey = null;
+
+ SymmetricKey encUnwrapKey = null;
+
+ if (tempKey != null) {
+ encUnwrapKey = tempKey;
+ } else {
+ encUnwrapKey = unwrappingKey;
+ }
+
+ try {
+ encryptor = token.getCipherContext(EncryptionAlgorithm.DES3_ECB);
+
+ encryptor.initEncrypt(encUnwrapKey);
+
+ if (finalKeyArray != null) {
+ wrappedKey = encryptor.doFinal(finalKeyArray);
+ } else {
+ wrappedKey = encryptor.doFinal(inputKeyArray);
+ }
+
+ CMS.debug(method + " done enrypting data");
+
+ // SecureChannelProtocol.debugByteArray(wrappedKey, " encrypted key");
+
+ KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB);
+ keyWrap.initUnwrap(encUnwrapKey, null);
+
+ if (isPerm == true) {
+ unwrapped = keyWrap.unwrapSymmetricPerm(wrappedKey,
+ SymmetricKey.DES3, 0);
+ } else {
+ unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 0);
+ }
+
+ } catch (Exception e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ } finally {
+ if (finalKeyArray != null) {
+ Arrays.fill(finalKeyArray, (byte) 0);
+ }
+ }
+
+ CMS.debug(method + "Returning symkey: " + unwrapped);
+
+ return unwrapped;
+ }
+
+ public SymmetricKey unwrapWrappedSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray,
+ boolean isPerm)
+ throws EBaseException {
+
+ String method = "SecureChannelProtocol.unwrapWrappedSymKeyOnToken:";
+ CMS.debug(method + "Entering...");
+ SymmetricKey unwrapped = null;
+ SymmetricKey finalUnwrapped = null;
+
+ if (token == null || unwrappingKey == null) {
+ throw new EBaseException(method + "Invalid input!");
+ }
+
+ if (inputKeyArray == null || (inputKeyArray.length != DES3_LENGTH && inputKeyArray.length != DES2_LENGTH)) {
+ throw new EBaseException(method + "No raw array to use to create key!");
+ }
+
+ try {
+ KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB);
+ keyWrap.initUnwrap(unwrappingKey, null);
+
+ if (isPerm) {
+ unwrapped = keyWrap.unwrapSymmetricPerm(inputKeyArray,
+ SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, inputKeyArray.length);
+ } else {
+ unwrapped = keyWrap.unwrapSymmetric(inputKeyArray, SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP,
+ inputKeyArray.length);
+ }
+
+ finalUnwrapped = makeDes3KeyDerivedFromDes2(unwrapped, token.getName());
+
+ } catch (Exception e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ CMS.debug(method + "Returning symkey: " + unwrapped);
+
+ return finalUnwrapped;
+ }
+
+ public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, byte[] inputKeyArray, boolean isPerm)
+ throws EBaseException {
+
+ String method = "SecureChannelProtocol.unwrapSymKeyOnToken:";
+ CMS.debug(method + "Entering...");
+ SymmetricKey unwrapped = null;
+
+ if (token == null) {
+ throw new EBaseException(method + "Invalide crypto token!");
+ }
+
+ if (inputKeyArray == null || (inputKeyArray.length != DES3_LENGTH && inputKeyArray.length != DES2_LENGTH)) {
+ throw new EBaseException(method + "No raw array to use to create key!");
+ }
+
+ SymmetricKey transport = getSharedSecretKey(token);
+ unwrapped = this.unwrapSymKeyOnToken(token, transport, inputKeyArray, isPerm);
+
+ CMS.debug(method + "Returning symkey: " + unwrapped);
+
+ return unwrapped;
+ }
+
+ public static SymmetricKey getSymKeyByName(CryptoToken token, String name) throws EBaseException {
+
+ String method = "SecureChannelProtocol.getSymKeyByName:";
+ if (token == null || name == null) {
+ throw new EBaseException(method + "Invalid input data!");
+ }
+ SymmetricKey[] keys;
+
+ CMS.debug(method + "Searching for sym key: " + name);
+ try {
+ keys = token.getCryptoStore().getSymmetricKeys();
+ } catch (TokenException e) {
+ throw new EBaseException(method + "Can't get the list of symmetric keys!");
+ }
+ int len = keys.length;
+ for (int i = 0; i < len; i++) {
+ SymmetricKey cur = keys[i];
+ if (cur != null) {
+ if (name.equals(cur.getNickName())) {
+ CMS.debug(method + "Found key: " + name);
+ return cur;
+ }
+ }
+ }
+
+ CMS.debug(method + " Sym Key not found.");
+ return null;
+ }
+
+ public CryptoToken returnTokenByName(String name, CryptoManager manager) throws NoSuchTokenException {
+
+ if (name == null || manager == null)
+ throw new NoSuchTokenException();
+
+ if (name.equals("internal") || name.equals("Internal KeyStorage Token")) {
+ return manager.getInternalKeyStorageToken();
+ } else {
+ return manager.getTokenByName(name);
+ }
+
+ }
+
+ public static byte[] makeDes3FromDes2(byte[] des2) {
+
+ if (des2 == null || des2.length != SecureChannelProtocol.DES2_LENGTH) {
+ return null;
+ }
+
+ byte[] des3 = new byte[SecureChannelProtocol.DES3_LENGTH];
+
+ System.arraycopy(des2, 0, des3, 0, SecureChannelProtocol.DES2_LENGTH);
+ System.arraycopy(des2, 0, des3, DES2_LENGTH, EIGHT_BYTES);
+
+ return des3;
+ }
+
+ public static void debugByteArray(byte[] array, String message) {
+
+ CMS.debug("About to dump array: " + message);
+
+ if (array == null) {
+ CMS.debug("Array to dump is empty!");
+ return;
+ }
+
+ CMS.debug("################### ");
+
+ String result = getHexString(array);
+ CMS.debug(result);
+ }
+
+ final protected static char[] hex = "0123456789abcdef".toCharArray();
+
+ public static String getHexString(byte[] bytes) {
+
+ char[] hexChars = new char[bytes.length * 3];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 3] = hex[v >>> 4];
+ hexChars[j * 3 + 1] = hex[v & 0x0F];
+ hexChars[j * 3 + 2] = ':';
+ }
+ return new String(hexChars);
+ }
+
+ public CryptoManager getCryptoManger() throws EBaseException {
+ String method = "SecureChannelProtocol.getCryptoManager";
+ CryptoManager cm = null;
+
+ if (cryptoManager != null)
+ return cryptoManager;
+
+ try {
+ cm = CryptoManager.getInstance();
+ } catch (NotInitializedException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+
+ }
+
+ cryptoManager = cm;
+
+ return cryptoManager;
+
+ }
+
+ public static byte[] longToBytes(long x) {
+ ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
+ buffer.putLong(x);
+ return buffer.array();
+ }
+
+ /* Generate 24 key, but with a DES2 key converted to DES3
+ This needed to appease the server side keygen and the coollkey applet.
+ */
+ public SymmetricKey generateSymKey(String selectedToken) throws EBaseException {
+ String method = "SecureChannelProtocol.generateSymKey:";
+
+ CMS.debug(method + " entering , token: " + selectedToken);
+ SymmetricKey symKey = null;
+ SymmetricKey symKeyFinal = null;
+
+ if (selectedToken == null) {
+ throw new EBaseException(method + " Invalid input data!");
+ }
+
+ try {
+ CryptoManager cm = this.getCryptoManger();
+ CryptoToken token = returnTokenByName(selectedToken, cm);
+
+ KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3);
+
+ symKey = kg.generate();
+
+ symKeyFinal = this.makeDes3KeyDerivedFromDes2(symKey, selectedToken);
+
+ } catch (NoSuchAlgorithmException | TokenException | NoSuchTokenException | IllegalStateException
+ | CharConversionException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ return symKeyFinal;
+
+ }
+
+ public byte[] ecbEncrypt(SymmetricKey devKey, SymmetricKey symKey, String selectedToken) throws EBaseException {
+ byte[] result = null;
+ String method = "SecureChannelProtocol.ecbEncrypt:";
+ CMS.debug(method + " Entering...");
+
+ if (devKey == null || symKey == null || selectedToken == null) {
+ throw new EBaseException(method + " Invalid input parameters.");
+ }
+
+ String devKeyToken = null;
+ try {
+ devKeyToken = symKey.getOwningToken().getName();
+ CMS.debug(method + " symKey token: " + devKeyToken);
+ CMS.debug(method + " devKey token: " + devKey.getOwningToken().getName());
+
+ } catch (TokenException e) {
+ }
+ SymmetricKey des2 = this.extractDes2FromDes3(symKey, devKeyToken);
+
+ result = this.wrapSessionKey(selectedToken, des2, devKey);
+
+ // SecureChannelProtocol.debugByteArray(result, " Wrapped des2 key");
+
+ return result;
+ }
+
+ /* Convenience routine to create a 3DES key from a 2DES key.
+ This is done by taking the first 8 bytes of the 2DES key and copying it to the end, making
+ a faux 3DES key. This is required due to applet requirements.
+ */
+ public SymmetricKey makeDes3KeyDerivedFromDes2(SymmetricKey des3Key, String selectedToken) throws EBaseException {
+ SymmetricKey des3 = null;
+
+ String method = "SecureChannelProtocol.makeDes3KeyDerivedFromDes2:";
+
+ CMS.debug(method + " Entering ...");
+
+ if (des3Key == null || selectedToken == null) {
+ throw new EBaseException(method + " Invalid input data!");
+ }
+
+ try {
+ CryptoManager cm = this.getCryptoManger();
+ CryptoToken token = returnTokenByName(selectedToken, cm);
+
+ long bitPosition = 0;
+
+ byte[] param = SecureChannelProtocol.longToBytes(bitPosition);
+
+ SymmetricKey extracted16 = this.extractDes2FromDes3(des3Key, selectedToken);
+
+ SymmetricKeyDeriver extract8 = token.getSymmetricKeyDeriver();
+
+ extract8.initDerive(
+ extracted16, PKCS11Constants.CKM_EXTRACT_KEY_FROM_KEY, param, null,
+ PKCS11Constants.CKA_ENCRYPT, PKCS11Constants.CKA_DERIVE, 8);
+
+ SymmetricKey extracted8 = extract8.derive();
+
+ CMS.debug(method + " extracted8 key: " + extracted8);
+
+ SymmetricKeyDeriver concat = token.getSymmetricKeyDeriver();
+ concat.initDerive(
+ extracted16, extracted8, PKCS11Constants.CKM_CONCATENATE_BASE_AND_KEY, null, null,
+ PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE, 0);
+
+ des3 = concat.derive();
+
+ } catch (NoSuchTokenException | IllegalStateException | TokenException | InvalidKeyException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ return des3;
+ }
+
+ public SymmetricKey extractDes2FromDes3(SymmetricKey baseKey, String selectedToken) throws EBaseException {
+ String method = "SecureChannelProtocol.extractDes2FromDes3:";
+ CMS.debug(method + " Entering: ");
+
+ SymmetricKey extracted16 = null;
+
+ if (baseKey == null || selectedToken == null) {
+ throw new EBaseException(method + " Invalid input data.");
+ }
+
+ try {
+ CryptoManager cm = this.getCryptoManger();
+ CryptoToken token = returnTokenByName(selectedToken, cm);
+
+ long bitPosition = 0;
+
+ byte[] param = SecureChannelProtocol.longToBytes(bitPosition);
+
+ SymmetricKeyDeriver extract16 = token.getSymmetricKeyDeriver();
+ extract16.initDerive(
+ baseKey, PKCS11Constants.CKM_EXTRACT_KEY_FROM_KEY, param, null,
+ PKCS11Constants.CKA_ENCRYPT, PKCS11Constants.CKA_DERIVE, 16);
+
+ extracted16 = extract16.derive();
+
+ } catch (NoSuchTokenException | IllegalStateException | TokenException | InvalidKeyException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ return extracted16;
+ }
+
+ /* If wrappingKey is not null, use it, otherwise use the shared secret key
+ */
+ public byte[] wrapSessionKey(String tokenName, SymmetricKey sessionKey, SymmetricKey wrappingKey)
+ throws EBaseException {
+ //Now wrap the key for the trip back to TPS with shared secret transport key
+
+ String method = "SecureChannelProtocol.wrapSessionKey";
+
+ KeyWrapper keyWrap = null;
+ byte[] wrappedSessKeyData = null;
+
+ if (tokenName == null || sessionKey == null) {
+ throw new EBaseException(method + " Invalid input data.");
+ }
+
+ SymmetricKey wrapper = null;
+
+ if (wrappingKey == null) {
+ wrapper = transportKey;
+ } else {
+ wrapper = wrappingKey;
+ }
+
+ try {
+ CryptoManager cm = this.getCryptoManger();
+ CryptoToken token = returnTokenByName(tokenName, cm);
+
+ keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB);
+ keyWrap.initWrap(wrapper, null);
+ wrappedSessKeyData = keyWrap.wrap(sessionKey);
+ } catch (NoSuchAlgorithmException | TokenException | InvalidKeyException | InvalidAlgorithmParameterException
+ | NoSuchTokenException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ CMS.debug(method + " About to return session key: " + wrappedSessKeyData);
+
+ return wrappedSessKeyData;
+
+ }
+
+ public byte[] computeDes3EcbEncryption(SymmetricKey desKey, String selectedToken, byte[] input)
+ throws EBaseException {
+
+ String method = "SecureChannelProtocol.computeDes3EcbEncryption";
+ byte[] output = null;
+
+ if (desKey == null || selectedToken == null) {
+ throw new EBaseException(method + " Invalid input data.");
+ }
+
+ try {
+ CryptoManager cm = this.getCryptoManger();
+ CryptoToken token = returnTokenByName(selectedToken, cm);
+ CMS.debug("desKey: owning token: " + desKey.getOwningToken().getName());
+ CMS.debug("desKey: current token: " + token.getName());
+ Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.DES3_ECB);
+ CMS.debug("got encryptor");
+ encryptor.initEncrypt(desKey);
+ CMS.debug("done initEncrypt");
+ output = encryptor.doFinal(input);
+ CMS.debug("done doFinal " + output);
+
+ // SecureChannelProtocol.debugByteArray(output, "Encrypted data:");
+ } catch (EBaseException | NoSuchTokenException | NoSuchAlgorithmException | TokenException
+ | InvalidKeyException | InvalidAlgorithmParameterException |
+ IllegalStateException | IllegalBlockSizeException | BadPaddingException e) {
+
+ CMS.debug(method + e);
+ throw new EBaseException(method + e);
+ }
+ CMS.debug("returning encrypted output.");
+ // SecureChannelProtocol.debugByteArray(output, "Encrypted data before leaving:");
+
+ return output;
+ }
+
+ public byte[] computeKeyCheck(SymmetricKey desKey, String selectedToken) throws EBaseException {
+
+ String method = "SecureChannelProtocol.computeKeyCheck:";
+
+ CMS.debug(method + " Entering...");
+
+ byte[] input = new byte[EIGHT_BYTES];
+ byte[] finalOutput = new byte[3];
+
+ if (desKey == null || selectedToken == null) {
+ throw new EBaseException(method + " Invalid input data.");
+ }
+
+ byte[] output = null;
+ String keysToken = null;
+ try {
+ keysToken = desKey.getOwningToken().getName();
+ } catch (TokenException e1) {
+ throw new EBaseException(e1 + " Can't get owning token for key/");
+ }
+
+ try {
+ output = computeDes3EcbEncryption(desKey, keysToken, input);
+ } catch (EBaseException e) {
+ CMS.debug(method + e);
+ throw e;
+
+ }
+
+ //Get the 3 bytes needed
+ System.arraycopy(output, 0, finalOutput, 0, 3);
+
+ SecureChannelProtocol.debugByteArray(finalOutput, "Calculated KeyCheck Value:");
+
+ return finalOutput;
+ }
+
+ public byte[] computeMAC_SCP01(SymmetricKey symKey, byte[] input, byte[] icv, String selectedToken)
+ throws EBaseException {
+ byte[] output = null;
+ byte[] result = null;
+
+ String method = "SecureChannelProtocol.computeMAC_SCP01:";
+
+ CMS.debug(method + " Entering...");
+
+ if (symKey == null || input == null || icv == null || icv.length != EIGHT_BYTES) {
+ throw new EBaseException(method + " invalid input data!");
+ }
+ int inputLen = input.length;
+
+ byte[] macPad = new byte[8];
+ macPad[0] = (byte) 0x80;
+
+ CryptoToken token = null;
+
+ try {
+
+ CryptoManager cm = this.getCryptoManger();
+ token = returnTokenByName(selectedToken, cm);
+
+ Cipher cipher = token.getCipherContext(EncryptionAlgorithm.DES3_ECB);
+ cipher.initEncrypt(symKey);
+
+ result = new byte[EIGHT_BYTES];
+ System.arraycopy(icv, 0, result, 0, EIGHT_BYTES);
+
+ /* Process whole blocks */
+ int inputOffset = 0;
+ while (inputLen >= 8)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ //Xor implicitly converts bytes to ints, we convert answer back to byte.
+ byte a = (byte) (result[i] ^ input[inputOffset + i]);
+ result[i] = a;
+ }
+
+ byte[] ciphResult = cipher.update(result);
+
+ if (ciphResult.length != result.length) {
+ throw new EBaseException(method + " Invalid cipher!");
+ }
+
+ System.arraycopy(ciphResult, 0, result, 0, EIGHT_BYTES);
+
+ inputLen -= 8;
+ inputOffset += 8;
+ }
+
+ /*
+ * Fold in remaining data (if any)
+ * Set i to number of bytes processed
+ */
+ int i = 0;
+ for (i = 0; i < inputLen; i++)
+ {
+ byte a = (byte) (result[i] ^ input[inputOffset + i]);
+ result[i] = a;
+ }
+
+ /*
+ * Fill remainder of last block. There
+ * will be at least one byte handled here.
+ */
+
+ //Start at the beginning of macPad
+ // Keep going with i in result where we left off.
+ int padOffset = 0;
+ while (i < 8)
+ {
+ byte a = (byte) (result[i] ^ macPad[padOffset++]);
+ result[i] = a;
+ i++;
+ }
+
+ output = cipher.doFinal(result);
+
+ if (output.length != result.length) {
+ throw new EBaseException(method + " Invalid cipher!");
+ }
+
+ } catch (Exception e) {
+ throw new EBaseException(method + " Cryptographic problem encountered! " + e.toString());
+ }
+
+ // SecureChannelProtocol.debugByteArray(output, method + " output: ");
+
+ return output;
+ }
+
+ public byte[] diversifyKey(String tokenName,
+ String newTokenName,
+ String oldMasterKeyName,
+ String newMasterKeyName,
+ byte[] oldKeyInfo,
+ byte[] newKeyInfo,
+ byte nistSP800_108KdfOnKeyVersion,
+ boolean nistSP800_108KdfUseCuidAsKdd,
+ byte[] CUIDValue,
+ byte[] KDD,
+ byte[] kekKeyArray,
+ String useSoftToken, String keySet, byte protocol) throws EBaseException {
+
+ String method = "SecureChannelProtocol.diversifyKey:";
+
+ CMS.debug(method + " Entering ... newTokenName: " + newTokenName);
+
+ SymmetricKey masterKey = null;
+ SymmetricKey oldMasterKey = null;
+
+ byte[] KDCenc = null;
+ byte[] KDCmac = null;
+ byte[] KDCkek = null;
+
+ SymmetricKey old_mac_sym_key = null;
+ SymmetricKey old_enc_sym_key = null;
+ SymmetricKey old_kek_sym_key = null;
+
+ SymmetricKey encKey = null;
+ SymmetricKey macKey = null;
+ SymmetricKey kekKey = null;
+
+ // The final answer
+ byte[] output = null;
+
+ if (oldMasterKeyName == null || oldKeyInfo == null || newKeyInfo == null
+ || keySet == null) {
+ throw new EBaseException(method + "Invalid input!");
+ }
+
+ if (oldKeyInfo.length < 2 || newKeyInfo.length < 2) {
+ throw new EBaseException(method + " Invalid input length for keyinfo versions.");
+ }
+
+ String fullNewMasterKeyName = getFullMasterKeyName(newMasterKeyName);
+ String fullOldMasterKeyName = getFullMasterKeyName(oldMasterKeyName);
+
+ CryptoManager cm = null;
+ CryptoToken token = null;
+ CryptoToken newToken = null;
+ try {
+ cm = CryptoManager.getInstance();
+ token = returnTokenByName(tokenName, cm);
+ if (newTokenName != null) {
+ newToken = returnTokenByName(newTokenName, cm);
+ }
+ } catch (NotInitializedException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+
+ } catch (NoSuchTokenException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ try {
+ if (newToken != null) {
+ masterKey = getSymKeyByName(newToken, fullNewMasterKeyName);
+ }
+ oldMasterKey = getSymKeyByName(token, fullOldMasterKeyName);
+ } catch (EBaseException e) {
+ masterKey = null;
+ CMS.debug(method + " Master key is null, possibly ok in moving from keyset 2 to 1");
+
+ if (oldMasterKey == null) {
+ throw new EBaseException(method + " Can't retrieve old master key!");
+ }
+ }
+
+ SecureChannelProtocol.debugByteArray(oldKeyInfo, " oldKeyInfo: ");
+ SecureChannelProtocol.debugByteArray(newKeyInfo, " newKeyInfo: ");
+
+ byte oldKeyVersion = oldKeyInfo[0];
+ byte newKeyVersion = newKeyInfo[0];
+
+ byte[] context = null;
+
+ if (nistSP800_108KdfUseCuidAsKdd == true) {
+ context = CUIDValue;
+ } else {
+ context = KDD;
+ }
+
+ if (context == null) {
+ throw new EBaseException(method + "Invalid token id information included!");
+ }
+
+ // We may need either or both of these
+
+ StandardKDF standardKDF = new StandardKDF(this);
+ NistSP800_108KDF nistKDF = new NistSP800_108KDF(this);
+
+ KDCenc = KDF.getDiversificationData(KDD, SecureChannelProtocol.encType);
+ KDCmac = KDF.getDiversificationData(KDD, SecureChannelProtocol.macType);
+ KDCkek = KDF.getDiversificationData(KDD, SecureChannelProtocol.kekType);
+
+ if (protocol == PROTOCOL_ONE) {
+ if (checkForDeveloperKeySet(oldMasterKeyName) == true) {
+ CMS.debug(method + " Developer key set case: ");
+ } else {
+ CMS.debug(method + " Not Developer key set case: ");
+
+ if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, oldKeyVersion)) {
+ CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF.");
+
+ Map<String, SymmetricKey> keys = null;
+ try {
+ keys = nistKDF.computeCardKeys(oldMasterKey, context, token);
+ } catch (EBaseException e) {
+ CMS.debug(method + "Can't compute card keys! " + e);
+ throw e;
+ }
+
+ old_enc_sym_key = keys.get(SecureChannelProtocol.encType);
+ old_mac_sym_key = keys.get(SecureChannelProtocol.macType);
+ old_kek_sym_key = keys.get(SecureChannelProtocol.kekType);
+
+ if (old_enc_sym_key == null || old_mac_sym_key == null || old_kek_sym_key == null) {
+ throw new EBaseException(method + " Can't derive session keys with Nist KDF.");
+ }
+
+ } else {
+ CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using original KDF.");
+
+ old_kek_sym_key = standardKDF.computeCardKey(oldMasterKey, KDCkek, token, PROTOCOL_ONE);
+ }
+
+ }
+
+ /* special case #01#01 */
+ if (fullNewMasterKeyName != null && fullNewMasterKeyName.equals("#01#01"))
+ {
+ CMS.debug(method + " Special case dev key set for DiversifyKey!");
+
+ encKey = returnDeveloperSymKey(newToken, SecureChannelProtocol.encType, keySet, null);
+ macKey = returnDeveloperSymKey(newToken, SecureChannelProtocol.macType, keySet, null);
+ kekKey = returnDeveloperSymKey(newToken, SecureChannelProtocol.kekType, keySet, null);
+ } else {
+ CMS.debug(method + " Compute card key on token case ! For new key version");
+
+ if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, newKeyVersion)) {
+ CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF. For new key version.");
+
+ Map<String, SymmetricKey> keys = null;
+ try {
+ keys = nistKDF.computeCardKeys(masterKey, context, newToken);
+ } catch (EBaseException e) {
+ CMS.debug(method + "Can't compute card keys! For new key version. " + e);
+ throw e;
+ }
+
+ encKey = keys.get(SecureChannelProtocol.encType);
+ macKey = keys.get(SecureChannelProtocol.macType);
+ kekKey = keys.get(SecureChannelProtocol.kekType);
+
+ } else {
+ CMS.debug(method
+ + " ComputeSessionKey NistSP800_108KDF code: Using original KDF. For new key version.");
+
+ encKey = standardKDF.computeCardKeyOnToken(masterKey, KDCenc, protocol);
+ macKey = standardKDF.computeCardKeyOnToken(masterKey, KDCmac, protocol);
+ kekKey = standardKDF.computeCardKeyOnToken(masterKey, KDCkek, protocol);
+ }
+
+ if (encKey == null || macKey == null || kekKey == null) {
+ throw new EBaseException(method
+ + " Can't derive session keys with selected KDF. For new key version.");
+ }
+
+ }
+
+ } else if (protocol == PROTOCOL_TWO) {
+ throw new EBaseException(method + " SCP 02 not yet supported here.");
+ } else {
+ throw new EBaseException(method + " Unsupported protocol verison.");
+ }
+
+ boolean showKeysForDebug = false;
+
+ if (showKeysForDebug == true) {
+ try {
+ SecureChannelProtocol.debugByteArray(encKey.getKeyData(), "DiversifyKey: new encKey: ");
+ SecureChannelProtocol.debugByteArray(macKey.getKeyData(), "DiversifyKey: new macKey:");
+ SecureChannelProtocol.debugByteArray(kekKey.getKeyData(), "DiversifyKey: new kekKey");
+ } catch (NotExtractableException e) {
+ CMS.debug(method + " Can not display debugging info for key");
+ }
+ }
+
+ if (old_kek_sym_key != null) {
+
+ CMS.debug(method + " old kek sym key is not null");
+ output = createKeySetDataWithSymKeys(newKeyVersion, (byte[]) null,
+ old_kek_sym_key,
+ encKey,
+ macKey,
+ kekKey,
+ protocol, tokenName);
+
+ } else {
+
+ CMS.debug(method + " old kek sym key is null");
+ old_kek_sym_key = returnDeveloperSymKey(token, SecureChannelProtocol.kekType, keySet, kekKeyArray);
+ output = createKeySetDataWithSymKeys(newKeyVersion, (byte[]) null,
+ old_kek_sym_key,
+ encKey,
+ macKey,
+ kekKey,
+ protocol, tokenName);
+
+ }
+
+ return output;
+ }
+
+ private byte[] createKeySetDataWithSymKeys(byte newKeyVersion, byte[] old_kek_key_array,
+ SymmetricKey old_kek_sym_key,
+ SymmetricKey encKey, SymmetricKey macKey, SymmetricKey kekKey, byte protocol, String tokenName)
+ throws EBaseException {
+
+ SymmetricKey wrappingKey = null;
+
+ String method = "SecureChannelProtocol.createKeySetDataWithSymKeys:";
+
+ byte alg = (byte) 0x81;
+
+ byte[] output = null;
+
+ if (encKey == null || macKey == null || kekKey == null || tokenName == null) {
+ throw new EBaseException(method + " Invalid input data!");
+ }
+
+ CryptoManager cm = null;
+ CryptoToken token = null;
+ try {
+ cm = CryptoManager.getInstance();
+ token = returnTokenByName(tokenName, cm);
+ } catch (NotInitializedException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+
+ } catch (NoSuchTokenException e) {
+ CMS.debug(method + " " + e);
+ throw new EBaseException(e);
+ }
+
+ SymmetricKey encKey16 = null;
+ SymmetricKey macKey16 = null;
+ SymmetricKey kekKey16 = null;
+
+ byte[] encrypted_enc_key = null;
+ byte[] encrypted_mac_key = null;
+ byte[] encrypted_kek_key = null;
+
+ byte[] keycheck_enc_key = null;
+ byte[] keycheck_mac_key = null;
+ byte[] keycheck_kek_key = null;
+
+ if (protocol == PROTOCOL_ONE) {
+ if (old_kek_sym_key == null) {
+ CMS.debug(method + " Using old kek key array.");
+ wrappingKey = unwrapSymKeyOnToken(token, old_kek_key_array, false);
+ } else {
+ CMS.debug(method + " Using input old key key sym key.");
+ wrappingKey = old_kek_sym_key;
+ }
+
+ alg = (byte) 0x81;
+ encKey16 = extractDes2FromDes3(encKey, tokenName);
+ macKey16 = extractDes2FromDes3(macKey, tokenName);
+ kekKey16 = extractDes2FromDes3(kekKey, tokenName);
+
+ encrypted_enc_key = this.wrapSessionKey(tokenName, encKey16, wrappingKey);
+ encrypted_mac_key = this.wrapSessionKey(tokenName, macKey16, wrappingKey);
+ encrypted_kek_key = this.wrapSessionKey(tokenName, kekKey16, wrappingKey);
+
+ keycheck_enc_key = this.computeKeyCheck(encKey, tokenName);
+ keycheck_mac_key = this.computeKeyCheck(macKey, tokenName);
+ keycheck_kek_key = this.computeKeyCheck(kekKey, tokenName);
+
+ debugByteArray(keycheck_enc_key, " Keycheck enc key: ");
+ debugByteArray(keycheck_mac_key, " Keycheck mac key: ");
+ debugByteArray(keycheck_kek_key, " KeyCheck kek key: ");
+
+ } else if (protocol == PROTOCOL_TWO) {
+ alg = (byte) 0x80;
+ throw new EBaseException(method + " SCP 02 not yet implemented!");
+ } else {
+ throw new EBaseException(method + " Invalid SCP version requested!");
+ }
+
+ // Compose the final key set data byte array
+
+ byte[] b1 = new byte[] { alg, 0x10 };
+ byte[] b2 = new byte[] { 0x3 };
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ try {
+ outputStream.write(newKeyVersion);
+ outputStream.write(b1);
+ outputStream.write(encrypted_enc_key);
+ outputStream.write(b2);
+ outputStream.write(keycheck_enc_key);
+
+ outputStream.write(b1);
+ outputStream.write(encrypted_mac_key);
+ outputStream.write(b2);
+ outputStream.write(keycheck_mac_key);
+
+ outputStream.write(b1);
+ outputStream.write(encrypted_kek_key);
+ outputStream.write(b2);
+ outputStream.write(keycheck_kek_key);
+
+ output = outputStream.toByteArray();
+
+ } catch (IOException e) {
+ throw new EBaseException(method + " Can't compose final output byte array!");
+ }
+
+ SecureChannelProtocol.debugByteArray(output, " Final output to createKeySetData: ");
+
+ return output;
+ }
+
+ private String getFullMasterKeyName(String masterKeyName)
+ {
+ if (masterKeyName == null)
+ {
+ return null;
+ }
+
+ String fullMasterKeyName = null;
+
+ fullMasterKeyName = "";
+
+ if (masterKeyName.length() > 0) {
+ fullMasterKeyName += masterKeyName;
+ }
+
+ return fullMasterKeyName;
+ }
+
+ private boolean checkForDeveloperKeySet(String keyInfo)
+ {
+ if (keyInfo == null)
+ return true;
+
+ //SCP01 or SCP02
+ if (keyInfo.equals("#01#01") || keyInfo.equals("#FF#01"))
+ return true;
+
+ //SCP02
+ if (keyInfo.equals("#01#02") || keyInfo.equals("#FF#02"))
+ return true;
+
+ return false;
+ }
+
+ public static void setDefaultPrefix(String masterkeyPrefix) {
+ if (SecureChannelProtocol.masterKeyPrefix == null) {
+ SecureChannelProtocol.masterKeyPrefix = masterkeyPrefix;
+ }
+ }
+
+ public byte[] encryptData(String selectedToken, String keyNickName, byte[] data, byte[] keyInfo,
+ byte nistSP800_108KdfOnKeyVersion, boolean nistSP800_108KdfUseCuidAsKdd, byte[] xCUID, byte[] xKDD,
+ byte[] kekKeyArray, String useSoftToken_s, String keySet) throws EBaseException {
+
+ String method = "SecureChannelProtocol.encryptData:";
+
+ CMS.debug(method + " Entering ....");
+
+ String transportKeyName = SecureChannelProtocol.getSharedSecretKeyName(null);
+
+ if (keyInfo == null || keySet == null || (keyInfo == null || keyInfo.length < 2)) {
+ throw new EBaseException(method + "Invalid input!");
+ }
+
+ if (xCUID == null || xCUID.length <= 0) {
+ throw new EBaseException(method + "CUID invalid size!");
+ }
+
+ if (xKDD == null || xKDD.length != NistSP800_108KDF.KDD_SIZE_BYTES) {
+ throw new EBaseException(method + "KDD invalid size!");
+ }
+
+ SymmetricKey kekKey = computeSessionKey_SCP01(kekType, selectedToken, keyNickName, null,
+ null, keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD,
+ kekKeyArray, useSoftToken_s, keySet, transportKeyName);
+
+ byte[] output = computeDes3EcbEncryption(kekKey, selectedToken, data);
+
+ // debugByteArray(output, " encryptData: Output: ");
+
+ return output;
+ }
+
+}
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java b/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java
new file mode 100644
index 000000000..1d61a465d
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java
@@ -0,0 +1,135 @@
+package com.netscape.cms.servlet.tks;
+
+import java.security.InvalidKeyException;
+
+import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.SymmetricKeyDeriver;
+import org.mozilla.jss.crypto.TokenException;
+
+import sun.security.pkcs11.wrapper.PKCS11Constants;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+
+public class StandardKDF extends KDF {
+ SecureChannelProtocol protocol = null;
+
+ StandardKDF(SecureChannelProtocol protocol) {
+ this.protocol = protocol;
+ }
+
+ public SymmetricKey computeCardKey(SymmetricKey masterKey, byte[] derivationData, CryptoToken token, int protocol)
+ throws EBaseException {
+
+ String method = "StandardKDF.computeCardKeys:";
+
+ SymmetricKey result = null;
+
+ CMS.debug(method + " entering ...");
+
+ if (masterKey == null || derivationData == null
+ || derivationData.length != SecureChannelProtocol.DES2_LENGTH || token == null) {
+
+ throw new EBaseException(method + " Invlalid input parameters!");
+ }
+
+ SymmetricKeyDeriver encryptDes3;
+ try {
+
+ encryptDes3 = token.getSymmetricKeyDeriver();
+
+ encryptDes3.initDerive(
+ masterKey, /* PKCS11Constants.CKM_DES3_ECB_ENCRYPT_DATA */4354L, derivationData, null,
+ PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE, 16);
+
+
+ SymmetricKey derivedKey = null;
+
+ try {
+ derivedKey = encryptDes3.derive();
+ } catch (TokenException e) {
+
+ CMS.debug(method + "Unable to derive the key with the proper mechanism!");
+ CMS.debug(method + "Now try this the old fashioned way");
+
+ byte[] encrypted = this.protocol.computeDes3EcbEncryption(masterKey, token.getName(), derivationData);
+ byte[] parityEncrypted = KDF.getDesParity(encrypted);
+ CMS.debug(method + "done computeDes3EcbEncryptiong");
+ derivedKey = this.protocol.unwrapSymKeyOnToken(token, null,
+ parityEncrypted, false);
+
+ // The key this way is aleady des3, return
+
+ return derivedKey;
+ }
+
+
+ CMS.debug(method + " derived card key first 16 :" + derivedKey);
+
+ // byte[] extracted = derivedKey.getEncoded();
+ // SecureChannelProtocol.debugByteArray(extracted, " Derived key 16.");
+
+ CMS.debug(method + " derivedKey 16: owning token: " + derivedKey.getOwningToken().getName());
+
+ long bitPosition = 0;
+
+ byte[] param = SecureChannelProtocol.longToBytes(bitPosition);
+
+ SymmetricKeyDeriver extract8 = token.getSymmetricKeyDeriver();
+ extract8.initDerive(
+ derivedKey, PKCS11Constants.CKM_EXTRACT_KEY_FROM_KEY,param,null,
+ PKCS11Constants.CKA_ENCRYPT, PKCS11Constants.CKA_DERIVE,8);
+
+
+ SymmetricKey extracted8 = extract8.derive();
+ // byte [] extracted8Bytes = extracted8.getEncoded();
+ // SecureChannelProtocol.debugByteArray(extracted8Bytes, " Derived key 8.");
+
+
+ SymmetricKeyDeriver concat = token.getSymmetricKeyDeriver();
+ concat.initDerive(
+ derivedKey,extracted8, PKCS11Constants.CKM_CONCATENATE_BASE_AND_KEY,null,null,
+ PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE,0);
+
+ result = concat.derive();
+ CMS.debug(method + " final 24 byte key: " + result);
+ // byte [] extracted24Bytes = result.getEncoded();
+ // SecureChannelProtocol.debugByteArray(extracted24Bytes, " Derived key 24.");
+
+ } catch (TokenException | InvalidKeyException e) {
+ CMS.debug(method + "Unable to derive the key with the proper mechanism!");
+ throw new EBaseException(e);
+ }
+
+ return result;
+
+ }
+
+ public SymmetricKey computeCardKeyOnSoftToken(SymmetricKey masterKey, byte[] data, int protocol)
+ throws EBaseException {
+ String method = "StandardKDF.computeCardKeys:";
+
+ CMS.debug(method + " entering...");
+
+ if (masterKey == null || data == null) {
+ throw new EBaseException(method + " Invlalid input parameters!");
+ }
+
+ CryptoToken token = this.protocol.getCryptoManger().getInternalKeyStorageToken();
+
+ return this.computeCardKey(masterKey, data, token, protocol);
+ }
+
+ public SymmetricKey computeCardKeyOnToken(SymmetricKey masterKey, byte[] data, int protocol) throws EBaseException {
+ String method = "StandardKDF.computeCardKeys:";
+
+ if (masterKey == null || data == null) {
+ throw new EBaseException(method + " Invlalid input parameters!");
+ }
+
+ CryptoToken token = masterKey.getOwningToken();
+
+ return this.computeCardKey(masterKey, data, token, protocol);
+ }
+}
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java b/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java
index 83b8bef92..00bb90594 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java
@@ -35,6 +35,7 @@ import org.mozilla.jss.CryptoManager.NotInitializedException;
import org.mozilla.jss.crypto.CryptoToken;
import org.mozilla.jss.crypto.KeyWrapAlgorithm;
import org.mozilla.jss.crypto.KeyWrapper;
+import org.mozilla.jss.crypto.SymmetricKey;
import org.mozilla.jss.crypto.X509Certificate;
import org.mozilla.jss.pkcs11.PK11SymKey;
@@ -213,7 +214,10 @@ public class TokenServlet extends CMSServlet {
}
}
- SessionKey.SetDefaultPrefix(masterKeyPrefix);
+ CMS.debug("Setting masteter keky prefix to: " + masterKeyPrefix);
+
+ SecureChannelProtocol.setDefaultPrefix(masterKeyPrefix);
+ /*SessionKey.SetDefaultPrefix(masterKeyPrefix);*/
} catch (Exception e) {
e.printStackTrace();
@@ -354,7 +358,6 @@ public class TokenServlet extends CMSServlet {
String rKDD = req.getParameter(IRemoteRequest.TOKEN_KDD);
-
String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO);
if ((rKeyInfo == null) || (rKeyInfo.equals(""))) {
@@ -561,7 +564,8 @@ public class TokenServlet extends CMSServlet {
selectedToken, keyNickName,
keyInfo,
nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
- nistSP800_108KdfUseCuidAsKdd,xCUID,xKDD, macKeyArray, sequenceCounter, derivationConstant, useSoftToken_s, keySet,
+ nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, macKeyArray, sequenceCounter, derivationConstant,
+ useSoftToken_s, keySet,
transportKeyName);
if (session_key == null) {
@@ -889,9 +893,9 @@ public class TokenServlet extends CMSServlet {
boolean serversideKeygen = false;
byte[] drm_trans_wrapped_desKey = null;
- PK11SymKey desKey = null;
+ SymmetricKey desKey = null;
// PK11SymKey kek_session_key;
- PK11SymKey kek_key;
+ SymmetricKey kek_key;
IConfigStore sconfig = CMS.getConfigStore();
boolean isCryptoValidate = true;
@@ -1092,14 +1096,15 @@ public class TokenServlet extends CMSServlet {
+ keySet + ".mac_key"));
CMS.debug("TokenServlet about to try ComputeSessionKey selectedToken="
+ selectedToken + " keyNickName=" + keyNickName);
- session_key = SessionKey.ComputeSessionKey(
- selectedToken, keyNickName, card_challenge,
- host_challenge, keyInfo,
- nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
- nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
- xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
- xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
- macKeyArray, useSoftToken_s, keySet, transportKeyName);
+
+ SecureChannelProtocol protocol = new SecureChannelProtocol();
+ SymmetricKey macKey = protocol.computeSessionKey_SCP01(SecureChannelProtocol.macType,
+ selectedToken,
+ keyNickName, card_challenge,
+ host_challenge, keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID,
+ xKDD, macKeyArray, useSoftToken_s, keySet, transportKeyName);
+
+ session_key = protocol.wrapSessionKey(selectedToken, macKey, null);
if (session_key == null) {
CMS.debug("TokenServlet:Tried ComputeSessionKey, got NULL ");
@@ -1110,14 +1115,13 @@ public class TokenServlet extends CMSServlet {
byte encKeyArray[] =
com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+ keySet + ".auth_key"));
- enc_session_key = SessionKey.ComputeEncSessionKey(
- selectedToken, keyNickName, card_challenge,
- host_challenge, keyInfo,
- nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
- nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
- xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
- xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
- encKeyArray, useSoftToken_s, keySet);
+ SymmetricKey encKey = protocol.computeSessionKey_SCP01(SecureChannelProtocol.encType,
+ selectedToken,
+ keyNickName, card_challenge, host_challenge, keyInfo, nistSP800_108KdfOnKeyVersion,
+ nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, encKeyArray, useSoftToken_s, keySet,
+ transportKeyName);
+
+ enc_session_key = protocol.wrapSessionKey(selectedToken, encKey, null);
if (enc_session_key == null) {
CMS.debug("TokenServlet:Tried ComputeEncSessionKey, got NULL ");
@@ -1140,14 +1144,11 @@ public class TokenServlet extends CMSServlet {
com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+ keySet + ".kek_key"));
- kek_key = SessionKey.ComputeKekKey(
- selectedToken, keyNickName, card_challenge,
- host_challenge, keyInfo,
- nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
- nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
- xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
- xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
- kekKeyArray, useSoftToken_s, keySet);
+ kek_key = protocol.computeKEKKey_SCP01(selectedToken,
+ keyNickName,
+ keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd,
+ xCUID,
+ xKDD, kekKeyArray, useSoftToken_s, keySet, transportKeyName);
CMS.debug("TokenServlet: called ComputeKekKey");
@@ -1177,11 +1178,12 @@ public class TokenServlet extends CMSServlet {
if (useSoftToken_s.equals("true")) {
CMS.debug("TokenServlet: key encryption key generated on internal");
//cfu audit here? sym key gen
- desKey = SessionKey.GenerateSymkey("internal");
+
+ desKey = protocol.generateSymKey("internal");
//cfu audit here? sym key gen done
} else {
CMS.debug("TokenServlet: key encryption key generated on " + selectedToken);
- desKey = SessionKey.GenerateSymkey(selectedToken);
+ desKey = protocol.generateSymKey(selectedToken);
}
if (desKey != null) {
// AC: KDF SPEC CHANGE - Output using CUID and KDD
@@ -1204,9 +1206,9 @@ public class TokenServlet extends CMSServlet {
* and discard the last 8 bytes before it encrypts.
* This is done so that the applet can digest it
*/
- byte[] encDesKey =
- SessionKey.ECBencrypt(kek_key,
- desKey);
+
+ byte[] encDesKey = protocol.ecbEncrypt(kek_key, desKey, selectedToken);
+
/*
CMS.debug("computeSessionKey:encrypted desKey size = "+encDesKey.length);
CMS.debug(encDesKey);
@@ -1216,8 +1218,8 @@ public class TokenServlet extends CMSServlet {
com.netscape.cmsutil.util.Utils.SpecialEncode(encDesKey);
// get keycheck
- byte[] keycheck =
- SessionKey.ComputeKeyCheck(desKey);
+
+ byte[] keycheck = protocol.computeKeyCheck(desKey, selectedToken);
/*
CMS.debug("computeSessionKey:keycheck size = "+keycheck.length);
CMS.debug(keycheck);
@@ -1266,28 +1268,21 @@ public class TokenServlet extends CMSServlet {
byte authKeyArray[] =
com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+ keySet + ".auth_key"));
- host_cryptogram = SessionKey.ComputeCryptogram(
- selectedToken, keyNickName, card_challenge,
- host_challenge, keyInfo,
- nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
- nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
- xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
- xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
- 0, authKeyArray, useSoftToken_s, keySet);
+
+ host_cryptogram = protocol.computeCryptogram_SCP01(selectedToken, keyNickName, card_challenge,
+ host_challenge,
+ xkeyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, SecureChannelProtocol.HOST_CRYPTOGRAM,
+ authKeyArray, useSoftToken_s, keySet, transportKeyName);
if (host_cryptogram == null) {
CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL ");
throw new Exception("Can't compute host cryptogram!");
}
- card_crypto = SessionKey.ComputeCryptogram(
- selectedToken, keyNickName, card_challenge,
- host_challenge, keyInfo,
- nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
- nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
- xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
- xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
- 1, authKeyArray, useSoftToken_s, keySet);
+
+ card_crypto = protocol.computeCryptogram_SCP01(selectedToken, keyNickName, card_challenge,
+ host_challenge, xkeyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd,
+ xCUID, xKDD, SecureChannelProtocol.CARD_CRYPTOGRAM, authKeyArray, useSoftToken_s, keySet, transportKeyName);
if (card_crypto == null) {
CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL ");
@@ -1302,6 +1297,10 @@ public class TokenServlet extends CMSServlet {
}
input_card_crypto =
com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_cryptogram);
+
+ SecureChannelProtocol.debugByteArray(input_card_crypto, "input_card_crypto");
+ SecureChannelProtocol.debugByteArray(card_crypto, "card_crypto");
+
if (card_crypto.length == input_card_crypto.length) {
for (int i = 0; i < card_crypto.length; i++) {
if (card_crypto[i] != input_card_crypto[i]) {
@@ -1782,18 +1781,28 @@ public class TokenServlet extends CMSServlet {
byte kekKeyArray[] =
com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key"));
+ SecureChannelProtocol secProtocol = new SecureChannelProtocol();
// AC: KDF SPEC CHANGE - check for error reading settings
if (missingSetting_exception == null) {
- KeySetData = SessionKey.DiversifyKey(oldSelectedToken,
- newSelectedToken, oldKeyNickName,
- newKeyNickName,
- xkeyInfo, // AC: KDF SPEC CHANGE - pass in old key info so symkey can make decision about which KDF version to use
- xnewkeyInfo, // AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be byte[]
- nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
- nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
- xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
- xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
- (protocol == 2) ? xWrappedDekKey : kekKeyArray, useSoftToken_s, keySet, (byte) protocol);
+ if (protocol == 1) {
+ KeySetData = secProtocol.diversifyKey(oldSelectedToken,
+ newSelectedToken, oldKeyNickName,
+ newKeyNickName,
+ xkeyInfo, // AC: KDF SPEC CHANGE - pass in old key info so symkey can make decision about which KDF version to use
+ xnewkeyInfo, // AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be byte[]
+ nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+ nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+ xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+ xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+ (protocol == 2) ? xWrappedDekKey : kekKeyArray, useSoftToken_s, keySet, (byte) protocol);
+
+ } else if (protocol == 2) {
+ KeySetData = SessionKey.DiversifyKey(oldSelectedToken, newSelectedToken, oldKeyNickName,
+ newKeyNickName, xkeyInfo,
+ xnewkeyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD,
+ (protocol == 2) ? xWrappedDekKey : kekKeyArray, useSoftToken_s, keySet, (byte) protocol);
+ }
+ SecureChannelProtocol.debugByteArray(KeySetData, " New keyset data: ");
if (KeySetData == null || KeySetData.length <= 1) {
CMS.getLogger().log(ILogger.EV_AUDIT,
@@ -1817,7 +1826,6 @@ public class TokenServlet extends CMSServlet {
} // ! missingParam
-
String value = "";
String status = "0";
@@ -2092,7 +2100,9 @@ public class TokenServlet extends CMSServlet {
// AC: KDF SPEC CHANGE - check for error reading settings
if (missingSetting_exception == null) {
- encryptedData = SessionKey.EncryptData(
+ SecureChannelProtocol protocol = new SecureChannelProtocol();
+
+ encryptedData = protocol.encryptData(
selectedToken, keyNickName, data, keyInfo,
nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
@@ -2100,6 +2110,8 @@ public class TokenServlet extends CMSServlet {
xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
kekKeyArray, useSoftToken_s, keySet);
+ SecureChannelProtocol.debugByteArray(encryptedData, "New Encrypt Data: ");
+
// AC: KDF SPEC CHANGE - Log both CUID and KDD
CMS.getLogger().log(ILogger.EV_AUDIT,