mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

david_page
13.28.2007 f8765f4b03f0eac32de52dfaf59ff1c06d5063f9
no issue
CryptoManager
- Add config test for encryption and key-wrapping cipher transformation syntax algorithm/mode/padding
- elide duplicate configuration validation and assignment code
3 files modified
413 ■■■■ changed files
opends/src/messages/messages/core.properties 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/CryptoManager.java 392 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java 15 ●●●● patch | view | raw | blame | history
opends/src/messages/messages/core.properties
@@ -1749,3 +1749,9 @@
 to instantiate a KeyGenerator for algorithm "%s":  %s
SEVERE_ERR_CRYPTOMGR_SYMMETRIC_KEY_ENTRY_ADD_FAILED_692=CryptoManager failed \
 to add locally produced symmetric key entry "%s":  %s
SEVERE_ERR_CRYPTOMGR_FULL_CIPHER_TRANSFORMATION_REQUIRED_693=CryptoManager \
 cipher transformation specification "%s" is invalid: it must be of the form \
 "algorithm/mode/padding"
SEVERE_ERR_CRYPTOMGR_FULL_KEY_WRAPPING_TRANSFORMATION_REQUIRED_694=CryptoManager \
 cipher transformation specification "%s" is invalid: it must be of the form \
 "algorithm/mode/padding"
opends/src/server/org/opends/server/types/CryptoManager.java
@@ -103,6 +103,11 @@
 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.
 @see "src/admin/defn/org/opends/server/admin/std\
                                      /CryptoManagerConfiguration.xml"
 @see org.opends.server.core.CryptoManagerSync
 @see org.opends.server.extensions.GetSymmetricKeyExtendedOperation
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
@@ -158,7 +163,8 @@
  // The preferred message digest algorithm for the Directory Server.
  private String preferredDigestAlgorithm;
  // The map from encryption key ID to MacKeyEntry (cache).
  // The map from encryption key ID to MacKeyEntry (cache). The cache
  // is accessed by methods that request, publish, and import keys.
  private final Map<KeyEntryID, MacKeyEntry> macKeyEntryCache
          = new ConcurrentHashMap<KeyEntryID, MacKeyEntry>();
@@ -168,7 +174,9 @@
  // The preferred key length for the preferred MAC algorithm.
  private int preferredMACAlgorithmKeyLengthBits;
  // The map from encryption key ID to CipherKeyEntry (cache).
  // The map from encryption key ID to CipherKeyEntry (cache). The
  // cache is accessed by methods that request, publish, and import
  // keys.
  private final Map<KeyEntryID, CipherKeyEntry> cipherKeyEntryCache
       = new ConcurrentHashMap<KeyEntryID, CipherKeyEntry>();
@@ -193,9 +201,10 @@
  // The set of SSL cipher suites enabled or null for the default set.
  private final SortedSet<String> sslCipherSuites;
  /**
   * Creates a new instance of this crypto manager object from a given
   * configuration.
   * configuration, plus some static member initialization.
   *
   * @param   cfg  The configuration of this crypto manager.
   *
@@ -260,68 +269,134 @@
      schemaInitDone = true;
    }
    // Preferred digest and validation.
    preferredDigestAlgorithm = cfg.getDigestAlgorithm();
    try{
      MessageDigest.getInstance(preferredDigestAlgorithm);
    // CryptoMangager crypto config parameters.
    List<Message> why = new LinkedList<Message>();
    if (! isConfigurationChangeAcceptable(cfg, why)) {
      throw new InitializationException(why.get(0));
    }
    catch (Exception ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
    applyConfigurationChange(cfg);
    // Secure replication related...
    sslCertNickname = cfg.getSSLCertNickname();
    sslEncryption   = cfg.isSSLEncryption();
    sslProtocols    = cfg.getSSLProtocol();
    sslCipherSuites = cfg.getSSLCipherSuite();
    // Register as a configuration change listener.
    cfg.addChangeListener(this);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
       CryptoManagerCfg cfg,
       List<Message> unacceptableReasons)
  {
    // Acceptable until we find an error.
    boolean acceptable = true;
    // Preferred digest validation.
    String preferredDigestAlgorithm =
         cfg.getDigestAlgorithm();
    if (!preferredDigestAlgorithm.equals(
         this.preferredDigestAlgorithm))
    {
      try{
        MessageDigest.getInstance(preferredDigestAlgorithm);
      }
      throw new InitializationException(
              ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_DIGEST.get(
                      getExceptionMessage(ex)), ex);
      catch (Exception ex) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        unacceptableReasons.add(
             ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_DIGEST.get(
                  getExceptionMessage(ex)));
        acceptable = false;
      }
    }
    // Preferred MAC engine and validation.
    preferredMACAlgorithm = cfg.getMacAlgorithm();
    preferredMACAlgorithmKeyLengthBits =
    // Preferred MAC algorithm validation.
    String preferredMACAlgorithm = cfg.getMacAlgorithm();
    Integer preferredMACAlgorithmKeyLengthBits =
         cfg.getMacKeyLength();
    try {
      MacKeyEntry.generateKeyEntry(null,
              preferredMACAlgorithm,
              preferredMACAlgorithmKeyLengthBits);
    }
    catch (Exception ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
    if (!preferredMACAlgorithm.equals(this.preferredMACAlgorithm) ||
         preferredMACAlgorithmKeyLengthBits !=
              this.preferredMACAlgorithmKeyLengthBits)
    {
      try {
        MacKeyEntry.generateKeyEntry(
             null,
             preferredMACAlgorithm,
             preferredMACAlgorithmKeyLengthBits);
      }
      throw new InitializationException(
              ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_MAC_ENGINE.get(
                      getExceptionMessage(ex)), ex);
      catch (Exception ex) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        unacceptableReasons.add(
             ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_MAC_ENGINE.get(
                  getExceptionMessage(ex)));
        acceptable = false;
      }
    }
    // Preferred encryption cipher and validation.
    preferredCipherTransformation =
    // Preferred encryption cipher validation.
    String preferredCipherTransformation =
         cfg.getCipherTransformation();
    preferredCipherTransformationKeyLengthBits =
    Integer preferredCipherTransformationKeyLengthBits =
         cfg.getCipherKeyLength();
    try {
      CipherKeyEntry.generateKeyEntry(null,
              preferredCipherTransformation,
              preferredCipherTransformationKeyLengthBits);
    }
    catch (Exception ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
    if (! preferredCipherTransformation.equals(
            this.preferredCipherTransformation) ||
        preferredCipherTransformationKeyLengthBits !=
            this.preferredCipherTransformationKeyLengthBits) {
      if (3 != preferredCipherTransformation.split("/",0).length) {
        unacceptableReasons.add(
                ERR_CRYPTOMGR_FULL_CIPHER_TRANSFORMATION_REQUIRED.get(
                        preferredCipherTransformation));
        acceptable = false;
      }
      throw new InitializationException(
      else {
        try {
          CipherKeyEntry.generateKeyEntry(
                  null,
                  preferredCipherTransformation,
                  preferredCipherTransformationKeyLengthBits);
        }
        catch (Exception ex) {
          if (debugEnabled()) {
            TRACER.debugCaught(DebugLogLevel.ERROR, ex);
          }
          unacceptableReasons.add(
             ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_ENCRYPTION_CIPHER.get(
                     getExceptionMessage(ex)), ex);
                          getExceptionMessage(ex)));
          acceptable = false;
        }
      }
    }
    // Preferred secret key wrapping cipher and validation. Depends
    // on MAC cipher for a candidate secret key. Note that the
    // TrustStoreBackend not available at this point, hence a "dummy"
    // certificate must be used to validate the choice of secret key
    // wrapping cipher.
    // TODO: Trying OAEPWITHSHA-512ANDMGF1PADDING throws an exception
    // "Key too small...".
    preferredKeyWrappingTransformation
    // Preferred secret key wrapping cipher and validation. Validation
    // depends on MAC cipher for secret key.
    String preferredKeyWrappingTransformation
            = cfg.getKeyWrappingTransformation();
    try {
      final String certificateBase64 =
    if (!preferredKeyWrappingTransformation.equals(
         this.preferredKeyWrappingTransformation)) {
      if (3 != preferredKeyWrappingTransformation
                                              .split("/", 0).length) {
        unacceptableReasons.add(
          ERR_CRYPTOMGR_FULL_KEY_WRAPPING_TRANSFORMATION_REQUIRED.get(
                        preferredKeyWrappingTransformation));
        acceptable = false;
      }
      else {
        try {
          /* Note that the TrustStoreBackend not available at initial,
             CryptoManager configuration, hence a "dummy" certificate
             must be used to validate the choice of secret key
             wrapping cipher. Otherwise, call
             getInstanceKeyCertificateFromLocalTruststore() */
          final String certificateBase64 =
      "MIIB2jCCAUMCBEb7wpYwDQYJKoZIhvcNAQEEBQAwNDEbMBkGA1UEChMST3B" +
      "lbkRTIENlcnRpZmljYXRlMRUwEwYDVQQDEwwxMC4wLjI0OC4yNTEwHhcNMD" +
      "cwOTI3MTQ0NzUwWhcNMjcwOTIyMTQ0NzUwWjA0MRswGQYDVQQKExJPcGVuR" +
@@ -333,29 +408,61 @@
      "jucN34MZwvzbmFHT/leUu3/cpykbGM9HL2QUX7iKvv2LJVqexhj7CLoXxZP" +
      "oNL+HHKW0vi5/7W5KwOZsPqKI2SdYV7nDqTZklm5ZP0gmIuNO6mTqBRtC2D" +
      "lplX1Iq+BrQJAmteiPtwhdZD+EIghe51CaseImjlLlY2ZK8w==";
      final byte[] certificate = Base64.decode(certificateBase64);
      final String keyID = getInstanceKeyID(certificate);
      final SecretKey macKey = MacKeyEntry.generateKeyEntry(null,
              preferredMACAlgorithm,
              preferredMACAlgorithmKeyLengthBits).getSecretKey();
      encodeSymmetricKeyAttribute(keyID, certificate, macKey);
    }
    catch (Exception ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new InitializationException(
          final byte[] certificate = Base64.decode(certificateBase64);
          final String keyID = getInstanceKeyID(certificate);
          preferredKeyWrappingTransformation
            = cfg.getKeyWrappingTransformation();
          final SecretKey macKey =
                  MacKeyEntry.generateKeyEntry(
                          null,
                          preferredMACAlgorithm,
                          preferredMACAlgorithmKeyLengthBits).
                          getSecretKey();
          encodeSymmetricKeyAttribute(
                  preferredKeyWrappingTransformation, keyID,
                  certificate, macKey);
        }
        catch (Exception ex) {
          if (debugEnabled()) {
            TRACER.debugCaught(DebugLogLevel.ERROR, ex);
          }
          unacceptableReasons.add(
           ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_KEY_WRAPPING_CIPHER.get(
                   getExceptionMessage(ex)), ex);
                          getExceptionMessage(ex)));
          acceptable = false;
        }
      }
    }
    sslCertNickname = cfg.getSSLCertNickname();
    sslEncryption   = cfg.isSSLEncryption();
    sslProtocols    = cfg.getSSLProtocol();
    sslCipherSuites = cfg.getSSLCipherSuite();
    return acceptable;
  }
    // Register as a configuration change listener.
    cfg.addChangeListener(this);
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
       CryptoManagerCfg cfg)
  {
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    List<Message> messages = new ArrayList<Message>();
    preferredDigestAlgorithm =
         cfg.getDigestAlgorithm();
    preferredMACAlgorithm =
         cfg.getMacAlgorithm();
    preferredMACAlgorithmKeyLengthBits =
         cfg.getMacKeyLength();
    preferredCipherTransformation =
         cfg.getCipherTransformation();
    preferredCipherTransformationKeyLengthBits =
         cfg.getCipherKeyLength();
    preferredKeyWrappingTransformation =
         cfg.getKeyWrappingTransformation();
    return new ConfigChangeResult(resultCode,
                                  adminActionRequired, messages);
  }
@@ -3314,152 +3421,5 @@
      super(message, cause);
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
       CryptoManagerCfg cfg,
       List<Message> unacceptableReasons)
  {
    // Acceptable until we find an error.
    boolean acceptable = true;
    // Preferred digest validation.
    String preferredDigestAlgorithm =
         cfg.getDigestAlgorithm();
    if (!preferredDigestAlgorithm.equals(
         this.preferredDigestAlgorithm))
    {
      try{
        MessageDigest.getInstance(preferredDigestAlgorithm);
      }
      catch (Exception ex) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        unacceptableReasons.add(
             ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_DIGEST.get(
                  getExceptionMessage(ex)));
        acceptable = false;
      }
    }
    // Preferred MAC algorithm validation.
    String preferredMACAlgorithm = cfg.getMacAlgorithm();
    Integer preferredMACAlgorithmKeyLengthBits =
         cfg.getMacKeyLength();
    if (!preferredMACAlgorithm.equals(this.preferredMACAlgorithm) ||
         preferredMACAlgorithmKeyLengthBits !=
              this.preferredMACAlgorithmKeyLengthBits)
    {
      try {
        MacKeyEntry.generateKeyEntry(
             null,
             preferredMACAlgorithm,
             preferredMACAlgorithmKeyLengthBits);
      }
      catch (Exception ex) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        unacceptableReasons.add(
             ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_MAC_ENGINE.get(
                  getExceptionMessage(ex)));
        acceptable = false;
      }
    }
    // Preferred encryption cipher validation.
    String preferredCipherTransformation =
         cfg.getCipherTransformation();
    Integer preferredCipherTransformationKeyLengthBits =
         cfg.getCipherKeyLength();
    if (!preferredCipherTransformation.equals(
         this.preferredCipherTransformation) ||
         preferredCipherTransformationKeyLengthBits !=
              this.preferredCipherTransformationKeyLengthBits)
    {
      try {
        CipherKeyEntry.generateKeyEntry(
             null,
             preferredCipherTransformation,
             preferredCipherTransformationKeyLengthBits);
      }
      catch (Exception ex) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        unacceptableReasons.add(
             ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_ENCRYPTION_CIPHER.get(
                  getExceptionMessage(ex)));
        acceptable = false;
      }
    }
    // Preferred secret key wrapping cipher and validation. Depends
    // on MAC cipher for secret key. Note that the TrustStoreBackend
    // not available at this point, hence a "dummy" certificate must
    // be used to validate the choice of secret key wrapping cipher.
    String preferredKeyWrappingTransformation
            = cfg.getKeyWrappingTransformation();
    if (!preferredKeyWrappingTransformation.equals(
         this.preferredKeyWrappingTransformation))
    {
      try {
        final byte[] certificate =
             getInstanceKeyCertificateFromLocalTruststore();
        final String keyID = getInstanceKeyID(certificate);
        final SecretKey macKey =
             MacKeyEntry.generateKeyEntry(
                  null,
                  preferredMACAlgorithm,
                  preferredMACAlgorithmKeyLengthBits).
                  getSecretKey();
        encodeSymmetricKeyAttribute(
             preferredKeyWrappingTransformation, keyID,
             certificate, macKey);
      }
      catch (Exception ex) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        unacceptableReasons.add(
          ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_KEY_WRAPPING_CIPHER.get(
                  getExceptionMessage(ex)));
        acceptable = false;
      }
    }
    return acceptable;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
       CryptoManagerCfg cfg)
  {
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    List<Message> messages = new ArrayList<Message>();
    preferredDigestAlgorithm =
         cfg.getDigestAlgorithm();
    preferredMACAlgorithm =
         cfg.getMacAlgorithm();
    preferredMACAlgorithmKeyLengthBits =
         cfg.getMacKeyLength();
    preferredCipherTransformation =
         cfg.getCipherTransformation();
    preferredCipherTransformationKeyLengthBits =
         cfg.getCipherKeyLength();
    preferredKeyWrappingTransformation =
         cfg.getKeyWrappingTransformation();
    return new ConfigChangeResult(resultCode,
                                  adminActionRequired, messages);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
@@ -95,7 +95,8 @@
  public void testGetInstanceKeyCertificate()
          throws Exception {
    final CryptoManager cm = DirectoryServer.getCryptoManager();
    final byte[] cert = cm.getInstanceKeyCertificateFromLocalTruststore();
    final byte[] cert
            = CryptoManager.getInstanceKeyCertificateFromLocalTruststore();
    assertNotNull(cert);
    // The certificate should now be accessible in the truststore backend via LDAP.
@@ -128,8 +129,8 @@
         md.digest(ldapCert)).equals(cm.getInstanceKeyID()));
    // Call twice to ensure idempotent. 
    cm.publishInstanceKeyEntryInADS();
    cm.publishInstanceKeyEntryInADS();
    CryptoManager.publishInstanceKeyEntryInADS();
    CryptoManager.publishInstanceKeyEntryInADS();
  }
  @Test
@@ -201,7 +202,7 @@
    // default (preferred) AES/CBC/PKCS5Padding 128bit key.
    paramList.add(new CipherParameters(null, null, null, 128, 128));
    // custom
// TODO:  paramList.add(new CipherParameters("Blowfish", "CFB", "NoPadding", 192, 64));
// TODO: paramList.add(new CipherParameters("Blowfish", "CFB", "NoPadding", 448, 64));
    paramList.add(new CipherParameters("Blowfish", "CFB", "NoPadding", 128, 64));
    paramList.add(new CipherParameters("RC4", null, null, 104, 0));
    paramList.add(new CipherParameters("DES", "CFB", "NoPadding", 56, 56));
@@ -453,6 +454,12 @@
  }
  /**
   TODO: Test key wrapping
   Trying OAEPWITHSHA-512ANDMGF1PADDING throws an exception "Key too small...".
   */
  /**
   TODO: Test the secret key synchronization protocol.
     1. Create the first instance; add reversible password storage scheme