Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,60 @@ At this point you can use OpenDJ directory server, or you can perform additional

====

[#install-fips]
.To Install OpenDJ Directory Server on a FIPS 140 Java Runtime
====
OpenDJ ships the Bouncy Castle FIPS provider. The `setup` command and the server register it themselves when the server key store is a BCFKS key store (`--useBcfksKeystore`), and the server registers it at start whenever the `org.openidentityplatform.opendj.fips.register` Java system property is `true`, for example through the `OPENDJ_JAVA_ARGS` environment variable. With this provider the default configuration works as it is.

The crypto manager wraps the secret keys it shares with the other servers of a replication topology with each server's public key. The transformation it uses, the `key-wrapping-transformation` property of the crypto manager, is `RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING` by default: RSA-OAEP, the key transport scheme of NIST SP 800-56B, which Bouncy Castle FIPS provides. A Java runtime whose cryptography comes from a `SunPKCS11` provider alone, such as `SunPKCS11-NSS-FIPS` on a Linux system in FIPS mode, provides no RSA-OAEP at all: its only RSA cipher is `RSA/ECB/PKCS1Padding`, which NIST SP 800-131A Rev. 2 disallows for key transport. On such a runtime the server refuses to start with the default transformation, `setup` says so, and the choice of another one is yours to make: OpenDJ does not make it for you. If you make it, keep in mind that every server of a replication topology must use the same transformation, since each server unwraps what the others wrapped.

. Install the server without starting it:
+

[source, console]
----
$ ./setup --cli --doNotStart \
--hostname opendj.example.com \
--ldapPort 1389 \
--adminConnectorPort 4444 \
--rootUserDN "cn=Directory Manager" \
--rootUserPassword password \
--baseDN dc=example,dc=com \
--acceptLicense \
--no-prompt
----

. Set the transformation in the server configuration file, which the `dsconfig` command cannot change while the server is stopped:
+

[source, console]
----
$ cat changes.ldif
dn: cn=Crypto Manager,cn=config
changetype: modify
replace: ds-cfg-key-wrapping-transformation
ds-cfg-key-wrapping-transformation: RSA/ECB/PKCS1Padding

$ ldifmodify \
--sourceLDIF /path/to/opendj/config/config.ldif \
--changesLDIF changes.ldif \
--targetLDIF /path/to/opendj/config/config.ldif.new

$ mv /path/to/opendj/config/config.ldif.new /path/to/opendj/config/config.ldif
----

. Start the server:
+

[source, console]
----
$ start-ds
----
+
Once the server runs, the `dsconfig set-crypto-manager-prop` command changes the property, and refuses a transformation the runtime does not support.

====

[#pdb-to-je]
.To Move Data from a PDB Backend to a JE Backend
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ public void initializePasswordStorageScheme(PBKDF2PasswordStorageSchemeCfg confi
}
catch (NoSuchAlgorithmException e)
{
throw new InitializationException(null);
throw new InitializationException(
ERR_PWSCHEME_CANNOT_INITIALIZE_MESSAGE_DIGEST.get(getMessageDigestAlgorithm(), e), e);
}

this.config = configuration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions copyright 2013-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.extensions;

Expand Down Expand Up @@ -153,11 +154,6 @@ public class ExtensionsConstants
public static final String MESSAGE_DIGEST_ALGORITHM_PBKDF2_HMAC_SHA512 =
"PBKDF2WithHmacSHA512";

/**
* The name of the pseudo-random number generator using SHA-1.
*/
public static final String SECURE_PRNG_SHA1 = "SHA1PRNG";



/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2014-2016 ForgeRock AS.
* Portions Copyright 2014 Emidio Stani & Andrea Stani
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.extensions;

Expand Down Expand Up @@ -85,13 +86,15 @@ public void initializePasswordStorageScheme(PKCS5S2PasswordStorageSchemeCfg conf
{
try
{
random = SecureRandom.getInstance(SECURE_PRNG_SHA1);
// The provider's default random source: a FIPS-restricted JCE registers no SHA1PRNG.
random = new SecureRandom();
// Just try to verify if the algorithm is supported
SecretKeyFactory.getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2);
}
catch (NoSuchAlgorithmException e)
{
throw new InitializationException(null);
throw new InitializationException(
ERR_PWSCHEME_CANNOT_INITIALIZE_MESSAGE_DIGEST.get(MESSAGE_DIGEST_ALGORITHM_PBKDF2, e), e);
}
}

Expand Down Expand Up @@ -246,8 +249,7 @@ private static byte[] encodeWithRandomSalt(ByteString plaintext, byte[] saltByte
{
try
{
final SecureRandom random = SecureRandom.getInstance(SECURE_PRNG_SHA1);
return encodeWithRandomSalt(plaintext, saltBytes, random);
return encodeWithRandomSalt(plaintext, saltBytes, new SecureRandom());
}
catch (DirectoryException e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1224,29 +1224,32 @@ private void updateCryptoCipher() throws ConfigureDSException

if (defaultCipher != null)
{
// Check that the default cipher is supported by the JVM.
final String cipher;
try
{
Cipher.getInstance(defaultCipher);
cipher = supportedKeyWrappingTransformation(defaultCipher);
}
catch (final GeneralSecurityException ex)
{
// The cipher is not supported: try to find an alternative one.
final String alternativeCipher = getAlternativeCipher();
if (alternativeCipher != null)
// The default stays, and the server will refuse to start with it: there is no secure
// transformation to fall back to (#776), so the administrator has to choose one, and
// has to be told so here rather than by the failed start.
printWrappedText(err, WARN_CONFIGDS_KEY_WRAPPING_TRANSFORMATION_UNSUPPORTED.get(defaultCipher, ex.getMessage()));
return;
}
if (!cipher.equals(defaultCipher))
{
try
{
try
{
updateConfigEntryWithAttribute(
DN_CRYPTO_MANAGER,
ATTR_CRYPTO_CIPHER_KEY_WRAPPING_TRANSFORMATION,
CoreSchema.getDirectoryStringSyntax(),
alternativeCipher);
}
catch (final Exception e)
{
throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_CRYPTO_MANAGER.get(e));
}
updateConfigEntryWithAttribute(
DN_CRYPTO_MANAGER,
ATTR_CRYPTO_CIPHER_KEY_WRAPPING_TRANSFORMATION,
CoreSchema.getDirectoryStringSyntax(),
cipher);
}
catch (final Exception e)
{
throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_CRYPTO_MANAGER.get(e));
}
}
}
Expand Down Expand Up @@ -1326,6 +1329,34 @@ private Entry removeAttribute(Entry entry, String attrName)
return duplicateEntry;
}

/**
* Returns the key wrapping transformation this Java runtime supports: the default one when it
* does, otherwise the OAEP alternative of {@link #getAlternativeCipher()}.
*
* @param defaultCipher
* The default key wrapping transformation of the crypto manager.
* @return The transformation to configure.
* @throws GeneralSecurityException
* If the runtime supports neither, with the reason the default one is not.
*/
static String supportedKeyWrappingTransformation(final String defaultCipher) throws GeneralSecurityException
{
try
{
Cipher.getInstance(defaultCipher);
return defaultCipher;
}
catch (final GeneralSecurityException ex)
{
final String alternativeCipher = getAlternativeCipher();
if (alternativeCipher == null)
{
throw ex;
}
return alternativeCipher;
}
}

/**
* Returns a cipher that is supported by the JVM we are running at.
* Returns <CODE>null</CODE> if no alternative cipher could be found.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,9 @@ ERR_CRYPTOMGR_CANNOT_GET_REQUESTED_MAC_ENGINE_662=CryptoManager cannot \
ERR_CRYPTOMGR_CANNOT_GET_REQUESTED_ENCRYPTION_CIPHER_663=CryptoManager \
cannot get the requested encryption cipher %s: %s
ERR_CRYPTOMGR_CANNOT_GET_PREFERRED_KEY_WRAPPING_CIPHER_664=CryptoManager \
cannot get the preferred key wrapping cipher: %s
cannot get the preferred key wrapping cipher: %s. The \
key-wrapping-transformation property of the crypto manager must name a \
transformation which this Java runtime supports
ERR_CRYPTOMGR_FAILED_TO_INITIATE_INSTANCE_KEY_GENERATION_665=CryptoManager \
failed to add entry "%s" to initiate instance key generation
ERR_CRYPTOMGR_FAILED_TO_RETRIEVE_INSTANCE_CERTIFICATE_666=CryptoManager \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2634,6 +2634,11 @@ ERR_FILE_NOT_FULLY_READABLE_20015=Could not completely read file '%s'
SUPPLEMENT_DESCRIPTION_BACKEND_TOOL_SUBCMD_LIST_INDEX_STATUS_20016=\
<xinclude:include href="variablelist-backendstat-index-status.xml" />
INFO_DESCRIPTION_DEFAULT_ADD_20017=Legacy argument for ForgeRock OpenDJ compatibility.
WARN_CONFIGDS_KEY_WRAPPING_TRANSFORMATION_UNSUPPORTED_20018=This Java runtime \
supports neither the default key wrapping transformation %s nor an alternative \
to it: %s. The server will not start until the key-wrapping-transformation \
property of the crypto manager names a transformation which the runtime \
supports; set it in config/config.ldif before starting the server
INFO_LDAP_CONN_PROMPT_SECURITY_LDAP=LDAP
INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL=LDAP with SSL
INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TLS=LDAP with StartTLS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.file.Paths;
import java.security.Provider;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -67,6 +69,7 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -316,6 +319,64 @@ static class TestPorts
}
}

/**
* Runs {@code action} while no installed JCE provider offers the given service, the way a
* FIPS-restricted JVM lacks it, and puts the withdrawn providers back where they were
* afterwards. The {@code standIns} are installed ahead of the remaining providers for the
* duration, for whatever the action still needs that only the withdrawn providers offered.
*
* @param type
* The JCE service type, e.g. {@code SecureRandom}.
* @param algorithm
* The algorithm to withdraw, e.g. {@code SHA1PRNG}.
* @param action
* What to run without the service.
* @param standIns
* Providers to install first while the service is withdrawn.
* @throws Exception
* If the action fails, or if the service could not be withdrawn.
*/
public static void withoutJceService(final String type, final String algorithm,
final Callable<Void> action, final Provider... standIns) throws Exception
{
final String service = type + "." + algorithm;
final List<Provider> installed = Arrays.asList(Security.getProviders());
final Provider[] offering = Security.getProviders(service);
assertNotNull(offering, "no installed provider offers " + service + ": nothing to withdraw");
for (Provider provider : offering)
{
Security.removeProvider(provider.getName());
}
final List<Provider> addedStandIns = new ArrayList<>();
for (int i = 0; i < standIns.length; i++)
{
if (Security.insertProviderAt(standIns[i], i + 1) != -1)
{
addedStandIns.add(standIns[i]);
}
}
try
{
assertNull(Security.getProviders(service), service + " is still offered: the fixture does not withdraw it");
action.call();
}
finally
{
for (Provider standIn : addedStandIns)
{
Security.removeProvider(standIn.getName());
}
// Ascending original positions, so that the list comes back in its original order.
for (Provider provider : installed)
{
if (Arrays.asList(offering).contains(provider))
{
Security.insertProviderAt(provider, installed.indexOf(provider) + 1);
}
}
}
}

public static void startServer() throws Exception
{
System.setProperty(PROPERTY_RUNNING_UNIT_TESTS, "true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import java.util.UUID;

Expand Down Expand Up @@ -297,6 +299,28 @@ public void testSslCertNicknameChangeReportsATrustStoreItCannotRead() throws Exc
.contains(trustStore.getTrustStoreFile());
}

/**
A key wrapping transformation this Java runtime cannot provide is refused, at start (where
the refusal is what keeps the server from starting) as on a change, and the refusal has to
name the property to set, not only the cipher which failed: on a FIPS-restricted runtime
without RSA-OAEP that is all the administrator has to go on.
*/
@Test
public void testUnsupportedKeyWrappingTransformationIsRefusedNamingTheProperty() throws Exception
{
final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
final String unsupported = "RSA/ECB/NoSuchPadding";
final List<LocalizableMessage> why = new ArrayList<>();

final boolean acceptable =
cm.isConfigurationChangeAcceptable(withProperty(cfg, "getKeyWrappingTransformation", unsupported), why);

assertThat(acceptable).isFalse();
assertThat(why).hasSize(1);
assertThat(why.get(0).toString()).contains(unsupported).contains("key-wrapping-transformation");
}

/**
Returns the crypto manager configuration as it stands, with the ssl-cert-nickname
property answering the provided nicknames, so that a change to that property is applied
Expand Down
Loading
Loading