From d13181faea23cdb5a07136d3fdabeedb70effda9 Mon Sep 17 00:00:00 2001 From: Ade Lee Date: Tue, 28 Feb 2017 12:18:29 -0500 Subject: Change internal wrapping to AES There are several changes in this patch: 1. Simplify EncryptionUnit by moving the methods called by either the StorageUnit or the TransportUnit into those classes. This helps to determine which methods are called by which class (because in general they require different arguments). It may be possible to later simplify and reduce code repetition by pulling core functionality back into the EncryptionUnit. 2. Add methods to WrappingParameters and KeyRecord to store the Wrapping Parameter values as part of the KeyRecord when the key is stored. On retrieval, this data is read and used to extract the data. If the data is not present, then use the old DES3 parameters. 3. Change the internal (storageUnit) wrapping to use AES-CBC for encryption and AES-KeyWrap for storage by default. If a parameter kra.storageUnit.useOldWrapping=true, then the old wrapping will be used instead. Change-Id: I098b0b3bd3b0ad917483e4e07925adfedacc3562 --- .../src/com/netscape/cms/servlet/key/KeyRecordParser.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'base/server/cms/src') diff --git a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java index 256f72879..c1711c240 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java +++ b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java @@ -46,6 +46,18 @@ public class KeyRecordParser { public final static String OUT_RECOVERED_BY = "recoveredBy"; public final static String OUT_RECOVERED_ON = "recoveredOn"; + /* parameters to populate WrappingParams */ + public final static String OUT_SK_TYPE = "sessionKeyType"; + public final static String OUT_SK_KEYGEN_ALGORITHM = "sessionKeyKeyGenAlgorithm"; + public final static String OUT_SK_LENGTH = "sessionKeyLength"; + public final static String OUT_SK_WRAP_ALGORITHM = "sessionKeyWrapAlgorithm"; + public final static String OUT_PL_WRAP_ALGORITHM = "payloadWrapAlgorithm"; + public final static String OUT_PL_WRAP_IV = "payloadWrapIV"; + public final static String OUT_PL_ENCRYPTION_ALGORITHM = "payloadEncryptionAlgorithm"; + public final static String OUT_PL_ENCRYPTION_MODE = "payloadEncryptionMode"; + public final static String OUT_PL_ENCRYPTION_PADDING = "payloadEncryptionPadding"; + public final static String OUT_PL_ENCRYPTION_IV = "payloadEncryptionIV"; + /** * Fills key record into argument block. */ -- cgit From 49177c6911a5d6d97816f43c5c9568de56af8eff Mon Sep 17 00:00:00 2001 From: Ade Lee Date: Wed, 8 Mar 2017 10:34:31 -0500 Subject: Change transport unit to create wrapping parameters based on incoming data The PKIArchiveOptions object contains an OID for the encryption algorithm. Use this to create the correct WrappingParam for the tranport unit instead of defaulting to DES3. Change-Id: Id591fff8b7fc5e4506afbe619621904e4937c44f --- base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java | 1 + 1 file changed, 1 insertion(+) (limited to 'base/server/cms/src') diff --git a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java index c1711c240..c471a2869 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java +++ b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRecordParser.java @@ -57,6 +57,7 @@ public class KeyRecordParser { public final static String OUT_PL_ENCRYPTION_MODE = "payloadEncryptionMode"; public final static String OUT_PL_ENCRYPTION_PADDING = "payloadEncryptionPadding"; public final static String OUT_PL_ENCRYPTION_IV = "payloadEncryptionIV"; + public final static String OUT_PL_ENCRYPTION_OID = "payloadEncryptionOID"; /** * Fills key record into argument block. -- cgit From 648361bac96996e76339b9390b8a8882dcde8ad7 Mon Sep 17 00:00:00 2001 From: Ade Lee Date: Thu, 9 Mar 2017 12:54:57 -0500 Subject: Continue to move more crypto into CryptoUtil Change-Id: I6024ca5a32769b460d578dfad46598432381784c --- .../cms/servlet/csadmin/ConfigurationUtils.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'base/server/cms/src') diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java index 0f3153d3d..ed2423ff1 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java +++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java @@ -845,11 +845,7 @@ public class ConfigurationUtils { return false; } - public static void restoreCertsFromP12(String p12File, String p12Pass) throws EPropertyNotFound, EBaseException, - InvalidKeyException, CertificateException, NoSuchAlgorithmException, - InvalidAlgorithmParameterException, IllegalStateException, TokenException, IllegalBlockSizeException, - BadPaddingException, NotInitializedException, NicknameConflictException, UserCertConflictException, - NoSuchItemOnTokenException, InvalidBERException, IOException { + public static void restoreCertsFromP12(String p12File, String p12Pass) throws Exception { // TODO: The PKCS #12 file is already imported in security_database.py. // This method should be removed. @@ -1018,11 +1014,7 @@ public class ConfigurationUtils { public static void importKeyCert( Vector> pkeyinfo_collection, Vector> cert_collection - ) throws IOException, CertificateException, TokenException, - NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, - IllegalStateException, - IllegalBlockSizeException, BadPaddingException, NotInitializedException, NicknameConflictException, - UserCertConflictException, NoSuchItemOnTokenException, EPropertyNotFound, EBaseException { + ) throws Exception { CMS.debug("ConfigurationUtils.importKeyCert()"); CryptoManager cm = CryptoManager.getInstance(); @@ -1072,13 +1064,10 @@ public class ConfigurationUtils { } // encrypt private key - KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3); - SymmetricKey sk = kg.generate(); + SymmetricKey sk = CryptoUtil.generateKey(token, KeyGenAlgorithm.DES3, 0, null, true); byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; IVParameterSpec param = new IVParameterSpec(iv); - Cipher c = token.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); - c.initEncrypt(sk, param); - byte[] encpkey = c.doFinal(pkey); + byte[] encpkey = CryptoUtil.encryptUsingSymmetricKey(token, sk, pkey, EncryptionAlgorithm.DES3_CBC_PAD, param); // unwrap private key to load into database KeyWrapper wrapper = token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); -- cgit From 6d6b6f954a5bf6730d4b53875c7cc122eb3ab5eb Mon Sep 17 00:00:00 2001 From: Jack Magne Date: Wed, 1 Jun 2016 10:23:33 -0700 Subject: First cut of scp03 support. Supports the g&d smartcafe out of the box. Developer keyset token operations and key change over supported. Caveats. -The diversification step going from master key to card key uses DES3 as required for the token. -After that point, everything is scp03 to the spec with minor excpetions so far. Supports 128 bit AES for now. Will resolve this. Minor config tweaks: TPS Symmetric Key Changeover Use this applet for scp03: RSA/KeyRecovery/GP211/SCP02/SCP03 applet : 1.5.558cdcff.ijc TKS: Symmetric Key Changeover tks.mk_mappings.#02#03=internal:new_master tks.defKeySet.mk_mappings.#02#03=internal:new_master Use the uncommented one because scp03 returns a different key set data string. ToDo: -Support the rest of the AES sizes other than 128. -Support optional RMAC apdu. -Test and adjust the config capability for other tokens. -Support AES master key. Right now the standard key ends up creating AES card and session keys. --- .../netscape/cms/authentication/SharedSecret.java | 4 + .../src/com/netscape/cms/servlet/tks/GPParams.java | 108 +++ .../cms/src/com/netscape/cms/servlet/tks/KDF.java | 59 +- .../netscape/cms/servlet/tks/NistSP800_108KDF.java | 426 ++++++++++- .../cms/servlet/tks/SecureChannelProtocol.java | 785 ++++++++++++++++++-- .../com/netscape/cms/servlet/tks/StandardKDF.java | 41 +- .../com/netscape/cms/servlet/tks/TokenServlet.java | 800 ++++++++++++++++++++- .../dogtagpki/server/connector/IRemoteRequest.java | 2 + 8 files changed, 2131 insertions(+), 94 deletions(-) create mode 100644 base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java (limited to 'base/server/cms/src') diff --git a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java index 7a0784c53..48a23536a 100644 --- a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java +++ b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java @@ -35,4 +35,8 @@ public class SharedSecret implements ISharedToken { public String getSharedToken(BigInteger serial) { return "testing"; } + + public String getSharedToken(String identification) { + return "testing"; + } } diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java b/base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java new file mode 100644 index 000000000..f16481be5 --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java @@ -0,0 +1,108 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.servlet.tks; + + +//Simple class used to hold scp03 related settings in TKS keyset config block +// Ex: tks.defKeySet.prot3.divers=emv +// tks.defKeySet.prot3.diversVer1Keys=emv + +// Will probably be extended to allow params for future tokens + +public class GPParams { + + public static String DIVER_EMV = "emv"; + public static String DIVER_NONE = "none"; + public static String DIVER_VISA2 = "visa2"; + public static String NIST_SP800 = "nistsp_800"; + + public GPParams() { + } + + // Diversification scheme for all keysets after 1 + private String diversificationScheme; + //Diversification scheme for just version one or developer keys + private String version1DiversificationScheme; + + public boolean isDiversEmv() { + if (DIVER_EMV.equalsIgnoreCase(diversificationScheme)) + return true; + else + return false; + } + + public boolean isDiversVisa2() { + if (DIVER_VISA2.equalsIgnoreCase(diversificationScheme)) + return true; + else + return false; + } + + public boolean isDiversNone() { + if (DIVER_NONE.equalsIgnoreCase(diversificationScheme)) + return true; + else + return false; + } + + public boolean isVer1DiversEmv() { + if (DIVER_EMV.equalsIgnoreCase(version1DiversificationScheme)) + return true; + else + return false; + } + + public boolean isVer1DiversVisa2() { + if (DIVER_VISA2.equalsIgnoreCase(version1DiversificationScheme)) + return true; + else + return false; + + } + + public boolean isVer1DiversNone() { + if (DIVER_NONE.equalsIgnoreCase(version1DiversificationScheme)) + return true; + else + return false; + } + + public void setDiversificationScheme(String scheme) { + diversificationScheme = scheme; + } + + public String getDiversificationScheme() { + return diversificationScheme; + } + + public String getVersion1DiversificationScheme() { + return version1DiversificationScheme; + } + + public void setVersion1DiversificationScheme(String version1DiversificationScheme) { + this.version1DiversificationScheme = version1DiversificationScheme; + } + + public String toString() { + String output = " Version1 Diversification Scheme: " + version1DiversificationScheme + " All other versions : " + + diversificationScheme; + + return output; + } + +} 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 index 0407e2934..e7b183b4e 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java @@ -41,9 +41,64 @@ public class KDF { /* 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 { + //Add the emv diversification method, used in SCP03 g&d card. + public static byte[] getDiversificationData_EMV(byte[] context, String type) throws EBaseException { - String method = "KDF.getDiversificationData:"; + String method = "KDF.getDiversificationData_EMV:"; + + CMS.debug(method + " entering ..."); + + if (context == null || type == null) { + throw new EBaseException(method + "Invalid input parameters!"); + } + + byte[] KDC = new byte[SecureChannelProtocol.DES2_LENGTH]; + + KDC[0] = context[4 + 0]; + KDC[1] = context[4 + 1]; + KDC[2] = context[4 + 2]; + KDC[3] = context[4 + 3]; + KDC[4] = context[4 + 4]; + KDC[5] = context[4 + 5]; + KDC[6] = (byte) 0xF0; + + KDC[7] = 0x1; + + KDC[8] = context[4 + 0]; + KDC[9] = context[4 + 1]; + KDC[10] = context[4 + 2]; + KDC[11] = context[4 +3]; + KDC[12] = context[4 + 4]; + KDC[13] = context[4 + 5]; + KDC[14] = (byte) 0x0f; + + KDC[15] = 0x1; + + if (type.equals(SecureChannelProtocol.encType)) + return KDC; + + KDC[7] = 0x02; + KDC[15] = 0x02; + if (type.equals(SecureChannelProtocol.macType)) + return KDC; + + KDC[7] = 0x03; + KDC[15] = 0x03; + if (type.equals(SecureChannelProtocol.kekType)) + return KDC; + + KDC[7] = 0x04; + KDC[15] = 0x04; + if (type.equals(SecureChannelProtocol.rmacType)) + return KDC; + return KDC; + + } + + //Standard visa2 diversification method + public static byte[] getDiversificationData_VISA2(byte[] context, String type) throws EBaseException { + + String method = "KDF.getDiversificationData_VISA2:"; CMS.debug(method + " entering ..."); 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 index e392ce1a3..ad4a370c2 100644 --- 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 @@ -1,13 +1,24 @@ package com.netscape.cms.servlet.tks; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +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.HMACAlgorithm; +import org.mozilla.jss.crypto.IVParameterSpec; +import org.mozilla.jss.crypto.IllegalBlockSizeException; import org.mozilla.jss.crypto.JSSMessageDigest; import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.TokenException; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.EBaseException; @@ -22,7 +33,18 @@ public class NistSP800_108KDF extends KDF { static final byte KDF_LABEL = 0x04; // arbitra - static final int SHA256_LENGTH = 32; + //SCP03, AES related constants + + public static final int SHA256_LENGTH = 32; + private static final int AES_CMAC_BLOCK_SIZE = 16; + private static final byte AES_CMAC_CONSTANT = (byte) 0x87; + public static final byte ENC_KDF_CONSTANT = (byte) 0x04; + public static final byte MAC_KDF_CONSTANT = (byte) 0x06; + public static final byte RMAC_KDF_CONSTANT = (byte) 0x07; + public static final byte CARD_CRYPTO_KDF_CONSTANT = 0x0; + public static final byte HOST_CRYPTO_KDF_CONSTANT = 0x1; + + SecureChannelProtocol protocol = null; @@ -57,7 +79,7 @@ public class NistSP800_108KDF extends KDF { String method = "NistSP800_108KDF.computeCardKeys:"; if (masterKey == null || context == null || token == null) { - throw new EBaseException(method + " Invlalid input parameters!"); + throw new EBaseException(method + " Invalid input parameters!"); } Map keys = new HashMap(); @@ -101,9 +123,9 @@ public class NistSP800_108KDF extends KDF { 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); + SymmetricKey macKey = protocol.unwrapSymKeyOnToken(token, null, macFinal, false,SymmetricKey.DES3); + SymmetricKey encKey = protocol.unwrapSymKeyOnToken(token, null, encFinal, false,SymmetricKey.DES3); + SymmetricKey kekKey = protocol.unwrapSymKeyOnToken(token, null, kekFinal, false,SymmetricKey.DES3); Arrays.fill(encFinal, (byte) 0); Arrays.fill(macFinal, (byte) 0); @@ -117,6 +139,79 @@ public class NistSP800_108KDF extends KDF { } + //Compute the AES based CMAC operation. Used to derive session keys and cryptograms + public byte[] kdf_AES_CMAC_SCP03(SymmetricKey masterKey, byte[] context, byte kdfConstant, + int kdfOutputSizeBytes) throws EBaseException { + + String method = "NistSP800_108KDF.kdf_AES_CMAC_SCP03:"; + // 11 bytes label + byte[] label = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // sanity checking + + if (masterKey == null || context == null || kdfOutputSizeBytes <= 0) { + throw new EBaseException(method + " Invalid input!"); + } + + ByteArrayOutputStream data = new ByteArrayOutputStream(); + + int outputBits = kdfOutputSizeBytes * 8; + + //output size of cmac PRF + final int h = 128; + + int remainder = outputBits % h; + + //calculate counter size + int n = 0; + if (remainder == 0) { + n = outputBits / h; + } else { + n = outputBits / h + 1; + } + + byte b1 = (byte) ((outputBits >> 8) & 0xFF); + byte b2 = (byte) (outputBits & 0xFF); + + byte[] outputBitsBinary = new byte[2]; + outputBitsBinary[0] = b1; + outputBitsBinary[1] = b2; + + try { + data.write(label); + data.write(kdfConstant); + data.write(0x0); + data.write(outputBitsBinary); + } catch (IOException e) { + throw new EBaseException(method + "Unable to calculate kdf!"); + } + + byte[] headerBytes = data.toByteArray(); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + ByteArrayOutputStream input = new ByteArrayOutputStream(); + + byte[] kI = null; + for (int i = 1; i <= n; i++) { + + try { + input.write(headerBytes); + input.write((byte) i); + input.write(context); + + kI = computeAES_CMAC(masterKey, input.toByteArray()); + + output.write(kI); + + } catch (IOException e) { + throw new EBaseException(method + "Unable to calculate kdf!"); + } + + } + + return output.toByteArray(); + } + /******************************************************************************* 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) @@ -132,7 +227,6 @@ public class NistSP800_108KDF extends KDF { 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) { @@ -215,4 +309,324 @@ public class NistSP800_108KDF extends KDF { return digestBytes; } + // Implements agorithm http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38b.pdf + // Input an aes key of 128, 192, or 256 bits + // For now calling code only using 128 + // Will move later to common class used by both tks and tps + + public static byte[] computeAES_CMAC(SymmetricKey aesKey, byte[] input) throws EBaseException { + + String method = "NistSP800_108KDF.computeAES_CMAC:"; + byte iv[] = null; + + if (aesKey == null || input == null) { + throw new EBaseException(method + " invalid input data!"); + } + + byte[] data = new byte[input.length]; + System.arraycopy(input, 0, data, 0, input.length); + + String alg = aesKey.getAlgorithm(); + System.out.println(" AES ALG: " + alg); + + EncryptionAlgorithm eAlg = EncryptionAlgorithm.AES_128_CBC; + int ivLength = eAlg.getIVLength(); + + if (ivLength > 0) { + iv = new byte[ivLength]; + } + + if (!("AES".equals(alg))) { + throw new EBaseException(method + " invalid in put key type , must be AES!"); + } + + byte[] k0 = new byte[AES_CMAC_BLOCK_SIZE]; + + //Encrypt the zero array + CryptoToken token = aesKey.getOwningToken(); + Cipher encryptor = null; + + try { + encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC); + encryptor.initEncrypt(aesKey, new IVParameterSpec(iv)); + k0 = encryptor.doFinal(k0); + + } catch (NoSuchAlgorithmException | TokenException | IllegalStateException | IllegalBlockSizeException + | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new EBaseException(e); + } + + byte[] k1 = getAES_CMAC_SubKey(k0); + byte[] k2 = getAES_CMAC_SubKey(k1); + + int numBlocks = 0; + int messageSize = data.length;; + boolean perfectBlocks = false; + + if (((messageSize % AES_CMAC_BLOCK_SIZE) == 0) && (messageSize != 0)) { + numBlocks = messageSize / AES_CMAC_BLOCK_SIZE; + perfectBlocks = true; + } + else { + numBlocks = messageSize / AES_CMAC_BLOCK_SIZE + 1; + perfectBlocks = false; + } + + int index = 0; + byte inb = 0; + + byte[] finalData = null; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream( ); + + if (perfectBlocks == true) + { + // If the size of the message is an integer multiple of the block block size (namely, 128 bits) (16 bytes) + // the last block shall be exclusive-OR'ed with the first subKey k1 + + for (int j = 0; j < k1.length; j++) { + index = messageSize - AES_CMAC_BLOCK_SIZE + j; + inb = data[index]; + data[index] = (byte) (inb ^ k1[j]); + } + try { + outputStream.write(data); + } catch (IOException e) { + throw new EBaseException(method + " internal buffer erro!"); + } + finalData = outputStream.toByteArray(); + } + else + { + // Otherwise, the last block shall be padded with 10^i + byte[] padding = new byte[AES_CMAC_BLOCK_SIZE - messageSize % AES_CMAC_BLOCK_SIZE]; + padding[0] = (byte) 0x80; + + try { + outputStream.write(data); + outputStream.write(padding); + } catch (IOException e) { + throw new EBaseException(method + " internal buffer error!"); + } + + finalData = outputStream.toByteArray(); + + //Get new data size , it's changed + messageSize = finalData.length; + + // and exclusive-OR'ed with K2 + for (int j = 0; j < k2.length; j++) { + index = messageSize - AES_CMAC_BLOCK_SIZE + j; + inb = finalData[index]; + finalData[index] = (byte) (inb ^ k2[j]); + } + } + + // Initialization vector starts as zeroes but changes inside the loop's + // subsequent iterations, it becomes the last encryption output + byte[] encData = new byte[AES_CMAC_BLOCK_SIZE]; + byte[] currentBlock = new byte[AES_CMAC_BLOCK_SIZE]; + for (int i = 0; i < numBlocks; i++) { + try { + encryptor.initEncrypt(aesKey, new IVParameterSpec(encData)); + System.arraycopy(finalData, i * AES_CMAC_BLOCK_SIZE, currentBlock, 0, AES_CMAC_BLOCK_SIZE); + encData = encryptor.doFinal(currentBlock); + } catch (TokenException | IllegalStateException | IllegalBlockSizeException + | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new EBaseException(e); + } + } + + return encData; + + } + + // SCP03 AES-CMAC support function + private static byte[] getAES_CMAC_SubKey(byte[] input) { + + byte[] output = new byte[input.length]; + + boolean msbSet = ((input[0]&0x80) != 0); + for (int i=0; i 0) { + iv = new byte[ivLength]; // all zeroes + } + + encryptor.initEncrypt(tempKey, new IVParameterSpec(iv)); + byte[] wrappedKey = encryptor.doFinal(devKey); + + KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC); + keyWrap.initUnwrap(tempKey, new IVParameterSpec(iv)); + + encKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 16); + macKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 16); + kekKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 16); + + String transportName = "TPS-dhcp-16-206.sjc.redhat.com-8443 sharedSecret"; + SecureChannelProtocol prot = new SecureChannelProtocol(SecureChannelProtocol.PROTOCOL_THREE); + + SymmetricKey masterKey = SecureChannelProtocol.getSymKeyByName(token,"new_master"); + + GPParams params = new GPParams(); + params.setVersion1DiversificationScheme("visa2"); + params.setDiversificationScheme("visa2"); + + putEncKey = prot.computeSessionKey_SCP03("internal", "new_master",test_old_key_info, + SecureChannelProtocol.encType, devKey, "defKeySet", test_cuid, test_kdd, null, null, + transportName,params); + + putMacKey = prot.computeSessionKey_SCP03("internal", "new_master",test_old_key_info, + SecureChannelProtocol.macType, devKey, "defKeySet", test_cuid, test_kdd, null, null, + transportName,params); + + putKekKey = prot.computeSessionKey_SCP03("internal", "new_master",test_old_key_info, + SecureChannelProtocol.kekType, devKey, "defKeySet", test_cuid, test_kdd, null, null, + transportName,params); + + //create test session keys + encKey = prot.computeSessionKey_SCP03("internal", "new_master",test_key_info, + SecureChannelProtocol.encType, devKey, "defKeySet", test_cuid, test_kdd, test_host_challenge, test_card_challenge, + transportName,params); + + macKey = prot.computeSessionKey_SCP03("internal", "new_master",test_key_info, + SecureChannelProtocol.macType,devKey,"defKeySet", test_cuid, test_kdd, test_host_challenge, test_card_challenge, + transportName,params); + + kekKey = prot.computeSessionKey_SCP03("internal", "new_master",test_key_info, + SecureChannelProtocol.kekType, devKey, "defKeySet", test_cuid, test_kdd, test_host_challenge, test_card_challenge, + transportName,params); + + System.out.println("masterKey: " + masterKey); + + System.out.println("\n"); + + SecureChannelProtocol.debugByteArray(putEncKey.getKeyData(), " derived putEnc session key data: "); + SecureChannelProtocol.debugByteArray(putMacKey.getKeyData(), " derived putMac session key data: "); + SecureChannelProtocol.debugByteArray(putKekKey.getKeyData(), " derived putKek session key data: "); + + System.out.println("\n"); + + SecureChannelProtocol.debugByteArray(encKey.getKeyData(), " derived enc session key data: "); + SecureChannelProtocol.debugByteArray(macKey.getKeyData(), " derived mac session key data: "); + SecureChannelProtocol.debugByteArray(kekKey.getKeyData(), " derived kek session key data: "); + + ByteArrayOutputStream contextStream = new ByteArrayOutputStream(); + try { + contextStream.write(test_host_challenge); + contextStream.write(test_card_challenge); + } catch (IOException e) { + } + + StandardKDF standard = new StandardKDF(prot); + + ByteArrayOutputStream testContext = new ByteArrayOutputStream(); + + testContext.write(test_host_challenge); + testContext.write(test_card_challenge); + + NistSP800_108KDF nistKdf = new NistSP800_108KDF(prot); + + byte[] finalEncBytes = nistKdf.kdf_AES_CMAC_SCP03(encKey, testContext.toByteArray(), (byte) 0x04, 16); + byte[] finalMacBytes = nistKdf.kdf_AES_CMAC_SCP03(macKey, testContext.toByteArray(), (byte) 0x06, 16); + + SymmetricKey sEnc = prot.unwrapAESSymKeyOnToken(token, finalEncBytes, false); + SymmetricKey sMac = macKey = prot.unwrapAESSymKeyOnToken(token, finalMacBytes, false); + + byte[] cardCryptoVerify = nistKdf.kdf_AES_CMAC_SCP03(sMac, testContext.toByteArray(), CARD_CRYPTO_KDF_CONSTANT, 8); + SecureChannelProtocol.debugByteArray(cardCryptoVerify, " calculated card cryptogram"); + + byte[] hostCrypto = nistKdf.kdf_AES_CMAC_SCP03(sMac, testContext.toByteArray(), HOST_CRYPTO_KDF_CONSTANT, 8); + SecureChannelProtocol.debugByteArray(hostCrypto, " calculated host cryptogram"); + + } catch (AlreadyInitializedException e) { + // it is ok if it is already initialized + } catch (Exception e) { + System.err.println("JSS error!" + e); + System.exit(1); + } +*/ + } } 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 index 1766f0459..371e734df 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java @@ -15,6 +15,7 @@ import org.mozilla.jss.NoSuchTokenException; import org.mozilla.jss.crypto.Cipher; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.EncryptionAlgorithm; +import org.mozilla.jss.crypto.IVParameterSpec; import org.mozilla.jss.crypto.KeyGenAlgorithm; import org.mozilla.jss.crypto.KeyGenerator; import org.mozilla.jss.crypto.KeyWrapAlgorithm; @@ -24,12 +25,12 @@ 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; import com.netscape.cmsutil.crypto.CryptoUtil; -import sun.security.pkcs11.wrapper.PKCS11Constants; - public class SecureChannelProtocol { static String sharedSecretKeyName = null; @@ -43,12 +44,14 @@ public class SecureChannelProtocol { static final int KEYNAMELENGTH = PREFIXLENGHT + 7; static final String TRANSPORT_KEY_NAME = "sharedSecret"; static final String DEFKEYSET_NAME = "defKeySet"; + static int protocol = 1; 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 String rmacType = "rmac"; static final int PROTOCOL_ONE = 1; static final int PROTOCOL_TWO = 2; static final int PROTOCOL_THREE = 3; @@ -57,12 +60,26 @@ public class SecureChannelProtocol { //Size of long type in bytes, since java7 has no define for this static final int LONG_SIZE = 8; + // constants + + static final int AES_128_BYTES = 16; + static final int AES_192_BYTES = 24; + static final int AES_256_BYTES = 32; + + static final int AES_128_BITS = 128; + static final int AES_192_BITS = 192; + static final int AES_256_BITS = 256; + private SymmetricKey transportKey = null; CryptoManager cryptoManager = null; public SecureChannelProtocol() { } + public SecureChannelProtocol(int theProtocol) { + protocol = theProtocol; + } + public byte[] computeCryptogram_SCP01( String selectedToken, String keyNickName, byte[] card_challenge, byte[] host_challenge, byte[] keyInfo, @@ -144,6 +161,222 @@ public class SecureChannelProtocol { throw new EBaseException(method + " Not yet implemented!"); } + public int getProtocol() { + return protocol; + } + + // Either calculate a full session key, with the KDF applied or + // Merely calculate the card key. Card key mode is when host_challenge and + // card_challenge are passed in as null. Card keys are calculated + // when creating a new keyset to send to the token + public SymmetricKey computeSessionKey_SCP03(String selectedToken, + String keyNickName, byte[] keyInfo, String keyType, + byte[] devKeyArray, String keySet, byte[] xCUID, byte[] xKDD, + byte[] host_challenge, byte[] card_challenge, String transportKeyName, GPParams params) + throws EBaseException { + + final byte mac_constant = 0x06; + final byte enc_constant = 0x04; + final byte rmac_constant = 0x07; + + boolean noDerive = false; + + byte constant = 0; + + String method = "SecureChannelProtocol.computeSessionKey_SCP03:"; + + if (keyType == null || devKeyArray == null + || transportKeyName == null) { + throw new EBaseException(method + " invalid input data"); + } + + 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!"); + } + + + //Detect card key mode or full derivation mode + if (card_challenge == null && host_challenge == null) { + noDerive = true; + } else { + if (card_challenge == null || host_challenge == null) { + throw new EBaseException(method + " Invalid challenge data!"); + } + } + + CMS.debug(method + " entering. nickname: " + keyNickName + " selectedToken: " + selectedToken); + + 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); + + //concat host and card challenge: + + byte[] context = null; + + ByteArrayOutputStream contextStream = new ByteArrayOutputStream(); + + // Full derivation mode create context used in derivation + // host_challenge + card_challenge concatenated + if (noDerive == false) { + try { + contextStream.write(host_challenge); + contextStream.write(card_challenge); + } catch (IOException e) { + throw new EBaseException(method + " Error calculating derivation data!"); + } + + context = contextStream.toByteArray(); + } + + //Calculate the constant based on what type of key we want. + // Note the kek key never goes through final derivation in scp03 + + if (keyType.equalsIgnoreCase(SecureChannelProtocol.encType)) { + constant = enc_constant; + } + + if (keyType.equalsIgnoreCase(SecureChannelProtocol.macType)) { + constant = mac_constant; + } + + if (keyType.equalsIgnoreCase(SecureChannelProtocol.rmacType)) { + constant = rmac_constant; + } + + if (keyType.equalsIgnoreCase(SecureChannelProtocol.kekType)) { + constant = 0; + } + + String keyNameStr = null; + + SymmetricKey sessionKey = null; + SymmetricKey masterKey = null; + + if (keyNickName == null) { + keyNameStr = this.getKeyName(keyInfo); + } else { + keyNameStr = keyNickName; + } + + boolean noDivers = false; + + CMS.debug(method + " keyNameStr: " + keyNameStr); + + //Starting with version 1 or factory keyset. + if ((keyInfo[0] == 0x1 && keyNameStr.contains("#01#")) || + (keyInfo[0] == -1 && keyNameStr.indexOf("#FF") != -1)) + + { + String finalKeyType = keyType; + SymmetricKey devSymKey = returnDeveloperSymKey(token, finalKeyType, keySet, devKeyArray); + + StandardKDF standard = new StandardKDF(this); + SymmetricKey divKey = null; + + byte[] keyDiversified = null; + + //Consult the config to determine with diversification method to use. + if (params.isVer1DiversNone()) { + noDivers = true; + } else if (params.isVer1DiversEmv()) { + keyDiversified = KDF.getDiversificationData_EMV(xKDD, keyType); + } else if (params.isVer1DiversVisa2()) { + keyDiversified = KDF.getDiversificationData_VISA2(xKDD, keyType); + } else { + throw new EBaseException(method + " Invalid diversification method!"); + } + + //Obtain the card key,it may just be the raw developer key + if (noDivers == true) { + divKey = unwrapAESSymKeyOnToken(token, devKeyArray, false); + } else { + + // The g&d calls for computing the aes card key with DES, it will then be treated as aes + divKey = standard.computeCardKey_SCP03_WithDES3(devSymKey, keyDiversified, token); + } + + NistSP800_108KDF nistKdf = new NistSP800_108KDF(this); + + //IN scp03, the kek key IS the card key + if (constant == 0 /* kek key */) { + sessionKey = divKey; + } else { // session keys will become AES + if (noDerive) { + sessionKey = divKey; + } + else { + byte[] finalKeyBytes = nistKdf.kdf_AES_CMAC_SCP03(divKey, context, constant, 16); + sessionKey = unwrapAESSymKeyOnToken(token, finalKeyBytes, false); + + Arrays.fill(finalKeyBytes,(byte) 0); + + //The final session key is AES. + } + } + } else { // Creating a session key for the case where we have already upgraded the keys on the token, using the master key + CMS.debug(method + "In master key mode."); + + masterKey = getSymKeyByName(token, keyNameStr); + + StandardKDF standard = new StandardKDF(this); + + byte[] keyDiversified = null; + + if (params.isDiversNone()) { + throw new EBaseException(method + " No diversification requested in master key mode. Aborting..."); + } //Allow choice of emv or standard diversification + else if (params.isDiversEmv()) { + keyDiversified = KDF.getDiversificationData_EMV(xKDD, keyType); + } else if (params.isDiversVisa2()) { + keyDiversified = KDF.getDiversificationData_VISA2(xKDD, keyType); + } + + SymmetricKey divKey = null; + + divKey = standard.computeCardKey_SCP03_WithDES3(masterKey, keyDiversified, token); + + NistSP800_108KDF nistKdf = new NistSP800_108KDF(this); + // The kek session key does not call for derivation + if (constant == 0 /* kek key */) { + sessionKey = divKey; + } else { + if (noDerive) { + sessionKey = divKey; + } + else { + byte[] finalKeyBytes = nistKdf.kdf_AES_CMAC_SCP03(divKey, context, constant, 16); + sessionKey = unwrapAESSymKeyOnToken(token, finalKeyBytes, false); + + Arrays.fill(finalKeyBytes,(byte) 0); + } + } + } + + //SecureChannelProtocol.debugByteArray(sessionKey.getEncoded(), keyType + " : session key"); + + return sessionKey; + } + public SymmetricKey computeKEKKey_SCP01( String selectedToken, String keyNickName, byte[] keyInfo, @@ -153,7 +386,6 @@ public class SecureChannelProtocol { 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... "); @@ -294,7 +526,7 @@ public class SecureChannelProtocol { } else { StandardKDF standardKDF = new StandardKDF(this); CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using original KDF."); - byte[] data = KDF.getDiversificationData(context, keyType); + byte[] data = KDF.getDiversificationData_VISA2(context, keyType); devKey = standardKDF.computeCardKey(masterKey, data, token, PROTOCOL_ONE); } @@ -349,7 +581,7 @@ public class SecureChannelProtocol { byte[] parityEncrypted = KDF.getDesParity(encrypted); CMS.debug(method + "encryption completed"); - derivedKey = this.unwrapSymKeyOnToken(token, null, parityEncrypted, false); + derivedKey = this.unwrapSymKeyOnToken(token, null, parityEncrypted, false, SymmetricKey.DES3); } } catch (TokenException | InvalidKeyException | EBaseException e) { @@ -440,7 +672,7 @@ public class SecureChannelProtocol { 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) + public SymmetricKey returnDeveloperSymKey(CryptoToken token, String keyType, String keySet, byte[] inputKeyArray) throws EBaseException { SymmetricKey devKey = null; @@ -497,10 +729,157 @@ public class SecureChannelProtocol { return devKey; } - public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray, + //Takes raw des key 16 bytes, such as developer key and returns an AES key of the same size + //Supports 128 bits for now + public SymmetricKey unwrapAESSymKeyOnToken(CryptoToken token, byte[] inputKeyArray, + boolean isPerm) + throws EBaseException { + + String method = "SecureChannelProtocol.unwrapAESSymKeyOnToken:"; + CMS.debug(method + "Entering..."); + + if(token == null || inputKeyArray == null) { + throw new EBaseException(method + " Invalid input data!"); + } + + if(inputKeyArray.length < 16) { + throw new EBaseException(method + " Invalid key size!"); + } + + byte[] finalInputKeyArray = inputKeyArray; + if(inputKeyArray.length > 16) { + finalInputKeyArray = new byte[16]; + System.arraycopy(inputKeyArray, 0, finalInputKeyArray, 0, 16);; + + } + + KeyGenerator kg; + SymmetricKey finalAESKey; + try { + kg = token.getKeyGenerator(KeyGenAlgorithm.AES); + + 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); + kg.initialize(128); + SymmetricKey tempKey = kg.generate(); + + //unwrap the test aes keys onto the token + + Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC); + + int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength(); + byte[] iv = null; + + if (ivLength > 0) { + iv = new byte[ivLength]; // all zeroes + } + + encryptor.initEncrypt(tempKey, new IVParameterSpec(iv)); + byte[] wrappedKey = encryptor.doFinal(finalInputKeyArray); + + KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC); + keyWrap.initUnwrap(tempKey, new IVParameterSpec(iv)); + finalAESKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.AES, 16); + + } catch (Exception e) { + throw new EBaseException(method + " Can't unwrap key onto token!"); + } + + return finalAESKey; + } + + //Supports 128 bits for now + //Used to convert a des key (on token) to aes + //Not used as of now, future if needed + public SymmetricKey unwrapAESSymKeyOnToken(CryptoToken token, SymmetricKey keyToUnwrap, boolean isPerm) throws EBaseException { + String method = "SecureChannelProtocol.unwrapAESSymKeyOnToken:"; + CMS.debug(method + "Entering..."); + + if(token == null || keyToUnwrap == null) { + throw new EBaseException(method + " Invalid input data!"); + } + + if(keyToUnwrap.getLength()< 16) { + throw new EBaseException(method + " Invalid key size!"); + } + + KeyGenerator kg; + SymmetricKey finalAESKey; + try { + kg = token.getKeyGenerator(KeyGenAlgorithm.AES); + + 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); + kg.initialize(128); + SymmetricKey tempKey = kg.generate(); + + + int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength(); + byte[] iv = null; + + if (ivLength > 0) { + iv = new byte[ivLength]; // all zeroes + } + + //Wrap the arbitrary key first + + int len = keyToUnwrap.getLength(); + + SymmetricKey finalKeyToWrap = null; + SymmetricKey key16 = null; + if(len > 16) { + key16 = extractDes2FromDes3(keyToUnwrap, token.getName()); + if(key16 != null) + len = key16.getLength(); + finalKeyToWrap = key16; + } else { + finalKeyToWrap = keyToUnwrap; + } + + KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC); + keyWrap.initWrap(tempKey, new IVParameterSpec(iv)); + byte[] wrappedKey = keyWrap.wrap(finalKeyToWrap); + + //Now unwrap to an AES key + + KeyWrapper keyUnWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC); + keyUnWrap.initUnwrap(tempKey, new IVParameterSpec(iv)); + finalAESKey = keyUnWrap.unwrapSymmetric(wrappedKey, SymmetricKey.AES, 16); + + + Arrays.fill(wrappedKey,(byte) 0); + + //byte[] finalKeyBytes = finalAESKey.getKeyData(); + //displayByteArray(finalKeyBytes, false); + + } catch (Exception e) { + throw new EBaseException(method + " Can't unwrap key onto token!"); + } + + return finalAESKey; + + } + + //Final param allows us to request the final type, DES or AES + public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray, + boolean isPerm, SymmetricKey.Type finalKeyType) + throws EBaseException { + String method = "SecureChannelProtocol.unwrapSymKeyOnToken:"; CMS.debug(method + "Entering..."); SymmetricKey unwrapped = null; @@ -535,7 +914,7 @@ public class SecureChannelProtocol { byte[] finalKeyArray = null; - if (inputKeyArray.length == DES2_LENGTH) { + if (inputKeyArray.length == DES2_LENGTH && finalKeyType == SymmetricKey.DES3) { finalKeyArray = SecureChannelProtocol.makeDes3FromDes2(inputKeyArray); } @@ -570,9 +949,9 @@ public class SecureChannelProtocol { if (isPerm == true) { unwrapped = keyWrap.unwrapSymmetricPerm(wrappedKey, - SymmetricKey.DES3, 0); + finalKeyType, 0); } else { - unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 0); + unwrapped = keyWrap.unwrapSymmetric(wrappedKey, finalKeyType, 0); } } catch (Exception e) { @@ -590,8 +969,9 @@ public class SecureChannelProtocol { return unwrapped; } + //Final param allows us to request the final type, DES or AES public SymmetricKey unwrapWrappedSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray, - boolean isPerm) + boolean isPerm, SymmetricKey.Type keyType) throws EBaseException { String method = "SecureChannelProtocol.unwrapWrappedSymKeyOnToken:"; @@ -613,13 +993,15 @@ public class SecureChannelProtocol { if (isPerm) { unwrapped = keyWrap.unwrapSymmetricPerm(inputKeyArray, - SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, inputKeyArray.length); + keyType, SymmetricKey.Usage.UNWRAP, inputKeyArray.length); } else { - unwrapped = keyWrap.unwrapSymmetric(inputKeyArray, SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, + unwrapped = keyWrap.unwrapSymmetric(inputKeyArray, keyType, SymmetricKey.Usage.UNWRAP, inputKeyArray.length); } - finalUnwrapped = makeDes3KeyDerivedFromDes2(unwrapped, token.getName()); + if (keyType == SymmetricKey.DES3) { + finalUnwrapped = makeDes3KeyDerivedFromDes2(unwrapped, token.getName()); + } } catch (Exception e) { CMS.debug(method + " " + e); @@ -629,7 +1011,10 @@ public class SecureChannelProtocol { //CMS.debug(method + "Returning symkey: " + unwrapped); CMS.debug(method + "Returning symkey..."); - return finalUnwrapped; + if (finalUnwrapped != null) + return finalUnwrapped; + else + return unwrapped; } public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, byte[] inputKeyArray, boolean isPerm) @@ -648,7 +1033,7 @@ public class SecureChannelProtocol { } SymmetricKey transport = getSharedSecretKey(token); - unwrapped = this.unwrapSymKeyOnToken(token, transport, inputKeyArray, isPerm); + unwrapped = this.unwrapSymKeyOnToken(token, transport, inputKeyArray, isPerm, SymmetricKey.DES3); CMS.debug(method + "Returning symkey: " + unwrapped); @@ -710,16 +1095,36 @@ public class SecureChannelProtocol { public static void debugByteArray(byte[] array, String message) { CMS.debug("About to dump array: " + message); + System.out.println("About to dump array: " + message); if (array == null) { CMS.debug("Array to dump is empty!"); return; } + System.out.println("################### "); CMS.debug("################### "); String result = getHexString(array); CMS.debug(result); + System.out.println(result); + } + + public static void + displayByteArray(byte[] ba, boolean has_check_sum) { + char mask = 0xff; + + if (has_check_sum == true) + mask = 0xfe; + + for (int i = 0; i < ba.length; i++) { + + System.out.print(Integer.toHexString(ba[i] & mask) + " "); + if ((i % 26) == 25) { + System.out.println(""); + } + } + System.out.println(""); } final protected static char[] hex = "0123456789abcdef".toCharArray(); @@ -815,6 +1220,8 @@ public class SecureChannelProtocol { } SymmetricKey des2 = this.extractDes2FromDes3(symKey, devKeyToken); + //SecureChannelProtocol.debugByteArray(des2.getEncoded(), method + " raw des2 key, to be wrapped."); + result = this.wrapSessionKey(selectedToken, des2, devKey); // SecureChannelProtocol.debugByteArray(result, " Wrapped des2 key"); @@ -929,25 +1336,93 @@ public class SecureChannelProtocol { wrapper = wrappingKey; } - try { - CryptoManager cm = this.getCryptoManger(); - CryptoToken token = returnTokenByName(tokenName, cm); + CMS.debug(method + " wrapper key type: " + wrapper.getType()); - keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB); - keyWrap.initWrap(wrapper, null); - wrappedSessKeyData = keyWrap.wrap(sessionKey); - } catch (Exception e) { - CMS.debug(method + " " + e); - throw new EBaseException(e); + if (wrapper.getType() != SymmetricKey.AES) { + CMS.debug(method + "Trying to wrap a key with an DES key!"); + + 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 ( + Exception e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + } else if (wrapper.getType() == SymmetricKey.AES) { + CMS.debug(method + "Trying to wrap a key with an AES key!"); + try { + CryptoManager cm = this.getCryptoManger(); + CryptoToken token = returnTokenByName(tokenName, cm); + + int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength(); + byte[] iv = null; + + if (ivLength > 0) { + iv = new byte[ivLength]; // all zeroes + } + + keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC); + keyWrap.initWrap(wrapper, new IVParameterSpec(iv)); + wrappedSessKeyData = keyWrap.wrap(sessionKey); + + + } catch (Exception e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } } - //CMS.debug(method + " About to return session key: " + wrappedSessKeyData); + + //SecureChannelProtocol.debugByteArray(wrappedSessKeyData, "wrappedSessKeyData"); CMS.debug(method + " returning session key"); return wrappedSessKeyData; } + //128 for now. + public byte[] computeAES_CBCEncryption(SymmetricKey symKey, String selectedToken, byte[] input, byte[] iv) + throws EBaseException + { + String method = "SecureChannelProtocol.computeAES_CBCEncryption"; + byte[] output = null; + byte[] finalIv = null; + + if (symKey == null || selectedToken == null) { + throw new EBaseException(method + " Invalid input data."); + } + + if (iv == null) { + finalIv = new byte[16]; + + } else { + finalIv = iv; + } + + try { + CryptoManager cm = this.getCryptoManger(); + CryptoToken token = returnTokenByName(selectedToken, cm); + Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC); + encryptor.initEncrypt(symKey, new IVParameterSpec(finalIv)); + output = encryptor.doFinal(input); + + SecureChannelProtocol.debugByteArray(output, "Encrypted data:"); + } catch (Exception e) { + + CMS.debug(method + e); + throw new EBaseException(method + e); + } + + return output; + } + public byte[] computeDes3EcbEncryption(SymmetricKey desKey, String selectedToken, byte[] input) throws EBaseException { @@ -983,6 +1458,61 @@ public class SecureChannelProtocol { return output; } + //SCP03 uses aes + public byte[] computeKeyCheck_SCP03(SymmetricKey symKey, String selectedToken) throws EBaseException { + + String method = "SecureChannelProtocol.computeKeyCheck_SCP03:"; + + if (symKey == null || selectedToken == null) { + throw new EBaseException(method + " invalid input data!"); + } + + byte[] key_check_message = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; + //zero iv vector + byte[] key_check_iv = new byte[16]; + + byte[] output = null; + byte[] finalOutput = new byte[3]; + + try { + output = computeAES_CBCEncryption(symKey, selectedToken, key_check_message, key_check_iv); + } catch (EBaseException e) { + CMS.debug(method + e); + throw e; + + } + + //Get the 3 bytes needed + System.arraycopy(output, 0, finalOutput, 0, 3); + + //SecureChannelProtocol.debugByteArray(finalOutput, method + " output: "); + + return finalOutput; + } + + //AES, uses AES_CMAC alg to do the work. + public byte[] computeCryptogram_SCP03(SymmetricKey symKey, String selectedToken, byte[] context, byte cryptoType) + throws EBaseException { + String method = "SecureChannelProtocol.computeCryptogram_"; + + CMS.debug(method + " entering .."); + + if (symKey == null || selectedToken == null || (cryptoType != NistSP800_108KDF.CARD_CRYPTO_KDF_CONSTANT) + && cryptoType != NistSP800_108KDF.HOST_CRYPTO_KDF_CONSTANT) { + throw new EBaseException(method + " Invalid input data."); + } + + NistSP800_108KDF nistKdf = new NistSP800_108KDF(this); + byte[] crypto = nistKdf.kdf_AES_CMAC_SCP03(symKey, context, cryptoType, 8); + //SecureChannelProtocol.debugByteArray(crypto, " calculated cryptogram"); + + byte[] finalCrypto = new byte[8]; + + System.arraycopy(crypto, 0, finalCrypto, 0, 8); + + return finalCrypto; + } + public byte[] computeKeyCheck(SymmetricKey desKey, String selectedToken) throws EBaseException { String method = "SecureChannelProtocol.computeKeyCheck:"; @@ -1115,6 +1645,9 @@ public class SecureChannelProtocol { return output; } + //Calculates the 3 new card keys to be written to the token for + //Symmetric key changeover. Supports SCP03 now. + //Provide all the static developer key arrays should we need them public byte[] diversifyKey(String tokenName, String newTokenName, String oldMasterKeyName, @@ -1125,12 +1658,18 @@ public class SecureChannelProtocol { boolean nistSP800_108KdfUseCuidAsKdd, byte[] CUIDValue, byte[] KDD, - byte[] kekKeyArray, - String useSoftToken, String keySet, byte protocol) throws EBaseException { + byte[] kekKeyArray, byte[] encKeyArray, byte[] macKeyArray, + String useSoftToken, String keySet, byte protocol, GPParams params) throws EBaseException { String method = "SecureChannelProtocol.diversifyKey:"; - CMS.debug(method + " Entering ... newTokenName: " + newTokenName); + CMS.debug(method + " Entering ... newTokenName: " + newTokenName + " protocol: " + protocol); + CMS.debug(method + " oldMasterKeyName: " + oldMasterKeyName); + CMS.debug(method + " newMasterKeyName: " + newMasterKeyName); + + SecureChannelProtocol.debugByteArray(encKeyArray, " Developer enc key array: "); + SecureChannelProtocol.debugByteArray(macKeyArray, " Developer mac key array: "); + SecureChannelProtocol.debugByteArray(kekKeyArray, " Developer kek key array: "); SymmetricKey masterKey = null; SymmetricKey oldMasterKey = null; @@ -1151,7 +1690,7 @@ public class SecureChannelProtocol { byte[] output = null; if (oldMasterKeyName == null || oldKeyInfo == null || newKeyInfo == null - || keySet == null) { + || keySet == null || params == null) { throw new EBaseException(method + "Invalid input!"); } @@ -1162,6 +1701,9 @@ public class SecureChannelProtocol { String fullNewMasterKeyName = getFullMasterKeyName(newMasterKeyName); String fullOldMasterKeyName = getFullMasterKeyName(oldMasterKeyName); + CMS.debug(method + " fullOldMasterKeyName: " + fullOldMasterKeyName); + CMS.debug(method + " fullNewMasterKeyName: " + fullNewMasterKeyName); + CryptoManager cm = null; CryptoToken token = null; CryptoToken newToken = null; @@ -1217,16 +1759,27 @@ public class SecureChannelProtocol { 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); + KDCenc = KDF.getDiversificationData_VISA2(KDD, SecureChannelProtocol.encType); + KDCmac = KDF.getDiversificationData_VISA2(KDD, SecureChannelProtocol.macType); + KDCkek = KDF.getDiversificationData_VISA2(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: "); + //This routine does not even support protocol 2, bail if asked to do so. + if (protocol == PROTOCOL_TWO) { + throw new EBaseException(method + " SCP 02 not yet supported here."); + } + String transportKeyName = SecureChannelProtocol.getSharedSecretKeyName(null); + + if (checkForDeveloperKeySet(oldMasterKeyName) == true) { + //Starting with the deve key set, do nothing in this clause + CMS.debug(method + " Developer key set case: protocol: " + protocol); + } else { + //Case where down grading back to the deve key set, or to another master key set + // This clause does nothing but calculate the kek key of the + // Current keyset, which will be used to wrap the new keys, to be calculated + CMS.debug(method + " Not Developer key set case: "); + + if (protocol == PROTOCOL_ONE ) { if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, oldKeyVersion)) { CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF."); @@ -1252,19 +1805,44 @@ public class SecureChannelProtocol { old_kek_sym_key = standardKDF.computeCardKey(oldMasterKey, KDCkek, token, PROTOCOL_ONE); } - } + } else { // Protocol 3 - /* special case #01#01 */ - if (fullNewMasterKeyName != null && fullNewMasterKeyName.equals("#01#01")) - { - CMS.debug(method + " Special case dev key set for DiversifyKey!"); + old_kek_sym_key = this.computeSessionKey_SCP03(tokenName, oldMasterKeyName, + oldKeyInfo, SecureChannelProtocol.kekType, kekKeyArray, keySet, + CUIDValue, KDD, null, null, transportKeyName, params); + CMS.debug(method + " Moving back to the developer key set case, protocol 3"); + } + } + + // Now compute the new keys to be written to the token. + /* special case #01#01 */ + if (fullNewMasterKeyName != null + && (fullNewMasterKeyName.equals("#01#01") || fullNewMasterKeyName.contains("#01#03"))) + { + //This is the case where we revert to the original developer key set or key set 1 + if (protocol == PROTOCOL_ONE) { + CMS.debug(method + " Special case returning to the dev key set (1) for DiversifyKey, protocol 1!"); 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"); + } else if (protocol == PROTOCOL_THREE) { + CMS.debug(method + " Special case or returning to the dev key set (or ver 1) for DiversifyKey, protocol 3!"); + encKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, newKeyInfo, + SecureChannelProtocol.encType, kekKeyArray, + keySet, CUIDValue, KDD, null, null, transportKeyName, params); + macKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, newKeyInfo, + SecureChannelProtocol.macType, kekKeyArray, + keySet, CUIDValue, KDD, null, null, transportKeyName, params); + kekKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, newKeyInfo, + SecureChannelProtocol.kekType, kekKeyArray, + keySet, CUIDValue, KDD, null, null, transportKeyName, params); + } + } else { + //Compute new card keys for upgraded key set + CMS.debug(method + " Compute card key on token case ! For new key version."); + if (protocol == PROTOCOL_ONE) { if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, newKeyVersion)) { CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF. For new key version."); @@ -1289,17 +1867,31 @@ public class SecureChannelProtocol { 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 { // protocol 3 + CMS.debug(method + " Generating new card keys to upgrade to, protocol 3."); + encKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, oldKeyInfo, + SecureChannelProtocol.encType, kekKeyArray, + keySet, CUIDValue, KDD, null, null, transportKeyName, params); + macKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, oldKeyInfo, + SecureChannelProtocol.macType, kekKeyArray, + keySet, CUIDValue, KDD, null, null, transportKeyName, params); + kekKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, oldKeyInfo, + SecureChannelProtocol.kekType, kekKeyArray, + keySet, CUIDValue, KDD, null, null, transportKeyName, params); + + // Generate an old kek key to do the encrypting of the new static keys + + old_kek_sym_key = this.computeSessionKey_SCP03(tokenName, oldMasterKeyName, oldKeyInfo, + SecureChannelProtocol.kekType, kekKeyArray, + keySet, CUIDValue, KDD, null, null, transportKeyName, params); + } + + 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; @@ -1327,7 +1919,9 @@ public class SecureChannelProtocol { } 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, @@ -1340,6 +1934,8 @@ public class SecureChannelProtocol { return output; } + //Create the actual blob of new keys to be written to the token + // Suports prot1 and prot3 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) @@ -1392,6 +1988,8 @@ public class SecureChannelProtocol { wrappingKey = old_kek_sym_key; } + CMS.debug(method + "Wrapping key: length: " + wrappingKey.getLength()); + alg = (byte) 0x81; encKey16 = extractDes2FromDes3(encKey, tokenName); macKey16 = extractDes2FromDes3(macKey, tokenName); @@ -1404,23 +2002,48 @@ public class SecureChannelProtocol { 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 if (protocol == PROTOCOL_THREE) { + CMS.debug(method + " Attempting SCP03"); + + if (old_kek_sym_key == null) { + CMS.debug(method + " SCP03: Using old kek key array."); + wrappingKey = unwrapAESSymKeyOnToken(token, old_kek_key_array, false); + } else { + CMS.debug(method + "SCP03: Using input old key key sym key."); + wrappingKey = old_kek_sym_key; + } + + alg = (byte) 0x88; + + encrypted_enc_key = this.wrapSessionKey(tokenName, encKey, wrappingKey); + encrypted_mac_key = this.wrapSessionKey(tokenName, macKey, wrappingKey); + encrypted_kek_key = this.wrapSessionKey(tokenName, kekKey, wrappingKey); + + keycheck_enc_key = this.computeKeyCheck_SCP03(encKey, tokenName); + keycheck_mac_key = this.computeKeyCheck_SCP03(macKey, tokenName); + keycheck_kek_key = this.computeKeyCheck_SCP03(kekKey, tokenName); + } 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 }; + byte[] b1 = null; + byte[] b2 = null; + + if (protocol == PROTOCOL_THREE) { + //Will be different if the key is bigger than AES 128 + // Support 128 for now + b1 = new byte[] { alg, 0x11, (byte) encrypted_enc_key.length }; + } else { + b1 = new byte[] { alg, 0x10 }; + } + + b2 = new byte[] { 0x3 }; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -1484,6 +2107,10 @@ public class SecureChannelProtocol { if (keyInfo.equals("#01#02") || keyInfo.equals("#FF#02")) return true; + //SCP03 + if (keyInfo.contains("#01#03") || keyInfo.contains("#FF#03")) + return true; + return false; } @@ -1493,6 +2120,42 @@ public class SecureChannelProtocol { } } + + //SCP03 wrap a session key with AES kek key. + public byte[] encryptData_SCP03(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, GPParams params) throws EBaseException { + + String method = "SecureChannelProtocol.encryptData_SCP03:"; + + CMS.debug(method + " Entering ...."); + + String transportKeyName = SecureChannelProtocol.getSharedSecretKeyName(null); + + if (keyInfo == null || keySet == null || (keyInfo == null || keyInfo.length < 2) || params == null) { + 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_SCP03(selectedToken, keyNickName, + keyInfo, kekType, kekKeyArray, keySet, xCUID, xKDD, + null, null, transportKeyName, params); + + byte[] output = null; + output = computeAES_CBCEncryption(kekKey, selectedToken, data, null); + + debugByteArray(output, method + " encryptData: Output: "); + + return output; + } + 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 { 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 index 1d61a465d..8c228a15c 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java @@ -19,6 +19,39 @@ public class StandardKDF extends KDF { this.protocol = protocol; } + // For the scp03 g&d smart cafe, the dev keys must start out as DES3 keys + // but then this routine must return the AES version of the key + + public SymmetricKey computeCardKey_SCP03_WithDES3(SymmetricKey masterKey, byte[] derivationData, CryptoToken token) + throws EBaseException { + + String method = "StandardKDF.computeCardKey_SCP03_WithDES3:"; + + CMS.debug(method + " entering ..."); + + if (masterKey == null || token == null + || derivationData == null || ((derivationData.length != SecureChannelProtocol.AES_128_BYTES) && + (derivationData.length != SecureChannelProtocol.AES_192_BYTES) && + (derivationData.length != SecureChannelProtocol.AES_256_BYTES))) { + + CMS.debug(method + " Invalid input parameters!"); + + throw new EBaseException(method + " Invalid input parameters!"); + } + + //Now the new key data is the derivation data encrypted with DESS3 + + byte[] encrypted; + try { + encrypted = this.protocol.computeDes3EcbEncryption(masterKey, token.getName(), derivationData); + } catch (TokenException e) { + throw new EBaseException(method + "Can't derive key data!"); + } + //SecureChannelProtocol.debugByteArray(encrypted, "calculated key: "); + + return this.protocol.unwrapAESSymKeyOnToken(token, encrypted, false); + + } public SymmetricKey computeCardKey(SymmetricKey masterKey, byte[] derivationData, CryptoToken token, int protocol) throws EBaseException { @@ -54,12 +87,14 @@ public class StandardKDF extends KDF { CMS.debug(method + "Now try this the old fashioned way"); byte[] encrypted = this.protocol.computeDes3EcbEncryption(masterKey, token.getName(), derivationData); - byte[] parityEncrypted = KDF.getDesParity(encrypted); + SecureChannelProtocol.debugByteArray(encrypted, "calculated key: "); + // byte[] parityEncrypted = KDF.getDesParity(encrypted); CMS.debug(method + "done computeDes3EcbEncryptiong"); + derivedKey = this.protocol.unwrapSymKeyOnToken(token, null, - parityEncrypted, false); + encrypted, false,SymmetricKey.DES3); - // The key this way is aleady des3, return + // The key this way is already final, return return derivedKey; } 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 a282cd26f..6a1746616 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 @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.OutputStream; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.StringTokenizer; import javax.servlet.ServletConfig; @@ -1570,8 +1571,11 @@ public class TokenServlet extends CMSServlet { return cs.getString("tks.tksSharedSymKeyName", TRANSPORT_KEY_NAME); } + //Accepts protocol param and supports scp03. private void processDiversifyKey(HttpServletRequest req, HttpServletResponse resp) throws EBaseException { + + String method = "TokenServlet.processDiversifyKey: "; byte[] KeySetData, xCUID, xKDD; // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD // AC: BUGFIX: Record the actual parameters to DiversifyKey in the audit log. @@ -1609,6 +1613,9 @@ public class TokenServlet extends CMSServlet { String rProtocol = req.getParameter(IRemoteRequest.CHANNEL_PROTOCOL); String rWrappedDekKey = req.getParameter(IRemoteRequest.WRAPPED_DEK_SESSION_KEY); + + CMS.debug(method + "rWrappedDekKey: " + rWrappedDekKey); + int protocol = 1; String auditMessage = ""; @@ -1670,13 +1677,13 @@ public class TokenServlet extends CMSServlet { if (!missingParam) { xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(oldMasterKeyName); - if (xkeyInfo == null || xkeyInfo.length != 2) { + if (xkeyInfo == null || (xkeyInfo.length != 2 && xkeyInfo.length != 3)) { badParams += " KeyInfo length,"; CMS.debug("TokenServlet: Invalid key info length"); missingParam = true; } xnewkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(newMasterKeyName); - if (xnewkeyInfo == null || xnewkeyInfo.length != 2) { + if (xnewkeyInfo == null || (xnewkeyInfo.length != 2 && xnewkeyInfo.length != 3)) { badParams += " NewKeyInfo length,"; CMS.debug("TokenServlet: Invalid new key info length"); missingParam = true; @@ -1692,7 +1699,6 @@ public class TokenServlet extends CMSServlet { CMS.debug("process DiversifyKey: protocol value: " + protocol); if (protocol == 2) { - if ((rWrappedDekKey == null) || (rWrappedDekKey.equals(""))) { badParams += " WrappedDekKey,"; CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: WrappedDekKey, with SCP02."); @@ -1766,7 +1772,12 @@ public class TokenServlet extends CMSServlet { if (mNewKeyNickName != null) newMasterKeyName = mNewKeyNickName; - String oldKeyInfoMap = "tks." + keySet + ".mk_mappings." + req.getParameter(IRemoteRequest.TOKEN_KEYINFO); //#xx#xx + String tokKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO); + + // Get the first 6 characters, since scp03 gives us extra characters. + tokKeyInfo = tokKeyInfo.substring(0,6); + String oldKeyInfoMap = "tks." + keySet + ".mk_mappings." + tokKeyInfo; //#xx#xx + CMS.debug(method + " oldKeyInfoMap: " + oldKeyInfoMap); String oldMappingValue = CMS.getConfigStore().getString(oldKeyInfoMap, null); String oldSelectedToken = null; if (oldMappingValue == null) { @@ -1778,7 +1789,9 @@ public class TokenServlet extends CMSServlet { oldKeyNickName = st.nextToken(); } - String newKeyInfoMap = "tks.mk_mappings." + rnewKeyInfo; //#xx#xx + + String newKeyInfoMap = "tks.mk_mappings." + rnewKeyInfo.substring(0,6); //#xx#xx + CMS.debug(method + " newKeyInfoMap: " + newKeyInfoMap); String newMappingValue = CMS.getConfigStore().getString(newKeyInfoMap, null); String newSelectedToken = null; if (newMappingValue == null) { @@ -1795,14 +1808,22 @@ public class TokenServlet extends CMSServlet { " oldKeyNickName=" + oldKeyNickName + " newKeyNickName=" + newKeyNickName); - byte kekKeyArray[] = - com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); + byte kekKeyArray[] = getDeveKeyArray("kek_key", sconfig, keySet); + byte macKeyArray[] = getDeveKeyArray("auth_key", sconfig, keySet); + byte encKeyArray[] = getDeveKeyArray("mac_key", sconfig, keySet); + + // com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); + + //GPParams for scp03 right now, reads some scp03 specific values from the config of a given keyset + // passed down to the SecureChannelProtocol functions that deal with SCP03 + + GPParams gp3Params = readGPSettings(keySet); - SecureChannelProtocol secProtocol = new SecureChannelProtocol(); + SecureChannelProtocol secProtocol = new SecureChannelProtocol(protocol); // AC: KDF SPEC CHANGE - check for error reading settings if (missingSetting_exception == null) { - if (protocol == 1) { - KeySetData = secProtocol.diversifyKey(oldSelectedToken, + if (protocol == 1 || protocol == 3) { + 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 @@ -1811,7 +1832,7 @@ public class TokenServlet extends CMSServlet { 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); + kekKeyArray,encKeyArray,macKeyArray, useSoftToken_s, keySet, (byte) protocol,gp3Params); } else if (protocol == 2) { KeySetData = SessionKey.DiversifyKey(oldSelectedToken, newSelectedToken, oldKeyNickName, @@ -1954,6 +1975,8 @@ public class TokenServlet extends CMSServlet { String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO); String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID); + String protocolValue = req.getParameter(IRemoteRequest.CHANNEL_PROTOCOL); + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS String rKDD = req.getParameter("KDD"); if ((rKDD == null) || (rKDD.length() == 0)) { @@ -1996,6 +2019,8 @@ public class TokenServlet extends CMSServlet { s_isRandom); audit(auditMessage); + GPParams gp3Params = readGPSettings(keySet); + if (isRandom) { if ((rdata == null) || (rdata.equals(""))) { CMS.debug("TokenServlet: processEncryptData(): no data in request. Generating random number as data"); @@ -2058,7 +2083,7 @@ public class TokenServlet extends CMSServlet { } xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo); - if (xkeyInfo == null || xkeyInfo.length != 2) { + if (xkeyInfo == null || (xkeyInfo.length != 2 && xkeyInfo.length != 3)) { badParams += " KeyInfo length,"; CMS.debug("TokenServlet: Invalid key info length"); missingParam = true; @@ -2103,7 +2128,7 @@ public class TokenServlet extends CMSServlet { data = com.netscape.cmsutil.util.Utils.SpecialDecode(rdata); keyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo); - String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo; + String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo.substring(0,6); String mappingValue = CMS.getConfigStore().getString(keyInfoMap, null); if (mappingValue == null) { selectedToken = CMS.getConfigStore().getString("tks.defaultSlot", CryptoUtil.INTERNAL_TOKEN_NAME); @@ -2114,20 +2139,53 @@ public class TokenServlet extends CMSServlet { keyNickName = st.nextToken(); } + + //calculate the protocol + + int protocolInt = SecureChannelProtocol.PROTOCOL_ONE; + try + { + protocolInt = Integer.parseInt(protocolValue); + } + catch (NumberFormatException nfe) + { + protocolInt = SecureChannelProtocol.PROTOCOL_ONE; + } + + CMS.debug( "TokenServerlet.encryptData: protocol input: " + protocolInt); + + //Check for reasonable sanity, leave room for future versions + if(protocolInt <= 0 || protocolInt > 20) { + CMS.debug( "TokenServerlet.encryptData: unfamliar protocl, assume default of 1."); + protocolInt = 1; + + } + byte kekKeyArray[] = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); // AC: KDF SPEC CHANGE - check for error reading settings if (missingSetting_exception == null) { - 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 - 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); + SecureChannelProtocol protocol = new SecureChannelProtocol(protocolInt); + + if (protocolInt != SecureChannelProtocol.PROTOCOL_THREE) { + + 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 + 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); + + } else { + + encryptedData = protocol.encryptData_SCP03(selectedToken, keyNickName, data, xkeyInfo, + nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, kekKeyArray, + useSoftToken_s, keySet,gp3Params); + + } SecureChannelProtocol.debugByteArray(encryptedData, "New Encrypt Data: "); @@ -2402,7 +2460,7 @@ public class TokenServlet extends CMSServlet { //CMS.debug("Protocol: " + protocol + " temp: " + temp); setDefaultSlotAndKeyName(req); - if (temp != null) { + if (temp != null && protocol == null) { processComputeSessionKey(req, resp); } else if (req.getParameter(IRemoteRequest.TOKEN_DATA) != null) { processEncryptData(req, resp); @@ -2413,10 +2471,515 @@ public class TokenServlet extends CMSServlet { } else if (protocol != null && protocol.contains("2") && (derivationConstant != null)) { //SCP02 compute one session key. processComputeSessionKeySCP02(req, resp); + + } else if (protocol != null && protocol.contains("3") ) { + processComputeSessionKeysSCP03(req,resp); } else { throw new EBaseException("Process: Can't decide upon function to call!"); + } + } + + //Create all the session keys for scp03 at once and return. + //ToDo: calcualte the optional rmac key + private void processComputeSessionKeysSCP03(HttpServletRequest req, HttpServletResponse resp) throws EBaseException { + String method = "processComputeSessionKeysSCP03:"; + CMS.debug(method + " entering ..."); + + byte[] card_challenge, host_challenge, xCUID, xKDD; + byte[] card_crypto, host_cryptogram, input_card_crypto; + byte[] xcard_challenge, xhost_challenge; + byte[] enc_session_key, xkeyInfo,mac_session_key, kek_session_key; + String auditMessage = null; + String errorMsg = ""; + String badParams = ""; + String transportKeyName = ""; + String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID); + + String rKDD = req.getParameter("KDD"); + if ((rKDD == null) || (rKDD.length() == 0)) { + // KDF phase1: default to rCUID if not present + CMS.debug("TokenServlet: KDD not supplied, set to CUID before TPS change"); + rKDD = rCUID; + } + + String keySet = req.getParameter(IRemoteRequest.TOKEN_KEYSET); + if (keySet == null || keySet.equals("")) { + keySet = "defKeySet"; + } + CMS.debug("keySet selected: " + keySet); + + GPParams gp3Params = readGPSettings(keySet); + + boolean serversideKeygen = false; + + IConfigStore sconfig = CMS.getConfigStore(); + boolean isCryptoValidate = true; + boolean missingParam = false; + + Exception missingSetting_exception = null; + + mac_session_key = null; + kek_session_key = null; + card_crypto = null; + host_cryptogram = null; + enc_session_key = null; + + SessionContext sContext = SessionContext.getContext(); + + String agentId = ""; + if (sContext != null) { + agentId = + (String) sContext.get(SessionContext.USER_ID); + } + + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST, + rCUID, + rKDD, + ILogger.SUCCESS, + agentId); + + audit(auditMessage); + + String kek_wrapped_desKeyString = null; + String keycheck_s = null; + + String useSoftToken_s = CMS.getConfigStore().getString("tks.useSoftToken", "true"); + if (!useSoftToken_s.equalsIgnoreCase("true")) + useSoftToken_s = "false"; + + CMS.debug(method + " useSoftToken: " + useSoftToken_s); + + String rServersideKeygen = req.getParameter(IRemoteRequest.SERVER_SIDE_KEYGEN); + if (rServersideKeygen.equals("true")) { + + serversideKeygen = true; + } + + CMS.debug(method + " serversideKeygen: " + serversideKeygen); + + try { + isCryptoValidate = sconfig.getBoolean("cardcryptogram.validate.enable", true); + } catch (EBaseException eee) { + } + + CMS.debug(method + " Do crypto validation: " + isCryptoValidate); + + transportKeyName = getSharedSecretName(sconfig); + + String rcard_challenge = req.getParameter(IRemoteRequest.TOKEN_CARD_CHALLENGE); + String rhost_challenge = req.getParameter(IRemoteRequest.TOKEN_HOST_CHALLENGE); + String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO); + String rcard_cryptogram = req.getParameter(IRemoteRequest.TOKEN_CARD_CRYPTOGRAM); + + if ((rCUID == null) || (rCUID.equals(""))) { + CMS.debug(method + " missing request parameter: CUID"); + badParams += " CUID,"; + missingParam = true; + } + + if ((rKDD == null) || (rKDD.length() == 0)) { + CMS.debug(method + " missing request parameter: KDD"); + badParams += " KDD,"; + missingParam = true; + } + + if ((rcard_challenge == null) || (rcard_challenge.equals(""))) { + badParams += " card_challenge,"; + CMS.debug(method + " missing request parameter: card challenge"); + missingParam = true; + } + + if ((rhost_challenge == null) || (rhost_challenge.equals(""))) { + badParams += " host_challenge,"; + CMS.debug(method + " missing request parameter: host challenge"); + missingParam = true; + } + + if ((rcard_cryptogram == null) || (rcard_cryptogram.equals(""))) { + badParams += " card_cryptogram,"; + CMS.debug(method + " missing request parameter: card_cryptogram"); + missingParam = true; + } + + if ((rKeyInfo == null) || (rKeyInfo.equals(""))) { + badParams += " KeyInfo,"; + CMS.debug(method + "missing request parameter: key info"); + missingParam = true; + } + + String selectedToken = null; + String keyNickName = null; + boolean sameCardCrypto = true; + + xCUID = null; + xKDD = null; + xkeyInfo = null; + xcard_challenge = null; + xhost_challenge = null; + + if (!missingParam) { + xCUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); + if (xCUID == null || xCUID.length != 10) { + badParams += " CUID length,"; + CMS.debug("TokenServlet: Invalid CUID length"); + missingParam = true; + } + + xKDD = com.netscape.cmsutil.util.Utils.SpecialDecode(rKDD); + if (xKDD == null || xKDD.length != 10) { + badParams += " KDD length,"; + CMS.debug("TokenServlet: Invalid KDD length"); + missingParam = true; + } + + xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo); + if (xkeyInfo == null || xkeyInfo.length != 3) { + badParams += " KeyInfo length,"; + CMS.debug("TokenServlet: Invalid key info length."); + missingParam = true; + } + xcard_challenge = + com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_challenge); + if (xcard_challenge == null || xcard_challenge.length != 8) { + badParams += " card_challenge length,"; + CMS.debug("TokenServlet: Invalid card challenge length."); + missingParam = true; + } + + xhost_challenge = com.netscape.cmsutil.util.Utils.SpecialDecode(rhost_challenge); + if (xhost_challenge == null || xhost_challenge.length != 8) { + badParams += " host_challenge length,"; + CMS.debug("TokenServlet: Invalid host challenge length"); + missingParam = true; + } + } + + ArrayList serverSideValues = null; + + if (!missingParam) { + card_challenge = + com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_challenge); + + host_challenge = com.netscape.cmsutil.util.Utils.SpecialDecode(rhost_challenge); + + String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo.substring(0,6); //#xx#xx + String mappingValue = CMS.getConfigStore().getString(keyInfoMap, null); + + + if (mappingValue == null) { + selectedToken = + CMS.getConfigStore().getString("tks.defaultSlot", "internal"); + keyNickName = rKeyInfo; + } else { + StringTokenizer st = new StringTokenizer(mappingValue, ":"); + if (st.hasMoreTokens()) + selectedToken = st.nextToken(); + if (st.hasMoreTokens()) + keyNickName = st.nextToken(); + } + + CMS.debug(method + " selectedToken: " + selectedToken + " keyNickName: " + keyNickName ); + + SymmetricKey macSessionKey = null; + SymmetricKey encSessionKey = null; + SymmetricKey kekSessionKey = null; + + if (selectedToken != null && keyNickName != null + && missingSetting_exception == null) { + + try { + + byte macKeyArray[] = + com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + + keySet + ".mac_key")); + CMS.debug("TokenServlet about to try ComputeSessionKey selectedToken=" + + selectedToken + " keyNickName=" + keyNickName); + + SecureChannelProtocol protocol = new SecureChannelProtocol(SecureChannelProtocol.PROTOCOL_THREE); + + macSessionKey = protocol.computeSessionKey_SCP03(selectedToken, keyNickName,xkeyInfo, + SecureChannelProtocol.macType, macKeyArray, keySet,xCUID, xKDD, xhost_challenge, xcard_challenge, + transportKeyName,gp3Params); + + mac_session_key = protocol.wrapSessionKey(selectedToken, macSessionKey, null); + + if (mac_session_key == null) { + CMS.debug(method + " Can't get mac session key bytes"); + throw new Exception(method + " Can't get mac session key bytes"); + + } + + byte encKeyArray[] = + com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + + keySet + ".auth_key")); + + encSessionKey = protocol.computeSessionKey_SCP03(selectedToken, keyNickName,xkeyInfo, + SecureChannelProtocol.encType, encKeyArray, keySet, xCUID, xKDD, xhost_challenge, xcard_challenge, + transportKeyName,gp3Params); + + enc_session_key = protocol.wrapSessionKey(selectedToken, encSessionKey, null); + + if (enc_session_key == null) { + CMS.debug("TokenServlet:Tried ComputeEncSessionKey, got NULL "); + throw new Exception("Can't compute enc session key!"); + + } + + byte kekKeyArray[] = + com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + + keySet + ".kek_key")); + + kekSessionKey = protocol.computeSessionKey_SCP03(selectedToken, keyNickName, xkeyInfo, + SecureChannelProtocol.kekType, kekKeyArray, keySet, xCUID, xKDD, xhost_challenge, + xcard_challenge, + transportKeyName,gp3Params); + + kek_session_key = protocol.wrapSessionKey(selectedToken, kekSessionKey, null); + + + //Offload some of the tedious params gathering to another method + //ToDo, create a method that reads all this stuff at once for all major methods + if (serversideKeygen) { + try { + serverSideValues = calculateServerSideKeygenValues(useSoftToken_s, selectedToken, + kekSessionKey, protocol); + } catch (EBaseException e) { + + CMS.debug(method + " Can't calcualte server side keygen required values..."); + + } + } + + try { + isCryptoValidate = sconfig.getBoolean("cardcryptogram.validate.enable", true); + } catch (EBaseException eee) { + } + + ByteArrayOutputStream contextStream = new ByteArrayOutputStream(); + try { + contextStream.write(host_challenge); + contextStream.write(card_challenge); + } catch (IOException e) { + throw new EBaseException(method + " Error calculating derivation data!"); + } + + host_cryptogram = protocol.computeCryptogram_SCP03(macSessionKey, selectedToken, contextStream.toByteArray(),NistSP800_108KDF.HOST_CRYPTO_KDF_CONSTANT); + SecureChannelProtocol.debugByteArray(host_cryptogram, method + " calculated host crypto: " + host_cryptogram.length); + + + if( isCryptoValidate) { + if (rcard_cryptogram == null) { + CMS.debug(method + " missing card cryptogram"); + throw new Exception(method + "Missing card cryptogram"); + } + input_card_crypto = + com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_cryptogram); + card_crypto = protocol.computeCryptogram_SCP03(macSessionKey, selectedToken, contextStream.toByteArray(),NistSP800_108KDF.CARD_CRYPTO_KDF_CONSTANT); + SecureChannelProtocol.debugByteArray(card_crypto, method + " calculated card crypto: "); + SecureChannelProtocol.debugByteArray(input_card_crypto, method + " original card crypto: "); + + if(!cryptoGramsAreEqual(input_card_crypto, card_crypto)) { + throw new Exception(method + "Card cryptogram mismatch!"); + } + + } + } catch (Exception e) { + CMS.debug(e); + CMS.debug("TokenServlet Computing Session Key: " + e.toString()); + if (isCryptoValidate) + sameCardCrypto = false; + } + } + } // ! missingParam + + String value = ""; + + resp.setContentType("text/html"); + + String encSessionKeyString = ""; + String macSessionKeyString = ""; + String kekSessionKeyString = ""; + + String drm_trans_wrapped_desKeyString = ""; + String cryptogram = ""; + String status = "0"; + + if (enc_session_key != null && enc_session_key.length > 0) { + encSessionKeyString = + com.netscape.cmsutil.util.Utils.SpecialEncode(enc_session_key); + } else { + status = "1"; + } + + if (mac_session_key != null && mac_session_key.length > 0) { + macSessionKeyString = + com.netscape.cmsutil.util.Utils.SpecialEncode(mac_session_key); + } else { + status = "1"; + } + + if (kek_session_key != null && kek_session_key.length > 0) { + kekSessionKeyString = + com.netscape.cmsutil.util.Utils.SpecialEncode(kek_session_key); + } else { + status = "1"; + } + + if (serversideKeygen == true) { + if (serverSideValues.size() == 3) { + drm_trans_wrapped_desKeyString = serverSideValues.get(2); + kek_wrapped_desKeyString = serverSideValues.get(0); + keycheck_s = serverSideValues.get(1); + } + else { + status = "1"; + } + } + if (host_cryptogram != null && host_cryptogram.length > 0) { + cryptogram = + com.netscape.cmsutil.util.Utils.SpecialEncode(host_cryptogram); + } else { + if (status.equals("0") == true) { + status = "2"; + } + } + + if (selectedToken == null || keyNickName == null) { + // AC: Bugfix: Don't override status's value if an error was already flagged + if (status.equals("0") == true) { + status = "4"; + } } + + if (!sameCardCrypto) { + if (status.equals("0") == true) { + status = "5"; + } + } + + if (missingSetting_exception != null) { + status = "6"; + } + + if (missingParam) { + status = "3"; + } + + if (!status.equals("0")) { + + if (status.equals("1")) { + errorMsg = "Problem generating session key info."; + } + + if (status.equals("2")) { + errorMsg = "Problem creating host_cryptogram."; + } + + if (status.equals("5")) { + errorMsg = "Card cryptogram mismatch. Token likely has incorrect keys."; + } + + if (status.equals("4")) { + errorMsg = "Problem obtaining token information."; + } + + if (status.equals("6")) { + errorMsg = "Problem reading required configuration value."; + } + + if (status.equals("3")) { + if (badParams.endsWith(",")) { + badParams = badParams.substring(0, badParams.length() - 1); + } + errorMsg = "Missing input parameters :" + badParams; + } + + value = IRemoteRequest.RESPONSE_STATUS + "=" + status; + } else { + if (serversideKeygen == true) { + StringBuffer sb = new StringBuffer(); + sb.append(IRemoteRequest.RESPONSE_STATUS + "=0&"); + sb.append(IRemoteRequest.TKS_RESPONSE_MacSessionKey + "="); + sb.append(macSessionKeyString); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_HostCryptogram + "="); + sb.append(cryptogram); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_EncSessionKey + "="); + sb.append(encSessionKeyString); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_KekSessionKey + "="); + sb.append(kekSessionKeyString); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_KEK_DesKey + "="); + sb.append(kek_wrapped_desKeyString); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_KeyCheck + "="); + sb.append(keycheck_s); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey + "="); + sb.append(drm_trans_wrapped_desKeyString); + value = sb.toString(); + } else { + StringBuffer sb = new StringBuffer(); + sb.append(IRemoteRequest.RESPONSE_STATUS + "=0&"); + sb.append(IRemoteRequest.TKS_RESPONSE_MacSessionKey + "="); + sb.append(macSessionKeyString); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_HostCryptogram + "="); + sb.append(cryptogram); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_EncSessionKey + "="); + sb.append(encSessionKeyString); + sb.append("&" + IRemoteRequest.TKS_RESPONSE_KekSessionKey + "="); + value = sb.toString(); + } + + } + //CMS.debug(method + "outputString.encode " + value); + + try { + resp.setContentLength(value.length()); + CMS.debug("TokenServlet:outputString.length " + value.length()); + OutputStream ooss = resp.getOutputStream(); + ooss.write(value.getBytes()); + ooss.flush(); + mRenderResult = false; + } catch (IOException e) { + CMS.debug("TokenServlet: " + e.toString()); + } + + if (status.equals("0")) { + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.SUCCESS, // Outcome + status, // status + agentId, // AgentID + isCryptoValidate ? "true" : "false", // IsCryptoValidate + serversideKeygen ? "true" : "false", // IsServerSideKeygen + selectedToken, // SelectedToken + keyNickName, // KeyNickName + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, + logParams); + + } else { + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.FAILURE, // Outcome + status, // status + agentId, // AgentID + isCryptoValidate ? "true" : "false", // IsCryptoValidate + serversideKeygen ? "true" : "false", // IsServerSideKeygen + selectedToken, // SelectedToken + keyNickName, // KeyNickName + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion + errorMsg // Error + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE, + logParams); + + } + + audit(auditMessage); + } /** @@ -2479,4 +3042,197 @@ public class TokenServlet extends CMSServlet { } + //returns ArrayList of following values + // 0 : Kek wrapped des key + // 1 : keycheck value + // 2 : trans wrapped des key + private ArrayList calculateServerSideKeygenValues(String useSoftToken, String selectedToken, + SymmetricKey kekSessionKey, SecureChannelProtocol protocol) throws EBaseException { + + SymmetricKey desKey = null; + String method = "TokenServlet.calculateSErverSideKeygenValues: "; + ArrayList values = new ArrayList(); + + /** + * 0. generate des key + * 1. encrypt des key with kek key + * 2. encrypt des key with DRM transport key + * These two wrapped items are to be sent back to + * TPS. 2nd item is to DRM + **/ + CMS.debug(method + " entering..."); + + // (1) generate DES key + /* applet does not support DES3 + org.mozilla.jss.crypto.KeyGenerator kg = + internalToken.getKeyGenerator(KeyGenAlgorithm.DES3); + desKey = kg.generate();*/ + + /* + * GenerateSymkey firt generates a 16 byte DES2 key. + * It then pads it into a 24 byte key with last + * 8 bytes copied from the 1st 8 bytes. Effectively + * making it a 24 byte DES2 key. We need this for + * wrapping private keys on DRM. + */ + /*generate it on whichever token the master key is at*/ + + if (useSoftToken.equals("true")) { + CMS.debug(method + " key encryption key generated on internal"); + desKey = protocol.generateSymKey("internal"); + //cfu audit here? sym key gen done + } else { + CMS.debug("TokenServlet: key encryption key generated on " + selectedToken); + desKey = protocol.generateSymKey(selectedToken); + } + if (desKey == null) { + throw new EBaseException(method + "can't generate key encryption key"); + } + + /* + * ECBencrypt actually takes the 24 byte DES2 key + * and discard the last 8 bytes before it encrypts. + * This is done so that the applet can digest it + */ + + + // protocol.wrapSessionKey(tokenName, sessionKey, wrappingKey) + + byte[] encDesKey = protocol.ecbEncrypt(kekSessionKey, desKey, selectedToken); + + String kek_wrapped_desKeyString = + com.netscape.cmsutil.util.Utils.SpecialEncode(encDesKey); + + CMS.debug(method + "kek_wrapped_desKeyString: " + kek_wrapped_desKeyString); + + values.add(kek_wrapped_desKeyString); + + // get keycheck + + byte[] keycheck = null; + + keycheck = protocol.computeKeyCheck(desKey, selectedToken); + + String keycheck_s = + com.netscape.cmsutil.util.Utils.SpecialEncode(keycheck); + + CMS.debug(method + "keycheck_s " + keycheck_s); + + values.add(keycheck_s); + + //use DRM transport cert to wrap desKey + String drmTransNickname = CMS.getConfigStore().getString("tks.drm_transport_cert_nickname", ""); + + if ((drmTransNickname == null) || (drmTransNickname == "")) { + CMS.debug(method + " did not find DRM transport certificate nickname"); + throw new EBaseException(method + "can't find DRM transport certificate nickname"); + } else { + CMS.debug(method + " drmtransport_cert_nickname=" + drmTransNickname); + } + + X509Certificate drmTransCert = null; + try { + + drmTransCert = CryptoManager.getInstance().findCertByNickname(drmTransNickname); + // wrap kek session key with DRM transport public key + CryptoToken token = null; + if (useSoftToken.equals("true")) { + //token = CryptoManager.getInstance().getTokenByName(selectedToken); + token = CryptoManager.getInstance().getInternalCryptoToken(); + } else { + token = CryptoManager.getInstance().getTokenByName(selectedToken); + } + PublicKey pubKey = drmTransCert.getPublicKey(); + String pubKeyAlgo = pubKey.getAlgorithm(); + CMS.debug("Transport Cert Key Algorithm: " + pubKeyAlgo); + KeyWrapper keyWrapper = null; + //For wrapping symmetric keys don't need IV, use ECB + if (pubKeyAlgo.equals("EC")) { + keyWrapper = token.getKeyWrapper(KeyWrapAlgorithm.AES_ECB); + keyWrapper.initWrap(pubKey, null); + } else { + keyWrapper = token.getKeyWrapper(KeyWrapAlgorithm.RSA); + keyWrapper.initWrap(pubKey, null); + } + CMS.debug("desKey token " + desKey.getOwningToken().getName() + " token: " + token.getName()); + byte[] drm_trans_wrapped_desKey = keyWrapper.wrap(desKey); + + String drmWrappedDesStr = + com.netscape.cmsutil.util.Utils.SpecialEncode(drm_trans_wrapped_desKey); + + CMS.debug(method + " drmWrappedDesStr: " + drmWrappedDesStr); + values.add(drmWrappedDesStr); + + } catch (Exception e) { + throw new EBaseException(e); + } + + return values; + } + + private boolean cryptoGramsAreEqual(byte[] original_cryptogram, byte[] calculated_cryptogram) { + boolean sameCardCrypto = true; + + if (original_cryptogram == null || calculated_cryptogram == null) { + return false; + } + if (original_cryptogram.length == calculated_cryptogram.length) { + for (int i = 0; i < original_cryptogram.length; i++) { + if (original_cryptogram[i] != calculated_cryptogram[i]) { + sameCardCrypto = false; + break; + } + } + } else { + // different length; must be different + sameCardCrypto = false; + } + + return sameCardCrypto; + } + + //For now only used for scp03 + + static GPParams readGPSettings(String keySet) { + GPParams params = new GPParams(); + + String method = "TokenServlet.readGPSettings: "; + String gp3Settings = "tks." + keySet + ".prot3"; + + String divers = "emv"; + try { + divers = CMS.getConfigStore().getString(gp3Settings + ".divers", "emv"); + } catch (EBaseException e) { + } + + params.setDiversificationScheme(divers); + + CMS.debug(method + " Divers: " + divers); + + String diversVer1Keys = "emv"; + + try { + diversVer1Keys = CMS.getConfigStore().getString(gp3Settings + ".diversVer1Keys","emv"); + } catch (EBaseException e) { + } + + params.setVersion1DiversificationScheme(diversVer1Keys); + CMS.debug(method + " Version 1 keys Divers: " + divers); + + return params; + } + + private byte[] getDeveKeyArray(String keyType,IConfigStore sconfig,String keySet) throws EBaseException { + byte devKeyArray[] = null; + try { + devKeyArray = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + + keySet + "." + keyType)); + } catch (Exception e) { + throw new EBaseException("Can't read static developer key array: " + keySet + ": " + keyType); + } + + return devKeyArray; + } + + } diff --git a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java index 233124968..82482d09c 100644 --- a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java +++ b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java @@ -53,6 +53,8 @@ public interface IRemoteRequest { /* computeSessionKey responses */ public static final String TKS_RESPONSE_SessionKey = "sessionKey"; public static final String TKS_RESPONSE_EncSessionKey = "encSessionKey"; + public static final String TKS_RESPONSE_MacSessionKey = "macSessionKey"; + public static final String TKS_RESPONSE_KekSessionKey = "kekSessionKey"; public static final String TKS_RESPONSE_KEK_DesKey = "kek_wrapped_desKey"; public static final String TKS_RESPONSE_DRM_Trans_DesKey = "drm_trans_desKey"; public static final String TKS_RESPONSE_KeyCheck = "keycheck"; -- cgit From 806d5ed6cc2e16c5d5ad06530d06a98b4ee68bb1 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Wed, 15 Mar 2017 18:41:52 +0100 Subject: Added exception chaining for EInvalidCredentials. A new constructor has been added into EInvalidCredentials to support exception chaining. --- .../netscape/cms/authentication/AgentCertAuthentication.java | 6 +++--- .../cms/authentication/SSLclientCertAuthentication.java | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'base/server/cms/src') diff --git a/base/server/cms/src/com/netscape/cms/authentication/AgentCertAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/AgentCertAuthentication.java index c65dd3971..e7f50fbc2 100644 --- a/base/server/cms/src/com/netscape/cms/authentication/AgentCertAuthentication.java +++ b/base/server/cms/src/com/netscape/cms/authentication/AgentCertAuthentication.java @@ -22,8 +22,6 @@ import java.security.cert.X509Certificate; import java.util.Enumeration; import java.util.Locale; -import netscape.security.x509.X509CertImpl; - import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.authentication.AuthToken; import com.netscape.certsrv.authentication.EInvalidCredentials; @@ -46,6 +44,8 @@ import com.netscape.certsrv.usrgrp.ICertUserLocator; import com.netscape.certsrv.usrgrp.IUGSubsystem; import com.netscape.certsrv.usrgrp.IUser; +import netscape.security.x509.X509CertImpl; + /** * Certificate server agent authentication. * Maps a SSL client authenticate certificate to a user (agent) entry in the @@ -196,7 +196,7 @@ public class AgentCertAuthentication implements IAuthManager, try { user = mCULocator.locateUser(certs); } catch (EUsrGrpException e) { - throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"), e); } catch (netscape.ldap.LDAPException e) { throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString())); diff --git a/base/server/cms/src/com/netscape/cms/authentication/SSLclientCertAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/SSLclientCertAuthentication.java index 2bec1b68d..a9b0ccc77 100644 --- a/base/server/cms/src/com/netscape/cms/authentication/SSLclientCertAuthentication.java +++ b/base/server/cms/src/com/netscape/cms/authentication/SSLclientCertAuthentication.java @@ -24,9 +24,6 @@ import java.util.Enumeration; import java.util.Locale; import java.util.StringTokenizer; -import netscape.security.x509.BasicConstraintsExtension; -import netscape.security.x509.X509CertImpl; - import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.authentication.AuthToken; import com.netscape.certsrv.authentication.EInvalidCredentials; @@ -45,6 +42,9 @@ import com.netscape.certsrv.property.IDescriptor; import com.netscape.certsrv.request.IRequest; import com.netscape.certsrv.usrgrp.Certificates; +import netscape.security.x509.BasicConstraintsExtension; +import netscape.security.x509.X509CertImpl; + /** * Certificate server SSL client authentication. * @@ -189,7 +189,7 @@ public class SSLclientCertAuthentication implements IAuthManager, } catch (Exception e) { CMS.debug("SSLclientCertAuthentication: authenticate: exception:" + e.toString()); - throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"), e); } } } @@ -199,7 +199,7 @@ public class SSLclientCertAuthentication implements IAuthManager, } } catch (CertificateException e) { CMS.debug(e.toString()); - throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"), e); } // check if certificate(s) is revoked -- cgit