From a7a83f2fdcc1647611bf9cf09e75ea434b546b5d Mon Sep 17 00:00:00 2001
From: david_page <david_page@localhost>
Date: Mon, 17 Sep 2007 17:31:38 +0000
Subject: [PATCH] Add support for MAC key entry type. Similar to Cipher key entry; however, caller must maintain key identifier (string), e.g., in backup directory, in order to verify signature. TODO: investigate prefixing MAC signed data with key identifier, and suffixing with signature, for both byte[] and stream. This enhancement will require wrapping the Mac API.
---
opends/src/server/org/opends/server/extensions/ConfigFileHandler.java | 22
opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java | 314 +++-----
opends/src/server/org/opends/server/backends/jeb/BackupManager.java | 20
opends/src/server/org/opends/server/backends/SchemaBackend.java | 22
opends/src/server/org/opends/server/types/CryptoManager.java | 1665 +++++++++++++++++++++++++++++------------------
opends/src/server/org/opends/server/util/ServerConstants.java | 7
6 files changed, 1,181 insertions(+), 869 deletions(-)
diff --git a/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opends/src/server/org/opends/server/backends/SchemaBackend.java
index 2b2a457..46ae527 100644
--- a/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -4204,18 +4204,18 @@
Mac mac = null;
MessageDigest digest = null;
String digestAlgorithm = null;
- String macAlgorithm = null;
+ String macKeyID = null;
if (hash)
{
if (signHash)
{
- macAlgorithm = cryptoManager.getPreferredMACAlgorithm();
- backupProperties.put(BACKUP_PROPERTY_MAC_ALGORITHM, macAlgorithm);
-
try
{
- mac = cryptoManager.getPreferredMACProvider();
+ macKeyID = cryptoManager.getMacEngineKeyEntryID();
+ backupProperties.put(BACKUP_PROPERTY_MAC_KEY_ID, macKeyID);
+
+ mac = cryptoManager.getMacEngine(macKeyID);
}
catch (Exception e)
{
@@ -4225,7 +4225,7 @@
}
Message message = ERR_SCHEMA_BACKUP_CANNOT_GET_MAC.get(
- macAlgorithm, stackTraceToSingleLineString(e));
+ macKeyID, stackTraceToSingleLineString(e));
throw new DirectoryException(
DirectoryServer.getServerErrorResultCode(), message,
e);
@@ -4653,9 +4653,9 @@
Mac mac = null;
if (signedHash != null)
{
- String macAlgorithm =
- backupInfo.getBackupProperty(BACKUP_PROPERTY_MAC_ALGORITHM);
- if (macAlgorithm == null)
+ String macKeyID =
+ backupInfo.getBackupProperty(BACKUP_PROPERTY_MAC_KEY_ID);
+ if (macKeyID == null)
{
Message message = ERR_SCHEMA_RESTORE_UNKNOWN_MAC.get(backupID);
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
@@ -4664,12 +4664,12 @@
try
{
- mac = DirectoryServer.getCryptoManager().getMACProvider(macAlgorithm);
+ mac = DirectoryServer.getCryptoManager().getMacEngine(macKeyID);
}
catch (Exception e)
{
Message message = ERR_SCHEMA_RESTORE_CANNOT_GET_MAC.get(
- backupID, macAlgorithm);
+ backupID, macKeyID);
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
message, e);
}
diff --git a/opends/src/server/org/opends/server/backends/jeb/BackupManager.java b/opends/src/server/org/opends/server/backends/jeb/BackupManager.java
index c2eca97..9b8f9c7 100644
--- a/opends/src/server/org/opends/server/backends/jeb/BackupManager.java
+++ b/opends/src/server/org/opends/server/backends/jeb/BackupManager.java
@@ -168,18 +168,18 @@
Mac mac = null;
MessageDigest digest = null;
String digestAlgorithm = null;
- String macAlgorithm = null;
+ String macKeyID = null;
if (hash)
{
if (signHash)
{
- macAlgorithm = cryptoManager.getPreferredMACAlgorithm();
- backupProperties.put(BACKUP_PROPERTY_MAC_ALGORITHM, macAlgorithm);
-
try
{
- mac = cryptoManager.getPreferredMACProvider();
+ macKeyID = cryptoManager.getMacEngineKeyEntryID();
+ backupProperties.put(BACKUP_PROPERTY_MAC_KEY_ID, macKeyID);
+
+ mac = cryptoManager.getMacEngine(macKeyID);
}
catch (Exception e)
{
@@ -189,7 +189,7 @@
}
Message message = ERR_JEB_BACKUP_CANNOT_GET_MAC.get(
- macAlgorithm, stackTraceToSingleLineString(e));
+ macKeyID, stackTraceToSingleLineString(e));
throw new DirectoryException(
DirectoryServer.getServerErrorResultCode(), message, e);
}
@@ -924,15 +924,15 @@
Mac mac = null;
MessageDigest digest = null;
String digestAlgorithm = null;
- String macAlgorithm = null;
+ String macKeyID = null;
if (signHash != null)
{
- macAlgorithm = backupProperties.get(BACKUP_PROPERTY_MAC_ALGORITHM);
+ macKeyID = backupProperties.get(BACKUP_PROPERTY_MAC_KEY_ID);
try
{
- mac = cryptoManager.getMACProvider(macAlgorithm);
+ mac = cryptoManager.getMacEngine(macKeyID);
}
catch (Exception e)
{
@@ -942,7 +942,7 @@
}
Message message = ERR_JEB_BACKUP_CANNOT_GET_MAC.get(
- macAlgorithm, stackTraceToSingleLineString(e));
+ macKeyID, stackTraceToSingleLineString(e));
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
message, e);
}
diff --git a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index 53f47d4..9cd1262 100644
--- a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -2693,18 +2693,18 @@
Mac mac = null;
MessageDigest digest = null;
String digestAlgorithm = null;
- String macAlgorithm = null;
+ String macKeyID = null;
if (hash)
{
if (signHash)
{
- macAlgorithm = cryptoManager.getPreferredMACAlgorithm();
- backupProperties.put(BACKUP_PROPERTY_MAC_ALGORITHM, macAlgorithm);
-
try
{
- mac = cryptoManager.getPreferredMACProvider();
+ macKeyID = cryptoManager.getMacEngineKeyEntryID();
+ backupProperties.put(BACKUP_PROPERTY_MAC_KEY_ID, macKeyID);
+
+ mac = cryptoManager.getMacEngine(macKeyID);
}
catch (Exception e)
{
@@ -2714,7 +2714,7 @@
}
Message message = ERR_CONFIG_BACKUP_CANNOT_GET_MAC.get(
- macAlgorithm, stackTraceToSingleLineString(e));
+ macKeyID, stackTraceToSingleLineString(e));
throw new DirectoryException(
DirectoryServer.getServerErrorResultCode(), message,
e);
@@ -3183,9 +3183,9 @@
Mac mac = null;
if (signedHash != null)
{
- String macAlgorithm =
- backupInfo.getBackupProperty(BACKUP_PROPERTY_MAC_ALGORITHM);
- if (macAlgorithm == null)
+ String macKeyID =
+ backupInfo.getBackupProperty(BACKUP_PROPERTY_MAC_KEY_ID);
+ if (macKeyID == null)
{
Message message = ERR_CONFIG_RESTORE_UNKNOWN_MAC.get(backupID);
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
@@ -3194,12 +3194,12 @@
try
{
- mac = DirectoryServer.getCryptoManager().getMACProvider(macAlgorithm);
+ mac = DirectoryServer.getCryptoManager().getMacEngine(macKeyID);
}
catch (Exception e)
{
Message message = ERR_CONFIG_RESTORE_CANNOT_GET_MAC.get(
- backupID, macAlgorithm);
+ backupID, macKeyID);
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
message, e);
}
diff --git a/opends/src/server/org/opends/server/types/CryptoManager.java b/opends/src/server/org/opends/server/types/CryptoManager.java
index 786cf44..14b3e40 100644
--- a/opends/src/server/org/opends/server/types/CryptoManager.java
+++ b/opends/src/server/org/opends/server/types/CryptoManager.java
@@ -93,21 +93,28 @@
private static final Random pseudoRandom
= new Random(secureRandom.nextLong());
- // The map from encryption key ID to SecretKeyEntry (cache).
- private final HashMap<ByteArray, SecretKeyEntry> secretKeyEntryMap
- = new HashMap<ByteArray, SecretKeyEntry>();
+ // The preferred message digest algorithm for the Directory Server.
+ private final String preferredDigestAlgorithm;
+
+ // The map from encryption key ID to MacKeyEntry (cache).
+ private final HashMap<KeyEntryID, MacKeyEntry> macKeyEntryCache
+ = new HashMap<KeyEntryID, MacKeyEntry>();
+
+ // The preferred MAC algorithm for the Directory Server.
+ private final String preferredMACAlgorithm;
+
+ // The preferred key length for the preferred MAC algorithm.
+ private final int preferredMACAlgorithmKeyLengthBits;
+
+ // The map from encryption key ID to CipherKeyEntry (cache).
+ private final HashMap<KeyEntryID, CipherKeyEntry>
+ cipherKeyEntryCache = new HashMap<KeyEntryID, CipherKeyEntry>();
// The preferred cipher for the Directory Server.
private final String preferredCipherTransformation;
// The preferred key length for the preferred cipher.
- private final int preferredCipherTransformationKeyLength;
-
- // The preferred message digest algorithm for the Directory Server.
- private final String preferredDigestAlgorithm;
-
- // The preferred MAC algorithm for the Directory Server.
- private final String preferredMACAlgorithm;
+ private final int preferredCipherTransformationKeyLengthBits;
// The name of the local certificate to use for SSL.
private final String sslCertNickname;
@@ -142,10 +149,11 @@
{
// TODO -- Get the defaults from the configuration rather than
// hard-coding them.
+ preferredCipherTransformation = "AES/CBC/PKCS5Padding";
+ preferredCipherTransformationKeyLengthBits = 128;
preferredDigestAlgorithm = "SHA-1";
preferredMACAlgorithm = "HmacSHA1";
- preferredCipherTransformation = "AES/CBC/PKCS5Padding";
- preferredCipherTransformationKeyLength = 128;
+ preferredMACAlgorithmKeyLengthBits = 128;
sslCertNickname = cfg.getSSLCertNickname();
sslEncryption = cfg.isSSLEncryption();
@@ -173,11 +181,9 @@
try
{
- // TODO: wrap like encryption
- byte[] keyValue = new byte[16];
- secureRandom.nextBytes(keyValue);
- Mac mac = Mac.getInstance(preferredMACAlgorithm);
- mac.init(new SecretKeySpec(keyValue, preferredMACAlgorithm));
+ MacKeyEntry.generateKeyEntry(null,
+ preferredMACAlgorithm,
+ preferredMACAlgorithmKeyLengthBits);
}
catch (Exception e)
{
@@ -194,9 +200,9 @@
try
{
- SecretKeyEntry.generateKeyEntry(null,
+ CipherKeyEntry.generateKeyEntry(null,
preferredCipherTransformation,
- preferredCipherTransformationKeyLength);
+ preferredCipherTransformationKeyLengthBits);
}
catch (Exception e)
{
@@ -214,57 +220,6 @@
/**
- * Converts a UUID string into a compact byte[16] representation.
- *
- * @param uuidString A string reprentation of a UUID.
- *
- * @return A new byte[16] containing the binary representation of
- * the UUID.
- *
- * @throws CryptoManagerException If the uuidString argument does
- * not conform to the UUID string syntax.
- */
- private static byte[] uuidToBytes(String uuidString)
- throws CryptoManagerException
- {
- UUID uuid;
- try {
- uuid = UUID.fromString(uuidString);
- }
- catch (Exception ex) {
- throw new CryptoManagerException(
- // TODO: i18n
- Message.raw("Invalid string representation of a UUID."),
- ex);
- }
- return uuidToBytes(uuid);
- }
-
-
- /**
- * Converts a UUID instance into a compact byte[16] representation.
- *
- * @param uuid A UUID instance.
- *
- * @return A new byte[16] containing the binary representation of
- * the UUID.
- */
- private static byte[] uuidToBytes(UUID uuid)
- {
- final byte[] uuidBytes = new byte[16];
- long hiBytes = uuid.getMostSignificantBits();
- long loBytes = uuid.getLeastSignificantBits();
- for (int i = 7; i >= 0; --i) {
- uuidBytes[i] = (byte)hiBytes;
- hiBytes >>>= 8;
- uuidBytes[8 + i] = (byte)loBytes;
- loBytes >>>= 8;
- }
- return uuidBytes;
- }
-
-
- /**
* Retrieves the name of the preferred message digest algorithm.
*
* @return The name of the preferred message digest algorithm
@@ -444,188 +399,134 @@
/**
- * Retrieves the name of the preferred MAC algorithm.
+ * For the current preferred MAC algorithm and key length, return
+ * the identifier of the corresponding key entry. Note: the result
+ * (key identifier) might change across invocations, due to either
+ * of the perferred parameters changing, or because the original
+ * key was marked compromised and a replacement key generated.
*
- * @return The name of the preferred MAC algorithm
+ * @return A String representation of the identifier of a key entry
+ * corresponding to the preferred MAC algorithm and key length.
+ *
+ * @throws CryptoManagerException In case one or more of the key
+ * parameters is invalid, or there is a problem instantiating the
+ * key entry in case it does not already exist.
*/
- public String getPreferredMACAlgorithm()
+ public String getMacEngineKeyEntryID()
+ throws CryptoManagerException
{
- return preferredMACAlgorithm;
+ return getMacEngineKeyEntryID(preferredMACAlgorithm,
+ preferredMACAlgorithmKeyLengthBits);
}
-
/**
- * Retrieves a MAC provider using the preferred algorithm.
+ * For the specified MAC algorithm and key length, return
+ * the identifier of the corresponding key entry. Note: the result
+ * (key identifier) might change across invocations, due to either
+ * of the perferred parameters changing, or because the original
+ * key was marked compromised and a replacement key generated.
*
- * @return A MAC provider using the preferred algorithm.
+ * @param macAlgorithm The algorithm to use for the MAC engine.
*
- * @throws NoSuchAlgorithmException If the requested algorithm is
- * not supported or is
- * unavailable.
+ * @param keyLengthBits The key length in bits to use with the
+ * specified algorithm.
*
- * @throws InvalidKeyException If the provided key is not
- * appropriate for use with the
- * requested MAC algorithm.
+ * @return A String representation of the identifier of a key entry
+ * corresponding to the specified MAC algorithm and key length.
+ *
+ * @throws CryptoManagerException In case one or more of the key
+ * parameters is invalid, or there is a problem instantiating the
+ * key entry in case it does not already exist.
*/
- public Mac getPreferredMACProvider()
- throws NoSuchAlgorithmException, InvalidKeyException
- {
- return getMACProvider(getPreferredMACAlgorithm());
+ public String getMacEngineKeyEntryID(final String macAlgorithm,
+ final int keyLengthBits)
+ throws CryptoManagerException {
+ Validator.ensureNotNull(macAlgorithm);
+
+ MacKeyEntry keyEntry = MacKeyEntry.getKeyEntry(this, macAlgorithm,
+ keyLengthBits);
+ if (null == keyEntry) {
+ keyEntry = MacKeyEntry.generateKeyEntry(this, macAlgorithm,
+ keyLengthBits);
+ }
+
+ return keyEntry.getKeyID().getStringValue();
}
+ /**
+ * For the specified key entry identifier, instantiate a MAC engine.
+ *
+ * @param keyEntryID The identifier of the key entry containing the
+ * desired MAC algorithm name and key length.
+ *
+ * @return The MAC engine instantiated with the parameters from the
+ * referenced key entry, or null if no such entry exists.
+ *
+ * @throws CryptoManagerException In case the key entry identifier
+ * is invalid or there is a problem instatiating the MAC engine from
+ * the parameters in the referenced key entry.
+ */
+ public Mac getMacEngine(String keyEntryID)
+ throws CryptoManagerException
+ {
+ MacKeyEntry keyEntry;
+ try {
+ keyEntry = MacKeyEntry.getKeyEntry(this,
+ new KeyEntryID(keyEntryID));
+ }
+ catch (IllegalArgumentException ex) {
+ throw new CryptoManagerException(
+ // TODO: i18n
+ Message.raw("MAC key entry identifier \"%s\" is not" +
+ " a valid UUID.", keyEntryID), ex);
+ }
+
+ if (null == keyEntry) return null;
+
+ return getMacEngine(keyEntry);
+ }
+
/**
- * Retrieves a MAC provider using the specified algorithm.
+ * This method produces an initialized MAC engine based on the
+ * supplied MacKeyEntry's state.
*
- * @param macAlgorithm The algorithm to use for the MAC provider.
+ * @param keyEntry The MacKeyEntry specifying the Mac properties.
*
- * @return A MAC provider using the specified algorithm.
+ * @return An initialized Mac object.
*
- * @throws NoSuchAlgorithmException If the requested algorithm is
- * not supported or is
- * unavailable.
- *
- * @throws InvalidKeyException If the provided key is not
- * appropriate for use with the
- * requested MAC algorithm.
+ * @throws CryptoManagerException In case there was a error
+ * instantiating the Mac object.
*/
- public Mac getMACProvider(String macAlgorithm)
- throws NoSuchAlgorithmException, InvalidKeyException
+ private static Mac getMacEngine(MacKeyEntry keyEntry)
+ throws CryptoManagerException
{
- Mac mac = Mac.getInstance(macAlgorithm);
- byte[] keyValue = new byte[16];
- secureRandom.nextBytes(keyValue);
- mac.init(new SecretKeySpec(keyValue, macAlgorithm));
+ Mac mac;
+ try {
+ mac = Mac.getInstance(keyEntry.getType());
+ }
+ catch (NoSuchAlgorithmException ex){
+ throw new CryptoManagerException(
+ // TODO: i18n
+ Message.raw("Invalid MAC algorithm specified: + %s",
+ keyEntry.getType()), ex);
+ }
+
+ try {
+ mac.init(keyEntry.getKeySpec());
+ }
+ catch (InvalidKeyException ex) {
+ throw new CryptoManagerException(
+ // TODO: i18n
+ Message.raw("Invalid key specification supplied to" +
+ " Mac object initialization"), ex);
+ }
return mac;
}
-
-
- /**
- * Retrieves a byte array containing a MAC based on the provided
- * data, using the preferred MAC algorithm.
- *
- * @param data The data for which to generate the MAC.
- *
- * @return A byte array containing the generated MAC.
- *
- * @throws NoSuchAlgorithmException If the requested algorithm is
- * not supported or is
- * unavailable.
- */
- public byte[] mac(byte[] data)
- throws NoSuchAlgorithmException
- {
- return Mac.getInstance(preferredMACAlgorithm).doFinal(data);
- }
-
-
-
- /**
- * Retrieves a byte array containing a MAC based on the provided
- * data, using the requested MAC algorithm.
- *
- * @param macAlgorithm The algorithm to use for the MAC.
- * @param data The data for which to generate the MAC.
- *
- * @return A byte array containing the generated MAC.
- *
- * @throws NoSuchAlgorithmException If the requested algorithm is
- * not supported or is
- * unavailable.
- */
- public byte[] mac(String macAlgorithm, byte[] data)
- throws NoSuchAlgorithmException
- {
- return Mac.getInstance(macAlgorithm).doFinal(data);
- }
-
-
-
- /**
- * Retrieves a byte array containing a MAC based on the data read
- * from the provided input stream, using the preferred MAC
- * algorithm. Data will be read until the end of the stream is
- * reached.
- *
- * @param inputStream The input stream from which the data is to
- * be read.
- *
- * @return A byte array containing the generated MAC.
- *
- * @throws IOException If a problem occurs while reading data from
- * the provided stream.
- *
- * @throws NoSuchAlgorithmException If the requested algorithm is
- * not supported or is
- * unavailable.
- */
- public byte[] mac(InputStream inputStream)
- throws IOException, NoSuchAlgorithmException
- {
- Mac mac = Mac.getInstance(preferredMACAlgorithm);
-
- byte[] buffer = new byte[8192];
- while (true)
- {
- int bytesRead = inputStream.read(buffer);
- if (bytesRead < 0)
- {
- break;
- }
-
- mac.update(buffer, 0, bytesRead);
- }
-
- return mac.doFinal();
- }
-
-
-
- /**
- * Retrieves a byte array containing a MAC based on the data read
- * from the provided input stream, using the preferred MAC
- * algorithm. Data will be read until the end of the stream is
- * reached.
- *
- * @param macAlgorithm The algorithm to use for the MAC.
- * @param inputStream The input stream from which the data is to
- * be read.
- *
- * @return A byte array containing the generated MAC.
- *
- * @throws IOException If a problem occurs while reading data from
- * the provided stream.
- *
- * @throws NoSuchAlgorithmException If the requested algorithm is
- * not supported or is
- * unavailable.
- */
- public byte[] mac(String macAlgorithm, InputStream inputStream)
- throws IOException, NoSuchAlgorithmException
- {
- Mac mac = Mac.getInstance(macAlgorithm);
-
- byte[] buffer = new byte[8192];
- while (true)
- {
- int bytesRead = inputStream.read(buffer);
- if (bytesRead < 0)
- {
- break;
- }
-
- mac.update(buffer, 0, bytesRead);
- }
-
- return mac.doFinal();
- }
-
-
-
/**
* Retrieves the name of the preferred cipher algorithm.
*
@@ -638,376 +539,8 @@
/**
- * This class corresponds to the encryption key entry in ADS. It is
- * used in the local cache of key entries that have been requested
- * by CryptoManager clients.
- */
- private static class SecretKeyEntry
- {
- /**
- * This method generates a key according to the key parameters,
- * and creates a key entry and registers it in the supplied map.
- *
- * @param map The SecretKeyEntry Map in which the key is to be
- * stored. Pass null as the argument to this parameter in order to
- * validate a proposed cipher transformation and key length.
- *
- * @param transformation The cipher transformation for which the
- * key is to be produced.
- *
- * @param keyLengthBits The cipher key length in bits.
- *
- * @return The key entry corresponding to the parameters.
- *
- * @throws CryptoManagerException If there is a problem
- * instantiating a Cipher object in order to validate the supplied
- * parameters when creating a new entry.
- */
- public static SecretKeyEntry generateKeyEntry(
- final Map<ByteArray, SecretKeyEntry> map,
- final String transformation,
- final int keyLengthBits)
- throws CryptoManagerException {
-
- SecretKeyEntry keyEntry;
-
- // check for existing uncompromised key.
- if (null != map) {
- keyEntry = getKeyEntry(map, transformation, keyLengthBits);
- if (null != keyEntry) {
- return keyEntry;
- }
- }
-
- // generate a new key
- if (0 != keyLengthBits % Byte.SIZE) {
- throw new CryptoManagerException(
- // TODO: i18n
- Message.raw("keyLengthBits argument must be evenly"
- + " divisible by %d.", Byte.SIZE));
- }
-
- // In case a transformation is supplied instead of an algorithm:
- // E.g., AES/CBC/PKCS5Padding -> AES
- final int separatorIndex = transformation.indexOf('/');
- final String keyAlgorithm = (0 < separatorIndex)
- ? transformation.substring(0, separatorIndex)
- : transformation;
- KeyGenerator keyGen;
- try {
- keyGen = KeyGenerator.getInstance(keyAlgorithm);
- }
- catch (NoSuchAlgorithmException ex) {
- throw new CryptoManagerException(
- // TODO: i18n
- Message.raw("Unable to produce key generator from" +
- " cipher transformation argument %s",
- transformation), ex);
- }
- keyGen.init(keyLengthBits, secureRandom);
- final byte[] key = keyGen.generateKey().getEncoded();
-
- // generate the key entry
- final byte[] keyID = uuidToBytes(UUID.randomUUID());
- keyEntry = new SecretKeyEntry(keyID, transformation,
- keyAlgorithm, key, /* compute IV length */ -1, false);
-
- // Validate the entry. Pass the keyAlgorithm for the cipher
- // transformation.
- final Cipher cipher
- = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
- final byte[] iv = cipher.getIV();
- keyEntry.setIVLengthBits(
- (null == iv) ? 0 : iv.length * Byte.SIZE);
-
- if (null != map) {
- map.put(new ByteArray(keyID), keyEntry);
- // TODO: publish key to ADS. (mark key "blocked" in map
- // until registered?)
- }
-
- return keyEntry;
- }
-
-
- /**
- * Initializes a secret key entry from the supplied parameters,
- * validates it, and registers it in the supplied map. The
- * anticipated use of this method is to import a key entry from
- * ADS.
- *
- * @param map The secret key map.
- *
- * @param keyID The key identifier.
- *
- * @param transformation The cipher transformation for which the
- * key entry was produced.
- *
- * @param keyAlgorithm The cipher algorithm for which the key was
- * produced.
- *
- * @param key The cipher key.
- *
- * @param ivLengthBits The length of the initialization vector,
- * which will be zero in the case of any stream cipher algorithm,
- * or any block cipher algorithm for which the transformation mode
- * does not use an initialization vector.
- *
- * @param isCompromised Mark the key as compromised, so that it
- * will not subsequently be used for encryption. The key must be
- * maintained in order to decrypt existing ciphertext.
- *
- * @return The key entry, if one was successfully produced.
- *
- * @throws CryptoManagerException In case of an error in the
- * parameters used to initialize or validate the key entry.
- */
- public static SecretKeyEntry setKeyEntry(
- final Map<ByteArray, SecretKeyEntry> map,
- final byte[] keyID,
- final String transformation,
- final String keyAlgorithm,
- final byte[] key,
- final int ivLengthBits,
- final boolean isCompromised)
- throws CryptoManagerException {
- Validator.ensureNotNull(keyID, transformation, keyAlgorithm,
- key);
- Validator.ensureTrue(16 == keyID.length);
- Validator.ensureTrue(0 <= ivLengthBits);
-
- // Check map for existing key entry with the supplied keyID.
- SecretKeyEntry keyEntry = getKeyEntry(map, keyID);
- if (null != keyEntry) {
- // TODO: compare keyEntry with supplied parameters to ensure
- // equal.
- return keyEntry;
- }
-
- // Instantiate new entry.
- keyEntry = new SecretKeyEntry(keyID, transformation,
- keyAlgorithm, key, ivLengthBits, isCompromised);
-
- // Validate the entry. Pass the keyAlgorithm for the cipher
- // transformation.
- byte[] iv = null;
- if (0 < ivLengthBits) {
- iv = new byte[ivLengthBits * Byte.SIZE];
- pseudoRandom.nextBytes(iv);
- }
- getCipher(keyEntry, Cipher.DECRYPT_MODE, iv);
-
- map.put(new ByteArray(keyID), keyEntry);
-
- return keyEntry;
- }
-
-
- /**
- * Retrieve a SecretKeyEntry from the SecretKeyEntry Map based on
- * cipher algorithm name and key length.
- *
- * @param map The SecretKeyEntry Map in which the key is stored.
- *
- * @param transformation The cipher algorithm for which the key
- * was produced.
- *
- * @param keyLengthBits The cipher key length in bits.
- *
- * @return The key entry corresponding to the parameters, or null
- * if no such entry exists.
- */
- public static SecretKeyEntry getKeyEntry(
- final Map<ByteArray, SecretKeyEntry> map,
- final String transformation,
- final int keyLengthBits) {
- Validator.ensureNotNull(map, transformation);
- Validator.ensureTrue(0 < keyLengthBits);
-
- SecretKeyEntry keyEntry = null;
-
- // search for an existing key that satisfies the request
- for (Map.Entry<ByteArray, SecretKeyEntry> i: map.entrySet()) {
- SecretKeyEntry entry = i.getValue();
- if (! entry.fIsCompromised
- && entry.getTransformation().equals(transformation)
- && entry.fKeyLengthBits == keyLengthBits) {
- assert Arrays.equals(i.getKey().array(), entry.fKeyID);
- keyEntry = entry;
- break;
- }
- }
-
- // TODO: if (null == keyEntry) Does ADS monitoring thread keep
- // map updated with keys produced at other sites? Otherwise,
- // search ADS for suitable key.
-
- // TODO: if (null == keyEntry) consider generating key here.
-
- if (null != keyEntry) {
- Validator.ensureTrue(0 <= keyEntry.getIVLength(),
- "SecretKeyEntry initialization is not complete.");
- }
-
- return keyEntry;
- }
-
-
- /**
- * Given a key identifier, return the associated secret key entry
- * from the supplied map. This method would typically be used by
- * a decryption routine.
- *
- * @param map The local cache of key entries.
- *
- * @param keyID The key identifier.
- *
- * @return The key entry associated with the key identifier.
- */
- public static SecretKeyEntry getKeyEntry(
- Map<ByteArray, SecretKeyEntry> map, byte[] keyID) {
- return map.get(new ByteArray(keyID));
- /* TODO: Does ADS monitorying thread keep map updated with keys
- produced at other sites? If not, fetch from ADS and update
- map (assuming a legitimate key ID, the key should exist in
- ADS because this routine is called for decryption). */
- }
-
-
- /**
- * Construct an instance of SecretKeyEntry using the specified
- * parameters. This constructor would typically be used for key
- * entries imported from ADS, for which the full set of paramters
- * is known, and for a new key entry, for which the initialization
- * vector length might not yet be known, but which must be set
- * before use.
- *
- * @param keyID The unique identifier of this cipher
- * transformation/key pair.
- *
- * @param transformation The secret-key cipher transformation for
- * which the key entry is to be produced.
- *
- * @param keyAlgorithm The secret key cipher algorithm for which
- * the key was produced.
- *
- * @param key The cipher key.
- *
- * @param ivLengthBits The length in bits of a mandatory
- * initialization vector or 0 if none is required. Set this
- * parameter to -1 when generating a new encryption key and this
- * method will attempt to compute the proper value by first using
- * the cipher block size and then, if the cipher block size is
- * non-zero, using 0 (i.e., no initialization vector).
- *
- * @param isCompromised If the key
- *
- * @throws CryptoManagerException If there is a problem
- * instantiating a Cipher object in order to validate the supplied
- * parameters when creating a new entry.
- */
- private SecretKeyEntry( final byte[] keyID,
- final String transformation,
- final String keyAlgorithm,
- final byte[] key,
- final int ivLengthBits,
- final boolean isCompromised)
- throws CryptoManagerException {
- Validator.ensureNotNull(keyID, transformation, key);
- Validator.ensureTrue(16 == keyID.length); // FIXME: const for id
-
- // copy arguments
- this.fKeyID = new byte[keyID.length];
- System.arraycopy(keyID, 0, this.fKeyID, 0, keyID.length);
- this.fTransformation = new String(transformation);
- this.fKeySpec = new SecretKeySpec(key, keyAlgorithm);
- this.fKeyLengthBits = key.length * Byte.SIZE;
- this.fIVLengthBits = ivLengthBits;
- this.fIsCompromised = isCompromised;
- }
-
-
- /**
- * The cipher transformation for which the key entry was created.
- *
- * @return The cipher transformation.
- */
- public String getTransformation() {
- return fTransformation;
- }
-
- /**
- * The unique identifier of this cipher transformation/key pair.
- *
- * @return The unique identifier of this cipher transformation/key
- * pair.
- */
- public byte[] getKeyID() {
- return fKeyID;
- }
-
-
- /**
- * The secret key spec containing the secret key.
- *
- * @return The secret key spec containing the secret key.
- */
- public SecretKeySpec getKeySpec() {
- return fKeySpec;
- }
-
-
- /**
- * The initialization vector length in bits: 0 is a stream cipher
- * or a block cipher that does not use an IV (e.g., ECB); or a
- * positive integer, typically the block size of the cipher.
- * <p>
- * This method returns -1 if the object initialization has not
- * been completed.
- *
- * @return The initialization vector length.
- */
- public int getIVLength() {
- return fIVLengthBits;
- }
-
-
- /**
- * Set the algorithm/key pair's required initialization vector
- * length in bits. Typically, this will be the cipher's block
- * size, or 0 for a stream cipher or a block cipher mode that does
- * not use an initialization vector (e.g., ECB).
- *
- * @param ivLengthBits The initiazliation vector length in bits.
- */
- private void setIVLengthBits(int ivLengthBits) {
- Validator.ensureTrue(-1 == fIVLengthBits && 0 <= ivLengthBits);
- fIVLengthBits = ivLengthBits;
- }
-
-
- /**
- * Mark a key entry as compromised. The entry will no longer be
- * eligible for use as an encryption key.
- */
- public void setIsCompromised() {
- // TODO: called from ADS monitoring thread. Lock entry?
- fIsCompromised = true;
- }
-
- // state
- private final byte[] fKeyID;
- private final String fTransformation;
- private final SecretKeySpec fKeySpec;
- private final int fKeyLengthBits;
- private int fIVLengthBits;
- private boolean fIsCompromised = false;
- }
-
-
- /**
- * This method produces and initialized Cipher based on this
- * SecretKeyEntry's state and the method parameters.
+ * This method produces an initialized Cipher based on the supplied
+ * CipherKeyEntry's state.
*
* @param keyEntry The secret key entry containing the cipher
* transformation and secret key for which to instantiate
@@ -1026,7 +559,7 @@
* include NoSuchAlgorithmException, NoSuchPaddingException,
* InvalidKeyException, and InvalidAlgorithmParameterException.
*/
- private static Cipher getCipher(final SecretKeyEntry keyEntry,
+ private static Cipher getCipher(final CipherKeyEntry keyEntry,
final int mode,
final byte[] initializationVector)
throws CryptoManagerException {
@@ -1034,30 +567,30 @@
|| Cipher.DECRYPT_MODE == mode);
Validator.ensureTrue(Cipher.ENCRYPT_MODE != mode
|| null == initializationVector);
- Validator.ensureTrue(-1 != keyEntry.getIVLength()
+ Validator.ensureTrue(-1 != keyEntry.getIVLengthBits()
|| Cipher.ENCRYPT_MODE == mode);
Validator.ensureTrue(null == initializationVector
|| initializationVector.length * Byte.SIZE
- == keyEntry.getIVLength());
+ == keyEntry.getIVLengthBits());
Cipher cipher;
try {
- cipher = Cipher.getInstance(keyEntry.getTransformation());
+ cipher = Cipher.getInstance(keyEntry.getType());
}
catch (GeneralSecurityException ex) {
// NoSuchAlgorithmException, NoSuchPaddingException
throw new CryptoManagerException(
// TODO: i18n
- Message.raw("Invalid cipher transformation specified"
- + " %s.", keyEntry.getTransformation()), ex);
+ Message.raw("Invalid Cipher transformation specified:"
+ + " %s.", keyEntry.getType()), ex);
}
try {
- if (0 < keyEntry.getIVLength()) {
+ if (0 < keyEntry.getIVLengthBits()) {
byte[] iv;
if (Cipher.ENCRYPT_MODE == mode
&& null == initializationVector) {
- iv = new byte[keyEntry.getIVLength() / Byte.SIZE];
+ iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE];
pseudoRandom.nextBytes(iv);
}
else {
@@ -1100,7 +633,7 @@
throws GeneralSecurityException, CryptoManagerException
{
return encrypt(preferredCipherTransformation,
- preferredCipherTransformationKeyLength, data);
+ preferredCipherTransformationKeyLengthBits, data);
}
@@ -1133,17 +666,17 @@
{
Validator.ensureNotNull(cipherTransformation, data);
- SecretKeyEntry keyEntry = SecretKeyEntry.getKeyEntry(
- secretKeyEntryMap, cipherTransformation, keyLengthBits);
+ CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(
+ this, cipherTransformation, keyLengthBits);
if (null == keyEntry) {
- keyEntry = SecretKeyEntry.generateKeyEntry(secretKeyEntryMap,
+ keyEntry = CipherKeyEntry.generateKeyEntry(this,
cipherTransformation, keyLengthBits);
}
final Cipher cipher
= getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
- final byte[] keyID = keyEntry.getKeyID();
+ final byte[] keyID = keyEntry.getKeyID().getByteValue();
final byte[] iv = cipher.getIV();
final int prologueLength
= keyID.length + ((null == iv) ? 0 : iv.length);
@@ -1175,7 +708,7 @@
OutputStream outputStream) throws CryptoManagerException
{
return getCipherOutputStream(preferredCipherTransformation,
- preferredCipherTransformationKeyLength, outputStream);
+ preferredCipherTransformationKeyLengthBits, outputStream);
}
@@ -1205,16 +738,16 @@
{
Validator.ensureNotNull(cipherTransformation, outputStream);
- SecretKeyEntry keyEntry = SecretKeyEntry.getKeyEntry(
- secretKeyEntryMap, cipherTransformation, keyLengthBits);
+ CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(
+ this, cipherTransformation, keyLengthBits);
if (null == keyEntry) {
- keyEntry = SecretKeyEntry.generateKeyEntry(secretKeyEntryMap,
+ keyEntry = CipherKeyEntry.generateKeyEntry(this,
cipherTransformation, keyLengthBits);
}
final Cipher cipher
= getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
- final byte[] keyID = keyEntry.getKeyID();
+ final byte[] keyID = keyEntry.getKeyID().getByteValue();
try {
outputStream.write(keyID);
if (null != cipher.getIV()) {
@@ -1253,9 +786,12 @@
throws GeneralSecurityException,
CryptoManagerException
{
- final byte[] keyID = new byte[16]; //FIXME: key length constant
+ KeyEntryID keyID;
try {
- System.arraycopy(data, 0, keyID, 0, keyID.length);
+ final byte[] keyIDBytes
+ = new byte[KeyEntryID.getByteValueLength()];
+ System.arraycopy(data, 0, keyIDBytes, 0, keyIDBytes.length);
+ keyID = new KeyEntryID(keyIDBytes);
}
catch (Exception ex) {
throw new CryptoManagerException(
@@ -1265,20 +801,20 @@
" data prologue."), ex);
}
- SecretKeyEntry keyEntry
- = SecretKeyEntry.getKeyEntry(secretKeyEntryMap, keyID);
+ CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(this, keyID);
if (null == keyEntry) {
throw new CryptoManagerException(
// TODO: i18N
- Message.raw("Invalid key identifier in data" +
- " prologue."));
+ Message.raw("Invalid or unknown key identifier in" +
+ " data prologue."));
}
byte[] iv = null;
- if (0 < keyEntry.getIVLength()) {
- iv = new byte[keyEntry.getIVLength()/Byte.SIZE];
+ if (0 < keyEntry.getIVLengthBits()) {
+ iv = new byte[keyEntry.getIVLengthBits()/Byte.SIZE];
try {
- System.arraycopy(data, keyID.length, iv, 0, iv.length);
+ System.arraycopy(data, KeyEntryID.getByteValueLength(), iv, 0,
+ iv.length);
}
catch (Exception ex) {
throw new CryptoManagerException(
@@ -1288,10 +824,10 @@
}
}
- final Cipher cipher
- = getCipher(keyEntry, Cipher.DECRYPT_MODE, iv);
- final int prologueLength
- = keyID.length + ((null == iv) ? 0 : iv.length);
+ final Cipher cipher = getCipher(keyEntry, Cipher.DECRYPT_MODE,
+ iv);
+ final int prologueLength = KeyEntryID.getByteValueLength()
+ + ((null == iv) ? 0 : iv.length);
return cipher.doFinal(data, prologueLength,
data.length - prologueLength);
}
@@ -1314,10 +850,10 @@
public CipherInputStream getCipherInputStream(
InputStream inputStream) throws CryptoManagerException
{
- SecretKeyEntry keyEntry;
+ CipherKeyEntry keyEntry;
byte[] iv = null;
try {
- final byte[] keyID = new byte[16]; //FIXME: key length constant
+ final byte[] keyID = new byte[KeyEntryID.getByteValueLength()];
if (keyID.length != inputStream.read(keyID)){
throw new CryptoManagerException(
// TODO: i18n
@@ -1325,15 +861,16 @@
" identifier from data prologue."));
}
- keyEntry = SecretKeyEntry.getKeyEntry(secretKeyEntryMap, keyID);
+ keyEntry = CipherKeyEntry.getKeyEntry(this,
+ new KeyEntryID(keyID));
if (null == keyEntry) {
throw new CryptoManagerException(
// TODO: i18N
Message.raw("Invalid key identifier in data prologue."));
}
- if (0 < keyEntry.getIVLength()) {
- iv = new byte[keyEntry.getIVLength() / Byte.SIZE];
+ if (0 < keyEntry.getIVLengthBits()) {
+ iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE];
if (iv.length != inputStream.read(iv)) {
throw new CryptoManagerException(
// TODO: i18n
@@ -1571,6 +1108,868 @@
}
/**
+ * This class implements a utility interface to the unique
+ * identifier corresponding to a cryptographic key. For each key
+ * stored in an entry in ADS, the key identifier is the naming
+ * attribute of the entry. The external binary representation of the
+ * key entry identifier is compact, because it is typically stored
+ * as a prefix of encrypted data.
+ */
+ private static class KeyEntryID
+ {
+ /**
+ * Constructs a KeyEntryID using a new unique identifier.
+ */
+ public KeyEntryID() {
+ fValue = UUID.randomUUID();
+ }
+
+ /**
+ * Construct a {@code KeyEntryID} from its {@code byte[]}
+ * representation.
+ *
+ * @param keyEntryID The {@code byte[]} representation of a
+ * {@code KeyEntryID}.
+ */
+ public KeyEntryID(final byte[] keyEntryID) {
+ Validator.ensureTrue(getByteValueLength() == keyEntryID.length);
+ long hiBytes = 0;
+ long loBytes = 0;
+ for (int i = 0; i < 8; ++i) {
+ hiBytes = (hiBytes << 8) | (keyEntryID[i] & 0xff);
+ loBytes = (loBytes << 8) | (keyEntryID[8 + i] & 0xff);
+ }
+ fValue = new UUID(hiBytes, loBytes);
+ }
+
+ /**
+ * Constructs a {@code KeyEntryID} from its {@code String}
+ * representation.
+ *
+ * @param keyEntryID The {@code String} reprentation of a
+ * {@code KeyEntryID}.
+ *
+ * @throws CryptoManagerException If the argument does
+ * not conform to the {@code KeyEntryID} string syntax.
+ */
+ public KeyEntryID(final String keyEntryID)
+ throws CryptoManagerException {
+ try {
+ fValue = UUID.fromString(keyEntryID);
+ }
+ catch (Exception ex) {
+ throw new CryptoManagerException(
+ // TODO: i18n
+ Message.raw("Key entry identifier \"%s\" has" +
+ " invalid syntax.", keyEntryID), ex);
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param keyEntryID The {@code KeyEntryID} to copy.
+ */
+ public KeyEntryID(final KeyEntryID keyEntryID) {
+ fValue = new UUID(keyEntryID.fValue.getMostSignificantBits(),
+ keyEntryID.fValue.getLeastSignificantBits());
+ }
+
+ /**
+ * Returns the compact {@code byte[]} representation of this
+ * {@code KeyEntryID}.
+ * @return The compact {@code byte[]} representation of this
+ * {@code KeyEntryID
+ */
+ public byte[] getByteValue(){
+ final byte[] uuidBytes = new byte[16];
+ long hiBytes = fValue.getMostSignificantBits();
+ long loBytes = fValue.getLeastSignificantBits();
+ for (int i = 7; i >= 0; --i) {
+ uuidBytes[i] = (byte)hiBytes;
+ hiBytes >>>= 8;
+ uuidBytes[8 + i] = (byte)loBytes;
+ loBytes >>>= 8;
+ }
+ return uuidBytes;
+ }
+
+ /**
+ * Returns the {@code String} representation of this
+ * {@code KeyEntryID}.
+ * @return The {@code String} representation of this
+ * {@code KeyEntryID}.
+ */
+ public String getStringValue() {
+ return fValue.toString();
+ }
+
+ /**
+ * Returns the length of the compact {@code byte[]} representation
+ * of a {@code KeyEntryID}.
+ *
+ * @return The length of the compact {@code byte[]} representation
+ * of a {@code KeyEntryID}.
+ */
+ public static int getByteValueLength() {
+ return 16;
+ }
+
+ /**
+ * Compares this object to the specified object. The result is
+ * true if and only if the argument is not null, is of type
+ * {@code KeyEntryID}, and has the same value (i.e., the
+ * {@code String} and {@code byte[]} representations are
+ * identical).
+ *
+ * @param obj The object to which to compare this instance.
+ *
+ * @return {@code true} if the objects are the same, {@code false}
+ * otherwise.
+ */
+ public boolean equals(final Object obj){
+ return obj instanceof KeyEntryID
+ && fValue.equals(((KeyEntryID) obj).fValue);
+ }
+
+ /**
+ * Returns a hash code for this {@code KeyEntryID}.
+ *
+ * @return a hash code value for this {@code KeyEntryID}.
+ */
+ public int hashCode() {
+ return fValue.hashCode();
+ }
+
+ // state
+ private final UUID fValue;
+ }
+
+
+ /**
+ * This class corresponds to the secret key portion if a secret
+ * key entry in ADS.
+ */
+ private static class SecretKeyEntry
+ {
+ /**
+ * Construct an instance of {@code SecretKeyEntry} using the
+ * specified parameters. This constructor is used for key
+ * generation.
+ *
+ * @param algorithm The name of the secret key algorithm for
+ * which the key entry is to be produced.
+ *
+ * @param keyLengthBits The length of the requested key in bits.
+ *
+ * @throws CryptoManagerException If there is a problem
+ * instantiating the key generator.
+ */
+ public SecretKeyEntry(String algorithm, int keyLengthBits)
+ throws CryptoManagerException {
+ KeyGenerator keyGen;
+ try {
+ keyGen = KeyGenerator.getInstance(algorithm);
+ }
+ catch (NoSuchAlgorithmException ex) {
+ throw new CryptoManagerException(
+ // TODO: i18n
+ Message.raw("Unable to produce key generator using" +
+ " key algorithm argument %s",
+ algorithm), ex);
+ }
+ keyGen.init(keyLengthBits, secureRandom);
+ final byte[] key = keyGen.generateKey().getEncoded();
+
+ this.fKeyID = new KeyEntryID();
+ this.fKeySpec = new SecretKeySpec(key, algorithm);
+ this.fKeyLengthBits = key.length * Byte.SIZE;
+ this.fIsCompromised = false;
+ }
+
+
+ /**
+ * Construct an instance of {@code SecretKeyEntry} using the
+ * specified parameters. This constructor would typically be used
+ * for key entries imported from ADS, for which the full set of
+ * paramters is known.
+ *
+ * @param keyID The unique identifier of this algorithm/key pair.
+ *
+ * @param algorithm The name of the secret key algorithm for
+ * which the key entry is to be produced.
+ *
+ * @param key The secret key.
+ *
+ * @param isCompromised {@code false} if the key may be used
+ * for operations on new data, or {@code true} if the key is being
+ * retained only for use in validation.
+ */
+ public SecretKeyEntry(final KeyEntryID keyID,
+ final String algorithm,
+ final byte[] key,
+ final boolean isCompromised) {
+ // copy arguments
+ this.fKeyID = new KeyEntryID(keyID);
+ this.fKeySpec = new SecretKeySpec(key, algorithm);
+ this.fKeyLengthBits = key.length * Byte.SIZE;
+ this.fIsCompromised = isCompromised;
+ }
+
+
+ /**
+ * The unique identifier of this algorithm/key pair.
+ *
+ * @return The unique identifier of this algorithm/key pair.
+ */
+ public KeyEntryID getKeyID() {
+ return fKeyID;
+ }
+
+
+ /**
+ * The secret key spec containing the secret key.
+ *
+ * @return The secret key spec containing the secret key.
+ */
+ public SecretKeySpec getKeySpec() {
+ return fKeySpec;
+ }
+
+
+ /**
+ * Mark a key entry as compromised. The entry will no longer be
+ * eligible for use as an encryption key.
+ */
+ public void setIsCompromised() {
+ // TODO: called from ADS monitoring thread. Lock entry?
+ fIsCompromised = true;
+ }
+
+ /**
+ * Returns the length of the secret key in bits.
+ * @return the length of the secret key in bits.
+ */
+ public int getKeyLengthBits() {
+ return fKeyLengthBits;
+ }
+
+ /**
+ * Returns the status of the key.
+ * @return {@code false} if the key may be used for operations on
+ * new data, or {@code true} if the key is being retained only for
+ * use in validation.
+ */
+ public boolean isCompromised() {
+ return fIsCompromised;
+ }
+
+ // state
+ private final KeyEntryID fKeyID;
+ private final SecretKeySpec fKeySpec;
+ private final int fKeyLengthBits;
+ private boolean fIsCompromised = false;
+ }
+
+ /**
+ * This class corresponds to the cipher key entry in ADS. It is
+ * used in the local cache of key entries that have been requested
+ * by CryptoManager clients.
+ */
+ private static class CipherKeyEntry extends SecretKeyEntry
+ {
+ /**
+ * This method generates a key according to the key parameters,
+ * and creates a key entry and registers it in the supplied map.
+ *
+ * @param cryptoManager The CryptoManager instance for which the
+ * key is to be generated. Pass {@code null} as the argument to
+ * this parameter in order to validate a proposed cipher
+ * transformation and key length without publishing the key.
+ *
+ * @param transformation The cipher transformation for which the
+ * key is to be produced.
+ *
+ * @param keyLengthBits The cipher key length in bits.
+ *
+ * @return The key entry corresponding to the parameters.
+ *
+ * @throws CryptoManagerException If there is a problem
+ * instantiating a Cipher object in order to validate the supplied
+ * parameters when creating a new entry.
+ *
+ * @see CipherKeyEntry#getKeyEntry(CryptoManager, String, int)
+ */
+ public static CipherKeyEntry generateKeyEntry(
+ final CryptoManager cryptoManager,
+ final String transformation,
+ final int keyLengthBits)
+ throws CryptoManagerException {
+
+ final Map<KeyEntryID, CipherKeyEntry> map
+ = (null == cryptoManager)
+ ? null : cryptoManager.cipherKeyEntryCache;
+
+ CipherKeyEntry keyEntry = new CipherKeyEntry(transformation,
+ keyLengthBits);
+
+ // Validate the key entry.
+ final Cipher cipher
+ = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
+ final byte[] iv = cipher.getIV();
+ keyEntry.setIVLengthBits(
+ (null == iv) ? 0 : iv.length * Byte.SIZE);
+
+ if (null != map) {
+ map.put(keyEntry.getKeyID(), keyEntry);
+ // TODO: publish key in ADS. (mark key "blocked" in map
+ // until registered? OTOH, Key should be in local map prior to
+ // publication, since data could arrive from a remote OpenDS
+ // instance encrypted with the key any time after publication.
+ // OTOH, the key should be published in ADS before any use,
+ // since that is the persistent shared secret key repository.)
+ }
+
+ return keyEntry;
+ }
+
+
+ /**
+ * Initializes a secret key entry from the supplied parameters,
+ * validates it, and registers it in the supplied map. The
+ * anticipated use of this method is to import a key entry from
+ * ADS.
+ *
+ * @param cryptoManager The CryptoManager instance.
+ *
+ * @param keyIDBytes The key identifier.
+ *
+ * @param transformation The cipher transformation for which the
+ * key entry was produced.
+ *
+ * @param keyAlgorithm The cipher algorithm for which the key was
+ * produced.
+ *
+ * @param key The cipher key.
+ *
+ * @param ivLengthBits The length of the initialization vector,
+ * which will be zero in the case of any stream cipher algorithm,
+ * any block cipher algorithm for which the transformation mode
+ * does not use an initialization vector, and any HMAC algorithm.
+ *
+ * @param isCompromised Mark the key as compromised, so that it
+ * will not subsequently be used for encryption. The key entry
+ * must be maintained in order to decrypt existing ciphertext.
+ *
+ * @return The key entry, if one was successfully produced.
+ *
+ * @throws CryptoManagerException In case of an error in the
+ * parameters used to initialize or validate the key entry.
+ */
+ public static CipherKeyEntry importCipherKeyEntry(
+ final CryptoManager cryptoManager,
+ final byte[] keyIDBytes,
+ final String transformation,
+ final String keyAlgorithm,
+ final byte[] key,
+ final int ivLengthBits,
+ final boolean isCompromised)
+ throws CryptoManagerException {
+ Validator.ensureNotNull(keyIDBytes, transformation,
+ keyAlgorithm, key);
+ Validator.ensureTrue(0 <= ivLengthBits);
+
+ final KeyEntryID keyID = new KeyEntryID(keyIDBytes);
+
+ // Check map for existing key entry with the supplied keyID.
+ CipherKeyEntry keyEntry = getKeyEntry(cryptoManager, keyID);
+ if (null != keyEntry) {
+ // TODO: compare keyEntry with supplied parameters to ensure
+ // equal.
+ return keyEntry;
+ }
+
+ // Instantiate new entry.
+ keyEntry = new CipherKeyEntry(keyID, transformation,
+ keyAlgorithm, key, ivLengthBits, isCompromised);
+
+ // Validate new entry.
+ byte[] iv = null;
+ if (0 < ivLengthBits) {
+ iv = new byte[ivLengthBits * Byte.SIZE];
+ pseudoRandom.nextBytes(iv);
+ }
+ getCipher(keyEntry, Cipher.DECRYPT_MODE, iv);
+
+ // Cache new entry.
+ cryptoManager.cipherKeyEntryCache.put(keyEntry.getKeyID(),
+ keyEntry);
+
+ return keyEntry;
+ }
+
+
+ /**
+ * Retrieve a CipherKeyEntry from the CipherKeyEntry Map based on
+ * the algorithm name and key length.
+ *
+ * @param cryptoManager The CryptoManager instance with which the
+ * key entry is associated.
+ *
+ * @param transformation The cipher transformation for which the
+ * key was produced.
+ *
+ * @param keyLengthBits The cipher key length in bits.
+ *
+ * @return The key entry corresponding to the parameters, or null
+ * if no such entry exists.
+ */
+ public static CipherKeyEntry getKeyEntry(
+ final CryptoManager cryptoManager,
+ final String transformation,
+ final int keyLengthBits) {
+ Validator.ensureNotNull(cryptoManager, transformation);
+ Validator.ensureTrue(0 < keyLengthBits);
+
+
+ CipherKeyEntry keyEntry = null;
+ // search for an existing key that satisfies the request
+ for (Map.Entry<KeyEntryID, CipherKeyEntry> i
+ : cryptoManager.cipherKeyEntryCache.entrySet()) {
+ CipherKeyEntry entry = i.getValue();
+ if (! entry.isCompromised()
+ && entry.getType().equals(transformation)
+ && entry.getKeyLengthBits() == keyLengthBits) {
+ keyEntry = entry;
+ break;
+ }
+ }
+
+ // TODO: if (null == keyEntry) Does ADS monitoring thread keep
+ // map updated with keys produced at other sites? Otherwise,
+ // search ADS for suitable key.
+
+ // TODO: if (null == keyEntry) consider generating key here.
+
+ return keyEntry;
+ }
+
+
+ /**
+ * Given a key identifier, return the associated cipher key entry
+ * from the supplied map. This method would typically be used by
+ * a decryption routine.
+ *
+ * @param cryptoManager The CryptoManager instance with which the
+ * key entry is associated.
+ *
+ * @param keyID The key identifier.
+ *
+ * @return The key entry associated with the key identifier.
+ */
+ public static CipherKeyEntry getKeyEntry(
+ CryptoManager cryptoManager,
+ final KeyEntryID keyID) {
+ return cryptoManager.cipherKeyEntryCache.get(keyID);
+ /* TODO: Does ADS monitorying thread keep map updated with keys
+ produced at other sites? If not, fetch from ADS and update
+ map (assuming a legitimate key ID, the key should exist in
+ ADS because this routine is called for decryption). */
+ }
+
+ /**
+ In case a transformation is supplied instead of an algorithm:
+ E.g., AES/CBC/PKCS5Padding -> AES.
+
+ @param transformation The cipher transformation from which to
+ extract the cipher algorithm.
+
+ @return The algorithm prefix of the Cipher transformation. If
+ the transformation is supplied as an algorithm-only (no mode or
+ padding), return the transformation as-is.
+ */
+ private static String keyAlgorithmFromTransformation(
+ String transformation){
+ final int separatorIndex = transformation.indexOf('/');
+ return (0 < separatorIndex)
+ ? transformation.substring(0, separatorIndex)
+ : transformation;
+ }
+
+ /**
+ * Construct an instance of {@code CipherKeyEntry} using the
+ * specified parameters. This constructor would typically be used
+ * for key generation.
+ *
+ * @param transformation The name of the Cipher transformation
+ * for which the key entry is to be produced.
+ *
+ * @param keyLengthBits The length of the requested key in bits.
+ *
+ * @throws CryptoManagerException If there is a problem
+ * instantiating the key generator.
+ */
+ private CipherKeyEntry(final String transformation,
+ final int keyLengthBits)
+ throws CryptoManagerException {
+ // Generate a new key.
+ super(keyAlgorithmFromTransformation(transformation),
+ keyLengthBits);
+
+ // copy arguments.
+ this.fType = new String(transformation);
+ this.fIVLengthBits = -1; /* compute IV length */
+ }
+
+ /**
+ * Construct an instance of CipherKeyEntry using the specified
+ * parameters. This constructor would typically be used for key
+ * entries imported from ADS, for which the full set of paramters
+ * is known, and for a newly generated key entry, for which the
+ * initialization vector length might not yet be known, but which
+ * must be set prior to using the key.
+ *
+ * @param keyID The unique identifier of this cipher
+ * transformation/key pair.
+ *
+ * @param transformation The name of the secret-key cipher
+ * transformation for which the key entry is to be produced.
+ *
+ * @param keyAlgorithm The name of the secret key cipher
+ * algorithm for which the key was produced.
+ *
+ * @param key The cipher key.
+ *
+ * @param ivLengthBits The length in bits of a mandatory
+ * initialization vector or 0 if none is required. Set this
+ * parameter to -1 when generating a new encryption key and this
+ * method will attempt to compute the proper value by first using
+ * the cipher block size and then, if the cipher block size is
+ * non-zero, using 0 (i.e., no initialization vector).
+ *
+ * @param isCompromised {@code false} if the key may be used
+ * for encryption, or {@code true} if the key is being retained
+ * only for use in decrypting existing data.
+ *
+ * @throws CryptoManagerException If there is a problem
+ * instantiating a Cipher object in order to validate the supplied
+ * parameters when creating a new entry.
+ */
+ private CipherKeyEntry(final KeyEntryID keyID,
+ final String transformation,
+ final String keyAlgorithm,
+ final byte[] key,
+ final int ivLengthBits,
+ final boolean isCompromised)
+ throws CryptoManagerException {
+ super(keyID, keyAlgorithm, key, isCompromised);
+
+ // copy arguments
+ this.fType = new String(transformation);
+ this.fIVLengthBits = ivLengthBits;
+ }
+
+
+ /**
+ * The cipher transformation for which the key entry was created.
+ *
+ * @return The cipher transformation.
+ */
+ public String getType() {
+ return fType;
+ }
+
+ /**
+ * Set the algorithm/key pair's required initialization vector
+ * length in bits. Typically, this will be the cipher's block
+ * size, or 0 for a stream cipher or a block cipher mode that does
+ * not use an initialization vector (e.g., ECB).
+ *
+ * @param ivLengthBits The initiazliation vector length in bits.
+ */
+ private void setIVLengthBits(int ivLengthBits) {
+ Validator.ensureTrue(-1 == fIVLengthBits && 0 <= ivLengthBits);
+ fIVLengthBits = ivLengthBits;
+ }
+
+ /**
+ * The initialization vector length in bits: 0 is a stream cipher
+ * or a block cipher that does not use an IV (e.g., ECB); or a
+ * positive integer, typically the block size of the cipher.
+ * <p>
+ * This method returns -1 if the object initialization has not
+ * been completed.
+ *
+ * @return The initialization vector length.
+ */
+ public int getIVLengthBits() {
+ return fIVLengthBits;
+ }
+
+ // state
+ private final String fType;
+ private int fIVLengthBits = -1;
+ }
+
+
+
+ /**
+ * This class corresponds to the MAC key entry in ADS. It is
+ * used in the local cache of key entries that have been requested
+ * by CryptoManager clients.
+ */
+ private static class MacKeyEntry extends SecretKeyEntry
+ {
+ /**
+ * This method generates a key according to the key parameters,
+ * creates a key entry, and optionally registers it in the
+ * supplied CryptoManager context.
+ *
+ * @param cryptoManager The CryptoManager instance for which the
+ * key is to be generated. Pass {@code null} as the argument to
+ * this parameter in order to validate a proposed MAC algorithm
+ * and key length, but not publish the key entry.
+ *
+ * @param algorithm The MAC algorithm for which the
+ * key is to be produced. This argument is required.
+ *
+ * @param keyLengthBits The MAC key length in bits. The argument
+ * must be a positive integer evenly divisible by the value
+ * Byte.SIZE.
+ *
+ * @return The key entry corresponding to the parameters.
+ *
+ * @throws CryptoManagerException If there is a problem
+ * instantiating a Mac object in order to validate the supplied
+ * parameters when creating a new entry.
+ *
+ * @see MacKeyEntry#getKeyEntry(CryptoManager, String, int)
+ */
+ public static MacKeyEntry generateKeyEntry(
+ final CryptoManager cryptoManager,
+ final String algorithm,
+ final int keyLengthBits)
+ throws CryptoManagerException {
+ Validator.ensureNotNull(algorithm);
+
+ final Map<KeyEntryID, MacKeyEntry> map = (null == cryptoManager)
+ ? null : cryptoManager.macKeyEntryCache;
+
+ final MacKeyEntry keyEntry = new MacKeyEntry(algorithm,
+ keyLengthBits);
+
+ // Validate the key entry.
+ getMacEngine(keyEntry);
+
+ if (null != map) {
+ map.put(keyEntry.getKeyID(), keyEntry);
+ // TODO: publish key in ADS. (mark key "blocked" in map
+ // until registered? OTOH, Key should be in local map prior to
+ // publication, since data could arrive from a remote OpenDS
+ // instance encrypted with the key any time after publication.
+ // OTOH, the key should be published in ADS before any use,
+ // since that is the persistent shared secret key repository.)
+ }
+
+ return keyEntry;
+ }
+
+
+ /**
+ * Initializes a secret key entry from the supplied parameters,
+ * validates it, and registers it in the supplied map. The
+ * anticipated use of this method is to import a key entry from
+ * ADS.
+ *
+ * @param cryptoManager The CryptoManager instance.
+ *
+ * @param keyIDString The key identifier.
+ *
+ * @param algorithm The algorithm for which the key entry was
+ * produced.
+ *
+ * @param key The cipher key.
+ *
+ * @param isCompromised Mark the key as compromised, so that it
+ * will not subsequently be used for new data. The key entry
+ * must be maintained in order to verify existing signatures.
+ *
+ * @return The key entry, if one was successfully produced.
+ *
+ * @throws CryptoManagerException In case of an error in the
+ * parameters used to initialize or validate the key entry.
+ */
+ public static MacKeyEntry importMacKeyEntry(
+ final CryptoManager cryptoManager,
+ final String keyIDString,
+ final String algorithm,
+ final byte[] key,
+ final boolean isCompromised)
+ throws CryptoManagerException {
+ Validator.ensureNotNull(keyIDString, algorithm, key);
+
+ final KeyEntryID keyID = new KeyEntryID(keyIDString);
+
+ // Check map for existing key entry with the supplied keyID.
+ MacKeyEntry keyEntry = getKeyEntry(cryptoManager, keyID);
+ if (null != keyEntry) {
+ // TODO: compare keyEntry with supplied parameters to ensure
+ // equal.
+ return keyEntry;
+ }
+
+ // Instantiate new entry.
+ keyEntry = new MacKeyEntry(keyID, algorithm, key,
+ isCompromised);
+
+ // Validate new entry.
+ getMacEngine(keyEntry);
+
+ // Cache new entry.
+ cryptoManager.macKeyEntryCache.put(keyEntry.getKeyID(),
+ keyEntry);
+
+ return keyEntry;
+ }
+
+
+ /**
+ * Retrieve a MacKeyEntry from the MacKeyEntry Map based on
+ * the algorithm name and key length.
+ *
+ * @param cryptoManager The CryptoManager instance with which the
+ * key entry is associated.
+ *
+ * @param algorithm The MAC algorithm for which the key was
+ * produced.
+ *
+ * @param keyLengthBits The MAC key length in bits.
+ *
+ * @return The key entry corresponding to the parameters, or null
+ * if no such entry exists.
+ */
+ public static MacKeyEntry getKeyEntry(
+ final CryptoManager cryptoManager,
+ final String algorithm,
+ final int keyLengthBits) {
+ Validator.ensureNotNull(cryptoManager, algorithm);
+ Validator.ensureTrue(0 < keyLengthBits);
+
+ MacKeyEntry keyEntry = null;
+ // search for an existing key that satisfies the request
+ for (Map.Entry<KeyEntryID, MacKeyEntry> i
+ : cryptoManager.macKeyEntryCache.entrySet()) {
+ MacKeyEntry entry = i.getValue();
+ if (! entry.isCompromised()
+ && entry.getType().equals(algorithm)
+ && entry.getKeyLengthBits() == keyLengthBits) {
+ keyEntry = entry;
+ break;
+ }
+ }
+
+ // TODO: if (null == keyEntry) Does ADS monitoring thread keep
+ // map updated with keys produced at other sites? Otherwise,
+ // search ADS for suitable key.
+
+ // TODO: if (null == keyEntry) consider generating key here.
+
+ return keyEntry;
+ }
+
+
+ /**
+ * Given a key identifier, return the associated cipher key entry
+ * from the supplied map. This method would typically be used by
+ * a decryption routine.
+ *
+ * @param cryptoManager The CryptoManager instance with which the
+ * key entry is associated.
+ *
+ * @param keyID The key identifier.
+ *
+ * @return The key entry associated with the key identifier.
+ */
+ public static MacKeyEntry getKeyEntry(
+ final CryptoManager cryptoManager,
+ final KeyEntryID keyID) {
+ return cryptoManager.macKeyEntryCache.get(keyID);
+
+ /* TODO: Does ADS monitorying thread keep map updated with keys
+ produced at other sites? If not, fetch from ADS and update
+ map (assuming a legitimate key ID, the key should exist in
+ ADS because this routine is called for decryption). */
+ }
+
+ /**
+ * Construct an instance of {@code MacKeyEntry} using the
+ * specified parameters. This constructor would typically be used
+ * for key generation.
+ *
+ * @param algorithm The name of the MAC algorithm for which the
+ * key entry is to be produced.
+ *
+ * @param keyLengthBits The length of the requested key in bits.
+ *
+ * @throws CryptoManagerException If there is a problem
+ * instantiating the key generator.
+ */
+ private MacKeyEntry(final String algorithm,
+ final int keyLengthBits)
+ throws CryptoManagerException {
+ // Generate a new key.
+ super(algorithm, keyLengthBits);
+
+ // copy arguments
+ this.fType = new String(algorithm);
+ }
+
+ /**
+ * Construct an instance of MacKeyEntry using the specified
+ * parameters. This constructor would typically be used for key
+ * entries imported from ADS, for which the full set of paramters
+ * is known.
+ *
+ * @param keyID The unique identifier of this MAC algorithm/key
+ * pair.
+ *
+ * @param algorithm The name of the MAC algorithm for which the
+ * key entry is to be produced.
+ *
+ * @param key The MAC key.
+ *
+ * @param isCompromised {@code false} if the key may be used
+ * for signing, or {@code true} if the key is being retained only
+ * for use in signature verification.
+ */
+ private MacKeyEntry(final KeyEntryID keyID,
+ final String algorithm,
+ final byte[] key,
+ final boolean isCompromised) {
+ super(keyID, algorithm, key, isCompromised);
+
+ // copy arguments
+ this.fType = new String(algorithm);
+ }
+
+
+ /**
+ * The algorithm for which the key entry was created.
+ *
+ * @return The algorithm.
+ */
+ public String getType() {
+ return fType;
+ }
+
+
+ // state
+ private final String fType;
+ }
+
+
+
+ /**
* This class defines an exception that is thrown in the case of
* problems with encryption key managagment, and is a wrapper for a
* variety of other cipher related exceptions.
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index cb9efc5..a202534 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -2235,10 +2235,11 @@
/**
- * The name of the backup property that holds the name of the MAC algorithm
- * used to generate the signed hash of a backup.
+ * The name of the backup property that holds the identifer of the key entry
+ * that contains the MAC algorithm and shared secret key used to generate
+ * the signed hash of a backup.
*/
- public static final String BACKUP_PROPERTY_MAC_ALGORITHM = "mac_algorithm";
+ public static final String BACKUP_PROPERTY_MAC_KEY_ID = "mac_key_id";
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
index 794970e..ef5b5d7 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
@@ -40,10 +40,16 @@
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Arrays;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+
+import javax.crypto.Mac;
/**
This class tests the CryptoManager.
@@ -69,30 +75,127 @@
}
+
+ @Test
+ public void testMacSuccess()
+ throws Exception {
+ final CryptoManager cm = DirectoryServer.getCryptoManager();
+ final String text = "1234";
+
+ final String macKeyID = cm.getMacEngineKeyEntryID();
+
+ final Mac signingMac = cm.getMacEngine(macKeyID);
+ final byte[] signedHash = signingMac.doFinal(text.getBytes());
+
+ final Mac validatingMac = cm.getMacEngine(macKeyID);
+ final byte[] calculatedSignature = validatingMac.doFinal(text.getBytes());
+
+ assertTrue(Arrays.equals(calculatedSignature, signedHash));
+ }
+
+
/**
- Tests a simple encryption-decryption cycle.
+ Cipher parameters
+ */
+ private class CipherParameters {
+ private final String fAlgorithm;
+ private final String fMode;
+ private final String fPadding;
+ private final int fKeyLength;
+ private final int fIVLength;
+
+ public CipherParameters(final String algorithm, final String mode,
+ final String padding, final int keyLength,
+ final int ivLength) {
+ fAlgorithm = algorithm;
+ fMode = mode;
+ fPadding = padding;
+ fKeyLength = keyLength;
+ fIVLength = ivLength;
+ }
+
+ public String getTransformation() {
+ if (null == fAlgorithm) return null; // default
+ return (null == fMode)
+ ? new String(fAlgorithm)
+ : (new StringBuilder(fAlgorithm)).append("/").append(fMode)
+ .append("/").append(fPadding).toString();
+ }
+
+ public int getKeyLength() {
+ return fKeyLength;
+ }
+
+ public int getIVLength() {
+ return fIVLength;
+ }
+ }
+
+
+ /**
+ Cipher parameter data set.
+
+ @return The set of Cipher parameters with which to test.
+ */
+ @DataProvider(name = "cipherParametersData")
+ public Object[][] cipherParametersData() {
+
+ List<CipherParameters> paramList = new LinkedList<CipherParameters>();
+ // default (preferred) AES/CBC/PKCS5Padding 128bit key.
+ paramList.add(new CipherParameters(null, null, null, 128, 128));
+ // custom
+ paramList.add(new CipherParameters("Blowfish", "CFB", "NoPadding", 128, 64));
+ paramList.add(new CipherParameters("RC4", null, null, 104, 0));
+ paramList.add(new CipherParameters("DES", "CFB", "NoPadding", 56, 56));
+ paramList.add(new CipherParameters("DESede", "ECB", "PKCS5Padding", 168, 56));
+
+
+ Object[][] cipherParameters = new Object[paramList.size()][1];
+ for (int i=0; i < paramList.size(); i++)
+ {
+ cipherParameters[i] = new Object[] { paramList.get(i) };
+ }
+
+ return cipherParameters;
+ }
+
+
+ /**
+ Tests a simple encryption-decryption cycle using the supplied cipher
+ parameters.
+
+ @param cp Cipher parameters to use for this test iteration.
@throws Exception If an exceptional condition arises.
*/
- @Test
- public void testEncryptDecryptSuccess() throws Exception {
+ @Test(dataProvider = "cipherParametersData")
+ public void testEncryptDecryptSuccess(CipherParameters cp)
+ throws Exception {
final CryptoManager cm = DirectoryServer.getCryptoManager();
final String secretMessage = "1234";
- final byte[] cipherText = cm.encrypt(secretMessage.getBytes());
+ final byte[] cipherText = (null == cp.getTransformation())
+ ? cm.encrypt(secretMessage.getBytes()) // default
+ : cm.encrypt(cp.getTransformation(), cp.getKeyLength(),
+ secretMessage.getBytes());
assertEquals(-1, (new String(cipherText)).indexOf(secretMessage));
final byte[] plainText = cm.decrypt(cipherText);
assertEquals((new String(plainText)), secretMessage);
}
+
/**
- Tests a simple cipher stream encryption-decryption cycle.
+ Tests a simple cipher stream encryption-decryption cycle using the supplied
+ cipher parameters.
+
+ @param cp Cipher parameters to use for this test iteration.
@throws Exception If an exceptional condition arises.
*/
- @Test
- public void testCipherEncryptDecryptSuccess() throws Exception {
+ @Test(dataProvider = "cipherParametersData")
+ public void testStreamEncryptDecryptSuccess(CipherParameters cp)
+ throws Exception {
final CryptoManager cm = DirectoryServer.getCryptoManager();
final String secretMessage = "56789";
@@ -101,200 +204,9 @@
tempFile.deleteOnExit();
OutputStream os = new FileOutputStream(tempFile);
- os = cm.getCipherOutputStream(os);
- os.write(secretMessage.getBytes());
- os.close();
-
- // TODO: check tempfile for plaintext.
-
- InputStream is = new FileInputStream(tempFile);
- is = cm.getCipherInputStream(is);
- byte[] plainText = new byte[secretMessage.getBytes().length];
- assertEquals(is.read(plainText), secretMessage.getBytes().length);
- assertEquals(is.read(), -1);
- is.close();
- assertEquals(new String(plainText), secretMessage);
- }
-
- // TODO: other-than-preferred cipher algorithms, failure cases...
- /**
- Tests a simple encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testEncryptDecryptSuccessX() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "1234";
-
- final byte[] cipherText = cm.encrypt("Blowfish/CFB/NoPadding", 128,
- secretMessage.getBytes());
- assertEquals(-1, (new String(cipherText)).indexOf(secretMessage));
-
- final byte[] plainText = cm.decrypt(cipherText);
- assertEquals((new String(plainText)), secretMessage);
- }
-
- /**
- Tests a simple cipher stream encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testCipherEncryptDecryptSuccessX() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "56789";
-
- final File tempFile
- = File.createTempFile(cm.getClass().getName(), null);
- tempFile.deleteOnExit();
-
- OutputStream os = new FileOutputStream(tempFile);
- os = cm.getCipherOutputStream("Blowfish/CFB/NoPadding", 128, os);
- os.write(secretMessage.getBytes());
- os.close();
-
- // TODO: check tempfile for plaintext.
-
- InputStream is = new FileInputStream(tempFile);
- is = cm.getCipherInputStream(is);
- byte[] plainText = new byte[secretMessage.getBytes().length];
- assertEquals(is.read(plainText), secretMessage.getBytes().length);
- assertEquals(is.read(), -1);
- is.close();
- assertEquals(new String(plainText), secretMessage);
- }
-
- /**
- Tests a simple encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testEncryptDecryptSuccessY() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "1234";
-
- final byte[] cipherText = cm.encrypt("RC4", 104,
- secretMessage.getBytes());
- assertEquals(-1, (new String(cipherText)).indexOf(secretMessage));
-
- final byte[] plainText = cm.decrypt(cipherText);
- assertEquals((new String(plainText)), secretMessage);
- }
-
- /**
- Tests a simple cipher stream encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testCipherEncryptDecryptSuccessY() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "56789";
-
- final File tempFile
- = File.createTempFile(cm.getClass().getName(), null);
- tempFile.deleteOnExit();
-
- OutputStream os = new FileOutputStream(tempFile);
- os = cm.getCipherOutputStream("RC4", 104, os);
- os.write(secretMessage.getBytes());
- os.close();
-
- // TODO: check tempfile for plaintext.
-
- InputStream is = new FileInputStream(tempFile);
- is = cm.getCipherInputStream(is);
- byte[] plainText = new byte[secretMessage.getBytes().length];
- assertEquals(is.read(plainText), secretMessage.getBytes().length);
- assertEquals(is.read(), -1);
- is.close();
- assertEquals(new String(plainText), secretMessage);
- }
-
- /**
- Tests a simple encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testEncryptDecryptSuccessZ() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "1234";
-
- final byte[] cipherText = cm.encrypt("DES/CFB/NoPadding", 56,
- secretMessage.getBytes());
- assertEquals(-1, (new String(cipherText)).indexOf(secretMessage));
-
- final byte[] plainText = cm.decrypt(cipherText);
- assertEquals((new String(plainText)), secretMessage);
- }
-
- /**
- Tests a simple cipher stream encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testCipherEncryptDecryptSuccessZ() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "56789";
-
- final File tempFile
- = File.createTempFile(cm.getClass().getName(), null);
- tempFile.deleteOnExit();
-
- OutputStream os = new FileOutputStream(tempFile);
- os = cm.getCipherOutputStream("DES/CFB/NoPadding", 56, os);
- os.write(secretMessage.getBytes());
- os.close();
-
- // TODO: check tempfile for plaintext.
-
- InputStream is = new FileInputStream(tempFile);
- is = cm.getCipherInputStream(is);
- byte[] plainText = new byte[secretMessage.getBytes().length];
- assertEquals(is.read(plainText), secretMessage.getBytes().length);
- assertEquals(is.read(), -1);
- is.close();
- assertEquals(new String(plainText), secretMessage);
- }
-
- /**
- Tests a simple encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testEncryptDecryptSuccessZZ() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "1234";
-
- final byte[] cipherText = cm.encrypt("DESede/ECB/PKCS5Padding", 168,
- secretMessage.getBytes());
- assertEquals(-1, (new String(cipherText)).indexOf(secretMessage));
-
- final byte[] plainText = cm.decrypt(cipherText);
- assertEquals((new String(plainText)), secretMessage);
- }
-
- /**
- Tests a simple cipher stream encryption-decryption cycle.
-
- @throws Exception If an exceptional condition arises.
- */
- @Test
- public void testCipherEncryptDecryptSuccessZZ() throws Exception {
- final CryptoManager cm = DirectoryServer.getCryptoManager();
- final String secretMessage = "56789";
-
- final File tempFile
- = File.createTempFile(cm.getClass().getName(), null);
- tempFile.deleteOnExit();
-
- OutputStream os = new FileOutputStream(tempFile);
- os = cm.getCipherOutputStream("DESede/ECB/PKCS5Padding", 168, os);
+ os = (null == cp.getTransformation())
+ ? cm.getCipherOutputStream(os) // default
+ : cm.getCipherOutputStream(cp.getTransformation(), cp.getKeyLength(), os);
os.write(secretMessage.getBytes());
os.close();
--
Gitblit v1.10.0