| | |
| | | import org.opends.server.schema.BinarySyntax; |
| | | |
| | | /** |
| | | * This class provides the interface to the Directory Server |
| | | * cryptographic framework, which may be used for hashing, encryption, |
| | | * and other kinds of cryptographic operations. Note that it also |
| | | * contains methods for compressing and uncompressing data. Although |
| | | * these are not strictly cryptographic operations, there are a lot of |
| | | * similarities and it may be conceivable at some point that |
| | | * accelerated compression may be available just as it is for |
| | | * cryptographic operations. |
| | | This class implements the Directory Server cryptographic framework, |
| | | which is described in the |
| | | <a href="https://www.opends.org/wiki//page/TheCryptoManager"> |
| | | CrytpoManager design document</a>. {@code CryptoManager} implements |
| | | inter-OpenDS-instance authentication and authorization using the |
| | | ADS-based truststore, and secret key distribution. The interface also |
| | | provides methods for hashing, encryption, and other kinds of |
| | | cryptographic operations. |
| | | <p> |
| | | Note that it also contains methods for compressing and uncompressing |
| | | data: while these are not strictly cryptographic operations, there |
| | | 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. |
| | | */ |
| | | @org.opends.server.types.PublicAPI( |
| | | stability=org.opends.server.types.StabilityLevel.VOLATILE, |
| | |
| | | public CryptoManager(CryptoManagerCfg cfg) |
| | | throws ConfigException, InitializationException |
| | | { |
| | | if (!schemaInitDone) |
| | | { |
| | | if (!schemaInitDone) { |
| | | // Initialize various schema references. |
| | | attrKeyID = DirectoryServer.getAttributeType( |
| | | ConfigConstants.ATTR_CRYPTO_KEY_ID); |
| | |
| | | ocMacKey = DirectoryServer.getObjectClass( |
| | | ConfigConstants.OC_CRYPTO_MAC_KEY); |
| | | |
| | | try |
| | | { |
| | | try { |
| | | localTruststoreDN |
| | | = DN.decode(ConfigConstants.DN_TRUST_STORE_ROOT); |
| | | DN adminSuffixDN = DN.decode( |
| | |
| | | secretKeysDN = adminSuffixDN.concat( |
| | | DN.decode("cn=secret keys")); |
| | | } |
| | | catch (DirectoryException e) |
| | | { |
| | | throw new InitializationException(e.getMessageObject()); |
| | | catch (DirectoryException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new InitializationException(ex.getMessageObject()); |
| | | } |
| | | |
| | | schemaInitDone = true; |
| | |
| | | try{ |
| | | MessageDigest.getInstance(preferredDigestAlgorithm); |
| | | } |
| | | catch (Exception e) { |
| | | catch (Exception ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | |
| | | throw new InitializationException( |
| | | // TODO: i18n |
| | | Message.raw("Cannot get preferred digest: " + |
| | | getExceptionMessage(e).toString()), e); |
| | | getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | // Preferred MAC engine and validation. |
| | |
| | | preferredMACAlgorithm, |
| | | preferredMACAlgorithmKeyLengthBits); |
| | | } |
| | | catch (Exception e) { |
| | | catch (Exception ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | |
| | | throw new InitializationException( |
| | | // TODO: i18n |
| | | Message.raw("Cannot get preferred MAC engine: " + |
| | | getExceptionMessage(e).toString()), e); |
| | | getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | // Preferred encryption cipher and validation. |
| | |
| | | preferredCipherTransformation, |
| | | preferredCipherTransformationKeyLengthBits); |
| | | } |
| | | catch (Exception e) { |
| | | catch (Exception ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | |
| | | throw new InitializationException( |
| | | // TODO: i18n |
| | | Message.raw("Cannot get preferred encryption cipher: " + |
| | | getExceptionMessage(e).toString()), e); |
| | | getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | |
| | |
| | | preferredMACAlgorithmKeyLengthBits).getSecretKey(); |
| | | encodeSymmetricKeyAttribute(keyID, certificate, macKey); |
| | | } |
| | | catch (Exception e) { |
| | | catch (Exception ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | |
| | | throw new InitializationException( |
| | | // TODO: i18n |
| | | Message.raw("Cannot get preferred key wrapping cipher: " + |
| | | getExceptionMessage(e).toString()), e); |
| | | Message.raw("Cannot get preferred key wrapping cipher: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | sslCertNickname = cfg.getSSLCertNickname(); |
| | |
| | | } |
| | | } |
| | | catch (DirectoryException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Failed to retrieve %s.", entryDN.toString()), |
| | | ex); |
| | | Message.raw("Failed to retrieve %s: " |
| | | + getExceptionMessage(ex).toString(), |
| | | entryDN.toString()), ex); |
| | | } |
| | | return(certificate); |
| | | } |
| | |
| | | md = MessageDigest.getInstance(mdAlgorithmName); |
| | | } |
| | | catch (NoSuchAlgorithmException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Failed to get MessageDigest instance for %s", |
| | | Message.raw("Failed to get MessageDigest instance for" + |
| | | " %s: " + getExceptionMessage(ex).toString(), |
| | | mdAlgorithmName), ex); |
| | | } |
| | | return StaticUtils.bytesToHexNoSpace( |
| | |
| | | } |
| | | } |
| | | } catch (DirectoryException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Failed to publish %s.", entryDN.toString()), ex); |
| | | Message.raw("Failed to publish %s: " |
| | | + getExceptionMessage(ex).toString(), |
| | | entryDN.toString()), ex); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | catch (DirectoryException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Error retrieving instance-key public key" + |
| | | " certificates from ADS container %s.", |
| | | Message.raw("Error retrieving instance-key public key" |
| | | + " certificates from ADS container %s: " |
| | | + getExceptionMessage(ex).toString(), |
| | | instanceKeysDN.toString()), ex); |
| | | } |
| | | return(certificateMap); |
| | |
| | | wrappedKeyElement = StaticUtils.bytesToHexNoSpace(wrappedKey); |
| | | } |
| | | catch (GeneralSecurityException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Failed to wrap secret key: " + |
| | |
| | | = StaticUtils.hexStringToByteArray(elements[4]); |
| | | } |
| | | catch (ParseException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException(((null == fieldName) |
| | | // TODO: i18n |
| | | ? Message.raw("The syntax of the symmetric key" + |
| | |
| | | symmetricKeyAttribute) |
| | | : Message.raw("The syntax of the symmetric key" + |
| | | " attribute value \"%s\" is invalid. Parsing failed" + |
| | | " in field: %s.", symmetricKeyAttribute, fieldName)), |
| | | ex); |
| | | " in field: %s, offset %d.", symmetricKeyAttribute, |
| | | fieldName, ex.getErrorOffset())), ex); |
| | | } |
| | | |
| | | // Confirm key can be unwrapped at this instance. |
| | |
| | | .getKey(ConfigConstants.ADS_CERTIFICATE_ALIAS); |
| | | } |
| | | catch (ConfigException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("The instance-key-pair private-key is not" + |
| | | "available."), ex); |
| | | Message.raw("The instance-key-pair private-key is not" |
| | | + " available: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | catch (DirectoryException ex) { |
| | | // TODO: is DirectoryException reasonable for getKey() ? |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("The instance-key-pair private-key is not" + |
| | | "available."), ex); |
| | | Message.raw("The instance-key-pair private-key is not" |
| | | + " available: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | // Unwrap secret key. |
| | |
| | | wrappedKeyAlgorithmElement, |
| | | wrappedKeyTypeElement); |
| | | } catch(GeneralSecurityException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Failed to decipher the wrapped secret-key" + |
| | | " value."), ex); |
| | | Message.raw("Failed to decipher the wrapped secret-key" |
| | | + " value: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | return secretKey; |
| | |
| | | new KeyEntryID(keyEntryID)); |
| | | } |
| | | catch (IllegalArgumentException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("MAC key entry identifier \"%s\" is not" + |
| | | " a valid UUID.", keyEntryID), ex); |
| | | Message.raw("MAC key entry identifier \"%s\" is not" |
| | | + " a valid UUID: " |
| | | + getExceptionMessage(ex).toString(), |
| | | keyEntryID), ex); |
| | | } |
| | | |
| | | if (null == keyEntry) return null; |
| | | |
| | | return getMacEngine(keyEntry); |
| | | return (null == keyEntry) ? null : getMacEngine(keyEntry); |
| | | } |
| | | |
| | | |
| | |
| | | mac = Mac.getInstance(keyEntry.getType()); |
| | | } |
| | | catch (NoSuchAlgorithmException ex){ |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Invalid MAC algorithm specified: + %s", |
| | | Message.raw("Invalid MAC algorithm \"%s\": " |
| | | + getExceptionMessage(ex).toString(), |
| | | keyEntry.getType()), ex); |
| | | } |
| | | |
| | |
| | | mac.init(keyEntry.getSecretKey()); |
| | | } |
| | | catch (InvalidKeyException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Invalid key specification supplied to" + |
| | | " Mac object initialization"), ex); |
| | | Message.raw("Invalid key specification supplied to" |
| | | + " Mac object initialization: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | return mac; |
| | |
| | | } |
| | | catch (GeneralSecurityException ex) { |
| | | // NoSuchAlgorithmException, NoSuchPaddingException |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Invalid Cipher transformation specified:" |
| | | + " %s.", keyEntry.getType()), ex); |
| | | Message.raw("Invalid Cipher transformation \"%s\": " |
| | | + getExceptionMessage(ex).toString(), |
| | | keyEntry.getType()), ex); |
| | | } |
| | | |
| | | try { |
| | |
| | | } |
| | | catch (GeneralSecurityException ex) { |
| | | // InvalidKeyException, InvalidAlgorithmParameterException |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Error initializing cipher."), ex); |
| | | Message.raw("Error initializing cipher: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | return cipher; |
| | |
| | | outputStream.write(cipher.getIV()); |
| | | } |
| | | } |
| | | catch (IOException ioe) { |
| | | catch (IOException ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Exception when writing CryptoManager" + |
| | | " prologue."), ioe); |
| | | " prologue: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | return new CipherOutputStream(outputStream, cipher); |
| | |
| | | keyID = new KeyEntryID(keyIDBytes); |
| | | } |
| | | catch (Exception ex) { |
| | | // IndexOutOfBoundsException, ArrayStoreException, ... |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw( |
| | | "Exception when reading key identifier from" + |
| | | " data prologue."), ex); |
| | | Message.raw("Exception when reading key identifier" |
| | | + " from data prologue: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(this, keyID); |
| | |
| | | iv.length); |
| | | } |
| | | catch (Exception ex) { |
| | | // IndexOutOfBoundsException, ArrayStoreException, ... |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Exception when reading initialization" + |
| | | " vector from data prologue."), ex); |
| | | Message.raw("Exception when reading initialization" |
| | | + " vector from data prologue: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | } |
| | | catch (IOException ioe) { |
| | | catch (IOException ex) { |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw( |
| | | "IO exception when reading CryptoManager prologue."), |
| | | ioe); |
| | | Message.raw("Exception when reading CryptoManager" |
| | | + " prologue: " |
| | | + getExceptionMessage(ex).toString()), ex); |
| | | } |
| | | |
| | | return new CipherInputStream(inputStream, |
| | |
| | | if (secretKey == null) |
| | | { |
| | | // TODO: i18n |
| | | Message message = |
| | | Message.raw("Key entry %s contains no " + |
| | | Message message = Message.raw("Key entry %s contains no " + |
| | | "symmetric key value that can be decoded " + |
| | | "by this server", |
| | | entry.getDN()); |
| | | "by this server", entry.getDN()); |
| | | throw new CryptoManagerException(message); |
| | | } |
| | | |
| | |
| | | } |
| | | catch (DirectoryException e) |
| | | { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | // TODO: i18n |
| | | Message message = |
| | | Message.raw("Error decoding cipher key entry %s: %s", |
| | |
| | | } |
| | | catch (DirectoryException e) |
| | | { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | Message message = |
| | | Message.raw("Error decoding mac key entry %s: %s", |
| | | entry.getDN(), e.getMessage()); |
| | |
| | | fValue = UUID.fromString(keyEntryID); |
| | | } |
| | | catch (Exception ex) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ex); |
| | | } |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Key entry identifier \"%s\" has" + |
| | | " invalid syntax.", keyEntryID), ex); |
| | | " invalid syntax: " |
| | | + getExceptionMessage(ex).toString(), |
| | | keyEntryID), ex); |
| | | } |
| | | } |
| | | |
| | |
| | | throw new CryptoManagerException( |
| | | // TODO: i18n |
| | | Message.raw("Unable to produce key generator using" + |
| | | " key algorithm argument %s", |
| | | " key algorithm argument \"%s\": " |
| | | + getExceptionMessage(ex).toString(), |
| | | algorithm), ex); |
| | | } |
| | | keyGen.init(keyLengthBits, secureRandom); |