Compare commits

..

1 Commits

Author SHA1 Message Date
hierynomus
936c6c8008 v0.11.0 2015-01-23 10:01:50 +01:00
106 changed files with 1006 additions and 3537 deletions

View File

@@ -1,2 +1 @@
language: java
sudo: false
language: java

View File

@@ -1,7 +1,7 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.15.0
:sshj_version: 0.11.0
:source-highlighter: pygments
image::https://travis-ci.org/hierynomus/sshj.svg?branch=master[]
@@ -59,15 +59,13 @@ In the `examples` directory, there is a separate Maven project that shows how th
Implementations / adapters for the following algorithms are included:
ciphers::
`aes{128,192,256}-{cbc,ctr}`, `blowfish-{cbc,ctr}`, `3des-{cbc,ctr}`, `twofish{128,192,256}-{cbc,ctr}`, `twofish-cbc`, `serpent{128,192,256}-{cbc,ctr}`, `idea-{cbc,ctr}`, `cast128-{cbc,ctr}`, `arcfour`, `arcfour{128,256}`
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
`aes{128,192,256}-{cbc,ctr}`, `blowfish-cbc`, `3des-cbc`
key exchange::
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`, `diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`
signatures::
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519
`ssh-rsa`, `ssh-dss`
mac::
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`
@@ -80,8 +78,6 @@ private key files::
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
== Comparing to other implementations
http://ssh-comparison.quendi.de/comparison.html[SSH Implementation Comparison]
== Dependencies
Java 6+. http://www.slf4j.org/download.html[slf4j] is required. http://www.bouncycastle.org/java.html[bouncycastle] is highly recommended and required for using some of the crypto algorithms. http://www.jcraft.com/jzlib/[jzlib] is required for using zlib compression.
@@ -96,31 +92,8 @@ Google Group: http://groups.google.com/group/sshj-users
Fork away!
== Release history
SSHJ 0.15.0 (2015-11-20)::
* Fixed https://github.com/hierynomus/sshj/issues/220[#220]: Added support for `ssh-ed25519` host keys
* Fixed https://github.com/hierynomus/sshj/issues/225[#225]: Fixed bug in ECDSA fingerprint calculation that sometimes produced an incorrect fingerprint
* Added `arcfour` Stream Ciphers from RFC4253 and RFC4345
* Added all Block Ciphers from RFC4344 and RFC4253
SSHJ 0.14.0 (2015-11-04)::
* Fixed https://github.com/hierynomus/sshj/issues/171[#171]: Added support for `curve25519-sha256@libssh.org` key exchange algorithm
* Added support for `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384` and `ecdh-sha2-nistp521` key exchange algorithms
* Fixed https://github.com/hierynomus/sshj/issues/167[#167]: Added support for `diffie-hellman-group-exchange-sha1` and `diffie-hellman-group-exchange-sha256` key exchange methods
* Fixed https://github.com/hierynomus/sshj/issues/212[#212]: Configure path escaping to enable shell expansion to work correctly
* Merged https://github.com/hierynomus/sshj/issues/210[#210]: RemoteFileInputStream.skip returns wrong value (Fixes https://github.com/hierynomus/sshj/issues/209[#209])
* Merged https://github.com/hierynomus/sshj/issues/208[#208]: Added SCP bandwidth limitation support
* Merged https://github.com/hierynomus/sshj/issues/211[#211]: Made keyfile format detection more robust
SSHJ 0.13.0 (2015-08-18)::
* Merged https://github.com/hierynomus/sshj/issues/199[#199]: Fix for IndexOutOfBoundsException in ReadAheadRemoteFileInputStream, fixes https://github.com/hierynomus/sshj/issues/183[#183]
* Merged https://github.com/hierynomus/sshj/issues/195[#195]: New authentication supported: `gssapi-with-mic`
* Merged https://github.com/hierynomus/sshj/issues/201[#201]: New option to verify negotiated key exchange algorithms
* Merged https://github.com/hierynomus/sshj/issues/196[#196]: Fix for looking up complete hostname in known hosts file
SSHJ 0.12.0 (2015-04-14)::
* Added support for HTTP proxies when running JDK6 or JDK7, fixes: https://github.com/hierynomus/sshj/issues/170[#170]
* Merged https://github.com/hierynomus/sshj/issues/186[#186]: Fix for detecting end-of-stream
* Compiling to JDK6, fixes https://github.com/hierynomus/sshj/issues/179[#179] and https://github.com/hierynomus/sshj/issues/185[#185]
* Correctly close socket and channel when LocalPortForwarder fails to open and start the channel (Fixes https://github.com/hierynomus/sshj/issues/175[#175] and https://github.com/hierynomus/sshj/issues/176[#176])
* Merged https://github.com/hierynomus/sshj/issues/181[#181]: Invalid write packet length when reading with offset (Fixes https://github.com/hierynomus/sshj/issues/180[#180])
SSHJ 0.11.0 (2015-01-23)::
SSHJ 0.11.0 (No date set yet)::
* New maven coordinates `com.hierynomus:sshj:0.11.0` as https://github.com/hierynomus[@hierynomus] took over as maintainer of SSHJ
* Migrated build system to Gradle 2.2.1
* Merged https://github.com/hierynomus/sshj/issues/150[#150]: Fix for incorrect file handle on some SSH servers, fixes: https://github.com/hierynomus/sshj/issues/54[#54], https://github.com/hierynomus/sshj/issues/119[#119], https://github.com/hierynomus/sshj/issues/168[#168], https://github.com/hierynomus/sshj/issues/169[#169]

View File

@@ -4,16 +4,13 @@ apply plugin: "signing"
apply plugin: "osgi"
group = "com.hierynomus"
version = "0.15.0"
version = "0.11.0"
repositories {
mavenCentral()
mavenLocal()
}
sourceCompatibility = 1.6
targetCompatibility = 1.6
configurations {
compile {
transitive = false
@@ -21,17 +18,7 @@ configurations {
}
test {
testLogging {
exceptionFormat = 'full'
}
include "**/*Test.*"
if (!project.hasProperty("allTests")) {
useJUnit {
excludeCategories 'com.hierynomus.sshj.test.SlowTests'
excludeCategories 'com.hierynomus.sshj.test.KnownFailingTests'
}
}
afterSuite { descriptor, result ->
if (descriptor.className != null) {
def indicator = "\u001B[32m✓\u001b[0m"
@@ -51,11 +38,9 @@ dependencies {
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
compile "com.jcraft:jzlib:1.1.3"
compile "net.vrallev.ecc:ecc-25519-java:1.0.1"
testCompile "junit:junit:4.11"
testCompile "org.mockito:mockito-core:1.9.5"
testCompile "org.apache.sshd:sshd-core:1.0.0"
testCompile "org.apache.sshd:sshd-core:0.11.0"
testRuntime "ch.qos.logback:logback-classic:1.1.2"
}

View File

@@ -21,10 +21,10 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.hierynomus</groupId>
<artifactId>sshj-examples</artifactId>
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<packaging>jar</packaging>
<version>0.14.0</version>
<version>0.10.0</version>
<name>sshj-examples</name>
<description>Examples for SSHv2 library for Java</description>
@@ -53,9 +53,9 @@
<dependencies>
<dependency>
<groupId>com.hierynomus</groupId>
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<version>0.15.0</version>
<version>0.10.0</version>
</dependency>
</dependencies>

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Wed Nov 04 09:47:51 CET 2015
#Wed Jan 21 09:17:25 CET 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip

344
pom.xml Normal file
View File

@@ -0,0 +1,344 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<packaging>bundle</packaging>
<version>0.10.1-SNAPSHOT</version>
<name>sshj</name>
<description>SSHv2 library for Java</description>
<url>http://github.com/hierynomus/sshj</url>
<inceptionYear>2009</inceptionYear>
<issueManagement>
<system>github</system>
<url>http://github.com/hierynomus/sshj/issues</url>
</issueManagement>
<scm>
<connection>scm:git:git://github.com/hierynomus/sshj.git</connection>
<developerConnection>scm:git:git@github.com:hierynomus/sshj.git</developerConnection>
<url>http://github.com/hierynomus/sshj</url>
</scm>
<licenses>
<license>
<name>Apache 2</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.51</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.51</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<developers>
<developer>
<id>hierynomus</id>
<name>Jeroen van Erp</name>
<email>jeroen@hierynomus.com</email>
</developer>
<developer>
<id>shikhar</id>
<name>Shikhar Bhushan</name>
<email>shikhar@schmizz.net</email>
<url>http://schmizz.net</url>
</developer>
<developer>
<id>iterate</id>
<name>David Kocher</name>
<email>dkocher@iterate.ch</email>
<organization>iterate GmbH</organization>
<organizationUrl>https://iterate.ch</organizationUrl>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
!net.schmizz.*,
javax.crypto*,
com.jcraft.jzlib*;version="[1.1,2)";resolution:=optional,
org.slf4j*;version="[1.7,5)",
org.bouncycastle*,
*
</Import-Package>
<Export-Package>net.schmizz.*</Export-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>clirr-maven-plugin</artifactId>
<version>2.6.1</version>
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.6</version>
<configuration>
<header>src/etc/license-header</header>
<properties>
<owner>sshj contributors</owner>
<email>sshj-users@googlegroups.com</email>
</properties>
<excludes>
<exclude>**/README</exclude>
<exclude>src/test/resources/**</exclude>
<exclude>src/main/resources/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.2</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
--> </plugins>
</build>
<distributionManagement>
<repository>
<id>sonatype-nexus-staging</id>
<name>Nexus Release Repository</name>
<url>http://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
<snapshotRepository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
<profiles>
<profile>
<id>full-deps</id>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>doclint-java8-disable</id>
<activation>
<jdk>[1.8,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>clirr-maven-plugin</artifactId>
<version>2.6.1</version>
</plugin>
</plugins>
</reporting>
</project>

View File

@@ -0,0 +1,39 @@
package nl.javadude.sshj.connection.channel;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
public class ChannelCloseEofTest {
private SSHClient sshClient;
@Before
public void setUp() throws Exception {
sshClient = new SSHClient();
}
@After
public void tearDown() throws IOException {
sshClient.disconnect();
}
@Test
public void shouldCorrectlyHandleSessionChannelEof() throws IOException, InterruptedException {
sshClient.addHostKeyVerifier(new PromiscuousVerifier());
sshClient.connect("172.16.37.129");
sshClient.authPassword("jeroen", "jeroen");
Session session = sshClient.startSession();
session.allocateDefaultPTY();
session.close();
Thread.sleep(1000);
assertThat("Should still be connected", sshClient.isConnected());
}
}

View File

@@ -1,13 +0,0 @@
package com.hierynomus.sshj.backport;
import java.math.BigDecimal;
public class JavaVersion {
public static boolean isJava7OrEarlier() {
String property = System.getProperty("java.specification.version");
float diff = Float.parseFloat(property) - 1.7f;
return diff < 0.01;
}
}

View File

@@ -1,62 +0,0 @@
package com.hierynomus.sshj.backport;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.nio.charset.Charset;
public class Jdk7HttpProxySocket extends Socket {
private Proxy httpProxy = null;
public Jdk7HttpProxySocket(Proxy proxy) {
super(proxy.type() == Proxy.Type.HTTP ? Proxy.NO_PROXY : proxy);
if (proxy.type() == Proxy.Type.HTTP) {
this.httpProxy = proxy;
}
}
@Override
public void connect(SocketAddress endpoint, int timeout) throws IOException {
if (httpProxy != null) {
connectHttpProxy(endpoint, timeout);
} else {
super.connect(endpoint, timeout);
}
}
private void connectHttpProxy(SocketAddress endpoint, int timeout) throws IOException {
super.connect(httpProxy.address(), timeout);
if (!(endpoint instanceof InetSocketAddress)) {
throw new SocketException("Expected an InetSocketAddress to connect to, got: " + endpoint);
}
InetSocketAddress isa = (InetSocketAddress) endpoint;
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
checkAndFlushProxyResponse();
}
private void checkAndFlushProxyResponse()throws IOException {
InputStream socketInput = getInputStream();
byte[] tmpBuffer = new byte[512];
int len = socketInput.read(tmpBuffer, 0, tmpBuffer.length);
if (len == 0) {
throw new SocketException("Empty response from proxy");
}
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
// Expecting HTTP/1.x 200 OK
if (proxyResponse.contains("200")) {
// Flush any outstanding message in buffer
if (socketInput.available() > 0) {
socketInput.skip(socketInput.available());
}
// Proxy Connect Successful
} else {
throw new SocketException("Fail to create Socket\nResponse was:" + proxyResponse);
}
}
}

View File

@@ -1,26 +0,0 @@
package com.hierynomus.sshj.backport;
import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
public class Sockets {
/**
* Java 7 and up have Socket implemented as Closeable, whereas Java6 did not have this inheritance.
* @param socket The socket to wrap as Closeable
* @return The (potentially wrapped) Socket as a Closeable.
*/
public static Closeable asCloseable(final Socket socket) {
if (Closeable.class.isAssignableFrom(socket.getClass())) {
return Closeable.class.cast(socket);
} else {
return new Closeable() {
@Override
public void close() throws IOException {
socket.close();
}
};
}
}
}

View File

@@ -1,55 +0,0 @@
package com.hierynomus.sshj.secg;
import net.schmizz.sshj.common.SSHRuntimeException;
import java.math.BigInteger;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.util.Arrays;
public class SecgUtils {
/**
* SECG 2.3.4 Octet String to ECPoint
*/
public static ECPoint getDecoded(byte[] M, EllipticCurve curve) {
int elementSize = getElementSize(curve);
if (M.length != 2 * elementSize + 1 || M[0] != 0x04) {
throw new SSHRuntimeException("Invalid 'f' for Elliptic Curve " + curve.toString());
}
byte[] xBytes = new byte[elementSize];
byte[] yBytes = new byte[elementSize];
System.arraycopy(M, 1, xBytes, 0, elementSize);
System.arraycopy(M, 1 + elementSize, yBytes, 0, elementSize);
return new ECPoint(new BigInteger(1, xBytes), new BigInteger(1, yBytes));
}
/**
* SECG 2.3.3 ECPoint to Octet String
*/
public static byte[] getEncoded(ECPoint point, EllipticCurve curve) {
int elementSize = getElementSize(curve);
byte[] M = new byte[2 * elementSize + 1];
M[0] = 0x04;
byte[] xBytes = stripLeadingZeroes(point.getAffineX().toByteArray());
byte[] yBytes = stripLeadingZeroes(point.getAffineY().toByteArray());
System.arraycopy(xBytes, 0, M, 1 + elementSize - xBytes.length, xBytes.length);
System.arraycopy(yBytes, 0, M, 1 + 2 * elementSize - yBytes.length, yBytes.length);
return M;
}
private static byte[] stripLeadingZeroes(byte[] bytes) {
int start = 0;
while (bytes[start] == 0x0) {
start++;
}
return Arrays.copyOfRange(bytes, start, bytes.length);
}
private static int getElementSize(EllipticCurve curve) {
int fieldSize = curve.getField().getFieldSize();
return (fieldSize + 7) / 8;
}
}

View File

@@ -1,40 +0,0 @@
package com.hierynomus.sshj.signature;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import net.schmizz.sshj.common.SSHRuntimeException;
import java.util.Arrays;
/**
* Our own extension of the EdDSAPublicKey that comes from ECC-25519, as that class does not implement equality.
* The code uses the equality of the keys as an indicator whether they're the same during host key verification.
*/
public class Ed25519PublicKey extends EdDSAPublicKey {
public Ed25519PublicKey(EdDSAPublicKeySpec spec) {
super(spec);
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
if (!spec.getParams().getCurve().equals(ed25519.getCurve())) {
throw new SSHRuntimeException("Cannot create Ed25519 Public Key from wrong spec");
}
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Ed25519PublicKey)) {
return false;
}
Ed25519PublicKey otherKey = (Ed25519PublicKey) other;
return Arrays.equals(getAbyte(), otherKey.getAbyte());
}
@Override
public int hashCode() {
return getA().hashCode();
}
}

View File

@@ -1,94 +0,0 @@
package com.hierynomus.sshj.signature;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.signature.Signature;
import java.security.*;
public class SignatureEdDSA implements Signature {
public static class Factory implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public String getName() {
return KeyType.ED25519.toString();
}
@Override
public Signature create() {
return new SignatureEdDSA();
}
}
final EdDSAEngine engine;
protected SignatureEdDSA() {
try {
engine = new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
} catch (NoSuchAlgorithmException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public void init(PublicKey pubkey, PrivateKey prvkey) {
try {
if (pubkey != null) {
engine.initVerify(pubkey);
}
if (prvkey != null) {
engine.initSign(prvkey);
}
} catch (InvalidKeyException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public void update(byte[] H) {
update(H, 0, H.length);
}
@Override
public void update(byte[] H, int off, int len) {
try {
engine.update(H, off, len);
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public byte[] sign() {
try {
return engine.sign();
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public byte[] encode(byte[] signature) {
return signature;
}
@Override
public boolean verify(byte[] sig) {
try {
Buffer.PlainBuffer plainBuffer = new Buffer.PlainBuffer(sig);
String algo = plainBuffer.readString();
if (!"ssh-ed25519".equals(algo)) {
throw new SSHRuntimeException("Expected 'ssh-ed25519' key algorithm, but was: " + algo);
}
byte[] bytes = plainBuffer.readBytes();
return engine.verify(bytes);
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
} catch (Buffer.BufferException e) {
throw new SSHRuntimeException(e);
}
}
}

View File

@@ -1,122 +0,0 @@
package com.hierynomus.sshj.transport.cipher;
import net.schmizz.sshj.transport.cipher.BaseCipher;
import net.schmizz.sshj.transport.cipher.BlockCipher;
import net.schmizz.sshj.transport.cipher.Cipher;
/**
* All BlockCiphers supported by SSH according to the following RFCs
*
* - https://tools.ietf.org/html/rfc4344#section-3.1
* - https://tools.ietf.org/html/rfc4253#section-6.3
*
* TODO: https://tools.ietf.org/html/rfc5647
*
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here.
*/
public class BlockCiphers {
public static final String COUNTER_MODE = "CTR";
public static final String CIPHER_BLOCK_CHAINING_MODE = "CBC";
public static Factory BlowfishCTR() {
return new Factory(8, 256, "blowfish-ctr", "Blowfish", COUNTER_MODE);
}
public static Factory Twofish128CTR() {
return new Factory(16, 128, "twofish128-ctr", "Twofish", COUNTER_MODE);
}
public static Factory Twofish192CTR() {
return new Factory(16, 192, "twofish192-ctr", "Twofish", COUNTER_MODE);
}
public static Factory Twofish256CTR() {
return new Factory(16, 256, "twofish256-ctr", "Twofish", COUNTER_MODE);
}
public static Factory Twofish128CBC() {
return new Factory(16, 128, "twofish128-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory Twofish192CBC() {
return new Factory(16, 192, "twofish192-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory Twofish256CBC() {
return new Factory(16, 256, "twofish256-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory TwofishCBC() {
return new Factory(16, 256, "twofish-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory Serpent128CTR() {
return new Factory(16, 128, "serpent128-ctr", "Serpent", COUNTER_MODE);
}
public static Factory Serpent192CTR() {
return new Factory(16, 192, "serpent192-ctr", "Serpent", COUNTER_MODE);
}
public static Factory Serpent256CTR() {
return new Factory(16, 256, "serpent256-ctr", "Serpent", COUNTER_MODE);
}
public static Factory Serpent128CBC() {
return new Factory(16, 128, "serpent128-cbc", "Serpent", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory Serpent192CBC() {
return new Factory(16, 192, "serpent192-cbc", "Serpent", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory Serpent256CBC() {
return new Factory(16, 256, "serpent256-cbc", "Serpent", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory IDEACTR() {
return new Factory(8, 128, "idea-ctr", "IDEA", COUNTER_MODE);
}
public static Factory IDEACBC() {
return new Factory(8, 128, "idea-cbc", "IDEA", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory Cast128CTR() {
return new Factory(8, 128, "cast128-ctr", "CAST5", COUNTER_MODE);
}
public static Factory Cast128CBC() {
return new Factory(8, 128, "cast128-cbc", "CAST5", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory TripleDESCTR() {
return new Factory(8, 192, "3des-ctr", "DESede", COUNTER_MODE);
}
/** Named factory for BlockCipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
private int keysize;
private String cipher;
private String mode;
private String name;
private int ivsize;
/**
* @param ivsize
* @param keysize The keysize used in bits.
* @param name
* @param cipher
* @param mode
*/
public Factory(int ivsize, int keysize, String name, String cipher, String mode) {
this.name = name;
this.keysize = keysize;
this.cipher = cipher;
this.mode = mode;
this.ivsize = ivsize;
}
@Override
public Cipher create() {
return new BlockCipher(ivsize, keysize / 8, cipher, cipher + "/" + mode + "/NoPadding");
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return getName();
}
}
}

View File

@@ -1,52 +0,0 @@
package com.hierynomus.sshj.transport.cipher;
import static com.hierynomus.sshj.transport.cipher.BlockCiphers.CIPHER_BLOCK_CHAINING_MODE;
import static com.hierynomus.sshj.transport.cipher.BlockCiphers.COUNTER_MODE;
/**
* Set of Block Ciphers that are (not yet) part of any of the official RFCs for SSH, but
* that are either supported by other SSH implementations, or are being pushed for to be
* included in a new RFC.
*
* - http://tools.ietf.org/id/draft-kanno-secsh-camellia-01.txt
*/
public class ExtendedBlockCiphers {
public static BlockCiphers.Factory Camellia128CTR() {
return new BlockCiphers.Factory(16, 128, "camellia128-ctr", "Camellia", COUNTER_MODE);
}
public static BlockCiphers.Factory Camellia128CTROpenSSHOrg() {
return new BlockCiphers.Factory(16, 128, "camellia128-ctr@openssh.org", "Camellia", COUNTER_MODE);
}
public static BlockCiphers.Factory Camellia192CTR() {
return new BlockCiphers.Factory(16, 192, "camellia192-ctr", "Camellia", COUNTER_MODE);
}
public static BlockCiphers.Factory Camellia192CTROpenSSHOrg() {
return new BlockCiphers.Factory(16, 192, "camellia192-ctr@openssh.org", "Camellia", COUNTER_MODE);
}
public static BlockCiphers.Factory Camellia256CTR() {
return new BlockCiphers.Factory(16, 256, "camellia256-ctr", "Camellia", COUNTER_MODE);
}
public static BlockCiphers.Factory Camellia256CTROpenSSHOrg() {
return new BlockCiphers.Factory(16, 256, "camellia256-ctr@openssh.org", "Camellia", COUNTER_MODE);
}
public static BlockCiphers.Factory Camellia128CBC() {
return new BlockCiphers.Factory(16, 128, "camellia128-cbc", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
}
public static BlockCiphers.Factory Camellia128CBCOpenSSHOrg() {
return new BlockCiphers.Factory(16, 128, "camellia128-cbc@openssh.org", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
}
public static BlockCiphers.Factory Camellia192CBC() {
return new BlockCiphers.Factory(16, 192, "camellia192-cbc", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
}
public static BlockCiphers.Factory Camellia192CBCOpenSSHOrg() {
return new BlockCiphers.Factory(16, 192, "camellia192-cbc@openssh.org", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
}
public static BlockCiphers.Factory Camellia256CBC() {
return new BlockCiphers.Factory(16, 256, "camellia256-cbc", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
}
public static BlockCiphers.Factory Camellia256CBCOpenSSHOrg() {
return new BlockCiphers.Factory(16, 256, "camellia256-cbc@openssh.org", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
}
}

View File

@@ -1,19 +0,0 @@
package com.hierynomus.sshj.transport.cipher;
import net.schmizz.sshj.transport.cipher.BaseCipher;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
public class StreamCipher extends BaseCipher {
public StreamCipher(int bsize, String algorithm, String transformation) {
super(0, bsize, algorithm, transformation);
}
@Override
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
cipher.init(getMode(mode), getKeySpec(key), new SecureRandom());
}
}

View File

@@ -1,61 +0,0 @@
package com.hierynomus.sshj.transport.cipher;
import net.schmizz.sshj.transport.cipher.Cipher;
/**
* Implementations of the Stream Ciphers that are defined in the RFCs
*
* - https://tools.ietf.org/html/rfc4253#section-6.3
* - https://tools.ietf.org/html/rfc4345
*/
public class StreamCiphers {
public static Factory Arcfour() {
return new Factory(128, "arcfour", "ARCFOUR", "ECB");
}
public static Factory Arcfour128() {
return new Factory(128, "arcfour128", "RC4", "ECB");
}
public static Factory Arcfour256() {
return new Factory(256, "arcfour256", "RC4", "ECB");
}
/** Named factory for BlockCipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
private int keysize;
private String cipher;
private String mode;
private String name;
/**
* @param keysize The keysize used in bits.
* @param name
* @param cipher
* @param mode
*/
public Factory(int keysize, String name, String cipher, String mode) {
this.name = name;
this.keysize = keysize;
this.cipher = cipher;
this.mode = mode;
}
@Override
public Cipher create() {
return new StreamCipher(keysize / 8, cipher, cipher + "/" + mode + "/NoPadding");
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return getName();
}
}
}

View File

@@ -15,9 +15,6 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.signature.SignatureEdDSA;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SecurityUtils;
@@ -34,7 +31,8 @@ import net.schmizz.sshj.transport.cipher.BlowfishCBC;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.cipher.TripleDESCBC;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.kex.*;
import net.schmizz.sshj.transport.kex.DHG1;
import net.schmizz.sshj.transport.kex.DHG14;
import net.schmizz.sshj.transport.mac.HMACMD5;
import net.schmizz.sshj.transport.mac.HMACMD596;
import net.schmizz.sshj.transport.mac.HMACSHA1;
@@ -51,7 +49,6 @@ import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.ByteLookupTable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
@@ -84,7 +81,7 @@ public class DefaultConfig
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String VERSION = "SSHJ_0_14_0";
private static final String VERSION = "SSHJ_0_9_2";
public DefaultConfig() {
setVersion(VERSION);
@@ -101,16 +98,9 @@ public class DefaultConfig
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered)
setKeyExchangeFactories(new Curve25519SHA256.Factory(),
new DHGexSHA256.Factory(),
new ECDHNistP.Factory521(),
new ECDHNistP.Factory384(),
new ECDHNistP.Factory256(),
new DHGexSHA1.Factory(),
new DHG14.Factory(),
new DHG1.Factory());
setKeyExchangeFactories(new DHG14.Factory(), new DHG1.Factory());
else
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
setKeyExchangeFactories(new DHG1.Factory());
}
protected void initRandomFactory(boolean bouncyCastleRegistered) {
@@ -134,29 +124,7 @@ public class DefaultConfig
new AES192CBC.Factory(),
new AES256CBC.Factory(),
new TripleDESCBC.Factory(),
new BlowfishCBC.Factory(),
BlockCiphers.BlowfishCTR(),
BlockCiphers.Cast128CBC(),
BlockCiphers.Cast128CTR(),
BlockCiphers.IDEACBC(),
BlockCiphers.IDEACTR(),
BlockCiphers.Serpent128CBC(),
BlockCiphers.Serpent128CTR(),
BlockCiphers.Serpent192CBC(),
BlockCiphers.Serpent192CTR(),
BlockCiphers.Serpent256CBC(),
BlockCiphers.Serpent256CTR(),
BlockCiphers.TripleDESCTR(),
BlockCiphers.Twofish128CBC(),
BlockCiphers.Twofish128CTR(),
BlockCiphers.Twofish192CBC(),
BlockCiphers.Twofish192CTR(),
BlockCiphers.Twofish256CBC(),
BlockCiphers.Twofish256CTR(),
BlockCiphers.TwofishCBC(),
StreamCiphers.Arcfour(),
StreamCiphers.Arcfour128(),
StreamCiphers.Arcfour256()));
new BlowfishCBC.Factory()));
boolean warn = false;
// Ref. https://issues.apache.org/jira/browse/SSHD-24
@@ -170,7 +138,6 @@ public class DefaultConfig
c.init(Cipher.Mode.Encrypt, key, iv);
} catch (Exception e) {
warn = true;
log.warn(e.getCause().getMessage());
i.remove();
}
}
@@ -178,11 +145,10 @@ public class DefaultConfig
log.warn("Disabling high-strength ciphers: cipher strengths apparently limited by JCE policy");
setCipherFactories(avail);
log.debug("Available cipher factories: {}", avail);
}
protected void initSignatureFactories() {
setSignatureFactories(new SignatureECDSA.Factory(), new SignatureRSA.Factory(), new SignatureDSA.Factory(), new SignatureEdDSA.Factory());
setSignatureFactories(new SignatureECDSA.Factory(), new SignatureRSA.Factory(), new SignatureDSA.Factory());
}
protected void initMACFactories() {

View File

@@ -39,7 +39,6 @@ import net.schmizz.sshj.transport.TransportImpl;
import net.schmizz.sshj.transport.compression.DelayedZlibCompression;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.compression.ZlibCompression;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import net.schmizz.sshj.userauth.UserAuth;
@@ -50,7 +49,6 @@ import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import net.schmizz.sshj.userauth.keyprovider.KeyPairWrapper;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
import net.schmizz.sshj.userauth.method.AuthGssApiWithMic;
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
import net.schmizz.sshj.userauth.method.AuthMethod;
import net.schmizz.sshj.userauth.method.AuthPassword;
@@ -60,19 +58,15 @@ import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.Resource;
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
import org.ietf.jgss.Oid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.login.LoginContext;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
@@ -159,19 +153,10 @@ public class SSHClient
* Add a {@link HostKeyVerifier} which will be invoked for verifying host key during connection establishment and
* future key exchanges.
*
* @param verifier {@link HostKeyVerifier} instance
* @param hostKeyVerifier {@link HostKeyVerifier} instance
*/
public void addHostKeyVerifier(HostKeyVerifier verifier) {
trans.addHostKeyVerifier(verifier);
}
/**
* Add a {@link AlgorithmsVerifier} which will be invoked for verifying negotiated algorithms.
*
* @param verifier {@link AlgorithmsVerifier} instance
*/
public void addAlgorithmsVerifier(AlgorithmsVerifier verifier) {
trans.addAlgorithmsVerifier(verifier);
public void addHostKeyVerifier(HostKeyVerifier hostKeyVerifier) {
trans.addHostKeyVerifier(hostKeyVerifier);
}
/**
@@ -380,30 +365,6 @@ public class SSHClient
authPublickey(username, keyProviders);
}
/**
* Authenticate {@code username} using the {@code "gssapi-with-mic"} authentication method, given a login context
* for the peer GSS machine and a list of supported OIDs.
* <p/>
* Supported OIDs should be ordered by preference as the SSH server will choose the first OID that it also
* supports. At least one OID is required
*
* @param username user to authenticate
* @param context {@code LoginContext} for the peer GSS machine
* @param supportedOid first supported OID
* @param supportedOids other supported OIDs
*
* @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error
*/
public void authGssApiWithMic(String username, LoginContext context, Oid supportedOid, Oid... supportedOids)
throws UserAuthException, TransportException {
// insert supportedOid to the front of the list since ordering matters
List<Oid> oids = new ArrayList<Oid>(Arrays.asList(supportedOids));
oids.add(0, supportedOid);
auth(username, new AuthGssApiWithMic(context, oids));
}
/**
* Disconnects from the connected SSH server. {@code SSHClient} objects are not reusable therefore it is incorrect
* to attempt connection after this method has been called.

View File

@@ -15,9 +15,6 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.backport.JavaVersion;
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
import javax.net.SocketFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -48,53 +45,34 @@ public abstract class SocketClient {
this.defaultPort = defaultPort;
}
public void connect(InetAddress host, int port) throws IOException {
public void connect(InetAddress host, int port)
throws IOException {
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(host, port), connectTimeout);
onConnect();
}
/**
* Connect to a host via a proxy.
* @param host The host address to connect to.
* @param port The port to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(InetAddress host, int port, Proxy proxy) throws IOException {
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);
}
public void connect(InetAddress host, int port, Proxy proxy)
throws IOException {
socket = new Socket(proxy);
socket.connect(new InetSocketAddress(host, port), connectTimeout);
onConnect();
}
public void connect(String hostname, int port) throws IOException {
public void connect(String hostname, int port)
throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port);
}
/**
* Connect to a host via a proxy.
* @param hostname The host name to connect to.
* @param port The port to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(String hostname, int port, Proxy proxy) throws IOException {
public void connect(String hostname, int port, Proxy proxy)
throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port, proxy);
}
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
public void connect(InetAddress host, int port,
InetAddress localAddr, int localPort)
throws IOException {
socket = socketFactory.createSocket();
socket.bind(new InetSocketAddress(localAddr, localPort));
@@ -102,44 +80,35 @@ public abstract class SocketClient {
onConnect();
}
public void connect(String hostname, int port, InetAddress localAddr, int localPort) throws IOException {
public void connect(String hostname, int port,
InetAddress localAddr, int localPort)
throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port, localAddr, localPort);
}
public void connect(InetAddress host) throws IOException {
public void connect(InetAddress host)
throws IOException {
connect(host, defaultPort);
}
public void connect(String hostname) throws IOException {
public void connect(String hostname)
throws IOException {
connect(hostname, defaultPort);
}
/**
* Connect to a host via a proxy.
* @param host The host address to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(InetAddress host, Proxy proxy) throws IOException {
public void connect(InetAddress host, Proxy proxy)
throws IOException {
connect(host, defaultPort, proxy);
}
/**
* Connect to a host via a proxy.
* @param hostname The host name to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(String hostname, Proxy proxy) throws IOException {
public void connect(String hostname, Proxy proxy)
throws IOException {
connect(hostname, defaultPort, proxy);
}
public void disconnect() throws IOException {
public void disconnect()
throws IOException {
if (socket != null) {
socket.close();
socket = null;
@@ -162,6 +131,7 @@ public abstract class SocketClient {
return socket.getLocalPort();
}
public InetAddress getLocalAddress() {
return socket.getLocalAddress();
}
@@ -179,11 +149,10 @@ public abstract class SocketClient {
}
public void setSocketFactory(SocketFactory factory) {
if (factory == null) {
if (factory == null)
socketFactory = SocketFactory.getDefault();
} else {
else
socketFactory = factory;
}
}
public SocketFactory getSocketFactory() {
@@ -218,7 +187,8 @@ public abstract class SocketClient {
return output;
}
void onConnect() throws IOException {
void onConnect()
throws IOException {
socket.setSoTimeout(timeout);
input = socket.getInputStream();
output = socket.getOutputStream();

View File

@@ -15,14 +15,6 @@
*/
package net.schmizz.sshj.common;
import com.hierynomus.sshj.secg.SecgUtils;
import com.hierynomus.sshj.signature.Ed25519PublicKey;
import net.i2p.crypto.eddsa.*;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.spec.ECParameterSpec;
@@ -33,7 +25,6 @@ import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.*;
import java.security.KeyFactory;
import java.security.interfaces.*;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;
@@ -144,7 +135,7 @@ public enum KeyType {
BigInteger bigY = new BigInteger(1, y);
X9ECParameters ecParams = NISTNamedCurves.getByName("p-256");
ECPoint pPublicPoint = ecParams.getCurve().createPoint(bigX, bigY);
ECPoint pPublicPoint = ecParams.getCurve().createPoint(bigX, bigY, false);
ECParameterSpec spec = new ECParameterSpec(ecParams.getCurve(),
ecParams.getG(), ecParams.getN());
ECPublicKeySpec publicSpec = new ECPublicKeySpec(pPublicPoint, spec);
@@ -160,54 +151,35 @@ public enum KeyType {
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
final ECPublicKey ecdsa = (ECPublicKey) pk;
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
final java.security.spec.ECPoint point = ecdsa.getW();
final byte[] x = trimStartingZeros(point.getAffineX().toByteArray());
final byte[] y = trimStartingZeros(point.getAffineY().toByteArray());
buf.putString(sType)
.putString(NISTP_CURVE)
.putBytes(encoded);
.putUInt32(1 + x.length + y.length)
.putRawBytes(new byte[] { (byte) 0x04 })
.putRawBytes(x)
.putRawBytes(y)
;
}
@Override
protected boolean isMyType(Key key) {
return ("ECDSA".equals(key.getAlgorithm()));
}
},
ED25519("ssh-ed25519") {
private final Logger logger = LoggerFactory.getLogger(KeyType.class);
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf) throws GeneralSecurityException {
try {
final int keyLen = buf.readUInt32AsInt();
final byte[] p = new byte[keyLen];
buf.readRawBytes(p);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Key algo: %s, Key curve: 25519, Key Len: %s\np: %s",
type,
keyLen,
Arrays.toString(p))
);
private byte[] trimStartingZeros(byte[] in) {
int i = 0;
for (; i < in.length; i++) {
if (in[i] != 0) {
break;
}
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
GroupElement point = ed25519.getCurve().createPoint(p, true);
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(point, ed25519);
return new Ed25519PublicKey(publicSpec);
} catch (Buffer.BufferException be) {
throw new SSHRuntimeException(be);
}
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
EdDSAPublicKey key = (EdDSAPublicKey) pk;
buf.putString(sType).putBytes(key.getAbyte());
}
@Override
protected boolean isMyType(Key key) {
return "EdDSA".equals(key.getAlgorithm());
final byte[] out = new byte[in.length - i];
System.arraycopy(in, i, out, 0, out.length);
return out;
}
},

View File

@@ -30,7 +30,7 @@ public enum Message {
KEXDH_INIT(30),
/** { KEXDH_REPLY, KEXDH_GEX_GROUP, SSH_MSG_KEX_ECDH_REPLY } */
/** { KEXDH_REPLY, KEXDH_GEX_GROUP } */
KEXDH_31(31),
KEX_DH_GEX_INIT(32),
@@ -46,9 +46,6 @@ public enum Message {
USERAUTH_60(60),
USERAUTH_INFO_RESPONSE(61),
USERAUTH_GSSAPI_EXCHANGE_COMPLETE(63),
USERAUTH_GSSAPI_MIC(66),
GLOBAL_REQUEST(80),
REQUEST_SUCCESS(81),
REQUEST_FAILURE(82),

View File

@@ -66,7 +66,7 @@ public class ConnectionImpl
*
* @param config the ssh config
* @param trans transport layer
* @param keepAlive the keep alive provider
* @param keepAlive
*/
public ConnectionImpl(Transport trans, KeepAliveProvider keepAlive) {
super("ssh-connection", trans);

View File

@@ -18,12 +18,11 @@ package net.schmizz.sshj.connection.channel;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.IOUtils;
import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
public class SocketStreamCopyMonitor
extends Thread {
@@ -33,6 +32,16 @@ public class SocketStreamCopyMonitor
setDaemon(true);
}
private static Closeable wrapSocket(final Socket socket) {
return new Closeable() {
@Override
public void close()
throws IOException {
socket.close();
}
};
}
public static void monitor(final int frequency, final TimeUnit unit,
final Event<IOException> x, final Event<IOException> y,
final Channel channel, final Socket socket) {
@@ -45,7 +54,7 @@ public class SocketStreamCopyMonitor
}
} catch (IOException ignored) {
} finally {
IOUtils.closeQuietly(channel, asCloseable(socket));
IOUtils.closeQuietly(channel, wrapSocket(socket));
}
}
}).start();

View File

@@ -16,12 +16,12 @@
package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,8 +30,6 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
public class LocalPortForwarder {
public static class Parameters {
@@ -114,15 +112,11 @@ public class LocalPortForwarder {
this.serverSocket = serverSocket;
}
private void startChannel(Socket socket) throws IOException {
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
try {
chan.open();
chan.start();
} catch (IOException e) {
IOUtils.closeQuietly(chan, asCloseable(socket));
throw e;
}
protected DirectTCPIPChannel openChannel(Socket socket)
throws TransportException, ConnectionException {
final DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
chan.open();
return chan;
}
/**
@@ -136,7 +130,7 @@ public class LocalPortForwarder {
while (!Thread.currentThread().isInterrupted()) {
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
startChannel(socket);
openChannel(socket).start();
}
log.debug("Interrupted!");
}

View File

@@ -1,12 +1,12 @@
/**
* Copyright 2009 sshj contributors
* <p/>
*
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
*
* 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.
@@ -19,7 +19,6 @@ import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.sftp.Response.StatusCode;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -34,31 +33,37 @@ public class RemoteFile
super(requester, path, handle);
}
public FileAttributes fetchAttributes() throws IOException {
public FileAttributes fetchAttributes()
throws IOException {
return requester.request(newRequest(PacketType.FSTAT))
.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS)
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
public long length() throws IOException {
public long length()
throws IOException {
return fetchAttributes().getSize();
}
public void setLength(long len) throws IOException {
public void setLength(long len)
throws IOException {
setAttributes(new FileAttributes.Builder().withSize(len).build());
}
public int read(long fileOffset, byte[] to, int offset, int len) throws IOException {
public int read(long fileOffset, byte[] to, int offset, int len)
throws IOException {
final Response res = asyncRead(fileOffset, len).retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS);
return checkReadResponse(res, to, offset);
}
protected Promise<Response, SFTPException> asyncRead(long fileOffset, int len) throws IOException {
protected Promise<Response, SFTPException> asyncRead(long fileOffset, int len)
throws IOException {
return requester.request(newRequest(PacketType.READ).putUInt64(fileOffset).putUInt32(len));
}
protected int checkReadResponse(Response res, byte[] to, int offset) throws Buffer.BufferException, SFTPException {
protected int checkReadResponse(Response res, byte[] to, int offset)
throws Buffer.BufferException, SFTPException {
switch (res.getType()) {
case DATA:
int recvLen = res.readUInt32AsInt();
@@ -74,25 +79,27 @@ public class RemoteFile
}
}
public void write(long fileOffset, byte[] data, int off, int len) throws IOException {
public void write(long fileOffset, byte[] data, int off, int len)
throws IOException {
checkWriteResponse(asyncWrite(fileOffset, data, off, len));
}
protected Promise<Response, SFTPException> asyncWrite(long fileOffset, byte[] data, int off, int len)
throws IOException {
return requester.request(newRequest(PacketType.WRITE)
.putUInt64(fileOffset)
// TODO The SFTP spec claims this field is unneeded...? See #187
.putUInt32(len)
.putRawBytes(data, off, len)
.putUInt64(fileOffset)
.putUInt32(len - off)
.putRawBytes(data, off, len)
);
}
private void checkWriteResponse(Promise<Response, SFTPException> responsePromise) throws SFTPException {
private void checkWriteResponse(Promise<Response, SFTPException> responsePromise)
throws SFTPException {
responsePromise.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS).ensureStatusPacketIsOK();
}
public void setAttributes(FileAttributes attrs) throws IOException {
public void setAttributes(FileAttributes attrs)
throws IOException {
requester.request(newRequest(PacketType.FSETSTAT).putFileAttributes(attrs))
.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS).ensureStatusPacketIsOK();
}
@@ -132,13 +139,15 @@ public class RemoteFile
}
@Override
public void write(int w) throws IOException {
public void write(int w)
throws IOException {
b[0] = (byte) w;
write(b, 0, 1);
}
@Override
public void write(byte[] buf, int off, int len) throws IOException {
public void write(byte[] buf, int off, int len)
throws IOException {
if (unconfirmedWrites.size() > maxUnconfirmedWrites) {
checkWriteResponse(unconfirmedWrites.remove());
}
@@ -147,20 +156,23 @@ public class RemoteFile
}
@Override
public void flush() throws IOException {
public void flush()
throws IOException {
while (!unconfirmedWrites.isEmpty()) {
checkWriteResponse(unconfirmedWrites.remove());
}
}
@Override
public void close() throws IOException {
public void close()
throws IOException {
flush();
}
}
public class RemoteFileInputStream extends InputStream {
public class RemoteFileInputStream
extends InputStream {
private final byte[] b = new byte[1];
@@ -188,32 +200,31 @@ public class RemoteFile
}
@Override
public void reset() throws IOException {
public void reset()
throws IOException {
fileOffset = markPos;
}
@Override
public long skip(long n) throws IOException {
final long fileLength = length();
final Long previousFileOffset = fileOffset;
fileOffset = Math.min(fileOffset + n, fileLength);
return fileOffset - previousFileOffset;
public long skip(long n)
throws IOException {
return (this.fileOffset = Math.min(fileOffset + n, length()));
}
@Override
public int read() throws IOException {
public int read()
throws IOException {
return read(b, 0, 1) == -1 ? -1 : b[0] & 0xff;
}
@Override
public int read(byte[] into, int off, int len) throws IOException {
public int read(byte[] into, int off, int len)
throws IOException {
int read = RemoteFile.this.read(fileOffset, into, off, len);
if (read != -1) {
fileOffset += read;
if (markPos != 0 && read > readLimit) {
// Invalidate mark position
if (markPos != 0 && read > readLimit) // Invalidate mark position
markPos = 0;
}
}
return read;
}
@@ -226,56 +237,27 @@ public class RemoteFile
private final byte[] b = new byte[1];
private final int maxUnconfirmedReads;
private final Queue<Promise<Response, SFTPException>> unconfirmedReads = new LinkedList<Promise<Response, SFTPException>>();
private final Queue<Long> unconfirmedReadOffsets = new LinkedList<Long>();
private final Queue<Promise<Response, SFTPException>> unconfirmedReads;
private long requestOffset;
private long responseOffset;
private long fileOffset;
private boolean eof;
public ReadAheadRemoteFileInputStream(int maxUnconfirmedReads) {
assert 0 <= maxUnconfirmedReads;
this.maxUnconfirmedReads = maxUnconfirmedReads;
this.unconfirmedReads = new LinkedList<Promise<Response, SFTPException>>();
this.fileOffset = 0;
}
public ReadAheadRemoteFileInputStream(int maxUnconfirmedReads, long fileOffset) {
assert 0 <= maxUnconfirmedReads;
assert 0 <= fileOffset;
this.maxUnconfirmedReads = maxUnconfirmedReads;
this.requestOffset = this.responseOffset = fileOffset;
this.unconfirmedReads = new LinkedList<Promise<Response, SFTPException>>();
this.fileOffset = fileOffset;
}
private ByteArrayInputStream pending = new ByteArrayInputStream(new byte[0]);
private boolean retrieveUnconfirmedRead(boolean blocking) throws IOException {
if (unconfirmedReads.size() <= 0) {
return false;
}
if (!blocking && !unconfirmedReads.peek().isDelivered()) {
return false;
}
unconfirmedReadOffsets.remove();
final Response res = unconfirmedReads.remove().retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS);
switch (res.getType()) {
case DATA:
int recvLen = res.readUInt32AsInt();
responseOffset += recvLen;
pending = new ByteArrayInputStream(res.array(), res.rpos(), recvLen);
break;
case STATUS:
res.ensureStatusIs(Response.StatusCode.EOF);
eof = true;
break;
default:
throw new SFTPException("Unexpected packet: " + res.getType());
}
return true;
@Override
public long skip(long n)
throws IOException {
throw new IOException("skip is not supported by ReadAheadFileInputStream, use RemoteFileInputStream instead");
}
@Override
@@ -285,66 +267,26 @@ public class RemoteFile
}
@Override
public int read(byte[] into, int off, int len) throws IOException {
while (!eof && pending.available() <= 0) {
// we also need to go here for len <= 0, because pending may be at
// EOF in which case it would return -1 instead of 0
while (unconfirmedReads.size() <= maxUnconfirmedReads) {
// Send read requests as long as there is no EOF and we have not reached the maximum parallelism
int reqLen = Math.max(1024, len); // don't be shy!
unconfirmedReads.add(RemoteFile.this.asyncRead(requestOffset, reqLen));
unconfirmedReadOffsets.add(requestOffset);
requestOffset += reqLen;
}
long nextOffset = unconfirmedReadOffsets.peek();
if (responseOffset != nextOffset) {
// the server could not give us all the data we needed, so
// we try to fill the gap synchronously
assert responseOffset < nextOffset;
assert 0 < (nextOffset - responseOffset);
assert (nextOffset - responseOffset) <= Integer.MAX_VALUE;
byte[] buf = new byte[(int) (nextOffset - responseOffset)];
int recvLen = RemoteFile.this.read(responseOffset, buf, 0, buf.length);
if (recvLen < 0) {
eof = true;
return -1;
}
if (0 == recvLen) {
// avoid infinite loops
throw new SFTPException("Unexpected response size (0), bailing out");
}
responseOffset += recvLen;
pending = new ByteArrayInputStream(buf, 0, recvLen);
} else if (!retrieveUnconfirmedRead(true /*blocking*/)) {
// this may happen if we change prefetch strategy
// currently, we should never get here...
throw new IllegalStateException("Could not retrieve data for pending read request");
}
public int read(byte[] into, int off, int len)
throws IOException {
while (!eof && unconfirmedReads.size() <= maxUnconfirmedReads) {
// Send read requests as long as there is no EOF and we have not reached the maximum parallelism
unconfirmedReads.add(asyncRead(fileOffset, len));
fileOffset += len;
}
return pending.read(into, off, len);
if (unconfirmedReads.isEmpty()) {
assert eof;
return -1;
}
// Retrieve first in
final Response res = unconfirmedReads.remove().retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS);
final int recvLen = checkReadResponse(res, into, off);
if (recvLen == -1) {
eof = true;
}
return recvLen;
}
@Override
public int available() throws IOException {
boolean lastRead = true;
while (!eof && (pending.available() <= 0) && lastRead) {
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
}
return pending.available();
}
}
}
}

View File

@@ -33,8 +33,6 @@ import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.kex.KeyExchange;
import net.schmizz.sshj.transport.mac.MAC;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,8 +68,6 @@ final class KeyExchanger
*/
private final Queue<HostKeyVerifier> hostVerifiers = new LinkedList<HostKeyVerifier>();
private final Queue<AlgorithmsVerifier> algorithmVerifiers = new LinkedList<AlgorithmsVerifier>();
private final AtomicBoolean kexOngoing = new AtomicBoolean();
/** What we are expecting from the next packet */
@@ -112,10 +108,6 @@ final class KeyExchanger
hostVerifiers.add(hkv);
}
synchronized void addAlgorithmsVerifier(AlgorithmsVerifier verifier) {
algorithmVerifiers.add(verifier);
}
/**
* Returns the session identifier computed during key exchange.
*
@@ -226,13 +218,6 @@ final class KeyExchanger
final Proposal serverProposal = new Proposal(buf);
negotiatedAlgs = clientProposal.negotiate(serverProposal);
log.debug("Negotiated algorithms: {}", negotiatedAlgs);
for(AlgorithmsVerifier v: algorithmVerifiers) {
log.debug("Trying to verify algorithms with {}", v);
if(!v.verify(negotiatedAlgs)) {
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED,
"Failed to verify negotiated algorithms `" + negotiatedAlgs + "`");
}
}
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
negotiatedAlgs.getKeyExchangeAlgorithm());
try {

View File

@@ -48,25 +48,23 @@ public final class Reader
int read;
try {
read = inp.read(recvbuf, 0, needed);
} catch(SocketTimeoutException e) {
}
catch(SocketTimeoutException e) {
if (isInterrupted()) {
throw e;
}
continue;
}
if (read == -1) {
if (read == -1)
throw new TransportException("Broken transport; encountered EOF");
} else {
else
needed = decoder.received(recvbuf, read);
}
}
} catch (Exception e) {
//noinspection StatementWithEmptyBody
if (isInterrupted()) {
// We are meant to shut up and draw to a close if interrupted
} else {
} else
trans.die(e);
}
}
log.debug("Stopping");

View File

@@ -20,7 +20,6 @@ import net.schmizz.sshj.Service;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import java.io.InputStream;
@@ -52,13 +51,6 @@ public interface Transport
*/
void addHostKeyVerifier(HostKeyVerifier hkv);
/**
* Adds the specified verifier.
*
* @param verifier The verifier to call with negotiated algorithms
*/
void addAlgorithmsVerifier(AlgorithmsVerifier verifier);
/**
* Do key exchange and algorithm negotiation. This can be the initial one or for algorithm renegotiation.
*

View File

@@ -27,7 +27,6 @@ import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -159,10 +158,7 @@ public final class TransportImpl
// Read server's ID
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
while ((serverID = readIdentification(buf)).isEmpty()) {
int b = connInfo.in.read();
if (b == -1)
throw new TransportException("Server closed connection during identification exchange");
buf.putByte((byte) b);
buf.putByte((byte) connInfo.in.read());
}
log.info("Server identity string: {}", serverID);
@@ -236,11 +232,6 @@ public final class TransportImpl
kexer.addHostKeyVerifier(hkv);
}
@Override
public void addAlgorithmsVerifier(AlgorithmsVerifier verifier) {
kexer.addAlgorithmsVerifier(verifier);
}
@Override
public void doKex()
throws TransportException {

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code aes128-cbc} cipher */
public class AES128CBC
extends BlockCipher {
extends BaseCipher {
/** Named factory for AES128CBC Cipher */
public static class Factory

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code aes128-ctr} cipher */
public class AES128CTR
extends BlockCipher {
extends BaseCipher {
/** Named factory for AES128CBC Cipher */
public static class Factory

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code aes192-cbc} cipher */
public class AES192CBC
extends BlockCipher {
extends BaseCipher {
/** Named factory for AES192CBC Cipher */
public static class Factory

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code aes192-ctr} cipher */
public class AES192CTR
extends BlockCipher {
extends BaseCipher {
/** Named factory for AES192CTR Cipher */
public static class Factory

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code aes256-ctr} cipher */
public class AES256CBC
extends BlockCipher {
extends BaseCipher {
/** Named factory for AES256CBC Cipher */
public static class Factory

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code aes256-ctr} cipher */
public class AES256CTR
extends BlockCipher {
extends BaseCipher {
/** Named factory for AES256CBC Cipher */
public static class Factory

View File

@@ -22,11 +22,9 @@ import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
/** Base class for all Cipher implementations delegating to the JCE provider. */
public abstract class BaseCipher
public class BaseCipher
implements Cipher {
private static byte[] resize(byte[] data, int size) {
@@ -68,22 +66,14 @@ public abstract class BaseCipher
iv = BaseCipher.resize(iv, ivsize);
try {
cipher = SecurityUtils.getCipher(transformation);
initCipher(cipher, mode, key, iv);
cipher.init((mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE),
new SecretKeySpec(key, algorithm), new IvParameterSpec(iv));
} catch (GeneralSecurityException e) {
cipher = null;
throw new SSHRuntimeException(e);
}
}
protected abstract void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException;
protected SecretKeySpec getKeySpec(byte[] key) {
return new SecretKeySpec(key, algorithm);
}
protected int getMode(Mode mode) {
return mode == Mode.Encrypt ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE;
}
@Override
public void update(byte[] input, int inputOffset, int inputLen) {
try {

View File

@@ -1,17 +0,0 @@
package net.schmizz.sshj.transport.cipher;
import javax.crypto.spec.IvParameterSpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
public class BlockCipher extends BaseCipher {
public BlockCipher(int ivsize, int bsize, String algorithm, String transformation) {
super(ivsize, bsize, algorithm, transformation);
}
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
cipher.init(getMode(mode),
getKeySpec(key), new IvParameterSpec(iv));
}
}

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code blowfish-ctr} cipher */
public class BlowfishCBC
extends BlockCipher {
extends BaseCipher {
/** Named factory for BlowfishCBC Cipher */
public static class Factory

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.transport.cipher;
/** {@code 3des-cbc} cipher */
public class TripleDESCBC
extends BlockCipher {
extends BaseCipher {
/** Named factory for TripleDESCBC Cipher */
public static class Factory

View File

@@ -1,26 +0,0 @@
package net.schmizz.sshj.transport.digest;
/** SHA256 Digest. */
public class SHA256 extends BaseDigest {
/** Named factory for SHA256 digest */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Digest> {
@Override
public Digest create() {
return new SHA256();
}
@Override
public String getName() {
return "sha256";
}
}
/** Create a new instance of a SHA256 digest */
public SHA256() {
super("SHA-256", 32);
}
}

View File

@@ -1,23 +0,0 @@
package net.schmizz.sshj.transport.digest;
public class SHA384 extends BaseDigest {
/** Named factory for SHA384 digest */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Digest> {
@Override
public Digest create() {
return new SHA384();
}
@Override
public String getName() {
return "sha384";
}
}
/** Create a new instance of a SHA384 digest */
public SHA384() {
super("SHA-384", 48);
}
}

View File

@@ -1,23 +0,0 @@
package net.schmizz.sshj.transport.digest;
public class SHA512 extends BaseDigest {
/** Named factory for SHA384 digest */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Digest> {
@Override
public Digest create() {
return new SHA512();
}
@Override
public String getName() {
return "sha512";
}
}
/** Create a new instance of a SHA384 digest */
public SHA512() {
super("SHA-512", 64);
}
}

View File

@@ -1,21 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.Digest;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.Arrays;
public abstract class AbstractDH extends KeyExchangeBase {
protected final DHBase dh;
public AbstractDH(DHBase dh, Digest digest) {
super(digest);
this.dh = dh;
}
@Override
public BigInteger getK() {
return dh.getK();
}
}

View File

@@ -38,24 +38,57 @@ import java.util.Arrays;
* Base class for DHG key exchange algorithms. Implementations will only have to configure the required data on the
* {@link DH} class in the
*/
public abstract class AbstractDHG extends AbstractDH
public abstract class AbstractDHG
implements KeyExchange {
private final Logger log = LoggerFactory.getLogger(getClass());
public AbstractDHG(DHBase dhBase, Digest digest) {
super(dhBase, digest);
private Transport trans;
private final Digest sha1 = new SHA1();
private final DH dh = new DH();
private String V_S;
private String V_C;
private byte[] I_S;
private byte[] I_C;
private byte[] H;
private PublicKey hostKey;
@Override
public byte[] getH() {
return Arrays.copyOf(H, H.length);
}
@Override
public BigInteger getK() {
return dh.getK();
}
@Override
public Digest getHash() {
return sha1;
}
@Override
public PublicKey getHostKey() {
return hostKey;
}
@Override
public void init(Transport trans, String V_S, String V_C, byte[] I_S, byte[] I_C)
throws GeneralSecurityException, TransportException {
super.init(trans, V_S, V_C, I_S, I_C);
digest.init();
this.trans = trans;
this.V_S = V_S;
this.V_C = V_C;
this.I_S = Arrays.copyOf(I_S, I_S.length);
this.I_C = Arrays.copyOf(I_C, I_C.length);
sha1.init();
initDH(dh);
log.debug("Sending SSH_MSG_KEXDH_INIT");
trans.write(new SSHPacket(Message.KEXDH_INIT).putBytes(dh.getE()));
trans.write(new SSHPacket(Message.KEXDH_INIT).putMPInt(dh.getE()));
}
@Override
@@ -66,11 +99,11 @@ public abstract class AbstractDHG extends AbstractDH
log.debug("Received SSH_MSG_KEXDH_REPLY");
final byte[] K_S;
final byte[] f;
final BigInteger f;
final byte[] sig; // signature sent by server
try {
K_S = packet.readBytes();
f = packet.readBytes();
f = packet.readMPInt();
sig = packet.readBytes();
hostKey = new Buffer.PlainBuffer(K_S).readPublicKey();
} catch (Buffer.BufferException be) {
@@ -79,13 +112,17 @@ public abstract class AbstractDHG extends AbstractDH
dh.computeK(f);
final Buffer.PlainBuffer buf = initializedBuffer()
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer()
.putString(V_C)
.putString(V_S)
.putString(I_C)
.putString(I_S)
.putString(K_S)
.putBytes(dh.getE())
.putBytes(f)
.putMPInt(dh.getE())
.putMPInt(f)
.putMPInt(dh.getK());
digest.update(buf.array(), buf.rpos(), buf.available());
H = digest.digest();
sha1.update(buf.array(), buf.rpos(), buf.available());
H = sha1.digest();
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
KeyType.fromKey(hostKey).toString());
@@ -97,7 +134,7 @@ public abstract class AbstractDHG extends AbstractDH
return true;
}
protected abstract void initDH(DHBase dh)
protected abstract void initDH(DH dh)
throws GeneralSecurityException;
}

View File

@@ -1,96 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.digest.Digest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.spec.DHParameterSpec;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
public abstract class AbstractDHGex extends AbstractDH {
private final Logger log = LoggerFactory.getLogger(getClass());
private int minBits = 1024;
private int maxBits = 8192;
private int preferredBits = 2048;
public AbstractDHGex(Digest digest) {
super(new DH(), digest);
}
@Override
public void init(Transport trans, String V_S, String V_C, byte[] I_S, byte[] I_C) throws GeneralSecurityException, TransportException {
super.init(trans, V_S, V_C, I_S, I_C);
digest.init();
log.debug("Sending {}", Message.KEX_DH_GEX_REQUEST);
trans.write(new SSHPacket(Message.KEX_DH_GEX_REQUEST).putUInt32(minBits).putUInt32(preferredBits).putUInt32(maxBits));
}
@Override
public boolean next(Message msg, SSHPacket buffer) throws GeneralSecurityException, TransportException {
log.debug("Got message {}", msg);
try {
switch (msg) {
case KEXDH_31:
return parseGexGroup(buffer);
case KEX_DH_GEX_REPLY:
return parseGexReply(buffer);
}
} catch (Buffer.BufferException be) {
throw new TransportException(be);
}
throw new TransportException("Unexpected message " + msg);
}
private boolean parseGexReply(SSHPacket buffer) throws Buffer.BufferException, GeneralSecurityException, TransportException {
byte[] K_S = buffer.readBytes();
byte[] f = buffer.readBytes();
byte[] sig = buffer.readBytes();
hostKey = new Buffer.PlainBuffer(K_S).readPublicKey();
dh.computeK(f);
BigInteger k = dh.getK();
final Buffer.PlainBuffer buf = initializedBuffer()
.putString(K_S)
.putUInt32(minBits)
.putUInt32(preferredBits)
.putUInt32(maxBits)
.putMPInt(((DH) dh).getP())
.putMPInt(((DH) dh).getG())
.putBytes(dh.getE())
.putBytes(f)
.putMPInt(k);
digest.update(buf.array(), buf.rpos(), buf.available());
H = digest.digest();
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
KeyType.fromKey(hostKey).toString());
signature.init(hostKey, null);
signature.update(H, 0, H.length);
if (!signature.verify(sig))
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED,
"KeyExchange signature verification failed");
return true;
}
private boolean parseGexGroup(SSHPacket buffer) throws Buffer.BufferException, GeneralSecurityException, TransportException {
BigInteger p = buffer.readMPInt();
BigInteger g = buffer.readMPInt();
int bitLength = p.bitLength();
if (bitLength < minBits || bitLength > maxBits) {
throw new GeneralSecurityException("Server generated gex p is out of range (" + bitLength + " bits)");
}
log.debug("Received server p bitlength {}", bitLength);
dh.init(new DHParameterSpec(p, g));
log.debug("Sending {}", Message.KEX_DH_GEX_INIT);
trans.write(new SSHPacket(Message.KEX_DH_GEX_INIT).putBytes(dh.getE()));
return false;
}
}

View File

@@ -1,54 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.SecurityUtils;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.custom.djb.Curve25519;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.Arrays;
import java.util.BitSet;
public class Curve25519DH extends DHBase {
private byte[] secretKey;
public Curve25519DH() {
super("ECDSA", "ECDH");
}
@Override
void computeK(byte[] f) throws GeneralSecurityException {
byte[] k = new byte[32];
djb.Curve25519.curve(k, secretKey, f);
setK(new BigInteger(1, k));
}
@Override
public void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
SecureRandom secureRandom = new SecureRandom();
byte[] secretBytes = new byte[32];
secureRandom.nextBytes(secretBytes);
byte[] publicBytes = new byte[32];
djb.Curve25519.keygen(publicBytes, null, secretBytes);
this.secretKey = Arrays.copyOf(secretBytes, secretBytes.length);
setE(publicBytes);
}
/**
* TODO want to figure out why BouncyCastle does not work.
* @return
*/
public static AlgorithmParameterSpec getCurve25519Params() {
X9ECParameters ecP = CustomNamedCurves.getByName("curve25519");
return new ECParameterSpec(ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
}
}

View File

@@ -1,38 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.digest.SHA256;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.GeneralSecurityException;
public class Curve25519SHA256 extends AbstractDHG {
private static final Logger logger = LoggerFactory.getLogger(Curve25519SHA256.class);
/** Named factory for Curve25519SHA256 key exchange */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
@Override
public KeyExchange create() {
return new Curve25519SHA256();
}
@Override
public String getName() {
return "curve25519-sha256@libssh.org";
}
}
public Curve25519SHA256() {
super(new Curve25519DH(), new SHA256());
}
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(Curve25519DH.getCurve25519Params());
}
}

View File

@@ -18,51 +18,59 @@ package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.common.SecurityUtils;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
/** Diffie-Hellman key generator. */
public class DH extends DHBase {
public class DH {
private BigInteger p;
private BigInteger g;
private BigInteger e; // my public key
private BigInteger K; // shared secret key
private final KeyPairGenerator generator;
private final KeyAgreement agreement;
public DH() {
super("DH", "DH");
try {
generator = SecurityUtils.getKeyPairGenerator("DH");
agreement = SecurityUtils.getKeyAgreement("DH");
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
}
}
@Override
protected void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
if (!(params instanceof DHParameterSpec)) {
throw new SSHRuntimeException("Wrong algorithm parameters for Diffie Hellman");
}
this.p = ((DHParameterSpec) params).getP();
this.g = ((DHParameterSpec) params).getG();
generator.initialize(params);
public void init(BigInteger p, BigInteger g)
throws GeneralSecurityException {
this.p = p;
this.g = g;
generator.initialize(new DHParameterSpec(p, g));
final KeyPair kp = generator.generateKeyPair();
agreement.init(kp.getPrivate());
setE(((javax.crypto.interfaces.DHPublicKey) kp.getPublic()).getY().toByteArray());
e = ((javax.crypto.interfaces.DHPublicKey) kp.getPublic()).getY();
}
@Override
void computeK(byte[] f) throws GeneralSecurityException {
public void computeK(BigInteger f)
throws GeneralSecurityException {
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DH");
final PublicKey yourPubKey = keyFactory.generatePublic(new DHPublicKeySpec(new BigInteger(f), p, g));
final PublicKey yourPubKey = keyFactory.generatePublic(new DHPublicKeySpec(f, p, g));
agreement.doPhase(yourPubKey, true);
setK(new BigInteger(1, agreement.generateSecret()));
K = new BigInteger(1, agreement.generateSecret());
}
public BigInteger getP() {
return p;
public BigInteger getE() {
return e;
}
public BigInteger getG() {
return g;
public BigInteger getK() {
return K;
}
}

View File

@@ -1,47 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.common.SecurityUtils;
import javax.crypto.KeyAgreement;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPairGenerator;
import java.security.spec.AlgorithmParameterSpec;
abstract class DHBase {
protected final KeyPairGenerator generator;
protected final KeyAgreement agreement;
private byte[] e; // my public key
private BigInteger K; // shared secret key
public DHBase(String generator, String agreement) {
try {
this.generator = SecurityUtils.getKeyPairGenerator(generator);
this.agreement = SecurityUtils.getKeyAgreement(agreement);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
}
}
abstract void computeK(byte[] f) throws GeneralSecurityException;
protected abstract void init(AlgorithmParameterSpec params) throws GeneralSecurityException;
void setE(byte[] e) {
this.e = e;
}
void setK(BigInteger k) {
K = k;
}
public byte[] getE() {
return e;
}
public BigInteger getK() {
return K;
}
}

View File

@@ -15,17 +15,12 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.SHA1;
import javax.crypto.spec.DHParameterSpec;
import java.security.GeneralSecurityException;
/**
* Diffie-Hellman key exchange with SHA-1 and Oakley Group 2 [RFC2409] (1024-bit MODP Group).
*
* @see <a href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</a>
*
* TODO refactor away the (unneeded) class
*/
public class DHG1
extends AbstractDHG {
@@ -43,14 +38,13 @@ public class DHG1
public String getName() {
return "diffie-hellman-group1-sha1";
}
}
public DHG1() {
super(new DH(), new SHA1());
}
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new DHParameterSpec(DHGroupData.P1, DHGroupData.G));
protected void initDH(DH dh)
throws GeneralSecurityException {
dh.init(DHGroupData.P1, DHGroupData.G);
}
}

View File

@@ -15,9 +15,6 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.SHA1;
import javax.crypto.spec.DHParameterSpec;
import java.security.GeneralSecurityException;
/**
@@ -45,12 +42,10 @@ public class DHG14
}
public DHG14() {
super(new DH(), new SHA1());
@Override
protected void initDH(DH dh)
throws GeneralSecurityException {
dh.init(DHGroupData.P14, DHGroupData.G);
}
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new DHParameterSpec(DHGroupData.P14, DHGroupData.G));
}
}

View File

@@ -1,25 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.SHA1;
public class DHGexSHA1 extends AbstractDHGex {
/** Named factory for DHGexSHA1 key exchange */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
@Override
public KeyExchange create() {
return new DHGexSHA1();
}
@Override
public String getName() {
return "diffie-hellman-group-exchange-sha1";
}
}
public DHGexSHA1() {
super(new SHA1());
}
}

View File

@@ -1,25 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.SHA256;
public class DHGexSHA256 extends AbstractDHGex {
/** Named factory for DHGexSHA256 key exchange */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
@Override
public KeyExchange create() {
return new DHGexSHA256();
}
@Override
public String getName() {
return "diffie-hellman-group-exchange-sha256";
}
}
public DHGexSHA256() {
super(new SHA256());
}
}

View File

@@ -1,44 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.SecurityUtils;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.*;
import static com.hierynomus.sshj.secg.SecgUtils.getDecoded;
import static com.hierynomus.sshj.secg.SecgUtils.getEncoded;
public class ECDH extends DHBase {
private ECParameterSpec ecParameterSpec;
public ECDH() {
super("EC", "ECDH");
}
protected void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
generator.initialize(params);
KeyPair keyPair = generator.generateKeyPair();
agreement.init(keyPair.getPrivate());
ECPublicKey ecPublicKey = (ECPublicKey) keyPair.getPublic();
this.ecParameterSpec = ecPublicKey.getParams();
ECPoint w = ecPublicKey.getW();
byte[] encoded = getEncoded(w, ecParameterSpec.getCurve());
setE(encoded);
}
@Override
public void computeK(byte[] f) throws GeneralSecurityException {
KeyFactory keyFactory = SecurityUtils.getKeyFactory("EC");
ECPublicKeySpec keySpec = new ECPublicKeySpec(getDecoded(f, ecParameterSpec.getCurve()), ecParameterSpec);
PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
agreement.doPhase(yourPubKey, true);
setK(new BigInteger(1, agreement.generateSecret()));
}
}

View File

@@ -1,70 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.SHA256;
import net.schmizz.sshj.transport.digest.SHA384;
import net.schmizz.sshj.transport.digest.SHA512;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
import java.security.GeneralSecurityException;
public class ECDHNistP extends AbstractDHG {
private String curve;
/** Named factory for ECDHNistP key exchange */
public static class Factory521
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
@Override
public KeyExchange create() {
return new ECDHNistP("P-521", new SHA512());
}
@Override
public String getName() {
return "ecdh-sha2-nistp521";
}
}
/** Named factory for ECDHNistP key exchange */
public static class Factory384
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
@Override
public KeyExchange create() {
return new ECDHNistP("P-384", new SHA384());
}
@Override
public String getName() {
return "ecdh-sha2-nistp384";
}
}
/** Named factory for ECDHNistP key exchange */
public static class Factory256
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
@Override
public KeyExchange create() {
return new ECDHNistP("P-256", new SHA256());
}
@Override
public String getName() {
return "ecdh-sha2-nistp256";
}
}
public ECDHNistP(String curve, Digest digest) {
super(new ECDH(), digest);
this.curve = curve;
}
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new ECNamedCurveGenParameterSpec(curve));
}
}

View File

@@ -1,60 +0,0 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.digest.Digest;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
public abstract class KeyExchangeBase implements KeyExchange {
protected Transport trans;
protected final Digest digest;
protected byte[] H;
protected PublicKey hostKey;
private String V_S;
private String V_C;
private byte[] I_S;
private byte[] I_C;
public KeyExchangeBase(Digest digest) {
this.digest = digest;
}
@Override
public void init(Transport trans, String V_S, String V_C, byte[] I_S, byte[] I_C) throws GeneralSecurityException, TransportException {
this.trans = trans;
this.V_S = V_S;
this.V_C = V_C;
this.I_S = Arrays.copyOf(I_S, I_S.length);
this.I_C = Arrays.copyOf(I_C, I_C.length);
}
protected Buffer.PlainBuffer initializedBuffer() {
return new Buffer.PlainBuffer()
.putString(V_C)
.putString(V_S)
.putString(I_C)
.putString(I_S);
}
@Override
public byte[] getH() {
return Arrays.copyOf(H, H.length);
}
@Override
public Digest getHash() {
return digest;
}
@Override
public PublicKey getHostKey() {
return hostKey;
}
}

View File

@@ -1,27 +0,0 @@
/**
* Copyright 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.transport.verification;
import net.schmizz.sshj.transport.NegotiatedAlgorithms;
public interface AlgorithmsVerifier {
/**
* Callback is invoked when algorithms have been negotiated between client and server.
* @return False to interrupt the connection
*/
boolean verify(NegotiatedAlgorithms algorithms);
}

View File

@@ -352,7 +352,7 @@ public class OpenSSHKnownHosts
@Override
public boolean appliesTo(KeyType type, String host)
throws IOException {
return type == this.type && hosts.contains(host);
return type == this.type && hostnames.contains(host);
}
}

View File

@@ -51,7 +51,8 @@ public class KeyProviderUtil {
* @return name of the key file format
* @throws java.io.IOException
*/
public static KeyFormat detectKeyFileFormat(String privateKey, boolean separatePubKey)
public static KeyFormat detectKeyFileFormat(String privateKey,
boolean separatePubKey)
throws IOException {
return detectKeyFileFormat(new StringReader(privateKey), separatePubKey);
}
@@ -66,44 +67,35 @@ public class KeyProviderUtil {
* @return name of the key file format
* @throws java.io.IOException
*/
public static KeyFormat detectKeyFileFormat(Reader privateKey, boolean separatePubKey)
public static KeyFormat detectKeyFileFormat(Reader privateKey,
boolean separatePubKey)
throws IOException {
String header = readHeader(privateKey);
if (header == null) {
throw new IOException("Empty file");
}
return keyFormatFromHeader(header, separatePubKey);
}
private static String readHeader(Reader privateKey) throws IOException {
BufferedReader br = new BufferedReader(privateKey);
final String firstLine;
try {
String header;
while ((header = br.readLine()) != null) {
header = header.trim();
if (!header.isEmpty()) {
break;
}
}
return header;
} finally {
firstLine = br.readLine();
}
finally {
IOUtils.closeQuietly(br);
}
}
private static KeyFormat keyFormatFromHeader(String header, boolean separatePubKey) {
if (header.startsWith("-----BEGIN") && header.endsWith("PRIVATE KEY-----")) {
if (separatePubKey) {
// Can delay asking for password since have unencrypted pubkey
if(firstLine == null) {
throw new IOException("Empty file");
}
if(firstLine.startsWith("-----BEGIN") && firstLine.endsWith("PRIVATE KEY-----")) {
if(separatePubKey)
// Can delay asking for password since have unencrypted pubkey
{
return KeyFormat.OpenSSH;
} else {
// More general
}
else
// More general
{
return KeyFormat.PKCS8;
}
} else if (header.startsWith("PuTTY-User-Key-File-")) {
return KeyFormat.PuTTY;
} else {
return KeyFormat.Unknown;
}
if(firstLine.startsWith("PuTTY-User-Key-File-")) {
return KeyFormat.PuTTY;
}
return KeyFormat.Unknown;
}
}

View File

@@ -92,7 +92,7 @@ public class OpenSSHKeyFile
try {
final String keydata = br.readLine();
if (keydata != null) {
String[] parts = keydata.trim().split(" ");
String[] parts = keydata.split(" ");
assert parts.length >= 2;
type = KeyType.fromString(parts[0]);
pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey();

View File

@@ -1,188 +0,0 @@
package net.schmizz.sshj.userauth.method;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import net.schmizz.sshj.common.Buffer.BufferException;
import net.schmizz.sshj.common.Buffer.PlainBuffer;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.UserAuthException;
/** Implements authentication by GSS-API. */
public class AuthGssApiWithMic
extends AbstractAuthMethod {
private final LoginContext loginContext;
private final List<Oid> mechanismOids;
private final GSSManager manager;
private GSSContext secContext;
public AuthGssApiWithMic(LoginContext loginContext, List<Oid> mechanismOids) {
this(loginContext, mechanismOids, GSSManager.getInstance());
}
public AuthGssApiWithMic(LoginContext loginContext, List<Oid> mechanismOids, GSSManager manager) {
super("gssapi-with-mic");
this.loginContext = loginContext;
this.mechanismOids = mechanismOids;
this.manager = manager;
secContext = null;
}
@Override
public SSHPacket buildReq()
throws UserAuthException {
SSHPacket packet = super.buildReq() // the generic stuff
.putUInt32(mechanismOids.size()); // number of OIDs we support
for (Oid oid : mechanismOids) {
try {
packet.putString(oid.getDER());
} catch (GSSException e) {
throw new UserAuthException("Mechanism OID could not be encoded: " + oid.toString(), e);
}
}
return packet;
}
/**
* PrivilegedExceptionAction to be executed within the given LoginContext for
* initializing the GSSContext.
*
* @author Ben Hamme
*/
private class InitializeContextAction implements PrivilegedExceptionAction<GSSContext> {
private final Oid selectedOid;
public InitializeContextAction(Oid selectedOid) {
this.selectedOid = selectedOid;
}
@Override
public GSSContext run() throws GSSException {
GSSName clientName = manager.createName(params.getUsername(), GSSName.NT_USER_NAME);
GSSCredential clientCreds = manager.createCredential(clientName, GSSContext.DEFAULT_LIFETIME, selectedOid, GSSCredential.INITIATE_ONLY);
GSSName peerName = manager.createName("host@" + params.getTransport().getRemoteHost(), GSSName.NT_HOSTBASED_SERVICE);
GSSContext context = manager.createContext(peerName, selectedOid, clientCreds, GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
context.requestInteg(true);
return context;
}
}
private void sendToken(byte[] token) throws TransportException {
SSHPacket packet = new SSHPacket(Message.USERAUTH_INFO_RESPONSE).putString(token);
params.getTransport().write(packet);
}
private void handleContextInitialization(SSHPacket buf)
throws UserAuthException, TransportException {
byte[] bytes;
try {
bytes = buf.readBytes();
} catch (BufferException e) {
throw new UserAuthException("Failed to read byte array from message buffer", e);
}
Oid selectedOid;
try {
selectedOid = new Oid(bytes);
} catch (GSSException e) {
throw new UserAuthException("Exception constructing OID from server response", e);
}
log.debug("Server selected OID: {}", selectedOid.toString());
log.debug("Initializing GSSAPI context");
Subject subject = loginContext.getSubject();
try {
secContext = Subject.doAs(subject, new InitializeContextAction(selectedOid));
} catch (PrivilegedActionException e) {
throw new UserAuthException("Exception during context initialization", e);
}
log.debug("Sending initial token");
byte[] inToken = new byte[0];
try {
byte[] outToken = secContext.initSecContext(inToken, 0, inToken.length);
sendToken(outToken);
} catch (GSSException e) {
throw new UserAuthException("Exception sending initial token", e);
}
}
private byte[] handleTokenFromServer(SSHPacket buf) throws UserAuthException {
byte[] token;
try {
token = buf.readStringAsBytes();
} catch (BufferException e) {
throw new UserAuthException("Failed to read string from message buffer", e);
}
try {
return secContext.initSecContext(token, 0, token.length);
} catch (GSSException e) {
throw new UserAuthException("Exception during token exchange", e);
}
}
private byte[] generateMIC() throws UserAuthException {
byte[] msg = new PlainBuffer().putString(params.getTransport().getSessionID())
.putByte(Message.USERAUTH_REQUEST.toByte())
.putString(params.getUsername())
.putString(params.getNextServiceName())
.putString(getName())
.getCompactData();
try {
return secContext.getMIC(msg, 0, msg.length, null);
} catch (GSSException e) {
throw new UserAuthException("Exception getting message integrity code", e);
}
}
@Override
public void handle(Message cmd, SSHPacket buf)
throws UserAuthException, TransportException {
if (cmd == Message.USERAUTH_60) {
handleContextInitialization(buf);
} else if (cmd == Message.USERAUTH_INFO_RESPONSE) {
byte[] token = handleTokenFromServer(buf);
if (!secContext.isEstablished()) {
log.debug("Sending token");
sendToken(token);
} else {
if (secContext.getIntegState()) {
log.debug("Per-message integrity protection available: finalizing authentication with message integrity code");
params.getTransport().write(new SSHPacket(Message.USERAUTH_GSSAPI_MIC).putString(generateMIC()));
} else {
log.debug("Per-message integrity protection unavailable: finalizing authentication");
params.getTransport().write(new SSHPacket(Message.USERAUTH_GSSAPI_EXCHANGE_COMPLETE));
}
}
} else {
super.handle(cmd, buf);
}
}
}

View File

@@ -1,16 +0,0 @@
package net.schmizz.sshj.xfer.scp;
abstract class AbstractSCPClient {
protected final SCPEngine engine;
protected int bandwidthLimit;
AbstractSCPClient(SCPEngine engine) {
this.engine = engine;
}
AbstractSCPClient(SCPEngine engine, int bandwidthLimit) {
this.engine = engine;
this.bandwidthLimit = bandwidthLimit;
}
}

View File

@@ -18,36 +18,32 @@ package net.schmizz.sshj.xfer.scp;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.TransferListener;
import net.schmizz.sshj.xfer.scp.SCPEngine.Arg;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/** Support for uploading files over a connected link using SCP. */
public final class SCPDownloadClient extends AbstractSCPClient {
public final class SCPDownloadClient {
private boolean recursiveMode = true;
SCPDownloadClient(SCPEngine engine) {
super(engine);
}
private final SCPEngine engine;
SCPDownloadClient(SCPEngine engine, int bandwidthLimit) {
super(engine, bandwidthLimit);
SCPDownloadClient(SCPEngine engine) {
this.engine = engine;
}
/** Download a file from {@code sourcePath} on the connected host to {@code targetPath} locally. */
public synchronized int copy(String sourcePath, LocalDestFile targetFile) throws IOException {
return copy(sourcePath, targetFile, ScpCommandLine.EscapeMode.NoEscape);
}
public synchronized int copy(String sourcePath, LocalDestFile targetFile, ScpCommandLine.EscapeMode escapeMode)
public synchronized int copy(String sourcePath, LocalDestFile targetFile)
throws IOException {
engine.cleanSlate();
try {
startCopy(sourcePath, targetFile, escapeMode);
startCopy(sourcePath, targetFile);
} finally {
engine.exit();
}
@@ -62,15 +58,15 @@ public final class SCPDownloadClient extends AbstractSCPClient {
this.recursiveMode = recursive;
}
private void startCopy(String sourcePath, LocalDestFile targetFile, ScpCommandLine.EscapeMode escapeMode)
void startCopy(String sourcePath, LocalDestFile targetFile)
throws IOException {
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SOURCE)
.and(ScpCommandLine.Arg.QUIET)
.and(ScpCommandLine.Arg.PRESERVE_TIMES)
.and(ScpCommandLine.Arg.RECURSIVE, recursiveMode)
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
commandLine.withPath(sourcePath, escapeMode);
engine.execSCPWith(commandLine);
List<Arg> args = new LinkedList<Arg>();
args.add(Arg.SOURCE);
args.add(Arg.QUIET);
args.add(Arg.PRESERVE_TIMES);
if (recursiveMode)
args.add(Arg.RECURSIVE);
engine.execSCPWith(args, sourcePath);
engine.signal("Start status OK");
@@ -123,7 +119,7 @@ public final class SCPDownloadClient extends AbstractSCPClient {
case (char) 1:
case (char) 2:
throw new SCPRemoteException("Remote SCP command returned error: " + msg.substring(1), msg.substring(1));
throw new SCPException("Remote SCP command returned error: " + msg.substring(1));
default:
final String err = "Unrecognized message: `" + msg + "`";
@@ -202,4 +198,4 @@ public final class SCPDownloadClient extends AbstractSCPClient {
return parts;
}
}
}

View File

@@ -28,14 +28,32 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
/** @see <a href="http://blogs.sun.com/janp/entry/how_the_scp_protocol_works">SCP Protocol</a> */
class SCPEngine {
static enum Arg {
SOURCE('f'),
SINK('t'),
RECURSIVE('r'),
VERBOSE('v'),
PRESERVE_TIMES('p'),
QUIET('q');
private final char a;
private Arg(char a) {
this.a = a;
}
@Override
public String toString() {
return "-" + a;
}
}
private static final String SCP_COMMAND = "scp";
private static final char LF = '\n';
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -69,8 +87,7 @@ class SCPEngine {
return;
case 1: // Warning? not
case 2:
final String remoteMessage = readMessage();
throw new SCPRemoteException("Remote SCP command had error: " + remoteMessage, remoteMessage);
throw new SCPException("Remote SCP command had error: " + readMessage());
default:
throw new SCPException("Received unknown response code");
}
@@ -80,9 +97,17 @@ class SCPEngine {
exitStatus = -1;
}
void execSCPWith(ScpCommandLine commandLine)
void execSCPWith(List<Arg> args, String path)
throws SSHException {
scp = host.startSession().exec(commandLine.toCommandLine());
final StringBuilder cmd = new StringBuilder(SCP_COMMAND);
for (Arg arg : args)
cmd.append(" ").append(arg);
cmd.append(" ");
if (path == null || path.isEmpty())
cmd.append(".");
else
cmd.append("'").append(path.replaceAll("'", "\\'")).append("'");
scp = host.startSession().exec(cmd.toString());
}
void exit() {
@@ -94,13 +119,11 @@ class SCPEngine {
exitStatus = scp.getExitStatus();
if (scp.getExitStatus() != 0)
log.warn("SCP exit status: {}", scp.getExitStatus());
} else {
} else
exitStatus = -1;
}
if (scp.getExitSignal() != null) {
if (scp.getExitSignal() != null)
log.warn("SCP exit signal: {}", scp.getExitSignal());
}
}
scp = null;
@@ -110,36 +133,36 @@ class SCPEngine {
throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
int x;
while ((x = scp.getInputStream().read()) != LF) {
while ((x = scp.getInputStream().read()) != LF)
if (x == -1) {
if (baos.size() == 0) {
if (baos.size() == 0)
return "";
} else {
else
throw new IOException("EOF while reading message");
}
} else {
} else
baos.write(x);
}
}
final String msg = baos.toString(IOUtils.UTF8.displayName());
log.debug("Read message: `{}`", msg);
return msg;
}
void sendMessage(String msg) throws IOException {
void sendMessage(String msg)
throws IOException {
log.debug("Sending message: {}", msg);
scp.getOutputStream().write((msg + LF).getBytes(IOUtils.UTF8));
scp.getOutputStream().flush();
check("Message ACK received");
}
void signal(String what) throws IOException {
void signal(String what)
throws IOException {
log.debug("Signalling: {}", what);
scp.getOutputStream().write(0);
scp.getOutputStream().flush();
}
long transferToRemote(StreamCopier.Listener listener, InputStream src, long length) throws IOException {
long transferToRemote(StreamCopier.Listener listener, InputStream src, long length)
throws IOException {
return new StreamCopier(src, scp.getOutputStream())
.bufSize(scp.getRemoteMaxPacketSize()).length(length)
.keepFlushing(false)
@@ -147,7 +170,8 @@ class SCPEngine {
.copy();
}
long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long length) throws IOException {
long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long length)
throws IOException {
return new StreamCopier(scp.getInputStream(), dest)
.bufSize(scp.getLocalMaxPacketSize()).length(length)
.keepFlushing(false)
@@ -158,4 +182,5 @@ class SCPEngine {
TransferListener getTransferListener() {
return listener;
}
}

View File

@@ -28,23 +28,18 @@ public class SCPFileTransfer
extends AbstractFileTransfer
implements FileTransfer {
/** Default bandwidth limit for SCP transfer in kilobit/s (-1 means unlimited) */
private static final int DEFAULT_BANDWIDTH_LIMIT = -1;
private final SessionFactory sessionFactory;
private int bandwidthLimit;
public SCPFileTransfer(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
this.bandwidthLimit = DEFAULT_BANDWIDTH_LIMIT;
}
public SCPDownloadClient newSCPDownloadClient() {
return new SCPDownloadClient(newSCPEngine(), bandwidthLimit);
return new SCPDownloadClient(newSCPEngine());
}
public SCPUploadClient newSCPUploadClient() {
return new SCPUploadClient(newSCPEngine(), bandwidthLimit);
return new SCPUploadClient(newSCPEngine());
}
private SCPEngine newSCPEngine() {
@@ -75,10 +70,4 @@ public class SCPFileTransfer
newSCPUploadClient().copy(localFile, remotePath);
}
public SCPFileTransfer bandwidthLimit(int limit) {
if (limit > 0) {
this.bandwidthLimit = limit;
}
return this;
}
}

View File

@@ -1,15 +0,0 @@
package net.schmizz.sshj.xfer.scp;
public class SCPRemoteException extends SCPException
{
private final String remoteMessage;
public SCPRemoteException(String message, String remoteMessage) {
super(message);
this.remoteMessage = remoteMessage;
}
public String getRemoteMessage() {
return remoteMessage;
}
}

View File

@@ -1,12 +1,12 @@
/**
* Copyright 2009 sshj contributors
* <p/>
*
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
*
* 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.
@@ -20,34 +20,29 @@ import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.xfer.LocalFileFilter;
import net.schmizz.sshj.xfer.LocalSourceFile;
import net.schmizz.sshj.xfer.TransferListener;
import net.schmizz.sshj.xfer.scp.SCPEngine.Arg;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
/** Support for uploading files over a connected link using SCP. */
public final class SCPUploadClient extends AbstractSCPClient {
public final class SCPUploadClient {
private final SCPEngine engine;
private LocalFileFilter uploadFilter;
SCPUploadClient(SCPEngine engine) {
super(engine);
}
SCPUploadClient(SCPEngine engine, int bandwidthLimit) {
super(engine, bandwidthLimit);
this.engine = engine;
}
/** Upload a local file from {@code localFile} to {@code targetPath} on the remote host. */
public synchronized int copy(LocalSourceFile sourceFile, String remotePath)
throws IOException {
return copy(sourceFile, remotePath, ScpCommandLine.EscapeMode.SingleQuote);
}
public synchronized int copy(LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
engine.cleanSlate();
try {
startCopy(sourceFile, remotePath, escapeMode);
startCopy(sourceFile, remotePath);
} finally {
engine.exit();
}
@@ -58,14 +53,14 @@ public final class SCPUploadClient extends AbstractSCPClient {
this.uploadFilter = uploadFilter;
}
private void startCopy(LocalSourceFile sourceFile, String targetPath, ScpCommandLine.EscapeMode escapeMode)
private synchronized void startCopy(LocalSourceFile sourceFile, String targetPath)
throws IOException {
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SINK)
.and(ScpCommandLine.Arg.RECURSIVE)
.and(ScpCommandLine.Arg.PRESERVE_TIMES, sourceFile.providesAtimeMtime())
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
commandLine.withPath(targetPath, escapeMode);
engine.execSCPWith(commandLine);
List<Arg> args = new LinkedList<Arg>();
args.add(Arg.SINK);
args.add(Arg.RECURSIVE);
if (sourceFile.providesAtimeMtime())
args.add(Arg.PRESERVE_TIMES);
engine.execSCPWith(args, targetPath);
engine.check("Start status OK");
process(engine.getTransferListener(), sourceFile);
}

View File

@@ -1,132 +0,0 @@
package net.schmizz.sshj.xfer.scp;
import java.util.LinkedHashMap;
/**
* Command line to be sent to the remote SSH process to setup an SCP process in the correct mode.
*/
public class ScpCommandLine {
private static final String SCP_COMMAND = "scp";
private EscapeMode mode;
enum Arg {
SOURCE('f'),
SINK('t'),
RECURSIVE('r'),
VERBOSE('v'),
PRESERVE_TIMES('p'),
QUIET('q'),
LIMIT('l');
private final char a;
private Arg(char a) {
this.a = a;
}
@Override
public String toString() {
return "-" + a;
}
}
public enum EscapeMode {
NoEscape,
Space {
@Override
String escapedPath(String path) {
return path.replace(" ", "\\ ");
}
},
DoubleQuote {
@Override
String escapedPath(String path) {
return "\"" + path.replace("\"", "\\\"") + "\"";
}
},
SingleQuote {
@Override
String escapedPath(String path) {
return "\'" + path.replace("'", "\\'") + "'";
}
};
String escapedPath(String path) {
return path;
}
}
private LinkedHashMap<Arg, String> arguments = new LinkedHashMap<Arg, String>();
private String path;
ScpCommandLine() {
}
static ScpCommandLine with(Arg name) {
return with(name, null, true);
}
static ScpCommandLine with(Arg name, String value) {
return with(name, value, true);
}
static ScpCommandLine with(Arg name, boolean accept) {
return with(name, null, accept);
}
static ScpCommandLine with(Arg name, String value, boolean accept) {
ScpCommandLine commandLine = new ScpCommandLine();
commandLine.addArgument(name, value, accept);
return commandLine;
}
private void addArgument(Arg name, String value, boolean accept) {
if (accept) {
arguments.put(name, value);
}
}
ScpCommandLine and(Arg name) {
addArgument(name, null, true);
return this;
}
ScpCommandLine and(Arg name, String value) {
addArgument(name, value, true);
return this;
}
ScpCommandLine and(Arg name, boolean accept) {
addArgument(name, null, accept);
return this;
}
ScpCommandLine and(Arg name, String value, boolean accept) {
addArgument(name, value, accept);
return this;
}
ScpCommandLine withPath(String path, EscapeMode mode) {
this.path = path;
this.mode = mode;
return this;
}
String toCommandLine() {
final StringBuilder cmd = new StringBuilder(SCP_COMMAND);
for (Arg arg : arguments.keySet()) {
cmd.append(" ").append(arg);
String s = arguments.get(arg);
if (s != null && !s.trim().isEmpty()) {
cmd.append(s);
}
}
cmd.append(" ");
if (path == null || path.trim().isEmpty()) {
cmd.append(".");
} else {
cmd.append(mode.escapedPath(path));
}
return cmd.toString();
}
}

View File

@@ -1,23 +0,0 @@
package com.hierynomus.sshj;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
public class IntegrationTest {
@Test
public void shouldConnect() throws IOException {
SSHClient sshClient = new SSHClient(new DefaultConfig());
sshClient.addHostKeyVerifier(new OpenSSHKnownHosts(new File("/Users/ajvanerp/.ssh/known_hosts")));
sshClient.connect("172.16.37.129");
sshClient.authPassword("jeroen", "jeroen");
assertThat("Is connected", sshClient.isAuthenticated());
}
}

View File

@@ -1,27 +0,0 @@
package com.hierynomus.sshj.connection.channel;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.connection.channel.direct.Session;
import org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
public class ChannelCloseEofTest {
@Rule
public SshFixture fixture = new SshFixture();
@Test
public void shouldCorrectlyHandleSessionChannelEof() throws IOException, InterruptedException {
fixture.setupConnectedDefaultClient().authPassword("jeroen", "jeroen");
Session session = fixture.getClient().startSession();
session.allocateDefaultPTY();
session.close();
Thread.sleep(1000);
assertThat("Should still be connected", fixture.getClient().isConnected());
}
}

View File

@@ -1,36 +0,0 @@
package com.hierynomus.sshj.connection.channel.direct;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
public class CommandTest {
@Rule
public SshFixture fixture = new SshFixture();
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Test
public void shouldExecuteBackgroundCommand() throws IOException {
SSHClient sshClient = fixture.setupConnectedDefaultClient();
sshClient.authPassword("jeroen", "jeroen");
File file = new File(temp.getRoot(), "testdir");
assertThat("File should not exist", !file.exists());
// TODO figure out why this does not really execute in the background.
Session.Command exec = sshClient.startSession().exec("mkdir " + file.getPath() + " &");
exec.join();
assertThat("File should exist", file.exists());
assertThat("File should be directory", file.isDirectory());
}
}

View File

@@ -1,54 +0,0 @@
package com.hierynomus.sshj.keepalive;
import com.hierynomus.sshj.test.KnownFailingTests;
import com.hierynomus.sshj.test.SlowTests;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.userauth.UserAuthException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import static org.junit.Assert.fail;
public class KeepAliveThreadTerminationTest {
@Rule
public SshFixture fixture = new SshFixture();
@Test
@Category({SlowTests.class, KnownFailingTests.class})
public void shouldCorrectlyTerminateThreadOnDisconnect() throws IOException, InterruptedException {
DefaultConfig defaultConfig = new DefaultConfig();
defaultConfig.setKeepAliveProvider(KeepAliveProvider.KEEP_ALIVE);
for (int i = 0; i < 10; i++) {
SSHClient sshClient = fixture.setupClient(defaultConfig);
fixture.connectClient(sshClient);
sshClient.getConnection().getKeepAlive().setKeepAliveInterval(1);
try {
sshClient.authPassword("bad", "credentials");
fail("Should not auth.");
} catch (UserAuthException e) {
// OK
}
fixture.stopClient();
Thread.sleep(2000);
}
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
for (long l : threadMXBean.getAllThreadIds()) {
ThreadInfo threadInfo = threadMXBean.getThreadInfo(l);
if (threadInfo.getThreadName().equals("keep-alive") && threadInfo.getThreadState() != Thread.State.TERMINATED) {
System.err.println("Found thread in state " + threadInfo.getThreadState());
throw new RuntimeException("Found alive keep-alive thread");
}
}
}
}

View File

@@ -1,74 +0,0 @@
package com.hierynomus.sshj.sftp;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.OpenMode;
import net.schmizz.sshj.sftp.RemoteFile;
import net.schmizz.sshj.sftp.SFTPEngine;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;
import java.util.Random;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
public class RemoteFileTest {
@Rule
public SshFixture fixture = new SshFixture();
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Test
public void shouldNotGoOutOfBoundsInReadAheadInputStream() throws IOException {
SSHClient ssh = fixture.setupConnectedDefaultClient();
ssh.authPassword("test", "test");
SFTPEngine sftp = new SFTPEngine(ssh).init();
RemoteFile rf;
File file = temp.newFile("SftpReadAheadTest.bin");
rf = sftp.open(file.getPath(), EnumSet.of(OpenMode.WRITE, OpenMode.CREAT));
byte[] data = new byte[8192];
new Random(53).nextBytes(data);
data[3072] = 1;
rf.write(0, data, 0, data.length);
rf.close();
assertThat("The file should exist", file.exists());
rf = sftp.open(file.getPath());
InputStream rs = rf.new ReadAheadRemoteFileInputStream(16 /*maxUnconfirmedReads*/);
byte[] test = new byte[4097];
int n = 0;
while (n < 2048) {
n += rs.read(test, n, 2048 - n);
}
while (n < 3072) {
n += rs.read(test, n, 3072 - n);
}
if (test[3072] != 0) {
System.err.println("buffer overrun!");
}
n += rs.read(test, n, test.length - n); // --> ArrayIndexOutOfBoundsException
byte[] test2 = new byte[data.length];
System.arraycopy(test, 0, test2, 0, test.length);
while (n < data.length) {
n += rs.read(test2, n, data.length - n);
}
assertThat("The written and received data should match", data, equalTo(test2));
}
}

View File

@@ -1,41 +0,0 @@
package com.hierynomus.sshj.sftp;
import com.hierynomus.sshj.test.SshFixture;
import com.hierynomus.sshj.test.util.FileUtil;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
public class SFTPClientTest {
@Rule
public SshFixture fixture = new SshFixture();
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Test
public void shouldNotThrowExceptionOnCloseBeforeDisconnect() throws IOException {
SSHClient sshClient = fixture.setupConnectedDefaultClient();
sshClient.authPassword("test", "test");
SFTPClient sftpClient = sshClient.newSFTPClient();
// TODO workaround for bug in Mina 1.0.0 --> Should be fixed in 1.1.0
sftpClient.getFileTransfer().setPreserveAttributes(false);
File file = temp.newFile("source.txt");
FileUtil.writeToFile(file, "This is the source");
try {
try {
sftpClient.put(file.getPath(), temp.newFile("dest.txt").getPath());
} finally {
sftpClient.close();
}
} finally {
sshClient.disconnect();
}
}
}

View File

@@ -1,54 +0,0 @@
package com.hierynomus.sshj.test;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import org.apache.sshd.server.SshServer;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
public abstract class BaseAlgorithmTest {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Rule
public SshFixture fixture = new SshFixture(false);
@After
public void stopServer() {
fixture.stopServer();
}
@Test
public void shouldVerifyAlgorithm() throws IOException {
attempt(100);
}
private void attempt(int times) throws IOException {
for (int i = 0; i < times; i++) {
logger.info("--> Attempt {}", i);
verify();
}
}
private void verify() throws IOException {
configureServer(fixture.getServer());
fixture.start();
Config config = getClientConfig(new DefaultConfig());
SSHClient sshClient = fixture.connectClient(fixture.setupClient(config));
assertThat("should be connected", sshClient.isConnected());
sshClient.disconnect();
// fixture.stopServer();
fixture.stopClient();
}
protected abstract Config getClientConfig(DefaultConfig defaultConfig);
protected abstract void configureServer(SshServer server);
}

View File

@@ -1,9 +0,0 @@
package com.hierynomus.sshj.test;
/**
* Marker interface for JUnit Categories.
*
* This denotes that the test is known to fail, and should be fixed at some time.
*/
public interface KnownFailingTests {
}

View File

@@ -1,4 +0,0 @@
package com.hierynomus.sshj.test;
public interface SlowTests {
}

View File

@@ -1,163 +0,0 @@
package com.hierynomus.sshj.test;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.util.gss.BogusGSSAuthenticator;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.keyprovider.AbstractClassLoadableResourceKeyPairProvider;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.CommandFactory;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.shell.ProcessShellFactory;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.junit.rules.ExternalResource;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Can be used as a rule to ensure the server is teared down after each test.
*/
public class SshFixture extends ExternalResource {
public static final String hostkey = "hostkey.pem";
public static final String fingerprint = "ce:a7:c1:cf:17:3f:96:49:6a:53:1a:05:0b:ba:90:db";
private SshServer server = defaultSshServer();
private SSHClient client = null;
private AtomicBoolean started = new AtomicBoolean(false);
private boolean autoStart = true;
public SshFixture(boolean autoStart) {
this.autoStart = autoStart;
}
public SshFixture() {
}
@Override
protected void before() throws Throwable {
if (autoStart) {
start();
}
}
@Override
protected void after() {
stopClient();
stopServer();
}
public void start() throws IOException {
if (!started.getAndSet(true)) {
server.start();
}
}
public SSHClient setupConnectedDefaultClient() throws IOException {
return connectClient(setupDefaultClient());
}
public SSHClient setupDefaultClient() {
return setupClient(new DefaultConfig());
}
public SSHClient setupClient(Config config) {
if (client == null) {
client = new SSHClient(config);
client.addHostKeyVerifier(fingerprint);
}
return client;
}
public SSHClient getClient() {
if (client != null) {
return client;
}
throw new IllegalStateException("First call one of the setup*Client methods");
}
public SSHClient connectClient(SSHClient client) throws IOException {
client.connect(server.getHost(), server.getPort());
return client;
}
private SshServer defaultSshServer() {
SshServer sshServer = SshServer.setUpDefaultServer();
sshServer.setPort(randomPort());
AbstractClassLoadableResourceKeyPairProvider fileKeyPairProvider = SecurityUtils.createClassLoadableResourceKeyPairProvider();
fileKeyPairProvider.setResources(Collections.singletonList(hostkey));
sshServer.setKeyPairProvider(fileKeyPairProvider);
sshServer.setPasswordAuthenticator(new PasswordAuthenticator() {
@Override
public boolean authenticate(String username, String password, ServerSession session) {
return username.equals(password);
}
});
sshServer.setGSSAuthenticator(new BogusGSSAuthenticator());
sshServer.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
ScpCommandFactory commandFactory = new ScpCommandFactory();
commandFactory.setDelegateCommandFactory(new CommandFactory() {
@Override
public Command createCommand(String command) {
return new ProcessShellFactory(command.split(" ")).create();
}
});
sshServer.setCommandFactory(commandFactory);
return sshServer;
}
private int randomPort() {
try {
ServerSocket s = null;
try {
s = new ServerSocket(0);
return s.getLocalPort();
} finally {
if (s != null)
s.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void stopClient() {
if (client != null && client.isConnected()) {
try {
client.disconnect();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
client = null;
}
} else if (client != null) {
client = null;
}
}
public void stopServer() {
if (started.getAndSet(false)) {
try {
server.stop(true);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public SshServer getServer() {
return server;
}
}

View File

@@ -1,27 +0,0 @@
package com.hierynomus.sshj.test.util;
import net.schmizz.sshj.common.IOUtils;
import java.io.*;
public class FileUtil {
public static void writeToFile(File f, String content) throws IOException {
FileWriter w = new FileWriter(f);
try {
w.write(content);
} finally {
IOUtils.closeQuietly(w);
}
}
public static String readFromFile(File f) throws IOException {
FileInputStream fileInputStream = new FileInputStream(f);
try {
ByteArrayOutputStream byteArrayOutputStream = IOUtils.readFully(fileInputStream);
return byteArrayOutputStream.toString("UTF-8");
} finally {
IOUtils.closeQuietly(fileInputStream);
}
}
}

View File

@@ -1,61 +0,0 @@
package com.hierynomus.sshj.transport.kex;
import com.hierynomus.sshj.test.BaseAlgorithmTest;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.transport.kex.DHGexSHA1;
import net.schmizz.sshj.transport.kex.DHGexSHA256;
import net.schmizz.sshj.transport.kex.ECDHNistP;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.kex.BuiltinDHFactories;
import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.kex.DHGEXServer;
import org.apache.sshd.server.kex.DHGServer;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import static org.hamcrest.MatcherAssert.assertThat;
@RunWith(Parameterized.class)
public class KeyExchangeTest extends BaseAlgorithmTest {
@Parameterized.Parameters
public static Collection<Object[]> getParameters() {
return Arrays.asList(new Object[][]{
{DHGEXServer.newFactory(BuiltinDHFactories.dhgex), new DHGexSHA1.Factory()},
{DHGEXServer.newFactory(BuiltinDHFactories.dhgex256), new DHGexSHA256.Factory()},
{DHGServer.newFactory(BuiltinDHFactories.ecdhp256), new ECDHNistP.Factory256()},
{DHGServer.newFactory(BuiltinDHFactories.ecdhp384), new ECDHNistP.Factory384()},
{DHGServer.newFactory(BuiltinDHFactories.ecdhp521), new ECDHNistP.Factory521()}
// Not supported yet by MINA {null, new Curve25519SHA256.Factory()}
});
}
private Factory.Named<net.schmizz.sshj.transport.kex.KeyExchange> clientFactory;
private NamedFactory<KeyExchange> serverFactory;
public KeyExchangeTest(NamedFactory<KeyExchange> serverFactory, Factory.Named<net.schmizz.sshj.transport.kex.KeyExchange> clientFactory) {
this.clientFactory = clientFactory;
this.serverFactory = serverFactory;
}
@Override
protected Config getClientConfig(DefaultConfig config) {
config.setKeyExchangeFactories(Collections.singletonList(clientFactory));
return config;
}
@Override
protected void configureServer(SshServer server) {
server.setKeyExchangeFactories(Collections.singletonList(serverFactory));
}
}

View File

@@ -1,52 +0,0 @@
package com.hierynomus.sshj.userauth;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.userauth.method.AuthGssApiWithMic;
import net.schmizz.sshj.util.gss.BogusGSSManager;
import org.junit.Rule;
import org.junit.Test;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import java.util.Collections;
import static org.junit.Assert.assertTrue;
public class GssApiTest {
@Rule
public SshFixture fixture = new SshFixture();
private static final String LOGIN_CONTEXT_NAME = "TestLoginContext";
private static class TestAuthConfiguration extends Configuration {
private AppConfigurationEntry entry = new AppConfigurationEntry(
"testLoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
Collections.<String, Object> emptyMap());
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
if (name.equals(LOGIN_CONTEXT_NAME)) {
return new AppConfigurationEntry[] { entry };
} else {
return new AppConfigurationEntry[0];
}
}
}
@Test
public void authenticated() throws Exception {
AuthGssApiWithMic authMethod = new AuthGssApiWithMic(
new LoginContext(LOGIN_CONTEXT_NAME, null, null, new TestAuthConfiguration()),
Collections.singletonList(BogusGSSManager.KRB5_MECH),
new BogusGSSManager());
SSHClient defaultClient = fixture.setupConnectedDefaultClient();
defaultClient.auth("user", authMethod);
assertTrue(defaultClient.isAuthenticated());
}
}

View File

@@ -15,7 +15,7 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.util.BasicFixture;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,17 +26,15 @@ public class LoadsOfConnects {
protected final Logger log = LoggerFactory.getLogger(getClass());
private final SshFixture fixture = new SshFixture();
private final BasicFixture fixture = new BasicFixture();
@Test
public void loadsOfConnects()
throws IOException, InterruptedException {
for (int i = 0; i < 1000; i++) {
System.out.println("Try " + i);
fixture.start();
fixture.setupConnectedDefaultClient();
fixture.stopClient();
fixture.stopServer();
fixture.init(false);
fixture.done();
}
}

View File

@@ -15,9 +15,9 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.UserAuthException;
import net.schmizz.sshj.util.BasicFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -29,20 +29,18 @@ import static org.junit.Assert.assertTrue;
/* Kinda basic right now */
public class SmokeTest {
private final SshFixture fixture = new SshFixture();
private final BasicFixture fixture = new BasicFixture();
@Before
public void setUp()
throws IOException {
fixture.start();
fixture.setupConnectedDefaultClient();
fixture.init(false);
}
@After
public void tearDown()
throws IOException, InterruptedException {
fixture.stopClient();
fixture.stopServer();
fixture.done();
}
@Test
@@ -54,7 +52,7 @@ public class SmokeTest {
@Test
public void authenticated()
throws UserAuthException, TransportException {
fixture.getClient().authPassword("dummy", "dummy");
fixture.dummyAuth();
assertTrue(fixture.getClient().isAuthenticated());
}

View File

@@ -1,40 +0,0 @@
package net.schmizz.sshj.keyprovider;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
public class KeyProviderUtilTest {
private static final File ROOT = new File("src/test/resources/keyformats");
@Test
public void testOpenSsh() throws IOException {
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "openssh"));
assertEquals(KeyFormat.OpenSSH, format);
}
@Test
public void testPkcs8() throws IOException {
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs8"));
assertEquals(KeyFormat.PKCS8, format);
}
@Test
public void testPutty() throws IOException {
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "putty"));
assertEquals(KeyFormat.PuTTY, format);
}
@Test
public void testSkipsBlankLines() throws IOException {
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs8-blanks"));
assertEquals(KeyFormat.PKCS8, format);
}
}

View File

@@ -30,12 +30,9 @@ import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Scanner;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -130,26 +127,6 @@ public class OpenSSHKeyFileTest {
assertEquals(KeyUtil.newDSAPrivateKey(x, p, q, g), dsa.getPrivate());
}
@Test
public void shouldHaveCorrectFingerprintForECDSA() throws IOException, GeneralSecurityException {
OpenSSHKeyFile keyFile = new OpenSSHKeyFile();
keyFile.init(new File("src/test/resources/keytypes/test_ecdsa_nistp256"));
String expected = "256 MD5:53:ae:db:ed:8f:2d:02:d4:d5:6c:24:bc:a4:66:88:79 root@itgcpkerberosstack-cbgateway-0-20151117031915 (ECDSA)\n";
PublicKey aPublic = keyFile.getPublic();
String sshjFingerprintSshjKey = net.schmizz.sshj.common.SecurityUtils.getFingerprint(aPublic);
assertThat(expected, containsString(sshjFingerprintSshjKey));
}
@Test
public void shouldHaveCorrectFingerprintForED25519() throws IOException {
OpenSSHKeyFile keyFile = new OpenSSHKeyFile();
keyFile.init(new File("src/test/resources/keytypes/test_ed25519"));
String expected = "256 MD5:d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32 root@sshj (ED25519)\n";
PublicKey aPublic = keyFile.getPublic();
String sshjFingerprintSshjKey = net.schmizz.sshj.common.SecurityUtils.getFingerprint(aPublic);
assertThat(expected, containsString(sshjFingerprintSshjKey));
}
@Before
public void setup()
throws UnsupportedEncodingException, GeneralSecurityException {

View File

@@ -1,39 +1,58 @@
package com.hierynomus.sshj.transport;
/**
* Copyright 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.transport;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.transport.DisconnectListener;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.util.BasicFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class DisconnectionTest {
private AtomicBoolean disconnected = null;
public class Disconnection {
@Rule
public SshFixture fixture = new SshFixture();
private final BasicFixture fixture = new BasicFixture();
private boolean notified;
@Before
public void setupFlag() throws IOException {
disconnected = new AtomicBoolean(false);
// Initialize the client
SSHClient defaultClient = fixture.setupDefaultClient();
defaultClient.getTransport().setDisconnectListener(new DisconnectListener() {
public void setUp()
throws IOException {
fixture.init();
notified = false;
fixture.getClient().getTransport().setDisconnectListener(new DisconnectListener() {
@Override
public void notifyDisconnect(DisconnectReason reason, String message) {
disconnected.set(true);
notified = true;
}
});
fixture.connectClient(defaultClient);
}
@After
public void tearDown()
throws IOException, InterruptedException {
fixture.done();
}
private boolean joinToClientTransport(int seconds) {
@@ -48,8 +67,8 @@ public class DisconnectionTest {
@Test
public void listenerNotifiedOnClientDisconnect()
throws IOException {
fixture.getClient().disconnect();
assertTrue(disconnected.get());
fixture.stopClient();
assertTrue(notified);
}
@Test
@@ -57,13 +76,13 @@ public class DisconnectionTest {
throws InterruptedException, IOException {
fixture.stopServer();
joinToClientTransport(2);
assertTrue(disconnected.get());
assertTrue(notified);
}
@Test
public void joinNotifiedOnClientDisconnect()
throws IOException {
fixture.getClient().disconnect();
fixture.stopClient();
assertTrue(joinToClientTransport(2));
}
@@ -74,4 +93,4 @@ public class DisconnectionTest {
assertFalse(joinToClientTransport(2));
}
}
}

View File

@@ -29,7 +29,6 @@ import java.security.PublicKey;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -79,7 +78,6 @@ public class OpenSSHKnownHostsTest {
assertTrue(kh.verify("schmizz.net", 22, key));
assertTrue(kh.verify("69.163.155.180", 22, key));
assertFalse(kh.verify("69.163.155.18", 22, key));
}
@Test

View File

@@ -0,0 +1,146 @@
/**
* Copyright 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.
*/
/*
* Copyright 2010, 2011 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.util;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.UserAuthException;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import java.io.IOException;
import java.net.ServerSocket;
public class BasicFixture {
public static final String hostkey = "src/test/resources/hostkey.pem";
public static final String fingerprint = "ce:a7:c1:cf:17:3f:96:49:6a:53:1a:05:0b:ba:90:db";
public static final String hostname = "localhost";
public final int port = gimmeAPort();
private SSHClient client;
private SshServer server;
private boolean clientRunning = false;
private boolean serverRunning = false;
private static int gimmeAPort() {
try {
ServerSocket s = null;
try {
s = new ServerSocket(0);
return s.getLocalPort();
} finally {
if (s != null)
s.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void init()
throws IOException {
init(false);
}
public void init(boolean authenticate)
throws IOException {
startServer();
startClient(authenticate);
}
public void done()
throws InterruptedException, IOException {
stopClient();
stopServer();
}
public void startServer()
throws IOException {
server = SshServer.setUpDefaultServer();
server.setPort(port);
server.setKeyPairProvider(new FileKeyPairProvider(new String[]{hostkey}));
server.setPasswordAuthenticator(new PasswordAuthenticator() {
@Override
public boolean authenticate(String u, String p, ServerSession s) {
return false;
}
});
server.start();
serverRunning = true;
}
public void stopServer()
throws InterruptedException {
if (serverRunning) {
server.stop();
serverRunning = false;
}
}
public SshServer getServer() {
return server;
}
public void startClient(boolean authenticate)
throws IOException {
client = new SSHClient();
client.addHostKeyVerifier(fingerprint);
client.connect(hostname, port);
if (authenticate)
dummyAuth();
clientRunning = true;
}
public void stopClient()
throws IOException {
if (clientRunning) {
client.disconnect();
clientRunning = false;
}
}
public SSHClient getClient() {
return client;
}
public void dummyAuth()
throws UserAuthException, TransportException {
server.setPasswordAuthenticator(new BogusPasswordAuthenticator());
client.authPassword("same", "same");
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright 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.
*/
/*
* Copyright 2010, 2011 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.util;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.session.ServerSession;
/** Successfully authenticates when username == password. */
public class BogusPasswordAuthenticator
implements PasswordAuthenticator {
@Override
public boolean authenticate(String username, String password, ServerSession s) {
return username.equals(password);
}
}

View File

@@ -1,22 +0,0 @@
package net.schmizz.sshj.util.gss;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
public class BogusGSSAuthenticator
extends GSSAuthenticator {
private final GSSManager manager = new BogusGSSManager();
@Override
public GSSManager getGSSManager() {
return manager;
}
@Override
public GSSCredential getGSSCredential(GSSManager mgr) throws GSSException {
return manager.createCredential(GSSCredential.ACCEPT_ONLY);
}
}

View File

@@ -1,243 +0,0 @@
package net.schmizz.sshj.util.gss;
import static net.schmizz.sshj.util.gss.BogusGSSManager.unavailable;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.ietf.jgss.ChannelBinding;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.Oid;
public class BogusGSSContext
implements GSSContext {
private static final byte[] INIT_TOKEN = fromString("INIT");
private static final byte[] ACCEPT_TOKEN = fromString("ACCEPT");
private static final byte[] MIC = fromString("LGTM");
private static byte[] fromString(String s) {
return s.getBytes(Charset.forName("UTF-8"));
}
private boolean initialized = false;
private boolean accepted = false;
private boolean integState = false;
private boolean mutualAuthState = false;
@Override
public byte[] initSecContext(byte[] inputBuf, int offset, int len) throws GSSException {
initialized = true;
return INIT_TOKEN;
}
@Override
public int initSecContext(InputStream inStream, OutputStream outStream) throws GSSException {
throw unavailable();
}
@Override
public byte[] acceptSecContext(byte[] inToken, int offset, int len) throws GSSException {
accepted = Arrays.equals(INIT_TOKEN, Arrays.copyOfRange(inToken, offset, offset + len));
return ACCEPT_TOKEN;
}
@Override
public void acceptSecContext(InputStream inStream, OutputStream outStream) throws GSSException {
throw unavailable();
}
@Override
public boolean isEstablished() {
return initialized || accepted;
}
@Override
public void dispose() throws GSSException {}
@Override
public int getWrapSizeLimit(int qop, boolean confReq, int maxTokenSize) throws GSSException {
throw unavailable();
}
@Override
public byte[] wrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public void wrap(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public byte[] unwrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public void unwrap(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public byte[] getMIC(byte[] inMsg, int offset, int len, MessageProp msgProp) throws GSSException {
return MIC;
}
@Override
public void getMIC(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public void verifyMIC(byte[] inToken, int tokOffset, int tokLen, byte[] inMsg, int msgOffset, int msgLen, MessageProp msgProp) throws GSSException {
if (!Arrays.equals(MIC, Arrays.copyOfRange(inToken, tokOffset, tokOffset + tokLen))) {
throw new GSSException(GSSException.BAD_MIC);
}
}
@Override
public void verifyMIC(InputStream tokStream, InputStream msgStream, MessageProp msgProp) throws GSSException {
throw unavailable();
}
@Override
public byte[] export() throws GSSException {
throw unavailable();
}
@Override
public void requestMutualAuth(boolean state) throws GSSException {
this.mutualAuthState = state;
}
@Override
public void requestInteg(boolean state) throws GSSException {
this.integState = state;
}
@Override
public void requestReplayDet(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestSequenceDet(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestCredDeleg(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestAnonymity(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestConf(boolean state) throws GSSException {
throw unavailable();
}
@Override
public void requestLifetime(int lifetime) throws GSSException {
throw unavailable();
}
@Override
public void setChannelBinding(ChannelBinding cb) throws GSSException {
throw unavailable();
}
@Override
public boolean getMutualAuthState() {
return mutualAuthState;
}
@Override
public boolean getIntegState() {
return integState;
}
@Override
public boolean getCredDelegState() {
return false;
}
@Override
public boolean getReplayDetState() {
return false;
}
@Override
public boolean getSequenceDetState() {
return false;
}
@Override
public boolean getAnonymityState() {
return false;
}
@Override
public boolean isTransferable() throws GSSException {
return false;
}
@Override
public boolean isProtReady() {
return false;
}
@Override
public boolean getConfState() {
return false;
}
@Override
public int getLifetime() {
return INDEFINITE_LIFETIME;
}
@Override
public GSSName getSrcName() throws GSSException {
try {
String hostname = InetAddress.getLocalHost().getCanonicalHostName();
return new BogusGSSName("user@" + hostname, GSSName.NT_HOSTBASED_SERVICE);
} catch (UnknownHostException e) {
throw new IllegalStateException(e);
}
}
@Override
public GSSName getTargName() throws GSSException {
throw unavailable();
}
@Override
public Oid getMech() throws GSSException {
return BogusGSSManager.KRB5_MECH;
}
@Override
public GSSCredential getDelegCred() throws GSSException {
throw unavailable();
}
@Override
public boolean isInitiator() throws GSSException {
return false;
}
}

View File

@@ -1,87 +0,0 @@
package net.schmizz.sshj.util.gss;
import static net.schmizz.sshj.util.gss.BogusGSSManager.unavailable;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
public class BogusGSSCredential
implements GSSCredential {
private final GSSName name;
private final int usage;
public BogusGSSCredential(GSSName name, int usage) {
this.name = name;
this.usage = usage;
}
@Override
public void dispose() throws GSSException {}
@Override
public GSSName getName() throws GSSException {
return name;
}
@Override
public GSSName getName(Oid mech) throws GSSException {
return name.canonicalize(mech);
}
@Override
public int getRemainingLifetime() throws GSSException {
return INDEFINITE_LIFETIME;
}
@Override
public int getRemainingInitLifetime(Oid mech) throws GSSException {
return INDEFINITE_LIFETIME;
}
@Override
public int getRemainingAcceptLifetime(Oid mech) throws GSSException {
return INDEFINITE_LIFETIME;
}
@Override
public int getUsage() throws GSSException {
return usage;
}
@Override
public int getUsage(Oid mech) throws GSSException {
return usage;
}
@Override
public Oid[] getMechs() throws GSSException {
return new Oid[] { BogusGSSManager.KRB5_MECH };
}
@Override
public void add(GSSName name, int initLifetime, int acceptLifetime, Oid mech, int usage) throws GSSException {
throw unavailable();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
return (name == null ? 0 : name.hashCode());
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BogusGSSCredential)) {
return false;
}
GSSName otherName = ((BogusGSSCredential) obj).name;
return name == null ? otherName == null : name.equals((Object) otherName);
}
}

View File

@@ -1,106 +0,0 @@
package net.schmizz.sshj.util.gss;
import java.security.Provider;
import org.apache.sshd.server.auth.gss.UserAuthGSS;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements a fake Kerberos 5 mechanism. MINA only supports Kerberos 5 over
* GSS-API, so we can't implement a separate mechanism.
*/
public class BogusGSSManager
extends GSSManager {
public static final Oid KRB5_MECH = UserAuthGSS.KRB5_MECH;
private static final Logger log = LoggerFactory.getLogger(BogusGSSManager.class);
@Override
public Oid[] getMechs() {
return new Oid[] { KRB5_MECH };
}
@Override
public Oid[] getNamesForMech(Oid mech) throws GSSException {
return new Oid[] { GSSName.NT_EXPORT_NAME, GSSName.NT_HOSTBASED_SERVICE };
}
@Override
public Oid[] getMechsForName(Oid nameType) {
return new Oid[] { KRB5_MECH };
}
@Override
public GSSName createName(String nameStr, Oid nameType) throws GSSException {
return new BogusGSSName(nameStr, nameType);
}
@Override
public GSSName createName(byte[] name, Oid nameType) throws GSSException {
throw unavailable();
}
@Override
public GSSName createName(String nameStr, Oid nameType, Oid mech) throws GSSException {
return this.createName(nameStr, nameType);
}
@Override
public GSSName createName(byte[] name, Oid nameType, Oid mech) throws GSSException {
throw unavailable();
}
@Override
public GSSCredential createCredential(int usage) throws GSSException {
return new BogusGSSCredential(null, usage);
}
@Override
public GSSCredential createCredential(GSSName name, int lifetime, Oid mech, int usage) throws GSSException {
return new BogusGSSCredential(name, usage);
}
@Override
public GSSCredential createCredential(GSSName name, int lifetime, Oid[] mechs, int usage) throws GSSException {
return new BogusGSSCredential(name, usage);
}
@Override
public GSSContext createContext(GSSName peer, Oid mech, GSSCredential myCred, int lifetime) throws GSSException {
return new BogusGSSContext();
}
@Override
public GSSContext createContext(GSSCredential myCred) throws GSSException {
return new BogusGSSContext();
}
@Override
public GSSContext createContext(byte[] interProcessToken) throws GSSException {
throw unavailable();
}
@Override
public void addProviderAtFront(Provider p, Oid mech) throws GSSException {
throw unavailable();
}
@Override
public void addProviderAtEnd(Provider p, Oid mech) throws GSSException {
throw unavailable();
}
static GSSException unavailable() throws GSSException {
GSSException e = new GSSException(GSSException.UNAVAILABLE);
log.error(e.getMessage(), e);
throw e;
}
}

View File

@@ -1,58 +0,0 @@
package net.schmizz.sshj.util.gss;
import static net.schmizz.sshj.util.gss.BogusGSSManager.unavailable;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
public class BogusGSSName
implements GSSName {
private final String name;
private final Oid oid;
public BogusGSSName(String name, Oid oid) {
this.name = name;
this.oid = oid;
}
@Override
public boolean equals(GSSName another) throws GSSException {
if (!(another instanceof BogusGSSName)) {
throw new GSSException(GSSException.BAD_NAMETYPE);
}
BogusGSSName otherName = (BogusGSSName) another;
return name.equals(otherName.name) && oid.equals(otherName.oid);
}
@Override
public GSSName canonicalize(Oid mech) throws GSSException {
return this;
}
@Override
public byte[] export() throws GSSException {
throw unavailable();
}
@Override
public Oid getStringNameType() throws GSSException {
return oid;
}
@Override
public boolean isAnonymous() {
return false;
}
@Override
public boolean isMN() {
return false;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -1,96 +0,0 @@
package net.schmizz.sshj.xfer.scp;
import com.hierynomus.sshj.test.SshFixture;
import com.hierynomus.sshj.test.util.FileUtil;
import net.schmizz.sshj.SSHClient;
import org.hamcrest.CoreMatchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
public class SCPFileTransferTest {
public static final String DEFAULT_FILE_NAME = "my_file.txt";
File targetDir;
File sourceFile;
File targetFile;
SSHClient sshClient;
@Rule
public SshFixture fixture = new SshFixture();
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Before
public void init() throws IOException {
sourceFile = tempFolder.newFile(DEFAULT_FILE_NAME);
FileUtil.writeToFile(sourceFile, "This is my file");
targetDir = tempFolder.newFolder();
targetFile = new File(targetDir, DEFAULT_FILE_NAME);
sshClient = fixture.setupConnectedDefaultClient();
sshClient.authPassword("test", "test");
}
@After
public void cleanup() {
if (targetFile.exists()) {
targetFile.delete();
}
}
@Test
public void shouldSCPUploadFile() throws IOException {
SCPFileTransfer scpFileTransfer = sshClient.newSCPFileTransfer();
assertFalse(targetFile.exists());
assertTrue(targetDir.exists());
scpFileTransfer.upload(sourceFile.getAbsolutePath(), targetDir.getAbsolutePath());
assertTrue(targetFile.exists());
}
@Test
public void shouldSCPUploadFileWithBandwidthLimit() throws IOException {
// Limit upload transfer at 2Mo/s
SCPFileTransfer scpFileTransfer = sshClient.newSCPFileTransfer().bandwidthLimit(16000);
assertFalse(targetFile.exists());
scpFileTransfer.upload(sourceFile.getAbsolutePath(), targetDir.getAbsolutePath());
assertTrue(targetFile.exists());
}
@Test
public void shouldSCPDownloadFile() throws IOException {
SCPFileTransfer scpFileTransfer = sshClient.newSCPFileTransfer();
assertFalse(targetFile.exists());
scpFileTransfer.download(sourceFile.getAbsolutePath(), targetDir.getAbsolutePath());
assertTrue(targetFile.exists());
}
@Test
public void shouldSCPDownloadFileWithBandwidthLimit() throws IOException {
// Limit download transfer at 128Ko/s
SCPFileTransfer scpFileTransfer = sshClient.newSCPFileTransfer().bandwidthLimit(1024);
assertFalse(targetFile.exists());
scpFileTransfer.download(sourceFile.getAbsolutePath(), targetDir.getAbsolutePath());
assertTrue(targetFile.exists());
}
@Test
public void shouldSCPDownloadFileWithoutPathEscaping() throws IOException {
SCPFileTransfer scpFileTransfer = sshClient.newSCPFileTransfer();
assertFalse(targetFile.exists());
File file = tempFolder.newFile("new file.txt");
FileUtil.writeToFile(file, "Some content");
scpFileTransfer.download(tempFolder.getRoot().getAbsolutePath() + "/new file.txt", targetFile.getAbsolutePath());
assertTrue(targetFile.exists());
assertThat(FileUtil.readFromFile(targetFile), CoreMatchers.containsString("Some content"));
}
}

View File

@@ -1,15 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCm2IJ9gWDkPTlQ37NNUB0za5mCsQ8bi++8fyEqw7wl8ZNBh3qt
TcnL+m+NZfQjUC0BXic7PcMLVm4A3ID2IAZQM+axfq9aL4huWerm4ua6tvdt4gQK
oL1+8JFmdFvFw5pWW/NZHtkIprbVf7KtYrU27WmMhXruN071UzqLsw08cwIDAQAB
AoGAHQ7cOyuLSnT3RISRX8eyLkBxLffUX8HRcQzbI+2PGTSnpuQHk6NWn/Xv87pr
+LKABBr3zjOFgrX81p2QwEz3jDxNXzbOeZzhuvGXCX5GocuEO4n5EhDvXRDF4uht
uvVV5FsQv/sTOR0PNo1nELiAA8k3NYDxraB83q7wtsmErtECQQDYWMnq8mwRe49d
jIXNKJeNiuLUYxO3CLI/vx279gDKlKrt677trr1e7JZqm/DapEWG511tw3cW63gQ
+qxtgkw1AkEAxW0UeaNaJd7DApqwGAcS1JkygCKwzQ4ns/Co15qUgMkqCkmQU9AU
/zQpt2+BjdYVe50r/nr8K1KYwrBsyndrBwJBALe90N+FvFqswfoFmq2/R9eimTsg
WmIdNKYHPs2gBNQIp5MhoSpkOdkgvi8U+d33nkUQwryyQbZpjbN98mufOfECQEML
eBiW0NZrf+4yefqu7EYmgG/jWAdK91C0OaJ+bFAQAKbdtJXB5F+GZ2RUCbsRKNqB
1Z7mRRyxQA9dupRHWaECQQCM9bbCtfGesgvZlhBavlWavu8iCvJlAbGdf5QMlFQE
kABmZg84Fy3NUFCD+RXCuatb4Oo9P/WPIbjYiC4p0hLJ
-----END RSA PRIVATE KEY-----

View File

@@ -1 +0,0 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCm2IJ9gWDkPTlQ37NNUB0za5mCsQ8bi++8fyEqw7wl8ZNBh3qtTcnL+m+NZfQjUC0BXic7PcMLVm4A3ID2IAZQM+axfq9aL4huWerm4ua6tvdt4gQKoL1+8JFmdFvFw5pWW/NZHtkIprbVf7KtYrU27WmMhXruN071UzqLsw08cw==

View File

@@ -1,15 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCm2IJ9gWDkPTlQ37NNUB0za5mCsQ8bi++8fyEqw7wl8ZNBh3qt
TcnL+m+NZfQjUC0BXic7PcMLVm4A3ID2IAZQM+axfq9aL4huWerm4ua6tvdt4gQK
oL1+8JFmdFvFw5pWW/NZHtkIprbVf7KtYrU27WmMhXruN071UzqLsw08cwIDAQAB
AoGAHQ7cOyuLSnT3RISRX8eyLkBxLffUX8HRcQzbI+2PGTSnpuQHk6NWn/Xv87pr
+LKABBr3zjOFgrX81p2QwEz3jDxNXzbOeZzhuvGXCX5GocuEO4n5EhDvXRDF4uht
uvVV5FsQv/sTOR0PNo1nELiAA8k3NYDxraB83q7wtsmErtECQQDYWMnq8mwRe49d
jIXNKJeNiuLUYxO3CLI/vx279gDKlKrt677trr1e7JZqm/DapEWG511tw3cW63gQ
+qxtgkw1AkEAxW0UeaNaJd7DApqwGAcS1JkygCKwzQ4ns/Co15qUgMkqCkmQU9AU
/zQpt2+BjdYVe50r/nr8K1KYwrBsyndrBwJBALe90N+FvFqswfoFmq2/R9eimTsg
WmIdNKYHPs2gBNQIp5MhoSpkOdkgvi8U+d33nkUQwryyQbZpjbN98mufOfECQEML
eBiW0NZrf+4yefqu7EYmgG/jWAdK91C0OaJ+bFAQAKbdtJXB5F+GZ2RUCbsRKNqB
1Z7mRRyxQA9dupRHWaECQQCM9bbCtfGesgvZlhBavlWavu8iCvJlAbGdf5QMlFQE
kABmZg84Fy3NUFCD+RXCuatb4Oo9P/WPIbjYiC4p0hLJ
-----END RSA PRIVATE KEY-----

Some files were not shown because too many files have changed in this diff Show More