diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2020-11-28 21:02:09 +0100 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2020-11-28 21:51:50 +0100 |
commit | 286ad23cb56ffeac77d4bfd03be575358fd5217c (patch) | |
tree | dc075c6b1d1e813f253ec465813b6251d9d07d61 /org.eclipse.jgit.junit.ssh | |
parent | 4887894ffd637030a311ca8d60b78515b1a5cf35 (diff) | |
parent | 4f2065d145c3fa3f8ad3de28af3ee5dfeb74c765 (diff) | |
download | jgit-286ad23cb56ffeac77d4bfd03be575358fd5217c.tar.gz jgit-286ad23cb56ffeac77d4bfd03be575358fd5217c.zip |
Merge branch 'master' into next
* master:
Remove unused imports
Silence API warnings
Remove erraneously merged source features
Add support for reading symrefs from pack capabilities
Prepare 5.3.9-SNAPSHOT builds
JGit v5.3.8.202011260953-r
Prepare 5.1.15-SNAPSHOT builds
JGit v5.1.14.202011251942-r
GC#deleteOrphans: log warning for deleted orphaned files
GC#deleteOrphans: handle failure to list files in pack directory
Ensure that GC#deleteOrphans respects pack lock
Prepare 5.10.0-SNAPSHOT builds
JGit v5.10.0.202011251205-m3
PacketLineIn: ensure that END != DELIM
Update Orbit to S20201118210000 and add target for 4.18
PacketLineIn: ensure that END != DELIM
PacketLineIn: ensure that END != DELIM
Allow to resolve a conflict by checking out a file
Update Orbit to I20201111205634
Document that setLastModified sets time of symlink target
Fix bug in PerformanceLogContext
Fix IOException occurring during gc
Prepare 5.10.0-SNAPSHOT builds
JGit v5.10.0.202011041322-m2
Revert "Client-side protocol V2 support for fetching"
Close Repository to fix tests failing on Windows
Client-side protocol V2 support for fetching
Update slf4j to 1.7.30
Update Orbit to S20201027182932 (2020-12 M2)
Fix formatting of config option values
Document options in core section supported by JGit
Ensure .gitmodules is loaded when accessing submodule name
Export new package org.eclipse.jgit.logging and import it where used
Ensure GC.deleteOrphans() can delete read-only orphaned files on Windows
Add new performance logging
Implement git describe --all
Compute time differences with Duration
Override config http.userAgent from environment GIT_HTTP_USER_AGENT
Upgrade spotbugs-maven-plugin to 4.1.3
Fix OperatorPrecedence warning flagged by error prone
UploadPackTest#testUploadRedundantBytes: ensure test repo is closed
ObjectDirectory#selectObjectRepresentation: fix formatting
Upgrade ecj to 3.23.0
Support "http.userAgent" and "http.extraHeader" from the git config
sshd: better error report when user cancels authentication
API filters for PackStatistics.Accumulator
Add TypedConfigGetter.getPath()
Make Javadoc consistent for PackStatistics fields
Measure time taken for reachability checks
Measure time taken for negotiation in protocol V2
IndexDiffFilter: handle path prefixes correctly
sshd: support the ProxyJump ssh config
Upgrade jacoco-maven-plugin to 0.8.6
ReceivePackStats: Add size and count of unnecessary pushed objects
Upgrade maven-project-info-reports-plugin to 3.1.1
Prepare 5.9.1-SNAPSHOT builds
JGit v5.9.0.202009080501-r
[releng] Enable japicmp for the fragments added in 5.8.0
GitlinkMergeTest: fix boxing warnings
Remove unused API problem filters
Add missing since tag on BundleWriter#addObjectsAsIs
SshdSession: close channel gracefully
GPG: include signer's user ID in the signature
jgit: Add DfsBundleWriter
Bump Bazel version to 3.5.0
Upgrade maven-resources-plugin to 3.2.0
Upgrade plexus-compiler version to 2.8.8
[bazel] Add missing dependency to slf4j-api
[errorprone] DirCacheEntry: make clear operator precedence
[errorprone] PackWriter#parallelDeltaSearch: avoid suppressed exception
[errorprone] Declare DirCache#version final
Add jgit-4.17-staging target platform for 2020-09
Update target platform to R20200831200620
Prepare 5.10.0-SNAPSHOT builds
Prepare 5.9.0-SNAPSHOT builds
ResolveMerger: do not content-merge gitlinks on del/mod conflicts
ResolveMerger: Adding test cases for GITLINK deletion
ResolveMerger: choose OURS on gitlink when ignoreConflicts
ResolveMerger: improving content merge readability
ResolveMerger: extracting createGitLinksMergeResult method
ResolveMerger: Adding test cases for GITLINK merge
JGit v5.9.0.202008260805-m3
Fix possible NegativeArraySizeException in PackIndexV1
FS: use binary search to determine filesystem timestamp resolution
Do not prematurely create directory of jgit's XDG config file
FS: write to JGit config in a background thread
FS: don't cache fallback if running in background
Keep line endings for text files committed with CR/LF on text=auto
Delay WindowCache statistics JMX MBean registration
[releng] Update plexus-compiler to 2.8.7
DirCache: support index V4
Update javadoc for RemoteSession and SshSessionFactory
Fix JSchProcess.waitFor() with time-out
sshd: work around a race condition in Apache MINA sshd 2.4.0/2.5.x
sshd: store per-session data on the sshd session object
FilterSpec: Use BigInteger.ZERO instead of valueOf(0)
Do not send empty blob in response to blob:none filter
Add support for tree filters when fetching
sshd: use PropertyResolver in test
FS_POSIX: avoid prompt to install the XCode tools on OS X
Remove dependency on JSch from SSH test framework
Use LinkedBlockingQueue for executor determining filesystem attributes
Update API warning filters
Remove unused imports
Bazel: Add workspace status command to stamp final artifact
DiffFormatter: correctly deal with tracked files in ignored folders
Prepare 5.8.2-SNAPSHOT builds
JGit v5.8.1.202007141445-r
Update Jetty to 9.4.30.v20200611
Fix writing GPG signatures with trailing newline
Rename a test method
Add a test for upstream bug SSHD-1028
Improve error message when receive.maxCommandBytes is exceeded
LfsConnectionFactory#getLfsUrl: Fix unconditional break in for-loop
DiffFormatterTest: Add a test to confirm the default rename detection settings
Upgrade maven-site-plugin to 3.9.1
Upgrade build-helper-maven-plugin to 3.2.0
Upgrade spotbugs to 4.0.4
MergedReftable: Include the last reftable in determining minUpdateIndex
Add new osgi fragments to maven-central deploy scripts
PackBitmapIndex: Not buffer inflated bitmap during bitmap creation.
Do not require org.assertj.core.annotations
Upgrade ecj to 3.22.0
Remove workaround for signing jars using Tycho plugins
Use https for URL of jgit website
Fix CI information in pom.xml
Use gitiles as scm url in pom.xml for browsing source code
Update API baseline to 5.8.0.202006091008-r
Remove trailing whitespace
Change-Id: Ie6bc6954741a47cfbd32c0886bdbd7b594f08b31
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit.junit.ssh')
6 files changed, 239 insertions, 60 deletions
diff --git a/org.eclipse.jgit.junit.ssh/BUILD b/org.eclipse.jgit.junit.ssh/BUILD index 61b5ce78cb..f7856b5273 100644 --- a/org.eclipse.jgit.junit.ssh/BUILD +++ b/org.eclipse.jgit.junit.ssh/BUILD @@ -13,8 +13,8 @@ java_library( "//org.eclipse.jgit.ssh.jsch.test:__pkg__", ], deps = [ - "//lib:jsch", "//lib:junit", + "//lib:slf4j-api", "//lib:sshd-osgi", "//lib:sshd-sftp", # We want these deps to be provided_deps diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF index 2b06d48047..8858c09cbd 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-Localization: plugin 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 cd4626037f..a6f466016a 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/SshTestBase.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestBase.java index 2d284cf1d5..3784741195 100644 --- 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 @@ -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 @@ -14,6 +14,7 @@ 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.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @@ -22,9 +23,14 @@ import java.io.IOException; import java.nio.file.Files; import java.util.List; import java.util.Locale; +import java.util.concurrent.TimeUnit; import org.eclipse.jgit.api.errors.TransportException; +import org.eclipse.jgit.errors.CommandFailedException; import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.SshSupport; import org.junit.Test; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theory; @@ -67,10 +73,45 @@ public abstract class SshTestBase extends SshTestHarness { defaultCloneDir = new File(getTemporaryDirectory(), "cloned"); } - @Test(expected = TransportException.class) + @Test public void testSshWithoutConfig() throws Exception { - cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort - + "/doesntmatter", defaultCloneDir, null); + assertThrows(TransportException.class, + () -> cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort + + "/doesntmatter", defaultCloneDir, null)); + } + + @Test + public void testSingleCommand() throws Exception { + installConfig("IdentityFile " + privateKey1.getAbsolutePath()); + String command = SshTestGitServer.ECHO_COMMAND + " 1 without timeout"; + long start = System.nanoTime(); + String reply = SshSupport.runSshCommand( + new URIish("ssh://" + TEST_USER + "@localhost:" + testPort), + null, FS.DETECTED, command, 0); // 0 == no timeout + long elapsed = System.nanoTime() - start; + assertEquals(command, reply); + // Now that we have an idea how long this takes on the test + // infrastructure, try again with a timeout. + command = SshTestGitServer.ECHO_COMMAND + " 1 expecting no timeout"; + // Still use a generous timeout. + int timeout = 10 * ((int) TimeUnit.NANOSECONDS.toSeconds(elapsed) + 1); + reply = SshSupport.runSshCommand( + new URIish("ssh://" + TEST_USER + "@localhost:" + testPort), + null, FS.DETECTED, command, timeout); + assertEquals(command, reply); + } + + @Test + public void testSingleCommandWithTimeoutExpired() throws Exception { + installConfig("IdentityFile " + privateKey1.getAbsolutePath()); + String command = SshTestGitServer.ECHO_COMMAND + " 2 EXPECTING TIMEOUT"; + + CommandFailedException e = assertThrows(CommandFailedException.class, + () -> SshSupport.runSshCommand(new URIish( + "ssh://" + TEST_USER + "@localhost:" + testPort), null, + FS.DETECTED, command, 1)); + assertTrue(e.getMessage().contains(command)); + assertTrue(e.getMessage().contains("time")); } @Test 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 03e2855829..ab8e0c1ca0 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 @@ -12,6 +12,8 @@ package org.eclipse.jgit.junit.ssh; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.security.GeneralSecurityException; @@ -22,8 +24,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.concurrent.TimeUnit; 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; @@ -66,6 +70,16 @@ import org.eclipse.jgit.transport.UploadPack; */ public class SshTestGitServer { + /** + * Simple echo test command. Replies with the command string as passed. If + * of the form "echo [int] anything", takes the integer value as a delay in + * seconds before replying, which may be useful to test various + * timeout-related things. + * + * @since 5.9 + */ + public static final String ECHO_COMMAND = "echo"; + @NonNull protected final String testUser; @@ -90,8 +104,7 @@ public class SshTestGitServer { * @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 @@ -102,17 +115,54 @@ public class SshTestGitServer { 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(); @@ -129,11 +179,27 @@ public class SshTestGitServer { return new GitUploadPackCommand(command, executorService); } else if (command.startsWith(RemoteConfig.DEFAULT_RECEIVE_PACK)) { return new GitReceivePackCommand(command, executorService); + } else if (command.startsWith(ECHO_COMMAND)) { + return new EchoCommand(command, executorService); } return new UnknownCommand(command); }); } + 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) @@ -298,6 +364,17 @@ public class SshTestGitServer { } /** + * Retrieves the server's {@link PropertyResolver}, giving access to server + * properties. + * + * @return the {@link PropertyResolver} + * @since 5.9 + */ + public PropertyResolver getPropertyResolver() { + return server; + } + + /** * Starts the test server, listening on a random port. * * @return the port the server listens on; test clients should connect to @@ -331,8 +408,7 @@ public class SshTestGitServer { */ public void setTestUserPublicKey(Path key) throws IOException, GeneralSecurityException { - this.testKey = AuthorizedKeyEntry.readAuthorizedKeys(key).get(0) - .resolvePublicKey(null, PublicKeyEntryResolver.IGNORING); + this.testKey = readPublicKey(key); } /** @@ -414,4 +490,52 @@ public class SshTestGitServer { } } + + /** + * Simple echo command that echoes back the command string. If the first + * argument is a positive integer, it's taken as a delay (in seconds) before + * replying. Assumes UTF-8 character encoding. + */ + private static class EchoCommand extends AbstractCommandSupport { + + protected EchoCommand(String command, + CloseableExecutorService executorService) { + super(command, ThreadUtils.noClose(executorService)); + } + + @Override + public void run() { + String[] parts = getCommand().split(" "); + int timeout = 0; + if (parts.length >= 2) { + try { + timeout = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + // No timeout. + } + if (timeout > 0) { + try { + Thread.sleep(TimeUnit.SECONDS.toMillis(timeout)); + } catch (InterruptedException e) { + // Ignore. + } + } + } + try { + doEcho(getCommand(), getOutputStream()); + onExit(0); + } catch (IOException e) { + log.warn( + MessageFormat.format("Could not run {0}", getCommand()), + e); + onExit(-1, e.toString()); + } + } + + private void doEcho(String text, OutputStream stream) + throws IOException { + stream.write(text.getBytes(StandardCharsets.UTF_8)); + stream.flush(); + } + } } 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 43f9dc4b24..90d981b772 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.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 @@ -75,6 +76,8 @@ public abstract class SshTestHarness extends RepositoryTestCase { protected File publicKey1; + protected File publicKey2; + protected SshTestGitServer server; private SshSessionFactory factory; @@ -104,50 +107,71 @@ public abstract class SshTestHarness extends RepositoryTestCase { 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); + publicKey2 = 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 +191,8 @@ public abstract class SshTestHarness extends RepositoryTestCase { */ 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. |