From 7d91a9db22fb5a42762192c737526ba090860ad5 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Thu, 29 Aug 2024 09:28:31 +1000 Subject: [PATCH] In FIPS environment using BCFIPS security a custom SecurityProvider needs to be register and due to how classloader is used in Mina this needs to be in the same classloader as org.apache.sshd.common.util.security.SecurityUtils so in mina-sshd-common Signed-off-by: Olivier Lamy --- mina-sshd-api-common/pom.xml | 12 ++- .../FIPSRegistarInitPlugin.java | 54 +++++++++++ .../TestBCFIPSRegistar.java | 89 +++++++++++++++++++ .../src/test/resources/ed25519.pem | 3 + .../src/test/resources/id_rsa-keygen-rsa-2048 | 50 +++++++++++ .../test/resources/id_rsa-keygen-rsa-2048.pub | 1 + .../src/test/resources/rsa2048 | 30 +++++++ pom.xml | 17 ++++ 8 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 mina-sshd-api-common/src/main/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/FIPSRegistarInitPlugin.java create mode 100644 mina-sshd-api-common/src/test/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/TestBCFIPSRegistar.java create mode 100644 mina-sshd-api-common/src/test/resources/ed25519.pem create mode 100644 mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048 create mode 100644 mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048.pub create mode 100644 mina-sshd-api-common/src/test/resources/rsa2048 diff --git a/mina-sshd-api-common/pom.xml b/mina-sshd-api-common/pom.xml index 59401f8..949b8c9 100644 --- a/mina-sshd-api-common/pom.xml +++ b/mina-sshd-api-common/pom.xml @@ -46,6 +46,16 @@ bouncycastle-api true - + + org.jenkins-ci.main + jenkins-test-harness + 2270.v87a_0ea_b_54da_0 + test + + + io.jenkins.test.fips + fips-bundle-test + 23.v76d4fd57f5b_d + diff --git a/mina-sshd-api-common/src/main/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/FIPSRegistarInitPlugin.java b/mina-sshd-api-common/src/main/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/FIPSRegistarInitPlugin.java new file mode 100644 index 0000000..107e802 --- /dev/null +++ b/mina-sshd-api-common/src/main/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/FIPSRegistarInitPlugin.java @@ -0,0 +1,54 @@ +/* + * The MIT License + * + * Copyright (c) 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 io.jenkins.plugins.mina_sshd_api.core.bouncycastle_registar; + +import hudson.Plugin; +import jenkins.security.FIPS140; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.security.Security; +import java.util.logging.Logger; + +/** + * In FIPS environment, Apache Mina needs to be tailored with a custom setup to enable FIPS mode + * and register BCFIPS as security provider + * + * This can be done via System properties but needs to be done BEFORE any Mina classes have been loaded + */ +@Restricted(NoExternalUse.class) +public class FIPSRegistarInitPlugin extends Plugin { + + private static final Logger LOG = Logger.getLogger(FIPSRegistarInitPlugin.class.getName()); + @Override + public void start() throws Exception { + if(FIPS140.useCompliantAlgorithms() && Security.getProvider("BCFIPS") != null) { + LOG.info("register enable FIPS mode for Apache Mina and register defaultProvider as BCFIPS"); + System.setProperty("org.apache.sshd.security.fipsEnabled", "true"); + System.setProperty("org.apache.sshd.security.defaultProvider", "BCFIPS"); + } else { + LOG.config("not needed to register FIPSBouncyCastleSecurityProviderRegistar"); + } + } +} diff --git a/mina-sshd-api-common/src/test/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/TestBCFIPSRegistar.java b/mina-sshd-api-common/src/test/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/TestBCFIPSRegistar.java new file mode 100644 index 0000000..9b9831b --- /dev/null +++ b/mina-sshd-api-common/src/test/java/io/jenkins/plugins/mina_sshd_api/core/bouncycastle_registar/TestBCFIPSRegistar.java @@ -0,0 +1,89 @@ +package io.jenkins.plugins.mina_sshd_api.core.bouncycastle_registar; + +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.RealJenkinsRule; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.util.Objects; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; + +public class TestBCFIPSRegistar { + + @Rule + public RealJenkinsRule j = new RealJenkinsRule().withFIPSEnabled() + .omitPlugins("eddsa-api").javaOptions("-Xmx256m"); + + @Test + public void BCFIPSRegistered() throws Throwable { + + URI path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("rsa2048")).toURI(); + j.then(r -> { + Iterable keyPairs = SecurityUtils.loadKeyPairIdentities(null, null, + Files.newInputStream(Path.of(path)) , FilePasswordProvider.of("theaustraliancricketteamisthebest")); + assertThat(keyPairs.iterator().next(), notNullValue()); + }); + } + + @Test + public void BCFIPSRegisteredRejectSSHkeyGen() throws Throwable { + + URI path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("id_rsa-keygen-rsa-2048")).toURI(); + j.then(r -> { + try { + Iterable keyPairs = SecurityUtils.loadKeyPairIdentities(null, null, + Files.newInputStream(Path.of(path)), FilePasswordProvider.of("theaustraliancricketteamisthebest")); + keyPairs.iterator().next(); + } catch (NoSuchAlgorithmException e) { + // all good + // we cannot use expected = NoSuchAlgorithmException.class + // as we get org.jvnet.hudson.test.RealJenkinsRule$StepException + } + }); + } + + @Test + public void BCFIPSRegisteredRejectSSHkeyGened25519() throws Throwable { + + URI path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("ed25519.pem")).toURI(); + j.then(r -> { + try { + Iterable keyPairs = SecurityUtils.loadKeyPairIdentities(null, null, + Files.newInputStream(Path.of(path)), FilePasswordProvider.of("")); + keyPairs.iterator().next(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + // all good + // we cannot use expected = NoSuchAlgorithmException.class + // as we get org.jvnet.hudson.test.RealJenkinsRule$StepException + } + }); + } + + @Test + public void loadSSHkeyGen_ed25519() throws Throwable { + URI path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("ed25519.pem")).toURI(); + Iterable keyPairs = SecurityUtils.loadKeyPairIdentities(null, null, + Files.newInputStream(Path.of(path)), FilePasswordProvider.of("")); + assertThat(keyPairs.iterator().next(), notNullValue()); + } + + @Test + public void loadSSHkeyGen() throws Throwable { + URI path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("id_rsa-keygen-rsa-2048")).toURI(); + Iterable keyPairs = SecurityUtils.loadKeyPairIdentities(null, null, + Files.newInputStream(Path.of(path)), FilePasswordProvider.of("theaustraliancricketteamisthebest")); + assertThat(keyPairs.iterator().next(), notNullValue()); + + } + +} \ No newline at end of file diff --git a/mina-sshd-api-common/src/test/resources/ed25519.pem b/mina-sshd-api-common/src/test/resources/ed25519.pem new file mode 100644 index 0000000..d0df135 --- /dev/null +++ b/mina-sshd-api-common/src/test/resources/ed25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIKZJkXaT3aQW9hmCefM7leOt/pXVAC5HAZTfw85tJGI+ +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048 b/mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048 new file mode 100644 index 0000000..bb4e45d --- /dev/null +++ b/mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048 @@ -0,0 +1,50 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +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 +Zb1gnNln2c/us1N2cIYRIssU8= +-----END OPENSSH PRIVATE KEY----- diff --git a/mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048.pub b/mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048.pub new file mode 100644 index 0000000..f5cf1d1 --- /dev/null +++ b/mina-sshd-api-common/src/test/resources/id_rsa-keygen-rsa-2048.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDVjUbLFP9Qjj6WC2vomiQJBLhSFHO8+J0oauXRe6Z0ZtVC+jZgyy7bmQhOypblipTzR5H95QmimMPQL4EKbjpW/PsCp4kG8kRlbFkTk2fPm9LHmOcPUs5iWpDtm8ciF7V1/an1APNX0p9XZU/ieIWDbW8p2Kd9vNlLwlu10/3NT0/RTjBKEtxhdtbkXPJ0jO1JlpNa3Q946LyIw3K7DieaHoNedsnPdBcIl7c3TM2ymQh4fPpBXwIGEcWiuftkITLXQH0z2Buy9x5t0bybXS1BaAiOzbrPGHZC8sW1yK7gvWU3a8ahQNMGLzjo89ni4TrVny3o5N2t6L4kQ/9W76iUkAFreM5Nepofuw6w709e8EvyH8jIT7WuqdVtgpEVTqEc24fyNHVYbV0EJC+jFwqvYEG+rF8aKbWMaI6G4OA319Ikby/jN/fQnx7TDQnkwBHKAusq4yBfB6cCSqpWnJW5tQLD/Ux92jQ3WtJq4qAV4xqlHo3AalH15NmyMb8fB3kzjunxuuQmoln06HkWw2mANQi8BhS1lBvfV/dMC3UQuRa2sGnsUXHXE7y68YXSpJTk0gHSQbTi5ktFlLkgqaBI/4Zpuw4pZVKVqfJ/FKO57QRFlKGsaho9VqUxqwTml0iZrCj13tlLgnkg1stb5xv0x+jxJZFujsWCH5czzzcCtw== olamy@pop-os diff --git a/mina-sshd-api-common/src/test/resources/rsa2048 b/mina-sshd-api-common/src/test/resources/rsa2048 new file mode 100644 index 0000000..64c3c83 --- /dev/null +++ b/mina-sshd-api-common/src/test/resources/rsa2048 @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQOLc7iLj3g03uXcZp +lQ+k7wICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIFSRgLbfugPgEggTI +nSQ13Z0m3okyCMg6gPJkrsT7fgES3d61yg0F5EVIY2iIrUXnu7RhjbpXiAMj9arc +QNJaHVOH9OyNhMd/djFXvKB0xLK+l+OF9vzipqFqBAWdyzRLIEmEHzmrF8qBRcCt +bJOkTbtLYOpZYIk0UEY4XwrN1X6kZupWItVOZxHOdORBrYgfAXXAzs1QA4NKzW0u +c21t/f+iaTvpSgCnLa83Xc20vkbI0VvNLkapimojXJennAjxBV1pm5R80crA2/HH +w4r8MZ7aoMJ96H/V09lLN35D+wqx3LAmF07u3nHW0QmhdH5lY8spzOSeqAYC1dPM +vTI8kRQ9xpjMzHY2DyE/FSSUxcVzz8czbVN+QvRvlxP8AYoedpsYyagTWbrUs0Fq +lVswNSAqbLxxrteMEquSbAtjz0sl2lK15FD1V+4CNzc6jOpmmeMA3duFYXTg6vb+ +ALxHHDtS1Y00b5gHnDPt3s/Uxpv6s4m7xvwZ3WztKaO9OC8rIanMK4lB+OxogPv9 +ai/HXItR1reCWDDnRU7uXfOkXRlAOKSPhSK3vgsYfP8TVc5arlenFOmUwAuwHE1x +CCFg58EKVEgTNKRfupr/63819XM9l/3MqLSEiprMb+aACYEGWCulSWEcF2xq1AOE +uuvzhhXfuzYfSg/Qg7uZRb96M0/Or5sxRY4SUct8Ml/aHd3+2TZLEZGETBGXZi7E +O2MB8mO759NToFcG2wXvcPQ1zzIyj/en37yf1zuYQjEFzis2uNA/FNg3i2gdmbxR +XlEs/Ixe3HFsETJIlgWHHzC4HHtzEvzEKOvYd4qLyU8nQEMfNSk0YCUDyGYrmOub +u0Mq1UCgzVxoJxBqUiOtJkzzwCgxkjCGFfI781zS4juViAiIv12VG0hF5TRRZwPy +VvnH1i8vDkjFa2eXkA5/XYpO4euDN0DW77vOg6XRoPJewzmsvcykTfTj8H4XvtA9 +BUxSEqd4Y++eMeQFLQ/CVrKmzAEhT2pYhWdlJ+y5PchHy5BE+HyjUxmWIVozvrBg +MXi538qFRykPZC60hDP66QFwYep2xlcDwpEdN5Wtsqur32GUzSvEV7Nl7NXouj5y +M9XiC38ze6+mgOQJqDWBcTQGZxI4AxBXTq3HPYsibFzWX8195+wJSYX/M9GaHrRN +brWuzCxAJJYZ5JMP0zY9eVQfz/BKg6V78vdKuQEm1R2+5j57tR4CkpTumYlaqGzp +L/m7LJC8ssQGMn23hoodcOGO760CXa3O5vA2zfr1jkUd56Rc0SRG8Mxgypv0JM5G ++BAmD71rbttdhNxBlfe9a3XTW0MJqSzoOKYoVMAnvyhdvSGtIZYGDM0WD0fTrK15 +DbbvyWao74VFHPjVA0Fuo2vvdzLH+pZeXA19mYK7yMw5vsveroD7fk7Z05DYuHpb +pnO9vIFns3/PhBm7/IcBBgOX/b+tHLTd0jf+TeCrIn5hZXMz5AhjLpeVxux1O8Yd +wOajN9cywUZ/GuGhU1XxbkPieouIFgchS1MF92gmr6LX8T9kqEJfuAALceJ8FlwP +A7k878FhY9HinPMyNtw3G+wx1FdiqUXoP2dh7URFjTSjZPl2GDEnQO05HeJ+NBeD +3tJ10eVgpVEBaOF9Uru6LROnpZYjdyww +-----END ENCRYPTED PRIVATE KEY----- \ No newline at end of file diff --git a/pom.xml b/pom.xml index d2059c0..f29bd29 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,23 @@ mina-sshd-api-core ${project.version} + + org.junit + junit-bom + 5.11.0 + pom + import + + + org.hamcrest + hamcrest + 3.0 + + + commons-io + commons-io + 2.16.1 +