| File was renamed from opends/src/server/org/opends/server/crypto/CryptoManager.java |
| | |
| | | are a lot of similarities and it is conceivable at some point that |
| | | accelerated compression may be available just as it is for |
| | | cryptographic operations. |
| | | |
| | | <p> |
| | | Other components of CryptoManager: |
| | | @see "src/admin/defn/org/opends/server/admin/std\ |
| | | /CryptoManagerConfiguration.xml" |
| | | @see org.opends.server.crypto.CryptoManagerSync |
| | | @see org.opends.server.crypto.GetSymmetricKeyExtendedOperation |
| | | */ |
| | | @org.opends.server.types.PublicAPI( |
| | | stability=org.opends.server.types.StabilityLevel.VOLATILE, |
| | | mayInstantiate=false, |
| | | mayExtend=false, |
| | | mayInvoke=true) |
| | | public class CryptoManager |
| | | implements ConfigurationChangeListener<CryptoManagerCfg> |
| | | public class CryptoManagerImpl |
| | | implements ConfigurationChangeListener<CryptoManagerCfg>, CryptoManager |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | |
| | | private static AttributeType attrInitVectorLength; |
| | | private static AttributeType attrKeyLength; |
| | | private static AttributeType attrCompromisedTime; |
| | | private static ObjectClass ocCertRequest; |
| | | private static ObjectClass ocCertRequest; |
| | | private static ObjectClass ocInstanceKey; |
| | | private static ObjectClass ocCipherKey; |
| | | private static ObjectClass ocMacKey; |
| | |
| | | occurs while creating this {@code CryptoManager} that is not the result of a |
| | | problem in the configuration. |
| | | */ |
| | | public CryptoManager(CryptoManagerCfg cfg) |
| | | public CryptoManagerImpl(CryptoManagerCfg cfg) |
| | | throws ConfigException, InitializationException { |
| | | if (!schemaInitDone) { |
| | | // Initialize various schema references. |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieve the ADS trust store backend. |
| | | * @return The ADS trust store backend. |
| | | * @throws ConfigException If the ADS trust store backend is |
| | | * not configured. |
| | | */ |
| | | private TrustStoreBackend getTrustStoreBackend() |
| | | throws ConfigException |
| | | { |
| | | Backend b = DirectoryServer.getBackend( |
| | | ConfigConstants.ID_ADS_TRUST_STORE_BACKEND); |
| | | if (b == null) |
| | | { |
| | | Message msg = |
| | | ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_NOT_ENABLED.get( |
| | | ConfigConstants.ID_ADS_TRUST_STORE_BACKEND); |
| | | throw new ConfigException(msg); |
| | | } |
| | | if (!(b instanceof TrustStoreBackend)) |
| | | { |
| | | Message msg = |
| | | ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_WRONG_CLASS.get( |
| | | ConfigConstants.ID_ADS_TRUST_STORE_BACKEND); |
| | | throw new ConfigException(msg); |
| | | } |
| | | return (TrustStoreBackend)b; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns this instance's instance-key public-key certificate from |
| | | * the local keystore (i.e., from the truststore-backend and not |
| | | * from the ADS backed keystore). If the certificate entry does not |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the name of the preferred message digest algorithm. |
| | | * Given a set of other servers' symmetric key values for |
| | | * a given secret key, use the Get Symmetric Key extended |
| | | * operation to request this server's symmetric key value. |
| | | * |
| | | * @return The name of the preferred message digest algorithm |
| | | * @param symmetricKeys The known symmetric key values for |
| | | * a given secret key. |
| | | * |
| | | * @return The symmetric key value for this server, or null if |
| | | * none could be obtained. |
| | | */ |
| | | public String getPreferredMessageDigestAlgorithm() |
| | | private String getSymmetricKey(List<String> symmetricKeys) |
| | | { |
| | | return preferredDigestAlgorithm; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Retrieves a <CODE>MessageDigest</CODE> object that may be used to |
| | | * generate digests using the preferred digest algorithm. |
| | | * |
| | | * @return A <CODE>MessageDigest</CODE> object that may be used to |
| | | * generate digests using the preferred digest algorithm. |
| | | * |
| | | * @throws NoSuchAlgorithmException If the requested algorithm is |
| | | * not supported or is |
| | | * unavailable. |
| | | */ |
| | | public MessageDigest getPreferredMessageDigest() |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(preferredDigestAlgorithm); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a <CODE>MessageDigest</CODE> object that may be used to |
| | | * generate digests using the specified algorithm. |
| | | * |
| | | * @param digestAlgorithm The algorithm to use to generate the |
| | | * message digest. |
| | | * |
| | | * @return A <CODE>MessageDigest</CODE> object that may be used to |
| | | * generate digests using the specified algorithm. |
| | | * |
| | | * @throws NoSuchAlgorithmException If the requested algorithm is |
| | | * not supported or is |
| | | * unavailable. |
| | | */ |
| | | public MessageDigest getMessageDigest(String digestAlgorithm) |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(digestAlgorithm); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a byte array containing a message digest based on the |
| | | * provided data, using the preferred digest algorithm. |
| | | * |
| | | * @param data The data to be digested. |
| | | * |
| | | * @return A byte array containing the generated message digest. |
| | | * |
| | | * @throws NoSuchAlgorithmException If the requested algorithm is |
| | | * not supported or is |
| | | * unavailable. |
| | | */ |
| | | public byte[] digest(byte[] data) |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(preferredDigestAlgorithm). |
| | | digest(data); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a byte array containing a message digest based on the |
| | | * provided data, using the requested digest algorithm. |
| | | * |
| | | * @param digestAlgorithm The algorithm to use to generate the |
| | | * message digest. |
| | | * @param data The data to be digested. |
| | | * |
| | | * @return A byte array containing the generated message digest. |
| | | * |
| | | * @throws NoSuchAlgorithmException If the requested algorithm is |
| | | * not supported or is |
| | | * unavailable. |
| | | */ |
| | | public byte[] digest(String digestAlgorithm, byte[] data) |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(digestAlgorithm).digest(data); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a byte array containing a message digest based on the |
| | | * data read from the provided input stream, using the preferred |
| | | * digest 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 message digest. |
| | | * |
| | | * @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[] digest(InputStream inputStream) |
| | | throws IOException, NoSuchAlgorithmException |
| | | { |
| | | MessageDigest digest = |
| | | MessageDigest.getInstance(preferredDigestAlgorithm); |
| | | |
| | | byte[] buffer = new byte[8192]; |
| | | while (true) |
| | | InternalClientConnection internalConnection = |
| | | InternalClientConnection.getRootConnection(); |
| | | for (String symmetricKey : symmetricKeys) |
| | | { |
| | | int bytesRead = inputStream.read(buffer); |
| | | if (bytesRead < 0) |
| | | try |
| | | { |
| | | break; |
| | | } |
| | | // Get the server instance key ID from the symmetric key. |
| | | String[] elements = symmetricKey.split(":", 0); |
| | | String instanceKeyID = elements[0]; |
| | | |
| | | digest.update(buffer, 0, bytesRead); |
| | | } |
| | | // Find the server entry from the instance key ID. |
| | | String filter = "(" + |
| | | ConfigConstants.ATTR_CRYPTO_KEY_ID + "=" + |
| | | instanceKeyID + ")"; |
| | | InternalSearchOperation internalSearch = |
| | | internalConnection.processSearch( |
| | | serversDN, SearchScope.SUBORDINATE_SUBTREE, |
| | | SearchFilter.createFilterFromString(filter)); |
| | | if (internalSearch.getResultCode() != ResultCode.SUCCESS) |
| | | continue; |
| | | |
| | | return digest.digest(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a byte array containing a message digest based on the |
| | | * data read from the provided input stream, using the requested |
| | | * digest algorithm. Data will be read until the end of the stream |
| | | * is reached. |
| | | * |
| | | * @param digestAlgorithm The algorithm to use to generate the |
| | | * message digest. |
| | | * @param inputStream The input stream from which the data is |
| | | * to be read. |
| | | * |
| | | * @return A byte array containing the generated message digest. |
| | | * |
| | | * @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[] digest(String digestAlgorithm, |
| | | InputStream inputStream) |
| | | throws IOException, NoSuchAlgorithmException |
| | | { |
| | | MessageDigest digest = MessageDigest.getInstance(digestAlgorithm); |
| | | |
| | | byte[] buffer = new byte[8192]; |
| | | while (true) |
| | | { |
| | | int bytesRead = inputStream.read(buffer); |
| | | if (bytesRead < 0) |
| | | { |
| | | break; |
| | | } |
| | | |
| | | digest.update(buffer, 0, bytesRead); |
| | | } |
| | | |
| | | return digest.digest(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 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 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 getMacEngineKeyEntryID() |
| | | throws CryptoManagerException |
| | | { |
| | | return getMacEngineKeyEntryID(preferredMACAlgorithm, |
| | | preferredMACAlgorithmKeyLengthBits); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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. |
| | | * |
| | | * @param macAlgorithm The algorithm to use for the MAC engine. |
| | | * |
| | | * @param keyLengthBits The key length in bits to use with the |
| | | * specified 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 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 |
| | | { |
| | | final MacKeyEntry keyEntry = MacKeyEntry.getKeyEntry(this, |
| | | new KeyEntryID(keyEntryID)); |
| | | return (null == keyEntry) ? null : getMacEngine(keyEntry); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * This method produces an initialized MAC engine based on the |
| | | * supplied MacKeyEntry's state. |
| | | * |
| | | * @param keyEntry The MacKeyEntry specifying the Mac properties. |
| | | * |
| | | * @return An initialized Mac object. |
| | | * |
| | | * @throws CryptoManagerException In case there was a error |
| | | * instantiating the Mac object. |
| | | */ |
| | | private static Mac getMacEngine(MacKeyEntry keyEntry) |
| | | throws CryptoManagerException |
| | | { |
| | | Mac mac; |
| | | try { |
| | | mac = Mac.getInstance(keyEntry.getType()); |
| | | } |
| | | catch (NoSuchAlgorithmException ex){ |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_MAC_ENGINE_INVALID_MAC_ALGORITHM.get( |
| | | keyEntry.getType(), getExceptionMessage(ex)), |
| | | ex); |
| | | } |
| | | |
| | | try { |
| | | mac.init(keyEntry.getSecretKey()); |
| | | } |
| | | catch (InvalidKeyException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_MAC_ENGINE_CANNOT_INITIALIZE.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return mac; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | | * the cipher. |
| | | * |
| | | * @param mode Either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE. |
| | | * |
| | | * @param initializationVector For Cipher.DECRYPT_MODE, supply |
| | | * any initialzation vector used in the corresponding encryption |
| | | * cipher. May be null. |
| | | * |
| | | * @return The initialized cipher object. |
| | | * |
| | | * @throws CryptoManagerException In case of a problem creating |
| | | * or initializing the requested cipher object. Possible causes |
| | | * include NoSuchAlgorithmException, NoSuchPaddingException, |
| | | * InvalidKeyException, and InvalidAlgorithmParameterException. |
| | | */ |
| | | private static Cipher getCipher(final CipherKeyEntry keyEntry, |
| | | final int mode, |
| | | final byte[] initializationVector) |
| | | throws CryptoManagerException { |
| | | Validator.ensureTrue(Cipher.ENCRYPT_MODE == mode |
| | | || Cipher.DECRYPT_MODE == mode); |
| | | Validator.ensureTrue(Cipher.ENCRYPT_MODE != mode |
| | | || null == initializationVector); |
| | | Validator.ensureTrue(-1 != keyEntry.getIVLengthBits() |
| | | || Cipher.ENCRYPT_MODE == mode); |
| | | Validator.ensureTrue(null == initializationVector |
| | | || initializationVector.length * Byte.SIZE |
| | | == keyEntry.getIVLengthBits()); |
| | | |
| | | Cipher cipher; |
| | | try { |
| | | cipher = Cipher.getInstance(keyEntry.getType()); |
| | | } |
| | | catch (GeneralSecurityException ex) { |
| | | // NoSuchAlgorithmException, NoSuchPaddingException |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_CIPHER_INVALID_CIPHER_TRANSFORMATION.get( |
| | | keyEntry.getType(), getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | try { |
| | | if (0 < keyEntry.getIVLengthBits()) { |
| | | byte[] iv; |
| | | if (Cipher.ENCRYPT_MODE == mode |
| | | && null == initializationVector) { |
| | | iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE]; |
| | | pseudoRandom.nextBytes(iv); |
| | | } |
| | | else { |
| | | iv = initializationVector; |
| | | } |
| | | cipher.init(mode, keyEntry.getSecretKey(), |
| | | new IvParameterSpec(iv)); |
| | | } |
| | | else { |
| | | cipher.init(mode, keyEntry.getSecretKey()); |
| | | } |
| | | } |
| | | catch (GeneralSecurityException ex) { |
| | | // InvalidKeyException, InvalidAlgorithmParameterException |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_CIPHER_CANNOT_INITIALIZE.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return cipher; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Encrypts the data in the provided byte array using the preferred |
| | | * cipher transformation. |
| | | * |
| | | * @param data The plain-text data to be encrypted. |
| | | * |
| | | * @return A byte array containing the encrypted representation of |
| | | * the provided data. |
| | | * |
| | | * @throws GeneralSecurityException If a problem occurs while |
| | | * encrypting the data. |
| | | * |
| | | * @throws CryptoManagerException If a problem occurs managing the |
| | | * encryption key or producing the cipher. |
| | | */ |
| | | public byte[] encrypt(byte[] data) |
| | | throws GeneralSecurityException, CryptoManagerException |
| | | { |
| | | return encrypt(preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLengthBits, data); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Encrypts the data in the provided byte array using the requested |
| | | * cipher algorithm. |
| | | * |
| | | * @param cipherTransformation The algorithm/mode/padding to use |
| | | * for the cipher. |
| | | * |
| | | * @param keyLengthBits The length in bits of the encryption key |
| | | * this method is to use. Note the specified key length and |
| | | * transformation must be compatible. |
| | | * |
| | | * @param data The plain-text data to be encrypted. |
| | | * |
| | | * @return A byte array containing the encrypted representation of |
| | | * the provided data. |
| | | * |
| | | * @throws GeneralSecurityException If a problem occurs while |
| | | * encrypting the data. |
| | | * |
| | | * @throws CryptoManagerException If a problem occurs managing the |
| | | * encryption key or producing the cipher. |
| | | */ |
| | | public byte[] encrypt(String cipherTransformation, |
| | | int keyLengthBits, |
| | | byte[] data) |
| | | throws GeneralSecurityException, CryptoManagerException |
| | | { |
| | | Validator.ensureNotNull(cipherTransformation, data); |
| | | |
| | | CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry( |
| | | this, cipherTransformation, keyLengthBits); |
| | | if (null == keyEntry) { |
| | | keyEntry = CipherKeyEntry.generateKeyEntry(this, |
| | | cipherTransformation, keyLengthBits); |
| | | } |
| | | |
| | | final Cipher cipher |
| | | = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null); |
| | | |
| | | final byte[] keyID = keyEntry.getKeyID().getByteValue(); |
| | | final byte[] iv = cipher.getIV(); |
| | | final int prologueLength |
| | | = keyID.length + ((null == iv) ? 0 : iv.length); |
| | | final int dataLength = cipher.getOutputSize(data.length); |
| | | final byte[] cipherText = new byte[prologueLength + dataLength]; |
| | | System.arraycopy(keyID, 0, cipherText, 0, keyID.length); |
| | | if (null != iv) { |
| | | System.arraycopy(iv, 0, cipherText, keyID.length, iv.length); |
| | | } |
| | | System.arraycopy(cipher.doFinal(data), 0, cipherText, |
| | | prologueLength, dataLength); |
| | | return cipherText; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Writes encrypted data to the provided output stream using the |
| | | * preferred cipher transformation. |
| | | * |
| | | * @param outputStream The output stream to be wrapped by the |
| | | * returned cipher output stream. |
| | | * |
| | | * @return The output stream wrapped with a CipherOutputStream. |
| | | * |
| | | * @throws CryptoManagerException If a problem occurs managing the |
| | | * encryption key or producing the cipher. |
| | | */ |
| | | public CipherOutputStream getCipherOutputStream( |
| | | OutputStream outputStream) throws CryptoManagerException |
| | | { |
| | | return getCipherOutputStream(preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLengthBits, outputStream); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Writes encrypted data to the provided output stream using the |
| | | * requested cipher transformation. |
| | | * |
| | | * @param cipherTransformation The algorithm/mode/padding to use |
| | | * for the cipher. |
| | | * |
| | | * @param keyLengthBits The length in bits of the encryption key |
| | | * this method will generate. Note the specified key length |
| | | * must be compatible with the transformation. |
| | | * |
| | | * @param outputStream The output stream to be wrapped by the |
| | | * returned cipher output stream. |
| | | * |
| | | * @return The output stream wrapped with a CipherOutputStream. |
| | | * |
| | | * @throws CryptoManagerException If a problem occurs managing the |
| | | * encryption key or producing the cipher. |
| | | */ |
| | | public CipherOutputStream getCipherOutputStream( |
| | | String cipherTransformation, int keyLengthBits, |
| | | OutputStream outputStream) |
| | | throws CryptoManagerException |
| | | { |
| | | Validator.ensureNotNull(cipherTransformation, outputStream); |
| | | |
| | | CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry( |
| | | this, cipherTransformation, keyLengthBits); |
| | | if (null == keyEntry) { |
| | | keyEntry = CipherKeyEntry.generateKeyEntry(this, |
| | | cipherTransformation, keyLengthBits); |
| | | } |
| | | |
| | | final Cipher cipher |
| | | = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null); |
| | | final byte[] keyID = keyEntry.getKeyID().getByteValue(); |
| | | try { |
| | | outputStream.write(keyID); |
| | | if (null != cipher.getIV()) { |
| | | outputStream.write(cipher.getIV()); |
| | | } |
| | | } |
| | | catch (IOException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_CIPHER_STREAM_PROLOGUE_WRITE_ERROR.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return new CipherOutputStream(outputStream, cipher); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Decrypts the data in the provided byte array using cipher |
| | | * specified by the key identifier prologue to the data. |
| | | * cipher. |
| | | * |
| | | * @param data The cipher-text data to be decrypted. |
| | | * |
| | | * @return A byte array containing the clear-text representation of |
| | | * the provided data. |
| | | * |
| | | * @throws GeneralSecurityException If a problem occurs while |
| | | * encrypting the data. |
| | | * |
| | | * @throws CryptoManagerException If a problem occurs reading the |
| | | * key identifier or initialization vector from the data |
| | | * prologue, or using these values to initialize a Cipher. |
| | | */ |
| | | public byte[] decrypt(byte[] data) |
| | | throws GeneralSecurityException, |
| | | CryptoManagerException |
| | | { |
| | | KeyEntryID keyID; |
| | | try { |
| | | final byte[] keyIDBytes |
| | | = new byte[KeyEntryID.getByteValueLength()]; |
| | | System.arraycopy(data, 0, keyIDBytes, 0, keyIDBytes.length); |
| | | keyID = new KeyEntryID(keyIDBytes); |
| | | } |
| | | catch (Exception ex) { |
| | | // IndexOutOfBoundsException, ArrayStoreException, ... |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get(), |
| | | ex); |
| | | } |
| | | |
| | | CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(this, keyID); |
| | | if (null == keyEntry) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get()); |
| | | } |
| | | |
| | | byte[] iv = null; |
| | | if (0 < keyEntry.getIVLengthBits()) { |
| | | iv = new byte[keyEntry.getIVLengthBits()/Byte.SIZE]; |
| | | try { |
| | | System.arraycopy(data, KeyEntryID.getByteValueLength(), iv, 0, |
| | | iv.length); |
| | | } |
| | | catch (Exception ex) { |
| | | // IndexOutOfBoundsException, ArrayStoreException, ... |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get(), ex); |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns a CipherInputStream instantiated with a cipher |
| | | * corresponding to the key identifier prologue to the data. |
| | | * |
| | | * @param inputStream The input stream be wrapped with the |
| | | * CipherInputStream. |
| | | * |
| | | * @return The CiperInputStream instantiated as specified. |
| | | * |
| | | * @throws CryptoManagerException If there is a problem reading the |
| | | * key ID or initialization vector from the input stream, |
| | | * or using these values to inititalize a Cipher. |
| | | */ |
| | | public CipherInputStream getCipherInputStream( |
| | | InputStream inputStream) throws CryptoManagerException |
| | | { |
| | | CipherKeyEntry keyEntry; |
| | | byte[] iv = null; |
| | | try { |
| | | final byte[] keyID = new byte[KeyEntryID.getByteValueLength()]; |
| | | if (keyID.length != inputStream.read(keyID)){ |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get()); |
| | | } |
| | | keyEntry = CipherKeyEntry.getKeyEntry(this, |
| | | new KeyEntryID(keyID)); |
| | | if (null == keyEntry) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get()); |
| | | } |
| | | |
| | | if (0 < keyEntry.getIVLengthBits()) { |
| | | iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE]; |
| | | if (iv.length != inputStream.read(iv)) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get()); |
| | | } |
| | | } |
| | | } |
| | | catch (IOException ex) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_CIPHER_INPUT_STREAM_ERROR.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return new CipherInputStream(inputStream, |
| | | getCipher(keyEntry, Cipher.DECRYPT_MODE, iv)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Attempts to compress the data in the provided source array into |
| | | * the given destination array. If the compressed data will fit |
| | | * into the destination array, then this method will return the |
| | | * number of bytes of compressed data in the array. Otherwise, it |
| | | * will return -1 to indicate that the compression was not |
| | | * successful. Note that if -1 is returned, then the data in the |
| | | * destination array should be considered invalid. |
| | | * |
| | | * @param src The array containing the raw data to compress. |
| | | * @param dst The array into which the compressed data should be |
| | | * written. |
| | | * |
| | | * @return The number of bytes of compressed data, or -1 if it was |
| | | * not possible to actually compress the data. |
| | | */ |
| | | public int compress(byte[] src, byte[] dst) |
| | | { |
| | | Deflater deflater = new Deflater(); |
| | | try |
| | | { |
| | | deflater.setInput(src); |
| | | deflater.finish(); |
| | | |
| | | int compressedLength = deflater.deflate(dst); |
| | | if (deflater.finished()) |
| | | { |
| | | return compressedLength; |
| | | } |
| | | else |
| | | { |
| | | return -1; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | deflater.end(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Attempts to uncompress the data in the provided source array into |
| | | * the given destination array. If the uncompressed data will fit |
| | | * into the given destination array, then this method will return |
| | | * the number of bytes of uncompressed data written into the |
| | | * destination buffer. Otherwise, it will return a negative value |
| | | * to indicate that the destination buffer was not large enough. |
| | | * The absolute value of that negative return value will indicate |
| | | * the buffer size required to fully decompress the data. Note that |
| | | * if a negative value is returned, then the data in the destination |
| | | * array should be considered invalid. |
| | | * |
| | | * @param src The array containing the compressed data. |
| | | * @param dst The array into which the uncompressed data should be |
| | | * written. |
| | | * |
| | | * @return A positive value containing the number of bytes of |
| | | * uncompressed data written into the destination buffer, |
| | | * or a negative value whose absolute value is the size of |
| | | * the destination buffer required to fully decompress the |
| | | * provided data. |
| | | * |
| | | * @throws DataFormatException If a problem occurs while |
| | | * attempting to uncompress the data. |
| | | */ |
| | | public int uncompress(byte[] src, byte[] dst) |
| | | throws DataFormatException |
| | | { |
| | | Inflater inflater = new Inflater(); |
| | | try |
| | | { |
| | | inflater.setInput(src); |
| | | |
| | | int decompressedLength = inflater.inflate(dst); |
| | | if (inflater.finished()) |
| | | { |
| | | return decompressedLength; |
| | | } |
| | | else |
| | | { |
| | | int totalLength = decompressedLength; |
| | | |
| | | while (! inflater.finished()) |
| | | LinkedList<SearchResultEntry> resultEntries = |
| | | internalSearch.getSearchEntries(); |
| | | for (SearchResultEntry resultEntry : resultEntries) |
| | | { |
| | | totalLength += inflater.inflate(dst); |
| | | AttributeType hostnameAttr = |
| | | DirectoryServer.getAttributeType("hostname", true); |
| | | String hostname = resultEntry.getAttributeValue( |
| | | hostnameAttr, DirectoryStringSyntax.DECODER); |
| | | AttributeType ldapPortAttr = |
| | | DirectoryServer.getAttributeType("ldapport", true); |
| | | Integer ldapPort = resultEntry.getAttributeValue( |
| | | ldapPortAttr, IntegerSyntax.DECODER); |
| | | |
| | | // Connect to the server. |
| | | AtomicInteger nextMessageID = new AtomicInteger(1); |
| | | LDAPConnectionOptions connectionOptions = |
| | | new LDAPConnectionOptions(); |
| | | PrintStream nullPrintStream = |
| | | new PrintStream(new OutputStream() { |
| | | public void write ( int b ) { } |
| | | }); |
| | | LDAPConnection connection = |
| | | new LDAPConnection(hostname, ldapPort, |
| | | connectionOptions, |
| | | nullPrintStream, |
| | | nullPrintStream); |
| | | |
| | | connection.connectToHost(null, null, nextMessageID); |
| | | |
| | | try |
| | | { |
| | | LDAPReader reader = connection.getLDAPReader(); |
| | | LDAPWriter writer = connection.getLDAPWriter(); |
| | | |
| | | // Send the Get Symmetric Key extended request. |
| | | |
| | | ASN1OctetString requestValue = |
| | | GetSymmetricKeyExtendedOperation.encodeRequestValue( |
| | | symmetricKey, getInstanceKeyID()); |
| | | |
| | | ExtendedRequestProtocolOp extendedRequest = |
| | | new ExtendedRequestProtocolOp( |
| | | ServerConstants. |
| | | OID_GET_SYMMETRIC_KEY_EXTENDED_OP, |
| | | requestValue); |
| | | |
| | | ArrayList<LDAPControl> controls = |
| | | new ArrayList<LDAPControl>(); |
| | | LDAPMessage requestMessage = |
| | | new LDAPMessage(nextMessageID.getAndIncrement(), |
| | | extendedRequest, controls); |
| | | writer.writeMessage(requestMessage); |
| | | LDAPMessage responseMessage = reader.readMessage(); |
| | | |
| | | ExtendedResponseProtocolOp extendedResponse = |
| | | responseMessage.getExtendedResponseProtocolOp(); |
| | | if (extendedResponse.getResultCode() == |
| | | LDAPResultCode.SUCCESS) |
| | | { |
| | | // Got our symmetric key value. |
| | | return extendedResponse.getValue().stringValue(); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | connection.close(nextMessageID); |
| | | } |
| | | } |
| | | |
| | | return -totalLength; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | inflater.end(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Retrieve the ADS trust store backend. |
| | | * @return The ADS trust store backend. |
| | | * @throws ConfigException If the ADS trust store backend is |
| | | * not configured. |
| | | */ |
| | | private TrustStoreBackend getTrustStoreBackend() |
| | | throws ConfigException |
| | | { |
| | | Backend b = DirectoryServer.getBackend( |
| | | ConfigConstants.ID_ADS_TRUST_STORE_BACKEND); |
| | | if (b == null) |
| | | { |
| | | Message msg = |
| | | ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_NOT_ENABLED.get( |
| | | ConfigConstants.ID_ADS_TRUST_STORE_BACKEND); |
| | | throw new ConfigException(msg); |
| | | } |
| | | if (!(b instanceof TrustStoreBackend)) |
| | | { |
| | | Message msg = |
| | | ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_WRONG_CLASS.get( |
| | | ConfigConstants.ID_ADS_TRUST_STORE_BACKEND); |
| | | throw new ConfigException(msg); |
| | | } |
| | | return (TrustStoreBackend)b; |
| | | } |
| | | |
| | | /** |
| | | * Create an SSL context that may be used for communication to |
| | | * another ADS component. |
| | | * |
| | | * @param sslCertNickname The name of the local certificate to use, |
| | | * or null if none is specified. |
| | | * @return A new SSL Context. |
| | | * @throws ConfigException If the context could not be created. |
| | | */ |
| | | public SSLContext getSslContext(String sslCertNickname) |
| | | throws ConfigException |
| | | { |
| | | SSLContext sslContext; |
| | | try |
| | | { |
| | | TrustStoreBackend trustStoreBackend = getTrustStoreBackend(); |
| | | KeyManager[] keyManagers = trustStoreBackend.getKeyManagers(); |
| | | TrustManager[] trustManagers = |
| | | trustStoreBackend.getTrustManagers(); |
| | | |
| | | sslContext = SSLContext.getInstance("TLS"); |
| | | |
| | | if (sslCertNickname == null) |
| | | catch (Exception e) |
| | | { |
| | | sslContext.init(keyManagers, trustManagers, null); |
| | | } |
| | | else |
| | | { |
| | | X509ExtendedKeyManager[] extendedKeyManagers = |
| | | SelectableCertificateKeyManager.wrap( |
| | | keyManagers, |
| | | sslCertNickname); |
| | | sslContext.init(extendedKeyManagers, trustManagers, null); |
| | | // Just try another server. |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | Message message = |
| | | ERR_CRYPTOMGR_SSL_CONTEXT_CANNOT_INITIALIZE.get( |
| | | getExceptionMessage(e)); |
| | | throw new ConfigException(message, e); |
| | | } |
| | | |
| | | return sslContext; |
| | | // Give up. |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the name of the local certificate to use for SSL. |
| | | * @return The name of the local certificate to use for SSL. |
| | | */ |
| | | public String getSslCertNickname() |
| | | { |
| | | return sslCertNickname; |
| | | } |
| | | |
| | | /** |
| | | * Determine whether SSL encryption is enabled. |
| | | * @return true if SSL encryption is enabled. |
| | | */ |
| | | public boolean isSslEncryption() |
| | | { |
| | | return sslEncryption; |
| | | } |
| | | |
| | | /** |
| | | * Get the set of enabled SSL protocols. |
| | | * @return The set of enabled SSL protocols. |
| | | */ |
| | | public SortedSet<String> getSslProtocols() |
| | | { |
| | | return sslProtocols; |
| | | } |
| | | |
| | | /** |
| | | * Get the set of enabled SSL cipher suites. |
| | | * @return The set of enabled SSL cipher suites. |
| | | */ |
| | | public SortedSet<String> getSslCipherSuites() |
| | | { |
| | | return sslCipherSuites; |
| | | } |
| | | |
| | | /** |
| | | * Imports a cipher key entry from an entry in ADS. |
| | | * |
| | | * @param entry The ADS cipher key entry to be imported. |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Given a set of other servers' symmetric key values for |
| | | * a given secret key, use the Get Symmetric Key extended |
| | | * operation to request this server's symmetric key value. |
| | | * |
| | | * @param symmetricKeys The known symmetric key values for |
| | | * a given secret key. |
| | | * |
| | | * @return The symmetric key value for this server, or null if |
| | | * none could be obtained. |
| | | */ |
| | | private String getSymmetricKey(List<String> symmetricKeys) |
| | | { |
| | | InternalClientConnection internalConnection = |
| | | InternalClientConnection.getRootConnection(); |
| | | for (String symmetricKey : symmetricKeys) |
| | | { |
| | | try |
| | | { |
| | | // Get the server instance key ID from the symmetric key. |
| | | String[] elements = symmetricKey.split(":", 0); |
| | | String instanceKeyID = elements[0]; |
| | | |
| | | // Find the server entry from the instance key ID. |
| | | String filter = "(" + |
| | | ConfigConstants.ATTR_CRYPTO_KEY_ID + "=" + |
| | | instanceKeyID + ")"; |
| | | InternalSearchOperation internalSearch = |
| | | internalConnection.processSearch( |
| | | serversDN, SearchScope.SUBORDINATE_SUBTREE, |
| | | SearchFilter.createFilterFromString(filter)); |
| | | if (internalSearch.getResultCode() != ResultCode.SUCCESS) |
| | | continue; |
| | | |
| | | LinkedList<SearchResultEntry> resultEntries = |
| | | internalSearch.getSearchEntries(); |
| | | for (SearchResultEntry resultEntry : resultEntries) |
| | | { |
| | | AttributeType hostnameAttr = |
| | | DirectoryServer.getAttributeType("hostname", true); |
| | | String hostname = resultEntry.getAttributeValue( |
| | | hostnameAttr, DirectoryStringSyntax.DECODER); |
| | | AttributeType ldapPortAttr = |
| | | DirectoryServer.getAttributeType("ldapport", true); |
| | | Integer ldapPort = resultEntry.getAttributeValue( |
| | | ldapPortAttr, IntegerSyntax.DECODER); |
| | | |
| | | // Connect to the server. |
| | | AtomicInteger nextMessageID = new AtomicInteger(1); |
| | | LDAPConnectionOptions connectionOptions = |
| | | new LDAPConnectionOptions(); |
| | | PrintStream nullPrintStream = |
| | | new PrintStream(new OutputStream() { |
| | | public void write ( int b ) { } |
| | | }); |
| | | LDAPConnection connection = |
| | | new LDAPConnection(hostname, ldapPort, |
| | | connectionOptions, |
| | | nullPrintStream, |
| | | nullPrintStream); |
| | | |
| | | connection.connectToHost(null, null, nextMessageID); |
| | | |
| | | try |
| | | { |
| | | LDAPReader reader = connection.getLDAPReader(); |
| | | LDAPWriter writer = connection.getLDAPWriter(); |
| | | |
| | | // Send the Get Symmetric Key extended request. |
| | | |
| | | ASN1OctetString requestValue = |
| | | GetSymmetricKeyExtendedOperation.encodeRequestValue( |
| | | symmetricKey, getInstanceKeyID()); |
| | | |
| | | ExtendedRequestProtocolOp extendedRequest = |
| | | new ExtendedRequestProtocolOp( |
| | | ServerConstants. |
| | | OID_GET_SYMMETRIC_KEY_EXTENDED_OP, |
| | | requestValue); |
| | | |
| | | ArrayList<LDAPControl> controls = |
| | | new ArrayList<LDAPControl>(); |
| | | LDAPMessage requestMessage = |
| | | new LDAPMessage(nextMessageID.getAndIncrement(), |
| | | extendedRequest, controls); |
| | | writer.writeMessage(requestMessage); |
| | | LDAPMessage responseMessage = reader.readMessage(); |
| | | |
| | | ExtendedResponseProtocolOp extendedResponse = |
| | | responseMessage.getExtendedResponseProtocolOp(); |
| | | if (extendedResponse.getResultCode() == |
| | | LDAPResultCode.SUCCESS) |
| | | { |
| | | // Got our symmetric key value. |
| | | return extendedResponse.getValue().stringValue(); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | connection.close(nextMessageID); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | // Just try another server. |
| | | } |
| | | } |
| | | |
| | | // Give up. |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Imports a mac key entry from an entry in ADS. |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * This class implements a utility interface to the unique |
| | | * identifier corresponding to a cryptographic key. For each key |
| | |
| | | * instantiating a Cipher object in order to validate the supplied |
| | | * parameters when creating a new entry. |
| | | * |
| | | * @see CipherKeyEntry#getKeyEntry(CryptoManager, String, int) |
| | | * @see CipherKeyEntry#getKeyEntry(CryptoManagerImpl, String, int) |
| | | */ |
| | | public static CipherKeyEntry generateKeyEntry( |
| | | final CryptoManager cryptoManager, |
| | | final CryptoManagerImpl cryptoManager, |
| | | final String transformation, |
| | | final int keyLengthBits) |
| | | throws CryptoManagerException { |
| | |
| | | * If the key entry could not be added to |
| | | * ADS. |
| | | */ |
| | | private static void publishKeyEntry(CryptoManager cryptoManager, |
| | | private static void publishKeyEntry(CryptoManagerImpl cryptoManager, |
| | | CipherKeyEntry keyEntry) |
| | | throws CryptoManagerException |
| | | { |
| | |
| | | |
| | | // Need to add our own instance certificate. |
| | | byte[] instanceKeyCertificate = |
| | | CryptoManager.getInstanceKeyCertificateFromLocalTruststore(); |
| | | CryptoManagerImpl.getInstanceKeyCertificateFromLocalTruststore(); |
| | | trustedCerts.put(getInstanceKeyID(instanceKeyCertificate), |
| | | instanceKeyCertificate); |
| | | |
| | |
| | | * parameters used to initialize or validate the key entry. |
| | | */ |
| | | public static CipherKeyEntry importCipherKeyEntry( |
| | | final CryptoManager cryptoManager, |
| | | final CryptoManagerImpl cryptoManager, |
| | | final String keyIDString, |
| | | final String transformation, |
| | | final SecretKey secretKey, |
| | |
| | | * {@code null} if no such entry exists. |
| | | */ |
| | | public static CipherKeyEntry getKeyEntry( |
| | | final CryptoManager cryptoManager, |
| | | final CryptoManagerImpl cryptoManager, |
| | | final String transformation, |
| | | final int keyLengthBits) { |
| | | Validator.ensureNotNull(cryptoManager, transformation); |
| | |
| | | * @return The key entry associated with the key identifier, or |
| | | * {@code null} if no such entry exists. |
| | | * |
| | | * @see org.opends.server.crypto.CryptoManager.MacKeyEntry |
| | | * @see CryptoManagerImpl.MacKeyEntry |
| | | * #getKeyEntry(org.opends.server.types.CryptoManager, |
| | | * java.lang.String, int) |
| | | */ |
| | | public static CipherKeyEntry getKeyEntry( |
| | | CryptoManager cryptoManager, |
| | | CryptoManagerImpl cryptoManager, |
| | | final KeyEntryID keyID) { |
| | | return cryptoManager.cipherKeyEntryCache.get(keyID); |
| | | } |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | | * the cipher. |
| | | * |
| | | * @param mode Either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE. |
| | | * |
| | | * @param initializationVector For Cipher.DECRYPT_MODE, supply |
| | | * any initialzation vector used in the corresponding encryption |
| | | * cipher. May be null. |
| | | * |
| | | * @return The initialized cipher object. |
| | | * |
| | | * @throws CryptoManagerException In case of a problem creating |
| | | * or initializing the requested cipher object. Possible causes |
| | | * include NoSuchAlgorithmException, NoSuchPaddingException, |
| | | * InvalidKeyException, and InvalidAlgorithmParameterException. |
| | | */ |
| | | private static Cipher getCipher(final CipherKeyEntry keyEntry, |
| | | final int mode, |
| | | final byte[] initializationVector) |
| | | throws CryptoManagerException { |
| | | Validator.ensureTrue(Cipher.ENCRYPT_MODE == mode |
| | | || Cipher.DECRYPT_MODE == mode); |
| | | Validator.ensureTrue(Cipher.ENCRYPT_MODE != mode |
| | | || null == initializationVector); |
| | | Validator.ensureTrue(-1 != keyEntry.getIVLengthBits() |
| | | || Cipher.ENCRYPT_MODE == mode); |
| | | Validator.ensureTrue(null == initializationVector |
| | | || initializationVector.length * Byte.SIZE |
| | | == keyEntry.getIVLengthBits()); |
| | | |
| | | Cipher cipher; |
| | | try { |
| | | cipher = Cipher.getInstance(keyEntry.getType()); |
| | | } |
| | | catch (GeneralSecurityException ex) { |
| | | // NoSuchAlgorithmException, NoSuchPaddingException |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_CIPHER_INVALID_CIPHER_TRANSFORMATION.get( |
| | | keyEntry.getType(), getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | try { |
| | | if (0 < keyEntry.getIVLengthBits()) { |
| | | byte[] iv; |
| | | if (Cipher.ENCRYPT_MODE == mode |
| | | && null == initializationVector) { |
| | | iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE]; |
| | | pseudoRandom.nextBytes(iv); |
| | | } |
| | | else { |
| | | iv = initializationVector; |
| | | } |
| | | cipher.init(mode, keyEntry.getSecretKey(), |
| | | new IvParameterSpec(iv)); |
| | | } |
| | | else { |
| | | cipher.init(mode, keyEntry.getSecretKey()); |
| | | } |
| | | } |
| | | catch (GeneralSecurityException ex) { |
| | | // InvalidKeyException, InvalidAlgorithmParameterException |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_CIPHER_CANNOT_INITIALIZE.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return cipher; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * This class corresponds to the MAC key entry in ADS. It is |
| | |
| | | * instantiating a Mac object in order to validate the supplied |
| | | * parameters when creating a new entry. |
| | | * |
| | | * @see MacKeyEntry#getKeyEntry(CryptoManager, String, int) |
| | | * @see MacKeyEntry#getKeyEntry(CryptoManagerImpl, String, int) |
| | | */ |
| | | public static MacKeyEntry generateKeyEntry( |
| | | final CryptoManager cryptoManager, |
| | | final CryptoManagerImpl cryptoManager, |
| | | final String algorithm, |
| | | final int keyLengthBits) |
| | | throws CryptoManagerException { |
| | |
| | | * If the key entry could not be added to |
| | | * ADS. |
| | | */ |
| | | private static void publishKeyEntry(CryptoManager cryptoManager, |
| | | private static void publishKeyEntry(CryptoManagerImpl cryptoManager, |
| | | MacKeyEntry keyEntry) |
| | | throws CryptoManagerException |
| | | { |
| | |
| | | |
| | | // Need to add our own instance certificate. |
| | | byte[] instanceKeyCertificate = |
| | | CryptoManager.getInstanceKeyCertificateFromLocalTruststore(); |
| | | CryptoManagerImpl.getInstanceKeyCertificateFromLocalTruststore(); |
| | | trustedCerts.put(getInstanceKeyID(instanceKeyCertificate), |
| | | instanceKeyCertificate); |
| | | |
| | |
| | | * parameters used to initialize or validate the key entry. |
| | | */ |
| | | public static MacKeyEntry importMacKeyEntry( |
| | | final CryptoManager cryptoManager, |
| | | final CryptoManagerImpl cryptoManager, |
| | | final String keyIDString, |
| | | final String algorithm, |
| | | final SecretKey secretKey, |
| | |
| | | * {@code null} if no such entry exists. |
| | | */ |
| | | public static MacKeyEntry getKeyEntry( |
| | | final CryptoManager cryptoManager, |
| | | final CryptoManagerImpl cryptoManager, |
| | | final String algorithm, |
| | | final int keyLengthBits) { |
| | | Validator.ensureNotNull(cryptoManager, algorithm); |
| | |
| | | * @return The key entry associated with the key identifier, or |
| | | * {@code null} if no such entry exists. |
| | | * |
| | | * @see org.opends.server.crypto.CryptoManager.CipherKeyEntry |
| | | * @see CryptoManagerImpl.CipherKeyEntry |
| | | * #getKeyEntry(org.opends.server.types.CryptoManager, |
| | | * java.lang.String, int) |
| | | */ |
| | | public static MacKeyEntry getKeyEntry( |
| | | final CryptoManager cryptoManager, |
| | | final CryptoManagerImpl cryptoManager, |
| | | final KeyEntryID keyID) { |
| | | return cryptoManager.macKeyEntryCache.get(keyID); |
| | | } |
| | |
| | | return fType; |
| | | } |
| | | |
| | | |
| | | // state |
| | | private final String fType; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * This method produces an initialized MAC engine based on the |
| | | * supplied MacKeyEntry's state. |
| | | * |
| | | * @param keyEntry The MacKeyEntry specifying the Mac properties. |
| | | * |
| | | * @return An initialized Mac object. |
| | | * |
| | | * @throws CryptoManagerException In case there was a error |
| | | * instantiating the Mac object. |
| | | */ |
| | | private static Mac getMacEngine(MacKeyEntry keyEntry) |
| | | throws CryptoManagerException |
| | | { |
| | | Mac mac; |
| | | try { |
| | | mac = Mac.getInstance(keyEntry.getType()); |
| | | } |
| | | catch (NoSuchAlgorithmException ex){ |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_MAC_ENGINE_INVALID_MAC_ALGORITHM.get( |
| | | keyEntry.getType(), getExceptionMessage(ex)), |
| | | ex); |
| | | } |
| | | |
| | | try { |
| | | mac.init(keyEntry.getSecretKey()); |
| | | } |
| | | catch (InvalidKeyException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_MAC_ENGINE_CANNOT_INITIALIZE.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return mac; |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public String getPreferredMessageDigestAlgorithm() |
| | | { |
| | | return preferredDigestAlgorithm; |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public MessageDigest getPreferredMessageDigest() |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(preferredDigestAlgorithm); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public MessageDigest getMessageDigest(String digestAlgorithm) |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(digestAlgorithm); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public byte[] digest(byte[] data) |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(preferredDigestAlgorithm). |
| | | digest(data); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public byte[] digest(String digestAlgorithm, byte[] data) |
| | | throws NoSuchAlgorithmException |
| | | { |
| | | return MessageDigest.getInstance(digestAlgorithm).digest(data); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public byte[] digest(InputStream inputStream) |
| | | throws IOException, NoSuchAlgorithmException |
| | | { |
| | | MessageDigest digest = |
| | | MessageDigest.getInstance(preferredDigestAlgorithm); |
| | | |
| | | byte[] buffer = new byte[8192]; |
| | | while (true) |
| | | { |
| | | int bytesRead = inputStream.read(buffer); |
| | | if (bytesRead < 0) |
| | | { |
| | | break; |
| | | } |
| | | |
| | | digest.update(buffer, 0, bytesRead); |
| | | } |
| | | |
| | | return digest.digest(); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public byte[] digest(String digestAlgorithm, |
| | | InputStream inputStream) |
| | | throws IOException, NoSuchAlgorithmException |
| | | { |
| | | MessageDigest digest = MessageDigest.getInstance(digestAlgorithm); |
| | | |
| | | byte[] buffer = new byte[8192]; |
| | | while (true) |
| | | { |
| | | int bytesRead = inputStream.read(buffer); |
| | | if (bytesRead < 0) |
| | | { |
| | | break; |
| | | } |
| | | |
| | | digest.update(buffer, 0, bytesRead); |
| | | } |
| | | |
| | | return digest.digest(); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public String getMacEngineKeyEntryID() |
| | | throws CryptoManagerException |
| | | { |
| | | return getMacEngineKeyEntryID(preferredMACAlgorithm, |
| | | preferredMACAlgorithmKeyLengthBits); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | 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(); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public Mac getMacEngine(String keyEntryID) |
| | | throws CryptoManagerException |
| | | { |
| | | final MacKeyEntry keyEntry = MacKeyEntry.getKeyEntry(this, |
| | | new KeyEntryID(keyEntryID)); |
| | | return (null == keyEntry) ? null : getMacEngine(keyEntry); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public byte[] encrypt(byte[] data) |
| | | throws GeneralSecurityException, CryptoManagerException |
| | | { |
| | | return encrypt(preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLengthBits, data); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public byte[] encrypt(String cipherTransformation, |
| | | int keyLengthBits, |
| | | byte[] data) |
| | | throws GeneralSecurityException, CryptoManagerException |
| | | { |
| | | Validator.ensureNotNull(cipherTransformation, data); |
| | | |
| | | CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry( |
| | | this, cipherTransformation, keyLengthBits); |
| | | if (null == keyEntry) { |
| | | keyEntry = CipherKeyEntry.generateKeyEntry(this, |
| | | cipherTransformation, keyLengthBits); |
| | | } |
| | | |
| | | final Cipher cipher |
| | | = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null); |
| | | |
| | | final byte[] keyID = keyEntry.getKeyID().getByteValue(); |
| | | final byte[] iv = cipher.getIV(); |
| | | final int prologueLength |
| | | = keyID.length + ((null == iv) ? 0 : iv.length); |
| | | final int dataLength = cipher.getOutputSize(data.length); |
| | | final byte[] cipherText = new byte[prologueLength + dataLength]; |
| | | System.arraycopy(keyID, 0, cipherText, 0, keyID.length); |
| | | if (null != iv) { |
| | | System.arraycopy(iv, 0, cipherText, keyID.length, iv.length); |
| | | } |
| | | System.arraycopy(cipher.doFinal(data), 0, cipherText, |
| | | prologueLength, dataLength); |
| | | return cipherText; |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public CipherOutputStream getCipherOutputStream( |
| | | OutputStream outputStream) throws CryptoManagerException |
| | | { |
| | | return getCipherOutputStream(preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLengthBits, outputStream); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public CipherOutputStream getCipherOutputStream( |
| | | String cipherTransformation, int keyLengthBits, |
| | | OutputStream outputStream) |
| | | throws CryptoManagerException |
| | | { |
| | | Validator.ensureNotNull(cipherTransformation, outputStream); |
| | | |
| | | CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry( |
| | | this, cipherTransformation, keyLengthBits); |
| | | if (null == keyEntry) { |
| | | keyEntry = CipherKeyEntry.generateKeyEntry(this, |
| | | cipherTransformation, keyLengthBits); |
| | | } |
| | | |
| | | final Cipher cipher |
| | | = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null); |
| | | final byte[] keyID = keyEntry.getKeyID().getByteValue(); |
| | | try { |
| | | outputStream.write(keyID); |
| | | if (null != cipher.getIV()) { |
| | | outputStream.write(cipher.getIV()); |
| | | } |
| | | } |
| | | catch (IOException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_GET_CIPHER_STREAM_PROLOGUE_WRITE_ERROR.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return new CipherOutputStream(outputStream, cipher); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public byte[] decrypt(byte[] data) |
| | | throws GeneralSecurityException, |
| | | CryptoManagerException |
| | | { |
| | | KeyEntryID keyID; |
| | | try { |
| | | final byte[] keyIDBytes |
| | | = new byte[KeyEntryID.getByteValueLength()]; |
| | | System.arraycopy(data, 0, keyIDBytes, 0, keyIDBytes.length); |
| | | keyID = new KeyEntryID(keyIDBytes); |
| | | } |
| | | catch (Exception ex) { |
| | | // IndexOutOfBoundsException, ArrayStoreException, ... |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get(), |
| | | ex); |
| | | } |
| | | |
| | | CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(this, keyID); |
| | | if (null == keyEntry) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get()); |
| | | } |
| | | |
| | | byte[] iv = null; |
| | | if (0 < keyEntry.getIVLengthBits()) { |
| | | iv = new byte[keyEntry.getIVLengthBits()/Byte.SIZE]; |
| | | try { |
| | | System.arraycopy(data, KeyEntryID.getByteValueLength(), iv, 0, |
| | | iv.length); |
| | | } |
| | | catch (Exception ex) { |
| | | // IndexOutOfBoundsException, ArrayStoreException, ... |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get(), ex); |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public CipherInputStream getCipherInputStream( |
| | | InputStream inputStream) throws CryptoManagerException |
| | | { |
| | | CipherKeyEntry keyEntry; |
| | | byte[] iv = null; |
| | | try { |
| | | final byte[] keyID = new byte[KeyEntryID.getByteValueLength()]; |
| | | if (keyID.length != inputStream.read(keyID)){ |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get()); |
| | | } |
| | | keyEntry = CipherKeyEntry.getKeyEntry(this, |
| | | new KeyEntryID(keyID)); |
| | | if (null == keyEntry) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get()); |
| | | } |
| | | |
| | | if (0 < keyEntry.getIVLengthBits()) { |
| | | iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE]; |
| | | if (iv.length != inputStream.read(iv)) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get()); |
| | | } |
| | | } |
| | | } |
| | | catch (IOException ex) { |
| | | throw new CryptoManagerException( |
| | | ERR_CRYPTOMGR_DECRYPT_CIPHER_INPUT_STREAM_ERROR.get( |
| | | getExceptionMessage(ex)), ex); |
| | | } |
| | | |
| | | return new CipherInputStream(inputStream, |
| | | getCipher(keyEntry, Cipher.DECRYPT_MODE, iv)); |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public int compress(byte[] src, byte[] dst) |
| | | { |
| | | Deflater deflater = new Deflater(); |
| | | try |
| | | { |
| | | deflater.setInput(src); |
| | | deflater.finish(); |
| | | |
| | | int compressedLength = deflater.deflate(dst); |
| | | if (deflater.finished()) |
| | | { |
| | | return compressedLength; |
| | | } |
| | | else |
| | | { |
| | | return -1; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | deflater.end(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public int uncompress(byte[] src, byte[] dst) |
| | | throws DataFormatException |
| | | { |
| | | Inflater inflater = new Inflater(); |
| | | try |
| | | { |
| | | inflater.setInput(src); |
| | | |
| | | int decompressedLength = inflater.inflate(dst); |
| | | if (inflater.finished()) |
| | | { |
| | | return decompressedLength; |
| | | } |
| | | else |
| | | { |
| | | int totalLength = decompressedLength; |
| | | |
| | | while (! inflater.finished()) |
| | | { |
| | | totalLength += inflater.inflate(dst); |
| | | } |
| | | |
| | | return -totalLength; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | inflater.end(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public SSLContext getSslContext(String sslCertNickname) |
| | | throws ConfigException |
| | | { |
| | | SSLContext sslContext; |
| | | try |
| | | { |
| | | TrustStoreBackend trustStoreBackend = getTrustStoreBackend(); |
| | | KeyManager[] keyManagers = trustStoreBackend.getKeyManagers(); |
| | | TrustManager[] trustManagers = |
| | | trustStoreBackend.getTrustManagers(); |
| | | |
| | | sslContext = SSLContext.getInstance("TLS"); |
| | | |
| | | if (sslCertNickname == null) |
| | | { |
| | | sslContext.init(keyManagers, trustManagers, null); |
| | | } |
| | | else |
| | | { |
| | | X509ExtendedKeyManager[] extendedKeyManagers = |
| | | SelectableCertificateKeyManager.wrap( |
| | | keyManagers, |
| | | sslCertNickname); |
| | | sslContext.init(extendedKeyManagers, trustManagers, null); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | Message message = |
| | | ERR_CRYPTOMGR_SSL_CONTEXT_CANNOT_INITIALIZE.get( |
| | | getExceptionMessage(e)); |
| | | throw new ConfigException(message, e); |
| | | } |
| | | |
| | | return sslContext; |
| | | } |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | public String getSslCertNickname() |
| | | { |
| | | return sslCertNickname; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public boolean isSslEncryption() |
| | | { |
| | | return sslEncryption; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public SortedSet<String> getSslProtocols() |
| | | { |
| | | return sslProtocols; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | public SortedSet<String> getSslCipherSuites() |
| | | { |
| | | return sslCipherSuites; |
| | | } |
| | | } |