| | |
| | | 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; |
| | |
| | | { |
| | | // 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(); |
| | |
| | | |
| | | 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) |
| | | { |
| | |
| | | |
| | | try |
| | | { |
| | | SecretKeyEntry.generateKeyEntry(null, |
| | | CipherKeyEntry.generateKeyEntry(null, |
| | | preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLength); |
| | | preferredCipherTransformationKeyLengthBits); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | |
| | | |
| | | |
| | | /** |
| | | * 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. |
| | | * |
| | |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | |
| | | * 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 { |
| | |
| | | || 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 { |
| | |
| | | throws GeneralSecurityException, CryptoManagerException |
| | | { |
| | | return encrypt(preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLength, data); |
| | | preferredCipherTransformationKeyLengthBits, data); |
| | | } |
| | | |
| | | |
| | |
| | | { |
| | | 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); |
| | |
| | | OutputStream outputStream) throws CryptoManagerException |
| | | { |
| | | return getCipherOutputStream(preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLength, outputStream); |
| | | preferredCipherTransformationKeyLengthBits, outputStream); |
| | | } |
| | | |
| | | |
| | |
| | | { |
| | | 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()) { |
| | |
| | | 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( |
| | |
| | | " 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( |
| | |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | } |
| | |
| | | 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 |
| | |
| | | " 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 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 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. |