Skip to content

Commit e333af0

Browse files
authored
Keep PKCS5S2 usable on a FIPS-restricted JCE, and name the key wrapping property when the runtime has no RSA-OAEP (#1058)
1 parent dd94891 commit e333af0

15 files changed

Lines changed: 603 additions & 46 deletions

File tree

‎opendj-doc-generated-ref/src/main/asciidoc/admin-guide/chap-import-export.adoc‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
information: "Portions copyright [year] [name of copyright owner]".
1313

1414
Copyright 2017 ForgeRock AS.
15-
Portions Copyright 2025 3A Systems LLC.
15+
Portions Copyright 2025-2026 3A Systems LLC.
1616
////
1717
1818
:figure-caption!:
@@ -228,9 +228,9 @@ replace: initials
228228
initials: AAA
229229
230230
$ ldifmodify \
231-
--sourceLDIF generated.ldif \
232-
--changesLDIF changes.ldif \
233-
--targetLDIF new.ldif
231+
--outputLDIF new.ldif \
232+
generated.ldif \
233+
changes.ldif
234234
----
235235
Notice that the resulting new LDIF file is likely to be about the same size as the source LDIF file.
236236

‎opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc‎

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,60 @@ At this point you can use OpenDJ directory server, or you can perform additional
789789
790790
====
791791
792+
[#install-fips]
793+
.To Install OpenDJ Directory Server on a FIPS 140 Java Runtime
794+
====
795+
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 in its default mode the default configuration works as it is. The approved-only mode of the provider (the `org.bouncycastle.fips.approved_only` Java system property), which OpenDJ does not turn on, is not covered here.
796+
797+
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.
798+
799+
. Install the server without starting it:
800+
+
801+
802+
[source, console]
803+
----
804+
$ ./setup --cli --doNotStart \
805+
--hostname opendj.example.com \
806+
--ldapPort 1389 \
807+
--adminConnectorPort 4444 \
808+
--rootUserDN "cn=Directory Manager" \
809+
--rootUserPassword password \
810+
--baseDN dc=example,dc=com \
811+
--acceptLicense \
812+
--no-prompt
813+
----
814+
815+
. Set the transformation in the server configuration file, which the `dsconfig` command cannot change while the server is stopped:
816+
+
817+
818+
[source, console]
819+
----
820+
$ cat changes.ldif
821+
dn: cn=Crypto Manager,cn=config
822+
changetype: modify
823+
replace: ds-cfg-key-wrapping-transformation
824+
ds-cfg-key-wrapping-transformation: RSA/ECB/PKCS1Padding
825+
826+
$ ldifmodify \
827+
--outputLDIF /path/to/opendj/config/config.ldif.new \
828+
/path/to/opendj/config/config.ldif \
829+
changes.ldif
830+
831+
$ mv /path/to/opendj/config/config.ldif.new /path/to/opendj/config/config.ldif
832+
----
833+
834+
. Start the server:
835+
+
836+
837+
[source, console]
838+
----
839+
$ start-ds
840+
----
841+
+
842+
Once the server runs, the `dsconfig set-crypto-manager-prop` command changes the property, and refuses a transformation the runtime does not support.
843+
844+
====
845+
792846
[#pdb-to-je]
793847
.To Move Data from a PDB Backend to a JE Backend
794848
====
@@ -933,7 +987,7 @@ replace: ds-cfg-java-class
933987
ds-cfg-java-class: org.opends.server.backends.jeb.JEBackend
934988
EOF
935989
936-
./bin/ldifmodify --targetLDIF "$LOC"/config/config.ldif.$$ --sourceLDIF "$LOC"/config/config.ldif --changesLDIF /tmp/changes_$$
990+
./bin/ldifmodify --outputLDIF "$LOC"/config/config.ldif.$$ "$LOC"/config/config.ldif /tmp/changes_$$
937991
if test $? -ne 0
938992
then
939993
echo "Modifications failed. Restoring the original configuration"

‎opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
import org.opends.server.backends.task.TaskState;
131131
import org.opends.server.tools.BackendTypeHelper;
132132
import org.opends.server.tools.BackendTypeHelper.BackendTypeUIAdapter;
133+
import org.opends.server.tools.ConfigureDS;
133134
import org.opends.server.types.HostPort;
134135
import org.opends.server.util.CertificateManager;
135136
import org.opends.server.util.CollectionUtils;
@@ -1363,6 +1364,13 @@ public void abort()
13631364
};
13641365
invokeLongOperation(thread);
13651366
notifyListeners(getFormattedDoneWithLineBreak());
1367+
// Given here rather than by ConfigureDS, whose output the listeners do not see while it runs.
1368+
final LocalizableMessage keyWrappingWarning = ConfigureDS.unsupportedKeyWrappingTransformationWarning();
1369+
if (keyWrappingWarning != null)
1370+
{
1371+
notifyListeners(getFormattedWarning(keyWrappingWarning));
1372+
notifyListeners(getLineBreak());
1373+
}
13661374
checkAbort();
13671375
configureCertificate(sec);
13681376
}

‎opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,9 @@ public boolean isConfigurationChangeAcceptable(
392392
requestedKeyWrappingTransformation));
393393
isAcceptable = false;
394394
}
395+
else if (!isKeyWrappingTransformationSupported(requestedKeyWrappingTransformation, unacceptableReasons)) {
396+
isAcceptable = false;
397+
}
395398
else {
396399
try {
397400
/* Note that the TrustStoreBackend not available at initial,
@@ -430,6 +433,29 @@ public boolean isConfigurationChangeAcceptable(
430433
return isAcceptable;
431434
}
432435

436+
/**
437+
* Checks that this Java runtime provides the key wrapping transformation. Only a refusal here
438+
* names the key-wrapping-transformation property: the wrap which follows it also needs an MD5
439+
* digest and a 1024-bit RSA key, and changing the property does not help when one of those is
440+
* what the runtime refuses.
441+
*/
442+
private static boolean isKeyWrappingTransformationSupported(
443+
final String transformation, final List<LocalizableMessage> unacceptableReasons)
444+
{
445+
try
446+
{
447+
Cipher.getInstance(transformation);
448+
return true;
449+
}
450+
catch (GeneralSecurityException ex)
451+
{
452+
logger.traceException(ex);
453+
unacceptableReasons.add(
454+
ERR_CRYPTOMGR_KEY_WRAPPING_TRANSFORMATION_UNSUPPORTED.get(transformation, getExceptionMessage(ex)));
455+
return false;
456+
}
457+
}
458+
433459
@Override
434460
public ConfigChangeResult applyConfigurationChange(CryptoManagerCfg cfg)
435461
{

‎opendj-server-legacy/src/main/java/org/opends/server/extensions/AbstractPBKDF2PasswordStorageScheme.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ public void initializePasswordStorageScheme(PBKDF2PasswordStorageSchemeCfg confi
8282
}
8383
catch (NoSuchAlgorithmException e)
8484
{
85-
throw new InitializationException(null);
85+
throw new InitializationException(
86+
ERR_PWSCHEME_CANNOT_INITIALIZE_MESSAGE_DIGEST.get(getMessageDigestAlgorithm(), e), e);
8687
}
8788

8889
this.config = configuration;

‎opendj-server-legacy/src/main/java/org/opends/server/extensions/ExtensionsConstants.java‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*
1414
* Copyright 2006-2008 Sun Microsystems, Inc.
1515
* Portions copyright 2013-2016 ForgeRock AS.
16+
* Portions Copyright 2026 3A Systems, LLC.
1617
*/
1718
package org.opends.server.extensions;
1819

@@ -153,11 +154,6 @@ public class ExtensionsConstants
153154
public static final String MESSAGE_DIGEST_ALGORITHM_PBKDF2_HMAC_SHA512 =
154155
"PBKDF2WithHmacSHA512";
155156

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

162158

163159
/**

‎opendj-server-legacy/src/main/java/org/opends/server/extensions/PKCS5S2PasswordStorageScheme.java‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*
1414
* Copyright 2014-2016 ForgeRock AS.
1515
* Portions Copyright 2014 Emidio Stani & Andrea Stani
16+
* Portions Copyright 2026 3A Systems, LLC.
1617
*/
1718
package org.opends.server.extensions;
1819

@@ -85,13 +86,15 @@ public void initializePasswordStorageScheme(PKCS5S2PasswordStorageSchemeCfg conf
8586
{
8687
try
8788
{
88-
random = SecureRandom.getInstance(SECURE_PRNG_SHA1);
89+
// The provider's default random source: a FIPS-restricted JCE registers no SHA1PRNG.
90+
random = new SecureRandom();
8991
// Just try to verify if the algorithm is supported
9092
SecretKeyFactory.getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2);
9193
}
9294
catch (NoSuchAlgorithmException e)
9395
{
94-
throw new InitializationException(null);
96+
throw new InitializationException(
97+
ERR_PWSCHEME_CANNOT_INITIALIZE_MESSAGE_DIGEST.get(MESSAGE_DIGEST_ALGORITHM_PBKDF2, e), e);
9598
}
9699
}
97100

@@ -246,8 +249,7 @@ private static byte[] encodeWithRandomSalt(ByteString plaintext, byte[] saltByte
246249
{
247250
try
248251
{
249-
final SecureRandom random = SecureRandom.getInstance(SECURE_PRNG_SHA1);
250-
return encodeWithRandomSalt(plaintext, saltBytes, random);
252+
return encodeWithRandomSalt(plaintext, saltBytes, new SecureRandom());
251253
}
252254
catch (DirectoryException e)
253255
{

‎opendj-server-legacy/src/main/java/org/opends/server/tools/ConfigureDS.java‎

Lines changed: 99 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,45 +1208,36 @@ private void updateCryptoManagerCertNickname() throws ConfigureDSException
12081208
*/
12091209
private void updateCryptoCipher() throws ConfigureDSException
12101210
{
1211-
final CryptoManagerCfgDefn cryptoManager = CryptoManagerCfgDefn.getInstance();
1212-
final StringPropertyDefinition prop = cryptoManager.getKeyWrappingTransformationPropertyDefinition();
1213-
String defaultCipher = null;
1214-
1215-
final DefaultBehaviorProvider<?> p = prop.getDefaultBehaviorProvider();
1216-
if (p instanceof DefinedDefaultBehaviorProvider)
1217-
{
1218-
final Collection<?> defaultValues = ((DefinedDefaultBehaviorProvider<?>) p).getDefaultValues();
1219-
if (!defaultValues.isEmpty())
1220-
{
1221-
defaultCipher = defaultValues.iterator().next().toString();
1222-
}
1223-
}
1224-
1211+
final String defaultCipher = defaultKeyWrappingTransformation();
12251212
if (defaultCipher != null)
12261213
{
1227-
// Check that the default cipher is supported by the JVM.
1214+
final String cipher;
12281215
try
12291216
{
1230-
Cipher.getInstance(defaultCipher);
1217+
cipher = supportedKeyWrappingTransformation(defaultCipher);
12311218
}
12321219
catch (final GeneralSecurityException ex)
12331220
{
1234-
// The cipher is not supported: try to find an alternative one.
1235-
final String alternativeCipher = getAlternativeCipher();
1236-
if (alternativeCipher != null)
1221+
// The default stays, and the server will refuse to start with it: there is no secure
1222+
// transformation to fall back to (#776), so the administrator has to choose one. Under
1223+
// setup this stream reaches the setup log only, and the installer gives the warning
1224+
// itself (see unsupportedKeyWrappingTransformationWarning()).
1225+
printWrappedText(err, unsupportedKeyWrappingTransformationWarning(defaultCipher, ex));
1226+
return;
1227+
}
1228+
if (!cipher.equals(defaultCipher))
1229+
{
1230+
try
12371231
{
1238-
try
1239-
{
1240-
updateConfigEntryWithAttribute(
1241-
DN_CRYPTO_MANAGER,
1242-
ATTR_CRYPTO_CIPHER_KEY_WRAPPING_TRANSFORMATION,
1243-
CoreSchema.getDirectoryStringSyntax(),
1244-
alternativeCipher);
1245-
}
1246-
catch (final Exception e)
1247-
{
1248-
throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_CRYPTO_MANAGER.get(e));
1249-
}
1232+
updateConfigEntryWithAttribute(
1233+
DN_CRYPTO_MANAGER,
1234+
ATTR_CRYPTO_CIPHER_KEY_WRAPPING_TRANSFORMATION,
1235+
CoreSchema.getDirectoryStringSyntax(),
1236+
cipher);
1237+
}
1238+
catch (final Exception e)
1239+
{
1240+
throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_CRYPTO_MANAGER.get(e));
12501241
}
12511242
}
12521243
}
@@ -1326,6 +1317,83 @@ private Entry removeAttribute(Entry entry, String attrName)
13261317
return duplicateEntry;
13271318
}
13281319

1320+
/**
1321+
* Returns the warning to give when this Java runtime supports neither the default key wrapping
1322+
* transformation of the crypto manager nor an alternative to it: the server will then refuse to
1323+
* start until the administrator sets one. The installer calls this itself, since what
1324+
* {@code configMain} writes while it runs under setup reaches the setup log only.
1325+
*
1326+
* @return The warning, or {@code null} when the runtime supports a transformation.
1327+
*/
1328+
public static LocalizableMessage unsupportedKeyWrappingTransformationWarning()
1329+
{
1330+
final String defaultCipher = defaultKeyWrappingTransformation();
1331+
if (defaultCipher == null)
1332+
{
1333+
return null;
1334+
}
1335+
try
1336+
{
1337+
supportedKeyWrappingTransformation(defaultCipher);
1338+
return null;
1339+
}
1340+
catch (final GeneralSecurityException ex)
1341+
{
1342+
return unsupportedKeyWrappingTransformationWarning(defaultCipher, ex);
1343+
}
1344+
}
1345+
1346+
private static LocalizableMessage unsupportedKeyWrappingTransformationWarning(
1347+
final String defaultCipher, final GeneralSecurityException ex)
1348+
{
1349+
return WARN_CONFIGDS_KEY_WRAPPING_TRANSFORMATION_UNSUPPORTED.get(defaultCipher, ex.getMessage());
1350+
}
1351+
1352+
/** Returns the default key wrapping transformation of the crypto manager, or {@code null}. */
1353+
private static String defaultKeyWrappingTransformation()
1354+
{
1355+
final StringPropertyDefinition prop =
1356+
CryptoManagerCfgDefn.getInstance().getKeyWrappingTransformationPropertyDefinition();
1357+
final DefaultBehaviorProvider<?> p = prop.getDefaultBehaviorProvider();
1358+
if (p instanceof DefinedDefaultBehaviorProvider)
1359+
{
1360+
final Collection<?> defaultValues = ((DefinedDefaultBehaviorProvider<?>) p).getDefaultValues();
1361+
if (!defaultValues.isEmpty())
1362+
{
1363+
return defaultValues.iterator().next().toString();
1364+
}
1365+
}
1366+
return null;
1367+
}
1368+
1369+
/**
1370+
* Returns the key wrapping transformation this Java runtime supports: the default one when it
1371+
* does, otherwise the OAEP alternative of {@link #getAlternativeCipher()}.
1372+
*
1373+
* @param defaultCipher
1374+
* The default key wrapping transformation of the crypto manager.
1375+
* @return The transformation to configure.
1376+
* @throws GeneralSecurityException
1377+
* If the runtime supports neither, with the reason the default one is not.
1378+
*/
1379+
static String supportedKeyWrappingTransformation(final String defaultCipher) throws GeneralSecurityException
1380+
{
1381+
try
1382+
{
1383+
Cipher.getInstance(defaultCipher);
1384+
return defaultCipher;
1385+
}
1386+
catch (final GeneralSecurityException ex)
1387+
{
1388+
final String alternativeCipher = getAlternativeCipher();
1389+
if (alternativeCipher == null)
1390+
{
1391+
throw ex;
1392+
}
1393+
return alternativeCipher;
1394+
}
1395+
}
1396+
13291397
/**
13301398
* Returns a cipher that is supported by the JVM we are running at.
13311399
* Returns <CODE>null</CODE> if no alternative cipher could be found.

‎opendj-server-legacy/src/messages/org/opends/messages/core.properties‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,3 +1376,7 @@ WARN_CRYPTOMGR_SSL_CERT_NICKNAME_LOOKUP_FAILED_765=The certificate nicknames now
13761376
up in the trust store used for server to server communication: %s. A nickname that \
13771377
trust store does not hold is only reported once the server has been restarted with it, \
13781378
so check the nicknames against the trust store before restarting the server
1379+
ERR_CRYPTOMGR_KEY_WRAPPING_TRANSFORMATION_UNSUPPORTED_766=This Java runtime \
1380+
does not provide the key wrapping transformation %s: %s. The \
1381+
key-wrapping-transformation property of the crypto manager must name a \
1382+
transformation which the runtime supports

‎opendj-server-legacy/src/messages/org/opends/messages/tool.properties‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2634,6 +2634,11 @@ ERR_FILE_NOT_FULLY_READABLE_20015=Could not completely read file '%s'
26342634
SUPPLEMENT_DESCRIPTION_BACKEND_TOOL_SUBCMD_LIST_INDEX_STATUS_20016=\
26352635
<xinclude:include href="variablelist-backendstat-index-status.xml" />
26362636
INFO_DESCRIPTION_DEFAULT_ADD_20017=Legacy argument for ForgeRock OpenDJ compatibility.
2637+
WARN_CONFIGDS_KEY_WRAPPING_TRANSFORMATION_UNSUPPORTED_20018=This Java runtime \
2638+
supports neither the default key wrapping transformation %s nor an alternative \
2639+
to it: %s. The server will not start until the key-wrapping-transformation \
2640+
property of the crypto manager names a transformation which the runtime \
2641+
supports; set it in config/config.ldif before starting the server
26372642
INFO_LDAP_CONN_PROMPT_SECURITY_LDAP=LDAP
26382643
INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL=LDAP with SSL
26392644
INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TLS=LDAP with StartTLS

0 commit comments

Comments
 (0)