All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.util.BouncyCastleFIPSHelper Maven / Gradle / Ivy

Go to download

The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use Java API for communicating with LDAP directory servers and performing related tasks like reading and writing LDIF, encoding and decoding data using base64 and ASN.1 BER, and performing secure communication. This package contains the Standard Edition of the LDAP SDK, which is a complete, general-purpose library for communicating with LDAPv3 directory servers.

There is a newer version: 7.0.2
Show newest version
/*
 * Copyright 2021-2023 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2021-2023 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2021-2023 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.util;



import java.io.File;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.unboundid.ldap.sdk.InternalSDKHelper;

import static com.unboundid.util.UtilityMessages.*;



/**
 * This class provides a helper to ensure that the Bouncy Castle FIPS provider
 * is properly loaded into the JVM so that the provider can be used for
 * cryptographic processing.  The appropriate jar file (typically
 * "bc-fips-{version}.jar") must be available in the JVM classpath.
 */
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class BouncyCastleFIPSHelper
{
  /**
   * A reference to the Bouncy Castle FIPS provider, if one is available.
   */
  @NotNull private static final AtomicReference
       BOUNCY_CASTLE_FIPS_PROVIDER = new AtomicReference<>();



  /**
   * A reference to the Bouncy Castle JSSE provider, if one is available.
   */
  @NotNull private static final AtomicReference
       BOUNCY_CASTLE_JSSE_PROVIDER = new AtomicReference<>();



  /**
   * A reference to the class that implements the Bouncy Castle FIPS provider,
   * if available.
   */
  @NotNull private static final AtomicReference>
       BOUNCY_CASTLE_FIPS_PROVIDER_CLASS = new AtomicReference<>();



  /**
   * A reference to the class that implements the Bouncy Castle JSSE provider,
   * if one is available.
   */
  @NotNull private static final AtomicReference>
       BOUNCY_CASTLE_JSSE_PROVIDER_CLASS = new AtomicReference<>();



  /**
   * The fully qualified name of the Java class that should be used as the
   * Bouncy Castle FIPS provider.
   */
  @NotNull public static final String
       BOUNCY_CASTLE_FIPS_PROVIDER_CLASS_NAME =
            "org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider";



  /**
   * The fully qualified name of the Java class that should be used as the
   * Bouncy Castle JSSE provider.
   */
  @NotNull public static final String
       BOUNCY_CASTLE_JSSE_PROVIDER_CLASS_NAME =
            "org.bouncycastle.jsse.provider.BouncyCastleJsseProvider";



  /**
   * The name that may be used to reference the Bouncy Castle FIPS provider.
   */
  @NotNull public static final String FIPS_PROVIDER_NAME = "BCFIPS";



  /**
   * The name that may be used to reference the Bouncy Castle JSSE provider.
   */
  @NotNull public static final String JSSE_PROVIDER_NAME = "BCJSSE";



  /**
   * The key store type name that should be used to reference the Bouncy Castle
   * FIPS key store.
   */
  @NotNull public static final String FIPS_KEY_STORE_TYPE = "BCFKS";



  /**
   * The name of the default key manager factory algorithm that should be used
   * with the Bouncy Castle JSSE provider.
   */
  @NotNull public static final String
       DEFAULT_KEY_MANAGER_FACTORY_ALGORITHM = "X.509";



  /**
   * The name of the SSLContext protocol that should be used when requesting
   * the default context from the Bouncy Castle JSSE provider.
   */
  @NotNull public static final String DEFAULT_SSL_CONTEXT_PROTOCOL =
       "DEFAULT";



  /**
   * The names of alternative SSLContext protocols that should be used when
   * requesting the default context from the Bouncy Castle JSSE provider when
   * the preferred default protocol is not available.
   */
  @NotNull public static final String[]
       ALTERNATIVE_DEFAULT_SSL_CONTEXT_PROTOCOLS = { "TLSv1.3", "TLSv1.2" };



  /**
   * The name of the default key manager factory algorithm that should be used
   * with the Bouncy Castle JSSE provider.
   */
  @NotNull public static final String
       DEFAULT_TRUST_MANAGER_FACTORY_ALGORITHM = "PKIX";



  /**
   * The name of a Java property (org.bouncycastle.rsa.allow_multi_use) that the
   * Bouncy Castle FIPS provider uses to determine whether to allow the same RSA
   * key to be used for multiple purposes (for example, both for
   * signing/verifying and encrypting/decrypting or use in TLS negotiation).
   */
  @NotNull public static final String PROPERTY_ALLOW_RSA_MULTI_USE =
       "org.bouncycastle.rsa.allow_multi_use";



  /**
   * The name of a Java property (org.bouncycastle.fips.approved_only) that the
   * Bouncy Castle FIPS provider uses to determine whether to start in approved
   * mode, in which non-approved functionality will be disabled.
   */
  @NotNull public static final String PROPERTY_APPROVED_ONLY =
       "org.bouncycastle.fips.approved_only";



  /**
   * The name of a Java property (org.bouncycastle.jca.enable_jks) that the
   * Bouncy Castle FIPS provider uses to determine whether to allow the use of
   * JKS key stores to access certificates.
   */
  @NotNull public static final String PROPERTY_ENABLE_JKS =
       "org.bouncycastle.jca.enable_jks";



  /**
   * The name of a Java property
   * (com.unboundid.util.BouncyCastleFIPSHelper.ENABLE_LOGGING) that indicates
   * whether to enable or disable the Bouncy Castle JSSE provider's logging.
   */
  @NotNull public static final String PROPERTY_ENABLE_LOGGING =
       BouncyCastleFIPSHelper.class.getName() + ".ENABLE_LOGGING";



  /**
   * The name of a Java property
   * (com.unboundid.util.BouncyCastleFIPSHelper.LOG_LEVEL) that can be used to
   * set the default log level for the Bouncy Castle JSSE provider's logging.
   * This will only be used if the {@link #PROPERTY_ENABLE_LOGGING} property is
   * set to {@code true}, and the value must match the name of one of of the
   * {@code java.util.logging.Level} constants ({@code SEVERE},
   * {@code WARNING}, {@code INFO}, {@code CONFIG}, {@code  FINE},
   * {@code FINER}, {@code FINEST}, {@code ALL}, or {@code OFF}).
   */
  @NotNull public static final String PROPERTY_LOG_LEVEL =
       BouncyCastleFIPSHelper.class.getName() + ".LOG_LEVEL";



  /**
   * The name of the logger used for Bouncy Castle functionality.
   */
  @NotNull private static final String LOGGER_NAME = "org.bouncycastle";



  /**
   * The logger for the Bouncy Castle JSSE provider's logging.
   */
  @NotNull private static final Logger LOGGER = Logger.getLogger(LOGGER_NAME);
  static
  {
    LOGGER.setUseParentHandlers(false);

    final String enabledPropertyValue =
         StaticUtils.getSystemProperty(PROPERTY_ENABLE_LOGGING);
    if ("true".equalsIgnoreCase(enabledPropertyValue))
    {
      Level level = Level.INFO;
      final String levelPropertyValue =
           StaticUtils.getSystemProperty(PROPERTY_LOG_LEVEL);
      if (levelPropertyValue != null)
      {
        switch (StaticUtils.toUpperCase(levelPropertyValue))
        {
          case "SEVERE":
            level = Level.SEVERE;
            break;
          case "WARNING":
            level = Level.WARNING;
            break;
          case "INFO":
            level = Level.INFO;
            break;
          case "CONFIG":
            level = Level.CONFIG;
            break;
          case "FINE":
            level = Level.FINE;
            break;
          case "FINER":
            level = Level.FINER;
            break;
          case "FINEST":
            level = Level.FINEST;
            break;
          case "ALL":
            level = Level.ALL;
            break;
          case "OFF":
            level = Level.OFF;
            break;
          default:
            Validator.violation("Unsupported " + PROPERTY_LOG_LEVEL +
                 " property value '" + levelPropertyValue + "'.");
            break;
        }
      }

      StaticUtils.setLoggerLevel(LOGGER, level);
    }
    else
    {
      StaticUtils.setLoggerLevel(LOGGER, Level.OFF);
    }
  }



  /**
   * Prevents this utility class from being instantiated.
   */
  private BouncyCastleFIPSHelper()
  {
    // No implementation required.
  }



  /**
   * Retrieves a reference to the the Bouncy Castle FIPS provider.
   *
   * @return   The Bouncy Castle FIPS provider instance.  It will not be
   *           {@code null}.
   *
   * @throws  NoSuchProviderException  If the Bouncy Castle FIPS provider is
   *                                   not available in the JVM.
   */
  @NotNull()
  public static Provider getBouncyCastleFIPSProvider()
         throws NoSuchProviderException
  {
    final Provider provider = BOUNCY_CASTLE_FIPS_PROVIDER.get();
    if (provider == null)
    {
      return loadBouncyCastleFIPSProvider(false);
    }
    else
    {
      return provider;
    }
  }



  /**
   * Loads the Bouncy Castle FIPS provider into the JVM, if it has not already
   * been loaded.
   *
   * @param  makeDefault  Indicates whether to make the Bouncy Castle FIPS
   *                      provider the default provider in the JVM.
   *
   * @return  The provider that was loaded.  It will not be {@code null}.
   *
   * @throws  NoSuchProviderException  If the Bouncy Castle FIPS provider is
   *                                   not available in the JVM.
   */
  @NotNull()
  static synchronized Provider loadBouncyCastleFIPSProvider(
                                    final boolean makeDefault)
          throws NoSuchProviderException
  {
    // If the provider class has already been loaded through some means, then
    // just return it.
    try
    {
      final Provider existingProvider =
           Security.getProvider(FIPS_PROVIDER_NAME);
      if (existingProvider != null)
      {
        BOUNCY_CASTLE_FIPS_PROVIDER.compareAndSet(null, existingProvider);
        return existingProvider;
      }
    }
    catch (final Exception e)
    {
      Debug.debugException(e);
    }


    // Load the provider class.  If this fails, then the Bouncy Castle FIPS
    // provider is not in the classpath.
    Class fipsProviderClass = BOUNCY_CASTLE_FIPS_PROVIDER_CLASS.get();
    if (fipsProviderClass == null)
    {
      try
      {
        fipsProviderClass =
             Class.forName(BOUNCY_CASTLE_FIPS_PROVIDER_CLASS_NAME);
        BOUNCY_CASTLE_FIPS_PROVIDER_CLASS.set(fipsProviderClass);
      }
      catch (final Exception e)
      {
        Debug.debugException(e);

        // Before giving up, check to see if the LDAP SDK is part of a Ping
        // Identity Directory Server installation, and if so, whether we can
        // find the library as part of that installation.
        boolean shouldThrow = true;
        try
        {
          final File instanceRoot =
               InternalSDKHelper.getPingIdentityServerRoot();
          if (instanceRoot != null)
          {
            File fipsProviderJarFile = null;
            File fipsJSSEProviderJarFile = null;
            final File resourceDir = new File(instanceRoot, "resource");
            final File bcDir = new File(resourceDir, "bc");
            final File fipsDir = new File(bcDir, "fips");
            if (fipsDir.exists())
            {
              for (final File f : fipsDir.listFiles())
              {
                final String name = f.getName();
                if (name.startsWith("bc-fips-") && name.endsWith(".jar"))
                {
                  fipsProviderJarFile = f;
                }
                else if (name.startsWith("bctls-fips-") &&
                     name.endsWith(".jar"))
                {
                  fipsJSSEProviderJarFile = f;
                }
              }
            }

            if ((fipsProviderJarFile != null) &&
                 (fipsJSSEProviderJarFile != null))
            {
              final URL[] fileURLs =
              {
                fipsProviderJarFile.toURI().toURL(),
                fipsJSSEProviderJarFile.toURI().toURL()
              };

              final URLClassLoader classLoader = new URLClassLoader(fileURLs,
                   BouncyCastleFIPSHelper.class.getClassLoader());
              fipsProviderClass = classLoader.loadClass(
                   BOUNCY_CASTLE_FIPS_PROVIDER_CLASS_NAME);
              BOUNCY_CASTLE_JSSE_PROVIDER_CLASS.set(classLoader.loadClass(
                   BOUNCY_CASTLE_JSSE_PROVIDER_CLASS_NAME));
              BOUNCY_CASTLE_FIPS_PROVIDER_CLASS.set(fipsProviderClass);
              shouldThrow = false;
            }
          }
        }
        catch (final Exception e2)
        {
          Debug.debugException(e2);
        }

        if (shouldThrow)
        {
          throw new NoSuchProviderException(
               ERR_BC_FIPS_HELPER_CANNOT_LOAD_FIPS_PROVIDER_CLASS.get(
                    BOUNCY_CASTLE_FIPS_PROVIDER_CLASS_NAME,
                    StaticUtils.getExceptionMessage(e)));
        }
      }
    }


    // Instantiate the provider class.
    final Provider provider;
    try
    {
      final Constructor constructor =
           fipsProviderClass.getConstructor(String.class);
      provider = (Provider) constructor.newInstance("C:HYBRID;ENABLE{All};");

      if (makeDefault)
      {
        Security.insertProviderAt(provider, 1);
      }
      else
      {
        Security.addProvider(provider);
      }

      BOUNCY_CASTLE_FIPS_PROVIDER.set(provider);
      return provider;
    }
    catch (final Exception e)
    {
      Debug.debugException(e);
      throw new NoSuchProviderException(
           ERR_BC_FIPS_PROVIDER_CANNOT_INSTANTIATE_FIPS_PROVIDER.get(
                BOUNCY_CASTLE_FIPS_PROVIDER_CLASS_NAME,
                StaticUtils.getExceptionMessage(e)));
    }
  }



  /**
   * Retrieves a reference to the the Bouncy Castle JSSE provider.
   *
   * @return   The Bouncy Castle JSSE provider instance.  It will not be
   *           {@code null}.
   *
   * @throws  NoSuchProviderException  If the Bouncy Castle JSSE provider is
   *                                   not available in the JVM.
   */
  @NotNull()
  public static Provider getBouncyCastleJSSEProvider()
         throws NoSuchProviderException
  {
    final Provider provider = BOUNCY_CASTLE_JSSE_PROVIDER.get();
    if (provider == null)
    {
      return loadBouncyCastleJSSEProvider(false);
    }
    else
    {
      return provider;
    }
  }



  /**
   * Loads the Bouncy Castle JSSE provider into the JVM, if it has not already
   * been loaded.
   *
   * @param  makeSecond  Indicates whether to make the Bouncy Castle JSSE
   *                     provider second in the JVM's search order (presumably
   *                     after the Bouncy Castle FIPS provider as the first
   *                     provider, in which case the Bouncy Castle FIPS provider
   *                     must have already been loaded and made first).
   *
   * @return  The provider that was loaded.  It will not be {@code null}.
   *
   * @throws  NoSuchProviderException  If the Bouncy Castle JSSE provider is
   *                                   not available in the JVM.
   */
  @NotNull()
  static synchronized Provider loadBouncyCastleJSSEProvider(
                                    final boolean makeSecond)
          throws NoSuchProviderException
  {
    // If the provider class has already been loaded through some means, then
    // just return it.
    try
    {
      final Provider existingProvider =
           Security.getProvider(JSSE_PROVIDER_NAME);
      if (existingProvider != null)
      {
        BOUNCY_CASTLE_JSSE_PROVIDER.compareAndSet(null, existingProvider);
        return existingProvider;
      }
    }
    catch (final Exception e)
    {
      Debug.debugException(e);
    }


    // Load the provider class.  If this fails, then the Bouncy Castle JSSE
    // provider is not in the classpath.
    Class jsseProviderClass = BOUNCY_CASTLE_JSSE_PROVIDER_CLASS.get();
    if (jsseProviderClass == null)
    {
      try
      {
        jsseProviderClass =
             Class.forName(BOUNCY_CASTLE_JSSE_PROVIDER_CLASS_NAME);
        BOUNCY_CASTLE_JSSE_PROVIDER_CLASS.set(jsseProviderClass);
      }
      catch (final Exception e)
      {
        Debug.debugException(e);
        throw new NoSuchProviderException(
             ERR_BC_FIPS_HELPER_CANNOT_LOAD_JSSE_PROVIDER_CLASS.get(
                  BOUNCY_CASTLE_JSSE_PROVIDER_CLASS_NAME,
                  StaticUtils.getExceptionMessage(e)));
      }
    }


    // Instantiate the provider class.
    final Provider provider;
    try
    {
      final Constructor constructor =
           jsseProviderClass.getConstructor(String.class);
      provider = (Provider) constructor.newInstance("fips:BCFIPS");

      if (makeSecond)
      {
        Security.insertProviderAt(provider, 2);
      }
      else
      {
        Security.addProvider(provider);
      }

      BOUNCY_CASTLE_JSSE_PROVIDER.set(provider);
      return provider;
    }
    catch (final Exception e)
    {
      Debug.debugException(e);
      throw new NoSuchProviderException(
           ERR_BC_FIPS_PROVIDER_CANNOT_INSTANTIATE_JSSE_PROVIDER.get(
                BOUNCY_CASTLE_JSSE_PROVIDER_CLASS_NAME,
                StaticUtils.getExceptionMessage(e)));
    }
  }



  /**
   * Disables logging for Bouncy Castle functionality.
   */
  public static void disableLogging()
  {
    StaticUtils.setLoggerLevel(LOGGER, Level.OFF);
    LOGGER.setUseParentHandlers(false);
  }



  /**
   * Enables logging for Bouncy Castle functionality.
   *
   * @param  level  The logging level to use.  If it is {@code null}, then a
   *                default level of {@code INFO} will be used.
   *
   * @return  The logger used for Bouncy Castle functionality.
   */
  @NotNull()
  public static Logger enableLogging(@Nullable final Level level)
  {
    StaticUtils.setLoggerLevel(LOGGER,
         (level == null) ? Level.INFO : level);
    return LOGGER;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy