mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Fixed itests for missing docker container (#892)
Migrated all tests to junit5 Signed-off-by: Jeroen van Erp <jeroen@hierynomus.com>
This commit is contained in:
@@ -109,6 +109,7 @@ Fork away!
|
|||||||
|
|
||||||
== Release history
|
== Release history
|
||||||
SSHJ 0.36.0 (2023-07-18)::
|
SSHJ 0.36.0 (2023-07-18)::
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pull/861[#861]: Add DefaultSecurityProviderConfig with has BouncyCastle disabled
|
||||||
* Merged https://github.com/hierynomus/sshj/pull/881[#881]: Rewrote test classes to JUnit Jupiter engine
|
* Merged https://github.com/hierynomus/sshj/pull/881[#881]: Rewrote test classes to JUnit Jupiter engine
|
||||||
* Merged https://github.com/hierynomus/sshj/pull/880[#880]: Removed Java 7 backport Socket utilities
|
* Merged https://github.com/hierynomus/sshj/pull/880[#880]: Removed Java 7 backport Socket utilities
|
||||||
* Merged https://github.com/hierynomus/sshj/pull/879[#879]: Replaced custom Base64 with java.util.Base64
|
* Merged https://github.com/hierynomus/sshj/pull/879[#879]: Replaced custom Base64 with java.util.Base64
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ testing {
|
|||||||
implementation "org.slf4j:slf4j-api:2.0.7"
|
implementation "org.slf4j:slf4j-api:2.0.7"
|
||||||
implementation 'org.spockframework:spock-core:2.3-groovy-3.0'
|
implementation 'org.spockframework:spock-core:2.3-groovy-3.0'
|
||||||
implementation "org.mockito:mockito-core:4.11.0"
|
implementation "org.mockito:mockito-core:4.11.0"
|
||||||
|
implementation "org.assertj:assertj-core:3.24.2"
|
||||||
implementation "ru.vyarus:spock-junit5:1.2.0"
|
implementation "ru.vyarus:spock-junit5:1.2.0"
|
||||||
implementation "org.apache.sshd:sshd-core:$sshdVersion"
|
implementation "org.apache.sshd:sshd-core:$sshdVersion"
|
||||||
implementation "org.apache.sshd:sshd-sftp:$sshdVersion"
|
implementation "org.apache.sshd:sshd-sftp:$sshdVersion"
|
||||||
@@ -101,6 +102,7 @@ testing {
|
|||||||
all {
|
all {
|
||||||
testTask.configure {
|
testTask.configure {
|
||||||
testLogging {
|
testLogging {
|
||||||
|
showStandardStreams = false
|
||||||
exceptionFormat = 'full'
|
exceptionFormat = 'full'
|
||||||
}
|
}
|
||||||
include "**/*Test.*"
|
include "**/*Test.*"
|
||||||
@@ -133,8 +135,8 @@ testing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sources {
|
sources {
|
||||||
groovy {
|
java {
|
||||||
srcDirs = ['src/itest/groovy']
|
srcDirs = ['src/itest/java']
|
||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
|
|||||||
7
src/itest/docker-image/entrypoint.sh
Normal file
7
src/itest/docker-image/entrypoint.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/ash
|
||||||
|
|
||||||
|
# generate host keys if not present
|
||||||
|
ssh-keygen -A
|
||||||
|
|
||||||
|
# do not detach (-D), log to stderr (-e), passthrough other arguments
|
||||||
|
exec /usr/sbin/sshd -D -e "$@"
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.key.KeyAlgorithms
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import net.schmizz.sshj.SSHClient
|
|
||||||
import net.schmizz.sshj.transport.TransportException
|
|
||||||
import net.schmizz.sshj.userauth.UserAuthException
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
@Testcontainers
|
|
||||||
class IntegrationSpec extends Specification {
|
|
||||||
@Shared
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should accept correct key for #signatureName"() {
|
|
||||||
given:
|
|
||||||
def config = new DefaultConfig()
|
|
||||||
config.setKeyAlgorithms(Collections.singletonList(signatureFactory))
|
|
||||||
SSHClient sshClient = new SSHClient(config)
|
|
||||||
sshClient.addHostKeyVerifier(fingerprint) // test-containers/ssh_host_ecdsa_key's fingerprint
|
|
||||||
|
|
||||||
when:
|
|
||||||
sshClient.connect(sshd.containerIpAddress, sshd.firstMappedPort)
|
|
||||||
|
|
||||||
then:
|
|
||||||
sshClient.isConnected()
|
|
||||||
|
|
||||||
where:
|
|
||||||
signatureFactory << [KeyAlgorithms.ECDSASHANistp256(), KeyAlgorithms.EdDSA25519()]
|
|
||||||
fingerprint << ["d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3", "dc:68:38:ce:fc:6f:2c:d6:6d:6b:34:eb:5c:f0:41:6a"]
|
|
||||||
signatureName = signatureFactory.getName()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should decline wrong key"() throws IOException {
|
|
||||||
given:
|
|
||||||
SSHClient sshClient = new SSHClient(new DefaultConfig())
|
|
||||||
sshClient.addHostKeyVerifier("d4:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3")
|
|
||||||
|
|
||||||
when:
|
|
||||||
sshClient.connect(sshd.containerIpAddress, sshd.firstMappedPort)
|
|
||||||
|
|
||||||
then:
|
|
||||||
thrown(TransportException.class)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should authenticate with key #key"() {
|
|
||||||
given:
|
|
||||||
SSHClient client = sshd.getConnectedClient()
|
|
||||||
|
|
||||||
when:
|
|
||||||
def keyProvider = passphrase != null ? client.loadKeys("src/itest/resources/keyfiles/$key", passphrase) : client.loadKeys("src/itest/resources/keyfiles/$key")
|
|
||||||
client.authPublickey(IntegrationTestUtil.USERNAME, keyProvider)
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.isAuthenticated()
|
|
||||||
|
|
||||||
where:
|
|
||||||
key | passphrase
|
|
||||||
// "id_ecdsa_nistp256" | null // TODO: Need to improve PKCS8 key support.
|
|
||||||
"id_ecdsa_opensshv1" | null
|
|
||||||
"id_ed25519_opensshv1" | null
|
|
||||||
"id_ed25519_opensshv1_aes256cbc.pem" | "foobar"
|
|
||||||
"id_ed25519_opensshv1_aes128cbc.pem" | "sshjtest"
|
|
||||||
"id_ed25519_opensshv1_protected" | "sshjtest"
|
|
||||||
"id_rsa" | null
|
|
||||||
"id_rsa_opensshv1" | null
|
|
||||||
"id_ecdsa_nistp384_opensshv1" | null
|
|
||||||
"id_ecdsa_nistp521_opensshv1" | null
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should not authenticate with wrong key"() {
|
|
||||||
given:
|
|
||||||
SSHClient client = sshd.getConnectedClient()
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_unknown_key")
|
|
||||||
|
|
||||||
then:
|
|
||||||
thrown(UserAuthException.class)
|
|
||||||
!client.isAuthenticated()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj
|
|
||||||
|
|
||||||
class IntegrationTestUtil {
|
|
||||||
static final String USERNAME = "sshj"
|
|
||||||
static final String KEYFILE = "src/itest/resources/keyfiles/id_rsa"
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj
|
|
||||||
|
|
||||||
import net.schmizz.sshj.SSHClient
|
|
||||||
import net.schmizz.sshj.common.IOUtils
|
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session
|
|
||||||
import spock.lang.Specification
|
|
||||||
|
|
||||||
import java.util.concurrent.*
|
|
||||||
|
|
||||||
import static org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable
|
|
||||||
|
|
||||||
class ManyChannelsSpec extends Specification {
|
|
||||||
|
|
||||||
def "should work with many channels without nonexistent channel error (GH issue #805)"() {
|
|
||||||
given:
|
|
||||||
SshdContainer sshd = new SshdContainer.Builder()
|
|
||||||
.withSshdConfig("""${SshdContainer.Builder.DEFAULT_SSHD_CONFIG}
|
|
||||||
MaxSessions 200
|
|
||||||
""".stripMargin())
|
|
||||||
.build()
|
|
||||||
sshd.start()
|
|
||||||
SSHClient client = sshd.getConnectedClient()
|
|
||||||
client.authPublickey("sshj", "src/test/resources/id_rsa")
|
|
||||||
|
|
||||||
when:
|
|
||||||
List<Future<Exception>> futures = []
|
|
||||||
ExecutorService executorService = Executors.newCachedThreadPool()
|
|
||||||
|
|
||||||
for (int i in 0..20) {
|
|
||||||
futures.add(executorService.submit((Callable<Exception>) {
|
|
||||||
return execute(client)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
executorService.shutdown()
|
|
||||||
executorService.awaitTermination(1, TimeUnit.DAYS)
|
|
||||||
|
|
||||||
then:
|
|
||||||
futures*.get().findAll { it != null }.empty
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
client.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static Exception execute(SSHClient sshClient) {
|
|
||||||
try {
|
|
||||||
for (def i in 0..100) {
|
|
||||||
withCloseable (sshClient.startSession()) {sshSession ->
|
|
||||||
Session.Command sshCommand = sshSession.exec("ls -la")
|
|
||||||
IOUtils.readFully(sshCommand.getInputStream()).toString()
|
|
||||||
sshCommand.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.key.KeyAlgorithms
|
|
||||||
import net.schmizz.sshj.Config
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that SSHJ is able to work with OpenSSH 8.8, which removed ssh-rsa signature from the default setup.
|
|
||||||
*/
|
|
||||||
class RsaShaKeySignatureTest extends Specification {
|
|
||||||
private static final Map<String, KeyAlgorithms.Factory> SSH_HOST_KEYS_AND_FACTORIES = [
|
|
||||||
'ssh_host_ecdsa_256_key': KeyAlgorithms.ECDSASHANistp256(),
|
|
||||||
'ssh_host_ecdsa_384_key': KeyAlgorithms.ECDSASHANistp384(),
|
|
||||||
'ssh_host_ecdsa_521_key': KeyAlgorithms.ECDSASHANistp521(),
|
|
||||||
'ssh_host_ed25519_384_key': KeyAlgorithms.EdDSA25519(),
|
|
||||||
'ssh_host_rsa_2048_key': KeyAlgorithms.RSASHA512(),
|
|
||||||
]
|
|
||||||
|
|
||||||
private static void dockerfileBuilder(DockerfileBuilder it, String hostKey, String pubkeyAcceptedAlgorithms) {
|
|
||||||
it.from("archlinux:base")
|
|
||||||
it.run('pacman -Sy --noconfirm core/openssh core/openssl' +
|
|
||||||
' && (' +
|
|
||||||
' V=$(echo $(/usr/sbin/sshd -h 2>&1) | grep -o \'OpenSSH_[0-9][0-9]*[.][0-9][0-9]*p[0-9]\');' +
|
|
||||||
' if [[ "$V" < OpenSSH_8.8p1 ]]; then' +
|
|
||||||
' echo $V is too old 1>&2;' +
|
|
||||||
' exit 1;' +
|
|
||||||
' fi' +
|
|
||||||
')' +
|
|
||||||
' && set -o pipefail ' +
|
|
||||||
' && useradd --create-home sshj' +
|
|
||||||
' && echo \"sshj:ultrapassword\" | chpasswd')
|
|
||||||
it.add("authorized_keys", "/home/sshj/.ssh/")
|
|
||||||
it.add(hostKey, '/etc/ssh/')
|
|
||||||
it.run('chmod go-rwx /etc/ssh/ssh_host_*' +
|
|
||||||
' && chown -R sshj /home/sshj/.ssh' +
|
|
||||||
' && chmod -R go-rwx /home/sshj/.ssh')
|
|
||||||
it.expose(22)
|
|
||||||
|
|
||||||
def cmd = [
|
|
||||||
'/usr/sbin/sshd',
|
|
||||||
'-D',
|
|
||||||
'-e',
|
|
||||||
'-f', '/dev/null',
|
|
||||||
'-o', "HostKey=/etc/ssh/$hostKey",
|
|
||||||
]
|
|
||||||
if (pubkeyAcceptedAlgorithms != null) {
|
|
||||||
cmd += ['-o', "PubkeyAcceptedAlgorithms=$pubkeyAcceptedAlgorithms"]
|
|
||||||
}
|
|
||||||
it.cmd(cmd as String[])
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SshdContainer makeSshdContainer(String hostKey, String pubkeyAcceptedAlgorithms) {
|
|
||||||
return new SshdContainer(new SshdContainer.DebugLoggingImageFromDockerfile()
|
|
||||||
.withFileFromPath("authorized_keys", Paths.get("src/itest/docker-image/authorized_keys"))
|
|
||||||
.withFileFromPath(hostKey, Paths.get("src/itest/docker-image/test-container/host_keys/$hostKey"))
|
|
||||||
.withDockerfileFromBuilder {
|
|
||||||
dockerfileBuilder(it, hostKey, pubkeyAcceptedAlgorithms)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "connect to a server with host key #hostKey that does not support ssh-rsa"() {
|
|
||||||
given:
|
|
||||||
SshdContainer sshd = makeSshdContainer(hostKey, "rsa-sha2-512,rsa-sha2-256,ssh-ed25519")
|
|
||||||
sshd.start()
|
|
||||||
|
|
||||||
and:
|
|
||||||
Config config = new DefaultConfig()
|
|
||||||
config.keyAlgorithms = [
|
|
||||||
KeyAlgorithms.RSASHA512(),
|
|
||||||
KeyAlgorithms.RSASHA256(),
|
|
||||||
SSH_HOST_KEYS_AND_FACTORIES[hostKey],
|
|
||||||
]
|
|
||||||
|
|
||||||
when:
|
|
||||||
def sshClient = sshd.getConnectedClient(config)
|
|
||||||
sshClient.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1")
|
|
||||||
|
|
||||||
then:
|
|
||||||
sshClient.isAuthenticated()
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
sshClient?.disconnect()
|
|
||||||
sshd.stop()
|
|
||||||
|
|
||||||
where:
|
|
||||||
hostKey << SSH_HOST_KEYS_AND_FACTORIES.keySet()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "connect to a default server with host key #hostKey using a default config"() {
|
|
||||||
given:
|
|
||||||
SshdContainer sshd = makeSshdContainer(hostKey, null)
|
|
||||||
sshd.start()
|
|
||||||
|
|
||||||
when:
|
|
||||||
def sshClient = sshd.getConnectedClient()
|
|
||||||
sshClient.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1")
|
|
||||||
|
|
||||||
then:
|
|
||||||
sshClient.isAuthenticated()
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
sshClient?.disconnect()
|
|
||||||
sshd.stop()
|
|
||||||
|
|
||||||
where:
|
|
||||||
hostKey << SSH_HOST_KEYS_AND_FACTORIES.keySet()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "connect to a server with host key #hostKey that supports only ssh-rsa"() {
|
|
||||||
given:
|
|
||||||
SshdContainer sshd = makeSshdContainer(hostKey, "ssh-rsa,ssh-ed25519")
|
|
||||||
sshd.start()
|
|
||||||
|
|
||||||
and:
|
|
||||||
Config config = new DefaultConfig()
|
|
||||||
config.keyAlgorithms = [
|
|
||||||
KeyAlgorithms.SSHRSA(),
|
|
||||||
SSH_HOST_KEYS_AND_FACTORIES[hostKey],
|
|
||||||
]
|
|
||||||
|
|
||||||
when:
|
|
||||||
def sshClient = sshd.getConnectedClient(config)
|
|
||||||
sshClient.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1")
|
|
||||||
|
|
||||||
then:
|
|
||||||
sshClient.isAuthenticated()
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
sshClient.disconnect()
|
|
||||||
sshd.stop()
|
|
||||||
|
|
||||||
where:
|
|
||||||
hostKey << SSH_HOST_KEYS_AND_FACTORIES.keySet()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj;
|
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
|
||||||
import ch.qos.logback.classic.Logger;
|
|
||||||
import net.schmizz.sshj.Config;
|
|
||||||
import net.schmizz.sshj.DefaultConfig;
|
|
||||||
import net.schmizz.sshj.SSHClient;
|
|
||||||
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.testcontainers.containers.GenericContainer;
|
|
||||||
import org.testcontainers.images.builder.ImageFromDockerfile;
|
|
||||||
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder;
|
|
||||||
import org.testcontainers.utility.DockerLoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A JUnit4 rule for launching a generic SSH server container.
|
|
||||||
*/
|
|
||||||
public class SshdContainer extends GenericContainer<SshdContainer> {
|
|
||||||
/**
|
|
||||||
* A workaround for strange logger names of testcontainers. They contain no dots, but contain slashes,
|
|
||||||
* square brackets, and even emoji. It's uneasy to set the logging level via the XML file of logback, the
|
|
||||||
* result would be less readable than the code below.
|
|
||||||
*/
|
|
||||||
public static class DebugLoggingImageFromDockerfile extends ImageFromDockerfile {
|
|
||||||
public DebugLoggingImageFromDockerfile() {
|
|
||||||
super();
|
|
||||||
Logger logger = (Logger) LoggerFactory.getILoggerFactory()
|
|
||||||
.getLogger(DockerLoggerFactory.getLogger(getDockerImageName()).getName());
|
|
||||||
logger.setLevel(Level.DEBUG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
|
||||||
public static final String DEFAULT_SSHD_CONFIG = "" +
|
|
||||||
"PermitRootLogin yes\n" +
|
|
||||||
"AuthorizedKeysFile .ssh/authorized_keys\n" +
|
|
||||||
"Subsystem sftp /usr/lib/ssh/sftp-server\n" +
|
|
||||||
"KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1\n" +
|
|
||||||
"macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-ripemd160-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com\n" +
|
|
||||||
"TrustedUserCAKeys /etc/ssh/trusted_ca_keys\n" +
|
|
||||||
"Ciphers 3des-cbc,blowfish-cbc,aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_rsa_key\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_dsa_key\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_ecdsa_key\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_ed25519_key\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_ecdsa_256_key\n" +
|
|
||||||
"HostCertificate /etc/ssh/ssh_host_ecdsa_256_key-cert.pub\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_ecdsa_384_key\n" +
|
|
||||||
"HostCertificate /etc/ssh/ssh_host_ecdsa_384_key-cert.pub\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_ecdsa_521_key\n" +
|
|
||||||
"HostCertificate /etc/ssh/ssh_host_ecdsa_521_key-cert.pub\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_ed25519_384_key\n" +
|
|
||||||
"HostCertificate /etc/ssh/ssh_host_ed25519_384_key-cert.pub\n" +
|
|
||||||
"HostKey /etc/ssh/ssh_host_rsa_2048_key\n" +
|
|
||||||
"HostCertificate /etc/ssh/ssh_host_rsa_2048_key-cert.pub\n" +
|
|
||||||
"LogLevel DEBUG2\n";
|
|
||||||
|
|
||||||
public static void defaultDockerfileBuilder(@NotNull DockerfileBuilder builder) {
|
|
||||||
builder.from("sickp/alpine-sshd:7.5-r2");
|
|
||||||
|
|
||||||
builder.add("authorized_keys", "/home/sshj/.ssh/authorized_keys");
|
|
||||||
|
|
||||||
builder.add("test-container/ssh_host_ecdsa_key", "/etc/ssh/ssh_host_ecdsa_key");
|
|
||||||
builder.add("test-container/ssh_host_ecdsa_key.pub", "/etc/ssh/ssh_host_ecdsa_key.pub");
|
|
||||||
builder.add("test-container/ssh_host_ed25519_key", "/etc/ssh/ssh_host_ed25519_key");
|
|
||||||
builder.add("test-container/ssh_host_ed25519_key.pub", "/etc/ssh/ssh_host_ed25519_key.pub");
|
|
||||||
builder.copy("test-container/trusted_ca_keys", "/etc/ssh/trusted_ca_keys");
|
|
||||||
builder.copy("test-container/host_keys/*", "/etc/ssh/");
|
|
||||||
|
|
||||||
builder.run("apk add --no-cache tini"
|
|
||||||
+ " && echo \"root:smile\" | chpasswd"
|
|
||||||
+ " && adduser -D -s /bin/ash sshj"
|
|
||||||
+ " && passwd -u sshj"
|
|
||||||
+ " && echo \"sshj:ultrapassword\" | chpasswd"
|
|
||||||
+ " && chmod 600 /home/sshj/.ssh/authorized_keys"
|
|
||||||
+ " && chmod 600 /etc/ssh/ssh_host_*_key"
|
|
||||||
+ " && chmod 644 /etc/ssh/*.pub"
|
|
||||||
+ " && chown -R sshj:sshj /home/sshj");
|
|
||||||
builder.entryPoint("/sbin/tini", "/entrypoint.sh", "-o", "LogLevel=DEBUG2");
|
|
||||||
|
|
||||||
builder.add("sshd_config", "/etc/ssh/sshd_config");
|
|
||||||
}
|
|
||||||
|
|
||||||
private @NotNull String sshdConfig = DEFAULT_SSHD_CONFIG;
|
|
||||||
|
|
||||||
public @NotNull Builder withSshdConfig(@NotNull String sshdConfig) {
|
|
||||||
this.sshdConfig = sshdConfig;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SshdContainer build() {
|
|
||||||
return new SshdContainer(buildInner());
|
|
||||||
}
|
|
||||||
|
|
||||||
private @NotNull Future<String> buildInner() {
|
|
||||||
return new DebugLoggingImageFromDockerfile()
|
|
||||||
.withDockerfileFromBuilder(Builder::defaultDockerfileBuilder)
|
|
||||||
.withFileFromPath(".", Paths.get("src/itest/docker-image"))
|
|
||||||
.withFileFromString("sshd_config", sshdConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused") // Used dynamically by Spock
|
|
||||||
public SshdContainer() {
|
|
||||||
this(new SshdContainer.Builder().buildInner());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SshdContainer(@NotNull Future<String> future) {
|
|
||||||
super(future);
|
|
||||||
withExposedPorts(22);
|
|
||||||
setWaitStrategy(new SshServerWaitStrategy());
|
|
||||||
withLogConsumer(outputFrame -> {
|
|
||||||
switch (outputFrame.getType()) {
|
|
||||||
case STDOUT:
|
|
||||||
logger().info("sshd stdout: {}", outputFrame.getUtf8String().stripTrailing());
|
|
||||||
break;
|
|
||||||
case STDERR:
|
|
||||||
logger().info("sshd stderr: {}", outputFrame.getUtf8String().stripTrailing());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public SSHClient getConnectedClient(Config config) throws IOException {
|
|
||||||
SSHClient sshClient = new SSHClient(config);
|
|
||||||
sshClient.addHostKeyVerifier(new PromiscuousVerifier());
|
|
||||||
sshClient.connect("127.0.0.1", getFirstMappedPort());
|
|
||||||
|
|
||||||
return sshClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SSHClient getConnectedClient() throws IOException {
|
|
||||||
return getConnectedClient(new DefaultConfig());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.sftp
|
|
||||||
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import net.schmizz.sshj.SSHClient
|
|
||||||
import net.schmizz.sshj.sftp.OpenMode
|
|
||||||
import net.schmizz.sshj.sftp.RemoteFile
|
|
||||||
import net.schmizz.sshj.sftp.SFTPClient
|
|
||||||
import org.junit.ClassRule
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Specification
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
import static org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable
|
|
||||||
|
|
||||||
@Testcontainers
|
|
||||||
class FileWriteSpec extends Specification {
|
|
||||||
@Shared
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
def "should append to file (GH issue #390)"() {
|
|
||||||
given:
|
|
||||||
SSHClient client = sshd.getConnectedClient()
|
|
||||||
client.authPublickey("sshj", "src/test/resources/id_rsa")
|
|
||||||
SFTPClient sftp = client.newSFTPClient()
|
|
||||||
def file = "/home/sshj/test.txt"
|
|
||||||
def initialText = "This is the initial text.\n".getBytes(StandardCharsets.UTF_16)
|
|
||||||
def appendText = "And here's the appended text.\n".getBytes(StandardCharsets.UTF_16)
|
|
||||||
|
|
||||||
when:
|
|
||||||
withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT))) { RemoteFile initial ->
|
|
||||||
initial.write(0, initialText, 0, initialText.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read ->
|
|
||||||
def bytes = new byte[initialText.length]
|
|
||||||
read.read(0, bytes, 0, bytes.length)
|
|
||||||
bytes == initialText
|
|
||||||
}
|
|
||||||
|
|
||||||
when:
|
|
||||||
withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.APPEND))) { RemoteFile append ->
|
|
||||||
append.write(0, appendText, 0, appendText.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read ->
|
|
||||||
def bytes = new byte[initialText.length + appendText.length]
|
|
||||||
read.read(0, bytes, 0, bytes.length)
|
|
||||||
Arrays.copyOfRange(bytes, 0, initialText.length) == initialText
|
|
||||||
Arrays.copyOfRange(bytes, initialText.length, initialText.length + appendText.length) == appendText
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
sftp.close()
|
|
||||||
client.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.signature
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import net.schmizz.sshj.SSHClient
|
|
||||||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
import java.nio.file.Files
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a brief test for verifying connection to a server using keys with certificates.
|
|
||||||
*
|
|
||||||
* Also, take a look at the unit test {@link net.schmizz.sshj.transport.verification.KeyWithCertificateUnitSpec}.
|
|
||||||
*/
|
|
||||||
class HostKeyWithCertificateSpec extends Specification {
|
|
||||||
@Unroll
|
|
||||||
def "accepting a signed host public key #hostKey"() {
|
|
||||||
given:
|
|
||||||
SshdContainer sshd = new SshdContainer.Builder()
|
|
||||||
.withSshdConfig("""
|
|
||||||
PasswordAuthentication yes
|
|
||||||
HostKey /etc/ssh/$hostKey
|
|
||||||
HostCertificate /etc/ssh/${hostKey}-cert.pub
|
|
||||||
""".stripMargin())
|
|
||||||
.build()
|
|
||||||
sshd.start()
|
|
||||||
|
|
||||||
and:
|
|
||||||
File knownHosts = Files.createTempFile("known_hosts", "").toFile()
|
|
||||||
knownHosts.deleteOnExit()
|
|
||||||
|
|
||||||
and:
|
|
||||||
File caPubKey = new File("src/itest/resources/keyfiles/certificates/CA_rsa.pem.pub")
|
|
||||||
def address = "127.0.0.1"
|
|
||||||
String knownHostsFileContents = "" +
|
|
||||||
"@cert-authority ${ address} ${caPubKey.text}" +
|
|
||||||
"\n@cert-authority [${address}]:${sshd.firstMappedPort} ${caPubKey.text}"
|
|
||||||
knownHosts.write(knownHostsFileContents)
|
|
||||||
|
|
||||||
and:
|
|
||||||
SSHClient sshClient = new SSHClient(new DefaultConfig())
|
|
||||||
sshClient.addHostKeyVerifier(new OpenSSHKnownHosts(knownHosts))
|
|
||||||
sshClient.connect(address, sshd.firstMappedPort)
|
|
||||||
|
|
||||||
when:
|
|
||||||
sshClient.authPassword("sshj", "ultrapassword")
|
|
||||||
|
|
||||||
then:
|
|
||||||
sshClient.authenticated
|
|
||||||
|
|
||||||
and:
|
|
||||||
knownHosts.getText() == knownHostsFileContents
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
sshd.stop()
|
|
||||||
|
|
||||||
where:
|
|
||||||
hostKey << [
|
|
||||||
"ssh_host_ecdsa_256_key",
|
|
||||||
"ssh_host_ecdsa_384_key",
|
|
||||||
"ssh_host_ecdsa_521_key",
|
|
||||||
"ssh_host_ed25519_384_key",
|
|
||||||
"ssh_host_rsa_2048_key",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.signature
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import net.schmizz.sshj.SSHClient
|
|
||||||
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a brief test for verifying connection to a server using keys with certificates.
|
|
||||||
*
|
|
||||||
* Also, take a look at the unit test {@link net.schmizz.sshj.transport.verification.KeyWithCertificateUnitSpec}.
|
|
||||||
*/
|
|
||||||
@Testcontainers
|
|
||||||
class PublicKeyAuthWithCertificateSpec extends Specification {
|
|
||||||
@Shared
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "authorising with a signed public key #keyName"() {
|
|
||||||
given:
|
|
||||||
SSHClient client = new SSHClient(new DefaultConfig())
|
|
||||||
client.addHostKeyVerifier(new PromiscuousVerifier())
|
|
||||||
client.connect("127.0.0.1", sshd.firstMappedPort)
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey("sshj", "src/itest/resources/keyfiles/certificates/$keyName")
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.authenticated
|
|
||||||
|
|
||||||
where:
|
|
||||||
keyName << [
|
|
||||||
"id_ecdsa_256_pem_signed_by_ecdsa",
|
|
||||||
"id_ecdsa_256_rfc4716_signed_by_ecdsa",
|
|
||||||
"id_ecdsa_256_pem_signed_by_ed25519",
|
|
||||||
"id_ecdsa_256_rfc4716_signed_by_ed25519",
|
|
||||||
"id_ecdsa_256_pem_signed_by_rsa",
|
|
||||||
"id_ecdsa_256_rfc4716_signed_by_rsa",
|
|
||||||
"id_ecdsa_384_pem_signed_by_ecdsa",
|
|
||||||
"id_ecdsa_384_rfc4716_signed_by_ecdsa",
|
|
||||||
"id_ecdsa_384_pem_signed_by_ed25519",
|
|
||||||
"id_ecdsa_384_rfc4716_signed_by_ed25519",
|
|
||||||
"id_ecdsa_384_pem_signed_by_rsa",
|
|
||||||
"id_ecdsa_384_rfc4716_signed_by_rsa",
|
|
||||||
"id_ecdsa_521_pem_signed_by_ecdsa",
|
|
||||||
"id_ecdsa_521_rfc4716_signed_by_ecdsa",
|
|
||||||
"id_ecdsa_521_pem_signed_by_ed25519",
|
|
||||||
"id_ecdsa_521_rfc4716_signed_by_ed25519",
|
|
||||||
"id_ecdsa_521_pem_signed_by_rsa",
|
|
||||||
"id_ecdsa_521_rfc4716_signed_by_rsa",
|
|
||||||
"id_rsa_2048_pem_signed_by_ecdsa",
|
|
||||||
"id_rsa_2048_rfc4716_signed_by_ecdsa",
|
|
||||||
"id_rsa_2048_pem_signed_by_ed25519",
|
|
||||||
"id_rsa_2048_rfc4716_signed_by_ed25519",
|
|
||||||
"id_rsa_2048_pem_signed_by_rsa",
|
|
||||||
"id_rsa_2048_rfc4716_signed_by_rsa",
|
|
||||||
"id_ed25519_384_rfc4716_signed_by_ecdsa",
|
|
||||||
"id_ed25519_384_rfc4716_signed_by_ed25519",
|
|
||||||
"id_ed25519_384_rfc4716_signed_by_rsa",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.signature
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.IntegrationTestUtil
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
@Testcontainers
|
|
||||||
class RsaSignatureClientKeySpec extends Specification {
|
|
||||||
@Shared
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should correctly connect using publickey auth with RSA key with signature"() {
|
|
||||||
given:
|
|
||||||
def client = sshd.getConnectedClient()
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey(IntegrationTestUtil.USERNAME, "src/itest/resources/keyfiles/id_rsa2")
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.authenticated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.signature
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.IntegrationTestUtil
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import com.hierynomus.sshj.key.KeyAlgorithms
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
@Testcontainers
|
|
||||||
class SignatureSpec extends Specification {
|
|
||||||
@Shared
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should correctly connect with #sig Signature"() {
|
|
||||||
given:
|
|
||||||
def cfg = new DefaultConfig()
|
|
||||||
cfg.setKeyAlgorithms(Collections.singletonList(sigFactory))
|
|
||||||
def client = sshd.getConnectedClient(cfg)
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey(IntegrationTestUtil.USERNAME, IntegrationTestUtil.KEYFILE)
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.authenticated
|
|
||||||
|
|
||||||
where:
|
|
||||||
sigFactory << [KeyAlgorithms.SSHRSA(), KeyAlgorithms.RSASHA256(), KeyAlgorithms.RSASHA512()]
|
|
||||||
sig = sigFactory.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.transport.cipher
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.IntegrationTestUtil
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
@Testcontainers
|
|
||||||
class CipherSpec extends Specification {
|
|
||||||
@Shared
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should correctly connect with #cipher Cipher"() {
|
|
||||||
given:
|
|
||||||
def cfg = new DefaultConfig()
|
|
||||||
cfg.setCipherFactories(cipherFactory)
|
|
||||||
def client = sshd.getConnectedClient(cfg)
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey(IntegrationTestUtil.USERNAME, IntegrationTestUtil.KEYFILE)
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.authenticated
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
client.disconnect()
|
|
||||||
|
|
||||||
where:
|
|
||||||
cipherFactory << [BlockCiphers.TripleDESCBC(),
|
|
||||||
BlockCiphers.BlowfishCBC(),
|
|
||||||
BlockCiphers.AES128CBC(),
|
|
||||||
BlockCiphers.AES128CTR(),
|
|
||||||
BlockCiphers.AES192CBC(),
|
|
||||||
BlockCiphers.AES192CTR(),
|
|
||||||
BlockCiphers.AES256CBC(),
|
|
||||||
BlockCiphers.AES256CTR(),
|
|
||||||
GcmCiphers.AES128GCM(),
|
|
||||||
GcmCiphers.AES256GCM(),
|
|
||||||
ChachaPolyCiphers.CHACHA_POLY_OPENSSH()]
|
|
||||||
cipher = cipherFactory.name
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.transport.kex
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.IntegrationTestUtil
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import net.schmizz.sshj.transport.kex.Curve25519SHA256
|
|
||||||
import net.schmizz.sshj.transport.kex.DHGexSHA1
|
|
||||||
import net.schmizz.sshj.transport.kex.DHGexSHA256
|
|
||||||
import net.schmizz.sshj.transport.kex.ECDHNistP
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
@Testcontainers
|
|
||||||
class KexSpec extends Specification {
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should correctly connect with #kex Key Exchange"() {
|
|
||||||
given:
|
|
||||||
def cfg = new DefaultConfig()
|
|
||||||
cfg.setKeyExchangeFactories(kexFactory)
|
|
||||||
def client = sshd.getConnectedClient(cfg)
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey(IntegrationTestUtil.USERNAME, IntegrationTestUtil.KEYFILE)
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.authenticated
|
|
||||||
|
|
||||||
where:
|
|
||||||
kexFactory << [DHGroups.Group1SHA1(),
|
|
||||||
DHGroups.Group14SHA1(),
|
|
||||||
DHGroups.Group14SHA256(),
|
|
||||||
DHGroups.Group16SHA512(),
|
|
||||||
DHGroups.Group18SHA512(),
|
|
||||||
new DHGexSHA1.Factory(),
|
|
||||||
new DHGexSHA256.Factory(),
|
|
||||||
new Curve25519SHA256.Factory(),
|
|
||||||
new Curve25519SHA256.FactoryLibSsh(),
|
|
||||||
new ECDHNistP.Factory256(),
|
|
||||||
new ECDHNistP.Factory384(),
|
|
||||||
new ECDHNistP.Factory521()]
|
|
||||||
kex = kexFactory.name
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C)2009 - SSHJ Contributors
|
|
||||||
*
|
|
||||||
* 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.hierynomus.sshj.transport.mac
|
|
||||||
|
|
||||||
import com.hierynomus.sshj.IntegrationTestUtil
|
|
||||||
import com.hierynomus.sshj.SshdContainer
|
|
||||||
import net.schmizz.sshj.DefaultConfig
|
|
||||||
import org.testcontainers.junit.jupiter.Container
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Specification
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
@Testcontainers
|
|
||||||
class MacSpec extends Specification {
|
|
||||||
@Shared
|
|
||||||
@Container
|
|
||||||
static SshdContainer sshd = new SshdContainer()
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should correctly connect with #mac MAC"() {
|
|
||||||
given:
|
|
||||||
def cfg = new DefaultConfig()
|
|
||||||
cfg.setMACFactories(macFactory)
|
|
||||||
def client = sshd.getConnectedClient(cfg)
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey(IntegrationTestUtil.USERNAME, IntegrationTestUtil.KEYFILE)
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.authenticated
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
client.disconnect()
|
|
||||||
|
|
||||||
where:
|
|
||||||
macFactory << [Macs.HMACRIPEMD160(), Macs.HMACRIPEMD160OpenSsh(), Macs.HMACSHA2256(), Macs.HMACSHA2512()]
|
|
||||||
mac = macFactory.name
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should correctly connect with Encrypt-Then-Mac #mac MAC"() {
|
|
||||||
given:
|
|
||||||
def cfg = new DefaultConfig()
|
|
||||||
cfg.setMACFactories(macFactory)
|
|
||||||
def client = sshd.getConnectedClient(cfg)
|
|
||||||
|
|
||||||
when:
|
|
||||||
client.authPublickey(IntegrationTestUtil.USERNAME, IntegrationTestUtil.KEYFILE)
|
|
||||||
|
|
||||||
then:
|
|
||||||
client.authenticated
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
client.disconnect()
|
|
||||||
|
|
||||||
where:
|
|
||||||
macFactory << [Macs.HMACRIPEMD160Etm(), Macs.HMACSHA2256Etm(), Macs.HMACSHA2512Etm()]
|
|
||||||
mac = macFactory.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
72
src/itest/java/com/hierynomus/sshj/HostKeyVerifierTest.java
Normal file
72
src/itest/java/com/hierynomus/sshj/HostKeyVerifierTest.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithms;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class HostKeyVerifierTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer();
|
||||||
|
|
||||||
|
public static Stream<Arguments> signatureAlgos() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(KeyAlgorithms.ECDSASHANistp256(), "d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3"),
|
||||||
|
Arguments.of(KeyAlgorithms.EdDSA25519(), "dc:68:38:ce:fc:6f:2c:d6:6d:6b:34:eb:5c:f0:41:6a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "Should connect with signature verified for Key Algorithm {0}")
|
||||||
|
@MethodSource("signatureAlgos")
|
||||||
|
public void shouldConnectWithSignatureVerified(KeyAlgorithms.Factory alg, String fingerprint) throws Throwable {
|
||||||
|
Config config = new DefaultConfig();
|
||||||
|
config.setKeyAlgorithms(List.of(alg));
|
||||||
|
|
||||||
|
try (SSHClient client = new SSHClient(config)) {
|
||||||
|
client.addHostKeyVerifier(fingerprint);
|
||||||
|
client.connect(sshd.getHost(), sshd.getFirstMappedPort());
|
||||||
|
|
||||||
|
assertTrue(client.isConnected());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDeclineWrongKey() throws Throwable {
|
||||||
|
try (SSHClient client = new SSHClient()) {
|
||||||
|
assertThrows(TransportException.class, () -> {
|
||||||
|
client.addHostKeyVerifier("d4:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3");
|
||||||
|
client.connect(sshd.getHost(), sshd.getFirstMappedPort());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/itest/java/com/hierynomus/sshj/ManyChannelsTest.java
Normal file
74
src/itest/java/com/hierynomus/sshj/ManyChannelsTest.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer.SshdConfigBuilder;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class ManyChannelsTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer(SshdContainer.Builder.defaultBuilder()
|
||||||
|
.withSshdConfig(SshdConfigBuilder.defaultBuilder().with("MaxSessions", "200")).withAllKeys());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldWorkWithManyChannelsWithoutNoExistentChannelError_GH805() throws Throwable {
|
||||||
|
try (SSHClient client = sshd.getConnectedClient()) {
|
||||||
|
client.authPublickey("sshj", "src/test/resources/id_rsa");
|
||||||
|
|
||||||
|
List<Future<Exception>> futures = new ArrayList<>();
|
||||||
|
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
futures.add(executorService.submit(() -> {
|
||||||
|
try {
|
||||||
|
for (int j = 0; j < 10; j++) {
|
||||||
|
try (Session sshSession = client.startSession()) {
|
||||||
|
try (Session.Command sshCommand = sshSession.exec("ls -la")) {
|
||||||
|
IOUtils.readFully(sshCommand.getInputStream()).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
executorService.shutdown();
|
||||||
|
executorService.awaitTermination(1, TimeUnit.DAYS);
|
||||||
|
|
||||||
|
assertThat(futures).allSatisfy(future -> assertThat(future.get()).isNull());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/itest/java/com/hierynomus/sshj/PublicKeyAuthTest.java
Normal file
86
src/itest/java/com/hierynomus/sshj/PublicKeyAuthTest.java
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer.SshdConfigBuilder;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.userauth.UserAuthException;
|
||||||
|
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class PublicKeyAuthTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer(SshdContainer.Builder.defaultBuilder().withSshdConfig(
|
||||||
|
SshdConfigBuilder.defaultBuilder().with("PubkeyAcceptedAlgorithms", "+ssh-rsa-cert-v01@openssh.com"))
|
||||||
|
.withAllKeys());
|
||||||
|
|
||||||
|
public static Stream<Arguments> keys() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of("id_rsa2", null),
|
||||||
|
// "id_ecdsa_nistp256" | null // TODO: Need to improve PKCS8 key support.
|
||||||
|
Arguments.of("id_ecdsa_opensshv1", null),
|
||||||
|
Arguments.of("id_ed25519_opensshv1", null),
|
||||||
|
Arguments.of("id_ed25519_opensshv1_aes256cbc.pem", "foobar"),
|
||||||
|
Arguments.of("id_ed25519_opensshv1_aes128cbc.pem", "sshjtest"),
|
||||||
|
Arguments.of("id_ed25519_opensshv1_protected", "sshjtest"),
|
||||||
|
Arguments.of("id_rsa", null),
|
||||||
|
Arguments.of("id_rsa_opensshv1", null),
|
||||||
|
Arguments.of("id_ecdsa_nistp384_opensshv1", null),
|
||||||
|
Arguments.of("id_ecdsa_nistp521_opensshv1", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "should authenticate with signed public key {0}")
|
||||||
|
@MethodSource("keys")
|
||||||
|
public void shouldAuthenticateWithSignedRsaKey(String key, String passphrase) throws Throwable {
|
||||||
|
try (SSHClient client = sshd.getConnectedClient()) {
|
||||||
|
KeyProvider p = null;
|
||||||
|
if (passphrase != null) {
|
||||||
|
p = client.loadKeys("src/itest/resources/keyfiles/" + key, passphrase);
|
||||||
|
} else {
|
||||||
|
p = client.loadKeys("src/itest/resources/keyfiles/" + key);
|
||||||
|
}
|
||||||
|
client.authPublickey("sshj", p);
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotAuthenticateWithUnknownKey() throws Throwable {
|
||||||
|
try (SSHClient client = sshd.getConnectedClient()) {
|
||||||
|
assertThrows(UserAuthException.class, () -> {
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_unknown_key");
|
||||||
|
});
|
||||||
|
|
||||||
|
assertFalse(client.isAuthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
100
src/itest/java/com/hierynomus/sshj/RsaShaKeySignatureTest.java
Normal file
100
src/itest/java/com/hierynomus/sshj/RsaShaKeySignatureTest.java
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer.SshdConfigBuilder;
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithms;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static com.hierynomus.sshj.SshdContainer.withSshdContainer;
|
||||||
|
|
||||||
|
public class RsaShaKeySignatureTest {
|
||||||
|
|
||||||
|
public static Stream<Arguments> hostKeysAndAlgorithms() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of("ssh_host_ecdsa_256_key", KeyAlgorithms.ECDSASHANistp256()),
|
||||||
|
Arguments.of("ssh_host_ecdsa_384_key", KeyAlgorithms.ECDSASHANistp384()),
|
||||||
|
Arguments.of("ssh_host_ecdsa_521_key", KeyAlgorithms.ECDSASHANistp521()),
|
||||||
|
Arguments.of("ssh_host_ed25519_384_key", KeyAlgorithms.EdDSA25519()),
|
||||||
|
Arguments.of("ssh_host_rsa_2048_key", KeyAlgorithms.RSASHA512()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "Should connect to server that does not support ssh-rsa with host key {1}")
|
||||||
|
@MethodSource("hostKeysAndAlgorithms")
|
||||||
|
public void shouldConnectToServerThatDoesNotSupportSshRsaWithHostKey(String key, KeyAlgorithms.Factory algorithm)
|
||||||
|
throws Throwable {
|
||||||
|
SshdConfigBuilder configBuilder = SshdConfigBuilder
|
||||||
|
.defaultBuilder()
|
||||||
|
.with("PubkeyAcceptedAlgorithms", "rsa-sha2-512,rsa-sha2-256,ssh-ed25519");
|
||||||
|
withSshdContainer(SshdContainer.Builder.defaultBuilder()
|
||||||
|
.withSshdConfig(configBuilder).addHostKey("test-container/host_keys/" + key), sshd -> {
|
||||||
|
Config c = new DefaultConfig();
|
||||||
|
c.setKeyAlgorithms(List.of(KeyAlgorithms.RSASHA512(), KeyAlgorithms.RSASHA256(), algorithm));
|
||||||
|
|
||||||
|
SSHClient client = sshd.getConnectedClient(c);
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1");
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
|
||||||
|
client.disconnect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "Should connect to a default server with host key {1} with a default config")
|
||||||
|
@MethodSource("hostKeysAndAlgorithms")
|
||||||
|
public void shouldConnectToDefaultServer(String key, KeyAlgorithms.Factory algorithm) throws Throwable {
|
||||||
|
withSshdContainer(SshdContainer.Builder.defaultBuilder().addHostKey("test-container/host_keys/" + key),
|
||||||
|
sshd -> {
|
||||||
|
SSHClient client = sshd.getConnectedClient();
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1");
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
|
||||||
|
client.disconnect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "Should connect to a server that only supports ssh-rsa with host key {1}")
|
||||||
|
@MethodSource("hostKeysAndAlgorithms")
|
||||||
|
public void shouldConnectToSshRsaOnlyServer(String key, KeyAlgorithms.Factory algorithm) throws Throwable {
|
||||||
|
SshdConfigBuilder configBuilder = SshdConfigBuilder
|
||||||
|
.defaultBuilder()
|
||||||
|
.with("PubkeyAcceptedAlgorithms", "ssh-rsa,ssh-ed25519");
|
||||||
|
|
||||||
|
withSshdContainer(SshdContainer.Builder.defaultBuilder()
|
||||||
|
.withSshdConfig(configBuilder).addHostKey("test-container/host_keys/" + key), sshd -> {
|
||||||
|
Config c = new DefaultConfig();
|
||||||
|
c.setKeyAlgorithms(List.of(KeyAlgorithms.SSHRSA(), algorithm));
|
||||||
|
SSHClient client = sshd.getConnectedClient(c);
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1");
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
client.disconnect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
242
src/itest/java/com/hierynomus/sshj/SshdContainer.java
Normal file
242
src/itest/java/com/hierynomus/sshj/SshdContainer.java
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.jupiter.api.function.ThrowingConsumer;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.images.builder.ImageFromDockerfile;
|
||||||
|
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder;
|
||||||
|
import org.testcontainers.utility.DockerLoggerFactory;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JUnit4 rule for launching a generic SSH server container.
|
||||||
|
*/
|
||||||
|
public class SshdContainer extends GenericContainer<SshdContainer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A workaround for strange logger names of testcontainers. They contain no
|
||||||
|
* dots, but contain slashes,
|
||||||
|
* square brackets, and even emoji. It's uneasy to set the logging level via the
|
||||||
|
* XML file of logback, the
|
||||||
|
* result would be less readable than the code below.
|
||||||
|
*/
|
||||||
|
public static class DebugLoggingImageFromDockerfile extends ImageFromDockerfile {
|
||||||
|
public DebugLoggingImageFromDockerfile() {
|
||||||
|
super();
|
||||||
|
Logger logger = (Logger) LoggerFactory.getILoggerFactory()
|
||||||
|
.getLogger(DockerLoggerFactory.getLogger(getDockerImageName()).getName());
|
||||||
|
logger.setLevel(Level.DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SshdConfigBuilder {
|
||||||
|
public static final String DEFAULT_SSHD_CONFIG = "" +
|
||||||
|
"PermitRootLogin yes\n" +
|
||||||
|
"AuthorizedKeysFile .ssh/authorized_keys\n" +
|
||||||
|
"Subsystem sftp /usr/lib/ssh/sftp-server\n" +
|
||||||
|
"KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1\n"
|
||||||
|
+
|
||||||
|
"macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512\n"
|
||||||
|
+
|
||||||
|
"TrustedUserCAKeys /etc/ssh/trusted_ca_keys\n" +
|
||||||
|
"Ciphers 3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com\n"
|
||||||
|
+
|
||||||
|
"LogLevel DEBUG2\n";
|
||||||
|
private String sshdConfig;
|
||||||
|
|
||||||
|
public SshdConfigBuilder(@NotNull String sshdConfig) {
|
||||||
|
this.sshdConfig = sshdConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SshdConfigBuilder defaultBuilder() {
|
||||||
|
return new SshdConfigBuilder(DEFAULT_SSHD_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SshdConfigBuilder withHostKey(@NotNull String hostKey) {
|
||||||
|
sshdConfig += "HostKey /etc/ssh/" + Paths.get(hostKey).getFileName() + "\n";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SshdConfigBuilder withHostKeyCertificate(@NotNull String hostKeyCertificate) {
|
||||||
|
sshdConfig += "HostCertificate /etc/ssh/" + Paths.get(hostKeyCertificate).getFileName() + "\n";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SshdConfigBuilder with(String key, String value) {
|
||||||
|
sshdConfig += key + " " + value + "\n";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String build() {
|
||||||
|
return sshdConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder implements Consumer<DockerfileBuilder> {
|
||||||
|
private List<String> hostKeys = new ArrayList<>();
|
||||||
|
private List<String> certificates = new ArrayList<>();
|
||||||
|
private @NotNull SshdConfigBuilder sshdConfig = SshdConfigBuilder.defaultBuilder();
|
||||||
|
|
||||||
|
public static Builder defaultBuilder() {
|
||||||
|
Builder b = new Builder();
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public @NotNull Builder withSshdConfig(@NotNull SshdConfigBuilder sshdConfig) {
|
||||||
|
this.sshdConfig = sshdConfig;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Builder withAllKeys() {
|
||||||
|
this.addHostKey("test-container/ssh_host_ecdsa_key");
|
||||||
|
this.addHostKey("test-container/ssh_host_ed25519_key");
|
||||||
|
this.addHostKey("test-container/host_keys/ssh_host_ecdsa_256_key");
|
||||||
|
this.addHostKey("test-container/host_keys/ssh_host_ecdsa_384_key");
|
||||||
|
this.addHostKey("test-container/host_keys/ssh_host_ecdsa_521_key");
|
||||||
|
this.addHostKey("test-container/host_keys/ssh_host_ed25519_384_key");
|
||||||
|
this.addHostKey("test-container/host_keys/ssh_host_rsa_2048_key");
|
||||||
|
this.addHostKeyCertificate("test-container/host_keys/ssh_host_ecdsa_256_key-cert.pub");
|
||||||
|
this.addHostKeyCertificate("test-container/host_keys/ssh_host_ecdsa_384_key-cert.pub");
|
||||||
|
this.addHostKeyCertificate("test-container/host_keys/ssh_host_ecdsa_521_key-cert.pub");
|
||||||
|
this.addHostKeyCertificate("test-container/host_keys/ssh_host_ed25519_384_key-cert.pub");
|
||||||
|
this.addHostKeyCertificate("test-container/host_keys/ssh_host_rsa_2048_key-cert.pub");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SshdContainer build() {
|
||||||
|
return new SshdContainer(buildInner());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull Future<String> buildInner() {
|
||||||
|
return new DebugLoggingImageFromDockerfile()
|
||||||
|
.withDockerfileFromBuilder(this)
|
||||||
|
.withFileFromPath(".", Paths.get("src/itest/docker-image"))
|
||||||
|
.withFileFromString("sshd_config", sshdConfig.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void accept(@NotNull DockerfileBuilder builder) {
|
||||||
|
builder.from("alpine:3.18.3");
|
||||||
|
builder.run("apk add --no-cache openssh");
|
||||||
|
builder.expose(22);
|
||||||
|
builder.copy("entrypoint.sh", "/entrypoint.sh");
|
||||||
|
|
||||||
|
builder.add("authorized_keys", "/home/sshj/.ssh/authorized_keys");
|
||||||
|
builder.copy("test-container/trusted_ca_keys", "/etc/ssh/trusted_ca_keys");
|
||||||
|
|
||||||
|
for (String hostKey : hostKeys) {
|
||||||
|
builder.copy(hostKey, "/etc/ssh/" + Paths.get(hostKey).getFileName());
|
||||||
|
builder.copy(hostKey + ".pub", "/etc/ssh/" + Paths.get(hostKey).getFileName() + ".pub");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String certificate : certificates) {
|
||||||
|
builder.copy(certificate, "/etc/ssh/" + Paths.get(certificate).getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
builder.run("apk add --no-cache tini"
|
||||||
|
+ " && echo \"root:smile\" | chpasswd"
|
||||||
|
+ " && adduser -D -s /bin/ash sshj"
|
||||||
|
+ " && passwd -u sshj"
|
||||||
|
+ " && echo \"sshj:ultrapassword\" | chpasswd"
|
||||||
|
+ " && chmod 600 /home/sshj/.ssh/authorized_keys"
|
||||||
|
+ " && chmod 600 /etc/ssh/ssh_host_*_key"
|
||||||
|
+ " && chmod 644 /etc/ssh/*.pub"
|
||||||
|
+ " && chmod 755 /entrypoint.sh"
|
||||||
|
+ " && chown -R sshj:sshj /home/sshj");
|
||||||
|
builder.entryPoint("/sbin/tini", "/entrypoint.sh", "-o", "LogLevel=DEBUG2");
|
||||||
|
|
||||||
|
builder.add("sshd_config", "/etc/ssh/sshd_config");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Builder addHostKey(@NotNull String hostKey) {
|
||||||
|
hostKeys.add(hostKey);
|
||||||
|
sshdConfig.withHostKey(hostKey);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Builder addHostKeyCertificate(@NotNull String hostKeyCertificate) {
|
||||||
|
certificates.add(hostKeyCertificate);
|
||||||
|
sshdConfig.withHostKeyCertificate(hostKeyCertificate);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") // Used dynamically by Spock
|
||||||
|
public SshdContainer() {
|
||||||
|
this(new SshdContainer.Builder().withAllKeys().buildInner());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SshdContainer(SshdContainer.Builder builder) {
|
||||||
|
this(builder.buildInner());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SshdContainer(@NotNull Future<String> future) {
|
||||||
|
super(future);
|
||||||
|
withExposedPorts(22);
|
||||||
|
setWaitStrategy(new SshServerWaitStrategy());
|
||||||
|
withLogConsumer(outputFrame -> {
|
||||||
|
switch (outputFrame.getType()) {
|
||||||
|
case STDOUT:
|
||||||
|
logger().info("sshd stdout: {}", outputFrame.getUtf8String().stripTrailing());
|
||||||
|
break;
|
||||||
|
case STDERR:
|
||||||
|
logger().info("sshd stderr: {}", outputFrame.getUtf8String().stripTrailing());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHClient getConnectedClient(Config config) throws IOException {
|
||||||
|
SSHClient sshClient = new SSHClient(config);
|
||||||
|
sshClient.addHostKeyVerifier(new PromiscuousVerifier());
|
||||||
|
sshClient.connect("127.0.0.1", getFirstMappedPort());
|
||||||
|
|
||||||
|
return sshClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHClient getConnectedClient() throws IOException {
|
||||||
|
return getConnectedClient(new DefaultConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void withSshdContainer(SshdContainer.Builder builder, @NotNull ThrowingConsumer<SshdContainer> consumer) throws Throwable {
|
||||||
|
SshdContainer sshdContainer = new SshdContainer(builder.buildInner());
|
||||||
|
sshdContainer.start();
|
||||||
|
try {
|
||||||
|
consumer.accept(sshdContainer);
|
||||||
|
} finally {
|
||||||
|
sshdContainer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/itest/java/com/hierynomus/sshj/sftp/FileWriteTest.java
Normal file
73
src/itest/java/com/hierynomus/sshj/sftp/FileWriteTest.java
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj.sftp;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
import org.testcontainers.shaded.org.bouncycastle.util.Arrays;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.sftp.OpenMode;
|
||||||
|
import net.schmizz.sshj.sftp.RemoteFile;
|
||||||
|
import net.schmizz.sshj.sftp.SFTPClient;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class FileWriteTest {
|
||||||
|
@Container
|
||||||
|
private static SshdContainer sshd = new SshdContainer();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAppendToFile_GH390() throws Throwable {
|
||||||
|
try (SSHClient client = sshd.getConnectedClient()) {
|
||||||
|
client.authPublickey("sshj", "src/test/resources/id_rsa");
|
||||||
|
try (SFTPClient sftp = client.newSFTPClient()) {
|
||||||
|
String file = "/home/sshj/test.txt";
|
||||||
|
byte[] initialText = "This is the initial text.\n".getBytes(StandardCharsets.UTF_16);
|
||||||
|
byte[] appendText = "And here's the appended text.\n".getBytes(StandardCharsets.UTF_16);
|
||||||
|
|
||||||
|
try (RemoteFile initial = sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT))) {
|
||||||
|
initial.write(0, initialText, 0, initialText.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (RemoteFile read = sftp.open(file, EnumSet.of(OpenMode.READ))) {
|
||||||
|
byte[] readBytes = new byte[initialText.length];
|
||||||
|
read.read(0, readBytes, 0, readBytes.length);
|
||||||
|
assertThat(readBytes).isEqualTo(initialText);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (RemoteFile initial = sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.APPEND))) {
|
||||||
|
initial.write(0, appendText, 0, appendText.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (RemoteFile read = sftp.open(file, EnumSet.of(OpenMode.READ))) {
|
||||||
|
byte[] readBytes = new byte[initialText.length + appendText.length];
|
||||||
|
read.read(0, readBytes, 0, readBytes.length);
|
||||||
|
assertThat(Arrays.copyOfRange(readBytes, 0, initialText.length)).isEqualTo(initialText);
|
||||||
|
assertThat(Arrays.copyOfRange(readBytes, initialText.length, initialText.length + appendText.length)).isEqualTo(appendText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj.signature;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer;
|
||||||
|
import com.hierynomus.sshj.SshdContainer.SshdConfigBuilder;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
||||||
|
|
||||||
|
import static com.hierynomus.sshj.SshdContainer.withSshdContainer;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class HostKeyWithCertificateTest {
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "Should connect to server that has a signed host public key {0}")
|
||||||
|
@ValueSource(strings = { "ssh_host_ecdsa_256_key", "ssh_host_ecdsa_384_key", "ssh_host_ecdsa_521_key",
|
||||||
|
"ssh_host_ed25519_384_key" })
|
||||||
|
// TODO "ssh_host_rsa_2048_key" fails with "HOST_KEY_NOT_VERIFIABLE" after upgrade to new OpenSSH version
|
||||||
|
public void shouldConnectToServerWithSignedHostKey(String hostkey) throws Throwable {
|
||||||
|
File caPubKey = new File("src/itest/resources/keyfiles/certificates/CA_rsa.pem.pub");
|
||||||
|
String caPubKeyContents = Files.readString(caPubKey.toPath());
|
||||||
|
String address = "127.0.0.1";
|
||||||
|
|
||||||
|
SshdConfigBuilder b = SshdConfigBuilder.defaultBuilder().with("PasswordAuthentication", "yes");
|
||||||
|
|
||||||
|
withSshdContainer(SshdContainer.Builder.defaultBuilder().withSshdConfig(b).addHostKey("test-container/host_keys/" + hostkey).addHostKeyCertificate("test-container/host_keys/" + hostkey + "-cert.pub"), sshd -> {
|
||||||
|
String knownHosts = List.of("@cert-authority " + address + " " + caPubKeyContents,
|
||||||
|
"@cert-authority [" + address + "]:" + sshd.getFirstMappedPort() + " " + caPubKeyContents).stream()
|
||||||
|
.reduce("", (a, b1) -> a + "\n" + b1);
|
||||||
|
DefaultConfig cfg = new DefaultConfig();
|
||||||
|
try (SSHClient c = new SSHClient(cfg)) {
|
||||||
|
c.addHostKeyVerifier(new OpenSSHKnownHosts(new StringReader(knownHosts)));
|
||||||
|
c.connect(address, sshd.getFirstMappedPort());
|
||||||
|
|
||||||
|
c.authPassword("sshj", "ultrapassword");
|
||||||
|
|
||||||
|
assertTrue(c.isAuthenticated());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj.signature;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer;
|
||||||
|
import com.hierynomus.sshj.SshdContainer.SshdConfigBuilder;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class PublicKeyAuthWithCertificateTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer(SshdContainer.Builder.defaultBuilder().withSshdConfig(SshdConfigBuilder.defaultBuilder().with("PubkeyAcceptedAlgorithms", "+ssh-rsa-cert-v01@openssh.com")).withAllKeys());
|
||||||
|
|
||||||
|
public static Stream<String> keys() {
|
||||||
|
return Stream.of(
|
||||||
|
"id_ecdsa_256_pem_signed_by_ecdsa",
|
||||||
|
"id_ecdsa_256_rfc4716_signed_by_ecdsa",
|
||||||
|
"id_ecdsa_256_pem_signed_by_ed25519",
|
||||||
|
"id_ecdsa_256_rfc4716_signed_by_ed25519",
|
||||||
|
"id_ecdsa_256_pem_signed_by_rsa",
|
||||||
|
"id_ecdsa_256_rfc4716_signed_by_rsa",
|
||||||
|
"id_ecdsa_384_pem_signed_by_ecdsa",
|
||||||
|
"id_ecdsa_384_rfc4716_signed_by_ecdsa",
|
||||||
|
"id_ecdsa_384_pem_signed_by_ed25519",
|
||||||
|
"id_ecdsa_384_rfc4716_signed_by_ed25519",
|
||||||
|
"id_ecdsa_384_pem_signed_by_rsa",
|
||||||
|
"id_ecdsa_384_rfc4716_signed_by_rsa",
|
||||||
|
"id_ecdsa_521_pem_signed_by_ecdsa",
|
||||||
|
"id_ecdsa_521_rfc4716_signed_by_ecdsa",
|
||||||
|
"id_ecdsa_521_pem_signed_by_ed25519",
|
||||||
|
"id_ecdsa_521_rfc4716_signed_by_ed25519",
|
||||||
|
"id_ecdsa_521_pem_signed_by_rsa",
|
||||||
|
"id_ecdsa_521_rfc4716_signed_by_rsa",
|
||||||
|
"id_rsa_2048_pem_signed_by_ecdsa",
|
||||||
|
"id_rsa_2048_rfc4716_signed_by_ecdsa",
|
||||||
|
"id_rsa_2048_pem_signed_by_ed25519",
|
||||||
|
"id_rsa_2048_rfc4716_signed_by_ed25519",
|
||||||
|
"id_rsa_2048_pem_signed_by_rsa",
|
||||||
|
"id_rsa_2048_rfc4716_signed_by_rsa",
|
||||||
|
"id_ed25519_384_rfc4716_signed_by_ecdsa",
|
||||||
|
"id_ed25519_384_rfc4716_signed_by_ed25519",
|
||||||
|
"id_ed25519_384_rfc4716_signed_by_rsa");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "should authenticate with signed public key {0}")
|
||||||
|
@MethodSource("keys")
|
||||||
|
public void shouldAuthenticateWithSignedPublicKey(String key) throws Throwable {
|
||||||
|
Config c = new DefaultConfig();
|
||||||
|
SSHClient client = sshd.getConnectedClient(c);
|
||||||
|
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/certificates/" + key);
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
|
||||||
|
client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj.signature;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer;
|
||||||
|
import com.hierynomus.sshj.SshdContainer.SshdConfigBuilder;
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithms;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class SignatureTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer(SshdContainer.Builder.defaultBuilder().withSshdConfig(SshdConfigBuilder.defaultBuilder().with("HostKeyAlgorithms", "+ssh-rsa").with("PubkeyAcceptedAlgorithms", "+ssh-rsa")).withAllKeys());
|
||||||
|
|
||||||
|
public static Stream<KeyAlgorithms.Factory> algs() {
|
||||||
|
return Stream.of(KeyAlgorithms.SSHRSA(), KeyAlgorithms.RSASHA256(), KeyAlgorithms.RSASHA512());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "should correctly connect with Signature {0}")
|
||||||
|
@MethodSource("algs")
|
||||||
|
public void shouldCorrectlyConnectWithMac(KeyAlgorithms.Factory alg) throws Throwable {
|
||||||
|
Config c = new DefaultConfig();
|
||||||
|
c.setKeyAlgorithms(List.of(alg));
|
||||||
|
try (SSHClient client = sshd.getConnectedClient(c)) {
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa");
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj.transport.cipher;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class CipherTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer();
|
||||||
|
|
||||||
|
public static Stream<Factory.Named<Cipher>> ciphers() {
|
||||||
|
return Stream.of(BlockCiphers.TripleDESCBC(),
|
||||||
|
BlockCiphers.AES128CBC(),
|
||||||
|
BlockCiphers.AES128CTR(),
|
||||||
|
BlockCiphers.AES192CBC(),
|
||||||
|
BlockCiphers.AES192CTR(),
|
||||||
|
BlockCiphers.AES256CBC(),
|
||||||
|
BlockCiphers.AES256CTR(),
|
||||||
|
GcmCiphers.AES128GCM(),
|
||||||
|
GcmCiphers.AES256GCM(),
|
||||||
|
ChachaPolyCiphers.CHACHA_POLY_OPENSSH());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "should correctly connect with Cipher {0}")
|
||||||
|
@MethodSource("ciphers")
|
||||||
|
public void shouldCorrectlyConnectWithCipher(Factory.Named<Cipher> cipher) throws Throwable {
|
||||||
|
Config c = new DefaultConfig();
|
||||||
|
c.setCipherFactories(List.of(cipher));
|
||||||
|
try (SSHClient client = sshd.getConnectedClient(c)) {
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1");
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj.transport.kex;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer;
|
||||||
|
import com.hierynomus.sshj.transport.mac.Macs;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
|
||||||
|
import net.schmizz.sshj.transport.kex.DHGexSHA1;
|
||||||
|
import net.schmizz.sshj.transport.kex.DHGexSHA256;
|
||||||
|
import net.schmizz.sshj.transport.kex.ECDHNistP;
|
||||||
|
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class KexTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer();
|
||||||
|
|
||||||
|
public static Stream<Factory.Named<KeyExchange>> kex() {
|
||||||
|
return Stream.of(
|
||||||
|
DHGroups.Group1SHA1(),
|
||||||
|
DHGroups.Group14SHA1(),
|
||||||
|
DHGroups.Group14SHA256(),
|
||||||
|
DHGroups.Group16SHA512(),
|
||||||
|
DHGroups.Group18SHA512(),
|
||||||
|
new DHGexSHA1.Factory(),
|
||||||
|
new DHGexSHA256.Factory(),
|
||||||
|
new Curve25519SHA256.Factory(),
|
||||||
|
new Curve25519SHA256.FactoryLibSsh(),
|
||||||
|
new ECDHNistP.Factory256(),
|
||||||
|
new ECDHNistP.Factory384(),
|
||||||
|
new ECDHNistP.Factory521());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "should correctly connect with Key Exchange {0}")
|
||||||
|
@MethodSource("kex")
|
||||||
|
public void shouldCorrectlyConnectWithMac(Factory.Named<KeyExchange> kex) throws Throwable {
|
||||||
|
Config c = new DefaultConfig();
|
||||||
|
c.setKeyExchangeFactories(List.of(kex));
|
||||||
|
try (SSHClient client = sshd.getConnectedClient(c)) {
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1");
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* 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.hierynomus.sshj.transport.mac;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.SshdContainer;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config;
|
||||||
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class MacTest {
|
||||||
|
@Container
|
||||||
|
private static final SshdContainer sshd = new SshdContainer();
|
||||||
|
|
||||||
|
public static Stream<Macs.Factory> macs() {
|
||||||
|
return Stream.of(Macs.HMACSHA2256(), Macs.HMACSHA2512(), Macs.HMACSHA2256Etm(), Macs.HMACSHA2512Etm());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "should correctly connect with MAC {0}")
|
||||||
|
@MethodSource("macs")
|
||||||
|
public void shouldCorrectlyConnectWithMac(Macs.Factory mac) throws Throwable {
|
||||||
|
Config c = new DefaultConfig();
|
||||||
|
c.setMACFactories(List.of(mac));
|
||||||
|
try (SSHClient client = sshd.getConnectedClient(c)) {
|
||||||
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_rsa_opensshv1");
|
||||||
|
|
||||||
|
assertTrue(client.isAuthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/itest/resources/logback-test.xml
Normal file
34
src/itest/resources/logback-test.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright (C)2009 - SSHJ Contributors
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%.-20thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
<logger name="org.apache.sshd.server" level="debug" />
|
||||||
|
<logger name="net.schmizz.sshj" level="debug"/>
|
||||||
|
<logger name="net.schmizz.sshj.transport" level="trace" />
|
||||||
|
|
||||||
|
</configuration>
|
||||||
@@ -67,5 +67,10 @@ public class KeyAlgorithms {
|
|||||||
public KeyAlgorithm create() {
|
public KeyAlgorithm create() {
|
||||||
return new BaseKeyAlgorithm(algorithmName, signatureFactory, keyType);
|
return new BaseKeyAlgorithm(algorithmName, signatureFactory, keyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return algorithmName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,11 @@ public class DHGroups {
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user