Compare commits

...

53 Commits

Author SHA1 Message Date
Jeroen van Erp
f4b71941a3 Updated release notes for 0.20.0 2017-02-10 10:38:43 +01:00
Jeroen van Erp
636f896850 Cleaned up some code 2017-02-10 09:43:12 +01:00
Jeroen van Erp
56c0baf814 Merge pull request #301 from adagios/master
adds current version to exported classes
2017-02-06 10:16:45 +01:00
João Nelas
edfb069f2a adds current version to exported classes 2017-02-03 17:33:07 +00:00
Jeroen van Erp
65b3003e72 Merge pull request #295 from adagios/master
converts bouncycastle OSGi dep from Import-Bundle to Import-Package
2017-01-24 09:20:15 +01:00
Jeroen van Erp
fbee0b3956 Update build.gradle 2017-01-23 22:56:23 +01:00
Jeroen van Erp
fd60139b98 Merge branch 'master' into master 2017-01-23 22:54:15 +01:00
Jeroen van Erp
0b397bc3d7 Added TODO 2017-01-23 15:33:47 +01:00
Jeroen van Erp
40f956b4b6 More code cleanup 2017-01-23 15:33:22 +01:00
Jeroen van Erp
ef3f7a2eaf Fixed more warnings 2017-01-23 14:40:33 +01:00
Jeroen van Erp
8134113510 Cleaned up more PMD/Codacy warnings 2017-01-23 10:46:01 +01:00
Jeroen van Erp
c883c87963 Fixed a few warnings 2017-01-23 09:55:04 +01:00
Jeroen van Erp
920537dac9 More cleaning up 2017-01-20 16:41:16 +01:00
Jeroen van Erp
356ec9ed08 Upgraded to mina sshd 1.2.0 2017-01-20 16:30:01 +01:00
Jeroen van Erp
aa47b0c5f7 Cleaning up unused code 2017-01-20 16:29:37 +01:00
Jeroen van Erp
d3ed3cfe0f Merge pull request #296 from hierynomus/new-dhgroups
Added new DH groups
2017-01-20 16:17:06 +01:00
Jeroen van Erp
786734ce26 Suppressing Method naming conventions for Factory classes 2017-01-20 15:43:36 +01:00
Jeroen van Erp
9cb5bf4e10 Organized imports 2017-01-20 15:26:21 +01:00
Jeroen van Erp
0e3f7c2bbf Added new DH groups 2017-01-20 15:05:18 +01:00
João Nelas
66d4b34eba re-adding Export-Package of com.hierynomus.sshj.* 2017-01-12 14:37:02 +00:00
João Nelas
aafb9942a3 converts bouncycastle OSGi dep from Import-Bundle to Import-Package
Import-Bundle is generally discouraged by OSGi best practices.

See http://stackoverflow.com/questions/1865819/when-should-i-use-import-package-and-when-should-i-use-require-bundle for more info.
2017-01-12 14:26:59 +00:00
Jeroen van Erp
d1dff550ce Updated OSGi bundling (Fixes #255 again) 2017-01-11 23:14:43 +01:00
Jeroen van Erp
ac2720becd Added bettercodehub config 2017-01-10 17:45:37 +01:00
Jeroen van Erp
48dd1fdc41 Merge pull request #294 from joval/master
Reference the EdDSANamedCurveTable constant instead of its value...
2017-01-09 11:21:13 +01:00
Jeroen van Erp
9826a71d2b Merge pull request #293 from adagios/osgi-fix
excludes net.i2p.crypto.eddsa.math from OSGI Import-Package
2017-01-09 11:20:48 +01:00
David A. Solin
936eb26e9e Reference the EdDSANamedCurveTable constant instead of its value, for forward-compatibility with the ed25519-java project on Github.
See: abdee146c3
2017-01-06 12:58:04 -06:00
João Nelas
9438157b93 excludes net.i2p.crypto.eddsa.math from OSGI Import-Package 2017-01-06 15:12:44 +00:00
Jeroen van Erp
7d326e5ae4 Updated example build 2016-12-30 09:26:43 +01:00
Jeroen van Erp
f038b5ce2b Updated Release notes 2016-12-30 09:26:00 +01:00
Jeroen van Erp
20879a4aa5 LocalPortForwarder interrupts its thread on close() 2016-12-29 16:06:57 +01:00
Jeroen van Erp
516abb0282 Disable version inference when no git history present (Fixes #256) 2016-12-28 10:09:48 +01:00
Jeroen van Erp
0ad51709c2 Use the configured Random factory in DH KEX (Fixes #292) 2016-12-28 10:00:24 +01:00
Jeroen van Erp
c9c68f019e StreamCipher can use the constructor without random. 2016-12-28 09:52:58 +01:00
Jeroen van Erp
fc75f9796c Added logging to trace time creation of Randoms 2016-12-28 09:52:20 +01:00
Jeroen van Erp
61af500c3e Added 'classes' directory to ignore 2016-12-28 09:51:03 +01:00
Jeroen van Erp
56553ea086 Prepared release notes 2016-12-23 11:58:49 +01:00
Jeroen van Erp
86e6631b1e Enabled PKCS5 in DefaultConfig 2016-12-23 11:54:43 +01:00
Jeroen van Erp
b6f437a932 Merge pull request #291 from joval/master
Two minor changes
2016-12-21 21:51:23 +01:00
David Solin
9e3b9f7c24 Obtain the sshj.properties resource from the ClassLoader that loaded the DefaultConfig class, not from the context classloader. 2016-12-21 10:04:56 -06:00
David Solin
766ab916ee Use cause message when initializing an SSHRuntimeException from a Throwable. 2016-12-21 07:48:02 -06:00
Jeroen van Erp
cdca43a848 Merge pull request #284 from gatesking/patch-1
Catch interrupt signal in keepalive thread
2016-11-30 09:42:29 +01:00
gatesking
3ce7c2ebfb Catch interrupt signal in keepalive thread
Interrupt signal may be catched when keepalive thread is sleeping. As a result, it will still go into conn.getTransport().die(e).
2016-11-30 09:30:57 +08:00
Jeroen van Erp
ca4e0bf2d7 Update README.adoc 2016-11-25 13:27:20 +01:00
Jeroen van Erp
2ca2bbd633 Prepared for next release 2016-11-25 13:14:48 +01:00
Jeroen van Erp
256e65dea4 Build jar with correct version string automatically (Fixes #280) 2016-11-23 13:30:37 +01:00
Jeroen van Erp
1feb7fe9a6 Update README.adoc 2016-10-31 09:57:12 +01:00
Jeroen van Erp
d95b4db930 Merge pull request #279 from hierynomus/issue-276
Support for OpenSSH new key file format (fixes #276)
2016-10-31 09:55:47 +01:00
Jeroen van Erp
677f482a69 Fixed text 2016-10-28 16:31:23 +02:00
Jeroen van Erp
179b30ef4e Fixed some codacy warnings 2016-10-28 14:50:37 +02:00
Jeroen van Erp
f59bbccc5f Fixed ed-25519 and openssh-key-v1 formats 2016-10-28 14:45:16 +02:00
Jeroen van Erp
bf34072c3a Reading first part of the new openssh key format 2016-10-24 09:49:45 +02:00
Jeroen van Erp
771751ca4c Fixed license header 2016-10-19 14:48:06 +01:00
Jeroen van Erp
968d4284a0 Extracted common key file methods into an abstract base class 2016-10-19 12:08:51 +01:00
85 changed files with 1156 additions and 522 deletions

8
.bettercodehub.yml Normal file
View File

@@ -0,0 +1,8 @@
exclude:
- /build-publishing.gradle
- /build.gradle
- /settings.gradle
component_depth: 1
languages:
- groovy
- java

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@
# Output dirs
target/
classes/
build/
docs/
.gradle/

View File

@@ -1,7 +1,7 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.18.0
:sshj_version: 0.19.0
:source-highlighter: pygments
image:https://travis-ci.org/hierynomus/sshj.svg?branch=master[link="https://travis-ci.org/hierynomus/sshj"]
@@ -66,8 +66,13 @@ ciphers::
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
key exchange::
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`, `diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`,
`diffie-hellman-group14-sha256`, `diffie-hellman-group15-sha512`, `diffie-hellman-group16-sha512`, `diffie-hellman-group17-sha512`, `diffie-hellman-group18-sha512`
`diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
SSHJ also supports the following extended (non official) key exchange algoriths:
`diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`,
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
signatures::
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519`
@@ -79,7 +84,7 @@ compression::
`zlib` and `zlib@openssh.com` (delayed zlib)
private key files::
`pkcs8` encoded (what openssh uses)
`pkcs5`, `pkcs8`, `openssh-key-v1`
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
@@ -99,6 +104,20 @@ Google Group: http://groups.google.com/group/sshj-users
Fork away!
== Release history
SSHJ 0.20.0 (2017-02-09)::
* Merged https://github.com/hierynomus/sshj/pulls/294[#294]: Reference ED25519 by constant instead of name
* Merged https://github.com/hierynomus/sshj/pulls/293[#293], https://github.com/hierynomus/sshj/pulls/295[#295] and https://github.com/hierynomus/sshj/pulls/301[#301]: Fixed OSGi packaging
* Added new Diffie Hellman groups 15-18 for stronger KeyExchange algorithms
SSHJ 0.19.1 (2016-12-30)::
* Enabled PKCS5 Key files in DefaultConfig
* Merged https://github.com/hierynomus/sshj/pulls/291[#291]: Fixed sshj.properties loading and chained exception messages
* Merged https://github.com/hierynomus/sshj/pulls/284[#284]: Correctly catch interrupt in keepalive thread
* Fixed https://github.com/hierynomus/sshj/issues/292[#292]: Pass the configured RandomFactory to Diffie Hellmann KEX
* Fixed https://github.com/hierynomus/sshj/issues/256[#256]: SSHJ now builds if no git repository present
* LocalPortForwarder now correctly interrupts its own thread on close()
SSHJ 0.19.0 (2016-11-25)::
* Fixed https://github.com/hierynomus/sshj/issues/276[#276]: Add support for ed-25519 and new OpenSSH key format
* Fixed https://github.com/hierynomus/sshj/issues/280[#280]: Read version from a generated sshj.properties file to correctly output version during negotiation
SSHJ 0.18.0 (2016-09-30)::
* Fixed Android compatibility
* Upgrade to Gradle 3.0

View File

@@ -37,7 +37,7 @@ dependencies {
testCompile "junit:junit:4.11"
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile "org.mockito:mockito-core:1.9.5"
testCompile "org.apache.sshd:sshd-core:1.1.0"
testCompile "org.apache.sshd:sshd-core:1.2.0"
testRuntime "ch.qos.logback:logback-classic:1.1.2"
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
@@ -53,8 +53,12 @@ license {
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java'])
}
release {
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
if (project.file('.git').isDirectory()) {
release {
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
}
} else {
version = "0.0.0-no.git"
}
// This disables the pedantic doclint feature of JDK8
@@ -64,18 +68,32 @@ if (JavaVersion.current().isJava8Compatible()) {
}
}
task writeSshjVersionProperties << {
project.file("${project.buildDir}/resources/main").mkdirs()
project.file("${project.buildDir}/resources/main/sshj.properties").withWriter { w ->
w.append("sshj.version=${version}")
}
}
jar.dependsOn writeSshjVersionProperties
jar {
manifest {
// please see http://bnd.bndtools.org/chapters/390-wrapping.html
instruction "Bundle-Description", "SSHv2 library for Java"
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
instruction "Import-Package", "!net.schmizz.*"
instruction "Import-Package", "!com.hierynomus.sshj.*"
instruction "Import-Package", "javax.crypto*"
instruction "Import-Package", "!net.i2p.crypto.eddsa.math"
instruction "Import-Package", "net.i2p*"
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
instruction "Import-Package", "org.bouncycastle*"
instruction "Import-Package", "org.bouncycastle*;resolution:=optional"
instruction "Import-Package", "org.bouncycastle.jce.provider;resolution:=optional"
instruction "Import-Package", "*"
instruction "Export-Package", "net.schmizz.*"
instruction "Export-Package", "com.hierynomus.sshj.*;version=\"${project.jar.manifest.version}\""
instruction "Export-Package", "net.schmizz.*;version=\"${project.jar.manifest.version}\""
}
}

View File

@@ -24,7 +24,7 @@
<groupId>com.hierynomus</groupId>
<artifactId>sshj-examples</artifactId>
<packaging>jar</packaging>
<version>0.14.0</version>
<version>0.19.1</version>
<name>sshj-examples</name>
<description>Examples for SSHv2 library for Java</description>
@@ -55,7 +55,7 @@
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>0.15.0</version>
<version>0.19.0</version>
</dependency>
</dependencies>

View File

@@ -3,14 +3,11 @@ package net.schmizz.sshj.examples;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** This examples demonstrates how to setup keep-alive to detect connection dropping. */
public class KeepAlive {

View File

@@ -9,6 +9,7 @@ import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import java.io.File;
import java.io.IOException;
import net.schmizz.sshj.common.LoggerFactory;
/** A very rudimentary psuedo-terminal based on console I/O. */
class RudimentaryPTY {
@@ -33,18 +34,18 @@ class RudimentaryPTY {
final Shell shell = session.startShell();
new StreamCopier(shell.getInputStream(), System.out)
new StreamCopier(shell.getInputStream(), System.out, LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stdout");
new StreamCopier(shell.getErrorStream(), System.err)
new StreamCopier(shell.getErrorStream(), System.err, LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stderr");
// Now make System.in act as stdin. To exit, hit Ctrl+D (since that results in an EOF on System.in)
// This is kinda messy because java only allows console input after you hit return
// But this is just an example... a GUI app could implement a proper PTY
new StreamCopier(System.in, shell.getOutputStream())
new StreamCopier(System.in, shell.getOutputStream(), LoggerFactory.DEFAULT)
.bufSize(shell.getRemoteMaxPacketSize())
.copy();

View File

@@ -5,6 +5,7 @@ import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectListener;
import net.schmizz.sshj.common.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -42,8 +43,8 @@ public class X11 {
final Command cmd = sess.exec("/usr/X11/bin/xcalc");
new StreamCopier(cmd.getInputStream(), System.out).spawn("stdout");
new StreamCopier(cmd.getErrorStream(), System.err).spawn("stderr");
new StreamCopier(cmd.getInputStream(), System.out, LoggerFactory.DEFAULT).spawn("stdout");
new StreamCopier(cmd.getErrorStream(), System.err, LoggerFactory.DEFAULT).spawn("stderr");
// Wait for session & X11 channel to get closed
ssh.getConnection().join();

View File

@@ -23,6 +23,8 @@ import net.schmizz.sshj.common.SSHRuntimeException;
import java.util.Arrays;
import static net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable.CURVE_ED25519_SHA512;
/**
* 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.
@@ -32,7 +34,7 @@ public class Ed25519PublicKey extends EdDSAPublicKey {
public Ed25519PublicKey(EdDSAPublicKeySpec spec) {
super(spec);
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
if (!spec.getParams().getCurve().equals(ed25519.getCurve())) {
throw new SSHRuntimeException("Cannot create Ed25519 Public Key from wrong spec");
}

View File

@@ -62,8 +62,11 @@ public class IdentificationStringParser {
}
}
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) {
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException {
byte[] bytes = new byte[lineBuffer.available()];
lineBuffer.readRawBytes(bytes);
String header = new String(bytes, 0, bytes.length - 1);
log.debug("Received header: {}", header);
}
private String readIdentification(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException, TransportException {

View File

@@ -28,6 +28,7 @@ import net.schmizz.sshj.transport.cipher.Cipher;
*
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here.
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class BlockCiphers {
public static final String COUNTER_MODE = "CTR";

View File

@@ -25,6 +25,7 @@ import static com.hierynomus.sshj.transport.cipher.BlockCiphers.COUNTER_MODE;
*
* - http://tools.ietf.org/id/draft-kanno-secsh-camellia-01.txt
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class ExtendedBlockCiphers {
public static BlockCiphers.Factory Camellia128CTR() {
return new BlockCiphers.Factory(16, 128, "camellia128-ctr", "Camellia", COUNTER_MODE);

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.transport.cipher.BaseCipher;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
public class StreamCipher extends BaseCipher {
@@ -29,6 +28,6 @@ public class StreamCipher extends BaseCipher {
@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());
cipher.init(getMode(mode), getKeySpec(key));
}
}

View File

@@ -23,6 +23,7 @@ import net.schmizz.sshj.transport.cipher.Cipher;
* - https://tools.ietf.org/html/rfc4253#section-6.3
* - https://tools.ietf.org/html/rfc4345
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class StreamCiphers {
public static Factory Arcfour() {

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.kex.AbstractDHG;
import net.schmizz.sshj.transport.kex.DH;
import net.schmizz.sshj.transport.kex.DHBase;
import javax.crypto.spec.DHParameterSpec;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
/**
*
*/
public class DHG extends AbstractDHG {
private BigInteger group;
private BigInteger generator;
public DHG(BigInteger group, BigInteger generator, Digest digest) {
super(new DH(), digest);
this.group = group;
this.generator = generator;
}
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new DHParameterSpec(group, generator), trans.getConfig().getRandomFactory());
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.*;
import net.schmizz.sshj.transport.kex.KeyExchange;
import java.math.BigInteger;
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
import static net.schmizz.sshj.transport.kex.DHGroupData.P16;
import static net.schmizz.sshj.transport.kex.DHGroupData.P18;
/**
* Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups
*
* - https://tools.ietf.org/html/rfc4253
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class DHGroups {
public static DHGroups.Factory Group1SHA1() {
return new DHGroups.Factory("diffie-hellman-group1-sha1", P1, G, new SHA1.Factory());
}
public static DHGroups.Factory Group14SHA1() {
return new DHGroups.Factory("diffie-hellman-group14-sha1", P14, G, new SHA1.Factory());
}
public static DHGroups.Factory Group14SHA256() {
return new DHGroups.Factory("diffie-hellman-group14-sha256", P14, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA512() {
return new DHGroups.Factory("diffie-hellman-group15-sha512", P15, G, new SHA512.Factory());
}
public static DHGroups.Factory Group16SHA512() {
return new DHGroups.Factory("diffie-hellman-group16-sha512", P16, G, new SHA512.Factory());
}
public static DHGroups.Factory Group17SHA512() {
return new DHGroups.Factory("diffie-hellman-group17-sha512", P17, G, new SHA512.Factory());
}
public static DHGroups.Factory Group18SHA512() {
return new DHGroups.Factory("diffie-hellman-group18-sha512", P18, G, new SHA512.Factory());
}
/**
* Named factory for DHG1 key exchange
*/
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
private String name;
private BigInteger group;
private BigInteger generator;
private Factory.Named<Digest> digestFactory;
public Factory(String name, BigInteger group, BigInteger generator, Named<Digest> digestFactory) {
this.name = name;
this.group = group;
this.generator = generator;
this.digestFactory = digestFactory;
}
@Override
public KeyExchange create() {
return new DHG(group, generator, digestFactory.create());
}
@Override
public String getName() {
return name;
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.SHA256;
import net.schmizz.sshj.transport.digest.SHA384;
import net.schmizz.sshj.transport.digest.SHA512;
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
/**
* Set of KEX methods that are not in official RFCs but are supported by some SSH servers.
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class ExtendedDHGroups {
public static DHGroups.Factory Group14SHA256AtSSH() {
return new DHGroups.Factory("diffie-hellman-group14-sha256@ssh.com", P14, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA256() {
return new DHGroups.Factory("diffie-hellman-group15-sha256", P15, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA256AtSSH() {
return new DHGroups.Factory("diffie-hellman-group15-sha256@ssh.com", P15, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA384AtSSH() {
return new DHGroups.Factory("diffie-hellman-group15-sha384@ssh.com", P15, G, new SHA384.Factory());
}
public static DHGroups.Factory Group16SHA256() {
return new DHGroups.Factory("diffie-hellman-group16-sha256", P16, G, new SHA256.Factory());
}
public static DHGroups.Factory Group16SHA384AtSSH() {
return new DHGroups.Factory("diffie-hellman-group16-sha384@ssh.com", P16, G, new SHA384.Factory());
}
public static DHGroups.Factory Group16SHA512AtSSH() {
return new DHGroups.Factory("diffie-hellman-group16-sha512@ssh.com", P16, G, new SHA512.Factory());
}
public static DHGroups.Factory Group18SHA512AtSSH() {
return new DHGroups.Factory("diffie-hellman-group18-sha512@ssh.com", P18, G, new SHA512.Factory());
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.userauth.keyprovider;
import java.io.BufferedReader;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.common.Buffer.PlainBuffer;
import net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import static net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable.CURVE_ED25519_SHA512;
/**
* Reads a key file in the new OpenSSH format.
* The format is described in the following document: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
*/
public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
private static final Logger logger = LoggerFactory.getLogger(OpenSSHKeyV1KeyFile.class);
private static final String BEGIN = "-----BEGIN ";
private static final String END = "-----END ";
private static final byte[] AUTH_MAGIC = "openssh-key-v1\0".getBytes();
public static final String OPENSSH_PRIVATE_KEY = "OPENSSH PRIVATE KEY-----";
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@Override
public FileKeyProvider create() {
return new OpenSSHKeyV1KeyFile();
}
@Override
public String getName() {
return KeyFormat.OpenSSHv1.name();
}
}
@Override
protected KeyPair readKeyPair() throws IOException {
BufferedReader reader = new BufferedReader(resource.getReader());
try {
if (!checkHeader(reader)) {
throw new IOException("This key is not in 'openssh-key-v1' format");
}
String keyFile = readKeyFile(reader);
byte[] decode = Base64.decode(keyFile);
PlainBuffer keyBuffer = new PlainBuffer(decode);
return readDecodedKeyPair(keyBuffer);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
} finally {
IOUtils.closeQuietly(reader);
}
}
private KeyPair readDecodedKeyPair(final PlainBuffer keyBuffer) throws IOException, GeneralSecurityException {
byte[] bytes = new byte[AUTH_MAGIC.length];
keyBuffer.readRawBytes(bytes); // byte[] AUTH_MAGIC
if (!ByteArrayUtils.equals(bytes, 0, AUTH_MAGIC, 0, AUTH_MAGIC.length)) {
throw new IOException("This key does not contain the 'openssh-key-v1' format magic header");
}
String cipherName = keyBuffer.readString(); // string ciphername
String kdfName = keyBuffer.readString(); // string kdfname
String kdfOptions = keyBuffer.readString(); // string kdfoptions
int nrKeys = keyBuffer.readUInt32AsInt(); // int number of keys N; Should be 1
if (nrKeys != 1) {
throw new IOException("We don't support having more than 1 key in the file (yet).");
}
PublicKey publicKey = readPublicKey(new PlainBuffer(keyBuffer.readBytes())); // string publickey1
PlainBuffer privateKeyBuffer = new PlainBuffer(keyBuffer.readBytes()); // string (possibly) encrypted, padded list of private keys
if ("none".equals(cipherName)) {
logger.debug("Reading unencrypted keypair");
return readUnencrypted(privateKeyBuffer, publicKey);
} else {
logger.info("Keypair is encrypted with: " + cipherName + ", " + kdfName + ", " + kdfOptions);
throw new IOException("Cannot read encrypted keypair with " + cipherName + " yet.");
}
}
private PublicKey readPublicKey(final PlainBuffer plainBuffer) throws Buffer.BufferException, GeneralSecurityException {
return KeyType.fromString(plainBuffer.readString()).readPubKeyFromBuffer(plainBuffer);
}
private String readKeyFile(final BufferedReader reader) throws IOException {
StringBuilder sb = new StringBuilder();
String line = reader.readLine();
while (!line.startsWith(END)) {
sb.append(line);
line = reader.readLine();
}
return sb.toString();
}
private boolean checkHeader(final BufferedReader reader) throws IOException {
String line = reader.readLine();
while (line != null && !line.startsWith(BEGIN)) {
line = reader.readLine();
}
line = line.substring(BEGIN.length());
return line.startsWith(OPENSSH_PRIVATE_KEY);
}
private KeyPair readUnencrypted(final PlainBuffer keyBuffer, final PublicKey publicKey) throws IOException, GeneralSecurityException {
int privKeyListSize = keyBuffer.available();
if (privKeyListSize % 8 != 0) {
throw new IOException("The private key section must be a multiple of the block size (8)");
}
int checkInt1 = keyBuffer.readUInt32AsInt(); // uint32 checkint1
int checkInt2 = keyBuffer.readUInt32AsInt(); // uint32 checkint2
if (checkInt1 != checkInt2) {
throw new IOException("The checkInts differed, the key was not correctly decoded.");
}
// The private key section contains both the public key and the private key
String keyType = keyBuffer.readString(); // string keytype
logger.info("Read key type: {}", keyType);
byte[] pubKey = keyBuffer.readBytes(); // string publickey (again...)
keyBuffer.readUInt32();
byte[] privKey = new byte[32];
keyBuffer.readRawBytes(privKey); // string privatekey
keyBuffer.readRawBytes(new byte[32]); // string publickey (again...)
String comment = keyBuffer.readString(); // string comment
byte[] padding = new byte[keyBuffer.available()];
keyBuffer.readRawBytes(padding); // char[] padding
for (int i = 0; i < padding.length; i++) {
if ((int) padding[i] != i + 1) {
throw new IOException("Padding of key format contained wrong byte at position: " + i);
}
}
return new KeyPair(publicKey, new EdDSAPrivateKey(new EdDSAPrivateKeySpec(privKey, EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512))));
}
}

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class KeepAlive extends Thread {
protected final Logger log;
@@ -65,6 +64,8 @@ public abstract class KeepAlive extends Thread {
}
Thread.sleep(hi * 1000);
}
} catch (InterruptedException e) {
// Interrupt signal may be catched when sleeping.
} catch (Exception e) {
// If we weren't interrupted, kill the transport, then this exception was unexpected.
// Else we're in shutdown-mode already, so don't forcibly kill the transport.

View File

@@ -18,6 +18,9 @@ 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 com.hierynomus.sshj.transport.kex.DHGroups;
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
@@ -27,20 +30,22 @@ import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.cipher.*;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.kex.*;
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
import net.schmizz.sshj.transport.kex.DHGexSHA1;
import net.schmizz.sshj.transport.kex.DHGexSHA256;
import net.schmizz.sshj.transport.kex.ECDHNistP;
import net.schmizz.sshj.transport.mac.*;
import net.schmizz.sshj.transport.random.BouncyCastleRandom;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
import net.schmizz.sshj.userauth.keyprovider.PKCS5KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import org.slf4j.Logger;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.io.IOException;
import java.util.*;
/**
* A {@link net.schmizz.sshj.Config} that is initialized as follows. Items marked with an asterisk are added to the config only if
@@ -67,13 +72,11 @@ import java.util.List;
public class DefaultConfig
extends ConfigImpl {
private static final String VERSION = "SSHJ_0_17_2";
private Logger log;
public DefaultConfig() {
setLoggerFactory(LoggerFactory.DEFAULT);
setVersion(VERSION);
setVersion(readVersionFromProperties());
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
initKeyExchangeFactories(bouncyCastleRegistered);
initRandomFactory(bouncyCastleRegistered);
@@ -85,6 +88,18 @@ public class DefaultConfig
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
}
private String readVersionFromProperties() {
try {
Properties properties = new Properties();
properties.load(DefaultConfig.class.getClassLoader().getResourceAsStream("sshj.properties"));
String property = properties.getProperty("sshj.version");
return "SSHJ_" + property.replace('-', '_'); // '-' is a disallowed character, see RFC-4253#section-4.2
} catch (IOException e) {
log.error("Could not read the sshj.properties file, returning an 'unknown' version as fallback.");
return "SSHJ_VERSION_UNKNOWN";
}
}
@Override
public void setLoggerFactory(LoggerFactory loggerFactory) {
super.setLoggerFactory(loggerFactory);
@@ -99,10 +114,23 @@ public class DefaultConfig
new ECDHNistP.Factory384(),
new ECDHNistP.Factory256(),
new DHGexSHA1.Factory(),
new DHG14.Factory(),
new DHG1.Factory());
DHGroups.Group1SHA1(),
DHGroups.Group14SHA1(),
DHGroups.Group14SHA256(),
DHGroups.Group15SHA512(),
DHGroups.Group16SHA512(),
DHGroups.Group17SHA512(),
DHGroups.Group18SHA512(),
ExtendedDHGroups.Group14SHA256AtSSH(),
ExtendedDHGroups.Group15SHA256(),
ExtendedDHGroups.Group15SHA256AtSSH(),
ExtendedDHGroups.Group15SHA384AtSSH(),
ExtendedDHGroups.Group16SHA256(),
ExtendedDHGroups.Group16SHA384AtSSH(),
ExtendedDHGroups.Group16SHA512AtSSH(),
ExtendedDHGroups.Group18SHA512AtSSH());
} else {
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
}
}
@@ -113,7 +141,12 @@ public class DefaultConfig
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered) {
setFileKeyProviderFactories(new PKCS8KeyFile.Factory(), new OpenSSHKeyFile.Factory(), new PuTTYKeyFile.Factory());
setFileKeyProviderFactories(
new OpenSSHKeyV1KeyFile.Factory(),
new PKCS8KeyFile.Factory(),
new PKCS5KeyFile.Factory(),
new OpenSSHKeyFile.Factory(),
new PuTTYKeyFile.Factory());
}
}

View File

@@ -316,7 +316,7 @@ public class SSHClient
public void authPublickey(String username)
throws UserAuthException, TransportException {
final String base = System.getProperty("user.home") + File.separator + ".ssh" + File.separator;
authPublickey(username, base + "id_rsa", base + "id_dsa");
authPublickey(username, base + "id_rsa", base + "id_dsa", base + "id_ed25519", base + "id_ecdsa");
}
/**
@@ -524,8 +524,13 @@ public class SSHClient
}
/**
* Creates a {@link KeyProvider} instance from given location on the file system. Currently only PKCS8 format
* private key files are supported (OpenSSH uses this format).
* Creates a {@link KeyProvider} instance from given location on the file system. Currently the following private key files are supported:
* <ul>
* <li>PKCS8 (OpenSSH uses this format)</li>
* <li>PKCS5</li>
* <li>Putty keyfile</li>
* <li>openssh-key-v1 (New OpenSSH keyfile format)</li>
* </ul>
* <p/>
*
* @param location the location of the key file

View File

@@ -311,7 +311,7 @@ public class Buffer<T extends Buffer<T>> {
public T putUInt32(long uint32) {
ensureCapacity(4);
if (uint32 < 0 || uint32 > 0xffffffffL)
throw new RuntimeException("Invalid value: " + uint32);
throw new IllegalArgumentException("Invalid value: " + uint32);
data[wpos++] = (byte) (uint32 >> 24);
data[wpos++] = (byte) (uint32 >> 16);
data[wpos++] = (byte) (uint32 >> 8);
@@ -346,7 +346,7 @@ public class Buffer<T extends Buffer<T>> {
@SuppressWarnings("unchecked")
public T putUInt64(long uint64) {
if (uint64 < 0)
throw new RuntimeException("Invalid value: " + uint64);
throw new IllegalArgumentException("Invalid value: " + uint64);
data[wpos++] = (byte) (uint64 >> 56);
data[wpos++] = (byte) (uint64 >> 48);
data[wpos++] = (byte) (uint64 >> 40);

View File

@@ -15,8 +15,6 @@
*/
package net.schmizz.sshj.common;
import org.slf4j.Logger;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;

View File

@@ -192,7 +192,7 @@ public enum KeyType {
);
}
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512);
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(p, ed25519);
return new Ed25519PublicKey(publicSpec);

View File

@@ -34,7 +34,7 @@ public class SSHRuntimeException
}
public SSHRuntimeException(Throwable cause) {
this(null, cause);
this(cause.getMessage(), cause);
}
}

View File

@@ -254,7 +254,7 @@ public class SecurityUtils {
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut JCE provider");
}
}
registrationDone = true;
}
registrationDone = true;
}
}

View File

@@ -15,7 +15,6 @@
*/
package net.schmizz.sshj.common;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.ExceptionChainer;
import org.slf4j.Logger;
@@ -68,8 +67,11 @@ public class StreamCopier {
}
public StreamCopier listener(Listener listener) {
if (listener == null) listener = NULL_LISTENER;
this.listener = listener;
if (listener == null) {
this.listener = NULL_LISTENER;
} else {
this.listener = listener;
}
return this;
}
@@ -126,11 +128,13 @@ public class StreamCopier {
final long startTime = System.currentTimeMillis();
if (length == -1) {
while ((read = in.read(buf)) != -1)
count = write(buf, count, read);
while ((read = in.read(buf)) != -1) {
count += write(buf, count, read);
}
} else {
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1)
count = write(buf, count, read);
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1) {
count += write(buf, count, read);
}
}
if (!keepFlushing)
@@ -146,14 +150,13 @@ public class StreamCopier {
return count;
}
private long write(byte[] buf, long count, int read)
private long write(byte[] buf, long curPos, int len)
throws IOException {
out.write(buf, 0, read);
count += read;
out.write(buf, 0, len);
if (keepFlushing)
out.flush();
listener.reportProgress(count);
return count;
listener.reportProgress(curPos + len);
return len;
}
}

View File

@@ -126,10 +126,9 @@ public class ConnectionImpl
@Override
public void handle(Message msg, SSHPacket buf)
throws SSHException {
if (msg.in(91, 100))
if (msg.in(91, 100)) {
getChannel(buf).handle(msg, buf);
else if (msg.in(80, 90))
} else if (msg.in(80, 90)) {
switch (msg) {
case REQUEST_SUCCESS:
gotGlobalReqResponse(buf);
@@ -142,10 +141,11 @@ public class ConnectionImpl
break;
default:
super.handle(msg, buf);
break;
}
else
} else {
super.handle(msg, buf);
}
}
@Override
@@ -174,11 +174,11 @@ public class ConnectionImpl
}
@Override
public void join()
throws InterruptedException {
public void join() throws InterruptedException {
synchronized (internalSynchronizer) {
while (!channels.isEmpty())
while (!channels.isEmpty()) {
internalSynchronizer.wait();
}
}
}

View File

@@ -189,7 +189,7 @@ public abstract class AbstractChannel
default:
gotUnknown(msg, buf);
break;
}
}
@@ -329,6 +329,8 @@ public abstract class AbstractChannel
protected void gotUnknown(Message msg, SSHPacket buf)
throws ConnectionException, TransportException {
log.warn("Got unknown packet with type {}", msg);
}
protected void handleRequest(String reqType, SSHPacket buf)

View File

@@ -20,7 +20,6 @@ import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;

View File

@@ -27,9 +27,7 @@ import java.io.OutputStream;
* {@link OutputStream} for channels. Buffers data upto the remote window's maximum packet size. Data can also be
* flushed via {@link #flush()} and is also flushed on {@link #close()}.
*/
public final class ChannelOutputStream
extends OutputStream
implements ErrorNotifiable {
public final class ChannelOutputStream extends OutputStream implements ErrorNotifiable {
private final Channel chan;
private final Transport trans;
@@ -56,8 +54,7 @@ public final class ChannelOutputStream
dataOffset = packet.wpos();
}
int write(byte[] data, int off, int len)
throws TransportException, ConnectionException {
int write(byte[] data, int off, int len) throws TransportException, ConnectionException {
final int bufferSize = packet.wpos() - dataOffset;
if (bufferSize >= win.getMaxPacketSize()) {
flush(bufferSize, true);
@@ -69,15 +66,13 @@ public final class ChannelOutputStream
}
}
boolean flush(boolean canAwaitExpansion)
throws TransportException, ConnectionException {
boolean flush(boolean canAwaitExpansion) throws TransportException, ConnectionException {
return flush(packet.wpos() - dataOffset, canAwaitExpansion);
}
boolean flush(int bufferSize, boolean canAwaitExpansion)
throws TransportException, ConnectionException {
while (bufferSize > 0) {
boolean flush(int bufferSize, boolean canAwaitExpansion) throws TransportException, ConnectionException {
int dataLeft = bufferSize;
while (dataLeft > 0) {
long remoteWindowSize = win.getSize();
if (remoteWindowSize == 0) {
if (canAwaitExpansion) {
@@ -91,7 +86,7 @@ public final class ChannelOutputStream
// a) how much data we have
// b) the max packet size
// c) what the current window size will allow
final int writeNow = Math.min(bufferSize, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
final int writeNow = Math.min(dataLeft, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
packet.wpos(headerOffset);
packet.putMessageID(Message.CHANNEL_DATA);
@@ -99,7 +94,7 @@ public final class ChannelOutputStream
packet.putUInt32(writeNow);
packet.wpos(dataOffset + writeNow);
final int leftOverBytes = bufferSize - writeNow;
final int leftOverBytes = dataLeft - writeNow;
if (leftOverBytes > 0) {
leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes);
}
@@ -115,7 +110,7 @@ public final class ChannelOutputStream
leftOvers.clear();
}
bufferSize = leftOverBytes;
dataLeft = leftOverBytes;
}
return true;
@@ -140,10 +135,12 @@ public final class ChannelOutputStream
public synchronized void write(final byte[] data, int off, int len)
throws IOException {
checkClose();
while (len > 0) {
final int n = buffer.write(data, off, len);
off += n;
len -= n;
int length = len;
int offset = off;
while (length > 0) {
final int n = buffer.write(data, offset, len);
offset += n;
length -= n;
}
}
@@ -182,8 +179,7 @@ public final class ChannelOutputStream
* @throws IOException
*/
@Override
public synchronized void flush()
throws IOException {
public synchronized void flush() throws IOException {
checkClose();
buffer.flush(true);
}

View File

@@ -107,6 +107,7 @@ public class LocalPortForwarder {
private final Connection conn;
private final Parameters parameters;
private final ServerSocket serverSocket;
private Thread runningThread;
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket, LoggerFactory loggerFactory) {
this.conn = conn;
@@ -132,10 +133,20 @@ public class LocalPortForwarder {
*
* @throws IOException
*/
public void listen()
throws IOException {
public void listen() throws IOException {
listen(Thread.currentThread());
}
/**
* Start listening for incoming connections and forward to remote host as a channel and ensure that the thread is registered.
* This is useful if for instance {@link #close() is called from another thread}
*
* @throws IOException
*/
public void listen(Thread runningThread) throws IOException {
this.runningThread = runningThread;
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
while (!Thread.currentThread().isInterrupted()) {
while (!runningThread.isInterrupted()) {
try {
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
@@ -162,6 +173,7 @@ public class LocalPortForwarder {
if (!serverSocket.isClosed()) {
log.info("Closing listener on {}", serverSocket.getLocalSocketAddress());
serverSocket.close();
runningThread.interrupt();
}
}

View File

@@ -21,7 +21,6 @@ import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.OpenFailException;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;

View File

@@ -130,7 +130,7 @@ public class RemotePortForwarder
// Addresses match up
return true;
}
if ("localhost".equals(address) && (channelForward.address.equals("127.0.0.1") || channelForward.address.equals("::1"))) {
if ("localhost".equals(address) && ("127.0.0.1".equals(channelForward.address) || "::1".equals(channelForward.address))) {
// Localhost special case.
return true;
}

View File

@@ -18,17 +18,17 @@ package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.SSHException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PacketReader
extends Thread {
public class PacketReader extends Thread {
/** Logger */
/**
* Logger
*/
private final Logger log;
private final InputStream in;
@@ -64,7 +64,7 @@ public class PacketReader
| lenBuf[3] & 0x000000ffL);
if (len > SFTPPacket.MAX_SIZE) {
throw new SSHException(String.format("Indicated packet length %d too large", len));
throw new SSHException(String.format("Indicated packet length %d too large", len));
}
return (int) len;
@@ -100,7 +100,7 @@ public class PacketReader
log.debug("Received {} packet", resp.getType());
if (promise == null)
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
+ ", no such request was made");
+ ", no such request was made");
else
promise.deliver(resp);
}

View File

@@ -73,7 +73,7 @@ public class PathHelper {
if (path.equals(pathSep))
return getComponents("", "");
if (path.isEmpty() || path.equals(".") || path.equals("." + pathSep))
if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path))
return getComponents(getDotDir());
final String withoutTrailSep = trimTrailingSeparator(path);
@@ -81,7 +81,7 @@ public class PathHelper {
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep);
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length());
if (name.equals(".") || name.equals("..")) {
if (".".equals(name) || "..".equals(name)) {
return getComponents(canonicalizer.canonicalize(path));
} else {
return getComponents(parent, name);

View File

@@ -32,6 +32,7 @@ public class RemoteDirectory
public List<RemoteResourceInfo> scan(RemoteResourceFilter filter)
throws IOException {
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
// TODO: Remove GOTO!
loop:
for (; ; ) {
final Response res = requester.request(newRequest(PacketType.READDIR))
@@ -46,8 +47,9 @@ public class RemoteDirectory
final FileAttributes attrs = res.readFileAttributes();
final PathComponents comps = requester.getPathHelper().getComponents(path, name);
final RemoteResourceInfo inf = new RemoteResourceInfo(comps, attrs);
if (!(name.equals(".") || name.equals("..")) && (filter == null || filter.accept(inf)))
if (!(".".equals(name) || "..".equals(name)) && (filter == null || filter.accept(inf))) {
rri.add(inf);
}
}
break;

View File

@@ -81,10 +81,10 @@ public class RemoteFile
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)
// TODO The SFTP spec claims this field is unneeded...? See #187
.putUInt32(len)
.putRawBytes(data, off, len)
);
}
@@ -194,10 +194,10 @@ public class RemoteFile
@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;
final long fileLength = length();
final Long previousFileOffset = fileOffset;
fileOffset = Math.min(fileOffset + n, fileLength);
return fileOffset - previousFileOffset;
}
@Override
@@ -341,7 +341,7 @@ public class RemoteFile
public int available() throws IOException {
boolean lastRead = true;
while (!eof && (pending.available() <= 0) && lastRead) {
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
}
return pending.available();
}

View File

@@ -16,7 +16,6 @@
package net.schmizz.sshj.sftp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;

View File

@@ -20,7 +20,7 @@ import net.schmizz.sshj.common.Buffer;
public final class Response
extends SFTPPacket<Response> {
public static enum StatusCode {
public enum StatusCode {
UNKNOWN(-1),
OK(0),
EOF(1),
@@ -122,6 +122,7 @@ public final class Response
return ensurePacketTypeIs(PacketType.STATUS).ensureStatusIs(StatusCode.OK);
}
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public Response ensureStatusIs(StatusCode acceptable)
throws SFTPException {
final StatusCode sc = readStatusCode();

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.xfer.FilePermission;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalSourceFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;

View File

@@ -60,14 +60,12 @@ public class SFTPFileTransfer
}
@Override
public void upload(LocalSourceFile localFile, String remotePath)
throws IOException {
public void upload(LocalSourceFile localFile, String remotePath) throws IOException {
new Uploader(localFile, remotePath).upload(getTransferListener());
}
@Override
public void download(String source, LocalDestFile dest)
throws IOException {
public void download(String source, LocalDestFile dest) throws IOException {
final PathComponents pathComponents = engine.getPathHelper().getComponents(source);
final FileAttributes attributes = engine.stat(source);
new Downloader().download(getTransferListener(), new RemoteResourceInfo(pathComponents, attributes), dest);
@@ -91,10 +89,10 @@ public class SFTPFileTransfer
private class Downloader {
@SuppressWarnings("PMD.MissingBreakInSwitch")
private void download(final TransferListener listener,
final RemoteResourceInfo remote,
final LocalDestFile local)
throws IOException {
final LocalDestFile local) throws IOException {
final LocalDestFile adjustedFile;
switch (remote.getAttributes().getType()) {
case DIRECTORY:
@@ -104,8 +102,7 @@ public class SFTPFileTransfer
log.warn("Server did not supply information about the type of file at `{}` " +
"-- assuming it is a regular file!", remote.getPath());
case REGULAR:
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()),
remote, local);
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()), remote, local);
break;
default:
throw new IOException(remote + " is not a regular file or directory");

View File

@@ -84,7 +84,7 @@ public abstract class AbstractSignature
| sig[i++] & 0x000000ff;
byte[] newSig = new byte[j];
System.arraycopy(sig, i, newSig, 0, j);
sig = newSig;
return newSig;
}
return sig;
}

View File

@@ -79,7 +79,7 @@ public class SignatureECDSA
throw new SSHRuntimeException(String.format("Signature :: ecdsa-sha2-nistp256 expected, got %s", algo));
}
final int rsLen = sigbuf.readUInt32AsInt();
if (!(sigbuf.available() == rsLen)) {
if (sigbuf.available() != rsLen) {
throw new SSHRuntimeException("Invalid key length");
}
r = sigbuf.readBytes();

View File

@@ -16,12 +16,10 @@
package net.schmizz.sshj.transport;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
import net.schmizz.sshj.transport.mac.MAC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Decodes packets from the SSH binary protocol per the current algorithms. */
final class Decoder

View File

@@ -39,19 +39,6 @@ final class Encoder
log = loggerFactory.getLogger(getClass());
}
private SSHPacket checkHeaderSpace(SSHPacket buffer) {
if (buffer.rpos() < 5) {
log.warn("Performance cost: when sending a packet, ensure that "
+ "5 bytes are available in front of the buffer");
SSHPacket nb = new SSHPacket(buffer.available() + 5);
nb.rpos(5);
nb.wpos(5);
nb.putBuffer(buffer);
buffer = nb;
}
return buffer;
}
private void compress(SSHPacket buffer) {
compression.compress(buffer);
}

View File

@@ -26,7 +26,6 @@ import net.schmizz.sshj.transport.mac.MAC;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
@@ -158,6 +157,7 @@ final class KeyExchanger
"Key exchange packet received when key exchange was not ongoing");
}
@SuppressWarnings("PMD.CompareObjectsWithEquals")
private static void ensureReceivedMatchesExpected(Message got, Message expected)
throws TransportException {
if (got != expected)

View File

@@ -16,7 +16,6 @@
package net.schmizz.sshj.transport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.net.SocketTimeoutException;

View File

@@ -33,7 +33,9 @@ import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/** A thread-safe {@link Transport} implementation. */
/**
* A thread-safe {@link Transport} implementation.
*/
public final class TransportImpl
implements Transport, DisconnectListener {
@@ -49,11 +51,11 @@ public final class TransportImpl
static final class ConnInfo {
final String host;
final int port;
final InputStream in;
final OutputStream out;
public ConnInfo(String host, int port, InputStream in, OutputStream out) {
ConnInfo(String host, int port, InputStream in, OutputStream out) {
this.host = host;
this.port = port;
this.in = in;
@@ -88,24 +90,32 @@ public final class TransportImpl
private final Event<TransportException> close;
/** Client version identification string */
/**
* Client version identification string
*/
private final String clientID;
private volatile int timeoutMs = 30 * 1000; // Crazy long, but it was the original default
private volatile boolean authed = false;
/** Currently active service e.g. UserAuthService, ConnectionService */
/**
* Currently active service e.g. UserAuthService, ConnectionService
*/
private volatile Service service;
private DisconnectListener disconnectListener;
private ConnInfo connInfo;
/** Server version identification string */
/**
* Server version identification string
*/
private String serverID;
/** Message identifier of last packet received */
/**
* Message identifier of last packet received
*/
private Message msg;
private final ReentrantLock writeLock = new ReentrantLock();
@@ -115,9 +125,9 @@ public final class TransportImpl
this.loggerFactory = config.getLoggerFactory();
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
this.nullService = new NullService(this);
this.nullService = new NullService(this);
this.service = nullService;
this.log = loggerFactory.getLogger(getClass());
this.log = loggerFactory.getLogger(getClass());
this.disconnectListener = this;
this.reader = new Reader(this);
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock, loggerFactory);
@@ -138,7 +148,7 @@ public final class TransportImpl
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
this.log = loggerFactory.getLogger(getClass());
this.nullService = new NullService(this);
this.nullService = new NullService(this);
this.service = nullService;
this.disconnectListener = this;
this.reader = new Reader(this);
@@ -194,6 +204,7 @@ public final class TransportImpl
/**
* Receive the server identification string.
*
* @throws IOException If there was an error writing to the outputstream.
*/
private void sendClientIdent() throws IOException {
@@ -212,9 +223,7 @@ public final class TransportImpl
* This is not efficient but is only done once.
*
* @param buffer The buffer to read from.
*
* @return empty string if full ident string has not yet been received
*
* @throws IOException
*/
private String readIdentification(Buffer.PlainBuffer buffer)
@@ -226,7 +235,7 @@ public final class TransportImpl
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
"Server does not support SSHv2, identified as: " + ident);
"Server does not support SSHv2, identified as: " + ident);
return ident;
}
@@ -337,7 +346,6 @@ public final class TransportImpl
* Sends a service request for the specified service
*
* @param serviceName name of the service being requested
*
* @throws TransportException if there is an error while sending the request
*/
private void sendServiceRequest(String serviceName)
@@ -456,9 +464,9 @@ public final class TransportImpl
log.debug("Sending SSH_MSG_DISCONNECT: reason=[{}], msg=[{}]", reason, message);
try {
write(new SSHPacket(Message.DISCONNECT)
.putUInt32(reason.toInt())
.putString(message)
.putString(""));
.putUInt32(reason.toInt())
.putString(message)
.putString(""));
} catch (IOException worthless) {
log.debug("Error writing packet: {}", worthless.toString());
}
@@ -476,7 +484,6 @@ public final class TransportImpl
*
* @param msg the message identifer
* @param buf buffer containg rest of the packet
*
* @throws SSHException if an error occurs during handling (unrecoverable)
*/
@Override
@@ -494,32 +501,27 @@ public final class TransportImpl
else
switch (msg) {
case DISCONNECT: {
case DISCONNECT:
gotDisconnect(buf);
break;
}
case IGNORE: {
case IGNORE:
log.debug("Received SSH_MSG_IGNORE");
break;
}
case UNIMPLEMENTED: {
case UNIMPLEMENTED:
gotUnimplemented(buf);
break;
}
case DEBUG: {
case DEBUG:
gotDebug(buf);
break;
}
case SERVICE_ACCEPT: {
case SERVICE_ACCEPT:
gotServiceAccept();
break;
}
case USERAUTH_BANNER: {
case USERAUTH_BANNER:
log.debug("Received USERAUTH_BANNER");
break;
}
default:
sendUnimplemented();
break;
}
}
@@ -552,7 +554,7 @@ public final class TransportImpl
try {
if (!serviceAccept.hasWaiters())
throw new TransportException(DisconnectReason.PROTOCOL_ERROR,
"Got a service accept notification when none was awaited");
"Got a service accept notification when none was awaited");
serviceAccept.set();
} finally {
serviceAccept.unlock();
@@ -563,7 +565,6 @@ public final class TransportImpl
* Got an SSH_MSG_UNIMPLEMENTED, so lets see where we're at and act accordingly.
*
* @param packet The 'unimplemented' packet received
*
* @throws TransportException
*/
private void gotUnimplemented(SSHPacket packet)

View File

@@ -46,10 +46,12 @@ public class NoneCipher
@Override
public void init(Mode mode, byte[] bytes, byte[] bytes1) {
// Nothing to do
}
@Override
public void update(byte[] input, int inputOffset, int inputLen) {
// Nothing to do
}
}

View File

@@ -103,7 +103,7 @@ public abstract class AbstractDHGex extends AbstractDH {
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));
dh.init(new DHParameterSpec(p, g), trans.getConfig().getRandomFactory());
log.debug("Sending {}", Message.KEX_DH_GEX_INIT);
trans.write(new SSHPacket(Message.KEX_DH_GEX_INIT).putBytes(dh.getE()));
return false;

View File

@@ -15,19 +15,19 @@
*/
package net.schmizz.sshj.transport.kex;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.jce.spec.ECParameterSpec;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.transport.random.Random;
public class Curve25519DH extends DHBase {
private byte[] secretKey;
public Curve25519DH() {
@@ -42,10 +42,10 @@ public class Curve25519DH extends DHBase {
}
@Override
public void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
SecureRandom secureRandom = new SecureRandom();
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
Random random = randomFactory.create();
byte[] secretBytes = new byte[32];
secureRandom.nextBytes(secretBytes);
random.fill(secretBytes);
byte[] publicBytes = new byte[32];
djb.Curve25519.keygen(publicBytes, null, secretBytes);
this.secretKey = Arrays.copyOf(secretBytes, secretBytes.length);

View File

@@ -16,14 +16,10 @@
package net.schmizz.sshj.transport.kex;
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> {
@@ -45,6 +41,6 @@ public class Curve25519SHA256 extends AbstractDHG {
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(Curve25519DH.getCurve25519Params());
dh.init(Curve25519DH.getCurve25519Params(), trans.getConfig().getRandomFactory());
}
}

View File

@@ -15,8 +15,10 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.random.Random;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
@@ -38,7 +40,7 @@ public class DH extends DHBase {
}
@Override
protected void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
if (!(params instanceof DHParameterSpec)) {
throw new SSHRuntimeException("Wrong algorithm parameters for Diffie Hellman");
}

View File

@@ -15,8 +15,10 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.random.Random;
import javax.crypto.KeyAgreement;
import java.math.BigInteger;
@@ -24,7 +26,7 @@ import java.security.GeneralSecurityException;
import java.security.KeyPairGenerator;
import java.security.spec.AlgorithmParameterSpec;
abstract class DHBase {
public abstract class DHBase {
protected final KeyPairGenerator generator;
protected final KeyAgreement agreement;
@@ -42,7 +44,7 @@ abstract class DHBase {
abstract void computeK(byte[] f) throws GeneralSecurityException;
protected abstract void init(AlgorithmParameterSpec params) throws GeneralSecurityException;
public abstract void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException;
void setE(byte[] e) {
this.e = e;

View File

@@ -26,6 +26,7 @@ import java.security.GeneralSecurityException;
* @see <a href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</a>
*
* TODO refactor away the (unneeded) class
* @deprecated Replaced by {@link com.hierynomus.sshj.transport.kex.DHG} with {@link com.hierynomus.sshj.transport.kex.DHGroups}
*/
public class DHG1
extends AbstractDHG {
@@ -51,6 +52,6 @@ public class DHG1
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new DHParameterSpec(DHGroupData.P1, DHGroupData.G));
dh.init(new DHParameterSpec(DHGroupData.P1, DHGroupData.G), trans.getConfig().getRandomFactory());
}
}

View File

@@ -25,6 +25,8 @@ import java.security.GeneralSecurityException;
* <p/>
* DHG14 does not work with the default JCE implementation provided by Sun because it does not support 2048 bits
* encryption. It requires BouncyCastle to be used.
*
* @deprecated Replaced by {@link com.hierynomus.sshj.transport.kex.DHG} with {@link com.hierynomus.sshj.transport.kex.DHGroups}
*/
public class DHG14
extends AbstractDHG {
@@ -51,6 +53,6 @@ public class DHG14
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new DHParameterSpec(DHGroupData.P14, DHGroupData.G));
dh.init(new DHParameterSpec(DHGroupData.P14, DHGroupData.G), trans.getConfig().getRandomFactory());
}
}

View File

@@ -17,26 +17,139 @@ package net.schmizz.sshj.transport.kex;
import java.math.BigInteger;
/** Simple class holding the data for DH group key exchanges. */
/**
* Simple class holding the data for DH group key exchanges.
*/
public final class DHGroupData {
public static final BigInteger G =
new BigInteger("2");
/**
* First Oakley Group (https://tools.ietf.org/html/rfc2409) - P1
* prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
*/
public static final BigInteger P1 =
new BigInteger("1797693134862315907708391567937874531978602960487560117064444236841971802161585193" +
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
"727629299387668709205606050270810842907692932019128194467627007");
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
"727629299387668709205606050270810842907692932019128194467627007");
/**
* 2048-bit MODP Group - P14 (https://tools.ietf.org/html/rfc3526#section-3)
* prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
*/
public static final BigInteger P14 =
new BigInteger("3231700607131100730033891392642382824881794124114023911284200975140074170663435422" +
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
"9448652468262416972035911852507045361090559");
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
"9448652468262416972035911852507045361090559");
/**
* 3072-bit MODP Group - P15 (https://tools.ietf.org/html/rfc3526#section-4)
* prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
*/
public static final BigInteger P15 =
new BigInteger("5809605995369958062791915965639201402176612226902900533702900882779736177890990861" +
"4720947744773395811473734101856463783280437298007504700982109244878669350591643715" +
"8816804754094398164451663275506750162643455639819318662899007124866081936120511979" +
"3693985433297036118232914410171876807536457391277857011849897410207519105333355801" +
"1211093568974594262718454713979526759594407934930716283941227805101246184882326024" +
"6464987685045886124578424092925842628769970531258450962541951346360515542801716571" +
"4465363094021609290561084025893662561222573202082865797821865270991145082200656978" +
"1771928270245389902399691755461907706456858934380117144304264093386763147435711545" +
"3714203157300427642870143303638180170530865983075119035294602548205993130657100472" +
"7362479688415574702596946457770284148435989129632853918392117997472632693078113129" +
"8864873993477969827727846158652326212896569442842168246113187097645351525073541163" +
"44703769998514148343807");
/**
* 4096-bit MODP Group - P16 (https://tools.ietf.org/html/rfc3526#section-5)
* prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
*/
public static final BigInteger P16 =
new BigInteger("10443888814131525066796027198465295458312690609921350090225887564443381720223226907" +
"10444046669809783930111585737890362691860127079270495454517218673016928427459146001" +
"86688577976298222932119236830334623520436805101030915567415569746034717694639407653" +
"51572849948952848216337009218117167389724518349794558970103063334685907513583651387" +
"82250372269117968985194322444535687415522007151638638141456178420621277822674995027" +
"99027867345862954439173691976629900551150544617766815444623488266596168079657690319" +
"91160893476349471877789065280080047566925716669229641225661745827767073324523710012" +
"72163776841229318324903125740713574141005124561965913888899753461735347970011693256" +
"31675166067895083002751025580484610558346505544661509044430958305077580850929704003" +
"96800574353422539265662408981958636315888889363641299200593084556694540340103914782" +
"38784189888594672336242763795138176353222845524644040094258962433613354036104643881" +
"92523848922401019419308891166616558422942466816544168892779046060826486420423771700" +
"20547443379889419746612146996897065215430062626045358909981257522759426087721743761" +
"07314217749233048217904944409836238235772306749874396760463376480215133461333478395" +
"682746608242585133953883882226786118030184028136755970045385534758453247");
/**
* 6144-bit MODP Group - P17 (https://tools.ietf.org/html/rfc3526#section-6)
* prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
*/
public static final BigInteger P17 =
new BigInteger("3375152182143856118451852315996741233006489780574184654817389047442942990132667244" +
"5203235101919165483964194359460994881062089387893762814044257438204432573941083014" +
"8270060902589258751610180963277323358005958319159760142088223040073278481327349332" +
"9788580321367526156496260334045722077682632250005809131096725397661997398803366366" +
"6385188155212656268079501726223369693427999804134467810120772356498596945532366527" +
"4005175754719693358549052745041195095923660137119541482588848792245999152034563158" +
"8103477655308367699571833559858639559116999957082451503501754353335269752528775333" +
"2500527176569576894926734950469293596134095086603716860086302051544539652689091299" +
"0997845889190523834630577894405654606814419024423999564190605216296046973478790246" +
"5431380018607831652696452928806274087901103517592005919217856147319900620589671943" +
"5014765345518490882366607110905303449152556221163232127426440691921134648766635695" +
"8502392313045917442156109850296368954067188807663082492273159842675422662594896843" +
"7222391644541101590050623941926790971632033120898897818086898743162371034761799235" +
"6201449023892203230133009421463914291201346063125219636964261683591541014344239275" +
"3407356909977322220697587739633908763605465157552805170421605254873028981223116697" +
"9967944753045360039934269703271445854959128593945394903498124811432232236723864504" +
"2515984447890788917823576330019151696568654314153058547592091366014550143819685170" +
"0683437001046776090411663697600809334136054989623820777788455998349074759534307874" +
"4620138456732853067527579296235488377080690082718368571835346957473168052062194454" +
"0947734619035177180057973022652571032196598229259194875709994709721793154158686515" +
"7485072742241813169487971046010682120152329216914824963468544136987197501906011027" +
"0527448105054323981513068607360107630451228454921845984604608225359676243382741906" +
"0089029417044871218316020923109988915707117567");
/**
* 8192-bit MODP Group - P18 (https://tools.ietf.org/html/rfc3526#section-7)
* prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
*/
public static final BigInteger P18 =
new BigInteger("10907481356194159294502949293597845003481551249531722117741011069661501689227856390" +
"28532473848836817769712164169076432969224698752674677662739994265785437233596157045" +
"97092233804069810050786103304731233182398243527947570019986097161273254052879655450" +
"28679197467769837593914759871425213158787195775191488118308799194269399584870875409" +
"65716419167467499326156226529675209172277001377591248147563782880558861083327174154" +
"01497513489312511601577631889029596069801161415772128252753946881651931933333750311" +
"47771923604122817210189558343776154804684792527488673203623853555966017951228067562" +
"17713579819870634321561907813255153703950795271232652404894983869492174481652303803" +
"49888136621050864726366837651413103110233683748899977574404673365182723939535354034" +
"84148728546397192946943234501868841898225445406472269872921606931847346549419069366" +
"46576130260972193280317171696418971553954161446191759093719524951116705577362073481" +
"31929604120128351615426904438925772770028968411946028348045230620413002491387998113" +
"59080269838682059693181678196808509986496944169079527129049624049377757896989172073" +
"56355227455066183815847669135530549755439819480321732925869069136146085326382334628" +
"74545639807160305805163420938670870330654590319960852382451372962513665912822110096" +
"77354505199524042481982628138310973742616503800172779169753241348465746813073370173" +
"80830353680623216336949471306191686438249305686413380231046096450953594089375540285" +
"03729247092939511402830554745258496207430943815182543790297601289174935519867842060" +
"37220349003113648930464957614043339386861400378480309162925432736845336400326376391" +
"00774502371542479302473698388692892420946478947733800387782741417786484770190108867" +
"87977899163321862864053398261932246615488301145229189025233648723608665439609385389" +
"86288058131775591620763631544364944775078712941198416378677017221666098312018454840" +
"78070518041336869808398454625586921201308185638888082699408686536045192649569198110" +
"35365994311180230063610650986502394366182943642656300791728205089442938884174888539" +
"82907077430529736053592775157496197308237732158947551217614678878653277071155738042" +
"64519206349215850195195364813387526811742474131549802130246506341207020335797706780" +
"70540694527543880626597851620970679570257924407538049023174103086261496878330620786" +
"96878681084236399719832090776247580804999882755913927872676271824428928096468742282" +
"63172435642368588260139161962836121481966092745325488641054238839295138992979335446" +
"110090325230955276870524611359124918392740353154294858383359");
}

View File

@@ -15,7 +15,9 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.random.Random;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
@@ -39,7 +41,7 @@ public class ECDH extends DHBase {
super("EC", "ECDH");
}
protected void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
generator.initialize(params);
KeyPair keyPair = generator.generateKeyPair();
agreement.init(keyPair.getPrivate());

View File

@@ -79,7 +79,7 @@ public class ECDHNistP extends AbstractDHG {
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new ECNamedCurveGenParameterSpec(curve));
dh.init(new ECNamedCurveGenParameterSpec(curve), trans.getConfig().getRandomFactory());
}
}

View File

@@ -17,6 +17,8 @@ package net.schmizz.sshj.transport.random;
import org.bouncycastle.crypto.prng.RandomGenerator;
import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.SecureRandom;
@@ -27,6 +29,8 @@ import java.security.SecureRandom;
public class BouncyCastleRandom
implements Random {
private static final Logger logger = LoggerFactory.getLogger(BouncyCastleRandom.class);
/** Named factory for the BouncyCastle <code>Random</code> */
public static class Factory
implements net.schmizz.sshj.common.Factory<Random> {
@@ -41,7 +45,10 @@ public class BouncyCastleRandom
private final RandomGenerator random = new VMPCRandomGenerator();
public BouncyCastleRandom() {
logger.info("Generating random seed from SecureRandom.");
long t = System.currentTimeMillis();
byte[] seed = new SecureRandom().generateSeed(8);
logger.debug("Creating random seed took {} ms", System.currentTimeMillis() - t);
random.addSeedMaterial(seed);
}
@@ -50,4 +57,9 @@ public class BouncyCastleRandom
random.nextBytes(bytes, start, len);
}
@Override
public void fill(byte[] bytes) {
random.nextBytes(bytes);
}
}

View File

@@ -16,10 +16,13 @@
package net.schmizz.sshj.transport.random;
import java.security.SecureRandom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** A {@link Random} implementation using the built-in {@link SecureRandom} PRNG. */
public class JCERandom
implements Random {
private static final Logger logger = LoggerFactory.getLogger(JCERandom.class);
/** Named factory for the JCE {@link Random} */
public static class Factory
@@ -38,7 +41,14 @@ public class JCERandom
}
private byte[] tmp = new byte[16];
private final SecureRandom random = new SecureRandom();
private final SecureRandom random;
JCERandom() {
logger.info("Creating new SecureRandom.");
long t = System.currentTimeMillis();
random = new SecureRandom();
logger.debug("Random creation took {} ms", System.currentTimeMillis() - t);
}
/**
* Fill the given byte-array with random bytes from this PRNG.
@@ -49,15 +59,20 @@ public class JCERandom
*/
@Override
public synchronized void fill(byte[] foo, int start, int len) {
if (start == 0 && len == foo.length)
if (start == 0 && len == foo.length) {
random.nextBytes(foo);
else
} else {
synchronized (this) {
if (len > tmp.length)
tmp = new byte[len];
random.nextBytes(tmp);
System.arraycopy(tmp, 0, foo, start, len);
}
}
}
@Override
public void fill(final byte[] bytes) {
random.nextBytes(bytes);
}
}

View File

@@ -18,6 +18,13 @@ package net.schmizz.sshj.transport.random;
/** A pseudo random number generator. */
public interface Random {
/**
* Fill the array of bytes with random values.
*
* @param bytes byte array to be filled.
*/
void fill(byte[] bytes);
/**
* Fill part of bytes with random values.
*

View File

@@ -37,4 +37,8 @@ public class SingletonRandomFactory
random.fill(bytes, start, len);
}
@Override
public void fill(final byte[] bytes) {
random.fill(bytes);
}
}

View File

@@ -32,7 +32,9 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/** {@link UserAuth} implementation. */
/**
* {@link UserAuth} implementation.
*/
public class UserAuthImpl
extends AbstractService
implements UserAuth {
@@ -111,22 +113,20 @@ public class UserAuthImpl
try {
switch (msg) {
case USERAUTH_BANNER: {
case USERAUTH_BANNER:
banner = buf.readString();
}
break;
break;
case USERAUTH_SUCCESS: {
case USERAUTH_SUCCESS:
// In order to prevent race conditions, we immediately set the authenticated flag on the transport
// And change the service before delivering the authenticated promise.
// Should fix https://github.com/hierynomus/sshj/issues/237
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
trans.setService(nextService); // We aren't in charge anymore, next service is
authenticated.deliver(true);
}
break;
break;
case USERAUTH_FAILURE: {
case USERAUTH_FAILURE:
allowedMethods = Arrays.asList(buf.readString().split(","));
partialSuccess |= buf.readBoolean();
if (allowedMethods.contains(currentMethod.getName()) && currentMethod.shouldRetry()) {
@@ -134,18 +134,16 @@ public class UserAuthImpl
} else {
authenticated.deliver(false);
}
}
break;
break;
default: {
default:
log.debug("Asking `{}` method to handle {} packet", currentMethod.getName(), msg);
try {
currentMethod.handle(msg, buf);
} catch (UserAuthException e) {
authenticated.deliverError(e);
}
}
break;
}
} finally {
authenticated.unlock();

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.userauth.keyprovider;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.password.*;
public abstract class BaseFileKeyProvider implements FileKeyProvider {
protected Resource<?> resource;
protected PasswordFinder pwdf;
protected KeyPair kp;
protected KeyType type;
@Override
public void init(Reader location) {
assert location != null;
resource = new PrivateKeyReaderResource(location);
}
@Override
public void init(Reader location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(File location) {
assert location != null;
resource = new PrivateKeyFileResource(location.getAbsoluteFile());
}
@Override
public void init(File location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(String privateKey, String publicKey) {
assert privateKey != null;
assert publicKey == null;
resource = new PrivateKeyStringResource(privateKey);
}
@Override
public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
init(privateKey, publicKey);
this.pwdf = pwdf;
}
@Override
public PrivateKey getPrivate()
throws IOException {
return kp != null ? kp.getPrivate() : (kp = readKeyPair()).getPrivate();
}
@Override
public PublicKey getPublic()
throws IOException {
return kp != null ? kp.getPublic() : (kp = readKeyPair()).getPublic();
}
@Override
public KeyType getType()
throws IOException {
return type != null ? type : (type = KeyType.fromKey(getPublic()));
}
protected abstract KeyPair readKeyPair() throws IOException;
}

View File

@@ -22,6 +22,7 @@ public enum KeyFormat {
PKCS5,
PKCS8,
OpenSSH,
OpenSSHv1,
PuTTY,
Unknown
}

View File

@@ -18,6 +18,7 @@ package net.schmizz.sshj.userauth.keyprovider;
import net.schmizz.sshj.common.IOUtils;
import java.io.*;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
public class KeyProviderUtil {
@@ -88,12 +89,14 @@ public class KeyProviderUtil {
private static KeyFormat keyFormatFromHeader(String header, boolean separatePubKey) {
if (header.startsWith("-----BEGIN") && header.endsWith("PRIVATE KEY-----")) {
if (separatePubKey) {
if (separatePubKey && header.contains(OpenSSHKeyV1KeyFile.OPENSSH_PRIVATE_KEY)) {
return KeyFormat.OpenSSHv1;
} else if (separatePubKey) {
// Can delay asking for password since have unencrypted pubkey
return KeyFormat.OpenSSH;
} else if (header.indexOf("BEGIN PRIVATE KEY") != -1 || header.indexOf("BEGIN ENCRYPTED PRIVATE KEY") != -1) {
} else if (header.contains("BEGIN PRIVATE KEY") || header.contains("BEGIN ENCRYPTED PRIVATE KEY")) {
return KeyFormat.PKCS8;
} else {
} else {
return KeyFormat.PKCS5;
}
} else if (header.startsWith("PuTTY-User-Key-File-")) {

View File

@@ -15,28 +15,28 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.transport.cipher.*;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.MD5;
import net.schmizz.sshj.userauth.password.*;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.*;
import java.security.spec.*;
import java.util.Arrays;
import javax.xml.bind.DatatypeConverter;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.transport.cipher.*;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.MD5;
/**
* Represents a PKCS5-encoded key file. This is the format typically used by OpenSSH, OpenSSL, Amazon, etc.
*/
public class PKCS5KeyFile
implements FileKeyProvider {
public class PKCS5KeyFile extends BaseFileKeyProvider {
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@@ -74,67 +74,8 @@ public class PKCS5KeyFile
}
}
protected PasswordFinder pwdf;
protected Resource<?> resource;
protected KeyPair kp;
protected KeyType type;
protected byte[] data;
@Override
public PrivateKey getPrivate()
throws IOException {
return kp != null ? kp.getPrivate() : (kp = readKeyPair()).getPrivate();
}
@Override
public PublicKey getPublic()
throws IOException {
return kp != null ? kp.getPublic() : (kp = readKeyPair()).getPublic();
}
@Override
public KeyType getType()
throws IOException {
return type != null ? type : (type = KeyType.fromKey(getPublic()));
}
@Override
public void init(Reader location) {
assert location != null;
resource = new PrivateKeyReaderResource(location);
}
@Override
public void init(Reader location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(File location) {
assert location != null;
resource = new PrivateKeyFileResource(location.getAbsoluteFile());
}
@Override
public void init(File location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(String privateKey, String publicKey) {
assert privateKey != null;
assert publicKey == null;
resource = new PrivateKeyStringResource(privateKey);
}
@Override
public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
init(privateKey, publicKey);
this.pwdf = pwdf;
}
protected KeyPair readKeyPair()
throws IOException {

View File

@@ -15,9 +15,8 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.password.*;
import java.io.IOException;
import java.security.KeyPair;
import org.bouncycastle.openssl.EncryptionException;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
@@ -27,16 +26,11 @@ import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.userauth.password.PasswordUtils;
/** Represents a PKCS8-encoded key file. This is the format used by OpenSSH and OpenSSL. */
public class PKCS8KeyFile
implements FileKeyProvider {
/** Represents a PKCS8-encoded key file. This is the format used by (old-style) OpenSSH and OpenSSL. */
public class PKCS8KeyFile extends BaseFileKeyProvider {
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@@ -53,68 +47,9 @@ public class PKCS8KeyFile
}
protected final Logger log = LoggerFactory.getLogger(getClass());
protected PasswordFinder pwdf;
protected Resource<?> resource;
protected KeyPair kp;
protected KeyType type;
protected char[] passphrase; // for blanking out
@Override
public PrivateKey getPrivate()
throws IOException {
return kp != null ? kp.getPrivate() : (kp = readKeyPair()).getPrivate();
}
@Override
public PublicKey getPublic()
throws IOException {
return kp != null ? kp.getPublic() : (kp = readKeyPair()).getPublic();
}
@Override
public KeyType getType()
throws IOException {
return type != null ? type : (type = KeyType.fromKey(getPublic()));
}
@Override
public void init(Reader location) {
assert location != null;
resource = new PrivateKeyReaderResource(location);
}
@Override
public void init(Reader location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(File location) {
assert location != null;
resource = new PrivateKeyFileResource(location.getAbsoluteFile());
}
@Override
public void init(File location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(String privateKey, String publicKey) {
assert privateKey != null;
assert publicKey == null;
resource = new PrivateKeyStringResource(privateKey);
}
@Override
public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
init(privateKey, publicKey);
this.pwdf = pwdf;
}
protected KeyPair readKeyPair()
throws IOException {

View File

@@ -15,21 +15,21 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.password.*;
import org.bouncycastle.util.encoders.Hex;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Hex;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.password.PasswordUtils;
/**
* <h2>Sample PuTTY file format</h2>
@@ -56,7 +56,7 @@ import java.util.Map;
*
* @version $Id:$
*/
public class PuTTYKeyFile implements FileKeyProvider {
public class PuTTYKeyFile extends BaseFileKeyProvider {
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@@ -75,56 +75,6 @@ public class PuTTYKeyFile implements FileKeyProvider {
private byte[] privateKey;
private byte[] publicKey;
private KeyPair kp;
protected PasswordFinder pwdf;
protected Resource<?> resource;
@Override
public void init(Reader location) {
this.resource = new PrivateKeyReaderResource(location);
}
public void init(Reader location, PasswordFinder pwdf) {
this.init(location);
this.pwdf = pwdf;
}
@Override
public void init(File location) {
resource = new PrivateKeyFileResource(location.getAbsoluteFile());
}
@Override
public void init(File location, PasswordFinder pwdf) {
this.init(location);
this.pwdf = pwdf;
}
@Override
public void init(String privateKey, String publicKey) {
resource = new PrivateKeyStringResource(privateKey);
}
@Override
public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
init(privateKey, publicKey);
this.pwdf = pwdf;
}
@Override
public PrivateKey getPrivate()
throws IOException {
return kp != null ? kp.getPrivate() : (kp = this.readKeyPair()).getPrivate();
}
@Override
public PublicKey getPublic()
throws IOException {
return kp != null ? kp.getPublic() : (kp = this.readKeyPair()).getPublic();
}
/**
* Key type. Either "ssh-rsa" for RSA key, or "ssh-dss" for DSA key.
*/
@@ -150,7 +100,7 @@ public class PuTTYKeyFile implements FileKeyProvider {
protected KeyPair readKeyPair() throws IOException {
this.parseKeyPair();
if(KeyType.RSA.equals(this.getType())) {
if (KeyType.RSA.equals(this.getType())) {
final KeyReader publicKeyReader = new KeyReader(publicKey);
publicKeyReader.skip(); // skip this
// public key exponent
@@ -165,8 +115,7 @@ public class PuTTYKeyFile implements FileKeyProvider {
final KeyFactory factory;
try {
factory = KeyFactory.getInstance("RSA");
}
catch(NoSuchAlgorithmException s) {
} catch (NoSuchAlgorithmException s) {
throw new IOException(s.getMessage(), s);
}
try {
@@ -174,12 +123,11 @@ public class PuTTYKeyFile implements FileKeyProvider {
factory.generatePublic(new RSAPublicKeySpec(n, e)),
factory.generatePrivate(new RSAPrivateKeySpec(n, d))
);
}
catch(InvalidKeySpecException i) {
} catch (InvalidKeySpecException i) {
throw new IOException(i.getMessage(), i);
}
}
if(KeyType.DSA.equals(this.getType())) {
if (KeyType.DSA.equals(this.getType())) {
final KeyReader publicKeyReader = new KeyReader(publicKey);
publicKeyReader.skip(); // skip this
BigInteger p = publicKeyReader.readInt();
@@ -194,8 +142,7 @@ public class PuTTYKeyFile implements FileKeyProvider {
final KeyFactory factory;
try {
factory = KeyFactory.getInstance("DSA");
}
catch(NoSuchAlgorithmException s) {
} catch (NoSuchAlgorithmException s) {
throw new IOException(s.getMessage(), s);
}
try {
@@ -203,12 +150,10 @@ public class PuTTYKeyFile implements FileKeyProvider {
factory.generatePublic(new DSAPublicKeySpec(y, p, q, g)),
factory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g))
);
}
catch(InvalidKeySpecException e) {
} catch (InvalidKeySpecException e) {
throw new IOException(e.getMessage(), e);
}
}
else {
} else {
throw new IOException(String.format("Unknown key type %s", this.getType()));
}
}
@@ -219,18 +164,16 @@ public class PuTTYKeyFile implements FileKeyProvider {
try {
String headerName = null;
String line;
while((line = r.readLine()) != null) {
while ((line = r.readLine()) != null) {
int idx = line.indexOf(": ");
if(idx > 0) {
if (idx > 0) {
headerName = line.substring(0, idx);
headers.put(headerName, line.substring(idx + 2));
}
else {
} else {
String s = payload.get(headerName);
if(s == null) {
if (s == null) {
s = line;
}
else {
} else {
// Append to previous line
s += line;
}
@@ -238,29 +181,25 @@ public class PuTTYKeyFile implements FileKeyProvider {
payload.put(headerName, s);
}
}
}
finally {
} finally {
r.close();
}
// Retrieve keys from payload
publicKey = Base64.decode(payload.get("Public-Lines"));
if(this.isEncrypted()) {
if (this.isEncrypted()) {
final char[] passphrase;
if(pwdf != null) {
if (pwdf != null) {
passphrase = pwdf.reqPassword(resource);
}
else {
} else {
passphrase = "".toCharArray();
}
try {
privateKey = this.decrypt(Base64.decode(payload.get("Private-Lines")), new String(passphrase));
this.verify(new String(passphrase));
}
finally {
} finally {
PasswordUtils.blankOut(passphrase);
}
}
else {
} else {
privateKey = Base64.decode(payload.get("Private-Lines"));
}
}
@@ -292,8 +231,7 @@ public class PuTTYKeyFile implements FileKeyProvider {
System.arraycopy(key2, 0, r, 20, 12);
return r;
}
catch(NoSuchAlgorithmException e) {
} catch (NoSuchAlgorithmException e) {
throw new IOException(e.getMessage(), e);
}
}
@@ -306,7 +244,7 @@ public class PuTTYKeyFile implements FileKeyProvider {
// The key to the MAC is itself a SHA-1 hash of:
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update("putty-private-key-file-mac-key".getBytes());
if(passphrase != null) {
if (passphrase != null) {
digest.update(passphrase.getBytes());
}
final byte[] key = digest.digest();
@@ -334,11 +272,10 @@ public class PuTTYKeyFile implements FileKeyProvider {
final String encoded = Hex.toHexString(mac.doFinal(out.toByteArray()));
final String reference = headers.get("Private-MAC");
if(!encoded.equals(reference)) {
if (!encoded.equals(reference)) {
throw new IOException("Invalid passphrase");
}
}
catch(GeneralSecurityException e) {
} catch (GeneralSecurityException e) {
throw new IOException(e.getMessage(), e);
}
}
@@ -355,8 +292,7 @@ public class PuTTYKeyFile implements FileKeyProvider {
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(expanded, 0, 32, "AES"),
new IvParameterSpec(new byte[16])); // initial vector=0
return cipher.doFinal(key);
}
catch(GeneralSecurityException e) {
} catch (GeneralSecurityException e) {
throw new IOException(e.getMessage(), e);
}
}
@@ -377,14 +313,14 @@ public class PuTTYKeyFile implements FileKeyProvider {
*/
public void skip() throws IOException {
final int read = di.readInt();
if(read != di.skipBytes(read)) {
if (read != di.skipBytes(read)) {
throw new IOException(String.format("Failed to skip %d bytes", read));
}
}
private byte[] read() throws IOException {
int len = di.readInt();
if(len <= 0 || len > 513) {
if (len <= 0 || len > 513) {
throw new IOException(String.format("Invalid length %d", len));
}
byte[] r = new byte[len];

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.connection.channel.direct
import com.hierynomus.sshj.test.SshFixture
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder
import org.junit.Rule
import spock.lang.Specification
class LocalPortForwarderSpec extends Specification {
@Rule
SshFixture tunnelFixture = new SshFixture()
@Rule
SshFixture realServer = new SshFixture()
def "should not hang when disconnect tunnel"() {
given:
def client = tunnelFixture.setupConnectedDefaultClient()
client.authPassword("test", "test")
def socket = new ServerSocket(0)
def lpf = client.newLocalPortForwarder(new LocalPortForwarder.Parameters("localhost", socket.getLocalPort(), "localhost", realServer.server.port), socket)
def thread = new Thread(new Runnable() {
@Override
void run() {
lpf.listen()
}
})
when:
thread.start()
then:
thread.isAlive()
when:
lpf.close()
then:
socket.isClosed()
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.userauth.keyprovider
import com.hierynomus.sshj.test.SshFixture
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.userauth.keyprovider.KeyFormat
import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator
import org.junit.Rule
import spock.lang.Specification
import spock.lang.Unroll
class FileKeyProviderSpec extends Specification {
@Rule
SshFixture fixture = new SshFixture(false)
def setup() {
fixture.getServer().setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE)
fixture.start()
}
def cleanup() {
fixture.stopServer()
}
@Unroll
def "should have #format FileKeyProvider enabled by default"() {
given:
SSHClient client = fixture.setupConnectedDefaultClient()
when:
client.authPublickey("jeroen", keyfile)
then:
client.isAuthenticated()
cleanup:
client.disconnect()
where:
format | keyfile
KeyFormat.PKCS5 | "src/test/resources/keyformats/pkcs5"
KeyFormat.OpenSSH | "src/test/resources/keyformats/openssh"
}
}

View File

@@ -15,14 +15,14 @@
*/
package com.hierynomus.sshj;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import java.io.File;
import java.io.IOException;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -32,8 +32,8 @@ public class IntegrationTest {
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");
sshClient.connect("172.16.37.147");
sshClient.authPublickey("jeroen");
assertThat("Is connected", sshClient.isAuthenticated());
}
}

View File

@@ -30,6 +30,8 @@ import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
@@ -39,11 +41,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
public class RemotePortForwarderTest {
// Credentials for an remote SSH Server to test against.
private static final String REMOTE_HOST = "x.x.x.x";
private static final String USER = "xxxx";
private static final String PASSWORD = "yyyy";
private static final Logger log = LoggerFactory.getLogger(RemotePortForwarderTest.class);
private static final PortRange RANGE = new PortRange(9000, 9999);
private static final InetSocketAddress HTTP_SERVER_SOCKET_ADDR = new InetSocketAddress("127.0.0.1", 8080);
@@ -55,7 +53,7 @@ public class RemotePortForwarderTest {
public HttpServer httpServer = new HttpServer();
@Before
public void setup() throws IOException {
public void setUp() throws IOException {
fixture.getServer().setTcpipForwardingFilter(new AcceptAllForwardingFilter());
File file = httpServer.getDocRoot().newFile("index.html");
FileUtil.writeToFile(file, "<html><head/><body><h1>Hi!</h1></body></html>");
@@ -64,49 +62,49 @@ public class RemotePortForwarderTest {
@Test
public void shouldHaveWorkingHttpServer() throws IOException {
// Just to check that we have a working http server...
httpGet("127.0.0.1", 8080);
assertThat(httpGet("127.0.0.1", 8080), equalTo(200));
}
@Test
public void shouldDynamicallyForwardPortForLocalhost() throws IOException {
SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", new SinglePort(0));
httpGet("127.0.0.1", bind.getPort());
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
}
@Test
public void shouldDynamicallyForwardPortForAllIPv4() throws IOException {
SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "0.0.0.0", new SinglePort(0));
httpGet("127.0.0.1", bind.getPort());
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
}
@Test
public void shouldDynamicallyForwardPortForAllProtocols() throws IOException {
SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "", new SinglePort(0));
httpGet("127.0.0.1", bind.getPort());
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
}
@Test
public void shouldForwardPortForLocalhost() throws IOException {
SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", RANGE);
httpGet("127.0.0.1", bind.getPort());
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
}
@Test
public void shouldForwardPortForAllIPv4() throws IOException {
SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "0.0.0.0", RANGE);
httpGet("127.0.0.1", bind.getPort());
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
}
@Test
public void shouldForwardPortForAllProtocols() throws IOException {
SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "", RANGE);
httpGet("127.0.0.1", bind.getPort());
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
}
private RemotePortForwarder.Forward forwardPort(SSHClient sshClient, String address, PortRange portRange) throws IOException {
@@ -127,12 +125,12 @@ public class RemotePortForwarderTest {
}
}
private void httpGet(String server, int port) throws IOException {
private int httpGet(String server, int port) throws IOException {
HttpClient client = HttpClientBuilder.create().build();
String urlString = "http://" + server + ":" + port;
System.out.println("Trying: GET " + urlString);
log.info("Trying: GET " + urlString);
HttpResponse execute = client.execute(new HttpGet(urlString));
assertThat(execute.getStatusLine().getStatusCode(), equalTo(200));
return execute.getStatusLine().getStatusCode();
}
private SSHClient getFixtureClient() throws IOException {

View File

@@ -61,8 +61,7 @@ public class KeepAliveThreadTerminationTest {
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");
fail("Found alive keep-alive thread in state " + threadInfo.getThreadState());
}
}
}

View File

@@ -42,25 +42,17 @@ public abstract class BaseAlgorithmTest {
@Test
public void shouldVerifyAlgorithm() throws IOException {
attempt(100);
}
private void attempt(int times) throws IOException {
for (int i = 0; i < times; i++) {
for (int i = 0; i < 100; 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();
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();
fixture.stopClient();
}
}
protected abstract Config getClientConfig(DefaultConfig defaultConfig);

View File

@@ -27,9 +27,6 @@ public class HttpServer extends ExternalResource {
private TemporaryFolder docRoot = new TemporaryFolder();
public HttpServer() {
}
@Override
protected void before() throws Throwable {
docRoot.create();

View File

@@ -69,7 +69,7 @@ public class AuthPasswordTest {
fixture.getServer().setPasswordAuthenticator(new PasswordAuthenticator() {
@Override
public boolean authenticate(String username, String password, ServerSession session) {
if (password.equals("changeme")) {
if ("changeme".equals(password)) {
throw new PasswordChangeRequiredException("Password was changeme", "Please provide your updated password", "en_US");
} else {
return password.equals(username);
@@ -84,6 +84,7 @@ public class AuthPasswordTest {
SSHClient sshClient = fixture.setupConnectedDefaultClient();
expectedException.expect(UserAuthException.class);
sshClient.authPassword("jeroen", "changeme");
assertThat("Should not have authenticated", !sshClient.isAuthenticated());
}
@Test

View File

@@ -18,6 +18,7 @@ package net.schmizz.sshj.keyprovider;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.Resource;
@@ -30,11 +31,13 @@ import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Scanner;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -142,7 +145,7 @@ public class OpenSSHKeyFileTest {
@Test
public void shouldHaveCorrectFingerprintForED25519() throws IOException {
OpenSSHKeyFile keyFile = new OpenSSHKeyFile();
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
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();
@@ -150,6 +153,14 @@ public class OpenSSHKeyFileTest {
assertThat(expected, containsString(sshjFingerprintSshjKey));
}
@Test
public void shouldLoadED25519PrivateKey() throws IOException {
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
keyFile.init(new File("src/test/resources/keytypes/test_ed25519"));
PrivateKey aPrivate = keyFile.getPrivate();
assertThat(aPrivate.getAlgorithm(), equalTo("EdDSA"));
}
@Before
public void setup()
throws UnsupportedEncodingException, GeneralSecurityException {
@@ -171,4 +182,4 @@ public class OpenSSHKeyFileTest {
scanner.close();
}
}
}
}

View File

@@ -33,8 +33,6 @@ public class PacketReaderTest {
private DataOutputStream dataout;
private PacketReader reader;
private SFTPEngine engine;
private Subsystem subsystem;
@Before
public void setUp() throws Exception {
@@ -42,8 +40,8 @@ public class PacketReaderTest {
PipedInputStream pipedin = new PipedInputStream(pipedout);
dataout = new DataOutputStream(pipedout);
engine = Mockito.mock(SFTPEngine.class);
subsystem = Mockito.mock(Subsystem.class);
SFTPEngine engine = Mockito.mock(SFTPEngine.class);
Subsystem subsystem = Mockito.mock(Subsystem.class);
Mockito.when(engine.getLoggerFactory()).thenReturn(LoggerFactory.DEFAULT);
Mockito.when(engine.getSubsystem()).thenReturn(subsystem);
Mockito.when(subsystem.getInputStream()).thenReturn(pipedin);

View File

@@ -30,9 +30,9 @@ public class PathHelperTest {
@Override
public String canonicalize(String path)
throws IOException {
if (path.equals("") || path.equals(".") || path.equals("./"))
if ("".equals(path) || ".".equals(path) || "./".equals(path))
return "/home/me";
if (path.equals("..") || path.equals("../"))
if ("..".equals(path) || "../".equals(path))
return "/home";
return path;
}

View File

@@ -70,7 +70,9 @@ public class BogusGSSContext
}
@Override
public void dispose() throws GSSException {}
public void dispose() throws GSSException {
// Nothing to do
}
@Override
public int getWrapSizeLimit(int qop, boolean confReq, int maxTokenSize) throws GSSException {

View File

@@ -34,7 +34,9 @@ public class BogusGSSCredential
}
@Override
public void dispose() throws GSSException {}
public void dispose() throws GSSException {
// Nothing to do
}
@Override
public GSSName getName() throws GSSException {

View File

@@ -0,0 +1,17 @@
#
# Copyright (C)2009 - SSHJ Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
sshj.version=0.18.0