summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.junit.ssh
diff options
context:
space:
mode:
authorMatthias Sohn <matthias.sohn@sap.com>2020-04-24 22:41:39 +0200
committerMatthias Sohn <matthias.sohn@sap.com>2020-06-01 01:46:59 +0200
commit8d2d683655e2de17cf465fa46af10e0e56b3aaed (patch)
tree52491eb0da64433efa2d41004dafd7933ddae8ee /org.eclipse.jgit.junit.ssh
parent77848d635b76d8294697ffaf11acf51256df2a5b (diff)
downloadjgit-8d2d683655e2de17cf465fa46af10e0e56b3aaed.tar.gz
jgit-8d2d683655e2de17cf465fa46af10e0e56b3aaed.zip
Decouple JSch from JGit Core
Motivation: JSch serves as 'default' implementations of the SSH transport. If a client application does not use it then there is no need to pull in this dependency. Move the classes depending on JSch to an OSGi fragment extending the org.eclipse.jgit bundle and keep them in the same package as before since moving them to another package would break API. Defer moving them to a separate package to the next major release. Add a new feature org.eclipse.jgit.ssh.jsch feature to enable installation. With that users can now decide which of the ssh client integrations (JCraft JSch or Apache Mina SSHD) they want to install. We will remove the JCraft JSch integration in a later step due to the reasons discussed in bug 520927. Bug: 553625 Change-Id: I5979c8a9dbbe878a2e8ac0fbfde7230059d74dc2 Also-by: Michael Dardis <git@md-5.net> Signed-off-by: Michael Dardis <git@md-5.net> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Signed-off-by: David Ostrovsky <david@ostrovsky.org>
Diffstat (limited to 'org.eclipse.jgit.junit.ssh')
-rw-r--r--org.eclipse.jgit.junit.ssh/.classpath1
-rw-r--r--org.eclipse.jgit.junit.ssh/BUILD7
-rw-r--r--org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF11
-rw-r--r--org.eclipse.jgit.junit.ssh/build.properties3
-rw-r--r--org.eclipse.jgit.junit.ssh/pom.xml19
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa12
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass15
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_2565
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass8
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_3846
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass9
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_5217
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass10
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed255197
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass8
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass8
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_102415
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass18
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_204827
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass30
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_307239
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass42
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_409651
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass54
-rw-r--r--org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass.pub1
-rw-r--r--org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java813
-rw-r--r--org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java418
45 files changed, 1660 insertions, 2 deletions
diff --git a/org.eclipse.jgit.junit.ssh/.classpath b/org.eclipse.jgit.junit.ssh/.classpath
index eca7bdba8f..110168ffa1 100644
--- a/org.eclipse.jgit.junit.ssh/.classpath
+++ b/org.eclipse.jgit.junit.ssh/.classpath
@@ -3,5 +3,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="resources"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.junit.ssh/BUILD b/org.eclipse.jgit.junit.ssh/BUILD
index 906053e39a..61b5ce78cb 100644
--- a/org.eclipse.jgit.junit.ssh/BUILD
+++ b/org.eclipse.jgit.junit.ssh/BUILD
@@ -8,10 +8,17 @@ java_library(
srcs = glob(["src/**/*.java"]),
resource_strip_prefix = "org.eclipse.jgit.junit.ssh/resources",
resources = glob(["resources/**"]),
+ visibility = [
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
+ ],
deps = [
+ "//lib:jsch",
+ "//lib:junit",
"//lib:sshd-osgi",
"//lib:sshd-sftp",
# We want these deps to be provided_deps
"//org.eclipse.jgit:jgit",
+ "//org.eclipse.jgit.junit:junit",
],
)
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 27223b2de8..9a34eff447 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -8,7 +8,8 @@ Bundle-Localization: plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.apache.sshd.common;version="[2.4.0,2.5.0)",
+Import-Package: com.jcraft.jsch;version="0.1.55",
+ org.apache.sshd.common;version="[2.4.0,2.5.0)",
org.apache.sshd.common.config.keys;version="[2.4.0,2.5.0)",
org.apache.sshd.common.file.virtualfs;version="[2.4.0,2.5.0)",
org.apache.sshd.common.helpers;version="[2.4.0,2.5.0)",
@@ -31,7 +32,15 @@ Import-Package: org.apache.sshd.common;version="[2.4.0,2.5.0)",
org.apache.sshd.server.subsystem;version="[2.4.0,2.5.0)",
org.apache.sshd.server.subsystem.sftp;version="[2.4.0,2.5.0)",
org.eclipse.jgit.annotations;version="[5.8.0,5.9.0)",
+ org.eclipse.jgit.api;version="[5.8.0,5.9.0)",
+ org.eclipse.jgit.api.errors;version="[5.8.0,5.9.0)",
+ org.eclipse.jgit.errors;version="[5.8.0,5.9.0)",
+ org.eclipse.jgit.junit;version="[5.8.0,5.9.0)",
org.eclipse.jgit.lib;version="[5.8.0,5.9.0)",
+ org.eclipse.jgit.revwalk;version="[5.8.0,5.9.0)",
org.eclipse.jgit.transport;version="[5.8.0,5.9.0)",
+ org.eclipse.jgit.util;version="[5.8.0,5.9.0)",
+ org.junit;version="[4.13,5.0.0)",
+ org.junit.experimental.theories;version="[4.13,5.0.0)",
org.slf4j;version="[1.7.0,2.0.0)"
Export-Package: org.eclipse.jgit.junit.ssh;version="5.8.0"
diff --git a/org.eclipse.jgit.junit.ssh/build.properties b/org.eclipse.jgit.junit.ssh/build.properties
index aa1a008269..84f1c95cfa 100644
--- a/org.eclipse.jgit.junit.ssh/build.properties
+++ b/org.eclipse.jgit.junit.ssh/build.properties
@@ -1,4 +1,5 @@
-source.. = src/
+source.. = src/,\
+ resources/
output.. = bin/
bin.includes = META-INF/,\
.,\
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index 9f9dc26e69..6eaea83ee6 100644
--- a/org.eclipse.jgit.junit.ssh/pom.xml
+++ b/org.eclipse.jgit.junit.ssh/pom.xml
@@ -40,6 +40,12 @@
</dependency>
<dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.junit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-osgi</artifactId>
<version>${apache-sshd-version}</version>
@@ -52,6 +58,16 @@
</dependency>
<dependency>
+ <groupId>com.jcraft</groupId>
+ <artifactId>jsch</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.jcraft</groupId>
+ <artifactId>jzlib</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>provided</scope>
@@ -68,6 +84,9 @@
<include>plugin.properties</include>
</includes>
</resource>
+ <resource>
+ <directory>resources/</directory>
+ </resource>
</resources>
<plugins>
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa
new file mode 100644
index 0000000000..f097516df2
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQC+mJEX/XBloWhNM+BEuoh5z+EAuZfVyJ8cHNKlQmC1sWrENKGh
+P8ZhzWeHW0A7JnvTQgMqW6yD4mDzCpbR1wEz5KeXAphEjCGPnRik7Q4RjpZTd6Nq
+nNF/CYYGYuwR7ZGUPITTpKJWgX6NkEk+a4tvTWP7xfxOq5iKIspFEhEOlQIVAIBi
+TdAR8M2twrXZdspBjdJprjDXAoGAOrRYdXRHhpsOewIi9GQah0lde7AVrmZawK9Z
+BwhDUagL58gS8PvcsNNVhS2dKEX45pqZmgayt2UEE/5bke3+CdZtStDsezBYMu8P
+I/0qjOULhl7xLJT5ayCIN2ZuvcH8vtqH89fXgZkIz0c68AzY1ZFjJPc+TdE0puI9
+3mMVRaoCgYEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s
+/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVD
+lQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMcCFFeUfQeX
+3mFPCfKJ5uXMjkPUqIo/
+-----END DSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa.pub
new file mode 100644
index 0000000000..676685332e
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL6YkRf9cGWhaE0z4ES6iHnP4QC5l9XInxwc0qVCYLWxasQ0oaE/xmHNZ4dbQDsme9NCAypbrIPiYPMKltHXATPkp5cCmESMIY+dGKTtDhGOllN3o2qc0X8JhgZi7BHtkZQ8hNOkolaBfo2QST5ri29NY/vF/E6rmIoiykUSEQ6VAAAAFQCAYk3QEfDNrcK12XbKQY3Saa4w1wAAAIA6tFh1dEeGmw57AiL0ZBqHSV17sBWuZlrAr1kHCENRqAvnyBLw+9yw01WFLZ0oRfjmmpmaBrK3ZQQT/luR7f4J1m1K0Ox7MFgy7w8j/SqM5QuGXvEslPlrIIg3Zm69wfy+2ofz19eBmQjPRzrwDNjVkWMk9z5N0TSm4j3eYxVFqgAAAIEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVDlQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMc= testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass
new file mode 100644
index 0000000000..375d38fe43
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass
@@ -0,0 +1,15 @@
+-----BEGIN DSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,EBB6ACAA4F1FC558865344E3C2B91A5F
+
+CWMAq20YBO8ueHnmQ7IaKa7ISEvNbwbzqoBIxor6TZYSU3JvlIf5AL2UvGpMJDk1
+fyROdCjdVAeWKQC0peU54D3YnD3am4gZlrclPMjMRnjBmqO+vnU7bTudIt/8y6vg
+gmHZki0/aceQ6QvGwGrxBezBPaK4Bc926lePujHHE/PbtuQgkBw7rhIBGKVuy0qN
+sFbC4AGnYl5tudy5RLvCcpQvpDCjnYAfGQVimRYSOsaOwTEBvsnQFUH1pqQAYLC4
+Capo1yj6Q0smzwsGoyFSvmPkyzLbMTT42m+M48gc5nuaOkbU5absqOb8cQgRVmWB
+W1HnpufqGtyF6vBK+qlzg157bhQDYMwZuubX+IrTRL67djBiSIpiRDZduJavT3zq
+iSrRGSnjnkhp4NxtJJjprDQe4VAZEccN5GWPjClbogjpsG+fmTJiNDMI88L11DrV
+Vjeaxsql31iur/xGwvmBYd+/V+Nu4v7kA4XViO/3ZIpqi8qvQ3si5hbALSX0OPnm
+9q0eMp9qfmzPvbmysq2BEenBaZDwEWYTYpcF23pjwc1EvmfP8EAYT+xH95ZhxVmc
+Sujq0VyGeIhy7+gRHZo2Fg==
+-----END DSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass.pub
new file mode 100644
index 0000000000..676685332e
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_dsa_testpass.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL6YkRf9cGWhaE0z4ES6iHnP4QC5l9XInxwc0qVCYLWxasQ0oaE/xmHNZ4dbQDsme9NCAypbrIPiYPMKltHXATPkp5cCmESMIY+dGKTtDhGOllN3o2qc0X8JhgZi7BHtkZQ8hNOkolaBfo2QST5ri29NY/vF/E6rmIoiykUSEQ6VAAAAFQCAYk3QEfDNrcK12XbKQY3Saa4w1wAAAIA6tFh1dEeGmw57AiL0ZBqHSV17sBWuZlrAr1kHCENRqAvnyBLw+9yw01WFLZ0oRfjmmpmaBrK3ZQQT/luR7f4J1m1K0Ox7MFgy7w8j/SqM5QuGXvEslPlrIIg3Zm69wfy+2ofz19eBmQjPRzrwDNjVkWMk9z5N0TSm4j3eYxVFqgAAAIEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVDlQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMc= testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256
new file mode 100644
index 0000000000..8a4c864afa
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIAqXVKoLNr7/wNluxmGZnZmJCD/5h06ptAICRk+8FIjfoAoGCCqGSM49
+AwEHoUQDQgAEoQHTUWwu3nJnCHeSv3YE59UxfuGNjAXLzK0MjDwoXt6/qePjjKAQ
+ehHdAIYQHr9zYJu5SA5b86HL5glqjcy+Pg==
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256.pub
new file mode 100644
index 0000000000..43540ec0ec
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKEB01FsLt5yZwh3kr92BOfVMX7hjYwFy8ytDIw8KF7ev6nj44ygEHoR3QCGEB6/c2CbuUgOW/Ohy+YJao3Mvj4= testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass
new file mode 100644
index 0000000000..b767c8e99a
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass
@@ -0,0 +1,8 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,86940587F5C93441B585F469FF31AC06
+
+LaIyzOCeBPJA6OkFOFnFfVorYO+Rm1g5QpvqEcFZ+FCuEvhMZN00NMZ5hHKvwQLt
+XSK5Se8MUD+e6qFH/ZcoYTixUqYjYJlOkxJzKaXg5nM82wQHa1LqQqcL4IDrJmzv
+qJbCLtl6XOfkQQUA6gezqhtiNYWLDZIPfZ0dsaIB/fU=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass.pub
new file mode 100644
index 0000000000..43540ec0ec
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_256_testpass.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKEB01FsLt5yZwh3kr92BOfVMX7hjYwFy8ytDIw8KF7ev6nj44ygEHoR3QCGEB6/c2CbuUgOW/Ohy+YJao3Mvj4= testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384
new file mode 100644
index 0000000000..dc2ac86508
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAgAgPcgkPaitxOrphrrLe+am0eUhYi346UUTnb5WZL3164MEjFByd9
+Egv6KwB4hCqgBwYFK4EEACKhZANiAAQhJrJ+vJLbkbd9C1he+4XuxaOyZ1IqYJqz
+PZCXcKkIlgy+0I07RAxRUd75GHKc4ViyUnLq5odV25H6FNzHJHO7ifE4H6jrEpA/
+UL6LkfZReYZ4sNmeQI7MBXm2IXQsIZ4=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384.pub
new file mode 100644
index 0000000000..3e813a5e48
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBCEmsn68ktuRt30LWF77he7Fo7JnUipgmrM9kJdwqQiWDL7QjTtEDFFR3vkYcpzhWLJScurmh1XbkfoU3Mckc7uJ8TgfqOsSkD9QvouR9lF5hniw2Z5AjswFebYhdCwhng== testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass
new file mode 100644
index 0000000000..06032d049f
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass
@@ -0,0 +1,9 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,80B1C4D6D9B45690A07B9886050C63A7
+
+WxS7EGs77p1aPZuxXW0G/yTFKAh4M30AaeGQBPjDR/HTAmPJe3irDH56fdmGhY4+
+zBT+6X1VppB+UqB0nJ/qHq7FeA37eJPXJnuskPh2BzLlBaVhmEnzZylEW33gzAuH
+XzC/Z2OjdWRjn+rBXM5fwo9IIC0WzTNpBokdeMo8tpnPzGTlsTFeyVgMZJ3wOlCO
+4ItX9ddY5P+MrLzWP672IyZZqAQGfLec4YoJ286wpHY=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass.pub
new file mode 100644
index 0000000000..3e813a5e48
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_384_testpass.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBCEmsn68ktuRt30LWF77he7Fo7JnUipgmrM9kJdwqQiWDL7QjTtEDFFR3vkYcpzhWLJScurmh1XbkfoU3Mckc7uJ8TgfqOsSkD9QvouR9lF5hniw2Z5AjswFebYhdCwhng== testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521
new file mode 100644
index 0000000000..c28151e5ab
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIB4zI/MCFSfJ0wfyLwZPxG1vP2o3fF7fEuOTpK+fxbDHKYz6r4bNv3
+HkPQEVTIAqDl7r5Ebcx0BMeYr9oe69tPZIigBwYFK4EEACOhgYkDgYYABAChltEM
+zT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1Bge
+wuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+
+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw==
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521.pub
new file mode 100644
index 0000000000..9bac1e80f0
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAChltEMzT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1BgewuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw== testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass
new file mode 100644
index 0000000000..c1c1bbaced
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass
@@ -0,0 +1,10 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,7070032284B3C310353B8C352AB2D8CE
+
+UBgXTwobcLX1VFtQaLNiwwVzdN1+TlmhSRCnU+kv2EpunXxfvyOVS1mZTam9NyhE
+O0Mc7REi5hDHp8UYM7MP+wrwK+QM3D2Vm2/Rh0+acd4Gu2XGACJHWXGIyKwNsU0R
+ZddusHIi+979sHw3vSUFCvuDwc9YZBoujpzls7NYEWXiAVv6wd1RCtAynkBk/uvc
+1F7iHLuRttejBPvrb/a2AxY0pFpCuCVmGjuiS5bfVWBj7xLEplqdU6/95rd9pRwx
+e2uRlU0AFiQGNPStfhjgfCWnmf+aX3vAgVqkLMYKYQE=
+-----END EC PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass.pub
new file mode 100644
index 0000000000..9bac1e80f0
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ecdsa_521_testpass.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAChltEMzT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1BgewuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw== testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519
new file mode 100644
index 0000000000..02afa54788
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBIJlrW8XB46iAVY0XqbjYKG8wJ95iILxOb5ONQhFBvPQAAAJC8jORLvIzk
+SwAAAAtzc2gtZWQyNTUxOQAAACBIJlrW8XB46iAVY0XqbjYKG8wJ95iILxOb5ONQhFBvPQ
+AAAECjklggj+glO2K60Ptg+aXYGBdvXtk9TQnKINhrEIxW9UgmWtbxcHjqIBVjRepuNgob
+zAn3mIgvE5vk41CEUG89AAAACHRlc3R1c2VyAQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519.pub
new file mode 100644
index 0000000000..7857db5602
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVjRepuNgobzAn3mIgvE5vk41CEUG89 testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass
new file mode 100644
index 0000000000..904cf302c2
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAsFN8vig
+Nw4/Ow6xbb7MAZAAABAAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEZXZRjuttLufaP8
+wFD/i4lYPnKk01z46Jwv/9U4mPioAAAAkHLErPaXeC179rzXMaSwClstzsKvJ/Gqh2cY8d
+cWzymXtKZcivWMKesRHbC+1qRx53ofx15IzT5Fmg6NuNk4sm2s+lH8x8HN3CPWBfjGIelP
+iQUR6M6Y91mPigpRC2HUJmJIaFNdrRqFF84a5+qyK//tdy1fv4gNMLi5yPdXiL/Ttw05FS
+LkFikjfvSGZSO/MA==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass.pub
new file mode 100644
index 0000000000..65038b5f4c
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_expensive_testpass.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEZXZRjuttLufaP8wFD/i4lYPnKk01z46Jwv/9U4mPio test
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass
new file mode 100644
index 0000000000..7ad4a77056
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABA4hLhtuV
+MNBBC+j45F4KFcAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVj
+RepuNgobzAn3mIgvE5vk41CEUG89AAAAkPH343T+NbHb05J/6CHnF9h7C11LJDHe2x9+HC
+dNB50fP9M+KJ/cC5cqIeHm8y0fg+wX2WLlJPjNVoSd5MciWCfUWO0k32ciVpoyrGCz5Gh6
+axKVVY42QjdgO0S2QxWClnAuMdkVdl2ke/PcGp4yqTTIruAAB0m3d0jZdKNT1Vziww0rQB
++DOo7xQ9Tx99U+rA==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass.pub
new file mode 100644
index 0000000000..7857db5602
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_ed25519_testpass.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVjRepuNgobzAn3mIgvE5vk41CEUG89 testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024
new file mode 100644
index 0000000000..0b403674fa
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYr
+MzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56Yx
+gAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vwIDAQAB
+AoGBAKU+bNP1BGDQGmEfJv+5DlSuofP19MREVSpx0zfVnv45SFc5G0EVl4Wb0GMi
+O4VXmIM2nipxLBZrJOBI0HDnaQcx1zQR6tpvBO7BbAU0sflOvUDldUStTnz3TTQW
+2ECm2y8bsArNqkeLndqis3ICmYL1budhDdUYYcqv10IlbjPJAkEA6yE0zduCE2wM
+Ob7lcqiQCOiXeZ0KijHTmSZV4Fn4HRbp+XuxUpjSWFaoDTO0bncGNE+JYjywe64V
+XvEORb1hTQJBANgcjEoCrUFY7VYWx3f1tpN0Q6jwwcj67Sd+ysaZNgghTPU32GTa
+auGQFv+tifUQMyyVrhAfZ6s8myKOH5SWUDsCQGVvqOkaRq58UXXkDfZ+E81UEm0L
+u81Mm52ZdTjZd3mNNhlELIaWmUA0+kDfynpRbOLKYVl5FyX0PxH7ao3Zmo0CQFpL
++1YFLk0KkggRdoCp+wI7ZvXUurN2HNcOxD0c0RWujFA9aD4jgNsEcIeeA/GQNkGf
+vN3hsVg793oFti5Ia/cCQAubCMvRqFTyXUBervPVC0kibO3OwYt2xN/7lQXAVSfm
+nRwV/46trioV3rMF84hpOk/46Qe5hqbWyQnL+dZljpY=
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024.pub
new file mode 100644
index 0000000000..4aded97ab6
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYrMzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56YxgAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vw== testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass
new file mode 100644
index 0000000000..0b66dc0aa9
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,4B8025AB7456E0A2B48408407C6E3FF4
+
+B9gztX+5QQPqMR/79eJHxjNdo9baoKjfWY+Ye7t1h7ucOPMCEXRSP8FwPwBfbzQh
+6W1AHOfiDCHTzArDRG9SXrFfRlU+8o5ffs/TStTNqde/AXJeNuM3pwbmqKV1m9oY
+oWelabmGtNUvGMAHMFm/2uk4BgS9Kjv71KnJg0cQQfIiPKTPBncJe/R5mf6O12rB
+ByOrrlDmjtgveZZsgggEZbU9Y9DYiHZp6yT0JepxIWNImQ/A9EeUPTQheVB2ECT6
+DLUOwRfyFhdvsfD2eXLK+u7T47keFny3rIfm1e8HC1y3X+T/nFxKGoShecx1NmEL
+HMgOKyFSwGSZh5jxE66dSQoc+rRZhCWSyPJEb9cjwp8JLON8oH3Yg+PIXYJhMFK+
+nghAIVXp3/H+cYXMN27j21cRGC7ePuF3YX242Gr+LSj42Wf4qCMTyvWur8WrSe6U
+iyrWJ8+w2J7O7rRHGM8v+GYGaiX1qIXFheM/774vsDmjuueOhkjiqs254gaap8xk
+LcJUuqJU2AL21+eW+R+EG3Rl/AbMIaQ4GFDpHfgEmmvVVoOvJunNQkDIP9JzKczO
+g7cN/EYLUC2TcdmNaiunB8RhXMiaTqw4kYJEzy4lsxk/xjubC7vlQKTvtnWCpob+
+WpHX/2FBdPPULt38AIk4HQq7vKvKw9TmvGeOvQmCUun7eCFFhxKrwNKO5YCXAHvs
+fv7JNGfrST4jwbqCvamuk+XTf0GkgJN83G7DT04EIzee6wwai/NRDybgYptJsj9G
+6wBpKH15BtkktuUzM1MCt5+T6Ccsg+d6xE6eStimwDxkXCjvgz/KlS+sPKe7uS4h
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass.pub
new file mode 100644
index 0000000000..4aded97ab6
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_1024_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYrMzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56YxgAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vw== testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048
new file mode 100644
index 0000000000..a2d7d62310
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1sFEWiIp8SVO8/sDhKJ67O7tQdtDwsqWi9Fm238tAuy26OH+
+ylireX/qVcndU8Yr0qYShcwloEUaeNe77VgffZa2ZIUee75u8u+WGCYjea4RQ1bZ
+tDcioWkxl+xYfVuuKaA8CQn47XUdyoA/5P3DpDhaJl8KevaYupJNHOo9Lt2E5dVT
+93OksZBOQ6E3nNlsefP/hnFByiczlde2GIXP2sWLoxsiVsbI+CLeGtxQxPubX9yu
+vWrl/nv/yERR5ZBOEVY5N2+1BdT7DvOIMg2q60FpJv6zZpQi7Ov1iONMVafytIRW
+Ma2rPkpS83Ebxh5c92T3rgLUf5DcZjKvBgxtpwIDAQABAoIBAHeDZv6iNKU3FhFB
+iFuv8Kka7n7P/43QIKf/CTbuN6aBBenkm18QqZ0cStUjWkDc8FZyhaxgSDBBRNIr
+fTJA8IV78lVOoABNooEgRG98ChIVhRXsp4tbg7JAUJEzvqtE8k/IFKETI61CmCmx
+5d0SPGaP1du02KhFxAlQkgmdch85st+tRFv5GZXqiKbR6QlNaJgIXIoOlykVvnz6
+rnl6Q1SDutBOKGC8xFrDzFI8KxLFe3RFQxtHtsLRPcrrpukNSHICTMO4jtGXSZ12
+9Zh29ZtkouCDk+b176dGrJKfIBbxXtBGVXtkuo7rj8EWVWrJiiYbL2hcWD+Pw1VL
+0GWkaEECgYEA+RrE4nVkfdJ0Zgx+sACQqs4uKi/JuFHU69JnO7RB2lDwzQbIPKl7
+nn0ExJ4V9m035/3mqKReBIyMIjIhwXgLFiakNO/+GAWa4ycRMB3pV8WaVFCnWZEF
+oLRg1ukoLs01TfOszcux831n8zmPlz/NLTTkC26O3WXsVmnCSlPXd1MCgYEA3LMW
+B8ONEDFYACB8xKA5zn3jrKq/yVFfiQzEO87zSkgG1mQbsb5T8jggWiIHVyZKQUSk
+8ZkrwBKW+LwyRik1lVwawALmcvvN3VyCW5BukniErAUu8jb3+R2aFdrjzpiNqzMF
+M18BPDElirTXMjJusC7z/0I7+gyAu9ttYJY1id0CgYBqjIiqVIwnRV2ESNPndFZs
+uMQGR2qA7H+mXtjJMND6EKTvDXeYeuXlZJQlhXjfbtf64x9GAwgz6eoGtmq51h7n
+2p9iBUUqATu+7Xbsnd6xLFRWvCjYpq9BjeXeBtypKB0kupWvcPEstPdBkd1ZVHDu
+ZTElsqRpDq+IRrRUFoiTAQKBgQCxTGmRWSa08H8asv6o03M9EONbrlyedXHDXu8y
+gQHwFcbwasHY2+cCetZ6skWlXIxgvK2prXx5NDX2ovHcbXSvhauzv2C01NdAUvYi
+avh5ULp8mzlouoIhrgdAMXW7XdDJzRYLe/I5Ed5v/PG4UM2dWksIMISQT4UH5bKL
+2oAuPQKBgCQaJ2oc5qE/f6MiL0XfGSdY26gOZcVrm9L1XKXtyHkfj4xWYQ58DSYa
+vNZH3fGyfR+q7g1WgUmLib5etOjUjbVYRjIEov8xLA41UZZLNGRLc4VzgeCT73CW
+YvbxeN93fL0tgvKeyNVzIsWRazHMo+aQodlXvpPckHXYxYHS93W+
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048.pub
new file mode 100644
index 0000000000..b787e36483
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDWwURaIinxJU7z+wOEonrs7u1B20PCypaL0Wbbfy0C7Lbo4f7KWKt5f+pVyd1TxivSphKFzCWgRRp417vtWB99lrZkhR57vm7y75YYJiN5rhFDVtm0NyKhaTGX7Fh9W64poDwJCfjtdR3KgD/k/cOkOFomXwp69pi6kk0c6j0u3YTl1VP3c6SxkE5DoTec2Wx58/+GcUHKJzOV17YYhc/axYujGyJWxsj4It4a3FDE+5tf3K69auX+e//IRFHlkE4RVjk3b7UF1PsO84gyDarrQWkm/rNmlCLs6/WI40xVp/K0hFYxras+SlLzcRvGHlz3ZPeuAtR/kNxmMq8GDG2n testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass
new file mode 100644
index 0000000000..7b3a3f4e58
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,DED10D02EF74A02F24F46AB44A84F4B3
+
+DL0M2dNu7cXNLE6KGPqEt3pSKiQw6ajlxYaaXyyAwvpZB0Pv44HUjXfhMONs5FH5
+rDjz1RSYDRdMF/h6FtltdEEareXwMtRTvP2wb0gsQKiYS5M9WeebM3TY55JmwS1U
+hhrPrEaP6hs6WEy9xp9DVxJN2y1MA5iss7M+fQ4/C6QeSp9On6bgCEvNPwdMTS4A
+3sLp+yzRvrefQmSi2SWbJoYlChitOMdc84iDJXDo951QQLX75GqMm41fFLHHrcTO
+7v/k/D7p/KLlNf43Ru+2yPNE7qyEK0pDSjvPnjPykIa6SWq3Qx2DnVdtZ7bWF8LA
+B349QmuE1r/YYHNvWnp0/5SztivJk3NMeTT29PIiZoHioo53Vtru6RcXYMOvHbh1
+maioVkgRl5gkhLC86o4V+3hiJQNrVCWMuT+lxLY2Tt6bFXulbf3WH69AEAFW4S4a
+e7zH4fwvkSwz+bFxg9B+Yynv42ke1a+tvDI+aDvsMmv9JUCy6G4Te+isXYxLdtT0
+nyqJ+wwP53AWS8gOvoUXzxxsEchTDtQnBQMWuSHEdFrk3OLGykNN6vZaxUROxpJf
+vcPl7JniWGhzDzUdHh0AQbLxXoZlv4YU1uO/+1OnrvIkuO5DCDg8v2sTFRW6sgiU
+JXm3QPJiU/bu3/FJ4XCU75cTcunZMXsL7TY9mURq7Y5FxcByuvSL2nlA7KfROTVq
+I6w+Ej+r99C1u0G63sk5b99Pm4cb2+V/sr7pslqlU9Yw1Z5hw55ibih04CiWZAhJ
+Az7s8ho4dY9E1n/XJSe26p14RPYU+w7WZuN6Xb04t3+BhF4Ubbsdn6F3lAVOrrWH
+6xNoncmIEYdfdcI089UPpV4/bIpdakXRIbaLmpshyU6aIRUXqYkzwduXcHUrxgq3
+1QCZHNvq1+9i5Wqj8JP8cZrq9YVldOeXdIIsm1SSepbDQ7820d5T4Dk6cj85BXYC
+6t12UNZ5mhzTvIAqbR3Who53jQ8cY0MSVXR6Jd6vPih2OhAnccnuJmRCNNJkL4mg
+pVcsSgYjoUx+w6Ou1muCIkkGpdEhLLwEnKFc0HUmPBToRqgiB1Aec+7oMv62XhXe
+yA26/dpT6N6SWYKN7MyDWUe2ilkmjXI+JrPCH+/w4FXh+GKafOn8XlcBnRWHVBEX
+ZQfYLckd1j9B6p7By7ed2H+8FxZLz3gthcSxRG89IP/EQImY/e9A3aoLrFX6C/W0
+Gd6JrIvzC2bZCvrq+VTYs3101j1xe6ZDJnq68HokjpG8P9DlFYDOpRetCjR7TuqN
+I5s606KAsGkt/jfbSNUMIEtuM0AC75m3TTJeWdfYh/PVYevUC+pUoreJ0ZsttQ2i
+D550sAAzU7PCzZQsDF1i2jv/YZ0wXz7+C7YFiGNmb3HmXH0Lb2HISJR5UL+x+hHY
+RArXtVubqjFz179pawzI0n03Z1OXiHolwer7C+Twmarv7SPe8rMU3HcHP25JeTAW
+mo0PxNGG3yQPlRZWpPz8LEWGo+fDqfA4kbqy4+Pvo7B8YFIQyE9QG+oBv+/7uqMU
+UOs1ZqsmvEUmvWMeQnWsjETmHKucbmTBm8ktsesb3sCKfY/pf8hAHbO6+9J3ebYf
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass.pub
new file mode 100644
index 0000000000..b787e36483
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_2048_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDWwURaIinxJU7z+wOEonrs7u1B20PCypaL0Wbbfy0C7Lbo4f7KWKt5f+pVyd1TxivSphKFzCWgRRp417vtWB99lrZkhR57vm7y75YYJiN5rhFDVtm0NyKhaTGX7Fh9W64poDwJCfjtdR3KgD/k/cOkOFomXwp69pi6kk0c6j0u3YTl1VP3c6SxkE5DoTec2Wx58/+GcUHKJzOV17YYhc/axYujGyJWxsj4It4a3FDE+5tf3K69auX+e//IRFHlkE4RVjk3b7UF1PsO84gyDarrQWkm/rNmlCLs6/WI40xVp/K0hFYxras+SlLzcRvGHlz3ZPeuAtR/kNxmMq8GDG2n testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072
new file mode 100644
index 0000000000..10d622c905
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072.pub
new file mode 100644
index 0000000000..686d3b06b1
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUzzht4+crpxH0SL8fRLfpCF19SXSpQhlesc29L2L+elNl03WYLNbF40ebRglBamf0Co9kYb908a3cJAQvnZMpdrBj/SSz7inQkn3brSipUEUmtcTmVq1VAJ0hnUZHaIkveRktcddKRRHKAQv1oF84RRKYLhKQ1jQs4a7AxnZPJeIVYctj2V7UNTZQxt+Trn7ouDNT4UrM7iyD/NkPegH0D6f6jNJm/9gQy4+9QBugxQmu7+5zoPlTGHAnoZIo2RK3fElQqeyiGQJtECr9qey9UmoSyBlz6gpUWJ0s8/P2suOgSXfD5xSUzRNttdqq0VcKgTxLoIAkb8blGR+vDttRSSMZtitWqNHVbfZ2De3HNkxWiMjOiUHkta2xtljrbRv6EeTtWH4XdXDbKKcY2s8NWdUzAH6lEKw+RZK+Uu+lHgLvGqqIS5wHV4WrBZJWF8ODOS+1OJownszti6cnmkVaM13TItk2AhrlIRLOufnrjDCNzAFsxWJ+KYynem8UFus= testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass
new file mode 100644
index 0000000000..353a24c206
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass
@@ -0,0 +1,42 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,040847DD0487D72145EB88CB09486EB0
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass.pub
new file mode 100644
index 0000000000..686d3b06b1
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_3072_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUzzht4+crpxH0SL8fRLfpCF19SXSpQhlesc29L2L+elNl03WYLNbF40ebRglBamf0Co9kYb908a3cJAQvnZMpdrBj/SSz7inQkn3brSipUEUmtcTmVq1VAJ0hnUZHaIkveRktcddKRRHKAQv1oF84RRKYLhKQ1jQs4a7AxnZPJeIVYctj2V7UNTZQxt+Trn7ouDNT4UrM7iyD/NkPegH0D6f6jNJm/9gQy4+9QBugxQmu7+5zoPlTGHAnoZIo2RK3fElQqeyiGQJtECr9qey9UmoSyBlz6gpUWJ0s8/P2suOgSXfD5xSUzRNttdqq0VcKgTxLoIAkb8blGR+vDttRSSMZtitWqNHVbfZ2De3HNkxWiMjOiUHkta2xtljrbRv6EeTtWH4XdXDbKKcY2s8NWdUzAH6lEKw+RZK+Uu+lHgLvGqqIS5wHV4WrBZJWF8ODOS+1OJownszti6cnmkVaM13TItk2AhrlIRLOufnrjDCNzAFsxWJ+KYynem8UFus= testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096 b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096
new file mode 100644
index 0000000000..1a10b388e6
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096.pub
new file mode 100644
index 0000000000..3c3c16feeb
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDNtJeBHx0z8RTfpPaUApShGWiMqyk+EpV8O3s3emsYBzYvoA/TLcbUcsuAo8rb1LQLJ1yrKXr3hJP6672pjMMtCUVAdvEgbvo86YdeP3ZsXjuUlRE3GLvyGgQOdla0MbSuxQIEPUjjP61eNZXzWm/cGpWDmKVHq6o2C/dHNePaMvpD7fPBfF4zDuMndO66SF/nSGXFuUfhsEg4u5nJvi3xHgTJeasA+6kdpRlKLBvFoOtX6yB27/sbx2fIHR1UvcBQ1QSntauHY97CeOBRw1BCbIm4H3zAQ/f4atOyS2LGMgSYJ17+B8OyXknEHPNtQW2VDwlU6ef0kJDZTftxGCCxbfKiMXEYJT5slpfPTNKA+Z9H+tPuheAMpHvolnZVJMa4AWs9x1HISs+Dg8LoW1bSY3GfZLpErbyzrGlMPLAQO7wROGUohcac31cNfbqBjDnpmWYBolDopA/8XQCR/ydEBHOQ8m0hrbWIGtQdFcsXtgqcHAvIJSBMDdZIOA10/0Zrv7okLmAY9bkoYgqgrtJlj0vE2hXrqKCWnScYQzbtlTWtrCWXBwkDORtJpWIP+++siK7PTizj7kr6sCF7wJThRMFZQmBS+1NJXt39IF8UuxAkj9bXWf35IIhsELH2c437GQyQxBXKsw9agP6hhHH/BcYg7aqSQ956AbJvygpiw== testuser
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass
new file mode 100644
index 0000000000..96e29fc15c
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,1EFAFB79DD5E78C98C5A2204D6747AF8
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass.pub b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass.pub
new file mode 100644
index 0000000000..3c3c16feeb
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/resources/org/eclipse/jgit/junit/ssh/id_rsa_4096_testpass.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDNtJeBHx0z8RTfpPaUApShGWiMqyk+EpV8O3s3emsYBzYvoA/TLcbUcsuAo8rb1LQLJ1yrKXr3hJP6672pjMMtCUVAdvEgbvo86YdeP3ZsXjuUlRE3GLvyGgQOdla0MbSuxQIEPUjjP61eNZXzWm/cGpWDmKVHq6o2C/dHNePaMvpD7fPBfF4zDuMndO66SF/nSGXFuUfhsEg4u5nJvi3xHgTJeasA+6kdpRlKLBvFoOtX6yB27/sbx2fIHR1UvcBQ1QSntauHY97CeOBRw1BCbIm4H3zAQ/f4atOyS2LGMgSYJ17+B8OyXknEHPNtQW2VDwlU6ef0kJDZTftxGCCxbfKiMXEYJT5slpfPTNKA+Z9H+tPuheAMpHvolnZVJMa4AWs9x1HISs+Dg8LoW1bSY3GfZLpErbyzrGlMPLAQO7wROGUohcac31cNfbqBjDnpmWYBolDopA/8XQCR/ydEBHOQ8m0hrbWIGtQdFcsXtgqcHAvIJSBMDdZIOA10/0Zrv7okLmAY9bkoYgqgrtJlj0vE2hXrqKCWnScYQzbtlTWtrCWXBwkDORtJpWIP+++siK7PTizj7kr6sCF7wJThRMFZQmBS+1NJXt39IF8UuxAkj9bXWf35IIhsELH2c437GQyQxBXKsw9agP6hhHH/BcYg7aqSQ956AbJvygpiw== testuser
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java
new file mode 100644
index 0000000000..2d284cf1d5
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java
@@ -0,0 +1,813 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.junit.ssh;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.jgit.api.errors.TransportException;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theory;
+
+/**
+ * The ssh tests. Concrete subclasses can re-use these tests by implementing the
+ * abstract operations from {@link SshTestHarness}. This gives a way to test
+ * different ssh clients against a unified test suite.
+ */
+public abstract class SshTestBase extends SshTestHarness {
+
+ @DataPoints
+ public static String[] KEY_RESOURCES = { //
+ "id_dsa", //
+ "id_rsa_1024", //
+ "id_rsa_2048", //
+ "id_rsa_3072", //
+ "id_rsa_4096", //
+ "id_ecdsa_256", //
+ "id_ecdsa_384", //
+ "id_ecdsa_521", //
+ "id_ed25519", //
+ // And now encrypted. Passphrase is "testpass".
+ "id_dsa_testpass", //
+ "id_rsa_1024_testpass", //
+ "id_rsa_2048_testpass", //
+ "id_rsa_3072_testpass", //
+ "id_rsa_4096_testpass", //
+ "id_ecdsa_256_testpass", //
+ "id_ecdsa_384_testpass", //
+ "id_ecdsa_521_testpass", //
+ "id_ed25519_testpass", //
+ "id_ed25519_expensive_testpass" };
+
+ protected File defaultCloneDir;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ defaultCloneDir = new File(getTemporaryDirectory(), "cloned");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithoutConfig() throws Exception {
+ cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter", defaultCloneDir, null);
+ }
+
+ @Test
+ public void testSshWithGlobalIdentity() throws Exception {
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ defaultCloneDir, null,
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithDefaultIdentity() throws Exception {
+ File idRsa = new File(privateKey1.getParentFile(), "id_rsa");
+ Files.copy(privateKey1.toPath(), idRsa.toPath());
+ // We expect the session factory to pick up these keys...
+ cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter", defaultCloneDir, null);
+ }
+
+ @Test
+ public void testSshWithConfig() throws Exception {
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithConfigEncryptedUnusedKey() throws Exception {
+ // Copy the encrypted test key from the bundle.
+ File encryptedKey = new File(sshDir, "id_dsa");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ assertEquals("CredentialsProvider should not have been called", 0,
+ provider.getLog().size());
+ }
+
+ @Test
+ public void testSshWithConfigEncryptedUnusedKeyInConfigLast()
+ throws Exception {
+ // Copy the encrypted test key from the bundle.
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(),
+ "IdentityFile " + encryptedKey.getAbsolutePath());
+ // This test passes with JSch per chance because JSch completely ignores
+ // the second IdentityFile
+ assertEquals("CredentialsProvider should not have been called", 0,
+ provider.getLog().size());
+ }
+
+ private boolean isJsch() {
+ return getSessionFactory().getType().equals("jsch");
+ }
+
+ @Test
+ public void testSshWithConfigEncryptedUnusedKeyInConfigFirst()
+ throws Exception {
+ // Test cannot pass with JSch; it handles only one IdentityFile.
+ // assumeTrue(!(getSessionFactory() instanceof
+ // JschConfigSessionFactory)); gives in bazel a failure with "Never
+ // found parameters that satisfied method assumptions."
+ // In maven it's fine!?
+ if (isJsch()) {
+ return;
+ }
+ // Copy the encrypted test key from the bundle.
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + encryptedKey.getAbsolutePath(),
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ assertEquals("CredentialsProvider should have been called once", 1,
+ provider.getLog().size());
+ }
+
+ @Test
+ public void testSshEncryptedUsedKeyCached() throws Exception {
+ // Make sure we are asked for the password only once if we do several
+ // operations with an encrypted key.
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
+ copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
+ server.setTestUserPublicKey(encryptedPublicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ pushTo(provider,
+ cloneWith("ssh://localhost/doesntmatter", //
+ defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + encryptedKey.getAbsolutePath()));
+ assertEquals("CredentialsProvider should have been called once", 1,
+ provider.getLog().size());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshEncryptedUsedKeyWrongPassword() throws Exception {
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
+ copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
+ server.setTestUserPublicKey(encryptedPublicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass");
+ cloneWith("ssh://localhost/doesntmatter", //
+ defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "NumberOfPasswordPrompts 1", //
+ "IdentityFile " + encryptedKey.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshEncryptedUsedKeySeveralPassword() throws Exception {
+ File encryptedKey = new File(sshDir, "id_dsa_test_key");
+ copyTestResource("id_dsa_testpass", encryptedKey);
+ File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
+ copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
+ server.setTestUserPublicKey(encryptedPublicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass2", "testpass");
+ cloneWith("ssh://localhost/doesntmatter", //
+ defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + encryptedKey.getAbsolutePath());
+ assertEquals("CredentialsProvider should have been called 3 times", 3,
+ provider.getLog().size());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithoutKnownHosts() throws Exception {
+ assertTrue("Could not delete known_hosts", knownHosts.delete());
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithoutKnownHostsWithProviderAsk()
+ throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ // The provider will answer "yes" to all questions, so we should be able
+ // to connect and end up with a new known_hosts file with the host key.
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ List<LogEntry> messages = provider.getLog();
+ assertFalse("Expected user interaction", messages.isEmpty());
+ if (isJsch()) {
+ // JSch doesn't create a non-existing file.
+ assertEquals("Expected to be asked about the key", 1,
+ messages.size());
+ return;
+ }
+ assertEquals(
+ "Expected to be asked about the key, and the file creation",
+ 2, messages.size());
+ assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
+ // Instead of checking the file contents, let's just clone again
+ // without provider. If it works, the server host key was written
+ // correctly.
+ File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
+ cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithoutKnownHostsWithProviderAcceptNew()
+ throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking accept-new", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ if (isJsch()) {
+ // JSch doesn't create new files.
+ assertTrue("CredentialsProvider not called",
+ provider.getLog().isEmpty());
+ return;
+ }
+ assertEquals("Expected to be asked about the file creation", 1,
+ provider.getLog().size());
+ assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
+ // Instead of checking the file contents, let's just clone again
+ // without provider. If it works, the server host key was written
+ // correctly.
+ File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
+ cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithoutKnownHostsDeny() throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking yes", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshModifiedHostKeyDeny()
+ throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ // Now produce a new known_hosts file containing some other key.
+ createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking yes", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshModifiedHostKeyWithProviderDeny() throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ // Now produce a new known_hosts file containing some other key.
+ createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ try {
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking yes", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ } catch (Exception e) {
+ assertEquals("Expected to be told about the modified key", 1,
+ provider.getLog().size());
+ assertTrue("Only messages expected", provider.getLog().stream()
+ .flatMap(l -> l.getItems().stream()).allMatch(
+ c -> c instanceof CredentialItem.InformationalMessage));
+ throw e;
+ }
+ }
+
+ private void checkKnownHostsModifiedHostKey(File backup, File newFile,
+ String wrongKey) throws IOException {
+ List<String> oldLines = Files.readAllLines(backup.toPath(), UTF_8);
+ // Find the original entry. We should have that again in known_hosts.
+ String oldKeyPart = null;
+ for (String oldLine : oldLines) {
+ if (oldLine.contains("[localhost]:")) {
+ String[] parts = oldLine.split("\\s+");
+ if (parts.length > 2) {
+ oldKeyPart = parts[parts.length - 2] + ' '
+ + parts[parts.length - 1];
+ break;
+ }
+ }
+ }
+ assertNotNull("Old key not found", oldKeyPart);
+ List<String> newLines = Files.readAllLines(newFile.toPath(), UTF_8);
+ assertFalse("Old host key still found in known_hosts file" + newFile,
+ hasHostKey("localhost", testPort, wrongKey, newLines));
+ assertTrue("New host key not found in known_hosts file" + newFile,
+ hasHostKey("localhost", testPort, oldKeyPart, newLines));
+
+ }
+
+ @Test
+ public void testSshModifiedHostKeyAllow() throws Exception {
+ assertTrue("Failed to delete known_hosts", knownHosts.delete());
+ createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
+ File backup = new File(getTemporaryDirectory(), "backupKnownHosts");
+ Files.copy(knownHosts.toPath(), backup.toPath());
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "StrictHostKeyChecking no", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ // File should not have been updated!
+ String[] oldLines = Files
+ .readAllLines(backup.toPath(), UTF_8)
+ .toArray(new String[0]);
+ String[] newLines = Files
+ .readAllLines(knownHosts.toPath(), UTF_8)
+ .toArray(new String[0]);
+ assertArrayEquals("Known hosts file should not be modified", oldLines,
+ newLines);
+ }
+
+ @Test
+ public void testSshModifiedHostKeyAsk() throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ String wrongKeyPart = createKnownHostsFile(knownHosts, "localhost",
+ testPort, publicKey1);
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ checkKnownHostsModifiedHostKey(copiedHosts, knownHosts, wrongKeyPart);
+ assertEquals("Expected to be asked about the modified key", 1,
+ provider.getLog().size());
+ }
+
+ @Test
+ public void testSshCloneWithConfigAndPush() throws Exception {
+ pushTo(cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath()));
+ }
+
+ @Test
+ public void testSftpWithConfig() throws Exception {
+ cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSftpCloneWithConfigAndPush() throws Exception {
+ pushTo(cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath()));
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithConfigWrongKey() throws Exception {
+ cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey2.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithWrongUserNameInConfig() throws Exception {
+ // Bug 526778
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "User sombody_else", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithWrongPortInConfig() throws Exception {
+ // Bug 526778
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ defaultCloneDir, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port 22", //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithAliasInConfig() throws Exception {
+ // Bug 531118
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), "", //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port 22", //
+ "User someone_else", //
+ "IdentityFile " + privateKey2.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshWithUnknownCiphersInConfig() throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
+ }
+
+ @Test
+ public void testSshWithUnknownHostKeyAlgorithmsInConfig()
+ throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "HostKeyAlgorithms foobar,ssh-rsa,ssh-dss");
+ }
+
+ @Test
+ public void testSshWithUnknownKexAlgorithmsInConfig()
+ throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "KexAlgorithms foobar,diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
+ }
+
+ @Test
+ public void testSshWithMinimalHostKeyAlgorithmsInConfig()
+ throws Exception {
+ // Bug 537790
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "HostKeyAlgorithms ssh-rsa,ssh-dss");
+ }
+
+ @Test
+ public void testSshWithUnknownAuthInConfig() throws Exception {
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "PreferredAuthentications gssapi-with-mic,hostbased,publickey,keyboard-interactive,password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshWithNoMatchingAuthInConfig() throws Exception {
+ // Server doesn't do password, and anyway we set no password.
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "PreferredAuthentications password");
+ }
+
+ @Test
+ public void testRsaHostKeySecond() throws Exception {
+ // See https://git.eclipse.org/r/#/c/130402/ : server has EcDSA
+ // (preferred), RSA, we have RSA in known_hosts: client and server
+ // should agree on RSA.
+ File newHostKey = new File(getTemporaryDirectory(), "newhostkey");
+ copyTestResource("id_ecdsa_256", newHostKey);
+ server.addHostKey(newHostKey.toPath(), true);
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testEcDsaHostKey() throws Exception {
+ // See https://git.eclipse.org/r/#/c/130402/ : server has RSA
+ // (preferred), EcDSA, we have EcDSA in known_hosts: client and server
+ // should agree on EcDSA.
+ File newHostKey = new File(getTemporaryDirectory(), "newhostkey");
+ copyTestResource("id_ecdsa_256", newHostKey);
+ server.addHostKey(newHostKey.toPath(), false);
+ File newHostKeyPub = new File(getTemporaryDirectory(),
+ "newhostkey.pub");
+ copyTestResource("id_ecdsa_256.pub", newHostKeyPub);
+ createKnownHostsFile(knownHosts, "localhost", testPort, newHostKeyPub);
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testPasswordAuth() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test
+ public void testPasswordAuthSeveralTimes() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testPasswordAuthWrongPassword() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass");
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testPasswordAuthNoPassword() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testPasswordAuthCorrectPasswordTooLate() throws Exception {
+ server.enablePasswordAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", "wrongpass",
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications password");
+ }
+
+ @Test
+ public void testKeyboardInteractiveAuth() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test
+ public void testKeyboardInteractiveAuthSeveralTimes() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testKeyboardInteractiveAuthWrongPassword() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass");
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testKeyboardInteractiveAuthNoPassword() throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Test(expected = TransportException.class)
+ public void testKeyboardInteractiveAuthCorrectPasswordTooLate()
+ throws Exception {
+ server.enableKeyboardInteractiveAuthentication();
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "wrongpass", "wrongpass", "wrongpass",
+ TEST_USER.toUpperCase(Locale.ROOT));
+ cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "PreferredAuthentications keyboard-interactive");
+ }
+
+ @Theory
+ public void testSshKeys(String keyName) throws Exception {
+ // JSch fails on ECDSA 384/521 keys. Compare
+ // https://sourceforge.net/p/jsch/patches/10/
+ assumeTrue(!(isJsch() && (keyName.contains("ed25519")
+ || keyName.startsWith("id_ecdsa_384")
+ || keyName.startsWith("id_ecdsa_521"))));
+ File cloned = new File(getTemporaryDirectory(), "cloned");
+ String keyFileName = keyName + "_key";
+ File privateKey = new File(sshDir, keyFileName);
+ copyTestResource(keyName, privateKey);
+ File publicKey = new File(sshDir, keyFileName + ".pub");
+ copyTestResource(keyName + ".pub", publicKey);
+ server.setTestUserPublicKey(publicKey.toPath());
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ pushTo(provider,
+ cloneWith("ssh://localhost/doesntmatter", //
+ cloned, provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey.getAbsolutePath()));
+ int expectedCalls = keyName.endsWith("testpass") ? 1 : 0;
+ assertEquals("Unexpected calls to CredentialsProvider", expectedCalls,
+ provider.getLog().size());
+ // Should now also work without credentials provider, even if the key
+ // was encrypted.
+ cloned = new File(getTemporaryDirectory(), "cloned2");
+ pushTo(null,
+ cloneWith("ssh://localhost/doesntmatter", //
+ cloned, null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey.getAbsolutePath()));
+ }
+}
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
new file mode 100644
index 0000000000..43f9dc4b24
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.junit.ssh;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jgit.api.CloneCommand;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.PushCommand;
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+import org.junit.After;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.KeyPair;
+
+/**
+ * Root class for ssh tests. Sets up the ssh test server. A set of pre-computed
+ * keys for testing is provided in the bundle and can be used in test cases via
+ * {@link #copyTestResource(String, File)}. These test key files names have four
+ * components, separated by a single underscore: "id", the algorithm, the bits
+ * (if variable), and the password if the private key is encrypted. For instance
+ * "{@code id_ecdsa_384_testpass}" is an encrypted ECDSA-384 key. The passphrase
+ * to decrypt is "testpass". The key "{@code id_ecdsa_384}" is the same but
+ * unencrypted. All keys were generated and encrypted via ssh-keygen. Note that
+ * DSA and ec25519 have no "bits" component. Available keys are listed in
+ * {@link SshTestBase#KEY_RESOURCES}.
+ */
+public abstract class SshTestHarness extends RepositoryTestCase {
+
+ protected static final String TEST_USER = "testuser";
+
+ protected File sshDir;
+
+ protected File privateKey1;
+
+ protected File privateKey2;
+
+ protected File publicKey1;
+
+ protected SshTestGitServer server;
+
+ private SshSessionFactory factory;
+
+ protected int testPort;
+
+ protected File knownHosts;
+
+ private File homeDir;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ writeTrashFile("file.txt", "something");
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("Initial commit").call();
+ }
+ mockSystemReader.setProperty("user.home",
+ getTemporaryDirectory().getAbsolutePath());
+ mockSystemReader.setProperty("HOME",
+ getTemporaryDirectory().getAbsolutePath());
+ homeDir = FS.DETECTED.userHome();
+ FS.DETECTED.setUserHome(getTemporaryDirectory().getAbsoluteFile());
+ sshDir = new File(getTemporaryDirectory(), ".ssh");
+ assertTrue(sshDir.mkdir());
+ File serverDir = new File(getTemporaryDirectory(), "srv");
+ assertTrue(serverDir.mkdir());
+ // Create two key pairs. Let's not call them "id_rsa".
+ privateKey1 = new File(sshDir, "first_key");
+ privateKey2 = new File(sshDir, "second_key");
+ publicKey1 = createKeyPair(privateKey1);
+ createKeyPair(privateKey2);
+ ByteArrayOutputStream publicHostKey = new ByteArrayOutputStream();
+ // Start a server with our test user and the first key.
+ server = new SshTestGitServer(TEST_USER, publicKey1.toPath(), db,
+ createHostKey(publicHostKey));
+ testPort = server.start();
+ assertTrue(testPort > 0);
+ knownHosts = new File(sshDir, "known_hosts");
+ Files.write(knownHosts.toPath(), Collections.singleton("[localhost]:"
+ + testPort + ' '
+ + publicHostKey.toString(US_ASCII.name())));
+ factory = createSessionFactory();
+ SshSessionFactory.setInstance(factory);
+ }
+
+ private static File createKeyPair(File privateKeyFile) throws Exception {
+ // Found no way to do this with MINA sshd except rolling it all
+ // ourselves...
+ JSch jsch = new JSch();
+ KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
+ try (OutputStream out = new FileOutputStream(privateKeyFile)) {
+ pair.writePrivateKey(out);
+ }
+ File publicKeyFile = new File(privateKeyFile.getParentFile(),
+ privateKeyFile.getName() + ".pub");
+ try (OutputStream out = new FileOutputStream(publicKeyFile)) {
+ pair.writePublicKey(out, TEST_USER);
+ }
+ return publicKeyFile;
+ }
+
+ private static byte[] createHostKey(OutputStream publicKey)
+ throws Exception {
+ JSch jsch = new JSch();
+ KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
+ pair.writePublicKey(publicKey, "");
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ pair.writePrivateKey(out);
+ out.flush();
+ return out.toByteArray();
+ }
+ }
+
+ /**
+ * Creates a new known_hosts file with one entry for the given host and port
+ * taken from the given public key file.
+ *
+ * @param file
+ * to write the known_hosts file to
+ * @param host
+ * for the entry
+ * @param port
+ * for the entry
+ * @param publicKey
+ * to use
+ * @return the public-key part of the line
+ * @throws IOException
+ */
+ protected static String createKnownHostsFile(File file, String host,
+ int port, File publicKey) throws IOException {
+ List<String> lines = Files.readAllLines(publicKey.toPath(), UTF_8);
+ assertEquals("Public key has too many lines", 1, lines.size());
+ String pubKey = lines.get(0);
+ // Strip off the comment.
+ String[] parts = pubKey.split("\\s+");
+ assertTrue("Unexpected key content",
+ parts.length == 2 || parts.length == 3);
+ String keyPart = parts[0] + ' ' + parts[1];
+ String line = '[' + host + "]:" + port + ' ' + keyPart;
+ Files.write(file.toPath(), Collections.singletonList(line));
+ return keyPart;
+ }
+
+ /**
+ * Checks whether there is a line for the given host and port that also
+ * matches the given key part in the list of lines.
+ *
+ * @param host
+ * to look for
+ * @param port
+ * to look for
+ * @param keyPart
+ * to look for
+ * @param lines
+ * to look in
+ * @return {@code true} if found, {@code false} otherwise
+ */
+ protected boolean hasHostKey(String host, int port, String keyPart,
+ List<String> lines) {
+ String h = '[' + host + "]:" + port;
+ return lines.stream()
+ .anyMatch(l -> l.contains(h) && l.contains(keyPart));
+ }
+
+ @After
+ public void shutdownServer() throws Exception {
+ if (server != null) {
+ server.stop();
+ server = null;
+ }
+ FS.DETECTED.setUserHome(homeDir);
+ SshSessionFactory.setInstance(null);
+ factory = null;
+ }
+
+ protected abstract SshSessionFactory createSessionFactory();
+
+ protected SshSessionFactory getSessionFactory() {
+ return factory;
+ }
+
+ protected abstract void installConfig(String... config);
+
+ /**
+ * Copies a test data file contained in the test bundle to the given file.
+ * Equivalent to {@link #copyTestResource(Class, String, File)} with
+ * {@code SshTestHarness.class} as first parameter.
+ *
+ * @param resourceName
+ * of the test resource to copy
+ * @param to
+ * file to copy the resource to
+ * @throws IOException
+ * if the resource cannot be copied
+ */
+ protected void copyTestResource(String resourceName, File to)
+ throws IOException {
+ copyTestResource(SshTestHarness.class, resourceName, to);
+ }
+
+ /**
+ * Copies a test data file contained in the test bundle to the given file,
+ * using {@link Class#getResourceAsStream(String)} to get the test resource.
+ *
+ * @param loader
+ * {@link Class} to use to load the resource
+ * @param resourceName
+ * of the test resource to copy
+ * @param to
+ * file to copy the resource to
+ * @throws IOException
+ * if the resource cannot be copied
+ */
+ protected void copyTestResource(Class<?> loader, String resourceName,
+ File to) throws IOException {
+ try (InputStream in = loader.getResourceAsStream(resourceName)) {
+ Files.copy(in, to.toPath());
+ }
+ }
+
+ protected File cloneWith(String uri, File to, CredentialsProvider provider,
+ String... config) throws Exception {
+ installConfig(config);
+ CloneCommand clone = Git.cloneRepository().setCloneAllBranches(true)
+ .setDirectory(to).setURI(uri);
+ if (provider != null) {
+ clone.setCredentialsProvider(provider);
+ }
+ try (Git git = clone.call()) {
+ Repository repo = git.getRepository();
+ assertNotNull(repo.resolve("master"));
+ assertNotEquals(db.getWorkTree(),
+ git.getRepository().getWorkTree());
+ assertTrue(new File(git.getRepository().getWorkTree(), "file.txt")
+ .exists());
+ return repo.getWorkTree();
+ }
+ }
+
+ protected void pushTo(File localClone) throws Exception {
+ pushTo(null, localClone);
+ }
+
+ protected void pushTo(CredentialsProvider provider, File localClone)
+ throws Exception {
+ RevCommit commit;
+ File newFile = null;
+ try (Git git = Git.open(localClone)) {
+ // Write a new file and modify a file.
+ Repository local = git.getRepository();
+ newFile = File.createTempFile("new", "sshtest",
+ local.getWorkTree());
+ write(newFile, "something new");
+ File existingFile = new File(local.getWorkTree(), "file.txt");
+ write(existingFile, "something else");
+ git.add().addFilepattern("file.txt")
+ .addFilepattern(newFile.getName())
+ .call();
+ commit = git.commit().setMessage("Local commit").call();
+ // Push
+ PushCommand push = git.push().setPushAll();
+ if (provider != null) {
+ push.setCredentialsProvider(provider);
+ }
+ Iterable<PushResult> results = push.call();
+ for (PushResult result : results) {
+ for (RemoteRefUpdate u : result.getRemoteUpdates()) {
+ assertEquals(
+ "Could not update " + u.getRemoteName() + ' '
+ + u.getMessage(),
+ RemoteRefUpdate.Status.OK, u.getStatus());
+ }
+ }
+ }
+ // Now check "master" in the remote repo directly:
+ assertEquals("Unexpected remote commit", commit, db.resolve("master"));
+ assertEquals("Unexpected remote commit", commit,
+ db.resolve(Constants.HEAD));
+ File remoteFile = new File(db.getWorkTree(), newFile.getName());
+ assertFalse("File should not exist on remote", remoteFile.exists());
+ try (Git git = new Git(db)) {
+ git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call();
+ }
+ assertTrue("File does not exist on remote", remoteFile.exists());
+ checkFile(remoteFile, "something new");
+ }
+
+ protected static class TestCredentialsProvider extends CredentialsProvider {
+
+ private final List<String> stringStore;
+
+ private final Iterator<String> strings;
+
+ public TestCredentialsProvider(String... strings) {
+ if (strings == null || strings.length == 0) {
+ stringStore = Collections.emptyList();
+ } else {
+ stringStore = Arrays.asList(strings);
+ }
+ this.strings = stringStore.iterator();
+ }
+
+ @Override
+ public boolean isInteractive() {
+ return true;
+ }
+
+ @Override
+ public boolean supports(CredentialItem... items) {
+ return true;
+ }
+
+ @Override
+ public boolean get(URIish uri, CredentialItem... items)
+ throws UnsupportedCredentialItem {
+ System.out.println("URI: " + uri);
+ for (CredentialItem item : items) {
+ System.out.println(item.getClass().getSimpleName() + ' '
+ + item.getPromptText());
+ }
+ logItems(uri, items);
+ for (CredentialItem item : items) {
+ if (item instanceof CredentialItem.InformationalMessage) {
+ continue;
+ }
+ if (item instanceof CredentialItem.YesNoType) {
+ ((CredentialItem.YesNoType) item).setValue(true);
+ } else if (item instanceof CredentialItem.CharArrayType) {
+ if (strings.hasNext()) {
+ ((CredentialItem.CharArrayType) item)
+ .setValue(strings.next().toCharArray());
+ } else {
+ return false;
+ }
+ } else if (item instanceof CredentialItem.StringType) {
+ if (strings.hasNext()) {
+ ((CredentialItem.StringType) item)
+ .setValue(strings.next());
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private List<LogEntry> log = new ArrayList<>();
+
+ private void logItems(URIish uri, CredentialItem... items) {
+ log.add(new LogEntry(uri, Arrays.asList(items)));
+ }
+
+ public List<LogEntry> getLog() {
+ return log;
+ }
+ }
+
+ protected static class LogEntry {
+
+ private URIish uri;
+
+ private List<CredentialItem> items;
+
+ public LogEntry(URIish uri, List<CredentialItem> items) {
+ this.uri = uri;
+ this.items = items;
+ }
+
+ public URIish getURIish() {
+ return uri;
+ }
+
+ public List<CredentialItem> getItems() {
+ return items;
+ }
+ }
+}