Compare commits

...

31 Commits

Author SHA1 Message Date
Jeroen van Erp
98063680bc Release version: 0.28.0 2020-05-01 11:30:34 +02:00
Jeroen van Erp
17754a65fe Update build.gradle for newer gradle versions 2020-04-16 09:38:04 +02:00
Jeroen van Erp
2bb52fcf7d Add checks for Channel.isOpen to ChannelOutputStream (Fixes #440) 2020-04-14 22:23:35 +02:00
Jeroen van Erp
1a70023e2d Fix task exclusion for java9 2020-04-14 21:48:04 +02:00
Jeroen van Erp
5e25c017bf Remove animalSniffer from Java9 build 2020-04-14 20:42:25 +02:00
Jeroen van Erp
27a5039831 Fixes for GH Actions 2020-04-14 20:39:46 +02:00
Jeroen van Erp
c2d25a9d62 Upgrade release plugin 2020-04-14 20:00:41 +02:00
Jeroen van Erp
2a22809de2 Update gradle.yml 2020-04-14 19:55:24 +02:00
Vladimir Dimitrov
9d1f6d9d83 Making OpenSSHKnownHosts.EntryFactory public 2020-04-14 19:45:11 +02:00
Jeroen van Erp
4542d94440 Add GitHub actions workflow 2020-04-14 16:47:04 +02:00
Jeroen van Erp
46a0cbac9e Upgrade BouncyCastle (Fixes #572) 2020-04-14 16:00:29 +02:00
Jeroen van Erp
f470ddf219 Fix race condition on SERVICE_ACCEPT (Fixes #559) (#560) 2020-02-25 13:33:38 +01:00
Meteorite
d09276fe01 extract makeInetSocketAddress (by hostname) in SocketClient (#509)
to allow overriding with InetSocketAddress.createUnresolved for use with proxy

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-02-20 09:04:38 +01:00
Josh Soref
241c355e20 Spelling (#547)
* spelling: accommodate

* spelling: algorithms

* spelling: containing

* spelling: creating

* spelling: developed

* spelling: environment

* spelling: exception

* spelling: heartbeat

* spelling: hellman

* spelling: identifier

* spelling: initiated

* spelling: interface

* spelling: negotiated

* spelling: occurred

* spelling: possibility

* spelling: requesting

* spelling: strong

* spelling: successfully

* spelling: suspended

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-02-19 11:06:27 +01:00
Jeroen van Erp
56ef6c1223 Fix divide by zero in trace logging (Fixes #550) (#561) 2020-02-19 10:27:00 +01:00
Jason
989fb8cde6 Added comment field in HostEntry for end-of-line comments in known_hosts file (#517)
* Added comment field in HostEntry for end-of-line comments in known_hosts file.

* Also modified the getLine() method to return the comment, if it exists.

* Fixed implementation

* Add CODEOWNERS file

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-02-14 11:40:15 +01:00
Michiel ten Hagen
d10a33ec59 Use nano time instead of current time millis. (#552)
* Use nano time instead of current time millis.

* Add license header
2020-01-22 09:40:53 +01:00
Vladimir Lagunov
327a4c4c5b Forgive redundant spaces in OpenSSHv2 public keys and known_hosts (#524)
* Forgive redundant spaces in OpenSSHv2 public keys and known_hosts

Sometimes users copy-pastes private and public keys in text editors. It leads to redundant spaces
and newlines. OpenSSH can easily read such keys, so users expect from SSHJ the same.

* Fixed bugs in OpenSSH key file and known_hosts parsers

* OpenSSHKnownHosts should not throw errors while parsing corrupted records
2019-09-18 15:14:45 +02:00
Vladimir Lagunov
d5c045defd Fix non-ASCII passwords 2019-09-18 14:36:20 +02:00
Tobias Gruetzmacher
02b70ef427 Minor updates (#520)
* Update Gradle Wrapper to Gradle 4.10.3

* Update test dependencies
2019-08-04 08:57:44 +02:00
Adam Iwaniuk
fdf08ef3c9 Add support for tunneling via channels (#511) 2019-06-03 16:33:39 +02:00
Adam Iwaniuk
633b42fec8 Added support for RSA certificates as host key (#514)
* Added support for RSA certificates as host key

* removed not needed check

* added rsa cert signature verification test
2019-05-27 10:46:01 +02:00
Adam Iwaniuk
3c594d9a1c fix exception when parsing max possible date (#513)
* fix exception when parsing max possible date

* added test with rsa cert with large date
2019-05-27 10:12:45 +02:00
Jeroen van Erp
c2b9c0266d Refactored out duplicate code. 2019-05-08 14:07:46 +02:00
Jeroen van Erp
0e784dd171 Merge branch 'master' into jumping 2019-03-01 21:45:09 +01:00
Jeroen van Erp
f322a4b060 Updated ed25519-java to 0.3.0 2019-03-01 21:44:18 +01:00
Michał Wyrzykowski
0cd19284ee Fix local port forwarding disconnecting issue (#491)
* `SocketStreamCopyMonitor` closes channel after setting the one event. It doesn't wait for the second stream to finish the job.

* #317 Fix `SocketStreamCopyMonitor` to wait for all events before closing the channel.
2019-02-20 15:18:58 +01:00
Jeroen van Erp
a5017d55c8 Cleanup some code 2019-01-24 15:09:00 +01:00
Olli Helenius
a96fbfcf2f Merge branch 'master' into jumping 2017-07-29 10:16:36 +03:00
Olli Helenius
15e6924fc4 Fix indentation 2017-07-06 22:44:31 +03:00
Olli Helenius
9e8bef24c5 Add support for tunneling TCP/IP connections. 2017-07-06 22:32:56 +03:00
54 changed files with 1128 additions and 242 deletions

2
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,2 @@
* @hierynomus

55
.github/workflows/gradle.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: Build SSHJ
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
java8:
name: Build with Java 8
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew check
java9:
name: Build with Java 9
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 9
uses: actions/setup-java@v1
with:
java-version: 9
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew check -xanimalsnifferMain -xanimalsnifferTest
integration:
name: Integration test
needs: [java8]
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- uses: actions/setup-java@v1
with:
java-version: 8
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew integrationTest

2
NOTICE
View File

@@ -15,7 +15,7 @@ The Apache Software Foundation (http://www.apache.org/):
== in this case for the SSHD distribution. ==
=========================================================================
This product contains software developped by JCraft,Inc. and subject to
This product contains software developed by JCraft,Inc. and subject to
the following license:
Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc.

View File

@@ -1,7 +1,7 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.27.0
:sshj_version: 0.28.0
:source-highlighter: pygments
image:https://api.bintray.com/packages/hierynomus/maven/sshj/images/download.svg[link="https://bintray.com/hierynomus/maven/sshj/_latestVersion"]
@@ -73,7 +73,7 @@ key exchange::
`diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
SSHJ also supports the following extended (non official) key exchange algoriths:
SSHJ also supports the following extended (non official) key exchange algorithms:
`diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`,
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
@@ -152,7 +152,7 @@ SSHJ 0.19.1 (2016-12-30)::
* Enabled PKCS5 Key files in DefaultConfig
* Merged https://github.com/hierynomus/sshj/pulls/291[#291]: Fixed sshj.properties loading and chained exception messages
* Merged https://github.com/hierynomus/sshj/pulls/284[#284]: Correctly catch interrupt in keepalive thread
* Fixed https://github.com/hierynomus/sshj/issues/292[#292]: Pass the configured RandomFactory to Diffie Hellmann KEX
* Fixed https://github.com/hierynomus/sshj/issues/292[#292]: Pass the configured RandomFactory to Diffie Hellman KEX
* Fixed https://github.com/hierynomus/sshj/issues/256[#256]: SSHJ now builds if no git repository present
* LocalPortForwarder now correctly interrupts its own thread on close()
SSHJ 0.19.0 (2016-11-25)::

View File

@@ -6,16 +6,16 @@ plugins {
id "java"
id "groovy"
id "jacoco"
id "osgi"
id "com.github.blindpirate.osgi" version '0.0.3'
id "maven-publish"
id 'pl.allegro.tech.build.axion-release' version '1.9.2'
id 'pl.allegro.tech.build.axion-release' version '1.11.0'
id "com.bmuschko.docker-remote-api" version "3.2.1"
id "com.github.hierynomus.license" version "0.12.1"
id "com.jfrog.bintray" version "1.7"
id 'ru.vyarus.java-lib' version '1.0.5'
// id 'ru.vyarus.pom' version '1.0.3'
id 'ru.vyarus.github-info' version '1.1.0'
id 'ru.vyarus.animalsniffer' version '1.4.2'
id 'ru.vyarus.animalsniffer' version '1.5.0'
}
group = "com.hierynomus"
@@ -37,38 +37,35 @@ defaultTasks "build"
repositories {
mavenCentral()
maven {
url "https://dl.bintray.com/mockito/maven/"
}
}
sourceCompatibility = 1.6
targetCompatibility = 1.6
sourceCompatibility = 1.7
targetCompatibility = 1.7
configurations.compile.transitive = false
def bouncycastleVersion = "1.60"
def bouncycastleVersion = "1.65"
def sshdVersion = "2.1.0"
dependencies {
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
compile "org.slf4j:slf4j-api:1.7.7"
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
compile "com.jcraft:jzlib:1.1.3"
implementation "org.slf4j:slf4j-api:1.7.7"
implementation "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
implementation "com.jcraft:jzlib:1.1.3"
compile "net.i2p.crypto:eddsa:0.2.0"
implementation "net.i2p.crypto:eddsa:0.3.0"
testCompile "junit:junit:4.11"
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile "org.mockito:mockito-core:2.9.2"
testCompile "org.apache.sshd:sshd-core:$sshdVersion"
testCompile "org.apache.sshd:sshd-sftp:$sshdVersion"
testCompile "org.apache.sshd:sshd-scp:$sshdVersion"
testRuntime "ch.qos.logback:logback-classic:1.1.2"
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
testImplementation "junit:junit:4.12"
testImplementation 'org.spockframework:spock-core:1.3-groovy-2.4'
testImplementation "org.mockito:mockito-core:2.28.2"
testImplementation "org.apache.sshd:sshd-core:$sshdVersion"
testImplementation "org.apache.sshd:sshd-sftp:$sshdVersion"
testImplementation "org.apache.sshd:sshd-scp:$sshdVersion"
testRuntimeOnly "ch.qos.logback:logback-classic:1.2.3"
testImplementation 'org.glassfish.grizzly:grizzly-http-server:2.4.4'
testImplementation 'org.apache.httpcomponents:httpclient:4.5.9'
}
@@ -132,8 +129,8 @@ sourcesJar {
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
}
sourceSets {

View File

@@ -0,0 +1,71 @@
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import net.schmizz.sshj.xfer.FileSystemFile;
import java.io.*;
import java.nio.charset.Charset;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
/** This examples demonstrates how to configure {@link net.schmizz.sshj.SSHClient} client with an in-memory known_hosts file */
public class InMemoryKnownHosts {
public static void main(String[] args) throws IOException {
InputStream entry = new ByteArrayInputStream("localhost ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPmhSBtMctNa4hsZt8QGlsYSE5/gMkjeand69Vj4ir13".getBytes(Charset.defaultCharset()));
SSHClient ssh = new SSHClient();
ssh.addHostKeyVerifier(new InMemoryHostKeyVerifier(entry, Charset.defaultCharset()));
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
ssh.newSCPFileTransfer().download("test_file", new FileSystemFile("/tmp/"));
} finally {
ssh.disconnect();
}
}
public static class InMemoryHostKeyVerifier implements HostKeyVerifier {
private final List<OpenSSHKnownHosts.KnownHostEntry> entries = new ArrayList<OpenSSHKnownHosts.KnownHostEntry>();
public InMemoryHostKeyVerifier(InputStream inputStream, Charset charset) throws IOException {
final OpenSSHKnownHosts.EntryFactory entryFactory = new OpenSSHKnownHosts.EntryFactory();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset));
while(reader.ready()) {
String line = reader.readLine();
try {
OpenSSHKnownHosts.KnownHostEntry entry = entryFactory.parseEntry(line);
if (entry != null) {
entries.add(entry);
}
} catch (Exception e) {
//log error
}
}
}
@Override
public boolean verify(String hostname, int port, PublicKey key) {
final KeyType type = KeyType.fromKey(key);
if (type == KeyType.UNKNOWN) {
return false;
}
for (OpenSSHKnownHosts.KnownHostEntry e : entries) {
try {
if (e.appliesTo(type, hostname) && e.verify(key)) {
return true;
}
} catch (IOException ioe) {
//log error
}
}
return false;
}
}
}

View File

@@ -0,0 +1,50 @@
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.DirectConnection;
import net.schmizz.sshj.connection.channel.direct.Session;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* This example demonstrates connecting via an intermediate "jump" server using a direct TCP/IP channel.
*/
public class Jump {
public static void main(String... args)
throws IOException {
SSHClient firstHop = new SSHClient();
firstHop.loadKnownHosts();
firstHop.connect("localhost");
try {
firstHop.authPublickey(System.getProperty("user.name"));
DirectConnection tunnel = firstHop.newDirectConnection("localhost", 22);
SSHClient ssh = new SSHClient();
try {
ssh.loadKnownHosts();
ssh.connectVia(tunnel);
ssh.authPublickey(System.getProperty("user.name"));
final Session session = ssh.startSession();
try {
final Session.Command cmd = session.exec("ping -c 1 google.com");
System.out.println(IOUtils.readFully(cmd.getInputStream()).toString());
cmd.join(5, TimeUnit.SECONDS);
System.out.println("\n** exit status: " + cmd.getExitStatus());
} finally {
session.close();
}
} finally {
ssh.disconnect();
}
} finally {
firstHop.disconnect();
}
}
}

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Fri Mar 18 11:26:35 CET 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip

110
gradlew vendored
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
##############################################################################
##
@@ -6,47 +6,6 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

14
gradlew.bat vendored
View File

@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

View File

@@ -26,7 +26,7 @@ import java.math.BigInteger;
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
/**
* Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups
* Factory methods for Diffie Hellman KEX algorithms based on MODP groups / Oakley Groups
*
* - https://tools.ietf.org/html/rfc4253
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01

View File

@@ -69,7 +69,7 @@ public class Macs {
return new Factory("hmac-sha2-512-etm@openssh.com", "HmacSHA512", 64, 64, true);
}
private static class Factory implements net.schmizz.sshj.common.Factory.Named<MAC> {
public static class Factory implements net.schmizz.sshj.common.Factory.Named<MAC> {
private String name;
private String algorithm;

View File

@@ -18,7 +18,6 @@ package com.hierynomus.sshj.transport.verification;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.transport.mac.HMACSHA1;
import net.schmizz.sshj.transport.mac.MAC;
import java.io.IOException;
@@ -26,6 +25,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import com.hierynomus.sshj.transport.mac.Macs;
public class KnownHostMatchers {
public static HostMatcher createMatcher(String hostEntry) throws SSHException {
@@ -63,7 +64,7 @@ public class KnownHostMatchers {
}
private static class HashedHostMatcher implements HostMatcher {
private final MAC sha1 = new HMACSHA1();
private final MAC sha1 = Macs.HMACSHA1().create();
private final String hash;
private final String salt;
private byte[] saltyBytes;

View File

@@ -40,7 +40,6 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Arrays;
@@ -194,7 +193,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
KeyPair kp;
switch (kt) {
case ED25519:
byte[] pubKey = keyBuffer.readBytes(); // string publickey (again...)
keyBuffer.readBytes(); // string publickey (again...)
keyBuffer.readUInt32(); // length of privatekey+publickey
byte[] privKey = new byte[32];
keyBuffer.readRawBytes(privKey); // string privatekey
@@ -203,7 +202,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
break;
case RSA:
BigInteger n = keyBuffer.readMPInt(); // Modulus
BigInteger e = keyBuffer.readMPInt(); // Public Exponent
keyBuffer.readMPInt(); // Public Exponent
BigInteger d = keyBuffer.readMPInt(); // Private Exponent
keyBuffer.readMPInt(); // iqmp (q^-1 mod p)
keyBuffer.readMPInt(); // p (Prime 1)
@@ -223,7 +222,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
default:
throw new IOException("Cannot decode keytype " + keyType + " in openssh-key-v1 files (yet).");
}
String comment = keyBuffer.readString(); // string comment
keyBuffer.readString(); // string comment
byte[] padding = new byte[keyBuffer.available()];
keyBuffer.readRawBytes(padding); // char[] padding
for (int i = 0; i < padding.length; i++) {
@@ -235,7 +234,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
}
private PrivateKey createECDSAPrivateKey(KeyType kt, PlainBuffer buffer, String name) throws GeneralSecurityException, Buffer.BufferException {
PublicKey pk = kt.readPubKeyFromBuffer(buffer); // Public key
kt.readPubKeyFromBuffer(buffer); // Public key
BigInteger s = new BigInteger(1, buffer.readBytes());
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());

View File

@@ -19,23 +19,23 @@ import java.util.Collection;
public class ErrorDeliveryUtil {
public static void alertPromises(Throwable x, Promise... promises) {
for (Promise p : promises)
public static void alertPromises(Throwable x, Promise<?, ?>... promises) {
for (Promise<?, ?> p : promises)
p.deliverError(x);
}
public static void alertPromises(Throwable x, Collection<? extends Promise> promises) {
for (Promise p : promises)
public static void alertPromises(Throwable x, Collection<? extends Promise<?, ?>> promises) {
for (Promise<?, ?> p : promises)
p.deliverError(x);
}
public static void alertEvents(Throwable x, Event... events) {
for (Event e : events)
public static void alertEvents(Throwable x, Event<?>... events) {
for (Event<?> e : events)
e.deliverError(x);
}
public static void alertEvents(Throwable x, Collection<? extends Event> events) {
for (Event e : events)
public static void alertEvents(Throwable x, Collection<? extends Event<?>> events) {
for (Event<?> e : events)
e.deliverError(x);
}

View File

@@ -26,7 +26,7 @@ package net.schmizz.concurrent;
* if (t instanceof SomeException)
* return (SomeException) t;
* else
* return new SomeExcepion(t);
* return new SomeException(t);
* }
* };
* </pre>

View File

@@ -160,7 +160,7 @@ public interface Config {
/**
* Gets whether the client should first wait for a received server ident, before sending the client ident.
* <p/>
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
* <strong>NB:</strong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
*
* The default value is set to false.
*
@@ -171,7 +171,7 @@ public interface Config {
/**
* Sets whether the SSH client should wait for a received server ident, before sending the client ident.
* <p/>
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
* <strong>NB:</strong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
*/

View File

@@ -35,7 +35,6 @@ import net.schmizz.sshj.transport.kex.Curve25519SHA256;
import net.schmizz.sshj.transport.kex.DHGexSHA1;
import net.schmizz.sshj.transport.kex.DHGexSHA256;
import net.schmizz.sshj.transport.kex.ECDHNistP;
import net.schmizz.sshj.transport.mac.*;
import net.schmizz.sshj.transport.random.BouncyCastleRandom;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
@@ -45,7 +44,6 @@ import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.*;
/**
@@ -216,6 +214,7 @@ public class DefaultConfig
new SignatureECDSA.Factory384(),
new SignatureECDSA.Factory521(),
new SignatureRSA.Factory(),
new SignatureRSA.FactoryCERT(),
new SignatureDSA.Factory()
);
}

View File

@@ -19,10 +19,7 @@ import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.SessionChannel;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.connection.channel.direct.*;
import net.schmizz.sshj.connection.channel.forwarded.ConnectListener;
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder;
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.ForwardedTCPIPChannel;
@@ -518,7 +515,7 @@ public class SSHClient
}
/**
* Utility function for createing a {@link KeyProvider} instance from given location on the file system. Creates a
* Utility function for creating a {@link KeyProvider} instance from given location on the file system. Creates a
* one-off {@link PasswordFinder} using {@link PasswordUtils#createOneOff(char[])}, and calls {@link
* #loadKeys(String, PasswordFinder)}.
*
@@ -664,13 +661,27 @@ public class SSHClient
*
* @return a {@link LocalPortForwarder}
*/
public LocalPortForwarder newLocalPortForwarder(LocalPortForwarder.Parameters parameters,
public LocalPortForwarder newLocalPortForwarder(Parameters parameters,
ServerSocket serverSocket) {
LocalPortForwarder forwarder = new LocalPortForwarder(conn, parameters, serverSocket, loggerFactory);
forwarders.add(forwarder);
return forwarder;
}
/** Create a {@link DirectConnection} channel that connects to a remote address from the server.
*
* This can be used to open a tunnel to, for example, an HTTP server that is only
* accessible from the SSH server, or opening an SSH connection via a 'jump' server.
*
* @param hostname name of the host to connect to from the server.
* @param port remote port number.
*/
public DirectConnection newDirectConnection(String hostname, int port) throws IOException {
DirectConnection tunnel = new DirectConnection(conn, hostname, port);
tunnel.open();
return tunnel;
}
/**
* Register a {@code listener} for handling forwarded X11 channels. Without having done this, an incoming X11
* forwarding will be summarily rejected.
@@ -743,7 +754,7 @@ public class SSHClient
/**
* Adds {@code zlib} compression to preferred compression algorithms. There is no guarantee that it will be
* successfully negotiatied.
* successfully negotiated.
* <p/>
* If the client is already connected renegotiation is done; otherwise this method simply returns (and compression
* will be negotiated during connection establishment).

View File

@@ -17,6 +17,8 @@ package net.schmizz.sshj;
import com.hierynomus.sshj.backport.JavaVersion;
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.direct.DirectConnection;
import javax.net.SocketFactory;
import java.io.IOException;
@@ -43,11 +45,18 @@ public abstract class SocketClient {
private int timeout = 0;
private String hostname;
private int port;
private boolean tunneled = false;
SocketClient(int defaultPort) {
this.defaultPort = defaultPort;
}
protected InetSocketAddress makeInetSocketAddress(String hostname, int port) {
return new InetSocketAddress(hostname, port);
}
/**
* Connect to a host via a proxy.
* @param hostname The host name to connect to.
@@ -71,13 +80,14 @@ public abstract class SocketClient {
@Deprecated
public void connect(String hostname, int port, Proxy proxy) throws IOException {
this.hostname = hostname;
this.port = port;
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
socket = new Jdk7HttpProxySocket(proxy);
} else {
socket = new Socket(proxy);
}
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
onConnect();
}
@@ -103,6 +113,7 @@ public abstract class SocketClient {
*/
@Deprecated
public void connect(InetAddress host, int port, Proxy proxy) throws IOException {
this.port = port;
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
socket = new Jdk7HttpProxySocket(proxy);
@@ -122,8 +133,9 @@ public abstract class SocketClient {
connect(InetAddress.getByName(null), port);
} else {
this.hostname = hostname;
this.port = port;
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
onConnect();
}
}
@@ -133,18 +145,34 @@ public abstract class SocketClient {
connect(InetAddress.getByName(null), port, localAddr, localPort);
} else {
this.hostname = hostname;
this.port = port;
socket = socketFactory.createSocket();
socket.bind(new InetSocketAddress(localAddr, localPort));
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
onConnect();
}
}
public void connectVia(Channel channel, String hostname, int port) throws IOException {
this.hostname = hostname;
this.port = port;
this.input = channel.getInputStream();
this.output = channel.getOutputStream();
this.tunneled = true;
onConnect();
}
/** Connect to a remote address via a direct TCP/IP connection from the server. */
public void connectVia(DirectConnection directConnection) throws IOException {
connectVia(directConnection, directConnection.getRemoteHost(), directConnection.getRemotePort());
}
public void connect(InetAddress host) throws IOException {
connect(host, defaultPort);
}
public void connect(InetAddress host, int port) throws IOException {
this.port = port;
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(host, port), connectTimeout);
onConnect();
@@ -152,6 +180,7 @@ public abstract class SocketClient {
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
throws IOException {
this.port = port;
socket = socketFactory.createSocket();
socket.bind(new InetSocketAddress(localAddr, localPort));
socket.connect(new InetSocketAddress(host, port), connectTimeout);
@@ -174,15 +203,15 @@ public abstract class SocketClient {
}
public boolean isConnected() {
return (socket != null) && socket.isConnected();
return tunneled || ((socket != null) && socket.isConnected());
}
public int getLocalPort() {
return socket.getLocalPort();
return tunneled ? 65536 : socket.getLocalPort();
}
public InetAddress getLocalAddress() {
return socket.getLocalAddress();
return socket == null ? null : socket.getLocalAddress();
}
public String getRemoteHostname() {
@@ -190,11 +219,11 @@ public abstract class SocketClient {
}
public int getRemotePort() {
return socket.getPort();
return socket == null ? this.port : socket.getPort();
}
public InetAddress getRemoteAddress() {
return socket.getInetAddress();
return socket == null ? null : socket.getInetAddress();
}
public void setSocketFactory(SocketFactory factory) {
@@ -238,9 +267,11 @@ public abstract class SocketClient {
}
void onConnect() throws IOException {
socket.setSoTimeout(timeout);
input = socket.getInputStream();
output = socket.getOutputStream();
if (socket != null) {
socket.setSoTimeout(timeout);
input = socket.getInputStream();
output = socket.getOutputStream();
}
}
}

View File

@@ -76,7 +76,7 @@ package net.schmizz.sshj.common;
* <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li>
* <li><em>Throws exceptions instead of returning null values.</em> Because some operations
* (especially those that may permit the GZIP option) use IO streams, there
* is a possiblity of an java.io.IOException being thrown. After some discussion and
* is a possibility of an java.io.IOException being thrown. After some discussion and
* thought, I've changed the behavior of the methods to throw java.io.IOExceptions
* rather than return null if ever there's an error. I think this is more
* appropriate, though it will require some changes to your code. Sorry,
@@ -476,7 +476,7 @@ public class Base64
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accomodate <var>srcOffset</var> + 3 for
* are large enough to accommodate <var>srcOffset</var> + 3 for
* the <var>source</var> array or <var>destOffset</var> + 4 for
* the <var>destination</var> array.
* The actual number of significant bytes in your array is
@@ -1007,7 +1007,7 @@ public class Base64
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accomodate <var>srcOffset</var> + 4 for
* are large enough to accommodate <var>srcOffset</var> + 4 for
* the <var>source</var> array or <var>destOffset</var> + 3 for
* the <var>destination</var> array.
* This method returns the actual number of bytes that
@@ -1928,7 +1928,7 @@ public class Base64
if( suspendEncoding ) {
this.out.write( theByte );
return;
} // end if: supsended
} // end if: suspended
// Encode?
if( encode ) {
@@ -1983,7 +1983,7 @@ public class Base64
if( suspendEncoding ) {
this.out.write( theBytes, off, len );
return;
} // end if: supsended
} // end if: suspended
for( int i = 0; i < len; i++ ) {
write( theBytes[ off + i ] );

View File

@@ -453,11 +453,14 @@ public class Buffer<T extends Buffer<T>> {
public T putSensitiveString(char[] str) {
if (str == null)
return putString("");
putUInt32(str.length);
ensureCapacity(str.length);
for (char c : str)
data[wpos++] = (byte) c;
Arrays.fill(str, ' ');
// RFC 4252, Section 8 says: passwords should be encoded as UTF-8.
// RFC 4256, Section 3.4 says: keyboard-interactive information responses should be encoded as UTF-8.
byte[] utf8 = ByteArrayUtils.encodeSensitiveStringToUtf8(str);
putUInt32(utf8.length);
ensureCapacity(utf8.length);
for (byte c : utf8)
data[wpos++] = c;
Arrays.fill(utf8, (byte) 0);
return (T) this;
}

View File

@@ -15,6 +15,12 @@
*/
package net.schmizz.sshj.common;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Arrays;
/** Utility functions for byte arrays. */
public class ByteArrayUtils {
@@ -124,4 +130,26 @@ public class ByteArrayUtils {
}
throw new IllegalArgumentException("Digit '" + c + "' out of bounds [0-9a-fA-F]");
}
/**
* Converts a char-array to UTF-8 byte-array and then blanks out source array and all intermediate arrays.
* <p/>
* This is useful when a plaintext password needs to be encoded as UTF-8.
*
* @param str A not-null string as a character array.
*
* @return UTF-8 bytes of the string
*/
public static byte[] encodeSensitiveStringToUtf8(char[] str) {
CharsetEncoder charsetEncoder = Charset.forName("UTF-8").newEncoder();
ByteBuffer utf8Buffer = ByteBuffer.allocate((int) (str.length * charsetEncoder.maxBytesPerChar()));
assert utf8Buffer.hasArray();
charsetEncoder.encode(CharBuffer.wrap(str), utf8Buffer, true);
Arrays.fill(str, ' ');
byte[] utf8Bytes = new byte[utf8Buffer.position()];
System.arraycopy(utf8Buffer.array(), 0, utf8Bytes, 0, utf8Bytes.length);
Arrays.fill(utf8Buffer.array(), (byte) 0);
return utf8Bytes;
}
}

View File

@@ -26,7 +26,7 @@ import java.util.List;
public interface Factory<T> {
/**
* Inteface for a named factory. Named factories are simply factories that are identified by a name. Such names are
* Interface for a named factory. Named factories are simply factories that are identified by a name. Such names are
* used mainly in SSH algorithm negotiation.
*
* @param <T> type of object created by this factory

View File

@@ -315,8 +315,8 @@ public enum KeyType {
builder.type(buf.readUInt32());
builder.id(buf.readString());
builder.validPrincipals(unpackList(buf.readBytes()));
builder.validAfter(dateFromEpoch(buf.readUInt64()));
builder.validBefore(dateFromEpoch(buf.readUInt64()));
builder.validAfter(dateFromEpoch(buf.readUInt64AsBigInteger()));
builder.validBefore(dateFromEpoch(buf.readUInt64AsBigInteger()));
builder.critOptions(unpackMap(buf.readBytes()));
builder.extensions(unpackMap(buf.readBytes()));
buf.readString(); // reserved
@@ -364,8 +364,13 @@ public enum KeyType {
return ((Certificate<PublicKey>) key);
}
private static Date dateFromEpoch(long seconds) {
return new Date(seconds * 1000);
private static Date dateFromEpoch(BigInteger seconds) {
BigInteger maxValue = BigInteger.valueOf(Long.MAX_VALUE / 1000);
if (seconds.compareTo(maxValue) > 0) {
return new Date(maxValue.longValue() * 1000);
} else {
return new Date(seconds.longValue() * 1000);
}
}
private static long epochFromDate(Date date) {

View File

@@ -22,6 +22,7 @@ import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
public class StreamCopier {
@@ -125,7 +126,7 @@ public class StreamCopier {
long count = 0;
int read = 0;
final long startTime = System.currentTimeMillis();
final long startTime = System.nanoTime();
if (length == -1) {
while ((read = in.read(buf)) != -1) {
@@ -140,7 +141,7 @@ public class StreamCopier {
if (!keepFlushing)
out.flush();
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
final double timeSeconds = TimeUnit.NANOSECONDS.toMillis (System.nanoTime() - startTime) / 1000.0;
final double sizeKiB = count / 1024.0;
log.debug(String.format("%1$,.1f KiB transferred in %2$,.1f seconds (%3$,.2f KiB/s)", sizeKiB, timeSeconds, (sizeKiB / timeSeconds)));

View File

@@ -150,7 +150,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
}
private void checkClose() throws SSHException {
if (closed) {
// Check whether either the Stream is closed, or the underlying channel is closed
if (closed || !chan.isOpen()) {
if (error != null)
throw error;
else
@@ -160,7 +161,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
@Override
public synchronized void close() throws IOException {
if (!closed) {
// Not closed yet, and underlying channel is open to flush the data to.
if (!closed && chan.isOpen()) {
try {
buffer.flush(false);
// trans.write(new SSHPacket(Message.CHANNEL_EOF).putUInt32(chan.getRecipient()));

View File

@@ -39,15 +39,21 @@ public class SocketStreamCopyMonitor
new SocketStreamCopyMonitor(new Runnable() {
public void run() {
try {
for (Event<IOException> ev = x;
!ev.tryAwait(frequency, unit);
ev = (ev == x) ? y : x) {
}
await(x);
await(y);
} catch (IOException ignored) {
} finally {
IOUtils.closeQuietly(channel, asCloseable(socket));
}
}
private void await(final Event<IOException> event) throws IOException {
while(true){
if(event.tryAwait(frequency, unit)){
break;
}
}
}
}).start();
}

View File

@@ -28,7 +28,7 @@ import net.schmizz.sshj.transport.TransportException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
/** Base class for direct channels whose open is initated by the client. */
/** Base class for direct channels whose open is initiated by the client. */
public abstract class AbstractDirectChannel
extends AbstractChannel
implements Channel.Direct {

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.sshj.connection.Connection;
/** A channel for creating a direct TCP/IP connection from the server to a remote address. */
public class DirectConnection extends DirectTCPIPChannel {
public static final String LOCALHOST = "localhost";
public static final int LOCALPORT = 65536;
public DirectConnection(Connection conn, String remoteHost, int remotePort) {
super(conn, new Parameters(LOCALHOST, LOCALPORT, remoteHost, remotePort));
}
public String getRemoteHost() {
return parameters.getRemoteHost();
}
public int getRemotePort() {
return parameters.getRemotePort();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.Connection;
public class DirectTCPIPChannel extends AbstractDirectChannel {
protected final Parameters parameters;
protected DirectTCPIPChannel(Connection conn, Parameters parameters) {
super(conn, "direct-tcpip");
this.parameters = parameters;
}
@Override
protected SSHPacket buildOpenReq() {
return super.buildOpenReq()
.putString(parameters.getRemoteHost())
.putUInt32(parameters.getRemotePort())
.putString(parameters.getLocalHost())
.putUInt32(parameters.getLocalPort());
}
}

View File

@@ -18,7 +18,6 @@ package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
@@ -34,48 +33,14 @@ import static com.hierynomus.sshj.backport.Sockets.asCloseable;
public class LocalPortForwarder {
public static class Parameters {
private final String localHost;
private final int localPort;
private final String remoteHost;
private final int remotePort;
public Parameters(String localHost, int localPort, String remoteHost, int remotePort) {
this.localHost = localHost;
this.localPort = localPort;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
public String getRemoteHost() {
return remoteHost;
}
public int getRemotePort() {
return remotePort;
}
public String getLocalHost() {
return localHost;
}
public int getLocalPort() {
return localPort;
}
}
public static class DirectTCPIPChannel
extends AbstractDirectChannel {
public static class ForwardedChannel
extends DirectTCPIPChannel {
protected final Socket socket;
protected final Parameters parameters;
public DirectTCPIPChannel(Connection conn, Socket socket, Parameters parameters) {
super(conn, "direct-tcpip");
public ForwardedChannel(Connection conn, Socket socket, Parameters parameters) {
super(conn, parameters);
this.socket = socket;
this.parameters = parameters;
}
protected void start()
@@ -90,16 +55,6 @@ public class LocalPortForwarder {
.spawnDaemon("chan2soc");
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
}
@Override
protected SSHPacket buildOpenReq() {
return super.buildOpenReq()
.putString(parameters.getRemoteHost())
.putUInt32(parameters.getRemotePort())
.putString(parameters.getLocalHost())
.putUInt32(parameters.getLocalPort());
}
}
private final LoggerFactory loggerFactory;
@@ -118,7 +73,7 @@ public class LocalPortForwarder {
}
private void startChannel(Socket socket) throws IOException {
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
ForwardedChannel chan = new ForwardedChannel(conn, socket, parameters);
try {
chan.open();
chan.start();

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.connection.channel.direct;
public class Parameters {
private final String localHost;
private final int localPort;
private final String remoteHost;
private final int remotePort;
public Parameters(String localHost, int localPort, String remoteHost, int remotePort) {
this.localHost = localHost;
this.localPort = localPort;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
public String getRemoteHost() {
return remoteHost;
}
public int getRemotePort() {
return remotePort;
}
public String getLocalHost() {
return localHost;
}
public int getLocalPort() {
return localPort;
}
}

View File

@@ -191,7 +191,7 @@ public interface Session
TransportException;
/**
* Set an enviornment variable.
* Set an environment variable.
*
* @param name name of the variable
* @param value value to set

View File

@@ -28,7 +28,7 @@ public interface ForwardedChannelOpener {
/**
* Delegates a {@code SSH_MSG_CHANNEL_OPEN} request for the channel type claimed by this opener.
*
* @param buf {@link SSHPacket} containg the request except for the message identifier and channel type field
* @param buf {@link SSHPacket} containing the request except for the message identifier and channel type field
*/
void handleOpen(SSHPacket buf)
throws ConnectionException, TransportException;

View File

@@ -15,10 +15,14 @@
*/
package net.schmizz.sshj.signature;
import com.hierynomus.sshj.userauth.certificate.Certificate;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.Date;
/** RSA {@link Signature} */
public class SignatureRSA
@@ -30,7 +34,7 @@ public class SignatureRSA
@Override
public Signature create() {
return new SignatureRSA();
return new SignatureRSA(KeyType.RSA.toString());
}
@Override
@@ -40,8 +44,42 @@ public class SignatureRSA
}
public SignatureRSA() {
/** A named factory for RSA {@link Signature} */
public static class FactoryCERT
implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureRSA(KeyType.RSA_CERT.toString());
}
@Override
public String getName() {
return KeyType.RSA_CERT.toString();
}
}
private String keyTypeName;
public SignatureRSA(String keyTypeName) {
super("SHA1withRSA");
this.keyTypeName = keyTypeName;
}
@Override
@SuppressWarnings("unchecked")
public void initVerify(PublicKey publicKey) {
try {
if (this.keyTypeName.equals(KeyType.RSA_CERT.toString()) && publicKey instanceof Certificate) {
signature.initVerify(((Certificate<PublicKey>) publicKey).getKey());
} else {
signature.initVerify(publicKey);
}
} catch (InvalidKeyException e) {
throw new SSHRuntimeException(e);
}
}
@Override
@@ -51,12 +89,11 @@ public class SignatureRSA
@Override
public boolean verify(byte[] sig) {
sig = extractSig(sig, "ssh-rsa");
sig = extractSig(sig, KeyType.RSA.toString());
try {
return signature.verify(sig);
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
}
}
}

View File

@@ -94,7 +94,7 @@ final class KeyExchanger
* Add a callback for host key verification.
* <p/>
* Any of the {@link HostKeyVerifier} implementations added this way can deem a host key to be acceptable, allowing
* key exchange to successfuly complete. Otherwise, a {@link TransportException} will result during key exchange.
* key exchange to successfully complete. Otherwise, a {@link TransportException} will result during key exchange.
*
* @param hkv object whose {@link HostKeyVerifier#verify} method will be invoked
*/

View File

@@ -92,7 +92,7 @@ public interface Transport
int getHeartbeatInterval();
/**
* @param interval the interval in seconds, {@code 0} means no hearbeat
* @param interval the interval in seconds, {@code 0} means no heartbeat
* @deprecated Moved to {@link net.schmizz.keepalive.KeepAlive#getKeepAliveInterval()}. This is accessible through the {@link net.schmizz.sshj.connection.Connection}.
* Scheduled to be removed in 0.12.0
*/
@@ -156,7 +156,7 @@ public interface Transport
*
* @return the sequence number of the packet sent
*
* @throws TransportException if an error occured sending the packet
* @throws TransportException if an error occurred sending the packet
*/
long sendUnimplemented()
throws TransportException;

View File

@@ -104,6 +104,11 @@ public final class TransportImpl
*/
private volatile Service service;
/**
* The next service that will be activated, only set when sending an SSH_MSG_SERVICE_REQUEST
*/
private volatile Service nextService;
private DisconnectListener disconnectListener;
private ConnInfo connInfo;
@@ -324,8 +329,9 @@ public final class TransportImpl
@Override
public synchronized void setService(Service service) {
if (service == null)
if (service == null) {
service = nullService;
}
log.debug("Setting active service to {}", service.getName());
this.service = service;
@@ -337,11 +343,12 @@ public final class TransportImpl
serviceAccept.lock();
try {
serviceAccept.clear();
this.nextService = service;
sendServiceRequest(service.getName());
serviceAccept.await(timeoutMs, TimeUnit.MILLISECONDS);
setService(service);
} finally {
serviceAccept.unlock();
this.nextService = null;
}
}
@@ -485,8 +492,8 @@ public final class TransportImpl
* This method is called in the context of the {@link #reader} thread via {@link Decoder#received} when a full
* packet has been decoded.
*
* @param msg the message identifer
* @param buf buffer containg rest of the packet
* @param msg the message identifier
* @param buf buffer containing rest of the packet
* @throws SSHException if an error occurs during handling (unrecoverable)
*/
@Override
@@ -496,13 +503,11 @@ public final class TransportImpl
log.trace("Received packet {}", msg);
if (msg.geq(50)) // not a transport layer packet
if (msg.geq(50)) { // not a transport layer packet
service.handle(msg, buf);
else if (msg.in(20, 21) || msg.in(30, 49)) // kex packet
} else if (msg.in(20, 21) || msg.in(30, 49)) { // kex packet
kexer.handle(msg, buf);
else
} else {
switch (msg) {
case DISCONNECT:
gotDisconnect(buf);
@@ -526,6 +531,7 @@ public final class TransportImpl
sendUnimplemented();
break;
}
}
}
private void gotDebug(SSHPacket buf)
@@ -558,6 +564,8 @@ public final class TransportImpl
if (!serviceAccept.hasWaiters())
throw new TransportException(DisconnectReason.PROTOCOL_ERROR,
"Got a service accept notification when none was awaited");
// Immediately switch to next service to prevent race condition mentioned in #559
setService(nextService);
serviceAccept.set();
} finally {
serviceAccept.unlock();

View File

@@ -190,7 +190,7 @@ public class OpenSSHKnownHosts
* Lines starting with `#' and empty lines are ignored as comments.
*/
public class EntryFactory {
EntryFactory() {
public EntryFactory() {
}
public KnownHostEntry parseEntry(String line)
@@ -199,17 +199,22 @@ public class OpenSSHKnownHosts
return new CommentEntry(line);
}
final String[] split = line.split(" ");
final String trimmed = line.trim();
int minComponents = 3;
if (trimmed.startsWith("@")) {
minComponents = 4;
}
String[] split = trimmed.split("\\s+", minComponents + 1); // Add 1 for optional comments
if(split.length < minComponents) {
log.error("Error reading entry `{}`", line);
return new BadHostEntry(line);
}
int i = 0;
final Marker marker = Marker.fromString(split[i]);
if (marker != null) {
i++;
}
if(split.length < 3) {
log.error("Error reading entry `{}`", line);
return new BadHostEntry(line);
}
final String hostnames = split[i++];
final String sType = split[i++];
@@ -227,6 +232,9 @@ public class OpenSSHKnownHosts
}
} else if (isBits(sType)) {
type = KeyType.RSA;
minComponents += 1;
// re-split
split = trimmed.split("\\s+", minComponents + 1); // Add 1 for optional comments
// int bits = Integer.valueOf(sType);
final BigInteger e = new BigInteger(split[i++]);
final BigInteger n = new BigInteger(split[i++]);
@@ -242,7 +250,13 @@ public class OpenSSHKnownHosts
return new BadHostEntry(line);
}
return new HostEntry(marker, hostnames, type, key);
final String comment;
if (i < split.length) {
comment = split[i++];
} else {
comment = null;
}
return new HostEntry(marker, hostnames, type, key, comment);
}
private boolean isBits(String type) {
@@ -323,13 +337,19 @@ public class OpenSSHKnownHosts
private final String hostPart;
protected final KeyType type;
protected final PublicKey key;
private final String comment;
private final KnownHostMatchers.HostMatcher matcher;
public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException {
this(marker, hostPart, type, key, "");
}
public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key, String comment) throws SSHException {
this.marker = marker;
this.hostPart = hostPart;
this.type = type;
this.key = key;
this.comment = comment;
this.matcher = KnownHostMatchers.createMatcher(hostPart);
}
@@ -366,6 +386,9 @@ public class OpenSSHKnownHosts
line.append(getHostPart());
line.append(" ").append(type.toString());
line.append(" ").append(getKeyString(key));
if (!comment.isEmpty()) line.append(" ").append(comment);
return line.toString();
}
@@ -377,6 +400,10 @@ public class OpenSSHKnownHosts
protected String getHostPart() {
return hostPart;
}
public String getComment() {
return comment;
}
}
public static class BadHostEntry implements KnownHostEntry {

View File

@@ -93,13 +93,21 @@ public class OpenSSHKeyFile
private void initPubKey(Reader publicKey) throws IOException {
final BufferedReader br = new BufferedReader(publicKey);
try {
final String keydata = br.readLine();
if (keydata != null) {
String[] parts = keydata.trim().split(" ");
assert parts.length >= 2;
type = KeyType.fromString(parts[0]);
pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey();
String keydata;
while ((keydata = br.readLine()) != null) {
keydata = keydata.trim();
if (!keydata.isEmpty()) {
String[] parts = keydata.trim().split("\\s+");
if (parts.length >= 2) {
type = KeyType.fromString(parts[0]);
pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey();
} else {
throw new IOException("Got line with only one column");
}
return;
}
}
throw new IOException("Public key file is blank");
} finally {
br.close();
}

View File

@@ -24,7 +24,7 @@ import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
/**
* Implements the {@code "publickey"} SSH authentication method.
* <p/>
* Requesteing authentication with this method first sends a "feeler" request with just the public key, and if the
* Requesting authentication with this method first sends a "feeler" request with just the public key, and if the
* server responds with {@code SSH_MSG_USERAUTH_PK_OK} indicating that the key is acceptable, it proceeds to send a
* request signed with the private key. Therefore, private keys are not requested from the associated {@link
* KeyProvider} unless needed.

View File

@@ -54,7 +54,11 @@ public class LoggingTransferListener
public void reportProgress(long transferred)
throws IOException {
if (log.isTraceEnabled()) {
log.trace("transferred {}% of `{}`", ((transferred * 100) / size), path);
long percent = 100;
if (size > 0) {
percent = (transferred * 100)/size;
}
log.trace("transferred {}% of `{}`", percent, path);
}
}
};

View File

@@ -16,7 +16,7 @@
package com.hierynomus.sshj.connection.channel.direct
import com.hierynomus.sshj.test.SshFixture
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder
import net.schmizz.sshj.connection.channel.direct.Parameters
import org.junit.Rule
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
@@ -33,7 +33,7 @@ class LocalPortForwarderSpec extends Specification {
def client = tunnelFixture.setupConnectedDefaultClient()
client.authPassword("test", "test")
def socket = new ServerSocket(0)
def lpf = client.newLocalPortForwarder(new LocalPortForwarder.Parameters("localhost", socket.getLocalPort(), "localhost", realServer.server.port), socket)
def lpf = client.newLocalPortForwarder(new Parameters("localhost", socket.getLocalPort(), "localhost", realServer.server.port), socket)
def thread = new Thread(new Runnable() {
@Override
void run() {

View File

@@ -116,6 +116,36 @@ host1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL
s << ["\n", "#comment\n"]
}
@Unroll
def "should contain comment at end of line"() {
given:
def f = knownHosts(host)
when:
OpenSSHKnownHosts knownHosts = new OpenSSHKnownHosts(f)
then:
knownHosts.entries().size() == 1
def entry = knownHosts.entries().get(0)
entry instanceof OpenSSHKnownHosts.HostEntry
entry.comment == comment
where:
host << [
"|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ== this is a comment",
"test.com,1.1.1.1 2048 35 22017496617994656680820635966392838863613340434802393112245951008866692373218840197754553998457793202561151141246686162285550121243768846314646395880632789308110750881198697743542374668273149584280424505890648953477691795864456749782348425425954366277600319096366690719901119774784695056100331902394094537054256611668966698242432417382422091372756244612839068092471592121759862971414741954991375710930168229171638843329213652899594987626853020377726482288618521941129157643483558764875338089684351824791983007780922947554898825663693324944982594850256042689880090306493029526546183035567296830604572253312294059766327 single",
"schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==",
"schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ== ",
"schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ== extra space"
]
comment << [
"this is a comment",
"single",
null,
null,
"extra space"
]
}
@Unroll
def "should match any host name from multi-host line"() {
given:
@@ -144,6 +174,47 @@ host1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL
toStringValue == "OpenSSHKnownHosts{khFile='" + f + "'}"
}
def "should forgive redundant spaces like OpenSSH does"() {
given:
def key = "AAAAC3NzaC1lZDI1NTE5AAAAIIRsJi92NJJTQwXHZiRiARoEy4n1jYsNTQePHFTSl7tG"
def f = knownHosts("""
|host1 ssh-ed25519 $key
|
| host2 ssh-ed25519 $key ,./gargage\\.,
|\t\t\t\t\t
|\t@revoked host3\tssh-ed25519\t \t$key\t
""".stripMargin())
def pk = new Buffer.PlainBuffer(Base64.decode(key)).readPublicKey()
when:
def knownhosts = new OpenSSHKnownHosts(f)
then:
["host1", "host2", "host3"].forEach {
knownhosts.verify(it, 22, pk)
}
}
def "should not throw errors while parsing corrupted records"() {
given:
def key = "AAAAC3NzaC1lZDI1NTE5AAAAIIRsJi92NJJTQwXHZiRiARoEy4n1jYsNTQePHFTSl7tG"
def f = knownHosts(
"\n" // empty line
+ " \n" // blank line
+ "bad-host1\n" // absent key type and key contents
+ "bad-host2 ssh-ed25519\n" // absent key contents
+ " bad-host3 ssh-ed25519\n" // absent key contents, with leading spaces
+ "@revoked bad-host5 ssh-ed25519\n" // absent key contents, with marker
+ "good-host ssh-ed25519 $key" // the only good host at the end
)
when:
def knownhosts = new OpenSSHKnownHosts(f)
then:
knownhosts.verify("good-host", 22, new Buffer.PlainBuffer(Base64.decode(key)).readPublicKey())
}
def knownHosts(String s) {
def f = temp.newFile("known_hosts")
f.write(s)

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.userauth.method;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
import net.schmizz.sshj.userauth.method.ChallengeResponseProvider;
import net.schmizz.sshj.userauth.password.Resource;
import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.server.auth.UserAuth;
import org.apache.sshd.server.auth.keyboard.UserAuthKeyboardInteractive;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
public class AuthKeyboardInteractiveTest {
@Rule
public SshFixture fixture = new SshFixture(false);
@Before
public void setKeyboardInteractiveAuthenticator() throws IOException {
fixture.getServer().setUserAuthFactories(Collections.<NamedFactory<UserAuth>>singletonList(new NamedFactory<UserAuth>() {
@Override
public String getName() {
return UserAuthKeyboardInteractiveFactory.NAME;
}
@Override
public UserAuth get() {
return new UserAuthKeyboardInteractive();
}
@Override
public UserAuth create() {
return get();
}
}));
fixture.getServer().setPasswordAuthenticator(new PasswordAuthenticator() {
@Override
public boolean authenticate(String username, String password, ServerSession session) {
return password.equals(username);
}
});
fixture.getServer().start();
}
@Test
public void shouldEncodePasswordsAsUtf8() throws IOException {
SSHClient sshClient = fixture.setupConnectedDefaultClient();
final String userAndPassword = "øæå";
sshClient.auth(userAndPassword, new AuthKeyboardInteractive(new ChallengeResponseProvider() {
@Override
public List<String> getSubmethods() {
return new ArrayList<String>();
}
@Override
public void init(Resource resource, String name, String instruction) {
// nothing
}
@Override
public char[] getResponse(String prompt, boolean echo) {
return userAndPassword.toCharArray();
}
@Override
public boolean shouldRetry() {
return false;
}
}));
assertThat("Should have been authenticated", sshClient.isAuthenticated());
}
}

View File

@@ -144,6 +144,14 @@ public class AuthPasswordTest {
assertThat("Should have been authenticated", sshClient.isAuthenticated());
}
@Test
public void shouldEncodePasswordsAsUtf8() throws IOException {
SSHClient sshClient = fixture.setupConnectedDefaultClient();
String userAndPassword = "øæå";
sshClient.authPassword(userAndPassword, userAndPassword);
assertThat("Should have been authenticated", sshClient.isAuthenticated());
}
private static class StaticPasswordUpdateProvider implements PasswordUpdateProvider {
private Stack<String> newPasswords = new Stack<String>();

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.common;
import org.junit.Test;
import java.io.*;
import java.util.Random;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
public class StreamCopierTest {
@Test
public void copy() throws IOException {
Random random = new Random();
byte[] data = new byte[1024];
random.nextBytes(data);
InputStream inputStream = new ByteArrayInputStream(data);
OutputStream outputStream = new ByteArrayOutputStream();
LoggerFactory loggerFactory = mock(LoggerFactory.class);
org.slf4j.Logger logger = mock(org.slf4j.Logger.class);
when(loggerFactory.getLogger(StreamCopier.class)).thenReturn(logger);
StreamCopier streamCopier = new StreamCopier(inputStream, outputStream, loggerFactory);
long copied = streamCopier.copy();
assertThat(copied, is(1024L));
verify(logger).debug(contains("1.0 KiB transferred"));
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.ExceptionChainer;
import net.schmizz.sshj.common.LoggerFactory;
import org.junit.Test;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.*;
public class SocketStreamCopyMonitorTest {
@Test
public void shouldNotCloseChannelIfOnlyFirstEventSet() throws Exception {
final Channel channel = mock(Channel.class);
final Socket socket = mock(Socket.class);
final Event<IOException> xEvent = createEvent();
final Event<IOException> yEvent = createEvent();
SocketStreamCopyMonitor.monitor(1, TimeUnit.MILLISECONDS, xEvent, yEvent, channel, socket);
xEvent.set();
waitForMonitorThreadToCloseTheChannel();
verify(channel, never()).close();
}
@Test
public void shouldNotCloseChannelIfOnlySecondEventSet() throws Exception {
final Channel channel = mock(Channel.class);
final Socket socket = mock(Socket.class);
final Event<IOException> xEvent = createEvent();
final Event<IOException> yEvent = createEvent();
SocketStreamCopyMonitor.monitor(1, TimeUnit.MILLISECONDS, xEvent, yEvent, channel, socket);
yEvent.set();
waitForMonitorThreadToCloseTheChannel();
verify(channel, never()).close();
}
@Test
public void shouldCloseChannelIfBothEventsSet() throws Exception {
final Channel channel = mock(Channel.class);
final Socket socket = mock(Socket.class);
final Event<IOException> xEvent = createEvent();
final Event<IOException> yEvent = createEvent();
SocketStreamCopyMonitor.monitor(1, TimeUnit.MILLISECONDS, xEvent, yEvent, channel, socket);
xEvent.set();
yEvent.set();
waitForMonitorThreadToCloseTheChannel();
verify(channel, times(1)).close();
}
private void waitForMonitorThreadToCloseTheChannel() throws InterruptedException {
Thread.sleep(50);
}
private Event<IOException> createEvent() {
return new Event<IOException>("event", new ExceptionChainer<IOException>() {
@Override
public IOException chain(Throwable t) {
return new IOException(t);
}
}, LoggerFactory.DEFAULT);
}
}

View File

@@ -26,10 +26,18 @@ import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.Resource;
import net.schmizz.sshj.util.KeyUtil;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
@@ -95,6 +103,9 @@ public class OpenSSHKeyFileTest {
}
};
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void blankingOut()
throws IOException, GeneralSecurityException {
@@ -264,6 +275,19 @@ public class OpenSSHKeyFileTest {
}
@Test
public void shouldSuccessfullyLoadSignedRSAPublicKeyWithMaxDate() throws IOException {
FileKeyProvider keyFile = new OpenSSHKeyFile();
keyFile.init(new File("src/test/resources/keytypes/certificate/test_rsa_max_date"),
PasswordUtils.createOneOff(correctPassphrase));
PublicKey pubKey = keyFile.getPublic();
@SuppressWarnings("unchecked")
Certificate<RSAPublicKey> certificate = (Certificate<RSAPublicKey>) pubKey;
assertTrue(parseDate("9999-04-11 18:09:27 -0400").before(certificate.getValidBefore()));
}
@Test
public void shouldSuccessfullyLoadSignedDSAPublicKey() throws IOException {
FileKeyProvider keyFile = new OpenSSHKeyFile();
@@ -292,6 +316,47 @@ public class OpenSSHKeyFileTest {
assertEquals("", certificate.getExtensions().get("permit-pty"));
}
/**
* Sometimes users copy-pastes private and public keys in text editors. It leads to redundant
* spaces and newlines. OpenSSH can easily read such keys, so users expect from SSHJ the same.
*/
@Test
public void notTrimmedKeys() throws IOException {
File initialPrivateKey = new File("src/test/resources/id_rsa");
File initialPublicKey = new File("src/test/resources/id_rsa.pub");
File corruptedPrivateKey = new File(temporaryFolder.newFolder(), "id_rsa");
File corruptedPublicKey = new File(corruptedPrivateKey.getParent(), "id_rsa.pub");
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(initialPrivateKey)));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(corruptedPrivateKey)));
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.write("\n");
}
writer.write("\n\n");
reader.close();
writer.close();
reader = new BufferedReader(new InputStreamReader(new FileInputStream(initialPublicKey)));
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(corruptedPublicKey)));
writer.write("\n\n \t ");
writer.write(reader.readLine().replace(" ", " \t "));
writer.write("\n\n");
reader.close();
writer.close();
FileKeyProvider initialKeyFile = new OpenSSHKeyFile();
FileKeyProvider corruptedKeyFile = new OpenSSHKeyFile();
initialKeyFile.init(initialPrivateKey);
corruptedKeyFile.init(corruptedPrivateKey);
assertEquals(initialKeyFile.getPrivate(),
corruptedKeyFile.getPrivate());
assertEquals(initialKeyFile.getPublic(),
corruptedKeyFile.getPublic());
}
@Before
public void checkBCRegistration() {
if (!SecurityUtils.isBouncyCastleRegistered()) {

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.signature;
import net.schmizz.sshj.common.Buffer;
import org.junit.Assert;
import org.junit.Test;
import java.security.PublicKey;
public class SignatureRSATest {
@Test
public void testRSACERTVerifies() throws Buffer.BufferException {
byte[] certBytes = fromString("0, 0, 0, 28, 115, 115, 104, 45, 114, 115, 97, 45, 99, 101, 114, 116, 45, 118, 48, 49, 64, 111, 112, 101, 110, 115, 115, 104, 46, 99, 111, 109, 0, 0, 0, 32, 111, 79, -117, -106, 111, -73, 4, -86, -70, -97, -106, 40, 49, -22, 72, -41, -14, 42, -89, -123, -89, 72, 86, 24, 110, -103, -67, 99, 72, -53, 108, -109, 0, 0, 0, 3, 1, 0, 1, 0, 0, 1, 1, 0, -59, -10, 91, 117, -120, 1, -103, 88, 106, -115, -76, -92, 116, -28, 116, 76, 75, 36, 82, 27, 105, 98, -48, 122, -69, -33, 101, 54, -55, 15, 76, -103, 100, -128, 19, 9, 109, 5, 114, 11, 9, 74, -45, -98, 66, 78, 88, -125, 24, -56, -90, 56, 99, 109, -118, -7, -94, 22, 50, 105, -55, 116, 79, 44, 122, 23, -39, -23, -33, -96, -49, 109, 50, 103, -55, 7, -99, -48, 123, 10, 114, -89, -22, -116, 97, -22, 69, -71, 52, 1, -78, 123, 121, 3, 71, -51, 60, 47, -51, -6, 83, 101, 113, 48, -56, 41, 76, 127, -27, 6, 46, 61, -45, -37, 74, 127, -85, -64, 99, 106, -88, 27, -14, -42, -31, -5, 69, 89, -57, -12, -115, 78, -44, -14, 92, 37, -68, -98, 38, -116, -97, 75, 119, -109, -101, 104, -109, -102, -42, 29, -44, 68, 117, -87, -119, 123, -110, 107, -14, 69, 85, 51, 21, 3, 63, 25, -18, 30, -95, 94, -100, 43, -70, -3, -121, 66, 82, -119, 26, -51, -102, -60, -2, -11, -92, 100, 122, 86, 120, -74, 124, 54, 120, -111, 105, -7, 116, -26, -17, -40, -32, 57, 84, -72, -109, 15, -26, 25, -75, 13, 30, 116, 1, 10, 57, 28, -124, 77, -96, -125, -62, 0, -96, 95, -118, -115, -63, -47, -124, -4, 13, -5, 115, 80, 122, 43, -85, 70, 102, -75, -122, -115, 118, -113, 0, 42, 78, 22, 60, 35, 85, 88, 68, 35, -60, -49, -128, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 7, 116, 101, 115, 116, 114, 115, 97, 0, 0, 0, 20, 0, 0, 0, 6, 106, 101, 114, 111, 101, 110, 0, 0, 0, 6, 110, 111, 98, 111, 100, 121, 0, 0, 0, 0, 88, -19, 76, -72, 0, 0, 0, 0, 88, -19, 84, 23, 0, 0, 0, 0, 0, 0, 0, -126, 0, 0, 0, 21, 112, 101, 114, 109, 105, 116, 45, 88, 49, 49, 45, 102, 111, 114, 119, 97, 114, 100, 105, 110, 103, 0, 0, 0, 0, 0, 0, 0, 23, 112, 101, 114, 109, 105, 116, 45, 97, 103, 101, 110, 116, 45, 102, 111, 114, 119, 97, 114, 100, 105, 110, 103, 0, 0, 0, 0, 0, 0, 0, 22, 112, 101, 114, 109, 105, 116, 45, 112, 111, 114, 116, 45, 102, 111, 114, 119, 97, 114, 100, 105, 110, 103, 0, 0, 0, 0, 0, 0, 0, 10, 112, 101, 114, 109, 105, 116, 45, 112, 116, 121, 0, 0, 0, 0, 0, 0, 0, 14, 112, 101, 114, 109, 105, 116, 45, 117, 115, 101, 114, 45, 114, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 23, 0, 0, 0, 7, 115, 115, 104, 45, 114, 115, 97, 0, 0, 0, 3, 1, 0, 1, 0, 0, 1, 1, 0, -39, -47, -83, 61, 114, -97, -95, -80, -82, -59, -27, -24, 56, 122, -36, 30, -103, 108, -119, -116, 15, 123, 46, 84, -79, 2, -59, -114, -60, -45, 78, -78, 67, 98, -40, 80, -21, -123, -128, -87, 49, -24, -21, -42, -73, 107, 7, 92, -121, -53, 117, 24, -108, 109, 0, 27, 118, -26, -51, 125, 116, -69, 38, -99, -41, -65, -64, 30, 82, -81, 52, -60, 33, -22, -68, -84, 109, -18, -125, 14, 7, 79, 57, 84, 45, 102, -2, 84, 8, 43, -81, 60, 57, -25, 80, 9, -2, -7, 37, -22, -59, -122, -17, 84, 84, -118, -29, -64, 73, 93, -28, 100, -38, 98, -85, -100, 45, -94, 90, -55, -55, 9, 111, 53, -113, -46, 2, 3, 100, -72, 44, 75, 88, 19, 64, -30, 97, 26, -52, -39, 68, -3, -107, 72, 38, 115, 12, 75, 26, -94, -6, 38, 69, -101, 37, 49, 126, -28, -94, -14, 70, -13, -58, -75, -67, 58, 82, -114, 74, 57, -47, 101, -54, 49, 21, 103, -115, -5, -113, -4, 13, 78, -71, -10, -75, -76, 9, 104, -60, -7, -87, -93, 110, -121, -125, -14, -15, 101, 123, 104, 41, -41, -17, 118, 31, 36, 119, 107, -100, 38, -31, 56, -3, 76, 5, 127, 103, 79, -25, 105, 50, -62, -40, 94, 112, -94, -48, -81, -53, 44, 34, -119, 33, 105, -4, -56, -36, 49, 62, 106, -44, 115, 67, -92, -88, -26, 16, 89, -72, -58, -112, -87, -10, 118, -33, -9, 0, 0, 1, 15, 0, 0, 0, 7, 115, 115, 104, 45, 114, 115, 97, 0, 0, 1, 0, -46, -20, -128, -63, 96, -123, 52, 81, 25, 92, -69, -78, -41, -23, 112, 119, -43, 109, -128, 39, 2, -25, 65, -54, 51, 33, -14, -64, -84, -28, -56, -87, 67, 33, -69, 10, 108, 34, -36, 64, 110, -8, 127, 73, -61, 63, -73, 62, -14, 48, 5, 68, 44, 75, 125, -95, -103, -25, -108, -104, 94, 67, -24, 92, -61, -113, -88, 91, 37, -97, -74, 47, -119, -48, -69, -98, 82, -95, 23, 100, 59, 22, 3, -127, 119, 78, 18, -94, -72, -128, -66, 45, 76, -37, -127, 0, 19, -26, 61, 45, 76, -6, -119, -46, -60, -71, -24, 23, -73, 125, -100, -95, -69, -2, 126, -91, 74, 68, 22, 116, 87, -91, 41, 13, -50, 86, -25, -49, -6, -108, 57, 54, 96, -61, 78, 33, -76, 16, 55, 45, -8, 45, -3, 71, -88, -120, 113, 105, 96, -70, -90, 105, -45, -34, -77, -36, 103, 41, 111, 100, -114, -16, 63, -97, 3, 118, 18, 39, 90, -127, 21, 49, -106, -80, 103, 73, -76, 63, 45, -126, -40, -10, -5, -52, -20, 5, 99, -51, 103, 94, -25, 2, 29, 71, -62, 47, -123, 39, -128, -35, 80, 56, -72, 61, -1, 75, -16, 25, -103, -108, 86, 68, -25, 62, -58, -34, 31, -88, 28, 6, -124, 24, -23, 119, 99, 108, -16, 124, 28, -1, -38, -43, 72, 43, -37, 11, 19, -56, 82, 74, 117, -51, 126, -26, -65, -54, -75, -64, -117, -119, 119, -75, 74, 105, -121, 64");
byte[] msg = fromString("116, 101, 115, 116, 10");
byte[] sig = fromString("0, 0, 0, 7, 115, 115, 104, 45, 114, 115, 97, 0, 0, 1, 0, 121, 15, 34, -11, 125, 124, 91, 62, -47, 36, 77, -44, 13, -29, -36, -100, 65, -87, -67, -64, 11, 31, -24, -35, -53, 74, 18, 65, 12, 119, -33, -105, 65, 21, -55, -21, 89, 49, -97, -98, -48, 49, -20, -75, 88, 3, -84, 105, -51, -83, 92, 117, -118, -63, -5, -119, -69, -25, 22, -62, 83, -106, -112, -3, -104, -95, 89, -72, -59, -6, -18, 114, 113, 43, 32, 31, -11, 27, -39, -58, 75, -42, -30, -48, -81, 15, -92, 71, -85, -45, -101, 101, -60, 54, 7, -69, -120, -69, -3, 125, -52, -29, 44, 120, -50, -29, -38, -50, 7, 51, -68, -16, -87, -114, -105, 22, 31, 104, 110, 89, -50, -35, 124, 79, 26, 82, -26, 121, 47, 80, 26, -80, 65, 6, 35, -90, 87, -30, 63, -24, -82, -79, 79, -88, -26, 55, 120, 91, 119, 56, -26, 44, -106, -89, 118, -105, 88, -98, -46, -115, -6, -69, 121, -63, 100, -55, -126, -106, 79, -6, -56, 26, -26, 96, -113, -95, -121, -16, 44, -122, 6, 90, -56, -7, 99, 91, -67, 33, -53, -48, 63, 85, -29, 95, 35, 110, -89, 67, 84, -116, 85, -85, -21, -26, 19, 15, 36, 99, 22, 80, -99, -41, -119, 89, -36, -26, 113, -53, -39, 94, 74, 5, -96, -96, -121, -56, -61, 62, -14, 20, 69, 10, 80, -102, -68, -19, -59, 30, -76, -2, 89, -118, -48, 104, -46, 45, -63, 112, -110, 68, -108, 48, 61, 73, 103, 62");
PublicKey hostKey = new Buffer.PlainBuffer(certBytes).readPublicKey();
Signature signature = new SignatureRSA.FactoryCERT().create();
signature.initVerify(hostKey);
signature.update(msg, 0, msg.length);
Assert.assertTrue("RSACERT signature verifies", signature.verify(sig));
}
private byte[] fromString(String string) {
String[] split = string.split(", ");
byte[] res = new byte[split.length];
for (int i = 0; i < split.length; i++)
res[i] = Byte.parseByte(split[i]);
return res;
}
}

View File

@@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,5F1AEDBE1E9E4D83EBD9C7B71CD84DB5
N8SuKdWK3dEA7K6dNodjE++qqXPpVXJlDhiJbrXm1vbs2u+hckzZflK9OQZU1SsY
hs6ZyFg/jagdMNEKKrCEK60QMcMg1iPlkMvTwEvQmdWSWtMiTFes9Ec4njUbcbgN
qHzixGfWjpTXCRq3QC2Bydq7+njvIEUeLIjTaCIXqwoZoMcBc0N6Aj529gbgDD/i
JC0auaSfejSZQm4Ppn+OuO1JKgNkwsjoTrrZjbKHuayYQaioaR5OcAKgSSs5YDdo
SJI+PDeIOVb6aMcXwYP5J7C9zzkZU1Ml3dGHk2JMs0Omu2gICRm3mXXmoHRkiBKj
2h7EsghiV/Be4lkOF+T3HN19F4G44EIUj7DotbtqNPEFRZa+SSpN3OZmLJQSwBXD
A0zek5BxDu7M4VfbLryxNWcHinLe8fBKLQJe3F2/c5ZbMA4Z9098FIL1xWOqvbzS
SjiuWhIDxdYVeVe579mUlWjPrMF3NnD4BP9oQlYmbSTxL7B486Ckh7ACFzQhUXVV
t2JNZC2FcxM2WU7cHABvGUQNeq/Y2pB5Xq8AmzdpdAs2VZLHtgOvmnPjfo+Q3s1Y
rlRe5sCkyn0Ju4M09sMBIQfwszVKa3/l/ZVrG5LCOEHkF+35ex6Gz8ePRlm5jhnU
ZQVuLjxbnjnDMaJ0c0IoW7s44cdEDkv3fDzfQHrPzM50oFUN5WOKKWFnT4V1DBLJ
9YW8AcNL6bSlgZNkLntn5Wvl3BBKbf5SMmqegfR3eqz50KNwzKKQU8dcIwkJsK2s
Z5fNWbydgNDZhrebJL/jpuXpXcvfO0cVMja7y/O0bQ6M37Arpk8bQhTBKZZSaXeF
I0Bx3IOxsodWpA9O3Yum8fqqPGrCxsAsP/mWNG0Ov29Myi3MeZ0zgQH283En0Pnx
8hvDlw8vNcFe22Jycgg4846FlIWzkT7VfDVxDoYCyjlMTZ2ASys8llH5in4i1w4m
j0ZREJE/+evCXBHLoTO96Rgnjt7hp9g62FHJ+ivRlCQVY6sTB03GNv//4v7xGMPF
S4eBB9gnGKEEe8zRcOPgUEhUOrFv9cUpdhYz/SLuTZIPPiNwkAMZ6zM77FMsrdWw
18wiemOhizCd/JZDlH9COue6EXnr7rexmTp7rUsJOTq8q3rpYmz2s0rGJbqxyxlq
BiCjJqN004ZmtFCTD0wCGuNsVLiNyWSspIfzWXkfCyO0SbiH8QjlkUZVr456/5sI
cvCQ3ltAFfxR8wMZlfgtT3mEu8JAQCml89yMbttisdfz48XnLeXAzZuZzEr4OllN
AMQ8RINlZC2fxqV25jJi+E1da0yvwM9x0NPaM/ZUQqGzMf+dGldcVWdlNFBsthx0
wULaGfTYmmn6rBdbeGlt0JKGGL8Ak8+adNSHHxaJuL9W/5wLH41HsE89ZldlTHuI
GeHM1lDA/DwO85c4Fk7Ai0Ny44PjdGn27pLNhAfoT2HivF7zMNV+SUXU57iL/qdR
yJYGZJyXdreK4qaSCx7BdaK0AFvhBTXW/uRXJCX/XW6zdzdqy5/wkqWt1sWHKTxf
Z0CBKWd/se6JCQoixG2Cpo87SeLZsJEscK8eDdLDQwJdKdmIqgw6LddF7RPpq/5D
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1 @@
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb0+Llm+3BKq6n5YoMepI1/Iqp4WnSFYYbpm9Y0jLbJMAAAADAQABAAABAQDF9lt1iAGZWGqNtKR05HRMSyRSG2li0Hq732U2yQ9MmWSAEwltBXILCUrTnkJOWIMYyKY4Y22K+aIWMmnJdE8sehfZ6d+gz20yZ8kHndB7CnKn6oxh6kW5NAGye3kDR808L836U2VxMMgpTH/lBi4909tKf6vAY2qoG/LW4ftFWcf0jU7U8lwlvJ4mjJ9Ld5ObaJOa1h3URHWpiXuSa/JFVTMVAz8Z7h6hXpwruv2HQlKJGs2axP71pGR6Vni2fDZ4kWn5dObv2OA5VLiTD+YZtQ0edAEKORyETaCDwgCgX4qNwdGE/A37c1B6K6tGZrWGjXaPACpOFjwjVVhEI8TPgAAAAAAAAAEAAAABAAAAB3Rlc3Ryc2EAAAAUAAAABmplcm9lbgAAAAZub2JvZHkAAAAAWO1MuP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBANnRrT1yn6GwrsXl6Dh63B6ZbImMD3suVLECxY7E006yQ2LYUOuFgKkx6OvWt2sHXIfLdRiUbQAbdubNfXS7Jp3Xv8AeUq80xCHqvKxt7oMOB085VC1m/lQIK688OedQCf75JerFhu9UVIrjwEld5GTaYqucLaJayckJbzWP0gIDZLgsS1gTQOJhGszZRP2VSCZzDEsaovomRZslMX7kovJG88a1vTpSjko50WXKMRVnjfuP/A1Oufa1tAloxPmpo26Hg/LxZXtoKdfvdh8kd2ucJuE4/UwFf2dP52kywthecKLQr8ssIokhafzI3DE+atRzQ6So5hBZuMaQqfZ23/cAAAEPAAAAB3NzaC1yc2EAAAEA0uyAwWCFNFEZXLuy1+lwd9VtgCcC50HKMyHywKzkyKlDIbsKbCLcQG74f0nDP7c+8jAFRCxLfaGZ55SYXkPoXMOPqFsln7YvidC7nlKhF2Q7FgOBd04SoriAvi1M24EAE+Y9LUz6idLEuegXt32cobv+fqVKRBZ0V6UpDc5W58/6lDk2YMNOIbQQNy34Lf1HqIhxaWC6pmnT3rPcZylvZI7wP58DdhInWoEVMZawZ0m0Py2C2Pb7zOwFY81nXucCHUfCL4UngN1QOLg9/0vwGZmUVkTnPsbeH6gcBoQY6XdjbPB8HP/a1Ugr2wsTyFJKdc1+5r/KtcCLiXe1SmmHQA== adami@computer