Merge changes from topic "sshd"

* changes:
  sshd: use PropertyResolver in test
  Remove dependency on JSch from SSH test framework
diff --git a/org.eclipse.jgit.junit.ssh/BUILD b/org.eclipse.jgit.junit.ssh/BUILD
index 61b5ce7..50142fe 100644
--- a/org.eclipse.jgit.junit.ssh/BUILD
+++ b/org.eclipse.jgit.junit.ssh/BUILD
@@ -13,7 +13,6 @@
         "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
     ],
     deps = [
-        "//lib:jsch",
         "//lib:junit",
         "//lib:sshd-osgi",
         "//lib:sshd-sftp",
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 77f65e4..2bf9c1f 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -8,8 +8,7 @@
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: com.jcraft.jsch;version="0.1.55",
- org.apache.sshd.common;version="[2.4.0,2.5.0)",
+Import-Package: 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)",
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index c4dae96..e1d35ce 100644
--- a/org.eclipse.jgit.junit.ssh/pom.xml
+++ b/org.eclipse.jgit.junit.ssh/pom.xml
@@ -58,16 +58,6 @@
     </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>
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
index 9aa4afc..250a138 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
@@ -22,9 +22,9 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
@@ -91,8 +91,7 @@
 	 * @param testUser
 	 *            user name of the test user
 	 * @param testKey
-	 *            <em>private</em> key file of the test user; the server will
-	 *            only user the public key from it
+	 *            public key file of the test user
 	 * @param repository
 	 *            to serve
 	 * @param hostKey
@@ -103,17 +102,54 @@
 	public SshTestGitServer(@NonNull String testUser, @NonNull Path testKey,
 			@NonNull Repository repository, @NonNull byte[] hostKey)
 			throws IOException, GeneralSecurityException {
+		this(testUser, readPublicKey(testKey), repository,
+				readKeyPair(hostKey));
+	}
+
+	/**
+	 * Creates a ssh git <em>test</em> server. It serves one single repository,
+	 * and accepts public-key authentication for exactly one test user.
+	 *
+	 * @param testUser
+	 *            user name of the test user
+	 * @param testKey
+	 *            public key file of the test user
+	 * @param repository
+	 *            to serve
+	 * @param hostKey
+	 *            the unencrypted private key to use as host key
+	 * @throws IOException
+	 * @throws GeneralSecurityException
+	 * @since 5.9
+	 */
+	public SshTestGitServer(@NonNull String testUser, @NonNull Path testKey,
+			@NonNull Repository repository, @NonNull KeyPair hostKey)
+			throws IOException, GeneralSecurityException {
+		this(testUser, readPublicKey(testKey), repository, hostKey);
+	}
+
+	/**
+	 * Creates a ssh git <em>test</em> server. It serves one single repository,
+	 * and accepts public-key authentication for exactly one test user.
+	 *
+	 * @param testUser
+	 *            user name of the test user
+	 * @param testKey
+	 *            the {@link PublicKey} of the test user
+	 * @param repository
+	 *            to serve
+	 * @param hostKey
+	 *            the {@link KeyPair} to use as host key
+	 * @since 5.9
+	 */
+	public SshTestGitServer(@NonNull String testUser,
+			@NonNull PublicKey testKey, @NonNull Repository repository,
+			@NonNull KeyPair hostKey) {
 		this.testUser = testUser;
 		setTestUserPublicKey(testKey);
 		this.repository = repository;
 		server = SshServer.setUpDefaultServer();
-		// Set host key
-		try (ByteArrayInputStream in = new ByteArrayInputStream(hostKey)) {
-			SecurityUtils.loadKeyPairIdentities(null, null, in, null)
-					.forEach((k) -> hostKeys.add(k));
-		} catch (IOException | GeneralSecurityException e) {
-			// Ignore.
-		}
+		hostKeys.add(hostKey);
 		server.setKeyPairProvider((session) -> hostKeys);
 
 		configureAuthentication();
@@ -135,6 +171,20 @@
 		});
 	}
 
+	private static PublicKey readPublicKey(Path key)
+			throws IOException, GeneralSecurityException {
+		return AuthorizedKeyEntry.readAuthorizedKeys(key).get(0)
+				.resolvePublicKey(null, PublicKeyEntryResolver.IGNORING);
+	}
+
+	private static KeyPair readKeyPair(byte[] keyMaterial)
+			throws IOException, GeneralSecurityException {
+		try (ByteArrayInputStream in = new ByteArrayInputStream(keyMaterial)) {
+			return SecurityUtils.loadKeyPairIdentities(null, null, in, null)
+					.iterator().next();
+		}
+	}
+
 	private static class FakeUserAuthGSS extends UserAuthGSS {
 		@Override
 		protected Boolean doAuth(Buffer buffer, boolean initial)
@@ -299,14 +349,14 @@
 	}
 
 	/**
-	 * Retrieves the server's property map. This is a live map; changing it
-	 * affects the server.
+	 * Retrieves the server's {@link PropertyResolver}, giving access to server
+	 * properties.
 	 *
-	 * @return a live map of the server's properties
+	 * @return the {@link PropertyResolver}
 	 * @since 5.9
 	 */
-	public Map<String, Object> getProperties() {
-		return server.getProperties();
+	public PropertyResolver getPropertyResolver() {
+		return server;
 	}
 
 	/**
@@ -343,8 +393,7 @@
 	 */
 	public void setTestUserPublicKey(Path key)
 			throws IOException, GeneralSecurityException {
-		this.testKey = AuthorizedKeyEntry.readAuthorizedKeys(key).get(0)
-				.resolvePublicKey(null, PublicKeyEntryResolver.IGNORING);
+		this.testKey = readPublicKey(key);
 	}
 
 	/**
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
index 43f9dc4..7970685 100644
--- 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2020 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
@@ -9,27 +9,31 @@
  */
 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.BufferedWriter;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
 import org.eclipse.jgit.api.CloneCommand;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.PushCommand;
@@ -48,9 +52,6 @@
 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
@@ -104,50 +105,71 @@
 		File serverDir = new File(getTemporaryDirectory(), "srv");
 		assertTrue(serverDir.mkdir());
 		// Create two key pairs. Let's not call them "id_rsa".
+		KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
+		generator.initialize(2048);
 		privateKey1 = new File(sshDir, "first_key");
 		privateKey2 = new File(sshDir, "second_key");
-		publicKey1 = createKeyPair(privateKey1);
-		createKeyPair(privateKey2);
-		ByteArrayOutputStream publicHostKey = new ByteArrayOutputStream();
+		publicKey1 = createKeyPair(generator.generateKeyPair(), privateKey1);
+		createKeyPair(generator.generateKeyPair(), privateKey2);
+		// Create a host key
+		KeyPair hostKey = generator.generateKeyPair();
 		// Start a server with our test user and the first key.
 		server = new SshTestGitServer(TEST_USER, publicKey1.toPath(), db,
-				createHostKey(publicHostKey));
+				hostKey);
 		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())));
+		StringBuilder knownHostsLine = new StringBuilder();
+		knownHostsLine.append("[localhost]:").append(testPort).append(' ');
+		PublicKeyEntry.appendPublicKeyEntry(knownHostsLine,
+				hostKey.getPublic());
+		Files.write(knownHosts.toPath(),
+				Collections.singleton(knownHostsLine.toString()));
 		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);
+	private static File createKeyPair(KeyPair newKey, File privateKeyFile)
+			throws Exception {
+		// Write PKCS#8 PEM unencrypted. Both JSch and sshd can read that.
+		PrivateKey privateKey = newKey.getPrivate();
+		String format = privateKey.getFormat();
+		if (!"PKCS#8".equalsIgnoreCase(format)) {
+			throw new IOException("Cannot write " + privateKey.getAlgorithm()
+					+ " key in " + format + " format");
+		}
+		try (BufferedWriter writer = Files.newBufferedWriter(
+				privateKeyFile.toPath(), StandardCharsets.US_ASCII)) {
+			writer.write("-----BEGIN PRIVATE KEY-----");
+			writer.newLine();
+			write(writer, privateKey.getEncoded(), 64);
+			writer.write("-----END PRIVATE KEY-----");
+			writer.newLine();
 		}
 		File publicKeyFile = new File(privateKeyFile.getParentFile(),
 				privateKeyFile.getName() + ".pub");
+		StringBuilder builder = new StringBuilder();
+		PublicKeyEntry.appendPublicKeyEntry(builder, newKey.getPublic());
+		builder.append(' ').append(TEST_USER);
 		try (OutputStream out = new FileOutputStream(publicKeyFile)) {
-			pair.writePublicKey(out, TEST_USER);
+			out.write(builder.toString().getBytes(StandardCharsets.US_ASCII));
 		}
 		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();
+	private static void write(BufferedWriter out, byte[] bytes, int lineLength)
+			throws IOException {
+		String data = Base64.getEncoder().encodeToString(bytes);
+		int last = data.length();
+		for (int i = 0; i < last; i += lineLength) {
+			if (i + lineLength <= last) {
+				out.write(data.substring(i, i + lineLength));
+			} else {
+				out.write(data.substring(i));
+			}
+			out.newLine();
 		}
+		Arrays.fill(bytes, (byte) 0);
 	}
 
 	/**
@@ -167,7 +189,8 @@
 	 */
 	protected static String createKnownHostsFile(File file, String host,
 			int port, File publicKey) throws IOException {
-		List<String> lines = Files.readAllLines(publicKey.toPath(), UTF_8);
+		List<String> lines = Files.readAllLines(publicKey.toPath(),
+				StandardCharsets.UTF_8);
 		assertEquals("Public key has too many lines", 1, lines.size());
 		String pubKey = lines.get(0);
 		// Strip off the comment.
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
index a58ee2e..f27af6e 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
@@ -21,6 +21,7 @@
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.sshd.client.config.hosts.KnownHostEntry;
+import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.server.ServerFactoryManager;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.TransportException;
@@ -189,8 +190,8 @@
 	 */
 	@Test
 	public void testCloneAndFetchWithSessionLimit() throws Exception {
-		server.getProperties().put(ServerFactoryManager.MAX_CONCURRENT_SESSIONS,
-				Integer.valueOf(2));
+		PropertyResolverUtils.updateProperty(server.getPropertyResolver(),
+				ServerFactoryManager.MAX_CONCURRENT_SESSIONS, 2);
 		File localClone = cloneWith("ssh://localhost/doesntmatter",
 				defaultCloneDir, null, //
 				"Host localhost", //