Compare commits

...

77 Commits

Author SHA1 Message Date
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
Jeroen van Erp
63927a3e2b Setting version to 0.18.0 2016-09-30 11:43:02 +02:00
Jeroen van Erp
ac262f8086 Updated README for 0.17.3 2016-09-30 11:26:01 +02:00
Jeroen van Erp
6e56cd9d0a Changed release process to bintray 2016-09-30 11:03:22 +02:00
Jeroen van Erp
2f6025d9ba Merge branch 'CherifGh-gcif-patch-android-compat' 2016-09-30 10:38:22 +02:00
Jeroen van Erp
275e98e55b Refactored SecurityUtils a bit 2016-09-30 10:37:55 +02:00
Ghozzi Cherif
655d070571 Update AndroidConfig.java 2016-09-25 15:28:59 +02:00
Jeroen van Erp
c9775ca2c7 Fixed formatting warnings of Codacy 2016-09-13 15:34:08 +02:00
Jeroen van Erp
a2fb4fbd98 Merge pull request #271 from joval/master
Gracefully load OpenSSH known_hosts without requiring BouncyCastle
2016-09-13 15:21:09 +02:00
David Solin
6185ac4db8 Improvements per JVE. 2016-09-13 08:04:15 -05:00
David Solin
e420593fa9 Okay, more spaces Codacy, you win! 2016-09-09 16:03:45 -05:00
David Solin
68b924863e Fail gracefully when reading an OpenSSH Known Hosts file that uses key types requiring BouncyCastle, but we're not including BouncyCastle. 2016-09-09 14:39:21 -05:00
Jeroen van Erp
613ace1864 Upgrade to gradle 3.0 2016-09-05 11:32:59 +02:00
Jeroen van Erp
78e0ecd264 Merge pull request #270 from hierynomus/java6
Java6 re-enabled
2016-09-05 11:29:02 +02:00
Jeroen van Erp
64085e62f4 Removed openJDK6 as release plugin is incompatible 2016-09-05 09:54:15 +02:00
Jeroen van Erp
8c7d2fa8d0 Java6 re-enabled 2016-09-02 16:54:21 +02:00
Jeroen van Erp
766d292bad Merge pull request #269 from joval/master
Compatibility features
2016-09-02 16:46:56 +02:00
David Solin
a40957fffc Merge remote-tracking branch 'upstream/master' 2016-09-02 09:24:18 -05:00
David Solin
8ffd852e67 SPACES NOT TABS (like in Silicon Valley). 2016-09-02 09:16:26 -05:00
Jeroen van Erp
b90be512e7 REformatted 2016-09-02 16:08:26 +02:00
Jeroen van Erp
c0d49cf6b3 Removed dependency on VRallev ECC25519 library 2016-09-02 16:05:18 +02:00
David Solin
1b5b2b25b7 Merge branch 'master' into post-logging-factory 2016-09-02 06:45:58 -05:00
Jeroen van Erp
1dad19ca6e Merge pull request #267 from joval/logging-factory
Logging factory
2016-09-02 10:55:43 +02:00
David Solin
90fa26925d Merge branch 'logging-factory' into post-logging-factory 2016-08-29 15:19:57 -05:00
David Solin
9425300262 Better argument order for IdentificationStringParser constructor. 2016-08-29 15:19:06 -05:00
David A. Solin
f2bfe9bfcf Make Java-6 compatible. 2016-08-26 13:26:01 -05:00
David Solin
71498ad961 Added some features required to integrate sshj with Joval's remote jSAF provider.
1) Added boolean Channel.isEOF() method, for checking whether the server has sent EOF.
2) Added SFTP response constants to the Response enumeration
3) Spelling correction for the SYMLINK FileMode enum constant
2016-08-26 13:02:16 -05:00
David Solin
7b8b1cfdf5 Merge branch 'master' into logging-factory 2016-08-24 08:37:24 -05:00
David Solin
3f29879eca Test fixes 2016-08-24 08:35:08 -05:00
David Solin
79c1ae2bb0 Re-add public constructor used by Groovy 2016-08-24 08:08:33 -05:00
David Solin
819d411cf1 Merge branch 'master' of github.com:joval/sshj into logging-factory 2016-08-24 07:57:22 -05:00
David Solin
6579f6f710 Removed unnecessary DefaultLoggerFactory inner class from DefaultConfig
Fixed license header in LoggerFactory.java (via gradle licenseForamat)
2016-08-24 07:34:01 -05:00
David Solin
cf5830eda5 Indentation fixes. 2016-08-24 06:23:45 -05:00
Jeroen van Erp
36ad389ccf Merge pull request #266 from joval/master
Decryption fix and junit test updates for PKCS5
2016-08-24 13:04:36 +02:00
David Solin
f63a88ec9f Removed unnecessary import statements. 2016-08-23 19:24:11 -05:00
David Solin
7379a89268 Inadvertently added this class to a PR where it's not in-scope. 2016-08-23 19:18:51 -05:00
David Solin
219901211e Changes for injecting a LoggingFactory. 2016-08-23 19:13:43 -05:00
David Solin
8c1329036a Updated test case for PKCS5 over-"simplification", to prevent regression. 2016-08-23 19:08:03 -05:00
David Solin
733c19350c Un-break PKCS5 (I inadvertently over-simplified setting the salt length). 2016-08-23 17:40:00 -05:00
Jeroen van Erp
e5ec84c06a Merge pull request #265 from joval/master
Minor modifications to PKCS5KeyFile
2016-08-19 23:35:03 +02:00
David Solin
ed0156c985 Forgot to move 2 fields to the top of the file. 2016-08-19 15:54:48 -05:00
David Solin
6321685881 Moved constant declarations per codacy. 2016-08-19 15:51:07 -05:00
David Solin
f6b4d47945 Another missing import. This should be it. 2016-08-19 15:09:50 -05:00
David Solin
d198ef121c Missing import in test. 2016-08-19 15:04:55 -05:00
David Solin
7786468875 Simplification? 2016-08-19 15:00:12 -05:00
David Solin
0847e8460a Merge remote-tracking branch 'upstream/master' 2016-08-19 12:24:14 -05:00
David Solin
62b8726807 Fixed bug wherein PKCS5KeyFile messed up the data if a bad passphrase was used, which prevented decryption with a subsequent correct passphrase.
Also, made it possible for subclasses of PKCS5KeyFile to access the decrypted ASN.1 data directly (useful if you want to decrypt then store a PKCS5 key for later use elsewhere).
And I think I made the code a little prettier.
2016-08-19 12:18:49 -05:00
Jeroen van Erp
a6af27ae91 Added Codacy badge 2016-08-19 13:02:55 +02:00
Jeroen van Erp
628cbf5eba Organized imports 2016-08-19 12:57:37 +02:00
Jeroen van Erp
e134e00574 Merge pull request #263 from joval/master
Modifications for working with SSH gateways
2016-08-17 08:41:33 +02:00
David Solin
1caa7ac722 For null hostnames, use the loopback InetAddress (for backward-compatibility). 2016-08-16 10:26:57 -05:00
David Solin
4183776adb Updates per Jeroen van Erp. 2016-08-16 09:17:30 -05:00
David Solin
791f112752 Merge remote-tracking branch 'upstream/master' 2016-08-15 09:27:10 -05:00
Jeroen van Erp
233f3765c9 Added PKCS5 key file support (#262) 2016-08-15 15:49:47 +02:00
Jeroen van Erp
5f292d398f Fixed toString of FileAttributes (Fixes #258) 2016-08-15 09:56:13 +02:00
David Solin
ba347f927d Close any LocalPortForwarders with the SSHClient that produced them (assuming they're still open). 2016-08-15 00:17:48 -05:00
David Solin
6f9ecf69e4 Reordered connect methods so that the similar ones are grouped together.
Also, eliminated all use of InetAddress.getByName, because we'll want to be able to connect even when the target host is only visible to DNS from the Proxy, or the SocketFactory.
2016-08-14 14:06:40 -05:00
David Solin
e78ae4dbeb 4th time's the charm? 2016-08-13 15:11:20 -05:00
David Solin
618f2fd111 String.format error was causing tests to unexpectedly fail. 2016-08-13 14:45:00 -05:00
David Solin
8503046302 Made the pkcs8-blanks file an actual pkcs8 file (so it doesn't fail the test). 2016-08-13 14:21:59 -05:00
David Solin
c6cde27e4b Oops, I shouldn't have encrypted the pkcs8 key file. 2016-08-13 11:00:00 -05:00
David Solin
113aa0aebd Updated KeyProviderUtil, KeyFormat, KeyProviderUtilTest and test resources to properly differentiate between PKCS5 and PKCS8 file formats. 2016-08-13 10:24:05 -05:00
David Solin
e7c50165c7 Removed GNUmake-related files from source control
Removed 3rd-party JARs from source control
Re-introduced original BouncyCastle-dependent PKCS5KeyFile class
Re-named non-BouncyCastle-dependent PKCS8KeyFile class to PKCS5KeyFile, since it really only supports PKCS5 formats anyway
2016-08-13 10:03:52 -05:00
David Solin
09616c4834 Tweaks 2016-08-12 20:03:08 -05:00
David Solin
df710d8dc9 Added getFingerprint method to SSH Known Host entry API.
Removed BouncyCastle dependency for public key authentication.
2016-08-12 19:58:09 -05:00
David Solin
df82774ea3 Added resources and GNUmake files.
Updated StreamCopier class to make it compatible with older SLF4J versions.
2016-08-10 21:45:30 -05:00
Jeroen van Erp
caa6cca665 Updated README for 0.17.2 and upcoming 0.17.3 release 2016-07-19 11:36:03 +02:00
Jeroen van Erp
9a5ccefb5d Removed dependency on net.i2p.crypto.eddsa.math classes (Fixes #255) 2016-07-19 11:22:13 +02:00
117 changed files with 2820 additions and 1177 deletions

5
.gitignore vendored
View File

@@ -12,6 +12,9 @@
# Output dirs
target/
build/
docs/
.gradle/
sshj.jar
# MacOS X
.DS_Store

View File

@@ -1,2 +1,5 @@
language: java
sudo: false
jdk:
- oraclejdk7
- oraclejdk8

View File

@@ -1,10 +1,11 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.17.2
: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"]
image:https://api.codacy.com/project/badge/Grade/14a0a316bb9149739b5ea26dbfa8da8a["Codacy code quality", link="https://www.codacy.com/app/jeroen_2/sshj?utm_source=github.com&utm_medium=referral&utm_content=hierynomus/sshj&utm_campaign=Badge_Grade"]
image:https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj/badge.svg["Maven Central",link="https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj"]
image:https://javadoc-emblem.rhcloud.com/doc/com.hierynomus/sshj/badge.svg["Javadoc",link="http://www.javadoc.io/doc/com.hierynomus/sshj"]
@@ -98,6 +99,20 @@ Google Group: http://groups.google.com/group/sshj-users
Fork away!
== Release history
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
* Merged https://github.com/hierynomus/sshj/pulls/271[#271]: Load known_hosts without requiring BouncyCastle
* Merged https://github.com/hierynomus/sshj/pulls/269[#269]: Brought back Java6 support by popular demand
* Merged https://github.com/hierynomus/sshj/pulls/267[#267]: Added support for per connection logging (Fixes https://github.com/hierynomus/sshj/issues/264[#264])
* Merged https://github.com/hierynomus/sshj/pulls/262[#262], https://github.com/hierynomus/sshj/pulls/265[#265] and https://github.com/hierynomus/sshj/pulls/266[#266]: Added PKCS5 key file support
* Fixed toString of sftp FileAttributes (Fixes https://github.com/hierynomus/sshj/pulls/258[#258])
* Fixed https://github.com/hierynomus/sshj/issues/255[#255]: No longer depending on 'privately marked' classes in `net.i2p.crypto.eddsa.math` package, fixes OSGI dependencies
SSHJ 0.17.2 (2016-07-07)::
* Treating SSH Server identification line ending in '\n' instead of '\r\n' leniently.
SSHJ 0.17.1 (2016-07-06)::
* Improved parsing of the SSH Server identification. Too long header lines now no longer break the protocol.
SSHJ 0.17.0 (2016-07-05)::

View File

@@ -1,211 +1,218 @@
import java.text.SimpleDateFormat
plugins {
id "java"
id "groovy"
id "maven"
id "idea"
id "signing"
id "osgi"
id "org.ajoberstar.release-opinion" version "1.4.2"
id "com.github.hierynomus.license" version "0.12.1"
id "java"
id "groovy"
id "osgi"
id "maven-publish"
id "org.ajoberstar.release-opinion" version "1.4.2"
id "com.github.hierynomus.license" version "0.12.1"
id "com.jfrog.bintray" version "1.7"
id 'ru.vyarus.pom' version '1.0.3'
id 'ru.vyarus.github-info' version '1.1.0'
}
group = "com.hierynomus"
defaultTasks "build"
repositories {
mavenCentral()
mavenCentral()
}
sourceCompatibility = 1.7
targetCompatibility = 1.7
sourceCompatibility = 1.6
targetCompatibility = 1.6
configurations.compile.transitive = false
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
license {
mapping {
java = 'SLASHSTAR_STYLE'
}
header rootProject.file('LICENSE_HEADER')
strictCheck true
}
release {
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
}
test {
testLogging {
exceptionFormat = 'full'
}
include "**/*Test.*"
include "**/*Spec.*"
if (!project.hasProperty("allTests")) {
useJUnit {
excludeCategories 'com.hierynomus.sshj.test.SlowTests'
excludeCategories 'com.hierynomus.sshj.test.KnownFailingTests'
}
}
afterSuite { descriptor, result ->
if (descriptor.className != null) {
def indicator = "\u001B[32m✓\u001b[0m"
if (result.failedTestCount > 0) {
indicator = "\u001B[31m✘\u001b[0m"
}
logger.lifecycle("$indicator Test ${descriptor.name}; Executed: ${result.testCount}/\u001B[32m${result.successfulTestCount}\u001B[0m/\u001B[31m${result.failedTestCount}\u001B[0m")
}
}
}
def bouncycastleVersion = "1.51"
dependencies {
compile "org.slf4j:slf4j-api:1.7.7"
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
compile "com.jcraft:jzlib:1.1.3"
compile "org.slf4j:slf4j-api:1.7.7"
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
compile "com.jcraft:jzlib:1.1.3"
compile "net.vrallev.ecc:ecc-25519-java:1.0.1"
compile "net.i2p.crypto:eddsa:0.1.0"
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"
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'
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"
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'
}
jar {
manifest {
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", "javax.crypto*"
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", "*"
instruction "Export-Package", "net.schmizz.*"
}
license {
header rootProject.file('LICENSE_HEADER')
strictCheck true
mapping {
java = 'SLASHSTAR_STYLE'
}
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java'])
}
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
manifest {
attributes(
// Add the needed OSGI attributes
"Bundle-ManifestVersion": "2",
"Bundle-Name": "${project.jar.manifest.name} Source",
"Bundle-Version": project.jar.manifest.version,
"Eclipse-SourceBundle": "${project.jar.manifest.symbolicName};version=\"${project.jar.manifest.version}\";roots:=\".\"",
"Bundle-SymbolicName": "${project.jar.manifest.symbolicName}.source"
)
}
}
artifacts {
archives javadocJar, sourcesJar
}
signing {
required { !version.toString().contains("SNAPSHOT") && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
release {
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
}
// This disables the pedantic doclint feature of JDK8
if (JavaVersion.current().isJava8Compatible()) {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
uploadArchives {
if (project.hasProperty('sonatypeUsername')) {
repositories.mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
task writeSshjVersionProperties << {
project.file("${project.buildDir}/resources/main").mkdirs()
project.file("${project.buildDir}/resources/main/sshj.properties").withWriter { w ->
w.append("sshj.version=${version}")
}
}
configuration = configurations.archives
jar.dependsOn writeSshjVersionProperties
repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') {
authentication(userName: sonatypeUsername, password: sonatypePassword)
}
snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') {
authentication(userName: sonatypeUsername, password: sonatypePassword)
}
jar {
manifest {
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", "javax.crypto*"
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", "*"
instruction "Export-Package", "net.schmizz.*"
}
}
pom.project {
name "sshj"
description "SSHv2 library for Java"
url "https://github.com/hierynomus/sshj"
inceptionYear "2009"
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}
issueManagement {
system "github"
url "https://github.com/hierynomus/sshj/issues"
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
manifest {
attributes(
// Add the needed OSGI attributes
"Bundle-ManifestVersion": "2",
"Bundle-Name": "${project.jar.manifest.name} Source",
"Bundle-Version": project.jar.manifest.version,
"Eclipse-SourceBundle": "${project.jar.manifest.symbolicName};version=\"${project.jar.manifest.version}\";roots:=\".\"",
"Bundle-SymbolicName": "${project.jar.manifest.symbolicName}.source"
)
}
}
scm {
connection "scm:git:git://github.com/hierynomus/sshj.git"
developerConnection "scm:git:git@github.com:hierynomus/sshj.git"
url "https://github.com/hierynomus/sshj.git"
}
tasks.withType(Test) {
testLogging {
exceptionFormat = 'full'
}
include "**/*Test.*"
include "**/*Spec.*"
if (!project.hasProperty("allTests")) {
useJUnit {
excludeCategories 'com.hierynomus.sshj.test.SlowTests'
excludeCategories 'com.hierynomus.sshj.test.KnownFailingTests'
}
}
licenses {
license {
name "Apache 2"
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
distribution "repo"
}
}
afterSuite { descriptor, result ->
if (descriptor.className != null) {
def indicator = "\u001B[32m✓\u001b[0m"
if (result.failedTestCount > 0) {
indicator = "\u001B[31m✘\u001b[0m"
}
logger.lifecycle("$indicator Test ${descriptor.name}; Executed: ${result.testCount}/\u001B[32m${result.successfulTestCount}\u001B[0m/\u001B[31m${result.failedTestCount}\u001B[0m")
}
}
}
developers {
developer {
id "hierynomus"
name "Jeroen van Erp"
email "jeroen@javadude.nl"
roles {
role "Lead developer"
}
}
developer {
id "shikhar"
name "Shikhar Bhushan"
email "shikhar@schmizz.net"
url "http://schmizz.net"
roles {
role "Previous lead developer"
}
}
developer {
id "iterate"
name "David Kocher"
email "dkocher@iterate.ch"
organization "iterage GmbH"
organizationUrl "https://iterate.ch"
roles {
role "Developer"
}
}
}
}
project.tasks.compileGroovy.onlyIf { false }
github {
user 'hierynomus'
license 'Apache'
}
pom {
description "SSHv2 library for Java"
url "https://github.com/hierynomus/sshj"
inceptionYear "2009"
developers {
developer {
id "hierynomus"
name "Jeroen van Erp"
email "jeroen@javadude.nl"
roles {
role "Lead developer"
}
}
developer {
id "shikhar"
name "Shikhar Bhushan"
email "shikhar@schmizz.net"
url "http://schmizz.net"
roles {
role "Previous lead developer"
}
}
developer {
id "iterate"
name "David Kocher"
email "dkocher@iterate.ch"
organization "iterage GmbH"
organizationUrl "https://iterate.ch"
roles {
role "Developer"
}
}
}
}
publishing.publications {
Sshj(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
}
}
if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey")) {
bintray {
user = project.property("bintrayUsername")
key = project.property("bintrayApiKey")
publish = true
publications = ["Sshj"]
pkg {
repo = "maven"
name = project.name
licenses = ["Apache-2.0"]
vcsUrl = "https://github.com/hierynomus/sshj.git"
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
githubRepo = "hierynomus/sshj"
version {
name = project.version.toString()
vcsTag = "v${project.version}"
released = new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ').format(new Date())
gpg {
sign = true
passphrase = project.property("signing.password")
}
mavenCentralSync {
sync = true
user = project.property("sonatypeUsername")
password = project.property("sonatypePassword")
close = 1
}
}
}
}
}
tasks.compileGroovy.onlyIf { false }
tasks.release.dependsOn 'build', 'uploadArchives'
project.tasks.release.dependsOn([project.tasks.build, project.tasks.bintrayUpload])

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.0</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.18.0</version>
</dependency>
</dependencies>

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

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-all.zip

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

@@ -17,20 +17,25 @@ package com.hierynomus.sshj.transport;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ByteArrayUtils;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Arrays;
public class IdentificationStringParser {
private static final Logger logger = LoggerFactory.getLogger(IdentificationStringParser.class);
private final Logger log;
private final Buffer.PlainBuffer buffer;
private byte[] EXPECTED_START_BYTES = new byte[] {'S', 'S', 'H', '-'};
public IdentificationStringParser(Buffer.PlainBuffer buffer) {
this(buffer, LoggerFactory.DEFAULT);
}
public IdentificationStringParser(Buffer.PlainBuffer buffer, LoggerFactory loggerFactory) {
this.log = loggerFactory.getLogger(IdentificationStringParser.class);
this.buffer = buffer;
}
@@ -65,16 +70,16 @@ public class IdentificationStringParser {
byte[] bytes = new byte[lineBuffer.available()];
lineBuffer.readRawBytes(bytes);
if (bytes.length > 255) {
logger.error("Incorrect identification String received, line was longer than expected: {}", new String(bytes));
logger.error("Just for good measure, bytes were: {}", ByteArrayUtils.printHex(bytes, 0, bytes.length));
log.error("Incorrect identification String received, line was longer than expected: {}", new String(bytes));
log.error("Just for good measure, bytes were: {}", ByteArrayUtils.printHex(bytes, 0, bytes.length));
throw new TransportException("Incorrect identification: line too long: " + ByteArrayUtils.printHex(bytes, 0, bytes.length));
}
if (bytes[bytes.length - 2] != '\r') {
String ident = new String(bytes, 0, bytes.length - 1);
logger.warn("Server identification has bad line ending, was expecting a '\\r\\n' however got: '{}' (hex: {})", (char) (bytes[bytes.length - 2] & 0xFF), Integer.toHexString(bytes[bytes.length - 2] & 0xFF));
logger.warn("Will treat the identification of this server '{}' leniently", ident);
log.warn("Server identification has bad line ending, was expecting a '\\r\\n' however got: '{}' (hex: {})", (char) (bytes[bytes.length - 2] & 0xFF), Integer.toHexString(bytes[bytes.length - 2] & 0xFF));
log.warn("Will treat the identification of this server '{}' leniently", ident);
return ident;
// logger.error("Data received up til here was: {}", new String(bytes));
// log.error("Data received up til here was: {}", new String(bytes));
// throw new TransportException("Incorrect identification: bad line ending: " + ByteArrayUtils.toHex(bytes, 0, bytes.length));
}

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

@@ -0,0 +1,918 @@
/* Ported from C to Java by Dmitry Skiba [sahn0], 23/02/08.
* Original: http://cds.xs4all.nl:8081/ecdh/
*/
/* Generic 64-bit integer implementation of Curve25519 ECDH
* Written by Matthijs van Duin, 200608242056
* Public domain.
*
* Based on work by Daniel J Bernstein, http://cr.yp.to/ecdh.html
*/
package djb;
public class Curve25519 {
/* key size */
public static final int KEY_SIZE = 32;
/* 0 */
public static final byte[] ZERO = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* the prime 2^255-19 */
public static final byte[] PRIME = {
(byte)237, (byte)255, (byte)255, (byte)255,
(byte)255, (byte)255, (byte)255, (byte)255,
(byte)255, (byte)255, (byte)255, (byte)255,
(byte)255, (byte)255, (byte)255, (byte)255,
(byte)255, (byte)255, (byte)255, (byte)255,
(byte)255, (byte)255, (byte)255, (byte)255,
(byte)255, (byte)255, (byte)255, (byte)255,
(byte)255, (byte)255, (byte)255, (byte)127
};
/* group order (a prime near 2^252+2^124) */
public static final byte[] ORDER = {
(byte)237, (byte)211, (byte)245, (byte)92,
(byte)26, (byte)99, (byte)18, (byte)88,
(byte)214, (byte)156, (byte)247, (byte)162,
(byte)222, (byte)249, (byte)222, (byte)20,
(byte)0, (byte)0, (byte)0, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)16
};
/********* KEY AGREEMENT *********/
/* Private key clamping
* k [out] your private key for key agreement
* k [in] 32 random bytes
*/
public static final void clamp(byte[] k) {
k[31] &= 0x7F;
k[31] |= 0x40;
k[ 0] &= 0xF8;
}
/* Key-pair generation
* P [out] your public key
* s [out] your private key for signing
* k [out] your private key for key agreement
* k [in] 32 random bytes
* s may be NULL if you don't care
*
* WARNING: if s is not NULL, this function has data-dependent timing */
public static final void keygen(byte[] P, byte[] s, byte[] k) {
clamp(k);
core(P, s, k, null);
}
/* Key agreement
* Z [out] shared secret (needs hashing before use)
* k [in] your private key for key agreement
* P [in] peer's public key
*/
public static final void curve(byte[] Z, byte[] k, byte[] P) {
core(Z, null, k, P);
}
/********* DIGITAL SIGNATURES *********/
/* deterministic EC-KCDSA
*
* s is the private key for signing
* P is the corresponding public key
* Z is the context data (signer public key or certificate, etc)
*
* signing:
*
* m = hash(Z, message)
* x = hash(m, s)
* keygen25519(Y, NULL, x);
* r = hash(Y);
* h = m XOR r
* sign25519(v, h, x, s);
*
* output (v,r) as the signature
*
* verification:
*
* m = hash(Z, message);
* h = m XOR r
* verify25519(Y, v, h, P)
*
* confirm r == hash(Y)
*
* It would seem to me that it would be simpler to have the signer directly do
* h = hash(m, Y) and send that to the recipient instead of r, who can verify
* the signature by checking h == hash(m, Y). If there are any problems with
* such a scheme, please let me know.
*
* Also, EC-KCDSA (like most DS algorithms) picks x random, which is a waste of
* perfectly good entropy, but does allow Y to be calculated in advance of (or
* parallel to) hashing the message.
*/
/* Signature generation primitive, calculates (x-h)s mod q
* v [out] signature value
* h [in] signature hash (of message, signature pub key, and context data)
* x [in] signature private key
* s [in] private key for signing
* returns true on success, false on failure (use different x or h)
*/
public static final boolean sign(byte[] v, byte[] h, byte[] x, byte[] s) {
// v = (x - h) s mod q
int w, i;
byte[] h1 = new byte[32], x1 = new byte[32];
byte[] tmp1 = new byte[64];
byte[] tmp2 = new byte[64];
// Don't clobber the arguments, be nice!
cpy32(h1, h);
cpy32(x1, x);
// Reduce modulo group order
byte[] tmp3=new byte[32];
divmod(tmp3, h1, 32, ORDER, 32);
divmod(tmp3, x1, 32, ORDER, 32);
// v = x1 - h1
// If v is negative, add the group order to it to become positive.
// If v was already positive we don't have to worry about overflow
// when adding the order because v < ORDER and 2*ORDER < 2^256
mula_small(v, x1, 0, h1, 32, -1);
mula_small(v, v , 0, ORDER, 32, 1);
// tmp1 = (x-h)*s mod q
mula32(tmp1, v, s, 32, 1);
divmod(tmp2, tmp1, 64, ORDER, 32);
for (w = 0, i = 0; i < 32; i++)
w |= v[i] = tmp1[i];
return w != 0;
}
/* Signature verification primitive, calculates Y = vP + hG
* Y [out] signature public key
* v [in] signature value
* h [in] signature hash
* P [in] public key
*/
public static final void verify(byte[] Y, byte[] v, byte[] h, byte[] P) {
/* Y = v abs(P) + h G */
byte[] d=new byte[32];
long10[]
p=new long10[]{new long10(),new long10()},
s=new long10[]{new long10(),new long10()},
yx=new long10[]{new long10(),new long10(),new long10()},
yz=new long10[]{new long10(),new long10(),new long10()},
t1=new long10[]{new long10(),new long10(),new long10()},
t2=new long10[]{new long10(),new long10(),new long10()};
int vi = 0, hi = 0, di = 0, nvh=0, i, j, k;
/* set p[0] to G and p[1] to P */
set(p[0], 9);
unpack(p[1], P);
/* set s[0] to P+G and s[1] to P-G */
/* s[0] = (Py^2 + Gy^2 - 2 Py Gy)/(Px - Gx)^2 - Px - Gx - 486662 */
/* s[1] = (Py^2 + Gy^2 + 2 Py Gy)/(Px - Gx)^2 - Px - Gx - 486662 */
x_to_y2(t1[0], t2[0], p[1]); /* t2[0] = Py^2 */
sqrt(t1[0], t2[0]); /* t1[0] = Py or -Py */
j = is_negative(t1[0]); /* ... check which */
t2[0]._0 += 39420360; /* t2[0] = Py^2 + Gy^2 */
mul(t2[1], BASE_2Y, t1[0]);/* t2[1] = 2 Py Gy or -2 Py Gy */
sub(t1[j], t2[0], t2[1]); /* t1[0] = Py^2 + Gy^2 - 2 Py Gy */
add(t1[1-j], t2[0], t2[1]);/* t1[1] = Py^2 + Gy^2 + 2 Py Gy */
cpy(t2[0], p[1]); /* t2[0] = Px */
t2[0]._0 -= 9; /* t2[0] = Px - Gx */
sqr(t2[1], t2[0]); /* t2[1] = (Px - Gx)^2 */
recip(t2[0], t2[1], 0); /* t2[0] = 1/(Px - Gx)^2 */
mul(s[0], t1[0], t2[0]); /* s[0] = t1[0]/(Px - Gx)^2 */
sub(s[0], s[0], p[1]); /* s[0] = t1[0]/(Px - Gx)^2 - Px */
s[0]._0 -= 9 + 486662; /* s[0] = X(P+G) */
mul(s[1], t1[1], t2[0]); /* s[1] = t1[1]/(Px - Gx)^2 */
sub(s[1], s[1], p[1]); /* s[1] = t1[1]/(Px - Gx)^2 - Px */
s[1]._0 -= 9 + 486662; /* s[1] = X(P-G) */
mul_small(s[0], s[0], 1); /* reduce s[0] */
mul_small(s[1], s[1], 1); /* reduce s[1] */
/* prepare the chain */
for (i = 0; i < 32; i++) {
vi = (vi >> 8) ^ (v[i] & 0xFF) ^ ((v[i] & 0xFF) << 1);
hi = (hi >> 8) ^ (h[i] & 0xFF) ^ ((h[i] & 0xFF) << 1);
nvh = ~(vi ^ hi);
di = (nvh & (di & 0x80) >> 7) ^ vi;
di ^= nvh & (di & 0x01) << 1;
di ^= nvh & (di & 0x02) << 1;
di ^= nvh & (di & 0x04) << 1;
di ^= nvh & (di & 0x08) << 1;
di ^= nvh & (di & 0x10) << 1;
di ^= nvh & (di & 0x20) << 1;
di ^= nvh & (di & 0x40) << 1;
d[i] = (byte)di;
}
di = ((nvh & (di & 0x80) << 1) ^ vi) >> 8;
/* initialize state */
set(yx[0], 1);
cpy(yx[1], p[di]);
cpy(yx[2], s[0]);
set(yz[0], 0);
set(yz[1], 1);
set(yz[2], 1);
/* y[0] is (even)P + (even)G
* y[1] is (even)P + (odd)G if current d-bit is 0
* y[1] is (odd)P + (even)G if current d-bit is 1
* y[2] is (odd)P + (odd)G
*/
vi = 0;
hi = 0;
/* and go for it! */
for (i = 32; i--!=0; ) {
vi = (vi << 8) | (v[i] & 0xFF);
hi = (hi << 8) | (h[i] & 0xFF);
di = (di << 8) | (d[i] & 0xFF);
for (j = 8; j--!=0; ) {
mont_prep(t1[0], t2[0], yx[0], yz[0]);
mont_prep(t1[1], t2[1], yx[1], yz[1]);
mont_prep(t1[2], t2[2], yx[2], yz[2]);
k = ((vi ^ vi >> 1) >> j & 1)
+ ((hi ^ hi >> 1) >> j & 1);
mont_dbl(yx[2], yz[2], t1[k], t2[k], yx[0], yz[0]);
k = (di >> j & 2) ^ ((di >> j & 1) << 1);
mont_add(t1[1], t2[1], t1[k], t2[k], yx[1], yz[1],
p[di >> j & 1]);
mont_add(t1[2], t2[2], t1[0], t2[0], yx[2], yz[2],
s[((vi ^ hi) >> j & 2) >> 1]);
}
}
k = (vi & 1) + (hi & 1);
recip(t1[0], yz[k], 0);
mul(t1[1], yx[k], t1[0]);
pack(t1[1], Y);
}
///////////////////////////////////////////////////////////////////////////
/* sahn0:
* Using this class instead of long[10] to avoid bounds checks. */
private static final class long10 {
public long10() {}
public long10(
long _0, long _1, long _2, long _3, long _4,
long _5, long _6, long _7, long _8, long _9)
{
this._0=_0; this._1=_1; this._2=_2;
this._3=_3; this._4=_4; this._5=_5;
this._6=_6; this._7=_7; this._8=_8;
this._9=_9;
}
public long _0,_1,_2,_3,_4,_5,_6,_7,_8,_9;
}
/********************* radix 2^8 math *********************/
private static final void cpy32(byte[] d, byte[] s) {
int i;
for (i = 0; i < 32; i++)
d[i] = s[i];
}
/* p[m..n+m-1] = q[m..n+m-1] + z * x */
/* n is the size of x */
/* n+m is the size of p and q */
private static final int mula_small(byte[] p,byte[] q,int m,byte[] x,int n,int z) {
int v=0;
for (int i=0;i<n;++i) {
v+=(q[i+m] & 0xFF)+z*(x[i] & 0xFF);
p[i+m]=(byte)v;
v>>=8;
}
return v;
}
/* p += x * y * z where z is a small integer
* x is size 32, y is size t, p is size 32+t
* y is allowed to overlap with p+32 if you don't care about the upper half */
private static final int mula32(byte[] p, byte[] x, byte[] y, int t, int z) {
final int n = 31;
int w = 0;
int i = 0;
for (; i < t; i++) {
int zy = z * (y[i] & 0xFF);
w += mula_small(p, p, i, x, n, zy) +
(p[i+n] & 0xFF) + zy * (x[n] & 0xFF);
p[i+n] = (byte)w;
w >>= 8;
}
p[i+n] = (byte)(w + (p[i+n] & 0xFF));
return w >> 8;
}
/* divide r (size n) by d (size t), returning quotient q and remainder r
* quotient is size n-t+1, remainder is size t
* requires t > 0 && d[t-1] != 0
* requires that r[-1] and d[-1] are valid memory locations
* q may overlap with r+t */
private static final void divmod(byte[] q, byte[] r, int n, byte[] d, int t) {
int rn = 0;
int dt = ((d[t-1] & 0xFF) << 8);
if (t>1) {
dt |= (d[t-2] & 0xFF);
}
while (n-- >= t) {
int z = (rn << 16) | ((r[n] & 0xFF) << 8);
if (n>0) {
z |= (r[n-1] & 0xFF);
}
z/=dt;
rn += mula_small(r,r, n-t+1, d, t, -z);
q[n-t+1] = (byte)((z + rn) & 0xFF); /* rn is 0 or -1 (underflow) */
mula_small(r,r, n-t+1, d, t, -rn);
rn = (r[n] & 0xFF);
r[n] = 0;
}
r[t-1] = (byte)rn;
}
private static final int numsize(byte[] x,int n) {
while (n--!=0 && x[n]==0)
;
return n+1;
}
/* Returns x if a contains the gcd, y if b.
* Also, the returned buffer contains the inverse of a mod b,
* as 32-byte signed.
* x and y must have 64 bytes space for temporary use.
* requires that a[-1] and b[-1] are valid memory locations */
private static final byte[] egcd32(byte[] x,byte[] y,byte[] a,byte[] b) {
int an, bn = 32, qn, i;
for (i = 0; i < 32; i++)
x[i] = y[i] = 0;
x[0] = 1;
an = numsize(a, 32);
if (an==0)
return y; /* division by zero */
byte[] temp=new byte[32];
while (true) {
qn = bn - an + 1;
divmod(temp, b, bn, a, an);
bn = numsize(b, bn);
if (bn==0)
return x;
mula32(y, x, temp, qn, -1);
qn = an - bn + 1;
divmod(temp, a, an, b, bn);
an = numsize(a, an);
if (an==0)
return y;
mula32(x, y, temp, qn, -1);
}
}
/********************* radix 2^25.5 GF(2^255-19) math *********************/
private static final int P25=33554431; /* (1 << 25) - 1 */
private static final int P26=67108863; /* (1 << 26) - 1 */
/* Convert to internal format from little-endian byte format */
private static final void unpack(long10 x,byte[] m) {
x._0 = ((m[0] & 0xFF)) | ((m[1] & 0xFF))<<8 |
(m[2] & 0xFF)<<16 | ((m[3] & 0xFF)& 3)<<24;
x._1 = ((m[3] & 0xFF)&~ 3)>>2 | (m[4] & 0xFF)<<6 |
(m[5] & 0xFF)<<14 | ((m[6] & 0xFF)& 7)<<22;
x._2 = ((m[6] & 0xFF)&~ 7)>>3 | (m[7] & 0xFF)<<5 |
(m[8] & 0xFF)<<13 | ((m[9] & 0xFF)&31)<<21;
x._3 = ((m[9] & 0xFF)&~31)>>5 | (m[10] & 0xFF)<<3 |
(m[11] & 0xFF)<<11 | ((m[12] & 0xFF)&63)<<19;
x._4 = ((m[12] & 0xFF)&~63)>>6 | (m[13] & 0xFF)<<2 |
(m[14] & 0xFF)<<10 | (m[15] & 0xFF) <<18;
x._5 = (m[16] & 0xFF) | (m[17] & 0xFF)<<8 |
(m[18] & 0xFF)<<16 | ((m[19] & 0xFF)& 1)<<24;
x._6 = ((m[19] & 0xFF)&~ 1)>>1 | (m[20] & 0xFF)<<7 |
(m[21] & 0xFF)<<15 | ((m[22] & 0xFF)& 7)<<23;
x._7 = ((m[22] & 0xFF)&~ 7)>>3 | (m[23] & 0xFF)<<5 |
(m[24] & 0xFF)<<13 | ((m[25] & 0xFF)&15)<<21;
x._8 = ((m[25] & 0xFF)&~15)>>4 | (m[26] & 0xFF)<<4 |
(m[27] & 0xFF)<<12 | ((m[28] & 0xFF)&63)<<20;
x._9 = ((m[28] & 0xFF)&~63)>>6 | (m[29] & 0xFF)<<2 |
(m[30] & 0xFF)<<10 | (m[31] & 0xFF) <<18;
}
/* Check if reduced-form input >= 2^255-19 */
private static final boolean is_overflow(long10 x) {
return (
((x._0 > P26-19)) &&
((x._1 & x._3 & x._5 & x._7 & x._9) == P25) &&
((x._2 & x._4 & x._6 & x._8) == P26)
) || (x._9 > P25);
}
/* Convert from internal format to little-endian byte format. The
* number must be in a reduced form which is output by the following ops:
* unpack, mul, sqr
* set -- if input in range 0 .. P25
* If you're unsure if the number is reduced, first multiply it by 1. */
private static final void pack(long10 x,byte[] m) {
int ld = 0, ud = 0;
long t;
ld = (is_overflow(x)?1:0) - ((x._9 < 0)?1:0);
ud = ld * -(P25+1);
ld *= 19;
t = ld + x._0 + (x._1 << 26);
m[ 0] = (byte)t;
m[ 1] = (byte)(t >> 8);
m[ 2] = (byte)(t >> 16);
m[ 3] = (byte)(t >> 24);
t = (t >> 32) + (x._2 << 19);
m[ 4] = (byte)t;
m[ 5] = (byte)(t >> 8);
m[ 6] = (byte)(t >> 16);
m[ 7] = (byte)(t >> 24);
t = (t >> 32) + (x._3 << 13);
m[ 8] = (byte)t;
m[ 9] = (byte)(t >> 8);
m[10] = (byte)(t >> 16);
m[11] = (byte)(t >> 24);
t = (t >> 32) + (x._4 << 6);
m[12] = (byte)t;
m[13] = (byte)(t >> 8);
m[14] = (byte)(t >> 16);
m[15] = (byte)(t >> 24);
t = (t >> 32) + x._5 + (x._6 << 25);
m[16] = (byte)t;
m[17] = (byte)(t >> 8);
m[18] = (byte)(t >> 16);
m[19] = (byte)(t >> 24);
t = (t >> 32) + (x._7 << 19);
m[20] = (byte)t;
m[21] = (byte)(t >> 8);
m[22] = (byte)(t >> 16);
m[23] = (byte)(t >> 24);
t = (t >> 32) + (x._8 << 12);
m[24] = (byte)t;
m[25] = (byte)(t >> 8);
m[26] = (byte)(t >> 16);
m[27] = (byte)(t >> 24);
t = (t >> 32) + ((x._9 + ud) << 6);
m[28] = (byte)t;
m[29] = (byte)(t >> 8);
m[30] = (byte)(t >> 16);
m[31] = (byte)(t >> 24);
}
/* Copy a number */
private static final void cpy(long10 out, long10 in) {
out._0=in._0; out._1=in._1;
out._2=in._2; out._3=in._3;
out._4=in._4; out._5=in._5;
out._6=in._6; out._7=in._7;
out._8=in._8; out._9=in._9;
}
/* Set a number to value, which must be in range -185861411 .. 185861411 */
private static final void set(long10 out, int in) {
out._0=in; out._1=0;
out._2=0; out._3=0;
out._4=0; out._5=0;
out._6=0; out._7=0;
out._8=0; out._9=0;
}
/* Add/subtract two numbers. The inputs must be in reduced form, and the
* output isn't, so to do another addition or subtraction on the output,
* first multiply it by one to reduce it. */
private static final void add(long10 xy, long10 x, long10 y) {
xy._0 = x._0 + y._0; xy._1 = x._1 + y._1;
xy._2 = x._2 + y._2; xy._3 = x._3 + y._3;
xy._4 = x._4 + y._4; xy._5 = x._5 + y._5;
xy._6 = x._6 + y._6; xy._7 = x._7 + y._7;
xy._8 = x._8 + y._8; xy._9 = x._9 + y._9;
}
private static final void sub(long10 xy, long10 x, long10 y) {
xy._0 = x._0 - y._0; xy._1 = x._1 - y._1;
xy._2 = x._2 - y._2; xy._3 = x._3 - y._3;
xy._4 = x._4 - y._4; xy._5 = x._5 - y._5;
xy._6 = x._6 - y._6; xy._7 = x._7 - y._7;
xy._8 = x._8 - y._8; xy._9 = x._9 - y._9;
}
/* Multiply a number by a small integer in range -185861411 .. 185861411.
* The output is in reduced form, the input x need not be. x and xy may point
* to the same buffer. */
private static final long10 mul_small(long10 xy, long10 x, long y) {
long t;
t = (x._8*y);
xy._8 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x._9*y);
xy._9 = (t & ((1 << 25) - 1));
t = 19 * (t >> 25) + (x._0*y);
xy._0 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x._1*y);
xy._1 = (t & ((1 << 25) - 1));
t = (t >> 25) + (x._2*y);
xy._2 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x._3*y);
xy._3 = (t & ((1 << 25) - 1));
t = (t >> 25) + (x._4*y);
xy._4 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x._5*y);
xy._5 = (t & ((1 << 25) - 1));
t = (t >> 25) + (x._6*y);
xy._6 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x._7*y);
xy._7 = (t & ((1 << 25) - 1));
t = (t >> 25) + xy._8;
xy._8 = (t & ((1 << 26) - 1));
xy._9 += (t >> 26);
return xy;
}
/* Multiply two numbers. The output is in reduced form, the inputs need not
* be. */
private static final long10 mul(long10 xy, long10 x, long10 y) {
/* sahn0:
* Using local variables to avoid class access.
* This seem to improve performance a bit...
*/
long
x_0=x._0,x_1=x._1,x_2=x._2,x_3=x._3,x_4=x._4,
x_5=x._5,x_6=x._6,x_7=x._7,x_8=x._8,x_9=x._9;
long
y_0=y._0,y_1=y._1,y_2=y._2,y_3=y._3,y_4=y._4,
y_5=y._5,y_6=y._6,y_7=y._7,y_8=y._8,y_9=y._9;
long t;
t = (x_0*y_8) + (x_2*y_6) + (x_4*y_4) + (x_6*y_2) +
(x_8*y_0) + 2 * ((x_1*y_7) + (x_3*y_5) +
(x_5*y_3) + (x_7*y_1)) + 38 *
(x_9*y_9);
xy._8 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x_0*y_9) + (x_1*y_8) + (x_2*y_7) +
(x_3*y_6) + (x_4*y_5) + (x_5*y_4) +
(x_6*y_3) + (x_7*y_2) + (x_8*y_1) +
(x_9*y_0);
xy._9 = (t & ((1 << 25) - 1));
t = (x_0*y_0) + 19 * ((t >> 25) + (x_2*y_8) + (x_4*y_6)
+ (x_6*y_4) + (x_8*y_2)) + 38 *
((x_1*y_9) + (x_3*y_7) + (x_5*y_5) +
(x_7*y_3) + (x_9*y_1));
xy._0 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x_0*y_1) + (x_1*y_0) + 19 * ((x_2*y_9)
+ (x_3*y_8) + (x_4*y_7) + (x_5*y_6) +
(x_6*y_5) + (x_7*y_4) + (x_8*y_3) +
(x_9*y_2));
xy._1 = (t & ((1 << 25) - 1));
t = (t >> 25) + (x_0*y_2) + (x_2*y_0) + 19 * ((x_4*y_8)
+ (x_6*y_6) + (x_8*y_4)) + 2 * (x_1*y_1)
+ 38 * ((x_3*y_9) + (x_5*y_7) +
(x_7*y_5) + (x_9*y_3));
xy._2 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x_0*y_3) + (x_1*y_2) + (x_2*y_1) +
(x_3*y_0) + 19 * ((x_4*y_9) + (x_5*y_8) +
(x_6*y_7) + (x_7*y_6) +
(x_8*y_5) + (x_9*y_4));
xy._3 = (t & ((1 << 25) - 1));
t = (t >> 25) + (x_0*y_4) + (x_2*y_2) + (x_4*y_0) + 19 *
((x_6*y_8) + (x_8*y_6)) + 2 * ((x_1*y_3) +
(x_3*y_1)) + 38 *
((x_5*y_9) + (x_7*y_7) + (x_9*y_5));
xy._4 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x_0*y_5) + (x_1*y_4) + (x_2*y_3) +
(x_3*y_2) + (x_4*y_1) + (x_5*y_0) + 19 *
((x_6*y_9) + (x_7*y_8) + (x_8*y_7) +
(x_9*y_6));
xy._5 = (t & ((1 << 25) - 1));
t = (t >> 25) + (x_0*y_6) + (x_2*y_4) + (x_4*y_2) +
(x_6*y_0) + 19 * (x_8*y_8) + 2 * ((x_1*y_5) +
(x_3*y_3) + (x_5*y_1)) + 38 *
((x_7*y_9) + (x_9*y_7));
xy._6 = (t & ((1 << 26) - 1));
t = (t >> 26) + (x_0*y_7) + (x_1*y_6) + (x_2*y_5) +
(x_3*y_4) + (x_4*y_3) + (x_5*y_2) +
(x_6*y_1) + (x_7*y_0) + 19 * ((x_8*y_9) +
(x_9*y_8));
xy._7 = (t & ((1 << 25) - 1));
t = (t >> 25) + xy._8;
xy._8 = (t & ((1 << 26) - 1));
xy._9 += (t >> 26);
return xy;
}
/* Square a number. Optimization of mul25519(x2, x, x) */
private static final long10 sqr(long10 x2, long10 x) {
long
x_0=x._0,x_1=x._1,x_2=x._2,x_3=x._3,x_4=x._4,
x_5=x._5,x_6=x._6,x_7=x._7,x_8=x._8,x_9=x._9;
long t;
t = (x_4*x_4) + 2 * ((x_0*x_8) + (x_2*x_6)) + 38 *
(x_9*x_9) + 4 * ((x_1*x_7) + (x_3*x_5));
x2._8 = (t & ((1 << 26) - 1));
t = (t >> 26) + 2 * ((x_0*x_9) + (x_1*x_8) + (x_2*x_7) +
(x_3*x_6) + (x_4*x_5));
x2._9 = (t & ((1 << 25) - 1));
t = 19 * (t >> 25) + (x_0*x_0) + 38 * ((x_2*x_8) +
(x_4*x_6) + (x_5*x_5)) + 76 * ((x_1*x_9)
+ (x_3*x_7));
x2._0 = (t & ((1 << 26) - 1));
t = (t >> 26) + 2 * (x_0*x_1) + 38 * ((x_2*x_9) +
(x_3*x_8) + (x_4*x_7) + (x_5*x_6));
x2._1 = (t & ((1 << 25) - 1));
t = (t >> 25) + 19 * (x_6*x_6) + 2 * ((x_0*x_2) +
(x_1*x_1)) + 38 * (x_4*x_8) + 76 *
((x_3*x_9) + (x_5*x_7));
x2._2 = (t & ((1 << 26) - 1));
t = (t >> 26) + 2 * ((x_0*x_3) + (x_1*x_2)) + 38 *
((x_4*x_9) + (x_5*x_8) + (x_6*x_7));
x2._3 = (t & ((1 << 25) - 1));
t = (t >> 25) + (x_2*x_2) + 2 * (x_0*x_4) + 38 *
((x_6*x_8) + (x_7*x_7)) + 4 * (x_1*x_3) + 76 *
(x_5*x_9);
x2._4 = (t & ((1 << 26) - 1));
t = (t >> 26) + 2 * ((x_0*x_5) + (x_1*x_4) + (x_2*x_3))
+ 38 * ((x_6*x_9) + (x_7*x_8));
x2._5 = (t & ((1 << 25) - 1));
t = (t >> 25) + 19 * (x_8*x_8) + 2 * ((x_0*x_6) +
(x_2*x_4) + (x_3*x_3)) + 4 * (x_1*x_5) +
76 * (x_7*x_9);
x2._6 = (t & ((1 << 26) - 1));
t = (t >> 26) + 2 * ((x_0*x_7) + (x_1*x_6) + (x_2*x_5) +
(x_3*x_4)) + 38 * (x_8*x_9);
x2._7 = (t & ((1 << 25) - 1));
t = (t >> 25) + x2._8;
x2._8 = (t & ((1 << 26) - 1));
x2._9 += (t >> 26);
return x2;
}
/* Calculates a reciprocal. The output is in reduced form, the inputs need not
* be. Simply calculates y = x^(p-2) so it's not too fast. */
/* When sqrtassist is true, it instead calculates y = x^((p-5)/8) */
private static final void recip(long10 y, long10 x, int sqrtassist) {
long10
t0=new long10(),
t1=new long10(),
t2=new long10(),
t3=new long10(),
t4=new long10();
int i;
/* the chain for x^(2^255-21) is straight from djb's implementation */
sqr(t1, x); /* 2 == 2 * 1 */
sqr(t2, t1); /* 4 == 2 * 2 */
sqr(t0, t2); /* 8 == 2 * 4 */
mul(t2, t0, x); /* 9 == 8 + 1 */
mul(t0, t2, t1); /* 11 == 9 + 2 */
sqr(t1, t0); /* 22 == 2 * 11 */
mul(t3, t1, t2); /* 31 == 22 + 9
== 2^5 - 2^0 */
sqr(t1, t3); /* 2^6 - 2^1 */
sqr(t2, t1); /* 2^7 - 2^2 */
sqr(t1, t2); /* 2^8 - 2^3 */
sqr(t2, t1); /* 2^9 - 2^4 */
sqr(t1, t2); /* 2^10 - 2^5 */
mul(t2, t1, t3); /* 2^10 - 2^0 */
sqr(t1, t2); /* 2^11 - 2^1 */
sqr(t3, t1); /* 2^12 - 2^2 */
for (i = 1; i < 5; i++) {
sqr(t1, t3);
sqr(t3, t1);
} /* t3 */ /* 2^20 - 2^10 */
mul(t1, t3, t2); /* 2^20 - 2^0 */
sqr(t3, t1); /* 2^21 - 2^1 */
sqr(t4, t3); /* 2^22 - 2^2 */
for (i = 1; i < 10; i++) {
sqr(t3, t4);
sqr(t4, t3);
} /* t4 */ /* 2^40 - 2^20 */
mul(t3, t4, t1); /* 2^40 - 2^0 */
for (i = 0; i < 5; i++) {
sqr(t1, t3);
sqr(t3, t1);
} /* t3 */ /* 2^50 - 2^10 */
mul(t1, t3, t2); /* 2^50 - 2^0 */
sqr(t2, t1); /* 2^51 - 2^1 */
sqr(t3, t2); /* 2^52 - 2^2 */
for (i = 1; i < 25; i++) {
sqr(t2, t3);
sqr(t3, t2);
} /* t3 */ /* 2^100 - 2^50 */
mul(t2, t3, t1); /* 2^100 - 2^0 */
sqr(t3, t2); /* 2^101 - 2^1 */
sqr(t4, t3); /* 2^102 - 2^2 */
for (i = 1; i < 50; i++) {
sqr(t3, t4);
sqr(t4, t3);
} /* t4 */ /* 2^200 - 2^100 */
mul(t3, t4, t2); /* 2^200 - 2^0 */
for (i = 0; i < 25; i++) {
sqr(t4, t3);
sqr(t3, t4);
} /* t3 */ /* 2^250 - 2^50 */
mul(t2, t3, t1); /* 2^250 - 2^0 */
sqr(t1, t2); /* 2^251 - 2^1 */
sqr(t2, t1); /* 2^252 - 2^2 */
if (sqrtassist!=0) {
mul(y, x, t2); /* 2^252 - 3 */
} else {
sqr(t1, t2); /* 2^253 - 2^3 */
sqr(t2, t1); /* 2^254 - 2^4 */
sqr(t1, t2); /* 2^255 - 2^5 */
mul(y, t1, t0); /* 2^255 - 21 */
}
}
/* checks if x is "negative", requires reduced input */
private static final int is_negative(long10 x) {
return (int)(((is_overflow(x) || (x._9 < 0))?1:0) ^ (x._0 & 1));
}
/* a square root */
private static final void sqrt(long10 x, long10 u) {
long10 v=new long10(), t1=new long10(), t2=new long10();
add(t1, u, u); /* t1 = 2u */
recip(v, t1, 1); /* v = (2u)^((p-5)/8) */
sqr(x, v); /* x = v^2 */
mul(t2, t1, x); /* t2 = 2uv^2 */
t2._0--; /* t2 = 2uv^2-1 */
mul(t1, v, t2); /* t1 = v(2uv^2-1) */
mul(x, u, t1); /* x = uv(2uv^2-1) */
}
/********************* Elliptic curve *********************/
/* y^2 = x^3 + 486662 x^2 + x over GF(2^255-19) */
/* t1 = ax + az
* t2 = ax - az */
private static final void mont_prep(long10 t1, long10 t2, long10 ax, long10 az) {
add(t1, ax, az);
sub(t2, ax, az);
}
/* A = P + Q where
* X(A) = ax/az
* X(P) = (t1+t2)/(t1-t2)
* X(Q) = (t3+t4)/(t3-t4)
* X(P-Q) = dx
* clobbers t1 and t2, preserves t3 and t4 */
private static final void mont_add(long10 t1, long10 t2, long10 t3, long10 t4,long10 ax, long10 az, long10 dx) {
mul(ax, t2, t3);
mul(az, t1, t4);
add(t1, ax, az);
sub(t2, ax, az);
sqr(ax, t1);
sqr(t1, t2);
mul(az, t1, dx);
}
/* B = 2 * Q where
* X(B) = bx/bz
* X(Q) = (t3+t4)/(t3-t4)
* clobbers t1 and t2, preserves t3 and t4 */
private static final void mont_dbl(long10 t1, long10 t2, long10 t3, long10 t4,long10 bx, long10 bz) {
sqr(t1, t3);
sqr(t2, t4);
mul(bx, t1, t2);
sub(t2, t1, t2);
mul_small(bz, t2, 121665);
add(t1, t1, bz);
mul(bz, t1, t2);
}
/* Y^2 = X^3 + 486662 X^2 + X
* t is a temporary */
private static final void x_to_y2(long10 t, long10 y2, long10 x) {
sqr(t, x);
mul_small(y2, x, 486662);
add(t, t, y2);
t._0++;
mul(y2, t, x);
}
/* P = kG and s = sign(P)/k */
private static final void core(byte[] Px, byte[] s, byte[] k, byte[] Gx) {
long10
dx=new long10(),
t1=new long10(),
t2=new long10(),
t3=new long10(),
t4=new long10();
long10[]
x=new long10[]{new long10(),new long10()},
z=new long10[]{new long10(),new long10()};
int i, j;
/* unpack the base */
if (Gx!=null)
unpack(dx, Gx);
else
set(dx, 9);
/* 0G = point-at-infinity */
set(x[0], 1);
set(z[0], 0);
/* 1G = G */
cpy(x[1], dx);
set(z[1], 1);
for (i = 32; i--!=0; ) {
if (i==0) {
i=0;
}
for (j = 8; j--!=0; ) {
/* swap arguments depending on bit */
int bit1 = (k[i] & 0xFF) >> j & 1;
int bit0 = ~(k[i] & 0xFF) >> j & 1;
long10 ax = x[bit0];
long10 az = z[bit0];
long10 bx = x[bit1];
long10 bz = z[bit1];
/* a' = a + b */
/* b' = 2 b */
mont_prep(t1, t2, ax, az);
mont_prep(t3, t4, bx, bz);
mont_add(t1, t2, t3, t4, ax, az, dx);
mont_dbl(t1, t2, t3, t4, bx, bz);
}
}
recip(t1, z[0], 0);
mul(dx, x[0], t1);
pack(dx, Px);
/* calculate s such that s abs(P) = G .. assumes G is std base point */
if (s!=null) {
x_to_y2(t2, t1, dx); /* t1 = Py^2 */
recip(t3, z[1], 0); /* where Q=P+G ... */
mul(t2, x[1], t3); /* t2 = Qx */
add(t2, t2, dx); /* t2 = Qx + Px */
t2._0 += 9 + 486662; /* t2 = Qx + Px + Gx + 486662 */
dx._0 -= 9; /* dx = Px - Gx */
sqr(t3, dx); /* t3 = (Px - Gx)^2 */
mul(dx, t2, t3); /* dx = t2 (Px - Gx)^2 */
sub(dx, dx, t1); /* dx = t2 (Px - Gx)^2 - Py^2 */
dx._0 -= 39420360; /* dx = t2 (Px - Gx)^2 - Py^2 - Gy^2 */
mul(t1, dx, BASE_R2Y); /* t1 = -Py */
if (is_negative(t1)!=0) /* sign is 1, so just copy */
cpy32(s, k);
else /* sign is -1, so negate */
mula_small(s, ORDER_TIMES_8, 0, k, 32, -1);
/* reduce s mod q
* (is this needed? do it just in case, it's fast anyway) */
//divmod((dstptr) t1, s, 32, order25519, 32);
/* take reciprocal of s mod q */
byte[] temp1=new byte[32];
byte[] temp2=new byte[64];
byte[] temp3=new byte[64];
cpy32(temp1, ORDER);
cpy32(s, egcd32(temp2, temp3, s, temp1));
if ((s[31] & 0x80)!=0)
mula_small(s, s, 0, ORDER, 32, 1);
}
}
/* smallest multiple of the order that's >= 2^255 */
private static final byte[] ORDER_TIMES_8 = {
(byte)104, (byte)159, (byte)174, (byte)231,
(byte)210, (byte)24, (byte)147, (byte)192,
(byte)178, (byte)230, (byte)188, (byte)23,
(byte)245, (byte)206, (byte)247, (byte)166,
(byte)0, (byte)0, (byte)0, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)128
};
/* constants 2Gy and 1/(2Gy) */
private static final long10 BASE_2Y = new long10(
39999547, 18689728, 59995525, 1648697, 57546132,
24010086, 19059592, 5425144, 63499247, 16420658
);
private static final long10 BASE_R2Y = new long10(
5744, 8160848, 4790893, 13779497, 35730846,
12541209, 49101323, 30047407, 40071253, 6226132
);
}

View File

@@ -18,6 +18,8 @@ package net.schmizz.concurrent;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import net.schmizz.sshj.common.LoggerFactory;
/**
* An event can be set, cleared, or awaited, similar to Python's {@code threading.event}. The key difference is that a
* waiter may be delivered an exception of parameterized type {@code T}.
@@ -42,8 +44,8 @@ public class Event<T extends Throwable> {
* @param name name of this event
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
*/
public Event(String name, ExceptionChainer<T> chainer) {
promise = new Promise<Object, T>(name, chainer);
public Event(String name, ExceptionChainer<T> chainer, LoggerFactory loggerFactory) {
promise = new Promise<Object, T>(name, chainer, loggerFactory);
}
/**
@@ -53,8 +55,8 @@ public class Event<T extends Throwable> {
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
* @param lock lock to use
*/
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
promise = new Promise<Object, T>(name, chainer, lock);
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock, LoggerFactory loggerFactory) {
promise = new Promise<Object, T>(name, chainer, lock, loggerFactory);
}
/** Sets this event to be {@code true}. Short for {@code set(true)}. */

View File

@@ -16,13 +16,14 @@
package net.schmizz.concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import net.schmizz.sshj.common.LoggerFactory;
/**
* Represents promised data of the parameterized type {@code V} and allows waiting on it. An exception may also be
* delivered to a waiter, and will be of the parameterized type {@code T}.
@@ -32,8 +33,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class Promise<V, T extends Throwable> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log;
private final String name;
private final ExceptionChainer<T> chainer;
private final ReentrantLock lock;
@@ -49,8 +49,8 @@ public class Promise<V, T extends Throwable> {
* @param name name of this promise
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
*/
public Promise(String name, ExceptionChainer<T> chainer) {
this(name, chainer, null);
public Promise(String name, ExceptionChainer<T> chainer, LoggerFactory loggerFactory) {
this(name, chainer, null, loggerFactory);
}
/**
@@ -60,10 +60,11 @@ public class Promise<V, T extends Throwable> {
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
* @param lock lock to use
*/
public Promise(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
public Promise(String name, ExceptionChainer<T> chainer, ReentrantLock lock, LoggerFactory loggerFactory) {
this.name = name;
this.chainer = chainer;
this.lock = lock == null ? new ReentrantLock() : lock;
this.log = loggerFactory.getLogger(getClass());
this.cond = this.lock.newCondition();
}

View File

@@ -22,14 +22,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class KeepAlive extends Thread {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Logger log;
protected final ConnectionImpl conn;
protected int keepAliveInterval = 0;
protected KeepAlive(ConnectionImpl conn, String name) {
this.conn = conn;
log = conn.getTransport().getConfig().getLoggerFactory().getLogger(getClass());
setName(name);
}

View File

@@ -22,14 +22,13 @@ import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** An abstract class for {@link Service} that implements common or default functionality. */
public abstract class AbstractService
implements Service {
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Logger log;
/** Assigned name of this service */
protected final String name;
@@ -39,6 +38,7 @@ public abstract class AbstractService
public AbstractService(String name, Transport trans) {
this.name = name;
this.trans = trans;
log = trans.getConfig().getLoggerFactory().getLogger(getClass());
}
@Override

View File

@@ -15,12 +15,24 @@
*/
package net.schmizz.sshj;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.signature.SignatureDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
public class AndroidConfig
extends DefaultConfig {
static {
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
}
// don't add ECDSA
protected void initSignatureFactories() {
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory());
}
@Override
protected void initRandomFactory(boolean ignored) {
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));

View File

@@ -17,6 +17,7 @@ package net.schmizz.sshj;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
@@ -175,4 +176,14 @@ public interface Config {
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
*/
void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent);
/**
* Sets the LoggerFactory to use.
*/
void setLoggerFactory(LoggerFactory loggerFactory);
/**
* @return The LoggerFactory the SSHClient will use.
*/
LoggerFactory getLoggerFactory();
}

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.cipher.Cipher;
@@ -45,6 +46,7 @@ public class ConfigImpl
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
private boolean waitForServerIdentBeforeSendingClientIdent = false;
private LoggerFactory loggerFactory;
@Override
public List<Factory.Named<Cipher>> getCipherFactories() {
@@ -169,4 +171,14 @@ public class ConfigImpl
public void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent) {
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
}
@Override
public LoggerFactory getLoggerFactory() {
return loggerFactory;
}
@Override
public void setLoggerFactory(LoggerFactory loggerFactory) {
this.loggerFactory = loggerFactory;
}
}

View File

@@ -20,42 +20,26 @@ import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.signature.SignatureDSA;
import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.cipher.AES128CBC;
import net.schmizz.sshj.transport.cipher.AES128CTR;
import net.schmizz.sshj.transport.cipher.AES192CBC;
import net.schmizz.sshj.transport.cipher.AES192CTR;
import net.schmizz.sshj.transport.cipher.AES256CBC;
import net.schmizz.sshj.transport.cipher.AES256CTR;
import net.schmizz.sshj.transport.cipher.BlowfishCBC;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.cipher.TripleDESCBC;
import net.schmizz.sshj.transport.cipher.*;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.kex.*;
import net.schmizz.sshj.transport.mac.HMACMD5;
import net.schmizz.sshj.transport.mac.HMACMD596;
import net.schmizz.sshj.transport.mac.HMACSHA1;
import net.schmizz.sshj.transport.mac.HMACSHA196;
import net.schmizz.sshj.transport.mac.HMACSHA2256;
import net.schmizz.sshj.transport.mac.HMACSHA2512;
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 com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.ByteLookupTable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
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
@@ -82,12 +66,11 @@ import java.util.List;
public class DefaultConfig
extends ConfigImpl {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String VERSION = "SSHJ_0_17_2";
private Logger log;
public DefaultConfig() {
setVersion(VERSION);
setLoggerFactory(LoggerFactory.DEFAULT);
setVersion(readVersionFromProperties());
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
initKeyExchangeFactories(bouncyCastleRegistered);
initRandomFactory(bouncyCastleRegistered);
@@ -99,8 +82,26 @@ public class DefaultConfig
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
}
private String readVersionFromProperties() {
try {
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().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);
log = loggerFactory.getLogger(getClass());
}
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered)
if (bouncyCastleRegistered) {
setKeyExchangeFactories(new Curve25519SHA256.Factory(),
new DHGexSHA256.Factory(),
new ECDHNistP.Factory521(),
@@ -109,8 +110,9 @@ public class DefaultConfig
new DHGexSHA1.Factory(),
new DHG14.Factory(),
new DHG1.Factory());
else
} else {
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
}
}
protected void initRandomFactory(boolean bouncyCastleRegistered) {
@@ -120,7 +122,7 @@ 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 OpenSSHKeyFile.Factory(), new PuTTYKeyFile.Factory());
}
}
@@ -156,7 +158,8 @@ public class DefaultConfig
BlockCiphers.TwofishCBC(),
StreamCiphers.Arcfour(),
StreamCiphers.Arcfour128(),
StreamCiphers.Arcfour256()));
StreamCiphers.Arcfour256())
);
boolean warn = false;
// Ref. https://issues.apache.org/jira/browse/SSHD-24
@@ -182,17 +185,26 @@ public class DefaultConfig
}
protected void initSignatureFactories() {
setSignatureFactories(new SignatureECDSA.Factory(), new SignatureRSA.Factory(), new SignatureDSA.Factory(), new SignatureEdDSA.Factory());
setSignatureFactories(
new SignatureECDSA.Factory(),
new SignatureRSA.Factory(),
new SignatureDSA.Factory(),
new SignatureEdDSA.Factory()
);
}
protected void initMACFactories() {
setMACFactories(new HMACSHA1.Factory(), new HMACSHA196.Factory(), new HMACMD5.Factory(),
new HMACMD596.Factory(), new HMACSHA2256.Factory(), new HMACSHA2512.Factory());
setMACFactories(
new HMACSHA1.Factory(),
new HMACSHA196.Factory(),
new HMACMD5.Factory(),
new HMACMD596.Factory(),
new HMACSHA2256.Factory(),
new HMACSHA2512.Factory()
);
}
protected void initCompressionFactories() {
setCompressionFactories(new NoneCompression.Factory());
}
}

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.connection.Connection;
@@ -45,17 +46,8 @@ import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import net.schmizz.sshj.userauth.UserAuth;
import net.schmizz.sshj.userauth.UserAuthException;
import net.schmizz.sshj.userauth.UserAuthImpl;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import net.schmizz.sshj.userauth.keyprovider.KeyPairWrapper;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
import net.schmizz.sshj.userauth.method.AuthGssApiWithMic;
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
import net.schmizz.sshj.userauth.method.AuthMethod;
import net.schmizz.sshj.userauth.method.AuthPassword;
import net.schmizz.sshj.userauth.method.AuthPublickey;
import net.schmizz.sshj.userauth.method.PasswordResponseProvider;
import net.schmizz.sshj.userauth.keyprovider.*;
import net.schmizz.sshj.userauth.method.*;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUpdateProvider;
import net.schmizz.sshj.userauth.password.PasswordUtils;
@@ -63,21 +55,15 @@ import net.schmizz.sshj.userauth.password.Resource;
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
import org.ietf.jgss.Oid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.login.LoginContext;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
/**
* Secure SHell client API.
@@ -128,7 +114,8 @@ public class SSHClient
public static final int DEFAULT_PORT = 22;
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final LoggerFactory loggerFactory;
protected final Logger log;
/** Transport layer */
protected final Transport trans;
@@ -139,6 +126,8 @@ public class SSHClient
/** {@code ssh-connection} service */
protected final Connection conn;
private final List<LocalPortForwarder> forwarders = new ArrayList<LocalPortForwarder>();
/** Default constructor. Initializes this object using {@link DefaultConfig}. */
public SSHClient() {
this(new DefaultConfig());
@@ -151,6 +140,8 @@ public class SSHClient
*/
public SSHClient(Config config) {
super(DEFAULT_PORT);
loggerFactory = config.getLoggerFactory();
log = loggerFactory.getLogger(getClass());
this.trans = new TransportImpl(config, this);
this.auth = new UserAuthImpl(trans);
this.conn = new ConnectionImpl(trans, config.getKeepAliveProvider());
@@ -221,8 +212,9 @@ public class SSHClient
public void auth(String username, Iterable<AuthMethod> methods)
throws UserAuthException, TransportException {
checkConnected();
final Deque<UserAuthException> savedEx = new LinkedList<>();
final Deque<UserAuthException> savedEx = new LinkedList<UserAuthException>();
for (AuthMethod method: methods) {
method.setLoggerFactory(loggerFactory);
try {
if (auth.authenticate(username, (Service) conn, method, trans.getTimeoutMs()))
return;
@@ -324,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");
}
/**
@@ -342,7 +334,7 @@ public class SSHClient
*/
public void authPublickey(String username, Iterable<KeyProvider> keyProviders)
throws UserAuthException, TransportException {
final List<AuthMethod> am = new LinkedList<>();
final List<AuthMethod> am = new LinkedList<AuthMethod>();
for (KeyProvider kp : keyProviders)
am.add(new AuthPublickey(kp));
auth(username, am);
@@ -385,7 +377,7 @@ public class SSHClient
*/
public void authPublickey(String username, String... locations)
throws UserAuthException, TransportException {
final List<KeyProvider> keyProviders = new LinkedList<>();
final List<KeyProvider> keyProviders = new LinkedList<KeyProvider>();
for (String loc : locations) {
try {
log.debug("Attempting to load key from: {}", loc);
@@ -415,7 +407,7 @@ public class SSHClient
public void authGssApiWithMic(String username, LoginContext context, Oid supportedOid, Oid... supportedOids)
throws UserAuthException, TransportException {
// insert supportedOid to the front of the list since ordering matters
List<Oid> oids = new ArrayList<>(Arrays.asList(supportedOids));
List<Oid> oids = new ArrayList<Oid>(Arrays.asList(supportedOids));
oids.add(0, supportedOid);
auth(username, new AuthGssApiWithMic(context, oids));
@@ -431,6 +423,14 @@ public class SSHClient
@Override
public void disconnect()
throws IOException {
for (LocalPortForwarder forwarder : forwarders) {
try {
forwarder.close();
} catch (IOException e) {
log.warn("Error closing forwarder", e);
}
}
forwarders.clear();
trans.disconnect();
super.disconnect();
}
@@ -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
@@ -630,7 +635,7 @@ public class SSHClient
*/
public void loadKnownHosts(File location)
throws IOException {
addHostKeyVerifier(new OpenSSHKnownHosts(location));
addHostKeyVerifier(new OpenSSHKnownHosts(location, loggerFactory));
}
/**
@@ -648,7 +653,9 @@ public class SSHClient
*/
public LocalPortForwarder newLocalPortForwarder(LocalPortForwarder.Parameters parameters,
ServerSocket serverSocket) {
return new LocalPortForwarder(conn, parameters, serverSocket);
LocalPortForwarder forwarder = new LocalPortForwarder(conn, parameters, serverSocket, loggerFactory);
forwarders.add(forwarder);
return forwarder;
}
/**
@@ -675,7 +682,7 @@ public class SSHClient
public SCPFileTransfer newSCPFileTransfer() {
checkConnected();
checkAuthenticated();
return new SCPFileTransfer(this);
return new SCPFileTransfer(this, loggerFactory);
}
/**

View File

@@ -48,12 +48,50 @@ public abstract class SocketClient {
this.defaultPort = defaultPort;
}
public void connect(InetAddress host, int port) throws IOException {
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(host, port), connectTimeout);
/**
* Connect to a host via a proxy.
* @param hostname The host name to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(String hostname, Proxy proxy) throws IOException {
connect(hostname, defaultPort, proxy);
}
/**
* Connect to a host via a proxy.
* @param hostname The host name to connect to.
* @param port The port to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(String hostname, int port, Proxy proxy) throws IOException {
this.hostname = hostname;
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
socket = new Jdk7HttpProxySocket(proxy);
} else {
socket = new Socket(proxy);
}
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
onConnect();
}
/**
* Connect to a host via a proxy.
* @param host The host address to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(InetAddress host, Proxy proxy) throws IOException {
connect(host, defaultPort, proxy);
}
/**
* Connect to a host via a proxy.
@@ -75,23 +113,41 @@ public abstract class SocketClient {
onConnect();
}
public void connect(String hostname, int port) throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port);
public void connect(String hostname) throws IOException {
connect(hostname, defaultPort);
}
/**
* Connect to a host via a proxy.
* @param hostname The host name to connect to.
* @param port The port to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(String hostname, int port, Proxy proxy) throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port, proxy);
public void connect(String hostname, int port) throws IOException {
if (hostname == null) {
connect(InetAddress.getByName(null), port);
} else {
this.hostname = hostname;
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
onConnect();
}
}
public void connect(String hostname, int port, InetAddress localAddr, int localPort) throws IOException {
if (hostname == null) {
connect(InetAddress.getByName(null), port, localAddr, localPort);
} else {
this.hostname = hostname;
socket = socketFactory.createSocket();
socket.bind(new InetSocketAddress(localAddr, localPort));
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
onConnect();
}
}
public void connect(InetAddress host) throws IOException {
connect(host, defaultPort);
}
public void connect(InetAddress host, int port) throws IOException {
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(host, port), connectTimeout);
onConnect();
}
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
@@ -102,43 +158,6 @@ public abstract class SocketClient {
onConnect();
}
public void connect(String hostname, int port, InetAddress localAddr, int localPort) throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port, localAddr, localPort);
}
public void connect(InetAddress host) throws IOException {
connect(host, defaultPort);
}
public void connect(String hostname) throws IOException {
connect(hostname, defaultPort);
}
/**
* Connect to a host via a proxy.
* @param host The host address to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(InetAddress host, Proxy proxy) throws IOException {
connect(host, defaultPort, proxy);
}
/**
* Connect to a host via a proxy.
* @param hostname The host name to connect to.
* @param proxy The proxy to connect via.
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
*/
@Deprecated
public void connect(String hostname, Proxy proxy) throws IOException {
connect(hostname, defaultPort, proxy);
}
public void disconnect() throws IOException {
if (socket != null) {
socket.close();

View File

@@ -1,18 +1,3 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.common;
@@ -1742,4 +1727,4 @@ public class Base64 {
private Base64() {
}
} // end class Base64
} // end class Base64

View File

@@ -426,8 +426,7 @@ public class Buffer<T extends Buffer<T>> {
public PublicKey readPublicKey()
throws BufferException {
try {
final String type = readString();
return KeyType.fromString(type).readPubKeyFromBuffer(type, this);
return KeyType.fromString(readString()).readPubKeyFromBuffer(this);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
}

View File

@@ -16,7 +16,6 @@
package net.schmizz.sshj.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
@@ -26,25 +25,33 @@ import java.nio.charset.Charset;
public class IOUtils {
private static final Logger LOG = LoggerFactory.getLogger(IOUtils.class);
public static final Charset UTF8 = Charset.forName("UTF-8");
public static void closeQuietly(Closeable... closeables) {
for (Closeable c : closeables)
try {
if (c != null)
c.close();
} catch (IOException logged) {
LOG.warn("Error closing {} - {}", c, logged);
}
closeQuietly(LoggerFactory.DEFAULT, closeables);
}
public static ByteArrayOutputStream readFully(InputStream stream)
throws IOException {
return readFully(stream, LoggerFactory.DEFAULT);
}
public static void closeQuietly(LoggerFactory loggerFactory, Closeable... closeables) {
for (Closeable c : closeables) {
try {
if (c != null)
c.close();
} catch (IOException logged) {
loggerFactory.getLogger(IOUtils.class).warn("Error closing {} - {}", c, logged);
}
}
}
public static ByteArrayOutputStream readFully(InputStream stream, LoggerFactory loggerFactory)
throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
new StreamCopier(stream, baos).copy();
new StreamCopier(stream, baos, loggerFactory).copy();
return baos;
}
}
}

View File

@@ -18,7 +18,6 @@ package net.schmizz.sshj.common;
import com.hierynomus.sshj.secg.SecgUtils;
import com.hierynomus.sshj.signature.Ed25519PublicKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
@@ -47,7 +46,7 @@ public enum KeyType {
/** SSH identifier for RSA keys */
RSA("ssh-rsa") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
final BigInteger e, n;
try {
@@ -78,7 +77,7 @@ public enum KeyType {
/** SSH identifier for DSA keys */
DSA("ssh-dss") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
BigInteger p, q, g, y;
try {
@@ -115,8 +114,11 @@ public enum KeyType {
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new GeneralSecurityException("BouncyCastle is required to read a key of type " + sType);
}
try {
// final String algo = buf.readString(); it has been already read
final String curveName = buf.readString();
@@ -128,7 +130,7 @@ public enum KeyType {
buf.readRawBytes(y);
if(log.isDebugEnabled()) {
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
type,
sType,
curveName,
keyLen,
x04,
@@ -175,24 +177,23 @@ public enum KeyType {
},
ED25519("ssh-ed25519") {
private final Logger logger = LoggerFactory.getLogger(KeyType.class);
private final Logger log = LoggerFactory.getLogger(KeyType.class);
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf) throws GeneralSecurityException {
public PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException {
try {
final int keyLen = buf.readUInt32AsInt();
final byte[] p = new byte[keyLen];
buf.readRawBytes(p);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Key algo: %s, Key curve: 25519, Key Len: %s\np: %s",
type,
if (log.isDebugEnabled()) {
log.debug(String.format("Key algo: %s, Key curve: 25519, Key Len: %s\np: %s",
sType,
keyLen,
Arrays.toString(p))
);
}
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
GroupElement point = ed25519.getCurve().createPoint(p, true);
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(point, ed25519);
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(p, ed25519);
return new Ed25519PublicKey(publicSpec);
} catch (Buffer.BufferException be) {
@@ -215,9 +216,9 @@ public enum KeyType {
/** Unrecognized */
UNKNOWN("unknown") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
throw new UnsupportedOperationException("Don't know how to decode key:" + type);
throw new UnsupportedOperationException("Don't know how to decode key:" + sType);
}
@Override
@@ -240,7 +241,7 @@ public enum KeyType {
this.sType = type;
}
public abstract PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
public abstract PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException;
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
@@ -265,5 +266,4 @@ public enum KeyType {
public String toString() {
return sType;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.common;
import org.slf4j.Logger;
public interface LoggerFactory {
Logger getLogger(String name);
Logger getLogger(Class<?> clazz);
/**
* Default SLF4J-based implementation of the SSHJ LoggerFactory.
*/
LoggerFactory DEFAULT = new LoggerFactory() {
@Override
public Logger getLogger(String name) {
return org.slf4j.LoggerFactory.getLogger(name);
}
@Override
public Logger getLogger(Class<?> clazz) {
return org.slf4j.LoggerFactory.getLogger(clazz);
}
};
}

View File

@@ -15,47 +15,25 @@
*/
package net.schmizz.sshj.common;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// TODO refactor
import static java.lang.String.format;
/** Static utility method relating to security facilities. */
/**
* Static utility method relating to security facilities.
*/
public class SecurityUtils {
private static class BouncyCastleRegistration {
public void run()
throws Exception {
if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {
LOG.debug("Trying to register BouncyCastle as a JCE provider");
java.security.Security.addProvider(new BouncyCastleProvider());
MessageDigest.getInstance("MD5", BOUNCY_CASTLE);
KeyAgreement.getInstance("DH", BOUNCY_CASTLE);
LOG.info("BouncyCastle registration succeeded");
} else
LOG.info("BouncyCastle already registered as a JCE provider");
securityProvider = BOUNCY_CASTLE;
}
}
private static final Logger LOG = LoggerFactory.getLogger(SecurityUtils.class);
/** Identifier for the BouncyCastle JCE provider */
/**
* Identifier for the BouncyCastle JCE provider
*/
public static final String BOUNCY_CASTLE = "BC";
/*
@@ -67,6 +45,42 @@ public class SecurityUtils {
private static Boolean registerBouncyCastle;
private static boolean registrationDone;
public static boolean registerSecurityProvider(String providerClassName) {
Provider provider = null;
try {
Class<?> name = Class.forName(providerClassName);
provider = (Provider) name.newInstance();
} catch (ClassNotFoundException e) {
LOG.info("Security Provider class '{}' not found", providerClassName);
} catch (InstantiationException e) {
LOG.info("Security Provider class '{}' could not be created", providerClassName);
} catch (IllegalAccessException e) {
LOG.info("Security Provider class '{}' could not be accessed", providerClassName);
}
if (provider == null) {
return false;
}
try {
if (Security.getProvider(provider.getName()) == null) {
Security.addProvider(provider);
}
if (securityProvider == null) {
MessageDigest.getInstance("MD5", provider.getName());
KeyAgreement.getInstance("DH", provider.getName());
setSecurityProvider(provider.getName());
return true;
}
} catch (NoSuchAlgorithmException e) {
LOG.info(format("Security Provider '%s' does not support necessary algorithm", providerClassName), e);
} catch (NoSuchProviderException e) {
LOG.info("Registration of Security Provider '{}' unexpectedly failed", providerClassName);
}
return false;
}
public static synchronized Cipher getCipher(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
register();
@@ -80,9 +94,7 @@ public class SecurityUtils {
* Computes the fingerprint for a public key, in the standard SSH format, e.g. "4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"
*
* @param key the public key
*
* @return the fingerprint
*
* @see <a href="http://tools.ietf.org/html/draft-friedl-secsh-fingerprint-00">specification</a>
*/
public static String getFingerprint(PublicKey key) {
@@ -105,9 +117,7 @@ public class SecurityUtils {
* Creates a new instance of {@link KeyAgreement} with the given algorithm.
*
* @param algorithm key agreement algorithm
*
* @return new instance
*
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
*/
@@ -124,9 +134,7 @@ public class SecurityUtils {
* Creates a new instance of {@link KeyFactory} with the given algorithm.
*
* @param algorithm key factory algorithm e.g. RSA, DSA
*
* @return new instance
*
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
*/
@@ -143,9 +151,7 @@ public class SecurityUtils {
* Creates a new instance of {@link KeyPairGenerator} with the given algorithm.
*
* @param algorithm key pair generator algorithm
*
* @return new instance
*
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
*/
@@ -162,9 +168,7 @@ public class SecurityUtils {
* Create a new instance of {@link Mac} with the given algorithm.
*
* @param algorithm MAC algorithm
*
* @return new instance
*
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
*/
@@ -181,9 +185,7 @@ public class SecurityUtils {
* Create a new instance of {@link MessageDigest} with the given algorithm.
*
* @param algorithm MessageDigest algorithm name
*
* @return new instance
*
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
*/
@@ -243,20 +245,16 @@ public class SecurityUtils {
private static void register() {
if (!registrationDone) {
if (securityProvider == null && (registerBouncyCastle == null || registerBouncyCastle))
// Use an inner class to avoid a strong dependency on BouncyCastle
try {
new BouncyCastleRegistration().run();
} catch (Throwable t) {
if (registerBouncyCastle == null)
LOG.info("BouncyCastle not registered, using the default JCE provider");
else {
LOG.error("Failed to register BouncyCastle as the defaut JCE provider");
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut JCE provider", t);
}
if (securityProvider == null && (registerBouncyCastle == null || registerBouncyCastle)) {
registerSecurityProvider("org.bouncycastle.jce.provider.BouncyCastleProvider");
if (securityProvider == null && registerBouncyCastle == null) {
LOG.info("BouncyCastle not registered, using the default JCE provider");
} else if (securityProvider == null) {
LOG.error("Failed to register BouncyCastle as the defaut JCE provider");
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut JCE provider");
}
registrationDone = true;
}
}
registrationDone = true;
}
}
}

View File

@@ -15,10 +15,10 @@
*/
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;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -39,8 +39,8 @@ public class StreamCopier {
}
};
private final Logger log = LoggerFactory.getLogger(getClass());
private final LoggerFactory loggerFactory;
private final Logger log;
private final InputStream in;
private final OutputStream out;
@@ -50,9 +50,11 @@ public class StreamCopier {
private boolean keepFlushing = true;
private long length = -1;
public StreamCopier(InputStream in, OutputStream out) {
public StreamCopier(InputStream in, OutputStream out, LoggerFactory loggerFactory) {
this.in = in;
this.out = out;
this.loggerFactory = loggerFactory;
this.log = loggerFactory.getLogger(getClass());
}
public StreamCopier bufSize(int bufSize) {
@@ -91,7 +93,7 @@ public class StreamCopier {
public IOException chain(Throwable t) {
return (t instanceof IOException) ? (IOException) t : new IOException(t);
}
});
}, loggerFactory);
new Thread() {
{
@@ -107,7 +109,7 @@ public class StreamCopier {
log.debug("Done copying from {}", in);
doneEvent.set();
} catch (IOException ioe) {
log.error("In pipe from {} to {}: {}", in, out, ioe);
log.error(String.format("In pipe from %1$s to %2$s", in.toString(), out.toString()), ioe);
doneEvent.deliverError(ioe);
}
}
@@ -136,7 +138,7 @@ public class StreamCopier {
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
final double sizeKiB = count / 1024.0;
log.debug("{} KiB transferred in {} seconds ({} KiB/s)", sizeKiB, timeSeconds, (sizeKiB / timeSeconds));
log.debug(String.format("%1$,.1f KiB transferred in %2$,.1f seconds (%3$,.2f KiB/s)", sizeKiB, timeSeconds, (sizeKiB / timeSeconds)));
if (length != -1 && read == -1)
throw new IOException("Encountered EOF, could not transfer " + length + " bytes");

View File

@@ -156,4 +156,4 @@ public interface Connection {
* @return The configured {@link net.schmizz.keepalive.KeepAlive} mechanism.
*/
KeepAlive getKeepAlive();
}
}

View File

@@ -20,12 +20,7 @@ import net.schmizz.concurrent.Promise;
import net.schmizz.keepalive.KeepAlive;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.AbstractService;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.OpenFailException.Reason;
import net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener;
@@ -204,7 +199,7 @@ public class ConnectionImpl
Promise<SSHPacket, ConnectionException> promise = null;
if (wantReply) {
promise = new Promise<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer);
promise = new Promise<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer, trans.getConfig().getLoggerFactory());
globalReqPromises.add(promise);
}
return promise;

View File

@@ -17,19 +17,12 @@ package net.schmizz.sshj.connection.channel;
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;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.Connection;
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.InputStream;
import java.io.OutputStream;
@@ -44,7 +37,8 @@ public abstract class AbstractChannel
private static final int REMOTE_MAX_PACKET_SIZE_CEILING = 1024 * 1024;
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final LoggerFactory loggerFactory;
protected final Logger log;
/** Transport layer */
protected final Transport trans;
@@ -58,6 +52,8 @@ public abstract class AbstractChannel
/** Remote recipient ID */
private int recipient;
private boolean eof = false;
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
/* The lock used by to create the open & close events */
@@ -83,21 +79,23 @@ public abstract class AbstractChannel
protected AbstractChannel(Connection conn, String type) {
this.conn = conn;
this.loggerFactory = conn.getTransport().getConfig().getLoggerFactory();
this.type = type;
this.log = loggerFactory.getLogger(getClass());
this.trans = conn.getTransport();
id = conn.nextID();
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize());
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize(), loggerFactory);
in = new ChannelInputStream(this, trans, lwin);
openEvent = new Event<ConnectionException>("chan#" + id + " / " + "open", ConnectionException.chainer, openCloseLock);
closeEvent = new Event<ConnectionException>("chan#" + id + " / " + "close", ConnectionException.chainer, openCloseLock);
openEvent = new Event<ConnectionException>("chan#" + id + " / " + "open", ConnectionException.chainer, openCloseLock, loggerFactory);
closeEvent = new Event<ConnectionException>("chan#" + id + " / " + "close", ConnectionException.chainer, openCloseLock, loggerFactory);
}
protected void init(int recipient, long remoteWinSize, long remoteMaxPacketSize) {
this.recipient = recipient;
rwin = new Window.Remote(remoteWinSize, (int) Math.min(remoteMaxPacketSize, REMOTE_MAX_PACKET_SIZE_CEILING));
rwin = new Window.Remote(remoteWinSize, (int) Math.min(remoteMaxPacketSize, REMOTE_MAX_PACKET_SIZE_CEILING), loggerFactory);
out = new ChannelOutputStream(this, trans, rwin);
log.debug("Initialized - {}", this);
}
@@ -195,6 +193,16 @@ public abstract class AbstractChannel
}
}
@Override
public boolean isEOF() {
return eof;
}
@Override
public LoggerFactory getLoggerFactory() {
return loggerFactory;
}
private void gotClose()
throws TransportException {
log.debug("Got close");
@@ -362,7 +370,7 @@ public abstract class AbstractChannel
Event<ConnectionException> responseEvent = null;
if (wantReply) {
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType,
ConnectionException.chainer);
ConnectionException.chainer, loggerFactory);
chanReqResponseEvents.add(responseEvent);
}
return responseEvent;
@@ -393,6 +401,7 @@ public abstract class AbstractChannel
/** Called when EOF has been received. Subclasses can override but must call super. */
protected void eofInputStreams() {
in.eof();
eof = true;
}
@Override
@@ -402,4 +411,4 @@ public abstract class AbstractChannel
}
}
}

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.transport.TransportException;
@@ -25,13 +26,15 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
/** A channel is the basic medium for application-layer data on top of an SSH transport. */
public interface Channel
extends Closeable, SSHPacketHandler, ErrorNotifiable {
/**
* A channel is the basic medium for application-layer data on top of an SSH transport.
*/
public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
/** Direct channels are those that are initiated by us. */
interface Direct
extends Channel {
/**
* Direct channels are those that are initiated by us.
*/
interface Direct extends Channel {
/**
* Request opening this channel from remote end.
@@ -40,27 +43,30 @@ public interface Channel
* @throws ConnectionException other connection-layer error
* @throws TransportException error writing packets etc.
*/
void open()
throws ConnectionException, TransportException;
void open() throws ConnectionException, TransportException;
}
/** Forwarded channels are those that are initiated by the server. */
interface Forwarded
extends Channel {
/**
* Forwarded channels are those that are initiated by the server.
*/
interface Forwarded extends Channel {
/**
* Confirm {@code CHANNEL_OPEN} request.
*
* @throws TransportException error sending confirmation packet
*/
void confirm()
throws TransportException;
void confirm() throws TransportException;
/** @return the IP of where the forwarded connection originates. */
/**
* @return the IP of where the forwarded connection originates.
*/
String getOriginatorIP();
/** @return port from which the forwarded connection originates. */
/**
* @return port from which the forwarded connection originates.
*/
int getOriginatorPort();
/**
@@ -68,55 +74,73 @@ public interface Channel
*
* @param reason indicate {@link OpenFailException.Reason reason} for rejection of the request
* @param message indicate a message for why the request is rejected
*
* @throws TransportException error sending rejection packet
*/
void reject(OpenFailException.Reason reason, String message)
throws TransportException;
void reject(OpenFailException.Reason reason, String message) throws TransportException;
}
/** Close this channel. */
/**
* Close this channel.
*/
@Override
void close()
throws TransportException, ConnectionException;
void close() throws TransportException, ConnectionException;
/**
* @return whether auto-expansion of local window is set.
*
* @see #setAutoExpand(boolean)
*/
boolean getAutoExpand();
/** @return the channel ID */
/**
* @return the channel ID
*/
int getID();
/** @return the {@code InputStream} for this channel. */
/**
* @return the {@code InputStream} for this channel.
*/
InputStream getInputStream();
/** @return the maximum packet size that we have specified. */
/**
* @return the maximum packet size that we have specified.
*/
int getLocalMaxPacketSize();
/** @return the current local window size. */
/**
* @return the current local window size.
*/
long getLocalWinSize();
/** @return an {@code OutputStream} for this channel. */
/**
* @return an {@code OutputStream} for this channel.
*/
OutputStream getOutputStream();
/** @return the channel ID at the remote end. */
/**
* @return the channel ID at the remote end.
*/
int getRecipient();
/** @return the maximum packet size as specified by the remote end. */
/**
* @return the maximum packet size as specified by the remote end.
*/
int getRemoteMaxPacketSize();
/** @return the current remote window size. */
/**
* @return the current remote window size.
*/
long getRemoteWinSize();
/** @return the channel type identifier. */
/**
* @return the channel type identifier.
*/
String getType();
/** @return whether the channel is open. */
/**
* @return whether the channel is open.
*/
boolean isOpen();
/**
@@ -128,10 +152,17 @@ public interface Channel
*/
void setAutoExpand(boolean autoExpand);
void join()
throws ConnectionException;
void join() throws ConnectionException;
void join(long timeout, TimeUnit unit)
throws ConnectionException;
void join(long timeout, TimeUnit unit) throws ConnectionException;
/**
* Returns whether EOF has been received.
*/
boolean isEOF();
/**
* Get the LoggerFactory associated with the SSH client.
*/
LoggerFactory getLoggerFactory();
}

View File

@@ -15,11 +15,7 @@
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
@@ -38,7 +34,7 @@ public final class ChannelInputStream
extends InputStream
implements ErrorNotifiable {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log;
private final Channel chan;
private final Transport trans;
@@ -51,6 +47,7 @@ public final class ChannelInputStream
public ChannelInputStream(Channel chan, Transport trans, Window.Local win) {
this.chan = chan;
log = chan.getLoggerFactory().getLogger(getClass());
this.trans = trans;
this.win = win;
buf = new Buffer.PlainBuffer(chan.getLocalMaxPacketSize());

View File

@@ -15,11 +15,7 @@
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;

View File

@@ -15,14 +15,14 @@
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.connection.ConnectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class Window {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Logger log;
protected final Object lock = new Object();
@@ -30,9 +30,10 @@ public abstract class Window {
protected long size;
public Window(long initialWinSize, int maxPacketSize) {
public Window(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
size = initialWinSize;
this.maxPacketSize = maxPacketSize;
log = loggerFactory.getLogger(getClass());
}
public void expand(long inc) {
@@ -72,8 +73,8 @@ public abstract class Window {
public static final class Remote
extends Window {
public Remote(long initialWinSize, int maxPacketSize) {
super(initialWinSize, maxPacketSize);
public Remote(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
super(initialWinSize, maxPacketSize, loggerFactory);
}
public long awaitExpansion(long was)
@@ -108,8 +109,8 @@ public abstract class Window {
private final long initialSize;
private final long threshold;
public Local(long initialWinSize, int maxPacketSize) {
super(initialWinSize, maxPacketSize);
public Local(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
super(initialWinSize, maxPacketSize, loggerFactory);
this.initialSize = initialWinSize;
threshold = Math.min(maxPacketSize * 20, initialSize / 4);
}

View File

@@ -94,4 +94,4 @@ public abstract class AbstractDirectChannel
}
}
}
}

View File

@@ -17,17 +17,17 @@ package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.TimeUnit;
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
@@ -82,10 +82,10 @@ public class LocalPortForwarder {
throws IOException {
socket.setSendBufferSize(getLocalMaxPacketSize());
socket.setReceiveBufferSize(getRemoteMaxPacketSize());
final Event<IOException> soc2chan = new StreamCopier(socket.getInputStream(), getOutputStream())
final Event<IOException> soc2chan = new StreamCopier(socket.getInputStream(), getOutputStream(), loggerFactory)
.bufSize(getRemoteMaxPacketSize())
.spawnDaemon("soc2chan");
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), socket.getOutputStream())
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), socket.getOutputStream(), loggerFactory)
.bufSize(getLocalMaxPacketSize())
.spawnDaemon("chan2soc");
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
@@ -102,16 +102,18 @@ public class LocalPortForwarder {
}
private final Logger log = LoggerFactory.getLogger(LocalPortForwarder.class);
private final LoggerFactory loggerFactory;
private final Logger log;
private final Connection conn;
private final Parameters parameters;
private final ServerSocket serverSocket;
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket) {
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket, LoggerFactory loggerFactory) {
this.conn = conn;
this.parameters = parameters;
this.serverSocket = serverSocket;
this.loggerFactory = loggerFactory;
this.log = loggerFactory.getLogger(getClass());
}
private void startChannel(Socket socket) throws IOException {
@@ -134,11 +136,33 @@ public class LocalPortForwarder {
throws IOException {
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
while (!Thread.currentThread().isInterrupted()) {
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
startChannel(socket);
try {
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
startChannel(socket);
} catch (SocketException e) {
if (!serverSocket.isClosed()) {
throw e;
}
}
}
if (serverSocket.isClosed()) {
log.debug("LocalPortForwarder closed");
} else {
log.debug("LocalPortForwarder interrupted!");
}
log.debug("Interrupted!");
}
}
/**
* Close the ServerSocket that's listening for connections to forward.
*
* @throws IOException
*/
public void close() throws IOException {
if (!serverSocket.isClosed()) {
log.info("Closing listener on {}", serverSocket.getLocalSocketAddress());
serverSocket.close();
}
}
}

View File

@@ -15,12 +15,7 @@
*/
package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.ChannelInputStream;

View File

@@ -73,4 +73,4 @@ public abstract class AbstractForwardedChannel
return origPort;
}
}
}

View File

@@ -29,14 +29,14 @@ import java.io.IOException;
public abstract class AbstractForwardedChannelOpener
implements ForwardedChannelOpener {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Logger log;
protected final String chanType;
protected final Connection conn;
protected AbstractForwardedChannelOpener(String chanType, Connection conn) {
this.chanType = chanType;
this.conn = conn;
log = conn.getTransport().getConfig().getLoggerFactory().getLogger(getClass());
}
@Override
@@ -72,4 +72,4 @@ public abstract class AbstractForwardedChannelOpener
}.start();
}
}
}

View File

@@ -19,8 +19,6 @@ import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.Socket;
@@ -31,8 +29,6 @@ import java.util.concurrent.TimeUnit;
public class SocketForwardingConnectListener
implements ConnectListener {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final SocketAddress addr;
/** Create with a {@link SocketAddress} this listener will forward to. */
@@ -44,7 +40,7 @@ public class SocketForwardingConnectListener
@Override
public void gotConnect(Channel.Forwarded chan)
throws IOException {
log.debug("New connection from {}:{}", chan.getOriginatorIP(), chan.getOriginatorPort());
chan.getLoggerFactory().getLogger(getClass()).debug("New connection from {}:{}", chan.getOriginatorIP(), chan.getOriginatorPort());
final Socket sock = new Socket();
sock.setSendBufferSize(chan.getLocalMaxPacketSize());
@@ -55,11 +51,11 @@ public class SocketForwardingConnectListener
// ok so far -- could connect, let's confirm the channel
chan.confirm();
final Event<IOException> soc2chan = new StreamCopier(sock.getInputStream(), chan.getOutputStream())
final Event<IOException> soc2chan = new StreamCopier(sock.getInputStream(), chan.getOutputStream(), chan.getLoggerFactory())
.bufSize(chan.getRemoteMaxPacketSize())
.spawnDaemon("soc2chan");
final Event<IOException> chan2soc = new StreamCopier(chan.getInputStream(), sock.getOutputStream())
final Event<IOException> chan2soc = new StreamCopier(chan.getInputStream(), sock.getOutputStream(), chan.getLoggerFactory())
.bufSize(chan.getLocalMaxPacketSize())
.spawnDaemon("chan2soc");

View File

@@ -222,7 +222,7 @@ public final class FileAttributes {
sb.append("size=").append(size).append(";");
if (has(Flag.UIDGID))
sb.append("uid=").append(size).append(",gid=").append(gid).append(";");
sb.append("uid=").append(uid).append(",gid=").append(gid).append(";");
if (has(Flag.MODE))
sb.append("mode=").append(mode.toString()).append(";");

View File

@@ -36,7 +36,7 @@ public class FileMode {
/** directory */
DIRECTORY(0040000),
/** symbolic link */
SYMKLINK(0120000),
SYMLINK(0120000),
/** unknown */
UNKNOWN(0);

View File

@@ -17,7 +17,6 @@ package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.SSHException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,7 +29,7 @@ public class PacketReader
extends Thread {
/** Logger */
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log;
private final InputStream in;
private final Map<Long, Promise<Response, SFTPException>> promises = new ConcurrentHashMap<Long, Promise<Response, SFTPException>>();
@@ -40,6 +39,7 @@ public class PacketReader
public PacketReader(SFTPEngine engine) {
this.engine = engine;
log = engine.getLoggerFactory().getLogger(getClass());
this.in = engine.getSubsystem().getInputStream();
setName("sftp reader");
}
@@ -107,7 +107,7 @@ public class PacketReader
public Promise<Response, SFTPException> expectResponseTo(long requestId) {
final Promise<Response, SFTPException> promise
= new Promise<Response, SFTPException>("sftp / " + requestId, SFTPException.chainer);
= new Promise<Response, SFTPException>("sftp / " + requestId, SFTPException.chainer, engine.getLoggerFactory());
promises.put(requestId, promise);
return promise;
}

View File

@@ -15,12 +15,7 @@
*/
package net.schmizz.sshj.sftp;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.*;
public class RandomAccessRemoteFile
implements DataInput, DataOutput {

View File

@@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit;
public class RemoteDirectory
extends RemoteResource {
public RemoteDirectory(Requester requester, String path, byte[] handle) {
public RemoteDirectory(SFTPEngine requester, String path, byte[] handle) {
super(requester, path, handle);
}

View File

@@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit;
public class RemoteFile
extends RemoteResource {
public RemoteFile(Requester requester, String path, byte[] handle) {
public RemoteFile(SFTPEngine requester, String path, byte[] handle) {
super(requester, path, handle);
}

View File

@@ -26,14 +26,15 @@ public abstract class RemoteResource
implements Closeable {
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Logger log;
protected final Requester requester;
protected final SFTPEngine requester;
protected final String path;
protected final byte[] handle;
protected RemoteResource(Requester requester, String path, byte[] handle) {
protected RemoteResource(SFTPEngine requester, String path, byte[] handle) {
this.requester = requester;
log = requester.getLoggerFactory().getLogger(getClass());
this.path = path;
this.handle = handle;
}

View File

@@ -30,7 +30,30 @@ public final class Response
BAD_MESSAGE(5),
NO_CONNECTION(6),
CONNECITON_LOST(7),
OP_UNSUPPORTED(8);
OP_UNSUPPORTED(8),
INVALID_HANDLE(9),
NO_SUCH_PATH(10),
FILE_ALREADY_EXISTS(11),
WRITE_PROTECT(12),
NO_MEDIA(13),
NO_SPACE_ON_FILESYSTEM(14),
QUOTA_EXCEEDED(15),
UNKNOWN_PRINCIPAL(16),
LOCK_CONFLICT(17),
DIR_NOT_EMPTY(18),
NOT_A_DIRECTORY(19),
INVALID_FILENAME(20),
LINK_LOOP(21),
CANNOT_DELETE(22),
INVALID_PARAMETER(23),
FILE_IS_A_DIRECTORY(24),
BYTE_RANGE_LOCK_CONFLICT(25),
BYTE_RANGE_LOCK_REFUSED(26),
DELETE_PENDING(27),
FILE_CORRUPT(28),
OWNER_INVALID(29),
GROUP_INVALID(30),
NO_MATCHING_BYTE_RANGE_LOCK(31);
private final int code;

View File

@@ -23,23 +23,20 @@ import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.util.Deque;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.*;
public class SFTPClient
implements Closeable {
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Logger log;
protected final SFTPEngine engine;
protected final SFTPFileTransfer xfer;
public SFTPClient(SFTPEngine engine) {
this.engine = engine;
log = engine.getLoggerFactory().getLogger(getClass());
this.xfer = new SFTPFileTransfer(engine);
}
@@ -89,7 +86,7 @@ public class SFTPClient
public void mkdirs(String path)
throws IOException {
final Deque<String> dirsToMake = new LinkedList<>();
final Deque<String> dirsToMake = new LinkedList<String>();
for (PathComponents current = engine.getPathHelper().getComponents(path); ;
current = engine.getPathHelper().getComponents(current.getParent())) {
final FileAttributes attrs = statExistence(current.getPath());

View File

@@ -16,11 +16,11 @@
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.connection.channel.direct.Session.Subsystem;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
@@ -38,13 +38,14 @@ public class SFTPEngine
public static final int DEFAULT_TIMEOUT_MS = 30 * 1000; // way too long, but it was the original default
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final LoggerFactory loggerFactory;
protected final Logger log;
protected volatile int timeoutMs = DEFAULT_TIMEOUT_MS;
protected final PathHelper pathHelper;
protected final Subsystem sub;
protected final Session.Subsystem sub;
protected final PacketReader reader;
protected final OutputStream out;
@@ -59,7 +60,10 @@ public class SFTPEngine
public SFTPEngine(SessionFactory ssh, String pathSep)
throws SSHException {
sub = ssh.startSession().startSubsystem("sftp");
Session session = ssh.startSession();
loggerFactory = session.getLoggerFactory();
log = loggerFactory.getLogger(getClass());
sub = session.startSubsystem("sftp");
out = sub.getOutputStream();
reader = new PacketReader(this);
pathHelper = new PathHelper(new PathHelper.Canonicalizer() {
@@ -94,7 +98,7 @@ public class SFTPEngine
return this;
}
public Subsystem getSubsystem() {
public Session.Subsystem getSubsystem() {
return sub;
}
@@ -248,6 +252,10 @@ public class SFTPEngine
reader.interrupt();
}
protected LoggerFactory getLoggerFactory() {
return loggerFactory;
}
protected FileAttributes stat(PacketType pt, String path)
throws IOException {
return doRequest(newRequest(pt).putString(path))

View File

@@ -17,19 +17,12 @@ package net.schmizz.sshj.sftp;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.sftp.Response.StatusCode;
import net.schmizz.sshj.xfer.AbstractFileTransfer;
import net.schmizz.sshj.xfer.FileSystemFile;
import net.schmizz.sshj.xfer.FileTransfer;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalFileFilter;
import net.schmizz.sshj.xfer.LocalSourceFile;
import net.schmizz.sshj.xfer.TransferListener;
import net.schmizz.sshj.xfer.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.EnumSet;
import java.util.List;
public class SFTPFileTransfer
extends AbstractFileTransfer
@@ -42,6 +35,7 @@ public class SFTPFileTransfer
private volatile boolean preserveAttributes = true;
public SFTPFileTransfer(SFTPEngine engine) {
super(engine.getLoggerFactory());
this.engine = engine;
}
@@ -145,7 +139,7 @@ public class SFTPFileTransfer
final RemoteFile.ReadAheadRemoteFileInputStream rfis = rf.new ReadAheadRemoteFileInputStream(16);
final OutputStream os = adjusted.getOutputStream();
try {
new StreamCopier(rfis, os)
new StreamCopier(rfis, os, engine.getLoggerFactory())
.bufSize(engine.getSubsystem().getLocalMaxPacketSize())
.keepFlushing(false)
.listener(listener)
@@ -235,14 +229,36 @@ public class SFTPFileTransfer
final String remote)
throws IOException {
final String adjusted = prepareFile(local, remote);
try (RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC))) {
try (InputStream fis = local.getInputStream();
RemoteFile.RemoteFileOutputStream rfos = rf.new RemoteFileOutputStream(0, 16)) {
new StreamCopier(fis, rfos)
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
.keepFlushing(false)
.listener(listener)
.copy();
RemoteFile rf = null;
InputStream fis = null;
RemoteFile.RemoteFileOutputStream rfos = null;
try {
rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC));
fis = local.getInputStream();
rfos = rf.new RemoteFileOutputStream(0, 16);
new StreamCopier(fis, rfos, engine.getLoggerFactory())
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
.keepFlushing(false)
.listener(listener)
.copy();
} finally {
if (rf != null) {
try {
rf.close();
} catch (IOException e) {
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
}
}
if (rfos != null) {
try {
rfos.close();
} catch (IOException e) {
}
}
}
return adjusted;

View File

@@ -15,11 +15,11 @@
*/
package net.schmizz.sshj.signature;
import java.security.SignatureException;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import java.security.SignatureException;
/** DSA {@link Signature} */
public class SignatureDSA
extends AbstractSignature {

View File

@@ -15,13 +15,13 @@
*/
package net.schmizz.sshj.signature;
import java.math.BigInteger;
import java.security.SignatureException;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import java.math.BigInteger;
import java.security.SignatureException;
/** ECDSA {@link Signature} */
public class SignatureECDSA
extends AbstractSignature {

View File

@@ -15,11 +15,11 @@
*/
package net.schmizz.sshj.signature;
import java.security.SignatureException;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import java.security.SignatureException;
/** RSA {@link Signature} */
public class SignatureRSA
extends AbstractSignature {

View File

@@ -15,12 +15,8 @@
*/
package net.schmizz.sshj.transport;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ByteArrayUtils;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.SSHPacketHandler;
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;
@@ -33,7 +29,7 @@ final class Decoder
private static final int MAX_PACKET_LEN = 256 * 1024;
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log;
/** What we pass decoded packets to */
private final SSHPacketHandler packetHandler;
@@ -53,8 +49,9 @@ final class Decoder
*/
private int needed = 8;
Decoder(SSHPacketHandler packetHandler) {
Decoder(Transport packetHandler) {
this.packetHandler = packetHandler;
log = packetHandler.getConfig().getLoggerFactory().getLogger(getClass());
}
/**
@@ -193,4 +190,4 @@ final class Decoder
return MAX_PACKET_LEN;
}
}
}

View File

@@ -15,13 +15,13 @@
*/
package net.schmizz.sshj.transport;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
import net.schmizz.sshj.transport.mac.MAC;
import net.schmizz.sshj.transport.random.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.locks.Lock;
@@ -29,15 +29,14 @@ import java.util.concurrent.locks.Lock;
final class Encoder
extends Converter {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log;
private final Random prng;
private final Lock encodeLock;
Encoder(Random prng, Lock encodeLock) {
Encoder(Random prng, Lock encodeLock, LoggerFactory loggerFactory) {
this.prng = prng;
this.encodeLock = encodeLock;
log = loggerFactory.getLogger(getClass());
}
private SSHPacket checkHeaderSpace(SSHPacket buffer) {
@@ -142,4 +141,4 @@ final class Encoder
return Compression.Mode.DEFLATE;
}
}
}

View File

@@ -17,24 +17,14 @@ package net.schmizz.sshj.transport;
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.KeyType;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.kex.KeyExchange;
import net.schmizz.sshj.transport.mac.MAC;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,8 +50,7 @@ final class KeyExchanger
NEWKEYS,
}
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log;
private final TransportImpl transport;
/**
@@ -86,18 +75,20 @@ final class KeyExchanger
private Proposal clientProposal;
private NegotiatedAlgorithms negotiatedAlgs;
private final Event<TransportException> kexInitSent =
new Event<TransportException>("kexinit sent", TransportException.chainer);
private final Event<TransportException> kexInitSent;
private final Event<TransportException> done;
KeyExchanger(TransportImpl trans) {
this.transport = trans;
log = trans.getConfig().getLoggerFactory().getLogger(getClass());
kexInitSent = new Event<TransportException>("kexinit sent", TransportException.chainer, trans.getConfig().getLoggerFactory());
/*
* Use TransportImpl's writeLock, since TransportImpl.write() may wait on this event and the lock should
* be released while waiting.
*/
this.done = new Event<TransportException>("kex done", TransportException.chainer, trans.getWriteLock());
this.done = new Event<TransportException>("kex done", TransportException.chainer, trans.getWriteLock(), trans.getConfig().getLoggerFactory());
}
/**
@@ -395,4 +386,4 @@ final class KeyExchanger
ErrorDeliveryUtil.alertEvents(error, kexInitSent, done);
}
}
}

View File

@@ -24,12 +24,12 @@ import java.net.SocketTimeoutException;
public final class Reader
extends Thread {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log;
private final TransportImpl trans;
public Reader(TransportImpl trans) {
this.trans = trans;
log = trans.getConfig().getLoggerFactory().getLogger(getClass());
setName("reader");
}

View File

@@ -235,4 +235,4 @@ public interface Transport
* @param e The exception that occurred.
*/
void die(Exception e);
}
}

View File

@@ -26,7 +26,6 @@ import net.schmizz.sshj.common.*;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -36,7 +35,7 @@ import java.util.concurrent.locks.ReentrantLock;
/** A thread-safe {@link Transport} implementation. */
public final class TransportImpl
implements Transport {
implements Transport, DisconnectListener {
private static final class NullService
extends AbstractService {
@@ -46,6 +45,7 @@ public final class TransportImpl
}
}
static final class ConnInfo {
final String host;
@@ -61,16 +61,12 @@ public final class TransportImpl
}
}
private final Logger log = LoggerFactory.getLogger(getClass());
private final Service nullService = new NullService(this);
private final LoggerFactory loggerFactory;
private final DisconnectListener nullDisconnectListener = new DisconnectListener() {
@Override
public void notifyDisconnect(DisconnectReason reason, String message) {
log.info("Disconnected - {}", reason);
}
};
private final Logger log;
private final Service nullService;
private final Config config;
@@ -88,9 +84,9 @@ public final class TransportImpl
private final Decoder decoder;
private final Event<TransportException> serviceAccept = new Event<TransportException>("service accept", TransportException.chainer);
private final Event<TransportException> serviceAccept;
private final Event<TransportException> close = new Event<TransportException>("transport close", TransportException.chainer);
private final Event<TransportException> close;
/** Client version identification string */
private final String clientID;
@@ -100,9 +96,9 @@ public final class TransportImpl
private volatile boolean authed = false;
/** Currently active service e.g. UserAuthService, ConnectionService */
private volatile Service service = nullService;
private volatile Service service;
private DisconnectListener disconnectListener = nullDisconnectListener;
private DisconnectListener disconnectListener;
private ConnInfo connInfo;
@@ -116,8 +112,15 @@ public final class TransportImpl
public TransportImpl(Config config) {
this.config = config;
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.service = nullService;
this.log = loggerFactory.getLogger(getClass());
this.disconnectListener = this;
this.reader = new Reader(this);
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock);
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock, loggerFactory);
this.decoder = new Decoder(this);
this.kexer = new KeyExchanger(this);
this.clientID = String.format("SSH-2.0-%s", config.getVersion());
@@ -131,16 +134,21 @@ public final class TransportImpl
@Deprecated
public TransportImpl(Config config, SSHClient sshClient) {
this.config = config;
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.log = loggerFactory.getLogger(getClass());
this.nullService = new NullService(this);
this.service = nullService;
this.disconnectListener = this;
this.reader = new Reader(this);
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock);
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock, loggerFactory);
this.decoder = new Decoder(this);
this.kexer = new KeyExchanger(this);
this.clientID = String.format("SSH-2.0-%s", config.getVersion());
this.sshClient = sshClient;
}
@Override
public void init(String remoteHost, int remotePort, InputStream in, OutputStream out)
throws TransportException {
@@ -166,6 +174,14 @@ public final class TransportImpl
reader.start();
}
/**
* TransportImpl implements its own default DisconnectListener.
*/
@Override
public void notifyDisconnect(DisconnectReason reason, String message) {
log.info("Disconnected - {}", reason);
}
private void receiveServerIdent() throws IOException {
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
while ((serverID = readIdentification(buf)).isEmpty()) {
@@ -203,47 +219,10 @@ public final class TransportImpl
*/
private String readIdentification(Buffer.PlainBuffer buffer)
throws IOException {
String ident = new IdentificationStringParser(buffer).parseIdentificationString();
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
if (ident.isEmpty()) {
return ident;
}
//
// byte[] data = new byte[256];
// for (; ; ) {
// int savedBufPos = buffer.rpos();
// int pos = 0;
// boolean needLF = false;
// for (; ; ) {
// if (buffer.available() == 0) {
// // Need more data, so undo reading and return null
// buffer.rpos(savedBufPos);
// return "";
// }
// byte b = buffer.readByte();
// if (b == '\r') {
// needLF = true;
// continue;
// }
// if (b == '\n')
// break;
// if (needLF) {
// log.error("Incorrect identification, was expecting a '\n' after the '\r', got: '{}' (hex: {})", b, Integer.toHexString(b & 0xFF));
// log.error("Data received up til here was: {}", new String(data, 0, pos));
// throw new TransportException("Incorrect identification: bad line ending: " + ByteArrayUtils.toHex(data, 0, pos));
// }
// if (pos >= data.length) {
// log.error("Incorrect identification String received, line was longer than expected: {}", new String(data, 0, pos));
// log.error("Just for good measure, bytes were: {}", ByteArrayUtils.printHex(data, 0, pos));
// throw new TransportException("Incorrect identification: line too long: " + ByteArrayUtils.printHex(data, 0, pos));
// }
// data[pos++] = b;
// }
// ident = new String(data, 0, pos);
// if (ident.startsWith("SSH-"))
// break;
// if (buffer.rpos() > 16 * 1024)
// throw new TransportException("Incorrect identification: too many header lines");
// }
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
@@ -432,7 +411,7 @@ public final class TransportImpl
@Override
public void setDisconnectListener(DisconnectListener listener) {
this.disconnectListener = listener == null ? nullDisconnectListener : listener;
this.disconnectListener = listener == null ? this : listener;
}
@Override

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.common.SecurityUtils;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;

View File

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

View File

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

View File

@@ -18,8 +18,6 @@ package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.Digest;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.Arrays;
public abstract class AbstractDH extends KeyExchangeBase {
protected final DHBase dh;

View File

@@ -15,24 +15,15 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.SHA1;
import org.slf4j.Logger;
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

View File

@@ -15,21 +15,15 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.SecurityUtils;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.custom.djb.Curve25519;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.Arrays;
import java.util.BitSet;
public class Curve25519DH extends DHBase {

View File

@@ -15,9 +15,6 @@
*/
package net.schmizz.sshj.transport.kex;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.digest.SHA256;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -23,7 +23,10 @@ import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import static com.hierynomus.sshj.secg.SecgUtils.getDecoded;
import static com.hierynomus.sshj.secg.SecgUtils.getEncoded;

View File

@@ -15,25 +15,12 @@
*/
package net.schmizz.sshj.transport.verification;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.transport.mac.HMACSHA1;
import net.schmizz.sshj.transport.mac.MAC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.*;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
@@ -50,29 +37,38 @@ import java.util.List;
public class OpenSSHKnownHosts
implements HostKeyVerifier {
private static final Logger LOG = LoggerFactory.getLogger(OpenSSHKnownHosts.class);
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Logger log;
protected final File khFile;
protected final List<HostEntry> entries = new ArrayList<HostEntry>();
public OpenSSHKnownHosts(File khFile)
throws IOException {
this(khFile, LoggerFactory.DEFAULT);
}
public OpenSSHKnownHosts(File khFile, LoggerFactory loggerFactory)
throws IOException {
this.khFile = khFile;
log = loggerFactory.getLogger(getClass());
if (khFile.exists()) {
final EntryFactory entryFactory = new EntryFactory();
final BufferedReader br = new BufferedReader(new FileReader(khFile));
try {
// Read in the file, storing each line as an entry
String line;
while ((line = br.readLine()) != null)
while ((line = br.readLine()) != null) {
try {
HostEntry entry = EntryFactory.parseEntry(line);
HostEntry entry = entryFactory.parseEntry(line);
if (entry != null) {
entries.add(entry);
}
} catch (SSHException ignore) {
log.debug("Bad line ({}): {} ", ignore.toString(), line);
} catch (SSHRuntimeException ignore) {
log.debug("Failed to process line ({}): {} ", ignore.toString(), line);
}
}
} finally {
IOUtils.closeQuietly(br);
}
@@ -185,9 +181,11 @@ public class OpenSSHKnownHosts
* <p/>
* Lines starting with `#' and empty lines are ignored as comments.
*/
public static class EntryFactory {
public class EntryFactory {
EntryFactory() {
}
public static HostEntry parseEntry(String line)
public HostEntry parseEntry(String line)
throws IOException {
if (isComment(line)) {
return new CommentEntry(line);
@@ -201,7 +199,7 @@ public class OpenSSHKnownHosts
i++;
}
if(split.length < 3) {
LOG.error("Error reading entry `{}`", line);
log.error("Error reading entry `{}`", line);
return null;
}
final String hostnames = split[i++];
@@ -212,7 +210,7 @@ public class OpenSSHKnownHosts
if (type != KeyType.UNKNOWN) {
final String sKey = split[i++];
key = getKey(sKey);
key = new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
} else if (isBits(sType)) {
type = KeyType.RSA;
// int bits = Integer.valueOf(sType);
@@ -222,11 +220,11 @@ public class OpenSSHKnownHosts
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
} catch (Exception ex) {
LOG.error("Error reading entry `{}`, could not create key", line, ex);
log.error("Error reading entry `{}`, could not create key", line, ex);
return null;
}
} else {
LOG.error("Error reading entry `{}`, could not determine type", line);
log.error("Error reading entry `{}`, could not determine type", line);
return null;
}
@@ -237,12 +235,7 @@ public class OpenSSHKnownHosts
}
}
private static PublicKey getKey(String sKey)
throws IOException {
return new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
}
private static boolean isBits(String type) {
private boolean isBits(String type) {
try {
Integer.parseInt(type);
return true;
@@ -251,17 +244,24 @@ public class OpenSSHKnownHosts
}
}
private static boolean isComment(String line) {
private boolean isComment(String line) {
return line.isEmpty() || line.startsWith("#");
}
public static boolean isHashed(String line) {
public boolean isHashed(String line) {
return line.startsWith("|1|");
}
}
public interface HostEntry {
KeyType getType();
String getFingerprint();
boolean appliesTo(String host)
throws IOException;
boolean appliesTo(KeyType type, String host)
throws IOException;
@@ -279,6 +279,22 @@ public class OpenSSHKnownHosts
this.comment = comment;
}
@Override
public KeyType getType() {
return KeyType.UNKNOWN;
}
@Override
public String getFingerprint() {
return null;
}
@Override
public boolean appliesTo(String host)
throws IOException {
return false;
}
@Override
public boolean appliesTo(KeyType type, String host) {
return false;
@@ -308,6 +324,16 @@ public class OpenSSHKnownHosts
this.key = key;
}
@Override
public KeyType getType() {
return type;
}
@Override
public String getFingerprint() {
return SecurityUtils.getFingerprint(key);
}
@Override
public boolean verify(PublicKey key)
throws IOException {
@@ -349,6 +375,12 @@ public class OpenSSHKnownHosts
return hostnames;
}
@Override
public boolean appliesTo(String host)
throws IOException {
return hosts.contains(host);
}
@Override
public boolean appliesTo(KeyType type, String host)
throws IOException {
@@ -377,6 +409,12 @@ public class OpenSSHKnownHosts
}
}
@Override
public boolean appliesTo(String host)
throws IOException {
return hashedHost.equals(hashHost(host));
}
@Override
public boolean appliesTo(KeyType type, String host)
throws IOException {
@@ -416,14 +454,14 @@ public class OpenSSHKnownHosts
public String getMarkerString() {
return sMarker;
}
public static Marker fromString(String str) {
for (Marker m: values())
if (m.sMarker.equals(str))
return m;
return null;
}
}
}
}

View File

@@ -37,8 +37,7 @@ public class UserAuthImpl
extends AbstractService
implements UserAuth {
private final Promise<Boolean, UserAuthException> authenticated
= new Promise<Boolean, UserAuthException>("authenticated", UserAuthException.chainer);
private final Promise<Boolean, UserAuthException> authenticated;
// Externally available
private volatile String banner = "";
@@ -51,6 +50,7 @@ public class UserAuthImpl
public UserAuthImpl(Transport trans) {
super("ssh-userauth", trans);
authenticated = new Promise<Boolean, UserAuthException>("authenticated", UserAuthException.chainer, trans.getConfig().getLoggerFactory());
}
@Override

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

@@ -19,8 +19,10 @@ package net.schmizz.sshj.userauth.keyprovider;
* @version $Id:$
*/
public enum KeyFormat {
PKCS5,
PKCS8,
OpenSSH,
OpenSSHv1,
PuTTY,
Unknown
}

View File

@@ -15,15 +15,11 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
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 net.schmizz.sshj.common.IOUtils;
import java.io.*;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
public class KeyProviderUtil {
/**
@@ -93,12 +89,15 @@ 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 {
// More general
} else if (header.contains("BEGIN PRIVATE KEY") || header.contains("BEGIN ENCRYPTED PRIVATE KEY")) {
return KeyFormat.PKCS8;
} else {
return KeyFormat.PKCS5;
}
} else if (header.startsWith("PuTTY-User-Key-File-")) {
return KeyFormat.PuTTY;

View File

@@ -19,12 +19,7 @@ import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.KeyType;
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.io.*;
import java.security.PublicKey;

View File

@@ -0,0 +1,270 @@
/*
* 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.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 extends BaseFileKeyProvider {
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@Override
public FileKeyProvider create() {
return new PKCS5KeyFile();
}
@Override
public String getName() {
return "PKCS5";
}
}
/**
* Indicates a format issue with PKCS5 data
*/
public static class FormatException
extends IOException {
FormatException(String msg) {
super(msg);
}
}
/**
* Indicates a problem decrypting the data
*/
public static class DecryptException
extends IOException {
DecryptException(String msg) {
super(msg);
}
}
protected byte[] data;
protected KeyPair readKeyPair()
throws IOException {
BufferedReader reader = new BufferedReader(resource.getReader());
try {
String line = null;
Cipher cipher = new NoneCipher();
StringBuffer sb = new StringBuffer();
byte[] iv = new byte[0]; // salt
while ((line = reader.readLine()) != null) {
if (line.startsWith("-----BEGIN ") && line.endsWith(" PRIVATE KEY-----")) {
int end = line.length() - 17;
if (end > 11) {
String s = line.substring(11, line.length() - 17);
if ("RSA".equals(s)) {
type = KeyType.RSA;
} else if ("DSA".equals(s)) {
type = KeyType.DSA;
} else if ("DSS".equals(s)) {
type = KeyType.DSA;
} else {
throw new FormatException("Unrecognized PKCS5 key type: " + s);
}
} else {
throw new FormatException("Bad header; possibly PKCS8 format?");
}
} else if (line.startsWith("-----END")) {
break;
} else if (type != null) {
if (line.startsWith("Proc-Type: ")) {
if (!"4,ENCRYPTED".equals(line.substring(11))) {
throw new FormatException("Unrecognized Proc-Type: " + line.substring(11));
}
} else if (line.startsWith("DEK-Info: ")) {
int ptr = line.indexOf(",");
if (ptr == -1) {
throw new FormatException("Unrecognized DEK-Info: " + line.substring(10));
} else {
String algorithm = line.substring(10, ptr);
if ("DES-EDE3-CBC".equals(algorithm)) {
cipher = new TripleDESCBC();
} else if ("AES-128-CBC".equals(algorithm)) {
cipher = new AES128CBC();
} else if ("AES-192-CBC".equals(algorithm)) {
cipher = new AES192CBC();
} else if ("AES-256-CBC".equals(algorithm)) {
cipher = new AES256CBC();
} else {
throw new FormatException("Not a supported algorithm: " + algorithm);
}
iv = Arrays.copyOfRange(DatatypeConverter.parseHexBinary(line.substring(ptr + 1)), 0, cipher.getIVSize());
}
} else if (line.length() > 0) {
sb.append(line);
}
}
}
if (type == null) {
throw new FormatException("PKCS5 header not found");
}
ASN1Data asn = new ASN1Data(data = decrypt(Base64.decode(sb.toString()), cipher, iv));
switch (type) {
case RSA: {
KeyFactory factory = KeyFactory.getInstance("RSA");
asn.readNext();
BigInteger modulus = asn.readNext();
BigInteger pubExp = asn.readNext();
BigInteger prvExp = asn.readNext();
PublicKey pubKey = factory.generatePublic(new RSAPublicKeySpec(modulus, pubExp));
PrivateKey prvKey = factory.generatePrivate(new RSAPrivateKeySpec(modulus, prvExp));
return new KeyPair(pubKey, prvKey);
}
case DSA: {
KeyFactory factory = KeyFactory.getInstance("DSA");
asn.readNext();
BigInteger p = asn.readNext();
BigInteger q = asn.readNext();
BigInteger g = asn.readNext();
BigInteger pub = asn.readNext();
BigInteger prv = asn.readNext();
PublicKey pubKey = factory.generatePublic(new DSAPublicKeySpec(pub, p, q, g));
PrivateKey prvKey = factory.generatePrivate(new DSAPrivateKeySpec(prv, p, q, g));
return new KeyPair(pubKey, prvKey);
}
default:
throw new IOException("Unrecognized PKCS5 key type: " + type);
}
} catch (NoSuchAlgorithmException e) {
throw new IOException(e);
} catch (InvalidKeySpecException e) {
throw new IOException(e);
} finally {
reader.close();
}
}
@Override
public String toString() {
return "PKCS5KeyFile{resource=" + resource + "}";
}
private byte[] getPassphraseBytes() {
CharBuffer cb = CharBuffer.wrap(pwdf.reqPassword(resource));
ByteBuffer bb = IOUtils.UTF8.encode(cb);
byte[] result = Arrays.copyOfRange(bb.array(), bb.position(), bb.limit());
Arrays.fill(cb.array(), '\u0000');
Arrays.fill(bb.array(), (byte) 0);
return result;
}
private byte[] decrypt(byte[] raw, Cipher cipher, byte[] iv) throws DecryptException {
if (pwdf == null) {
return raw;
}
Digest md5 = new MD5();
int bsize = cipher.getBlockSize();
int hsize = md5.getBlockSize();
int hnlen = bsize / hsize * hsize + (bsize % hsize == 0 ? 0 : hsize);
do {
md5.init();
byte[] hn = new byte[hnlen];
byte[] tmp = null;
byte[] passphrase = getPassphraseBytes();
for (int i = 0; i + hsize <= hn.length; ) {
if (tmp != null) {
md5.update(tmp, 0, tmp.length);
}
md5.update(passphrase, 0, passphrase.length);
md5.update(iv, 0, iv.length > 8 ? 8 : iv.length);
tmp = md5.digest();
System.arraycopy(tmp, 0, hn, i, tmp.length);
i += tmp.length;
}
Arrays.fill(passphrase, (byte) 0);
byte[] key = Arrays.copyOfRange(hn, 0, bsize);
cipher.init(Cipher.Mode.Decrypt, key, iv);
Arrays.fill(key, (byte) 0);
byte[] decrypted = Arrays.copyOf(raw, raw.length);
cipher.update(decrypted, 0, decrypted.length);
if (ASN1Data.MAGIC == decrypted[0]) {
return decrypted;
}
} while (pwdf.shouldRetry(resource));
throw new DecryptException("Decryption failed");
}
class ASN1Data {
static final byte MAGIC = (byte) 0x30;
private byte[] buff;
private int index, length;
ASN1Data(byte[] buff) throws FormatException {
this.buff = buff;
index = 0;
if (buff[index++] != MAGIC) {
throw new FormatException("Not ASN.1 data");
}
length = buff[index++] & 0xff;
if ((length & 0x80) != 0) {
int counter = length & 0x7f;
length = 0;
while (counter-- > 0) {
length = (length << 8) + (buff[index++] & 0xff);
}
}
if ((index + length) > buff.length) {
throw new FormatException("Length mismatch: " + buff.length + " != " + (index + length));
}
}
BigInteger readNext() throws IOException {
if (index >= length) {
throw new EOFException();
} else if (buff[index++] != 0x02) {
throw new IOException("Not an int code: " + Integer.toHexString(0xff & buff[index]));
}
int length = buff[index++] & 0xff;
if ((length & 0x80) != 0) {
int counter = length & 0x7f;
length = 0;
while (counter-- > 0) {
length = (length << 8) + (buff[index++] & 0xff);
}
}
byte[] sequence = new byte[length];
System.arraycopy(buff, index, sequence, 0, length);
index += length;
return new BigInteger(sequence);
}
}
}

View File

@@ -15,14 +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.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.PrivateKeyFileResource;
import net.schmizz.sshj.userauth.password.PrivateKeyReaderResource;
import net.schmizz.sshj.userauth.password.PrivateKeyStringResource;
import net.schmizz.sshj.userauth.password.Resource;
import java.io.IOException;
import java.security.KeyPair;
import org.bouncycastle.openssl.EncryptionException;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
@@ -32,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> {
@@ -58,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 {
@@ -170,4 +100,4 @@ public class PKCS8KeyFile
public String toString() {
return "PKCS8KeyFile{resource=" + resource + "}";
}
}
}

View File

@@ -15,45 +15,21 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import org.bouncycastle.util.encoders.Hex;
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 java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.util.encoders.Hex;
import net.schmizz.sshj.common.Base64;
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.PrivateKeyReaderResource;
import net.schmizz.sshj.userauth.password.PrivateKeyStringResource;
import net.schmizz.sshj.userauth.password.Resource;
/**
* <h2>Sample PuTTY file format</h2>
@@ -80,7 +56,7 @@ import net.schmizz.sshj.userauth.password.Resource;
*
* @version $Id:$
*/
public class PuTTYKeyFile implements FileKeyProvider {
public class PuTTYKeyFile extends BaseFileKeyProvider {
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@@ -99,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.
*/
@@ -174,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
@@ -189,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 {
@@ -198,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();
@@ -218,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 {
@@ -227,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()));
}
}
@@ -243,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;
}
@@ -262,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"));
}
}
@@ -316,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);
}
}
@@ -330,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();
@@ -358,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);
}
}
@@ -379,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);
}
}
@@ -401,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];
@@ -423,4 +335,4 @@ public class PuTTYKeyFile implements FileKeyProvider {
return new BigInteger(read());
}
}
}
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.userauth.method;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.TransportException;
@@ -22,14 +23,13 @@ import net.schmizz.sshj.userauth.AuthParams;
import net.schmizz.sshj.userauth.UserAuthException;
import net.schmizz.sshj.userauth.password.AccountResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** This abstract class for {@link AuthMethod} implements common or default functionality. */
public abstract class AbstractAuthMethod
implements AuthMethod {
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
protected Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
private final String name;
@@ -41,6 +41,11 @@ public abstract class AbstractAuthMethod
this.name = name;
}
@Override
public void setLoggerFactory(LoggerFactory loggerFactory) {
log = loggerFactory.getLogger(getClass());
}
@Override
public String getName() {
return name;

View File

@@ -15,26 +15,19 @@
*/
package net.schmizz.sshj.userauth.method;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import net.schmizz.sshj.common.Buffer.BufferException;
import net.schmizz.sshj.common.Buffer.PlainBuffer;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.UserAuthException;
import org.ietf.jgss.*;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
/** Implements authentication by GSS-API. */
public class AuthGssApiWithMic

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.userauth.method;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.AuthParams;
@@ -44,4 +45,5 @@ public interface AuthMethod
/** @return whether authentication should be reattempted if it failed. */
boolean shouldRetry();
void setLoggerFactory(LoggerFactory loggerFactory);
}

View File

@@ -25,8 +25,6 @@ import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUpdateProvider;
import net.schmizz.sshj.userauth.password.Resource;
import static net.schmizz.sshj.common.Message.USERAUTH_REQUEST;
/** Implements the {@code password} authentication method. Password-change request handling is not currently supported. */
public class AuthPassword
extends AbstractAuthMethod {

View File

@@ -15,10 +15,10 @@
*/
package net.schmizz.sshj.userauth.method;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
@@ -29,12 +29,11 @@ public class PasswordResponseProvider
public static final Pattern DEFAULT_PROMPT_PATTERN = Pattern.compile(".*[pP]assword:\\s?\\z", Pattern.DOTALL);
private final Logger log = LoggerFactory.getLogger(getClass());
private static final char[] EMPTY_RESPONSE = new char[0];
private final Pattern promptPattern;
private final PasswordFinder pwdf;
private final Logger log;
private Resource resource;
@@ -43,8 +42,13 @@ public class PasswordResponseProvider
}
public PasswordResponseProvider(PasswordFinder pwdf, Pattern promptPattern) {
this(pwdf, promptPattern, LoggerFactory.DEFAULT);
}
public PasswordResponseProvider(PasswordFinder pwdf, Pattern promptPattern, LoggerFactory loggerFactory) {
this.pwdf = pwdf;
this.promptPattern = promptPattern;
log = loggerFactory.getLogger(getClass());
}
@Override

View File

@@ -15,11 +15,7 @@
*/
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;
import java.io.*;
public class PrivateKeyFileResource
extends Resource<File> {

View File

@@ -15,23 +15,32 @@
*/
package net.schmizz.sshj.xfer;
import net.schmizz.sshj.common.LoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractFileTransfer {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final LoggerFactory loggerFactory;
protected final Logger log;
public static final LoggingTransferListener LOGGING_TRANSFER_LISTENER = new LoggingTransferListener();
private final LoggingTransferListener loggingTransferListener;
private volatile TransferListener transferListener = LOGGING_TRANSFER_LISTENER;
private volatile TransferListener transferListener;
protected AbstractFileTransfer(LoggerFactory loggerFactory) {
this.loggerFactory = loggerFactory;
log = loggerFactory.getLogger(getClass());
loggingTransferListener = new LoggingTransferListener(loggerFactory);
transferListener = loggingTransferListener;
}
public TransferListener getTransferListener() {
return transferListener;
}
public void setTransferListener(TransferListener transferListener) {
this.transferListener = (transferListener == null) ? LOGGING_TRANSFER_LISTENER : transferListener;
this.transferListener = (transferListener == null) ? loggingTransferListener : transferListener;
}
}
}

View File

@@ -18,13 +18,7 @@ package net.schmizz.sshj.xfer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

View File

@@ -15,31 +15,34 @@
*/
package net.schmizz.sshj.xfer;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.StreamCopier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class LoggingTransferListener
implements TransferListener {
private final Logger log = LoggerFactory.getLogger(getClass());
private final LoggerFactory loggerFactory;
private final Logger log;
private final String relPath;
public LoggingTransferListener() {
this("");
public LoggingTransferListener(LoggerFactory loggerFactory) {
this("", loggerFactory);
}
private LoggingTransferListener(String relPath) {
private LoggingTransferListener(String relPath, LoggerFactory loggerFactory) {
this.relPath = relPath;
this.loggerFactory = loggerFactory;
log = loggerFactory.getLogger(getClass());
}
@Override
public TransferListener directory(String name) {
log.debug("started transferring directory `{}`", name);
return new LoggingTransferListener(relPath + name + "/");
return new LoggingTransferListener(relPath + name + "/", loggerFactory);
}
@Override

View File

@@ -16,21 +16,18 @@
package net.schmizz.sshj.xfer.scp;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.TransferListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
/** @see <a href="http://blogs.sun.com/janp/entry/how_the_scp_protocol_works">SCP Protocol</a> */
class SCPEngine {
@@ -38,7 +35,8 @@ class SCPEngine {
private static final char LF = '\n';
private final Logger log = LoggerFactory.getLogger(getClass());
private final LoggerFactory loggerFactory;
private final Logger log;
private final SessionFactory host;
private final TransferListener listener;
@@ -46,9 +44,11 @@ class SCPEngine {
private Command scp;
private int exitStatus;
SCPEngine(SessionFactory host, TransferListener listener) {
SCPEngine(SessionFactory host, TransferListener listener, LoggerFactory loggerFactory) {
this.host = host;
this.listener = listener;
this.loggerFactory = loggerFactory;
log = loggerFactory.getLogger(getClass());
}
public int getExitStatus() {
@@ -60,7 +60,7 @@ class SCPEngine {
int code = scp.getInputStream().read();
switch (code) {
case -1:
String stderr = IOUtils.readFully(scp.getErrorStream()).toString();
String stderr = IOUtils.readFully(scp.getErrorStream(), loggerFactory).toString();
if (!stderr.isEmpty())
stderr = ". Additional info: `" + stderr + "`";
throw new SCPException("EOF while expecting response to protocol message" + stderr);
@@ -140,7 +140,7 @@ class SCPEngine {
}
long transferToRemote(StreamCopier.Listener listener, InputStream src, long length) throws IOException {
return new StreamCopier(src, scp.getOutputStream())
return new StreamCopier(src, scp.getOutputStream(), loggerFactory)
.bufSize(scp.getRemoteMaxPacketSize()).length(length)
.keepFlushing(false)
.listener(listener)
@@ -148,7 +148,7 @@ class SCPEngine {
}
long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long length) throws IOException {
return new StreamCopier(scp.getInputStream(), dest)
return new StreamCopier(scp.getInputStream(), dest, loggerFactory)
.bufSize(scp.getLocalMaxPacketSize()).length(length)
.keepFlushing(false)
.listener(listener)

View File

@@ -15,12 +15,9 @@
*/
package net.schmizz.sshj.xfer.scp;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.AbstractFileTransfer;
import net.schmizz.sshj.xfer.FileSystemFile;
import net.schmizz.sshj.xfer.FileTransfer;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalSourceFile;
import net.schmizz.sshj.xfer.*;
import java.io.IOException;
@@ -34,7 +31,8 @@ public class SCPFileTransfer
private final SessionFactory sessionFactory;
private int bandwidthLimit;
public SCPFileTransfer(SessionFactory sessionFactory) {
public SCPFileTransfer(SessionFactory sessionFactory, LoggerFactory loggerFactory) {
super(loggerFactory);
this.sessionFactory = sessionFactory;
this.bandwidthLimit = DEFAULT_BANDWIDTH_LIMIT;
}
@@ -48,7 +46,7 @@ public class SCPFileTransfer
}
private SCPEngine newSCPEngine() {
return new SCPEngine(sessionFactory, getTransferListener());
return new SCPEngine(sessionFactory, getTransferListener(), loggerFactory);
}
@Override

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

@@ -19,7 +19,6 @@ import com.hierynomus.sshj.test.HttpServer;
import com.hierynomus.sshj.test.SshFixture;
import com.hierynomus.sshj.test.util.FileUtil;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder;
import net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectListener;
@@ -34,9 +33,7 @@ import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicInteger;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

View File

@@ -18,8 +18,6 @@ package com.hierynomus.sshj.test;
import org.junit.rules.ExternalResource;
import org.junit.rules.TemporaryFolder;
import java.io.File;
/**
* Can be used to setup a test HTTP server
*/

View File

@@ -28,18 +28,13 @@ import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.kex.DHGEXServer;
import org.apache.sshd.server.kex.DHGServer;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import static org.hamcrest.MatcherAssert.assertThat;
@RunWith(Parameterized.class)
public class KeyExchangeTest extends BaseAlgorithmTest {

View File

@@ -139,7 +139,7 @@ public class AuthPasswordTest {
}
private static class StaticPasswordUpdateProvider implements PasswordUpdateProvider {
private Stack<String> newPasswords = new Stack<>();
private Stack<String> newPasswords = new Stack<String>();
public StaticPasswordUpdateProvider(String... newPasswords) {
for (int i = newPasswords.length - 1; i >= 0; i--) {

View File

@@ -15,12 +15,11 @@
*/
package net.schmizz.sshj.common;
import static org.junit.Assert.fail;
import net.schmizz.sshj.common.Buffer.PlainBuffer;
import org.junit.Test;
import static org.junit.Assert.fail;
public class BufferTest {
// Issue 72: previously, it entered an infinite loop trying to establish the buffer size

View File

@@ -15,15 +15,14 @@
*/
package net.schmizz.sshj.keyprovider;
import static org.junit.Assert.assertEquals;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
import static org.junit.Assert.assertEquals;
public class KeyProviderUtilTest {
@@ -35,6 +34,12 @@ public class KeyProviderUtilTest {
assertEquals(KeyFormat.OpenSSH, format);
}
@Test
public void testPkcs5() throws IOException {
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs5"));
assertEquals(KeyFormat.PKCS5, format);
}
@Test
public void testPkcs8() throws IOException {
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs8"));

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

@@ -0,0 +1,91 @@
/*
* 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.keyprovider;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.PKCS5KeyFile;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.Resource;
import net.schmizz.sshj.util.KeyUtil;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import static org.junit.Assert.assertEquals;
public class PKCS5KeyFileTest {
static final FileKeyProvider rsa = new PKCS5KeyFile();
static final String modulus = "a19f65e93926d9a2f5b52072db2c38c54e6cf0113d31fa92ff827b0f3bec609c45ea84264c88e64adba11ff093ed48ee0ed297757654b0884ab5a7e28b3c463bc9074b32837a2b69b61d914abf1d74ccd92b20fa44db3b31fb208c0dd44edaeb4ab097118e8ee374b6727b89ad6ce43f1b70c5a437ccebc36d2dad8ae973caad15cd89ae840fdae02cae42d241baef8fda8aa6bbaa54fd507a23338da6f06f61b34fb07d560e63fbce4a39c073e28573c2962cedb292b14b80d1b4e67b0465f2be0e38526232d0a7f88ce91a055fde082038a87ed91f3ef5ff971e30ea6cccf70d38498b186621c08f8fdceb8632992b480bf57fc218e91f2ca5936770fe9469";
static final String pubExp = "23";
static final String privExp = "57bcee2e2656eb2c94033d802dd62d726c6705fabad1fd0df86b67600a96431301620d395cbf5871c7af3d3974dfe5c30f5c60d95d7e6e75df69ed6c5a36a9c8aef554b5058b76a719b8478efa08ad1ebf08c8c25fe4b9bc0bfbb9be5d4f60e6213b4ab1c26ad33f5bba7d93e1cd65f65f5a79eb6ebfb32f930a2b0244378b4727acf83b5fb376c38d4abecc5dc3fc399e618e792d4c745d2dbbb9735242e5033129f2985ca3e28fa33cad2afe3e70e1b07ed2b6ec8a3f843fb4bffe3385ad211c6600618488f4ac70397e8eb036b82d811283dc728504cddbe1533c4dd31b1ec99ffa74fd0e3883a9cb3e2315cc1a56f55d38ed40520dd9ec91b4d2dd790d1b";
final char[] correctPassphrase = "passphrase".toCharArray();
final char[] incorrectPassphrase = "incorrect".toCharArray();
@Before
public void setUp()
throws UnsupportedEncodingException, GeneralSecurityException {
rsa.init(new File("src/test/resources/id_rsa"));
}
@Test
public void testKeys()
throws IOException, GeneralSecurityException {
assertEquals(KeyUtil.newRSAPublicKey(modulus, pubExp), rsa.getPublic());
assertEquals(KeyUtil.newRSAPrivateKey(modulus, privExp), rsa.getPrivate());
}
@Test
public void testType()
throws IOException {
assertEquals(rsa.getType(), KeyType.RSA);
}
final PasswordFinder givesOn3rdTry = new PasswordFinder() {
int triesLeft = 3;
@Override
public char[] reqPassword(Resource resource) {
if (triesLeft == 0)
return correctPassphrase;
else {
triesLeft--;
return incorrectPassphrase;
}
}
@Override
public boolean shouldRetry(Resource resource) {
return triesLeft >= 0;
}
};
@Test
public void retries()
throws IOException, GeneralSecurityException {
FileKeyProvider rsa = new PKCS5KeyFile();
rsa.init(new File("src/test/resources/rsa.pk5"), givesOn3rdTry);
assertEquals(KeyUtil.newRSAPublicKey(modulus, pubExp), rsa.getPublic());
assertEquals(KeyUtil.newRSAPrivateKey(modulus, privExp), rsa.getPrivate());
}
}

View File

@@ -15,15 +15,14 @@
*/
package net.schmizz.sshj.keyprovider;
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.Resource;
import org.junit.Test;
import java.io.IOException;
import java.io.StringReader;
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.Resource;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

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