This is an automated email from the ASF dual-hosted git repository. gansheer pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit ed3e429ceaf6554defe62a99edb92c53177b44b1 Author: Gaelle Fournier <[email protected]> AuthorDate: Thu Apr 16 18:36:35 2026 +0200 feat: Add Camel SFTP Host Certificate Verification --- .../component/ftp/deployment/FtpProcessor.java | 17 +++ .../support/sftp/SftpHostCertTestResource.java | 170 +++++++++++++++++++++ .../quarkus/component/sftp/it/SftpResource.java | 114 ++++++++++++++ .../ftp/src/main/resources/application.properties | 2 +- .../main/resources/certs/generate-certificates.sh | 66 ++++++-- .../ftp/src/main/resources/certs/host-ca.pub | 1 + .../src/main/resources/certs/host-key-rsa-cert.pub | 1 + .../ftp/src/main/resources/certs/host-key-rsa.key | 27 ++++ .../src/main/resources/certs/test-key-rsa-cert.pub | 2 +- .../ftp/src/main/resources/certs/test-key-rsa.key | 48 +++--- .../quarkus/component/sftp/it/SftpHostCertIT.java | 18 +-- .../component/sftp/it/SftpHostCertTest.java | 78 ++++++++++ 12 files changed, 493 insertions(+), 51 deletions(-) diff --git a/extensions/ftp/deployment/src/main/java/org/apache/camel/quarkus/component/ftp/deployment/FtpProcessor.java b/extensions/ftp/deployment/src/main/java/org/apache/camel/quarkus/component/ftp/deployment/FtpProcessor.java index 218c96e6f6..967123e502 100644 --- a/extensions/ftp/deployment/src/main/java/org/apache/camel/quarkus/component/ftp/deployment/FtpProcessor.java +++ b/extensions/ftp/deployment/src/main/java/org/apache/camel/quarkus/component/ftp/deployment/FtpProcessor.java @@ -18,6 +18,7 @@ package org.apache.camel.quarkus.component.ftp.deployment; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; class FtpProcessor { @@ -27,4 +28,20 @@ class FtpProcessor { FeatureBuildItem feature() { return new FeatureBuildItem(FEATURE); } + + @BuildStep + ReflectiveClassBuildItem registerJSchCertificateClasses() { + // JSch OpenSSH certificate support classes for @cert-authority parsing in known_hosts + // The quarkus-jsch reflection config is missing several classes that JSch loads dynamically. + return ReflectiveClassBuildItem.builder( + "com.jcraft.jsch.KeyPairRSA", + "com.jcraft.jsch.KeyPairECDSA", + "com.jcraft.jsch.KeyPairEd25519", + "com.jcraft.jsch.KeyPairEd448", + "com.jcraft.jsch.KeyPairDSA", + "com.jcraft.jsch.SignatureRSA", + "com.jcraft.jsch.SignatureECDSA", + "com.jcraft.jsch.jce.SignatureEd25519") + .build(); + } } diff --git a/integration-tests-support/sftp/src/main/java/org/apache/camel/quarkus/test/support/sftp/SftpHostCertTestResource.java b/integration-tests-support/sftp/src/main/java/org/apache/camel/quarkus/test/support/sftp/SftpHostCertTestResource.java new file mode 100644 index 0000000000..1f45db0327 --- /dev/null +++ b/integration-tests-support/sftp/src/main/java/org/apache/camel/quarkus/test/support/sftp/SftpHostCertTestResource.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.quarkus.test.support.sftp; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.PublicKey; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; +import org.apache.camel.quarkus.test.AvailablePortFinder; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.config.keys.OpenSshCertificate; +import org.apache.sshd.common.config.keys.PublicKeyEntry; +import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; +import org.apache.sshd.common.keyprovider.FileKeyPairProvider; +import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.apache.sshd.common.signature.BuiltinSignatures; +import org.apache.sshd.common.signature.Signature; +import org.apache.sshd.scp.server.ScpCommandFactory; +import org.apache.sshd.server.SshServer; +import org.apache.sshd.sftp.server.SftpSubsystemFactory; +import org.jboss.logging.Logger; + +/** + * SFTP test resource that presents host certificates for host certificate verification testing. + * This is separate from SftpTestResource to avoid interfering with other tests. + */ +public class SftpHostCertTestResource implements QuarkusTestResourceLifecycleManager { + private static final Logger LOGGER = Logger.getLogger(SftpHostCertTestResource.class); + + private SshServer sshServer; + private Path sftpHome; + + @Override + public Map<String, String> start() { + try { + final int port = AvailablePortFinder.getNextAvailable(); + + sftpHome = Files.createTempDirectory("sftp-hostcert-"); + Path adminHome = sftpHome.resolve("admin"); + Files.createDirectories(adminHome); + + VirtualFileSystemFactory factory = new VirtualFileSystemFactory(); + factory.setUserHomeDir("admin", adminHome.toAbsolutePath()); + + sshServer = SshServer.setUpDefaultServer(); + sshServer.setPort(port); + + sshServer.setKeyPairProvider(createHostCertKeyPairProvider()); + + sshServer.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory())); + sshServer.setCommandFactory(new ScpCommandFactory()); + sshServer.setPasswordAuthenticator((username, password, session) -> true); + sshServer.setPublickeyAuthenticator((username, key, session) -> true); + + // Add certificate signature factories + List<NamedFactory<Signature>> signatureFactories = sshServer.getSignatureFactories(); + signatureFactories.add(BuiltinSignatures.rsa_cert); + signatureFactories.add(BuiltinSignatures.rsaSHA256_cert); + signatureFactories.add(BuiltinSignatures.rsaSHA512_cert); + signatureFactories.add(BuiltinSignatures.ed25519_cert); + sshServer.setSignatureFactories(signatureFactories); + + sshServer.setFileSystemFactory(factory); + sshServer.start(); + + LOGGER.infof("SFTP server with host certificate started on port %d", port); + + return Collections.singletonMap("camel.sftp.hostcert.test-port", Integer.toString(port)); + } catch (Exception e) { + throw new RuntimeException("Failed to start SFTP server with host certificate", e); + } + } + + @Override + public void stop() { + try { + if (sshServer != null) { + sshServer.stop(); + LOGGER.info("SFTP server with host certificate stopped"); + } + } catch (Exception e) { + LOGGER.warn("Failed to stop SFTP server", e); + } + + try { + if (sftpHome != null && Files.exists(sftpHome)) { + Files.walk(sftpHome) + .sorted((a, b) -> -a.compareTo(b)) + .forEach(path -> { + try { + Files.delete(path); + } catch (Exception e) { + LOGGER.warn("Failed to delete SFTPHome file", e); + } + }); + } + } catch (Exception e) { + LOGGER.warnf("Failed to delete sftp home: %s, %s", sftpHome, e); + } + + AvailablePortFinder.releaseReservedPorts(); + } + + /** + * Creates a KeyPairProvider that wraps the host private key with the OpenSSH host certificate. + */ + private static KeyPairProvider createHostCertKeyPairProvider() { + try { + // Load host private key from classpath (works in both JVM and native mode) + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + // Write host key to temp file (FileKeyPairProvider needs a file path) + Path tempHostKey = Files.createTempFile("host-key-rsa", ".key"); + try (var keyStream = classLoader.getResourceAsStream("certs/host-key-rsa.key")) { + if (keyStream == null) { + throw new IllegalStateException("Host key resource not found: certs/host-key-rsa.key"); + } + Files.write(tempHostKey, keyStream.readAllBytes()); + } + + FileKeyPairProvider keyProvider = new FileKeyPairProvider(tempHostKey); + KeyPair originalKeyPair = keyProvider.loadKeys(null).iterator().next(); + + // Load host certificate from classpath + String certLine; + try (var certStream = classLoader.getResourceAsStream("certs/host-key-rsa-cert.pub")) { + if (certStream == null) { + throw new IllegalStateException("Host certificate resource not found: certs/host-key-rsa-cert.pub"); + } + certLine = new String(certStream.readAllBytes()).trim(); + } + + PublicKey certKey = PublicKeyEntry.parsePublicKeyEntry(certLine).resolvePublicKey(null, null, null); + + if (!(certKey instanceof OpenSshCertificate)) { + throw new IllegalStateException("Host certificate file does not contain an OpenSSH certificate"); + } + + // Create a key pair with the certificate as the public key + KeyPair certKeyPair = new KeyPair(certKey, originalKeyPair.getPrivate()); + + // Clean up temp file + Files.deleteIfExists(tempHostKey); + + return KeyPairProvider.wrap(certKeyPair); + + } catch (Exception e) { + throw new RuntimeException("Failed to load host certificate key pair", e); + } + } +} diff --git a/integration-tests/ftp/src/main/java/org/apache/camel/quarkus/component/sftp/it/SftpResource.java b/integration-tests/ftp/src/main/java/org/apache/camel/quarkus/component/sftp/it/SftpResource.java index 14de2c50c5..0a890acd93 100644 --- a/integration-tests/ftp/src/main/java/org/apache/camel/quarkus/component/sftp/it/SftpResource.java +++ b/integration-tests/ftp/src/main/java/org/apache/camel/quarkus/component/sftp/it/SftpResource.java @@ -225,4 +225,118 @@ public class SftpResource { TIMEOUT_MS, String.class); } + + @Path("/hostcert/create/{fileName}") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public Response createFileWithHostCertVerification(@PathParam("fileName") String fileName, String fileContent) + throws Exception { + String knownHostsFile = createHostCaKnownHostsFile(); + String port = context.resolvePropertyPlaceholders("{{camel.sftp.hostcert.test-port}}"); + String uri = "sftp://admin@localhost:" + port + + "/sftp?password=admin&strictHostKeyChecking=yes&useUserKnownHostsFile=false&caSignatureAlgorithms=ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa&knownHostsFile=" + + knownHostsFile; + producerTemplate.sendBodyAndHeader(uri, fileContent, Exchange.FILE_NAME, fileName); + return Response + .created(new URI("https://camel.apache.org/")) + .build(); + } + + @Path("/hostcert/get/{fileName}") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getFileWithHostCertVerification(@PathParam("fileName") String fileName) throws Exception { + String knownHostsFile = createHostCaKnownHostsFile(); + return consumerTemplate.receiveBody( + "sftp://admin@localhost:{{camel.sftp.hostcert.test-port}}/sftp?password=admin&strictHostKeyChecking=yes&useUserKnownHostsFile=false&caSignatureAlgorithms=ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa&knownHostsFile=" + + knownHostsFile + "&localWorkDirectory=target&fileName=" + fileName, + TIMEOUT_MS, + String.class); + } + + @Path("/hostcert/delete/{fileName}") + @DELETE + public Response deleteFileWithHostCert(@PathParam("fileName") String fileName) throws Exception { + String knownHostsFile = createHostCaKnownHostsFile(); + consumerTemplate.receiveBody( + "sftp://admin@localhost:{{camel.sftp.hostcert.test-port}}/sftp?password=admin&strictHostKeyChecking=yes&useUserKnownHostsFile=false&caSignatureAlgorithms=ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa&knownHostsFile=" + + knownHostsFile + "&delete=true&fileName=" + fileName, + TIMEOUT_MS, + String.class); + return Response.noContent().build(); + } + + @Path("/hostcertWithAlgorithms/create/{fileName}") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public Response createFileWithHostCertAndAlgorithms(@PathParam("fileName") String fileName, String fileContent) + throws Exception { + String knownHostsFile = createHostCaKnownHostsFile(); + producerTemplate.sendBodyAndHeader( + "sftp://admin@localhost:{{camel.sftp.hostcert.test-port}}/sftp?password=admin&strictHostKeyChecking=yes&useUserKnownHostsFile=false&knownHostsFile=" + + knownHostsFile + "&caSignatureAlgorithms=ssh-ed25519,rsa-sha2-512,rsa-sha2-256", + fileContent, + Exchange.FILE_NAME, fileName); + return Response + .created(new URI("https://camel.apache.org/")) + .build(); + } + + @Path("/hostcertWithAlgorithms/get/{fileName}") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getFileWithHostCertAndAlgorithms(@PathParam("fileName") String fileName) throws Exception { + String knownHostsFile = createHostCaKnownHostsFile(); + return consumerTemplate.receiveBody( + "sftp://admin@localhost:{{camel.sftp.hostcert.test-port}}/sftp?password=admin&strictHostKeyChecking=yes&useUserKnownHostsFile=false&knownHostsFile=" + + knownHostsFile + + "&caSignatureAlgorithms=ssh-ed25519,rsa-sha2-512,rsa-sha2-256&localWorkDirectory=target&fileName=" + + fileName, + TIMEOUT_MS, + String.class); + } + + @Path("/hostcertWithAlgorithms/delete/{fileName}") + @DELETE + public Response deleteFileWithHostCertAndAlgorithms(@PathParam("fileName") String fileName) throws Exception { + String knownHostsFile = createHostCaKnownHostsFile(); + consumerTemplate.receiveBody( + "sftp://admin@localhost:{{camel.sftp.hostcert.test-port}}/sftp?password=admin&strictHostKeyChecking=yes&useUserKnownHostsFile=false&knownHostsFile=" + + knownHostsFile + + "&caSignatureAlgorithms=ssh-ed25519,rsa-sha2-512,rsa-sha2-256&delete=true&fileName=" + + fileName, + TIMEOUT_MS, + String.class); + return Response.noContent().build(); + } + + /** + * Creates a known_hosts file with @cert-authority entry for the host CA. + * This allows the client to verify the server's host certificate. + */ + private String createHostCaKnownHostsFile() throws Exception { + String resourcePath = "certs/host-ca.pub"; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + try (InputStream stream = classLoader.getResourceAsStream(resourcePath)) { + if (stream == null) { + throw new RuntimeException("Failed to load host CA public key from: " + resourcePath); + } + return processHostCaStream(stream); + } + } + + private String processHostCaStream(InputStream hostCaStream) throws Exception { + String hostCaPubKey = new String(hostCaStream.readAllBytes()).trim(); + String port = context.resolvePropertyPlaceholders("{{camel.sftp.hostcert.test-port}}"); + + // Create known_hosts with @cert-authority entry + String knownHostsContent = String.format("@cert-authority [localhost]:%s %s%n", port, hostCaPubKey); + + // Use temp directory instead of "target" which may not exist in native mode runtime + java.nio.file.Path knownHostsPath = java.nio.file.Files.createTempFile("known_hosts_hostcert", ".txt"); + java.nio.file.Files.writeString(knownHostsPath, knownHostsContent); + + return knownHostsPath.toAbsolutePath().toString(); + } } diff --git a/integration-tests/ftp/src/main/resources/application.properties b/integration-tests/ftp/src/main/resources/application.properties index 9d05789d61..25bfaf1cfa 100644 --- a/integration-tests/ftp/src/main/resources/application.properties +++ b/integration-tests/ftp/src/main/resources/application.properties @@ -18,4 +18,4 @@ # Change to INFO level to get insights about commands run on the FTP server quarkus.log.category."org.apache.ftpserver".level = WARNING -quarkus.native.resources.includes=certs/test-key-rsa.key,certs/test-key-rsa-cert.pub +quarkus.native.resources.includes=certs/test-key-rsa.key,certs/test-key-rsa-cert.pub,certs/host-ca.pub,certs/host-key-rsa.key,certs/host-key-rsa-cert.pub diff --git a/integration-tests/ftp/src/main/resources/certs/generate-certificates.sh b/integration-tests/ftp/src/main/resources/certs/generate-certificates.sh index 8780f21483..8e9079b3e7 100755 --- a/integration-tests/ftp/src/main/resources/certs/generate-certificates.sh +++ b/integration-tests/ftp/src/main/resources/certs/generate-certificates.sh @@ -20,11 +20,17 @@ # Script to generate OpenSSH certificate files for SFTP integration tests # # This script creates: +# USER CERTIFICATES: # - test-key-rsa.key: RSA private key (2048 bits) -# - test-key-rsa-cert.pub: OpenSSH certificate signed by a temporary CA +# - test-key-rsa-cert.pub: OpenSSH user certificate signed by user CA # -# The certificate is valid for 52 weeks and can be used for testing -# certificate-based authentication with the mina-sftp component. +# HOST CERTIFICATES: +# - host-ca.pub: Host CA public key (for @cert-authority in known_hosts) +# - host-key-rsa.key: RSA host private key (2048 bits) +# - host-key-rsa-cert.pub: OpenSSH host certificate signed by host CA +# +# The certificates are valid for 52 weeks and can be used for testing +# certificate-based authentication with the FTP component. # set -e @@ -33,34 +39,66 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" echo "===================================================================" -echo "Generating OpenSSH Certificates for MINA SFTP Integration Tests" +echo "Generating OpenSSH Certificates for FTP Integration Tests" echo "===================================================================" echo "" echo "Cleaning up existing files..." -rm -f ca-key ca-key.pub test-key-rsa.key test-key-rsa.key.pub test-key-rsa-cert.pub +rm -f user-ca user-ca.pub test-key-rsa.key test-key-rsa.key.pub test-key-rsa-cert.pub +rm -f host-ca host-ca.pub host-key-rsa.key host-key-rsa.key.pub host-key-rsa-cert.pub echo "Cleaned up existing files" -echo "Generating temporary CA key pair..." -ssh-keygen -t rsa -b 2048 -f ca-key -N "" -C "test-ca" > /dev/null 2>&1 -echo "Created ca-key and ca-key.pub" +echo "" +echo "--- USER CERTIFICATE GENERATION ---" +echo "" + +echo "Generating user CA key pair..." +ssh-keygen -t rsa -b 2048 -f user-ca -N "" -C "user-ca" > /dev/null 2>&1 +echo "Created user-ca and user-ca.pub" echo "Generating user RSA key pair..." ssh-keygen -t rsa -b 2048 -f test-key-rsa.key -N "" -C "test-rsa@test" > /dev/null 2>&1 echo "Created test-key-rsa.key and test-key-rsa.key.pub" -echo "Signing public key with CA to create certificate..." -ssh-keygen -s ca-key \ +echo "Signing user public key with user CA to create certificate..." +ssh-keygen -s user-ca \ -I "test-user" \ - -n testuser \ + -n admin \ -V +520w \ test-key-rsa.key.pub > /dev/null 2>&1 echo "Created test-key-rsa.key-cert.pub" -echo "Renaming certificate to test-key-rsa-cert.pub..." +echo "Renaming user certificate to test-key-rsa-cert.pub..." mv test-key-rsa.key-cert.pub test-key-rsa-cert.pub echo "Renamed to test-key-rsa-cert.pub" +echo "" +echo "--- HOST CERTIFICATE GENERATION ---" +echo "" + +echo "Generating host CA key pair..." +ssh-keygen -t ed25519 -f host-ca -N "" -C "host-ca" > /dev/null 2>&1 +echo "Created host-ca and host-ca.pub" + +echo "Generating host RSA key pair..." +ssh-keygen -t rsa -b 2048 -f host-key-rsa.key -N "" -C "sftp-server@localhost" > /dev/null 2>&1 +echo "Created host-key-rsa.key and host-key-rsa.key.pub" + +echo "Signing host public key with host CA to create host certificate..." +ssh-keygen -s host-ca \ + -I "sftp-server" \ + -h \ + -n localhost \ + -V +520w \ + host-key-rsa.key.pub > /dev/null 2>&1 +echo "Created host-key-rsa.key-cert.pub" + +echo "Renaming host certificate to host-key-rsa-cert.pub..." +mv host-key-rsa.key-cert.pub host-key-rsa-cert.pub +echo "Renamed to host-key-rsa-cert.pub" + +echo "" echo "Cleaning up temporary files..." -rm -f ca-key ca-key.pub test-key-rsa.key.pub -echo "Removed CA keys and public key" +rm -f user-ca user-ca.pub test-key-rsa.key.pub +rm -f host-ca host-key-rsa.key.pub +echo "Removed CA private keys and public keys" diff --git a/integration-tests/ftp/src/main/resources/certs/host-ca.pub b/integration-tests/ftp/src/main/resources/certs/host-ca.pub new file mode 100644 index 0000000000..d0a10f97cf --- /dev/null +++ b/integration-tests/ftp/src/main/resources/certs/host-ca.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITv5DGdGNCNkU+DMJsMsAsFoufh4yO5DB2PK48eUmqq host-ca diff --git a/integration-tests/ftp/src/main/resources/certs/host-key-rsa-cert.pub b/integration-tests/ftp/src/main/resources/certs/host-key-rsa-cert.pub new file mode 100644 index 0000000000..79c92a7e12 --- /dev/null +++ b/integration-tests/ftp/src/main/resources/certs/host-key-rsa-cert.pub @@ -0,0 +1 @@ [email protected] AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgqfpKfP1HcEq0OkSV20BryP+RpxsjCw3QYoLiO7G8p7sAAAADAQABAAABAQCktIEA/teg39lFRRPsFNVfbE93LCj7cewNUjZX+xoHWpBWA6FfaM1xBFmx99W0SAq9TwX8RC8yNzZAYOY4HQIwJYP9NNskMXrV4efQ/HFxg/OGETKDLi/bFgGEJPzIUX+vwLw+RVBcH8HhxHbhvCX/TBliZ6StOlIgoYi/2RKKp9uh3EmOU5gHozNz0jdzRPgVVQvgQvrW7E+2PS/GI67IUxElZSaI8qYFslXbw+iyQSdBg+ZqFl8Z9HVQweV/H5w0o1+87lWkaSIQxJccbmTcaub4Q+KWLci4qALE7yKLnVpuPJxe7zNV/uqd7fh85v5pl8MxQasZLduPf2AyoM1rAAAAAAAAAAAAAAA [...] diff --git a/integration-tests/ftp/src/main/resources/certs/host-key-rsa.key b/integration-tests/ftp/src/main/resources/certs/host-key-rsa.key new file mode 100644 index 0000000000..a8cff6531f --- /dev/null +++ b/integration-tests/ftp/src/main/resources/certs/host-key-rsa.key @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEApLSBAP7XoN/ZRUUT7BTVX2xPdywo+3HsDVI2V/saB1qQVgOhX2jN +cQRZsffVtEgKvU8F/EQvMjc2QGDmOB0CMCWD/TTbJDF61eHn0PxxcYPzhhEygy4v2xYBhC +T8yFF/r8C8PkVQXB/B4cR24bwl/0wZYmekrTpSIKGIv9kSiqfbodxJjlOYB6Mzc9I3c0T4 +FVUL4EL61uxPtj0vxiOuyFMRJWUmiPKmBbJV28PoskEnQYPmahZfGfR1UMHlfx+cNKNfvO +5VpGkiEMSXHG5k3Grm+EPili3IuKgCxO8ii51abjycXu8zVf7qne34fOb+aZfDMUGrGS3b +j39gMqDNawAAA9CCNbkKgjW5CgAAAAdzc2gtcnNhAAABAQCktIEA/teg39lFRRPsFNVfbE +93LCj7cewNUjZX+xoHWpBWA6FfaM1xBFmx99W0SAq9TwX8RC8yNzZAYOY4HQIwJYP9NNsk +MXrV4efQ/HFxg/OGETKDLi/bFgGEJPzIUX+vwLw+RVBcH8HhxHbhvCX/TBliZ6StOlIgoY +i/2RKKp9uh3EmOU5gHozNz0jdzRPgVVQvgQvrW7E+2PS/GI67IUxElZSaI8qYFslXbw+iy +QSdBg+ZqFl8Z9HVQweV/H5w0o1+87lWkaSIQxJccbmTcaub4Q+KWLci4qALE7yKLnVpuPJ +xe7zNV/uqd7fh85v5pl8MxQasZLduPf2AyoM1rAAAAAwEAAQAAAQBERCqKGpaOL+nSm7iP +q+zqga6IMw4DdisENHSgz8twi9lyRUvwCzTHqKlyqcnyUL/eyi+taSd0tUyvr1oMnP1ork +wAOZWw8S88Ekeup8tvZOUdRuh8VbrxIDRdrKT3dEwrsQN0/e66WFFYfcFWe9D1+Xk1/8ZS +JG+g5cMT3WmhfRpBmIRZT3nHQTdjkrVIsK1dkbgCbxQBX/hKD/nV0AC/HjBbqx7MVvpK4N +MKy+Prg/JUvy51GtUeQNZw92Gc7osLpFAzh2apgMhK1ZbWyPjrb/mQ/NXVG6ETuW+3ktwh +qLLGPjwG/eE+UgWqn1LFgHINpD5y8NbuQ7bCkM5ADaF9AAAAgCIkD2uKemGJpnbEHBGVOO +sRoP5TGDMPwueagpH8JPll6GfQo7NzxZQJttp4FDCmzbbTN6u7Jk7f8UHprhhzk//Lgm+a +Ds1qGN11WA32MFCUJixtgc3re1W4nGF3BIshv4tScQVpMcBhDBtSJSzeVpHfeB+k2hzkK2 +/TCbMAHjmZAAAAgQDPbPrfiAbSjmCOjgf/aa82D73Rry/wL4d6bZTi2wwxu3HmzuA0G7L8 +qGV9UIEJ5jFHbQq/wP46XpiIESfkg+PmrUGOwZQ22cjpsPzxJzysvK3oYiuXduE8uz+c/F +HDx1ONNeDB011k3h1JRcquYrU5SMqWnZLF/vnyvqQgywLXNwAAAIEAy0Z0WJFIiMYJTf4N +Wa79DbsDutVei72QpoAwMlzBouD2YBArxznCsVC3rVKPVBlUTT2oszCfwQLo3fMjZs3yjW +SrKc2/EDex8drZg+w262mX+bDQZD+PI1LgrpF7L5e5//id4On5ie6cwIc+8RJRelqhrftT +T0lFiZ4C2JhdrW0AAAAVc2Z0cC1zZXJ2ZXJAbG9jYWxob3N0AQIDBAUG +-----END OPENSSH PRIVATE KEY----- diff --git a/integration-tests/ftp/src/main/resources/certs/test-key-rsa-cert.pub b/integration-tests/ftp/src/main/resources/certs/test-key-rsa-cert.pub index ba21c6c28b..fa138337ce 100644 --- a/integration-tests/ftp/src/main/resources/certs/test-key-rsa-cert.pub +++ b/integration-tests/ftp/src/main/resources/certs/test-key-rsa-cert.pub @@ -1 +1 @@ [email protected] AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgwsB0wUJYWF7WQayMI3EFz63+rzdGxxmy6QmkXXigkbcAAAADAQABAAABAQCd2V7v8ELetZfmObUn3zP28B44AhcDnOdZHErgl1fe6e2Jmnja4BhdtZAG+XZEmKwlQnWNkRJfDs1/ryfP8xthcZIlklEb/6F2D2zmot5BnY6zzL6XWPtuTny1Ym7iah6KSPv9vS9nc2wcEA9BFu+CYHHWJYskv/PE/hwebae/upKafppCWJ97+Kdkc87Whsd1y2PxhhPPCbH/lOOOmzw2qTyxpYciHsr9NaBfoHRwvTWBdWjk6pYKtw+4gmWOmyAngOh9jcXeJ5pKUhWeJh5fmMrHL/mv/0DtrGQ2fhhrYZjIbkuha5EOFqurnXHjwwBz3Ey8JOhaF9UwQlCvOOWnAAAAAAAAAAAAAAA [...] [email protected] AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgWeZP/HKtZqoYUuTZBJDuYgLpWoFI881IQ2mL7gMIVrIAAAADAQABAAABAQCQ6auYkjIBrtHexpzxGkfWoDU0IeRiye2HjGKZa7fHRdYYaQBstJXcnPgzcgOY70dzYe5A9WorWhi+33YWrox6k16WeeYREcogRZ12M0hnEZQa74J5yUHDxHGofHrgh4SIPqE/HOukC8X5LGYV5VPD4wabFdBxbpuPLuPcddWMhZCdd9OqXGizk5nwLhIOM7N3A5kDxn2NtyBPt3WsxCM8Y4d+yWR6BeoUzhBDRFx2yw1aNLbj7aoc7sMPOJXgIJDI0nm1mcdmF1FQqnPlG4zmh9G1B7B8cZfQ8kz+Q+GrFGkgPSwRqZ14+CUfymutuJzcU7hBDdfgbBu5UWnade7dAAAAAAAAAAAAAAA [...] diff --git a/integration-tests/ftp/src/main/resources/certs/test-key-rsa.key b/integration-tests/ftp/src/main/resources/certs/test-key-rsa.key index 751cc308be..ac6440d774 100644 --- a/integration-tests/ftp/src/main/resources/certs/test-key-rsa.key +++ b/integration-tests/ftp/src/main/resources/certs/test-key-rsa.key @@ -1,27 +1,27 @@ -----BEGIN OPENSSH PRIVATE KEY----- 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 -----END OPENSSH PRIVATE KEY----- diff --git a/extensions/ftp/deployment/src/main/java/org/apache/camel/quarkus/component/ftp/deployment/FtpProcessor.java b/integration-tests/ftp/src/test/java/org/apache/camel/quarkus/component/sftp/it/SftpHostCertIT.java similarity index 69% copy from extensions/ftp/deployment/src/main/java/org/apache/camel/quarkus/component/ftp/deployment/FtpProcessor.java copy to integration-tests/ftp/src/test/java/org/apache/camel/quarkus/component/sftp/it/SftpHostCertIT.java index 218c96e6f6..2562a13d33 100644 --- a/extensions/ftp/deployment/src/main/java/org/apache/camel/quarkus/component/ftp/deployment/FtpProcessor.java +++ b/integration-tests/ftp/src/test/java/org/apache/camel/quarkus/component/sftp/it/SftpHostCertIT.java @@ -14,17 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.quarkus.component.ftp.deployment; +package org.apache.camel.quarkus.component.sftp.it; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.test.junit.QuarkusIntegrationTest; -class FtpProcessor { - - private static final String FEATURE = "camel-ftp"; - - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(FEATURE); - } +/** + * Integration tests for SFTP host certificate verification in native mode. + */ +@QuarkusIntegrationTest +class SftpHostCertIT extends SftpHostCertTest { } diff --git a/integration-tests/ftp/src/test/java/org/apache/camel/quarkus/component/sftp/it/SftpHostCertTest.java b/integration-tests/ftp/src/test/java/org/apache/camel/quarkus/component/sftp/it/SftpHostCertTest.java new file mode 100644 index 0000000000..d60935976f --- /dev/null +++ b/integration-tests/ftp/src/test/java/org/apache/camel/quarkus/component/sftp/it/SftpHostCertTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.quarkus.component.sftp.it; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import org.apache.camel.quarkus.test.support.certificate.TestCertificates; +import org.apache.camel.quarkus.test.support.sftp.SftpHostCertTestResource; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; + +@TestCertificates(certificates = { + @Certificate(name = "ftp", formats = { + Format.PEM }, password = "password"), + @Certificate(name = "ftp", formats = { + Format.PKCS12 }, password = "password") }) +@QuarkusTest +@QuarkusTestResource(SftpHostCertTestResource.class) +class SftpHostCertTest { + + @Test + public void testHostCertificateVerification() { + RestAssured.given() + .contentType(ContentType.TEXT) + .body("Host certificate verification test") + .post("/sftp/hostcert/create/hostcert-test.txt") + .then() + .statusCode(201); + + RestAssured.get("/sftp/hostcert/get/hostcert-test.txt") + .then() + .statusCode(200) + .body(is("Host certificate verification test")); + + RestAssured.delete("/sftp/hostcert/delete/hostcert-test.txt") + .then() + .statusCode(204); + } + + @Test + public void testHostCertificateVerificationWithCaSignatureAlgorithms() { + // Test host certificate verification with specific CA signature algorithms + RestAssured.given() + .contentType(ContentType.TEXT) + .body("Host cert with CA algorithms test") + .post("/sftp/hostcertWithAlgorithms/create/hostcert-algo-test.txt") + .then() + .statusCode(201); + + RestAssured.get("/sftp/hostcertWithAlgorithms/get/hostcert-algo-test.txt") + .then() + .statusCode(200) + .body(is("Host cert with CA algorithms test")); + + RestAssured.delete("/sftp/hostcertWithAlgorithms/delete/hostcert-algo-test.txt") + .then() + .statusCode(204); + } +}
