Skip to content
Merged
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
5 changes: 5 additions & 0 deletions phase4-bdew-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@
<artifactId>ph-unittest-support-ext</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.helger.commons</groupId>
<artifactId>ph-bc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bctls-jdk18on</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions phase4-edelivery2-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@
<artifactId>ph-unittest-support-ext</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.helger.commons</groupId>
<artifactId>ph-bc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
3 changes: 3 additions & 0 deletions phase4-lib/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
<dependency>
<groupId>com.helger.commons</groupId>
<artifactId>ph-bc</artifactId>
<!-- Optional for normal AS4 use. Required for the deprecated BC-based OID API and for
reflection/AOT tools that eagerly resolve all method descriptors. -->
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.helger.commons</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import org.apache.wss4j.common.WSS4JConstants;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.cms.CMSAlgorithm;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

Expand All @@ -29,16 +28,19 @@
* Enumeration with all message encryption algorithms supported.
*
* @author Philip Helger
* @apiNote Direct use of this enum, except for the deprecated {@link #getOID()} method, does not
* require Bouncy Castle. Reflection and AOT tools that eagerly resolve all method
* descriptors still require it for binary compatibility with that method.
*/
public enum ECryptoAlgorithmCrypt implements ICryptoAlgorithmCrypt
{
CRYPT_3DES ("3des", CMSAlgorithm.DES_EDE3_CBC, WSS4JConstants.TRIPLE_DES),
AES_128_CBC ("aes128-cbc", CMSAlgorithm.AES128_CBC, WSS4JConstants.AES_128),
AES_128_GCM ("aes128-gcm", CMSAlgorithm.AES128_GCM, WSS4JConstants.AES_128_GCM),
AES_192_CBC ("aes192-cbc", CMSAlgorithm.AES192_CBC, WSS4JConstants.AES_192),
AES_192_GCM ("aes192-gcm", CMSAlgorithm.AES192_GCM, WSS4JConstants.AES_192_GCM),
AES_256_CBC ("aes256-cbc", CMSAlgorithm.AES256_CBC, WSS4JConstants.AES_256),
AES_256_GCM ("aes256-gcm", CMSAlgorithm.AES256_GCM, WSS4JConstants.AES_256_GCM);
CRYPT_3DES ("3des", "1.2.840.113549.3.7", WSS4JConstants.TRIPLE_DES),
AES_128_CBC ("aes128-cbc", "2.16.840.1.101.3.4.1.2", WSS4JConstants.AES_128),
AES_128_GCM ("aes128-gcm", "2.16.840.1.101.3.4.1.6", WSS4JConstants.AES_128_GCM),
AES_192_CBC ("aes192-cbc", "2.16.840.1.101.3.4.1.22", WSS4JConstants.AES_192),
AES_192_GCM ("aes192-gcm", "2.16.840.1.101.3.4.1.26", WSS4JConstants.AES_192_GCM),
AES_256_CBC ("aes256-cbc", "2.16.840.1.101.3.4.1.42", WSS4JConstants.AES_256),
AES_256_GCM ("aes256-gcm", "2.16.840.1.101.3.4.1.46", WSS4JConstants.AES_256_GCM);

/** Default encrypt algorithm */
public static final ECryptoAlgorithmCrypt ENCRYPTION_ALGORITHM_DEFAULT = AES_128_GCM;
Expand All @@ -48,15 +50,16 @@ public enum ECryptoAlgorithmCrypt implements ICryptoAlgorithmCrypt
public static final ECryptoAlgorithmCrypt ENCRPYTION_ALGORITHM_DEFAULT = ENCRYPTION_ALGORITHM_DEFAULT;

private final String m_sID;
private final ASN1ObjectIdentifier m_aOID;
private final String m_sOID;
private final String m_sAlgorithmURI;
private volatile ASN1ObjectIdentifier m_aOID;

ECryptoAlgorithmCrypt (@NonNull @Nonempty final String sID,
@NonNull final ASN1ObjectIdentifier aOID,
@NonNull @Nonempty final String sOID,
@NonNull @Nonempty final String sAlgorithmURI)
{
m_sID = sID;
m_aOID = aOID;
m_sOID = sOID;
m_sAlgorithmURI = sAlgorithmURI;
}

Expand All @@ -68,9 +71,25 @@ public String getID ()
}

@NonNull
@Nonempty
public String getOIDString ()
{
return m_sOID;
}

@NonNull
@Deprecated (since = "4.6.0")
public ASN1ObjectIdentifier getOID ()
{
return m_aOID;
ASN1ObjectIdentifier ret = m_aOID;
if (ret == null)
synchronized (this)
{
ret = m_aOID;
if (ret == null)
m_aOID = ret = new ASN1ObjectIdentifier (m_sOID);
}
return ret;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
*
* @author Philip Helger
* @since v1.4.4
* @apiNote Implementations that override {@link #getOIDString()} can support direct use without
* Bouncy Castle. This interface nevertheless retains the deprecated {@link #getOID()}
* method for binary compatibility. Reflection and AOT tools that eagerly resolve every
* method descriptor therefore still require Bouncy Castle until that method can be
* removed in a future major release.
*/
public interface ICryptoAlgorithmCrypt extends IHasID <String>
{
Expand All @@ -37,10 +42,27 @@ public interface ICryptoAlgorithmCrypt extends IHasID <String>
@Nonempty
String getID ();

/**
* @return The OID of the algorithm in dot-decimal notation.
* @implSpec Implementations should override this method to make direct invocation independent
* of Bouncy Castle. The default implementation delegates to {@link #getOID()} for
* binary compatibility with existing implementations.
* @since 4.6.0
*/
@NonNull
@Nonempty
default String getOIDString ()
{
return getOID ().getId ();
}

/**
* @return The OID of the algorithm to be used by the Security Provider.
* @deprecated Use {@link #getOIDString()} instead. This compatibility method requires Bouncy
* Castle to be present at runtime.
*/
@NonNull
@Deprecated (since = "4.6.0")
ASN1ObjectIdentifier getOID ();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.helger.phase4.crypto;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
Expand All @@ -32,12 +33,16 @@
public final class ECryptoAlgorithmCryptTest
{
@Test
@SuppressWarnings ("deprecation")
public void testBasic ()
{
for (final ECryptoAlgorithmCrypt e : ECryptoAlgorithmCrypt.values ())
{
assertTrue (StringHelper.isNotEmpty (e.getID ()));
assertTrue (StringHelper.isNotEmpty (e.getOIDString ()));
assertNotNull (e.getOID ());
assertEquals (e.getOIDString (), e.getOID ().getId ());
assertSame (e.getOID (), e.getOID ());
assertTrue (StringHelper.isNotEmpty (e.getAlgorithmURI ()));
assertSame (e, ECryptoAlgorithmCrypt.getFromIDOrNull (e.getID ()));
assertSame (e, ECryptoAlgorithmCrypt.getFromIDOrDefault (e.getID (), null));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2015-2026 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* 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.
*/
package com.helger.phase4.crypto;

/** Invoked through an isolated class loader by {@link ECryptoAlgorithmCryptWithoutBCTest}. */
public final class ECryptoAlgorithmCryptWithoutBCProbe
{
private ECryptoAlgorithmCryptWithoutBCProbe ()
{}

public static String [] getOIDStrings ()
{
final ECryptoAlgorithmCrypt [] aValues = ECryptoAlgorithmCrypt.values ();
final String [] ret = new String [aValues.length];
for (int i = 0; i < aValues.length; ++i)
ret[i] = aValues[i].getOIDString ();
return ret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (C) 2015-2026 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* 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.
*/
package com.helger.phase4.crypto;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.fail;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

import org.junit.Test;

/**
* Verifies direct enum initialization and provider-neutral OID access without Bouncy Castle. This
* deliberately does not reflect over all enum methods because the retained legacy method
* descriptor still references a Bouncy Castle type.
*/
public final class ECryptoAlgorithmCryptWithoutBCTest
{
private static final String ENUM_CLASS = ECryptoAlgorithmCrypt.class.getName ();
private static final String INTERFACE_CLASS = ICryptoAlgorithmCrypt.class.getName ();
private static final String PROBE_CLASS = ECryptoAlgorithmCryptWithoutBCProbe.class.getName ();

@Test
public void testDirectEnumUseWithoutBC () throws Exception
{
final URL [] aURLs = { new File ("target/classes").toURI ().toURL (),
new File ("target/test-classes").toURI ().toURL () };
try (final URLClassLoader aCL = new URLClassLoader (aURLs, getClass ().getClassLoader ())
{
@Override
protected Class <?> loadClass (final String sName, final boolean bResolve) throws ClassNotFoundException
{
if (sName.startsWith ("org.bouncycastle."))
throw new ClassNotFoundException ("Bouncy Castle deliberately hidden from test class loader");

if (sName.equals (ENUM_CLASS) || sName.equals (INTERFACE_CLASS) || sName.equals (PROBE_CLASS))
synchronized (getClassLoadingLock (sName))
{
Class <?> ret = findLoadedClass (sName);
if (ret == null)
ret = findClass (sName);
if (bResolve)
resolveClass (ret);
return ret;
}

return super.loadClass (sName, bResolve);
}
})
{
try
{
aCL.loadClass ("org.bouncycastle.asn1.ASN1ObjectIdentifier");
fail ("Bouncy Castle must not be visible to the isolated class loader");
}
catch (final ClassNotFoundException ex)
{
// Expected
}

final Class <?> aProbeClass = Class.forName (PROBE_CLASS, true, aCL);
final String [] aActual = (String []) aProbeClass.getMethod ("getOIDStrings").invoke (null);
assertArrayEquals (new String [] { "1.2.840.113549.3.7",
"2.16.840.1.101.3.4.1.2",
"2.16.840.1.101.3.4.1.6",
"2.16.840.1.101.3.4.1.22",
"2.16.840.1.101.3.4.1.26",
"2.16.840.1.101.3.4.1.42",
"2.16.840.1.101.3.4.1.46" },
aActual);
}
}
}
4 changes: 4 additions & 0 deletions phase4-profile-bdew/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
<groupId>com.helger.phase4</groupId>
<artifactId>phase4-lib</artifactId>
</dependency>
<dependency>
<groupId>com.helger.commons</groupId>
<artifactId>ph-bc</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
Expand Down
Loading