Compare commits

..

52 Commits

Author SHA1 Message Date
Shikhar Bhushan
be11cbb848 [maven-release-plugin] prepare release v0.6.0 2011-09-26 12:41:17 +01:00
Shikhar Bhushan
43b0599e1f let's make next release sshj 0.6.0 2011-09-26 12:37:21 +01:00
Shikhar Bhushan
b218186cae Merge pull request #34 from hierynomus/scp-filter-copy
Added upload filter capability to SCPUploadClient
2011-09-26 04:11:24 -07:00
hierynomus
184236c3d5 Added upload filter capability to SCPUploadClient 2011-09-26 11:49:07 +02:00
Shikhar Bhushan
cb1d773659 Merge branch 'master' of github.com:shikhar/sshj 2011-09-14 23:06:05 +01:00
Shikhar Bhushan
378665cb46 update contrib 2011-09-14 23:37:31 +02:00
Shikhar Bhushan
a5272dc413 Merge branch 'master' of github.com:shikhar/sshj 2011-09-14 22:11:29 +01:00
Shikhar Bhushan
60552fd001 Merge pull request #31 from hierynomus/filemode-typemask
Fixed bug in SFTP FileMode for AIX/Unix directory mask
2011-09-14 11:43:59 -07:00
hierynomus
ef082c668a Fixed bug in SFTP FileMode for AIX/Unix directory mask 2011-09-14 11:02:35 +02:00
Shikhar Bhushan
e66386eb1c Local window exhaustion -> ConnectionException 2011-09-07 21:45:44 +01:00
Shikhar Bhushan
0937ec9800 minor 2011-09-07 21:24:49 +01:00
Shikhar Bhushan
4b2f42804e Added version in pom.xml for maven-bundle-plugin 2011-08-29 16:49:49 +01:00
Shikhar Bhushan
01765d24d2 AbstractChannel - no reason for channel request response events to share the same underlying lock object. #27 2011-08-29 16:45:12 +01:00
Shikhar Bhushan
1a2351c5ee AbstractChannel - make chanReqResponseEvents a ConcurrentLinkedQueue, so don't have to have sendChannelRequest() & gotResponse() as synchronized methods. #27 2011-08-29 16:41:27 +01:00
Shikhar Bhushan
1cec011401 chown / chmod / chgrp in StatefulSFTPClient -- these all delegate to setattr which is overriden in StatefulSFTPClient to do cwdification. #28 2011-08-29 16:21:10 +01:00
Shikhar Bhushan
52338c13cb Merge pull request #28 from neilprosser/master
StatefulSFTPClient doesn't cwdify all commands
2011-08-27 04:23:07 -07:00
Neil Prosser
09cf21f61a Some stateful methods that needed cwdifying 2011-08-27 00:30:20 +01:00
Shikhar Bhushan
04c2e7b6b8 Client version string update 2011-07-27 19:52:27 +01:00
Shikhar Bhushan
822f196dd8 update contrib 2011-07-27 19:38:56 +01:00
Shikhar Bhushan
a88a574b10 [maven-release-plugin] prepare for next development iteration 2011-07-27 19:33:32 +01:00
Shikhar Bhushan
5cd6986355 [maven-release-plugin] prepare release v0.5.0 2011-07-27 19:33:26 +01:00
Shikhar Bhushan
b5d206bbcb Merge pull request #22 from iocanel/master
OSGi bundle for sshj
2011-07-25 12:22:43 -07:00
Ioannis Canellos
4eae26c551 OSGi-fied sshj 2011-07-25 06:29:20 -07:00
Shikhar Bhushan
b950f88f52 SSHClient implements Closeable 2011-07-24 20:50:01 +01:00
Shikhar Bhushan
3267860db4 mass auto-format 2011-07-24 20:48:00 +01:00
Shikhar Bhushan
d6eb5a040e Non-final ChannelOutputStream field may be null if we haven't received channel open confirmation or channel open failed. So do null-check. 2011-07-16 10:19:46 +01:00
Shikhar Bhushan
21da5b9f65 update contributor list 2011-07-03 14:50:43 -07:00
Shikhar Bhushan
6b66a952d4 Session#changeWindowDimensions was broken, wrong channel request.
Fix for #21
2011-07-03 10:59:40 +01:00
Shikhar Bhushan
aa4faf3f25 Merge pull request #20 from cloudera/forUpstream
Don't leak PrivateKeyStringResource private keys via toString()
2011-06-23 13:46:37 -07:00
Adar Dembo
4be02450dd Don't leak PrivateKeyStringResource private keys via toString() 2011-06-22 17:52:46 -07:00
Shikhar Bhushan
0cec27c28e Edited README.rst via GitHub 2011-06-20 11:44:56 -07:00
Shikhar Bhushan
4384367a1b point to issue tracker 2011-06-20 11:43:21 -07:00
Shikhar Bhushan
4549648a76 [maven-release-plugin] prepare for next development iteration 2011-06-19 18:32:36 +01:00
Shikhar Bhushan
20e2161022 [maven-release-plugin] prepare release v0.4.1 2011-06-19 18:32:30 +01:00
Shikhar Bhushan
fb0f3afa17 fix javadoc warnings 2011-06-19 18:30:52 +01:00
Shikhar Bhushan
114c2bb424 specify missing maven plugin versions & upgrade others 2011-06-19 18:25:04 +01:00
Shikhar Bhushan
079bde5dbf update dependencies in pom 2011-06-19 18:17:06 +01:00
Shikhar Bhushan
eaee42b017 Fix for GH-18
Need to explicitly tell the BigInteger constructor that K (generated secret) is a positive number.
2011-06-19 17:53:56 +01:00
Shikhar Bhushan
8b61d96808 changed some things around, lesser conversions / copying. still not found the bug. grr. 2011-06-19 17:52:00 +01:00
Shikhar Bhushan
73fcc81e83 Unused methods 2011-06-18 20:14:44 +01:00
Shikhar Bhushan
0f7926d4fa . 2011-06-18 14:45:28 +01:00
Shikhar Bhushan
ca6f15650a Useless indirection 2011-06-18 14:44:58 +01:00
Shikhar Bhushan
eb78dc499d . 2011-06-18 13:53:18 +01:00
Shikhar Bhushan
a852f33a15 way to reproduce GH-18 2011-06-18 13:52:03 +01:00
Shikhar Bhushan
ccabc1a20c vestige of eclipse formatting 2011-06-18 13:48:25 +01:00
Shikhar Bhushan
cb2986d32e Update client version string 2011-06-18 13:48:10 +01:00
Shikhar Bhushan
dc70f08e45 Update contributor list 2011-06-08 20:06:23 +01:00
Shikhar Bhushan
bf68ec18b2 Merge pull request #16 from cloudera/forUpstream
Adding support for public key authentication from strings
2011-06-08 11:45:32 -07:00
Philip Langdale
7e78260ca9 Add unit test for String originated key files. 2011-06-07 15:55:44 -07:00
Philip Langdale
27c60cee60 Add support for public key authentication with keys as strings.
Currently, only keys as file locations are supported. This change
adds support for keys as strings.

Significant changes are:

1) Introduction of a new Resource type for keys as strings.

2) Initialization of a key provider with two strings (private and public keys)
   Leaving the public key null is equivalent to not having a .pub file.

3) Obtaining the reader for the resource is refactored into the resource itself
   to avoid requiring knowledge of the type outside the resource.

The loadKeys and authPublickey convenience methods are not duplicated for
the string based loading as we currently don't need them but they could be
if desired (although method signature collisions will be a problem).
2011-06-07 15:55:39 -07:00
Shikhar Bhushan
551b8b4fcf Add forked-path to maven-release-plugin config to allow stdin password input 2011-06-06 20:55:12 +01:00
Shikhar Bhushan
fd591e70be [maven-release-plugin] prepare for next development iteration 2011-06-06 20:44:39 +01:00
90 changed files with 769 additions and 380 deletions

View File

@@ -1,3 +1,8 @@
Shikhar Bhushan <shikhar@schmizz.net>
Cyril Ledru <cledru@keynectis.net>
Incendium <incendium@gmail.com>
Incendium <incendium@gmail.com>
Philip Langdale <philipl@cloudera.com>
Adar Dembo <adar@cloudera.com>
Ioannis Canellos <iocanel@gmail.com>
Neil Prosser <neil.prosser@gmail.com>
hierynomus <jeroen@hierynomus.com>

View File

@@ -39,6 +39,11 @@ Dependencies
Java 6+. slf4j_ is required. bouncycastle_ is highly recommended and required for using some of the crypto algorithms. jzlib_ is required for using zlib compression.
Bugs, questions
----------------
`Issue tracker <https://github.com/shikhar/sshj/issues>`_
Contributing
------------

51
pom.xml
View File

@@ -5,8 +5,8 @@
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<packaging>jar</packaging>
<version>0.4.0</version>
<packaging>bundle</packaging>
<version>0.6.0</version>
<name>sshj</name>
<description>SSHv2 library for Java</description>
@@ -42,13 +42,13 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.45</version>
<version>1.46</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -60,21 +60,27 @@
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.4.0</version>
<version>0.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.24</version>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.24</version>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0-rc1</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -96,6 +102,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<excludes>
<exclude>examples/*.java</exclude>
@@ -107,11 +114,14 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.0</version>
<version>2.1</version>
<configuration>
<mavenExecutorId>forked-path</mavenExecutorId>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<version>2.2.1</version>
<configuration>
<descriptors>
<descriptor>src/assemble/examples.xml</descriptor>
@@ -130,6 +140,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -142,6 +153,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
@@ -154,6 +166,25 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.5</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
!net.schmizz.*,
javax.crypto*,
com.jcraft.jzlib*;version="[1.0,2)",
org.slf4j*;version="[1.6,2)",
org.bouncycastle*;version="[1.4,2)",
*
</Import-Package>
<Export-Package>net.schmizz.*</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
@@ -209,7 +240,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.0</version>
<version>1.3</version>
<configuration>
<passphrase>${gpg.passphrase}</passphrase>
</configuration>

View File

@@ -42,7 +42,7 @@ public class LocalPF {
* google.com:80
*/
ssh.newLocalPortForwarder(new InetSocketAddress("localhost", 8080), "google.com", 80)
.listen();
.listen();
} finally {
ssh.disconnect();

View File

@@ -30,12 +30,12 @@ public class ErrorDeliveryUtil {
}
public static void alertEvents(Throwable x, Event... events) {
for (Event e: events)
for (Event e : events)
e.deliverError(x);
}
public static void alertEvents(Throwable x, Collection<? extends Event> events) {
for (Event e: events)
for (Event e : events)
e.deliverError(x);
}

View File

@@ -73,8 +73,8 @@ public class Event<T extends Throwable> {
}
/**
* @return whether this event is in a 'set' state. An event is set by a call to {@link set()} or {@link
* deliverError}
* @return whether this event is in a 'set' state. An event is set by a call to {@link #set} or {@link
* #deliverError}
*/
public boolean isSet() {
return promise.isDelivered();

View File

@@ -18,7 +18,8 @@ package net.schmizz.sshj;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
public class AndroidConfig extends DefaultConfig {
public class AndroidConfig
extends DefaultConfig {
@Override
protected void initRandomFactory(boolean ignored) {

View File

@@ -31,6 +31,7 @@ import java.util.List;
* {@link Compression}, {@link MAC}, {@link Signature}, {@link Random}, and {@link FileKeyProvider}.
*/
public interface Config {
/**
* Retrieve the list of named factories for {@code Cipher}.
*

View File

@@ -91,7 +91,7 @@ public class DefaultConfig
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String VERSION = "SSHJ_0_3";
private static final String VERSION = "SSHJ_0_6_0";
public DefaultConfig() {
setVersion(VERSION);
@@ -137,7 +137,7 @@ public class DefaultConfig
boolean warn = false;
// Ref. https://issues.apache.org/jira/browse/SSHD-24
// "AES256 and AES192 requires unlimited cryptography extension"
for (Iterator<Factory.Named<Cipher>> i = avail.iterator(); i.hasNext();) {
for (Iterator<Factory.Named<Cipher>> i = avail.iterator(); i.hasNext(); ) {
final Factory.Named<Cipher> f = i.next();
try {
final Cipher c = f.create();

View File

@@ -16,7 +16,6 @@
package net.schmizz.sshj;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SecurityUtils;
@@ -62,6 +61,7 @@ import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.SocketAddress;
@@ -113,7 +113,7 @@ import java.util.List;
*/
public class SSHClient
extends SocketClient
implements SessionFactory {
implements Closeable, SessionFactory {
/** Default port for SSH */
public static final int DEFAULT_PORT = 22;
@@ -441,7 +441,7 @@ public class SSHClient
/**
* Utility function for createing a {@link KeyProvider} instance from given location on the file system. Creates a
* one-off {@link PasswordFinder} using {@link PasswordUtils#createOneOff(char[])}, and calls {@link
* #loadKeys(String,PasswordFinder)}.
* #loadKeys(String, PasswordFinder)}.
*
* @param location location of the key file
* @param passphrase passphrase as a char-array
@@ -501,6 +501,33 @@ public class SSHClient
return loadKeys(location, passphrase.toCharArray());
}
/**
* Creates a {@link KeyProvider} instance from passed strings. Currently only PKCS8 format private key files are
* supported (OpenSSH uses this format).
* <p/>
*
* @param privateKey the private key as a string
* @param publicKey the public key as a string if it's not included with the private key
* @param passwordFinder the {@link PasswordFinder} that can supply the passphrase for decryption (may be {@code
* null} in case keyfile is not encrypted)
*
* @return the key provider ready for use in authentication
*
* @throws SSHException if there was no suitable key provider available for the file format; typically because
* BouncyCastle is not in the classpath
* @throws IOException if the key file format is not known, etc.
*/
public KeyProvider loadKeys(String privateKey, String publicKey, PasswordFinder passwordFinder)
throws IOException {
final FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(privateKey, publicKey != null);
final FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format
.toString());
if (fkp == null)
throw new SSHException("No provider available for " + format + " key file");
fkp.init(privateKey, publicKey, passwordFinder);
return fkp;
}
/**
* Attempts loading the user's {@code known_hosts} file from the default locations, i.e. {@code ~/.ssh/known_hosts}
* and {@code ~/.ssh/known_hosts2} on most platforms. Adds the resulting {@link OpenSSHKnownHosts} object as a host
@@ -661,4 +688,15 @@ public class SSHClient
log.info("Key exchange took {} seconds", (System.currentTimeMillis() - start) / 1000.0);
}
/**
* Same as {@link #disconnect()}.
*
* @throws IOException
*/
@Override
public void close()
throws IOException {
disconnect();
}
}

View File

@@ -32,7 +32,8 @@ public class Base64 {
* @see Base64
* @since 1.3
*/
public static class InputStream extends java.io.FilterInputStream {
public static class InputStream
extends java.io.FilterInputStream {
private final boolean encode; // Encoding or decoding
private int position; // Current position in the buffer
@@ -99,7 +100,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public int read() throws java.io.IOException {
public int read()
throws java.io.IOException {
// Do we need to get data?
if (position < 0)
@@ -197,7 +199,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public int read(byte[] dest, int off, int len) throws java.io.IOException {
public int read(byte[] dest, int off, int len)
throws java.io.IOException {
int i;
int b;
for (i = 0; i < len; i++) {
@@ -222,7 +225,8 @@ public class Base64 {
* @see Base64
* @since 1.3
*/
public static class OutputStream extends java.io.FilterOutputStream {
public static class OutputStream
extends java.io.FilterOutputStream {
private final boolean encode;
private int position;
@@ -289,7 +293,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public void close() throws java.io.IOException {
public void close()
throws java.io.IOException {
// 1. Ensure that pending characters are written
flush();
@@ -308,7 +313,8 @@ public class Base64 {
* @since 2.3
*/
@Override
public void flush() throws java.io.IOException {
public void flush()
throws java.io.IOException {
flushBase64();
super.flush();
}
@@ -318,7 +324,8 @@ public class Base64 {
*
* @throws java.io.IOException if there's an error.
*/
public void flushBase64() throws java.io.IOException {
public void flushBase64()
throws java.io.IOException {
if (position > 0)
if (encode) {
out.write(encode3to4(b4, buffer, position, options));
@@ -346,7 +353,8 @@ public class Base64 {
* @throws java.io.IOException if there's an error flushing
* @since 1.5.1
*/
public void suspendEncoding() throws java.io.IOException {
public void suspendEncoding()
throws java.io.IOException {
flushBase64();
suspendEncoding = true;
} // end suspendEncoding
@@ -361,7 +369,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public void write(byte[] theBytes, int off, int len) throws java.io.IOException {
public void write(byte[] theBytes, int off, int len)
throws java.io.IOException {
// Encoding suspended?
if (suspendEncoding) {
super.out.write(theBytes, off, len);
@@ -383,7 +392,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public void write(int theByte) throws java.io.IOException {
public void write(int theByte)
throws java.io.IOException {
// Encoding suspended?
if (suspendEncoding) {
super.out.write(theByte);
@@ -673,7 +683,8 @@ public class Base64 {
* @throws java.io.IOException If bogus characters exist in source data
* @since 1.3
*/
public static byte[] decode(byte[] source, int off, int len, int options) throws java.io.IOException {
public static byte[] decode(byte[] source, int off, int len, int options)
throws java.io.IOException {
// Lots of error checking and exception throwing
if (source == null)
@@ -725,7 +736,7 @@ public class Base64 {
else
// There's a bad input character in the Base64 stream.
throw new java.io.IOException(String.format("Bad Base64 input character '%c' in array position %d",
source[i], i));
source[i], i));
} // each input character
byte[] out = new byte[outBuffPosn];
@@ -743,7 +754,8 @@ public class Base64 {
* @throws java.io.IOException If there is a problem
* @since 1.4
*/
public static byte[] decode(String s) throws java.io.IOException {
public static byte[] decode(String s)
throws java.io.IOException {
return decode(s, NO_OPTIONS);
}
@@ -759,7 +771,8 @@ public class Base64 {
* @throws NullPointerException if <tt>s</tt> is null
* @since 1.4
*/
public static byte[] decode(String s, int options) throws java.io.IOException {
public static byte[] decode(String s, int options)
throws java.io.IOException {
if (s == null)
throw new NullPointerException("Input string was null.");
@@ -833,7 +846,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.2
*/
public static void decodeFileToFile(String infile, String outfile) throws java.io.IOException {
public static void decodeFileToFile(String infile, String outfile)
throws java.io.IOException {
byte[] decoded = Base64.decodeFromFile(infile);
java.io.OutputStream out = null;
@@ -864,7 +878,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.1
*/
public static byte[] decodeFromFile(String filename) throws java.io.IOException {
public static byte[] decodeFromFile(String filename)
throws java.io.IOException {
byte[] decodedData = null;
Base64.InputStream bis = null;
@@ -878,12 +893,12 @@ public class Base64 {
// Check for size of file
if (file.length() > Integer.MAX_VALUE)
throw new java.io.IOException("File is too big for this convenience method (" + file.length()
+ " bytes).");
+ " bytes).");
buffer = new byte[(int) file.length()];
// Open a stream
bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
Base64.DECODE);
Base64.DECODE);
// Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
@@ -918,7 +933,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.1
*/
public static void decodeToFile(String dataToDecode, String filename) throws java.io.IOException {
public static void decodeToFile(String dataToDecode, String filename)
throws java.io.IOException {
Base64.OutputStream bos = null;
try {
@@ -950,8 +966,9 @@ public class Base64 {
* @throws ClassNotFoundException if the decoded object is of a class that cannot be found by the JVM
* @since 1.5
*/
public static Object decodeToObject(String encodedObject) throws java.io.IOException,
java.lang.ClassNotFoundException {
public static Object decodeToObject(String encodedObject)
throws java.io.IOException,
java.lang.ClassNotFoundException {
// Decode and gunzip if necessary
byte[] objBytes = decode(encodedObject);
@@ -1078,7 +1095,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.0
*/
public static String encodeBytes(byte[] source, int options) throws java.io.IOException {
public static String encodeBytes(byte[] source, int options)
throws java.io.IOException {
return encodeBytes(source, 0, source.length, options);
} // end encodeBytes
@@ -1137,7 +1155,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.0
*/
public static String encodeBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
public static String encodeBytes(byte[] source, int off, int len, int options)
throws java.io.IOException {
byte[] encoded = encodeBytesToBytes(source, off, len, options);
// Return value according to relevant encoding.
@@ -1189,7 +1208,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.3.1
*/
public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options)
throws java.io.IOException {
if (source == null)
throw new NullPointerException("Cannot serialize a null array.");
@@ -1302,7 +1322,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.2
*/
public static void encodeFileToFile(String infile, String outfile) throws java.io.IOException {
public static void encodeFileToFile(String infile, String outfile)
throws java.io.IOException {
String encoded = Base64.encodeFromFile(infile);
java.io.OutputStream out = null;
@@ -1333,7 +1354,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.1
*/
public static String encodeFromFile(String filename) throws java.io.IOException {
public static String encodeFromFile(String filename)
throws java.io.IOException {
String encodedData = null;
Base64.InputStream bis = null;
@@ -1348,7 +1370,7 @@ public class Base64 {
// Open a stream
bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
Base64.ENCODE);
Base64.ENCODE);
// Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
@@ -1387,7 +1409,8 @@ public class Base64 {
* @throws NullPointerException if serializedObject is null
* @since 1.4
*/
public static String encodeObject(java.io.Serializable serializableObject) throws java.io.IOException {
public static String encodeObject(java.io.Serializable serializableObject)
throws java.io.IOException {
return encodeObject(serializableObject, NO_OPTIONS);
} // end encodeObject
@@ -1420,7 +1443,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.0
*/
public static String encodeObject(java.io.Serializable serializableObject, int options) throws java.io.IOException {
public static String encodeObject(java.io.Serializable serializableObject, int options)
throws java.io.IOException {
if (serializableObject == null)
throw new NullPointerException("Cannot serialize a null object.");
@@ -1481,7 +1505,8 @@ public class Base64 {
* @throws NullPointerException if dataToEncode is null
* @since 2.1
*/
public static void encodeToFile(byte[] dataToEncode, String filename) throws java.io.IOException {
public static void encodeToFile(byte[] dataToEncode, String filename)
throws java.io.IOException {
if (dataToEncode == null)
throw new NullPointerException("Data to encode was null.");

View File

@@ -341,41 +341,13 @@ public class Buffer<T extends Buffer<T>> {
*/
public BigInteger readMPInt()
throws BufferException {
return new BigInteger(readMPIntAsBytes());
return new BigInteger(readBytes());
}
/**
* Writes an SSH multiple-precision integer from a {@code BigInteger}
*
* @param bi {@code BigInteger} to write
*
* @return this
*/
public T putMPInt(BigInteger bi) {
return putMPInt(bi.toByteArray());
}
/**
* Writes an SSH multiple-precision integer from a Java byte-array
*
* @param foo byte-array
*
* @return this
*/
public T putMPInt(byte[] foo) {
int i = foo.length;
if ((foo[0] & 0x80) != 0) {
i++;
putUInt32(i);
putByte((byte) 0);
} else
putUInt32(i);
return putRawBytes(foo);
}
public byte[] readMPIntAsBytes()
throws BufferException {
return readBytes();
final byte[] asBytes = bi.toByteArray();
putUInt32(asBytes.length);
return putRawBytes(asBytes);
}
public long readUInt64()

View File

@@ -35,25 +35,11 @@
*/
package net.schmizz.sshj.common;
import java.util.Arrays;
/** Utility functions for byte arrays. */
public class ByteArrayUtils {
final static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Check whether two byte arrays are the equal.
*
* @param a1
* @param a2
*
* @return <code>true</code> or <code>false</code>
*/
public static boolean equals(byte[] a1, byte[] a2) {
return (a1.length != a2.length && equals(a1, 0, a2, 0, a1.length));
}
/**
* Check whether some part or whole of two byte arrays is equal, for <code>length</code> bytes starting at some
* offset.
@@ -75,17 +61,6 @@ public class ByteArrayUtils {
return true;
}
/**
* Get a hexadecimal representation of <code>array</code>, with each octet separated by a space.
*
* @param array
*
* @return hex string, each octet delimited by a space
*/
public static String printHex(byte[] array) {
return printHex(array, 0, array.length);
}
/**
* Get a hexadecimal representation of a byte array starting at <code>offset</code> index for <code>len</code>
* bytes, with each octet separated by a space.
@@ -139,8 +114,4 @@ public class ByteArrayUtils {
return sb.toString();
}
public static byte[] copyOf(byte[] array) {
return Arrays.copyOf(array, array.length);
}
}

View File

@@ -22,6 +22,7 @@ public interface ErrorNotifiable {
/** Utility functions. */
class Util {
/** Notify all {@code notifiables} of given {@code error}. */
public static void alertAll(SSHException error, ErrorNotifiable... notifiables) {
for (ErrorNotifiable notifiable : notifiables)

View File

@@ -32,7 +32,6 @@ public enum KeyType {
/** SSH identifier for RSA keys */
RSA("ssh-rsa") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {
@@ -64,7 +63,6 @@ public enum KeyType {
/** SSH identifier for DSA keys */
DSA("ssh-dss") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {
@@ -100,7 +98,6 @@ public enum KeyType {
/** Unrecognized */
UNKNOWN("unknown") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {

View File

@@ -58,6 +58,7 @@ import java.security.Signature;
public class SecurityUtils {
private static class BouncyCastleRegistration {
public void run()
throws Exception {
if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {

View File

@@ -89,13 +89,13 @@ public interface Connection {
* @param wantReply whether a reply is requested
* @param specifics {@link SSHPacket} containing fields specific to the request
*
* @return a {@link net.schmizz.concurrent.Promise} for the reply data (in case {@code wantReply} is true) which allows waiting on the
* reply, or {@code null} if a reply is not requested.
* @return a {@link net.schmizz.concurrent.Promise} for the reply data (in case {@code wantReply} is true) which
* allows waiting on the reply, or {@code null} if a reply is not requested.
*
* @throws TransportException if there is an error sending the request
*/
public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
byte[] specifics)
byte[] specifics)
throws TransportException;
/**

View File

@@ -35,8 +35,8 @@
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ByteArrayUtils;
import net.schmizz.sshj.common.DisconnectReason;
@@ -53,8 +53,8 @@ import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -76,7 +76,7 @@ public abstract class AbstractChannel
/** Remote recipient ID */
private int recipient;
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new ConcurrentLinkedQueue<Event<ConnectionException>>();
/* The lock used by to create the open & close events */
private final ReentrantLock lock = new ReentrantLock();
@@ -240,7 +240,8 @@ public abstract class AbstractChannel
ErrorDeliveryUtil.alertEvents(error, chanReqResponseEvents);
in.notifyError(error);
out.notifyError(error);
if (out != null)
out.notifyError(error);
finishOff();
}
@@ -312,7 +313,8 @@ public abstract class AbstractChannel
handleRequest(reqType, buf);
}
private void gotWindowAdjustment(SSHPacket buf) throws ConnectionException {
private void gotWindowAdjustment(SSHPacket buf)
throws ConnectionException {
final int howMuch;
try {
howMuch = buf.readUInt32AsInt();
@@ -362,8 +364,8 @@ public abstract class AbstractChannel
stream.receive(buf.array(), buf.rpos(), len);
}
protected synchronized Event<ConnectionException> sendChannelRequest(String reqType, boolean wantReply,
Buffer.PlainBuffer reqSpecific)
protected Event<ConnectionException> sendChannelRequest(String reqType, boolean wantReply,
Buffer.PlainBuffer reqSpecific)
throws TransportException {
log.info("Sending channel request for `{}`", reqType);
trans.write(
@@ -375,13 +377,14 @@ public abstract class AbstractChannel
Event<ConnectionException> responseEvent = null;
if (wantReply) {
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType, ConnectionException.chainer, lock);
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType,
ConnectionException.chainer);
chanReqResponseEvents.add(responseEvent);
}
return responseEvent;
}
private synchronized void gotResponse(boolean success)
private void gotResponse(boolean success)
throws ConnectionException {
final Event<ConnectionException> responseEvent = chanReqResponseEvents.poll();
if (responseEvent != null) {
@@ -390,9 +393,8 @@ public abstract class AbstractChannel
else
responseEvent.deliverError(new ConnectionException("Request failed"));
} else
throw new ConnectionException(
DisconnectReason.PROTOCOL_ERROR,
"Received response to channel request when none was requested");
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Received response to channel request when none was requested");
}
private synchronized void gotEOF()
@@ -428,7 +430,7 @@ public abstract class AbstractChannel
@Override
public String toString() {
return "< " + type + " channel: id=" + id + ", recipient=" + recipient + ", localWin=" + lwin + ", remoteWin="
+ rwin + " >";
+ rwin + " >";
}

View File

@@ -32,6 +32,7 @@ public interface Channel
/** Direct channels are those that are initiated by us. */
interface Direct
extends Channel {
/**
* Request opening this channel from remote end.
*

View File

@@ -116,7 +116,7 @@ public final class ChannelInputStream
public int read(byte[] b, int off, int len)
throws IOException {
synchronized (buf) {
for (; ;) {
for (; ; ) {
if (buf.available() > 0)
break;
if (eof)
@@ -163,7 +163,7 @@ public final class ChannelInputStream
if (adjustment > 0) {
log.info("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST to #{} for {} bytes", chan.getRecipient(), adjustment);
trans.write(new SSHPacket(Message.CHANNEL_WINDOW_ADJUST)
.putUInt32(chan.getRecipient()).putUInt32(adjustment));
.putUInt32(chan.getRecipient()).putUInt32(adjustment));
win.expand(adjustment);
}
}

View File

@@ -43,8 +43,8 @@ public class SocketStreamCopyMonitor
}
public static void monitor(final int frequency, final TimeUnit unit,
final Event<IOException> x, final Event<IOException> y,
final Channel channel, final Socket socket) {
final Event<IOException> x, final Event<IOException> y,
final Channel channel, final Socket socket) {
new SocketStreamCopyMonitor(new Runnable() {
public void run() {
try {

View File

@@ -51,12 +51,13 @@ public abstract class Window {
return size;
}
public void consume(int dec) {
public void consume(int dec)
throws ConnectionException {
synchronized (lock) {
log.debug("Consuming by " + dec + " down to " + size);
size -= dec;
if (size < 0)
throw new SSHRuntimeException("Window consumed to below 0");
throw new ConnectionException("Window consumed to below 0");
}
}
@@ -88,6 +89,14 @@ public abstract class Window {
}
}
public void consume(int howMuch) {
try {
super.consume(howMuch);
} catch (ConnectionException e) {
throw new SSHRuntimeException(e);
}
}
}
/** Controls how much data remote end can send before an adjustment notification from us is required. */

View File

@@ -53,7 +53,7 @@ public class LocalPortForwarder {
.bufSize(getLocalMaxPacketSize())
.spawnDaemon("chan2soc");
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, sock);
}
}
@Override
protected SSHPacket buildOpenReq() {

View File

@@ -82,10 +82,12 @@ public interface Session
throws TransportException;
@Deprecated
String getOutputAsString() throws IOException;
String getOutputAsString()
throws IOException;
@Deprecated
String getErrorAsString() throws IOException;
String getErrorAsString()
throws IOException;
}
@@ -132,6 +134,7 @@ public interface Session
/** Subsystem API. */
interface Subsystem
extends Channel {
Integer getExitStatus();
}

View File

@@ -104,7 +104,7 @@ public class SessionChannel
public void changeWindowDimensions(int cols, int rows, int width, int height)
throws TransportException {
sendChannelRequest(
"pty-req",
"window-change",
false,
new Buffer.PlainBuffer()
.putUInt32(cols)

View File

@@ -69,9 +69,9 @@ public abstract class AbstractForwardedChannel
// Must ensure channel is attached before confirming, data could start coming in immediately!
conn.attach(this);
trans.write(newBuffer(Message.CHANNEL_OPEN_CONFIRMATION)
.putUInt32(getID())
.putUInt32(getLocalWinSize())
.putUInt32(getLocalMaxPacketSize()));
.putUInt32(getID())
.putUInt32(getLocalWinSize())
.putUInt32(getLocalMaxPacketSize()));
open.set();
}

View File

@@ -39,18 +39,18 @@ public class RemotePortForwarder
* address (or domain name) and port on which connections for forwarding
* are to be accepted. Some strings used for 'address to bind' have
* special-case semantics.
* <p/>
*
* o &quot;&quot; means that connections are to be accepted on all protocol
* families supported by the SSH implementation.
* <p/>
*
* o &quot;0.0.0.0&quot; means to listen on all IPv4 addresses.
* <p/>
*
* o &quot;::&quot; means to listen on all IPv6 addresses.
* <p/>
*
* o &quot;localhost&quot; means to listen on all protocol families supported by
* the SSH implementation on loopback addresses only ([RFC3330] and
* [RFC3513]).
* <p/>
*
* o &quot;127.0.0.1&quot; and &quot;::1&quot; indicate listening on the loopback
* interfaces for IPv4 and IPv6, respectively.
* </pre>
@@ -198,9 +198,9 @@ public class RemotePortForwarder
protected SSHPacket req(String reqName, Forward forward)
throws ConnectionException, TransportException {
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putUInt32(forward.port)
.getCompactData();
.getCompactData();
return conn.sendGlobalRequest(reqName, true, specifics)
.retrieve(conn.getTimeout(), TimeUnit.SECONDS);
.retrieve(conn.getTimeout(), TimeUnit.SECONDS);
}
/** @return the active forwards. */
@@ -227,7 +227,7 @@ public class RemotePortForwarder
callListener(listeners.get(chan.getParentForward()), chan);
else
chan.reject(OpenFailException.Reason.ADMINISTRATIVELY_PROHIBITED, "Forwarding was not requested on `"
+ chan.getParentForward() + "`");
+ chan.getParentForward() + "`");
}
}

View File

@@ -74,7 +74,7 @@ public class FileMode {
}
public int getTypeMask() {
return mask & 0770000;
return mask & 0170000;
}
public int getPermissionsMask() {

View File

@@ -57,9 +57,9 @@ public class PacketReader
readIntoBuffer(lenBuf, 0, lenBuf.length);
return (int) (lenBuf[0] << 24 & 0xff000000L
| lenBuf[1] << 16 & 0x00ff0000L
| lenBuf[2] << 8 & 0x0000ff00L
| lenBuf[3] & 0x000000ffL);
| lenBuf[1] << 16 & 0x00ff0000L
| lenBuf[2] << 8 & 0x0000ff00L
| lenBuf[3] & 0x000000ffL);
}
public SFTPPacket<Response> readPacket()
@@ -97,7 +97,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

@@ -32,7 +32,7 @@ public class RemoteDirectory
throws IOException {
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
loop:
for (; ;) {
for (; ; ) {
Response res = requester.doRequest(newRequest(PacketType.READDIR));
switch (res.getType()) {

View File

@@ -39,8 +39,8 @@ public class RemoteFile
public FileAttributes fetchAttributes()
throws IOException {
return requester.doRequest(newRequest(PacketType.FSTAT))
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
public long length()
@@ -74,9 +74,9 @@ public class RemoteFile
public void write(long fileOffset, byte[] data, int off, int len)
throws IOException {
requester.doRequest(newRequest(PacketType.WRITE)
.putUInt64(fileOffset)
.putUInt32(len - off)
.putRawBytes(data, off, len)
.putUInt64(fileOffset)
.putUInt32(len - off)
.putRawBytes(data, off, len)
).ensureStatusPacketIsOK();
}
@@ -87,12 +87,12 @@ public class RemoteFile
public int getOutgoingPacketOverhead() {
return 1 + // packet type
4 + // request id
4 + // next length
handle.length() + // next
8 + // file offset
4 + // data length
4; // packet length
4 + // request id
4 + // next length
handle.length() + // next
8 + // file offset
4 + // data length
4; // packet length
}
public class RemoteFileOutputStream

View File

@@ -51,7 +51,8 @@ public class Response
private final PacketType type;
private final long reqID;
public Response(Buffer<Response> pk, int protocolVersion) throws SFTPException {
public Response(Buffer<Response> pk, int protocolVersion)
throws SFTPException {
super(pk);
this.protocolVersion = protocolVersion;
this.type = readType();
@@ -74,7 +75,8 @@ public class Response
return type;
}
public StatusCode readStatusCode() throws SFTPException {
public StatusCode readStatusCode()
throws SFTPException {
try {
return StatusCode.fromInt(readUInt32AsInt());
} catch (BufferException be) {

View File

@@ -56,7 +56,8 @@ public class SFTPEngine
this(ssh, PathHelper.DEFAULT_PATH_SEPARATOR);
}
public SFTPEngine(SessionFactory ssh, String pathSep) throws SSHException {
public SFTPEngine(SessionFactory ssh, String pathSep)
throws SSHException {
sub = ssh.startSession().startSubsystem("sftp");
out = sub.getOutputStream();
reader = new PacketReader(this);

View File

@@ -15,6 +15,9 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalSourceFile;
import java.io.IOException;
import java.util.List;
import java.util.Set;
@@ -178,10 +181,22 @@ public class StatefulSFTPClient
super.get(cwdify(source), dest);
}
@Override
public void get(String source, LocalDestFile dest)
throws IOException {
super.get(cwdify(source), dest);
}
@Override
public void put(String source, String dest)
throws IOException {
super.put(source, cwdify(dest));
}
@Override
public void put(LocalSourceFile source, String dest)
throws IOException {
super.put(source, cwdify(dest));
}
}

View File

@@ -90,9 +90,9 @@ public abstract class AbstractSignature
| sig[i++] & 0x000000ff;
i += j;
j = sig[i++] << 24 & 0xff000000
| sig[i++] << 16 & 0x00ff0000
| sig[i++] << 8 & 0x0000ff00
| sig[i++] & 0x000000ff;
| sig[i++] << 16 & 0x00ff0000
| sig[i++] << 8 & 0x0000ff00
| sig[i++] & 0x000000ff;
byte[] newSig = new byte[j];
System.arraycopy(sig, i, newSig, 0, j);
sig = newSig;

View File

@@ -47,7 +47,7 @@ import net.schmizz.sshj.transport.mac.MAC;
* <p/>
* <pre>
* Each packet is in the following format:
* <p/>
*
* uint32 packet_length
* byte padding_length
* byte[n1] payload; n1 = packet_length - padding_length - 1

View File

@@ -88,7 +88,7 @@ final class Decoder
int need;
/* Decoding loop */
for (; ;)
for (; ; )
if (packetLength == -1) // Waiting for beginning of packet
{
@@ -124,7 +124,7 @@ final class Decoder
if (log.isTraceEnabled())
log.trace("Received packet #{}: {}", seq, plain.printHex());
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet //
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet
inputBuffer.clear();
packetLength = -1;

View File

@@ -63,7 +63,7 @@ final class Encoder
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");
+ "5 bytes are available in front of the buffer");
SSHPacket nb = new SSHPacket(buffer.available() + 5);
nb.rpos(5);
nb.wpos(5);
@@ -96,8 +96,6 @@ final class Encoder
long encode(SSHPacket buffer) {
encodeLock.lock();
try {
buffer = checkHeaderSpace(buffer);
if (log.isTraceEnabled())
log.trace("Encoding packet #{}: {}", seq, buffer.printHex());

View File

@@ -35,13 +35,12 @@
*/
package net.schmizz.sshj.transport;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
@@ -57,6 +56,7 @@ import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
@@ -234,17 +234,16 @@ final class KeyExchanger
private void gotKexInit(SSHPacket buf)
throws TransportException {
Proposal serverProposal = new Proposal(buf);
buf.rpos(buf.rpos() - 1);
final Proposal serverProposal = new Proposal(buf);
negotiatedAlgs = clientProposal.negotiate(serverProposal);
log.debug("Negotiated algorithms: {}", negotiatedAlgs);
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(), negotiatedAlgs
.getKeyExchangeAlgorithm());
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
negotiatedAlgs.getKeyExchangeAlgorithm());
try {
kex.init(transport,
transport.getServerID().getBytes(IOUtils.UTF8),
transport.getClientID().getBytes(IOUtils.UTF8),
buf.getCompactData(),
clientProposal.getPacket().getCompactData());
transport.getServerID(), transport.getClientID(),
serverProposal.getPacket().getCompactData(), clientProposal.getPacket().getCompactData());
} catch (GeneralSecurityException e) {
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, e);
}
@@ -262,7 +261,7 @@ final class KeyExchanger
*
* @return the resized key
*/
private static byte[] resizedKey(byte[] E, int blockSize, Digest hash, byte[] K, byte[] H) {
private static byte[] resizedKey(byte[] E, int blockSize, Digest hash, BigInteger K, byte[] H) {
while (blockSize > E.length) {
Buffer.PlainBuffer buffer = new Buffer.PlainBuffer().putMPInt(K).putRawBytes(H).putRawBytes(E);
hash.update(buffer.array(), 0, buffer.available());
@@ -280,13 +279,15 @@ final class KeyExchanger
private void gotNewKeys() {
final Digest hash = kex.getHash();
final byte[] H = kex.getH();
if (sessionID == null)
// session id is 'H' from the first key exchange and does not change thereafter
sessionID = Arrays.copyOf(kex.getH(), kex.getH().length);
sessionID = H;
final Buffer.PlainBuffer hashInput = new Buffer.PlainBuffer()
.putMPInt(kex.getK())
.putRawBytes(kex.getH())
.putRawBytes(H)
.putByte((byte) 0) // <placeholder>
.putRawBytes(sessionID);
final int pos = hashInput.available() - sessionID.length - 1; // Position of <placeholder>
@@ -360,7 +361,6 @@ final class KeyExchanger
* having sent the packet ourselves (would cause gotKexInit() to fail)
*/
kexInitSent.await(transport.getTimeout(), TimeUnit.SECONDS);
buf.rpos(buf.rpos() - 1);
gotKexInit(buf);
expected = Expected.FOLLOWUP;
break;

View File

@@ -92,15 +92,15 @@ public final class NegotiatedAlgorithms {
@Override
public String toString() {
return ("[ " + //
"kex=" + kex + "; " + //
"sig=" + sig + "; " + //
"c2sCipher=" + c2sCipher + "; " + //
"s2cCipher=" + s2cCipher + "; " + //
"c2sMAC=" + c2sMAC + "; " + //
"s2cMAC=" + s2cMAC + "; " + //
"c2sComp=" + c2sComp + "; " + //
"s2cComp=" + s2cComp + //
return ("[ " +
"kex=" + kex + "; " +
"sig=" + sig + "; " +
"c2sCipher=" + c2sCipher + "; " +
"s2cCipher=" + s2cCipher + "; " +
"c2sMAC=" + c2sMAC + "; " +
"s2cMAC=" + s2cMAC + "; " +
"c2sComp=" + c2sComp + "; " +
"s2cComp=" + s2cComp +
" ]");
}

View File

@@ -86,7 +86,8 @@ class Proposal {
packet.putUInt32(0); // "Reserved" for future by spec
}
public Proposal(SSHPacket packet) throws TransportException {
public Proposal(SSHPacket packet)
throws TransportException {
this.packet = packet;
final int savedPos = packet.rpos();
packet.rpos(packet.rpos() + 17); // Skip message ID & cookie
@@ -144,14 +145,14 @@ class Proposal {
public NegotiatedAlgorithms negotiate(Proposal other)
throws TransportException {
return new NegotiatedAlgorithms(
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()), //
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()), //
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()), //
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()), //
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()), //
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()), //
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()), //
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms()) //
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()),
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()),
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms())
);
}

View File

@@ -178,12 +178,14 @@ public interface Transport
*/
void join()
throws TransportException;
/**
* Joins the thread calling this method to the transport's death.
*
* @throws TransportException if the transport dies of an exception
*/
void join(int timeout, TimeUnit unit) throws TransportException;
void join(int timeout, TimeUnit unit)
throws TransportException;
/** Send a disconnection packet with reason as {@link DisconnectReason#BY_APPLICATION}, and closes this transport. */
void disconnect();
@@ -226,9 +228,7 @@ public interface Transport
*/
void setDisconnectListener(DisconnectListener listener);
/**
* @return the current disconnect listener.
*/
/** @return the current disconnect listener. */
DisconnectListener getDisconnectListener();
}

View File

@@ -35,8 +35,8 @@
*/
package net.schmizz.sshj.transport;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.AbstractService;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.Service;
@@ -62,12 +62,14 @@ public final class TransportImpl
private static final class NullService
extends AbstractService {
NullService(Transport trans) {
super("null-service", trans);
}
}
static final class ConnInfo {
final String host;
final int port;
final InputStream in;
@@ -185,11 +187,11 @@ public final class TransportImpl
String ident;
byte[] data = new byte[256];
for (; ;) {
for (; ; ) {
int savedBufPos = buffer.rpos();
int pos = 0;
boolean needLF = false;
for (; ;) {
for (; ; ) {
if (buffer.available() == 0) {
// Need more data, so undo reading and return null
buffer.rpos(savedBufPos);
@@ -217,7 +219,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;
}
@@ -438,9 +440,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());
}
@@ -501,7 +503,8 @@ public final class TransportImpl
}
}
private void gotDebug(SSHPacket buf) throws TransportException {
private void gotDebug(SSHPacket buf)
throws TransportException {
try {
final boolean display = buf.readBoolean();
final String message = buf.readString();
@@ -529,7 +532,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();

View File

@@ -42,6 +42,7 @@ public class AES128CBC
/** Named factory for AES128CBC Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new AES128CBC();

View File

@@ -42,6 +42,7 @@ public class AES128CTR
/** Named factory for AES128CBC Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new AES128CTR();

View File

@@ -42,6 +42,7 @@ public class AES192CBC
/** Named factory for AES192CBC Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new AES192CBC();

View File

@@ -42,6 +42,7 @@ public class AES192CTR
/** Named factory for AES192CTR Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new AES192CTR();

View File

@@ -42,6 +42,7 @@ public class AES256CBC
/** Named factory for AES256CBC Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new AES256CBC();

View File

@@ -42,6 +42,7 @@ public class AES256CTR
/** Named factory for AES256CBC Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new AES256CTR();

View File

@@ -42,6 +42,7 @@ public class BlowfishCBC
/** Named factory for BlowfishCBC Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new BlowfishCBC();

View File

@@ -42,6 +42,7 @@ public class NoneCipher
/** Named factory for the no-op Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new NoneCipher();

View File

@@ -42,6 +42,7 @@ public class TripleDESCBC
/** Named factory for TripleDESCBC Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@Override
public Cipher create() {
return new TripleDESCBC();

View File

@@ -46,6 +46,7 @@ public class DelayedZlibCompression
/** Named factory for the ZLib Delayed Compression. */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Compression> {
@Override
public Compression create() {
return new DelayedZlibCompression();

View File

@@ -42,6 +42,7 @@ public abstract class NoneCompression
/** Named factory for the no-op <code>Compression</code> */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Compression> {
@Override
public Compression create() {
return null;

View File

@@ -49,6 +49,7 @@ public class ZlibCompression
/** Named factory for the ZLib Compression. */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Compression> {
@Override
public Compression create() {
return new ZlibCompression();
@@ -125,7 +126,7 @@ public class ZlibCompression
return;
default:
throw new TransportException(DisconnectReason.COMPRESSION_ERROR, "uncompress: inflate returned "
+ status);
+ status);
}
}
}

View File

@@ -36,7 +36,6 @@
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ByteArrayUtils;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
@@ -53,6 +52,7 @@ import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
/**
* Base class for DHG key exchange algorithms. Implementations will only have to configure the required data on the
@@ -65,32 +65,30 @@ public abstract class AbstractDHG
private Transport trans;
private final Digest sha = new SHA1();
private final Digest sha1 = new SHA1();
private final DH dh = new DH();
private byte[] V_S;
private byte[] V_C;
private String V_S;
private String V_C;
private byte[] I_S;
private byte[] I_C;
private byte[] e;
private byte[] K;
private byte[] H;
private PublicKey hostKey;
@Override
public byte[] getH() {
return ByteArrayUtils.copyOf(H);
return Arrays.copyOf(H, H.length);
}
@Override
public byte[] getK() {
return ByteArrayUtils.copyOf(K);
public BigInteger getK() {
return dh.getK();
}
@Override
public Digest getHash() {
return sha;
return sha1;
}
@Override
@@ -99,19 +97,18 @@ public abstract class AbstractDHG
}
@Override
public void init(Transport trans, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C)
public void init(Transport trans, String V_S, String V_C, byte[] I_S, byte[] I_C)
throws GeneralSecurityException, TransportException {
this.trans = trans;
this.V_S = ByteArrayUtils.copyOf(V_S);
this.V_C = ByteArrayUtils.copyOf(V_C);
this.I_S = ByteArrayUtils.copyOf(I_S);
this.I_C = ByteArrayUtils.copyOf(I_C);
sha.init();
this.V_S = V_S;
this.V_C = V_C;
this.I_S = Arrays.copyOf(I_S, I_S.length);
this.I_C = Arrays.copyOf(I_C, I_C.length);
sha1.init();
initDH(dh);
e = dh.getE();
log.info("Sending SSH_MSG_KEXDH_INIT");
trans.write(new SSHPacket(Message.KEXDH_INIT).putMPInt(e));
trans.write(new SSHPacket(Message.KEXDH_INIT).putMPInt(dh.getE()));
}
@Override
@@ -122,19 +119,18 @@ public abstract class AbstractDHG
log.info("Received SSH_MSG_KEXDH_REPLY");
final byte[] K_S;
final byte[] f;
final BigInteger f;
final byte[] sig; // signature sent by server
try {
K_S = packet.readBytes();
f = packet.readMPIntAsBytes();
f = packet.readMPInt();
sig = packet.readBytes();
hostKey = new Buffer.PlainBuffer(K_S).readPublicKey();
} catch (Buffer.BufferException be) {
throw new TransportException(be);
}
dh.setF(new BigInteger(f));
K = dh.getK();
dh.computeK(f);
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer()
.putString(V_C)
@@ -142,11 +138,11 @@ public abstract class AbstractDHG
.putString(I_C)
.putString(I_S)
.putString(K_S)
.putMPInt(e)
.putMPInt(dh.getE())
.putMPInt(f)
.putMPInt(K);
sha.update(buf.array(), 0, buf.available());
H = sha.digest();
.putMPInt(dh.getK());
sha1.update(buf.array(), buf.rpos(), buf.available());
H = sha1.digest();
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
KeyType.fromKey(hostKey).toString());
@@ -158,6 +154,7 @@ public abstract class AbstractDHG
return true;
}
protected abstract void initDH(DH dh);
protected abstract void initDH(DH dh)
throws GeneralSecurityException;
}

View File

@@ -54,7 +54,6 @@ public class DH {
private BigInteger p;
private BigInteger g;
private BigInteger e; // my public key
private BigInteger f; // your public key
private BigInteger K; // shared secret key
private final KeyPairGenerator generator;
private final KeyAgreement agreement;
@@ -68,39 +67,30 @@ public class DH {
}
}
public void setF(BigInteger f) {
this.f = f;
}
public void setG(BigInteger g) {
this.g = g;
}
public void setP(BigInteger p) {
public void init(BigInteger p, BigInteger g)
throws GeneralSecurityException {
this.p = p;
this.g = g;
generator.initialize(new DHParameterSpec(p, g));
final KeyPair kp = generator.generateKeyPair();
agreement.init(kp.getPrivate());
e = ((javax.crypto.interfaces.DHPublicKey) kp.getPublic()).getY();
}
public byte[] getE()
public void computeK(BigInteger f)
throws GeneralSecurityException {
if (e == null) {
generator.initialize(new DHParameterSpec(p, g));
final KeyPair kp = generator.generateKeyPair();
agreement.init(kp.getPrivate());
e = ((javax.crypto.interfaces.DHPublicKey) kp.getPublic()).getY();
}
return e.toByteArray();
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DH");
final PublicKey yourPubKey = keyFactory.generatePublic(new DHPublicKeySpec(f, p, g));
agreement.doPhase(yourPubKey, true);
K = new BigInteger(1, agreement.generateSecret());
}
public byte[] getK()
throws GeneralSecurityException {
if (K == null) {
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DH");
final DHPublicKeySpec keySpec = new DHPublicKeySpec(f, p, g);
final PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
agreement.doPhase(yourPubKey, true);
K = new BigInteger(agreement.generateSecret());
}
return K.toByteArray();
public BigInteger getE() {
return e;
}
public BigInteger getK() {
return K;
}
}

View File

@@ -35,6 +35,8 @@
*/
package net.schmizz.sshj.transport.kex;
import java.security.GeneralSecurityException;
/**
* Diffie-Hellman key exchange with SHA-1 and Oakley Group 2 [RFC2409] (1024-bit MODP Group).
*
@@ -60,9 +62,9 @@ public class DHG1
}
@Override
protected void initDH(DH dh) {
dh.setG(DHGroupData.G);
dh.setP(DHGroupData.P1);
protected void initDH(DH dh)
throws GeneralSecurityException {
dh.init(DHGroupData.P1, DHGroupData.G);
}
}

View File

@@ -35,6 +35,8 @@
*/
package net.schmizz.sshj.transport.kex;
import java.security.GeneralSecurityException;
/**
* Diffie-Hellman key exchange with SHA-1 and Oakley Group 14 [RFC3526] (2048-bit MODP Group).
* <p/>
@@ -61,9 +63,9 @@ public class DHG14
}
@Override
protected void initDH(DH dh) {
dh.setG(DHGroupData.G);
dh.setP(DHGroupData.P14);
protected void initDH(DH dh)
throws GeneralSecurityException {
dh.init(DHGroupData.P14, DHGroupData.G);
}
}

View File

@@ -45,18 +45,18 @@ public final class DHGroupData {
public static final BigInteger P1 =
new BigInteger("1797693134862315907708391567937874531978602960487560117064444236841971802161585193" +
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
"727629299387668709205606050270810842907692932019128194467627007");
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
"727629299387668709205606050270810842907692932019128194467627007");
public static final BigInteger P14 =
new BigInteger("3231700607131100730033891392642382824881794124114023911284200975140074170663435422" +
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
"9448652468262416972035911852507045361090559");
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
"9448652468262416972035911852507045361090559");
}

View File

@@ -41,6 +41,7 @@ import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.digest.Digest;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
@@ -59,14 +60,14 @@ public interface KeyExchange {
* @throws GeneralSecurityException
* @throws TransportException if there is an error sending a packet
*/
void init(Transport trans, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C)
void init(Transport trans, String V_S, String V_C, byte[] I_S, byte[] I_C)
throws GeneralSecurityException, TransportException;
/** @return the computed H parameter */
byte[] getH();
/** @return the computed K parameter */
byte[] getK();
BigInteger getK();
/**
* The message digest used by this key exchange algorithm.

View File

@@ -58,10 +58,9 @@ public class BouncyCastleRandom
}
private final RandomGenerator random;
private final RandomGenerator random = new VMPCRandomGenerator();
public BouncyCastleRandom() {
random = new VMPCRandomGenerator();
byte[] seed = new SecureRandom().generateSeed(8);
random.addSeedMaterial(seed);
}

View File

@@ -58,11 +58,7 @@ public class JCERandom
}
private byte[] tmp = new byte[16];
private SecureRandom random = null;
public JCERandom() {
random = new SecureRandom();
}
private final SecureRandom random = new SecureRandom();
/**
* Fill the given byte-array with random bytes from this PRNG.

View File

@@ -40,6 +40,7 @@ import net.schmizz.sshj.common.Factory;
/** A random factory wrapper that uses a single random instance. The underlying random instance has to be thread safe. */
public class SingletonRandomFactory
implements Random, Factory<Random> {
private final Random random;
public SingletonRandomFactory(Factory<Random> factory) {

View File

@@ -41,7 +41,7 @@ public class ConsoleKnownHostsVerifier
protected boolean hostKeyUnverifiableAction(String hostname, PublicKey key) {
final KeyType type = KeyType.fromKey(key);
console.printf("The authenticity of host '%s' can't be established.\n" +
"%s key fingerprint is %s.\n", hostname, type, SecurityUtils.getFingerprint(key));
"%s key fingerprint is %s.\n", hostname, type, SecurityUtils.getFingerprint(key));
String response = console.readLine("Are you sure you want to continue connecting (yes/no)? ");
while (!(response.equalsIgnoreCase(YES) || response.equalsIgnoreCase(NO))) {
response = console.readLine("Please explicitly enter yes/no: ");
@@ -66,15 +66,15 @@ public class ConsoleKnownHostsVerifier
final String path = getFile().getAbsolutePath();
console.printf(
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" +
"@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\n" +
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" +
"IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n" +
"Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n" +
"It is also possible that the host key has just been changed.\n" +
"The fingerprint for the %s key sent by the remote host is\n" +
"%s.\n" +
"Please contact your system administrator or" +
"add correct host key in %s to get rid of this message.\n",
"@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\n" +
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" +
"IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n" +
"Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n" +
"It is also possible that the host key has just been changed.\n" +
"The fingerprint for the %s key sent by the remote host is\n" +
"%s.\n" +
"Please contact your system administrator or" +
"add correct host key in %s to get rid of this message.\n",
type, fp, path);
return false;
}

View File

@@ -139,11 +139,13 @@ public class UserAuthImpl
case USERAUTH_BANNER: {
banner = buf.readString();
} break;
}
break;
case USERAUTH_SUCCESS: {
authenticated.set();
} break;
}
break;
case USERAUTH_FAILURE: {
allowedMethods.clear();
@@ -154,7 +156,8 @@ public class UserAuthImpl
} else {
authenticated.deliverError(new UserAuthException(currentMethod.getName() + " auth failed"));
}
} break;
}
break;
default: {
log.debug("Asking `{}` method to handle {} packet", currentMethod.getName(), msg);

View File

@@ -33,4 +33,7 @@ public interface FileKeyProvider
void init(File location, PasswordFinder pwdf);
void init(String privateKey, String publicKey);
void init(String privateKey, String publicKey, PasswordFinder pwdf);
}

View File

@@ -21,6 +21,8 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class KeyProviderUtil {
@@ -37,13 +39,50 @@ public class KeyProviderUtil {
*/
public static FileKeyProvider.Format detectKeyFileFormat(File location)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(location));
return detectKeyFileFormat(new FileReader(location),
new File(location + ".pub").exists());
}
/**
* Attempts to detect how a key file is encoded.
* <p/>
* Return values are consistent with the {@code NamedFactory} implementations in the {@code keyprovider} package.
*
* @param privateKey Private key stored in a string
* @param separatePubKey Is the public key stored separately from the private key
*
* @return name of the key file format
*
* @throws java.io.IOException
*/
public static FileKeyProvider.Format detectKeyFileFormat(String privateKey,
boolean separatePubKey)
throws IOException {
return detectKeyFileFormat(new StringReader(privateKey), separatePubKey);
}
/**
* Attempts to detect how a key file is encoded.
* <p/>
* Return values are consistent with the {@code NamedFactory} implementations in the {@code keyprovider} package.
*
* @param privateKey Private key accessible through a {@code Reader}
* @param separatePubKey Is the public key stored separately from the private key
*
* @return name of the key file format
*
* @throws java.io.IOException
*/
private static FileKeyProvider.Format detectKeyFileFormat(Reader privateKey,
boolean separatePubKey)
throws IOException {
BufferedReader br = new BufferedReader(privateKey);
String firstLine = br.readLine();
IOUtils.closeQuietly(br);
if (firstLine == null)
throw new IOException("Empty file");
if (firstLine.startsWith("-----BEGIN") && firstLine.endsWith("PRIVATE KEY-----"))
if (new File(location + ".pub").exists())
if (separatePubKey)
// Can delay asking for password since have unencrypted pubkey
return FileKeyProvider.Format.OpenSSH;
else
@@ -54,5 +93,4 @@ public class KeyProviderUtil {
*/
return FileKeyProvider.Format.Unknown;
}
}

View File

@@ -23,8 +23,11 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.security.PublicKey;
/**
* Represents an OpenSSH identity that consists of a PKCS8-encoded private key file and an unencrypted public key file
* of the same name with the {@code ".pub"} extension. This allows to delay requesting of the passphrase until the
@@ -62,18 +65,7 @@ public class OpenSSHKeyFile
final File f = new File(location + ".pub");
if (f.exists())
try {
final BufferedReader br = new BufferedReader(new FileReader(f));
try {
final String keydata = br.readLine();
if (keydata != null) {
String[] parts = keydata.split(" ");
assert parts.length >= 2;
type = KeyType.fromString(parts[0]);
pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey();
}
} finally {
br.close();
}
initPubKey(new FileReader(f));
} catch (IOException e) {
// let super provide both public & private key
log.warn("Error reading public key file: {}", e.toString());
@@ -81,4 +73,36 @@ public class OpenSSHKeyFile
super.init(location);
}
@Override
public void init(String privateKey, String publicKey) {
if (publicKey != null) {
initPubKey(new StringReader(publicKey));
}
super.init(privateKey, null);
}
/**
* Read and store the separate public key provided alongside the private key
*
* @param publicKey Public key accessible through a {@code Reader}
*/
private void initPubKey(Reader publicKey) {
try {
final BufferedReader br = new BufferedReader(publicKey);
try {
final String keydata = br.readLine();
if (keydata != null) {
String[] parts = keydata.split(" ");
assert parts.length >= 2;
type = KeyType.fromString(parts[0]);
pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey();
}
} finally {
br.close();
}
} catch (IOException e) {
// let super provide both public & private key
log.warn("Error reading public key: {}", e.toString());
}
}
}

View File

@@ -20,15 +20,15 @@ import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.PrivateKeyFileResource;
import net.schmizz.sshj.userauth.password.PrivateKeyStringResource;
import net.schmizz.sshj.userauth.password.Resource;
import org.bouncycastle.openssl.EncryptionException;
import org.bouncycastle.openssl.PEMReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -39,6 +39,7 @@ public class PKCS8KeyFile
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@Override
public FileKeyProvider create() {
return new PKCS8KeyFile();
@@ -52,7 +53,8 @@ public class PKCS8KeyFile
protected final Logger log = LoggerFactory.getLogger(getClass());
protected PasswordFinder pwdf;
protected PrivateKeyFileResource resource;
@SuppressWarnings("unchecked")
protected Resource resource;
protected KeyPair kp;
protected KeyType type;
@@ -89,6 +91,19 @@ public class PKCS8KeyFile
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 org.bouncycastle.openssl.PasswordFinder makeBouncyPasswordFinder() {
if (pwdf == null)
return null;
@@ -108,10 +123,10 @@ public class PKCS8KeyFile
PEMReader r = null;
Object o = null;
try {
for (; ;) {
for (; ; ) {
// while the PasswordFinder tells us we should retry
try {
r = new PEMReader(new InputStreamReader(new FileInputStream(resource.getDetail())), pFinder);
r = new PEMReader(resource.getReader(), pFinder);
o = r.readObject();
} catch (EncryptionException e) {
if (pwdf.shouldRetry(resource))

View File

@@ -51,6 +51,7 @@ public class AuthKeyboardInteractive
}
private static class CharArrWrap {
private final char[] arr;
private CharArrWrap(char[] arr) {

View File

@@ -48,7 +48,7 @@ public abstract class KeyedAuthMethod
// public key as 2 strings: [ key type | key blob ]
reqBuf.putString(KeyType.fromKey(key).toString())
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
return reqBuf;
}
@@ -67,9 +67,10 @@ public abstract class KeyedAuthMethod
throw new UserAuthException("Could not create signature instance for " + kt + " key");
sigger.init(null, key);
sigger.update(new Buffer.PlainBuffer().putString(params.getTransport().getSessionID()) //
.putBuffer(reqBuf) // & rest of the data for sig
.getCompactData());
sigger.update(new Buffer.PlainBuffer()
.putString(params.getTransport().getSessionID())
.putBuffer(reqBuf) // & rest of the data for sig
.getCompactData());
reqBuf.putSignature(kt, sigger.sign());
return reqBuf;
}

View File

@@ -15,6 +15,10 @@
*/
package net.schmizz.sshj.userauth.password;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class AccountResource
extends Resource<String> {
@@ -22,4 +26,9 @@ public class AccountResource
super(user + "@" + host);
}
@Override
public Reader getReader()
throws IOException {
return new StringReader(getDetail());
}
}

View File

@@ -16,6 +16,10 @@
package net.schmizz.sshj.userauth.password;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class PrivateKeyFileResource
extends Resource<File> {
@@ -24,4 +28,9 @@ public class PrivateKeyFileResource
super(privateKeyFile);
}
@Override
public Reader getReader()
throws IOException {
return new InputStreamReader(new FileInputStream(getDetail()));
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.userauth.password;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class PrivateKeyStringResource
extends Resource<String> {
public PrivateKeyStringResource(String string) {
super(string);
}
@Override
public Reader getReader()
throws IOException {
return new StringReader(getDetail());
}
@Override
public String toString() {
// If not overridden, the superclass's will return the private key as
// part of the string.
return "[" + getClass().getSimpleName() + "]";
}
}

View File

@@ -15,6 +15,9 @@
*/
package net.schmizz.sshj.userauth.password;
import java.io.IOException;
import java.io.Reader;
/** A password-protected resource */
public abstract class Resource<H> {
@@ -28,6 +31,9 @@ public abstract class Resource<H> {
return detail;
}
public abstract Reader getReader()
throws IOException;
@Override
public boolean equals(Object o) {
if (this == o)

View File

@@ -26,9 +26,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FileSystemFile
implements LocalSourceFile, LocalDestFile {

View File

@@ -26,7 +26,7 @@ public interface FileTransfer {
throws IOException;
void upload(LocalSourceFile localFile, String remotePath)
throws IOException;
throws IOException;
void download(String remotePath, LocalDestFile localFile)
throws IOException;

View File

@@ -45,7 +45,6 @@ public interface LocalDestFile {
/**
* Set the permissions for the underlying file.
*
* @param f the file
* @param perms permissions e.g. 0644
*
* @throws IOException
@@ -66,7 +65,6 @@ public interface LocalDestFile {
/**
* Set the last modified time for the underlying file.
*
* @param f the file
* @param t time in seconds since Unix epoch
*
* @throws IOException

View File

@@ -34,7 +34,8 @@ public interface LocalSourceFile {
*
* @throws IOException
*/
int getPermissions() throws IOException;
int getPermissions()
throws IOException;
boolean isFile();

View File

@@ -19,6 +19,7 @@ import net.schmizz.sshj.common.SSHException;
public class SCPException
extends SSHException {
public SCPException(String message) {
super(message);
}

View File

@@ -15,8 +15,6 @@
*/
package net.schmizz.sshj.xfer.scp;
import java.io.IOException;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.AbstractFileTransfer;
import net.schmizz.sshj.xfer.FileSystemFile;
@@ -24,6 +22,8 @@ import net.schmizz.sshj.xfer.FileTransfer;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalSourceFile;
import java.io.IOException;
public class SCPFileTransfer
extends AbstractFileTransfer
implements FileTransfer {
@@ -43,7 +43,7 @@ public class SCPFileTransfer
}
private SCPEngine newSCPEngine() {
return new SCPEngine(sessionFactory, getTransferListener());
return new SCPEngine(sessionFactory, getTransferListener());
}
@Override
@@ -65,9 +65,9 @@ public class SCPFileTransfer
}
@Override
public void upload(LocalSourceFile localFile, String remotePath)
throws IOException {
newSCPUploadClient().copy(localFile, remotePath);
}
public void upload(LocalSourceFile localFile, String remotePath)
throws IOException {
newSCPUploadClient().copy(localFile, remotePath);
}
}

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj.xfer.scp;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.xfer.LocalFileFilter;
import net.schmizz.sshj.xfer.LocalSourceFile;
import net.schmizz.sshj.xfer.scp.SCPEngine.Arg;
@@ -28,8 +29,9 @@ import java.util.List;
public final class SCPUploadClient {
private final SCPEngine engine;
private LocalFileFilter uploadFilter;
SCPUploadClient(SCPEngine engine) {
SCPUploadClient(SCPEngine engine) {
this.engine = engine;
}
@@ -45,7 +47,11 @@ public final class SCPUploadClient {
return engine.getExitStatus();
}
private synchronized void startCopy(LocalSourceFile sourceFile, String targetPath)
public void setUploadFilter(LocalFileFilter uploadFilter) {
this.uploadFilter = uploadFilter;
}
private synchronized void startCopy(LocalSourceFile sourceFile, String targetPath)
throws IOException {
List<Arg> args = new LinkedList<Arg>();
args.add(Arg.SINK);
@@ -75,7 +81,7 @@ public final class SCPUploadClient {
throws IOException {
preserveTimeIfPossible(f);
engine.sendMessage("D0" + getPermString(f) + " 0 " + f.getName());
for (LocalSourceFile child : f.getChildren(null))
for (LocalSourceFile child : f.getChildren(uploadFilter))
process(child);
engine.sendMessage("E");
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj;
import net.schmizz.sshj.util.BasicFixture;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class LoadsOfConnects {
protected final Logger log = LoggerFactory.getLogger(getClass());
private final BasicFixture fixture = new BasicFixture();
@Test
public void loadsOfConnects()
throws IOException, InterruptedException {
for (int i = 0; i < 1000; i++) {
System.out.println("Try " + i);
fixture.init(false);
fixture.done();
}
}
}

View File

@@ -50,7 +50,8 @@ public class SmokeTest {
}
@Test
public void authenticated() throws UserAuthException, TransportException {
public void authenticated()
throws UserAuthException, TransportException {
fixture.dummyAuth();
assertTrue(fixture.getClient().isAuthenticated());
}

View File

@@ -31,6 +31,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Scanner;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -113,6 +114,19 @@ public class OpenSSHKeyFileTest {
assertEquals(KeyUtil.newDSAPrivateKey(x, p, q, g), dsa.getPrivate());
}
@Test
public void fromString()
throws IOException, GeneralSecurityException {
FileKeyProvider dsa = new OpenSSHKeyFile();
String privateKey = readFile("src/test/resources/id_dsa");
String publicKey = readFile("src/test/resources/id_dsa.pub");
dsa.init(privateKey, publicKey,
PasswordUtils.createOneOff(correctPassphrase));
assertEquals(dsa.getType(), KeyType.DSA);
assertEquals(KeyUtil.newDSAPublicKey(y, p, q, g), dsa.getPublic());
assertEquals(KeyUtil.newDSAPrivateKey(x, p, q, g), dsa.getPrivate());
}
@Before
public void setup()
throws UnsupportedEncodingException, GeneralSecurityException {
@@ -120,4 +134,18 @@ public class OpenSSHKeyFileTest {
throw new AssertionError("bouncy castle needed");
}
private String readFile(String pathname)
throws IOException {
StringBuilder fileContents = new StringBuilder();
Scanner scanner = new Scanner(new File(pathname));
String lineSeparator = System.getProperty("line.separator");
try {
while (scanner.hasNextLine()) {
fileContents.append(scanner.nextLine() + lineSeparator);
}
return fileContents.toString();
} finally {
scanner.close();
}
}
}

View File

@@ -0,0 +1,23 @@
package net.schmizz.sshj.sftp;
import org.junit.Test;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertThat;
public class FileModeTest {
@Test
public void shouldDetectDirectoryWithLinuxMask() {
FileMode fileMode = new FileMode(040755);
assertThat(fileMode.toString(), equalTo("[mask=40755]"));
assertThat(fileMode.getType(), equalTo(FileMode.Type.DIRECTORY));
}
@Test
public void shouldDetectDirectoryWithAixUnixMask() {
FileMode fileMode = new FileMode(0240755);
assertThat(fileMode.toString(), equalTo("[mask=240755]"));
assertThat(fileMode.getType(), equalTo(FileMode.Type.DIRECTORY));
}
}

View File

@@ -28,6 +28,7 @@ import static org.junit.Assert.assertEquals;
/** Tests {@link Buffer} functionality */
public class BufferTest {
private Buffer.PlainBuffer posBuf;
private Buffer.PlainBuffer handyBuf;

View File

@@ -29,34 +29,38 @@ import java.security.spec.RSAPublicKeySpec;
public class KeyUtil {
/** Creates a DSA private key. */
public static PrivateKey newDSAPrivateKey(String x, String p, String q, String g) throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("DSA").generatePrivate(new DSAPrivateKeySpec //
(new BigInteger(x, 16), //
new BigInteger(p, 16), //
new BigInteger(q, 16), //
new BigInteger(g, 16)));
public static PrivateKey newDSAPrivateKey(String x, String p, String q, String g)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("DSA").generatePrivate(new DSAPrivateKeySpec(new BigInteger(x, 16),
new BigInteger(p, 16),
new BigInteger(q, 16),
new BigInteger(g, 16))
);
}
/** Creates a DSA public key. */
public static PublicKey newDSAPublicKey(String y, String p, String q, String g) throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("DSA").generatePublic(new DSAPublicKeySpec //
(new BigInteger(y, 16), //
new BigInteger(p, 16), //
new BigInteger(q, 16), //
new BigInteger(g, 16)));
public static PublicKey newDSAPublicKey(String y, String p, String q, String g)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("DSA").generatePublic(new DSAPublicKeySpec(new BigInteger(y, 16),
new BigInteger(p, 16),
new BigInteger(q, 16),
new BigInteger(g, 16))
);
}
/** Creates an RSA private key. */
public static PrivateKey newRSAPrivateKey(String modulus, String exponent) throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec //
(new BigInteger(modulus, 16), //
new BigInteger(exponent, 16)));
public static PrivateKey newRSAPrivateKey(String modulus, String exponent)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(new BigInteger(modulus, 16),
new BigInteger(exponent, 16))
);
}
/** Creates an RSA public key. */
public static PublicKey newRSAPublicKey(String modulus, String exponent) throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("RSA").generatePublic(new RSAPublicKeySpec //
(new BigInteger(modulus, 16), new BigInteger(exponent, 16)));
public static PublicKey newRSAPublicKey(String modulus, String exponent)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("RSA").generatePublic(new RSAPublicKeySpec(new BigInteger(modulus, 16),
new BigInteger(exponent, 16)));
}
}

View File

@@ -0,0 +1,54 @@
package net.schmizz.sshj.xfer.scp;
import net.schmizz.sshj.xfer.FileSystemFile;
import net.schmizz.sshj.xfer.LocalFileFilter;
import net.schmizz.sshj.xfer.LocalSourceFile;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.verification.VerificationMode;
import java.io.File;
import java.io.IOException;
import static org.mockito.Matchers.endsWith;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class SCPUploadClientTest {
private SCPEngine engine;
private SCPUploadClient scpUploadClient;
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Before
public void init() {
engine = mock(SCPEngine.class);
scpUploadClient = new SCPUploadClient(engine);
}
@Test
public void shouldOnlySendFilterAcceptedFilesFromDirectory() throws IOException {
scpUploadClient.setUploadFilter(new LocalFileFilter() {
@Override
public boolean accept(LocalSourceFile file) {
return !file.getName().contains("not-");
}
});
File dir = temp.newFolder("filtered-scp-upload");
new File(dir, "not-sent.txt").createNewFile();
new File(dir, "sent.txt").createNewFile();
int copy = scpUploadClient.copy(new FileSystemFile(dir), "/tmp");
verify(engine).startedDir("filtered-scp-upload");
verify(engine).startedFile(eq("sent.txt"), isA(Long.class));
verify(engine, times(1)).startedFile(isA(String.class), isA(Long.class));
}
}