diff --git a/pom.xml b/pom.xml
index 54db78436..44d3c369d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -223,6 +223,11 @@ THE SOFTWARE.
+
+ io.jenkins.test.fips
+ fips-bundle-test
+ 23.v76d4fd57f5b_d
+
org.junit.jupiter
junit-jupiter-api
diff --git a/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java b/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java
index 59bc76fd0..ea784989b 100644
--- a/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java
+++ b/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java
@@ -65,6 +65,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
@@ -90,6 +91,8 @@
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+
+import io.jenkins.test.fips.FIPSTestBundleProvider;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsLocationConfiguration;
import jenkins.util.Timer;
@@ -200,6 +203,7 @@ public final class RealJenkinsRule implements TestRule {
private boolean prepareHomeLazily;
private boolean provisioned;
+ private final List bootClasspathFiles = new ArrayList<>();
// TODO may need to be relaxed for Gradle-based plugins
private static final Pattern SNAPSHOT_INDEX_JELLY = Pattern.compile("(file:/.+/target)/classes/index.jelly");
@@ -457,6 +461,36 @@ public RealJenkinsRule prepareHomeLazily(boolean prepareHomeLazily) {
return this;
}
+ /**
+ * Use {@link #withFIPSEnabled(FIPSTestBundleProvider)} with default value of {@link FIPSTestBundleProvider#get()}
+ */
+ public RealJenkinsRule withFIPSEnabled() {
+ return withFIPSEnabled(FIPSTestBundleProvider.get());
+ }
+
+ /**
+ +
+ * @param fipsTestBundleProvider the {@link FIPSTestBundleProvider} to use for testing
+ */
+ public RealJenkinsRule withFIPSEnabled(FIPSTestBundleProvider fipsTestBundleProvider) {
+ Objects.requireNonNull(fipsTestBundleProvider, "fipsTestBundleProvider must not be null");
+ try {
+ return withBootClasspath(fipsTestBundleProvider.getBootClasspathFiles().toArray(new File[0]))
+ .javaOptions(fipsTestBundleProvider.getJavaOptions().toArray(new String[0]));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ *
+ * @param files add some {@link File} to bootclasspath
+ */
+ public RealJenkinsRule withBootClasspath(File...files) {
+ this.bootClasspathFiles.addAll(List.of(files));
+ return this;
+ }
+
public static List getJacocoAgentOptions() {
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List arguments = runtimeMxBean.getInputArguments();
@@ -744,8 +778,12 @@ public void startJenkins() throws Throwable {
+ ",suspend=" + (debugSuspend ? "y" : "n")
+ (debugPort > 0 ? ",address=" + httpListenAddress + ":" + debugPort : ""));
}
- argv.addAll(javaOptions);
+ if(!bootClasspathFiles.isEmpty()) {
+ String fileList = bootClasspathFiles.stream().map(File::getAbsolutePath).collect(Collectors.joining(File.pathSeparator));
+ argv.add("-Xbootclasspath/a:" + fileList);
+ }
+ argv.addAll(javaOptions);
argv.addAll(List.of(
"-jar", war.getAbsolutePath(),
diff --git a/src/test/java/org/jvnet/hudson/test/RealJenkinsRuleFIPSTest.java b/src/test/java/org/jvnet/hudson/test/RealJenkinsRuleFIPSTest.java
new file mode 100644
index 000000000..336dcabbd
--- /dev/null
+++ b/src/test/java/org/jvnet/hudson/test/RealJenkinsRuleFIPSTest.java
@@ -0,0 +1,73 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2024 Olivier Lamy
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.jvnet.hudson.test;
+
+import io.jenkins.test.fips.FIPSTestBundleProvider;
+import jenkins.security.FIPS140;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.net.ssl.KeyManagerFactory;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+public class RealJenkinsRuleFIPSTest {
+
+ @Rule public RealJenkinsRule rr = new RealJenkinsRule().prepareHomeLazily(true)
+ .withDebugPort(4001).withDebugServer(false)
+ .withFIPSEnabled(FIPSTestBundleProvider.get())
+ .javaOptions("-Djava.security.debug=properties");
+
+ @Test
+ public void fipsMode() throws Throwable {
+ rr.then(r -> {
+ Provider[] providers = Security.getProviders();
+ System.out.println("fipsMode providers:" + Arrays.asList(providers));
+
+ Class> clazz = Thread.currentThread().getContextClassLoader().loadClass("org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider");
+ System.out.println("BouncyCastleFipsProvider class:" + clazz);
+
+ Provider provider = Security.getProvider("BCFIPS");
+ assertThat(provider, notNullValue());
+ assertThat(provider.getClass().getName(), is("org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider"));
+ assertThat(providers[0].getClass().getName(), is("org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider"));
+ assertThat(providers[1].getClass().getName(), is("org.bouncycastle.jsse.provider.BouncyCastleJsseProvider"));
+ assertThat(providers[2].getClass().getName(), is("sun.security.provider.Sun"));
+ assertThat(KeyStore.getDefaultType(), is("BCFKS"));
+ assertThat(KeyManagerFactory.getDefaultAlgorithm(), is("PKIX"));
+
+ assertThat(providers.length, is(3));
+
+ assertThat(FIPS140.useCompliantAlgorithms(), is(true));
+ });
+ }
+
+}