mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 07:10:53 +03:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ca2bbd633 | ||
|
|
256e65dea4 | ||
|
|
1feb7fe9a6 | ||
|
|
d95b4db930 | ||
|
|
677f482a69 | ||
|
|
179b30ef4e | ||
|
|
f59bbccc5f | ||
|
|
bf34072c3a | ||
|
|
771751ca4c | ||
|
|
968d4284a0 | ||
|
|
63927a3e2b | ||
|
|
ac262f8086 | ||
|
|
6e56cd9d0a | ||
|
|
2f6025d9ba | ||
|
|
275e98e55b | ||
|
|
655d070571 | ||
|
|
c9775ca2c7 | ||
|
|
a2fb4fbd98 | ||
|
|
6185ac4db8 | ||
|
|
e420593fa9 | ||
|
|
68b924863e | ||
|
|
613ace1864 | ||
|
|
78e0ecd264 | ||
|
|
64085e62f4 | ||
|
|
8c7d2fa8d0 | ||
|
|
766d292bad | ||
|
|
a40957fffc | ||
|
|
8ffd852e67 | ||
|
|
b90be512e7 | ||
|
|
c0d49cf6b3 | ||
|
|
1b5b2b25b7 | ||
|
|
1dad19ca6e | ||
|
|
90fa26925d | ||
|
|
9425300262 | ||
|
|
f2bfe9bfcf | ||
|
|
71498ad961 | ||
|
|
7b8b1cfdf5 | ||
|
|
3f29879eca | ||
|
|
79c1ae2bb0 | ||
|
|
819d411cf1 | ||
|
|
6579f6f710 | ||
|
|
cf5830eda5 | ||
|
|
36ad389ccf | ||
|
|
f63a88ec9f | ||
|
|
7379a89268 | ||
|
|
219901211e | ||
|
|
8c1329036a | ||
|
|
733c19350c | ||
|
|
e5ec84c06a | ||
|
|
ed0156c985 | ||
|
|
6321685881 | ||
|
|
f6b4d47945 | ||
|
|
d198ef121c | ||
|
|
7786468875 | ||
|
|
0847e8460a | ||
|
|
62b8726807 | ||
|
|
a6af27ae91 | ||
|
|
628cbf5eba | ||
|
|
e134e00574 | ||
|
|
1caa7ac722 | ||
|
|
4183776adb | ||
|
|
791f112752 | ||
|
|
233f3765c9 | ||
|
|
5f292d398f | ||
|
|
ba347f927d | ||
|
|
6f9ecf69e4 | ||
|
|
e78ae4dbeb | ||
|
|
618f2fd111 | ||
|
|
8503046302 | ||
|
|
c6cde27e4b | ||
|
|
113aa0aebd | ||
|
|
e7c50165c7 | ||
|
|
09616c4834 | ||
|
|
df710d8dc9 | ||
|
|
df82774ea3 | ||
|
|
caa6cca665 | ||
|
|
9a5ccefb5d | ||
|
|
90f8c592b0 | ||
|
|
f491e8d101 | ||
|
|
77c10334f1 | ||
|
|
0edc4a5787 | ||
|
|
b43cff07bf | ||
|
|
1ab72b7eaf | ||
|
|
3229584a95 | ||
|
|
364a82154d | ||
|
|
11fbf2964b | ||
|
|
a248d50301 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -12,6 +12,9 @@
|
||||
# Output dirs
|
||||
target/
|
||||
build/
|
||||
docs/
|
||||
.gradle/
|
||||
sshj.jar
|
||||
|
||||
|
||||
# MacOS X
|
||||
.DS_Store
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
language: java
|
||||
sudo: false
|
||||
jdk:
|
||||
- oraclejdk7
|
||||
- oraclejdk8
|
||||
|
||||
28
README.adoc
28
README.adoc
@@ -1,10 +1,13 @@
|
||||
= sshj - SSHv2 library for Java
|
||||
Jeroen van Erp
|
||||
:sshj_groupid: com.hierynomus
|
||||
:sshj_version: 0.15.0
|
||||
:sshj_version: 0.19.0
|
||||
:source-highlighter: pygments
|
||||
|
||||
image::https://travis-ci.org/hierynomus/sshj.svg?branch=master[]
|
||||
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"]
|
||||
|
||||
To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :)
|
||||
|
||||
@@ -96,6 +99,27 @@ 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)::
|
||||
* *Introduced breaking change in SFTP copy behaviour*: Previously an SFTP copy operation would behave differently if both source and target were folders with different names.
|
||||
In this case instead of copying the contents of the source into the target directory, the directory itself was copied as a sub directory of the target directory.
|
||||
This behaviour has been removed in favour of the default behaviour which is to copy the contents of the source into the target. Bringing the behaviour in line with how SCP works.
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/252[#252] (via: https://github.com/hierynomus/sshj/pulls/253[#253]): Same name subdirs are no longer merged by accident
|
||||
SSHJ 0.16.0 (2016-04-11)::
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/239[#239]: Remote port forwards did not work if you used the empty string as address, or a catch-all address.
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/242[#242]: Added OSGI headers to sources jar manifest
|
||||
|
||||
340
build.gradle
340
build.gradle
@@ -1,198 +1,218 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
id "maven"
|
||||
id "idea"
|
||||
id "signing"
|
||||
id "osgi"
|
||||
id "org.ajoberstar.release-opinion" version "1.4.0-rc.1"
|
||||
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.*"
|
||||
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.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 = project.tasks.jar.manifest
|
||||
}
|
||||
|
||||
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.release.dependsOn 'build', 'uploadArchives'
|
||||
project.tasks.release.dependsOn([project.tasks.build, project.tasks.bintrayUpload])
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.transport;
|
||||
|
||||
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 java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IdentificationStringParser {
|
||||
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;
|
||||
}
|
||||
|
||||
public String parseIdentificationString() throws IOException {
|
||||
for (;;) {
|
||||
Buffer.PlainBuffer lineBuffer = new Buffer.PlainBuffer();
|
||||
int lineStartPos = buffer.rpos();
|
||||
for (;;) {
|
||||
if (buffer.available() == 0) {
|
||||
buffer.rpos(lineStartPos);
|
||||
return "";
|
||||
}
|
||||
byte b = buffer.readByte();
|
||||
lineBuffer.putByte(b);
|
||||
if (b == '\n') {
|
||||
if (checkForIdentification(lineBuffer)) {
|
||||
return readIdentification(lineBuffer);
|
||||
} else {
|
||||
logHeaderLine(lineBuffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) {
|
||||
|
||||
}
|
||||
|
||||
private String readIdentification(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException, TransportException {
|
||||
byte[] bytes = new byte[lineBuffer.available()];
|
||||
lineBuffer.readRawBytes(bytes);
|
||||
if (bytes.length > 255) {
|
||||
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);
|
||||
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;
|
||||
// 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));
|
||||
}
|
||||
|
||||
// Strip off the \r\n
|
||||
return new String(bytes, 0, bytes.length - 2);
|
||||
}
|
||||
|
||||
private boolean checkForIdentification(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException {
|
||||
if (lineBuffer.available() < 4) {
|
||||
return false;
|
||||
}
|
||||
byte[] buf = new byte[4];
|
||||
lineBuffer.readRawBytes(buf);
|
||||
// Reset
|
||||
lineBuffer.rpos(0);
|
||||
return Arrays.equals(EXPECTED_START_BYTES, buf);
|
||||
}
|
||||
}
|
||||
@@ -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))));
|
||||
}
|
||||
}
|
||||
918
src/main/java/djb/Curve25519.java
Normal file
918
src/main/java/djb/Curve25519.java
Normal 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
|
||||
);
|
||||
}
|
||||
@@ -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)}. */
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_14_0";
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
38
src/main/java/net/schmizz/sshj/common/LoggerFactory.java
Normal file
38
src/main/java/net/schmizz/sshj/common/LoggerFactory.java
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -156,4 +156,4 @@ public interface Connection {
|
||||
* @return The configured {@link net.schmizz.keepalive.KeepAlive} mechanism.
|
||||
*/
|
||||
KeepAlive getKeepAlive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -94,4 +94,4 @@ public abstract class AbstractDirectChannel
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -73,4 +73,4 @@ public abstract class AbstractForwardedChannel
|
||||
return origPort;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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(";");
|
||||
|
||||
@@ -36,7 +36,7 @@ public class FileMode {
|
||||
/** directory */
|
||||
DIRECTORY(0040000),
|
||||
/** symbolic link */
|
||||
SYMKLINK(0120000),
|
||||
SYMLINK(0120000),
|
||||
/** unknown */
|
||||
UNKNOWN(0);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -17,13 +17,7 @@ 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;
|
||||
@@ -41,6 +35,7 @@ public class SFTPFileTransfer
|
||||
private volatile boolean preserveAttributes = true;
|
||||
|
||||
public SFTPFileTransfer(SFTPEngine engine) {
|
||||
super(engine.getLoggerFactory());
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
@@ -67,7 +62,7 @@ public class SFTPFileTransfer
|
||||
@Override
|
||||
public void upload(LocalSourceFile localFile, String remotePath)
|
||||
throws IOException {
|
||||
new Uploader().upload(getTransferListener(), localFile, remotePath);
|
||||
new Uploader(localFile, remotePath).upload(getTransferListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,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)
|
||||
@@ -173,6 +168,31 @@ public class SFTPFileTransfer
|
||||
|
||||
private class Uploader {
|
||||
|
||||
private final LocalSourceFile source;
|
||||
private final String remote;
|
||||
|
||||
private Uploader(final LocalSourceFile source, final String remote) {
|
||||
this.source = source;
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
private void upload(final TransferListener listener) throws IOException {
|
||||
if (source.isDirectory()) {
|
||||
makeDirIfNotExists(remote); // Ensure that the directory exists
|
||||
uploadDir(listener.directory(source.getName()), source, remote);
|
||||
setAttributes(source, remote);
|
||||
} else if (source.isFile() && isDirectory(remote)) {
|
||||
String adjustedRemote = engine.getPathHelper().adjustForParent(this.remote, source.getName());
|
||||
uploadFile(listener.file(source.getName(), source.getLength()), source, adjustedRemote);
|
||||
setAttributes(source, adjustedRemote);
|
||||
} else if (source.isFile()) {
|
||||
uploadFile(listener.file(source.getName(), source.getLength()), source, remote);
|
||||
setAttributes(source, remote);
|
||||
} else {
|
||||
throw new IOException(source + " is not a file or directory");
|
||||
}
|
||||
}
|
||||
|
||||
private void upload(final TransferListener listener,
|
||||
final LocalSourceFile local,
|
||||
final String remote)
|
||||
@@ -182,20 +202,26 @@ public class SFTPFileTransfer
|
||||
adjustedPath = uploadDir(listener.directory(local.getName()), local, remote);
|
||||
} else if (local.isFile()) {
|
||||
adjustedPath = uploadFile(listener.file(local.getName(), local.getLength()), local, remote);
|
||||
} else
|
||||
} else {
|
||||
throw new IOException(local + " is not a file or directory");
|
||||
if (getPreserveAttributes())
|
||||
engine.setAttributes(adjustedPath, getAttributes(local));
|
||||
}
|
||||
setAttributes(local, adjustedPath);
|
||||
}
|
||||
|
||||
private void setAttributes(LocalSourceFile local, String remotePath) throws IOException {
|
||||
if (getPreserveAttributes()) {
|
||||
engine.setAttributes(remotePath, getAttributes(local));
|
||||
}
|
||||
}
|
||||
|
||||
private String uploadDir(final TransferListener listener,
|
||||
final LocalSourceFile local,
|
||||
final String remote)
|
||||
throws IOException {
|
||||
final String adjusted = prepareDir(local, remote);
|
||||
makeDirIfNotExists(remote);
|
||||
for (LocalSourceFile f : local.getChildren(getUploadFilter()))
|
||||
upload(listener, f, adjusted);
|
||||
return adjusted;
|
||||
upload(listener, f, engine.getPathHelper().adjustForParent(remote, f.getName()));
|
||||
return remote;
|
||||
}
|
||||
|
||||
private String uploadFile(final StreamCopier.Listener listener,
|
||||
@@ -203,52 +229,72 @@ public class SFTPFileTransfer
|
||||
final String remote)
|
||||
throws IOException {
|
||||
final String adjusted = prepareFile(local, remote);
|
||||
final RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE,
|
||||
OpenMode.CREAT,
|
||||
OpenMode.TRUNC));
|
||||
RemoteFile rf = null;
|
||||
InputStream fis = null;
|
||||
RemoteFile.RemoteFileOutputStream rfos = null;
|
||||
try {
|
||||
final InputStream fis = local.getInputStream();
|
||||
final RemoteFile.RemoteFileOutputStream rfos = rf.new RemoteFileOutputStream(0, 16);
|
||||
try {
|
||||
new StreamCopier(fis, rfos)
|
||||
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
|
||||
.keepFlushing(false)
|
||||
.listener(listener)
|
||||
.copy();
|
||||
} finally {
|
||||
fis.close();
|
||||
rfos.close();
|
||||
}
|
||||
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 {
|
||||
rf.close();
|
||||
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;
|
||||
}
|
||||
|
||||
private String prepareDir(final LocalSourceFile local, final String remote)
|
||||
throws IOException {
|
||||
final FileAttributes attrs;
|
||||
private boolean makeDirIfNotExists(final String remote) throws IOException {
|
||||
try {
|
||||
attrs = engine.stat(remote);
|
||||
FileAttributes attrs = engine.stat(remote);
|
||||
if (attrs.getMode().getType() != FileMode.Type.DIRECTORY) {
|
||||
throw new IOException(remote + " exists and should be a directory, but was a " + attrs.getMode().getType());
|
||||
}
|
||||
// Was not created, but existed.
|
||||
return false;
|
||||
} catch (SFTPException e) {
|
||||
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
|
||||
log.debug("probeDir: {} does not exist, creating", remote);
|
||||
log.debug("makeDir: {} does not exist, creating", remote);
|
||||
engine.makeDir(remote);
|
||||
return remote;
|
||||
} else
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY)
|
||||
if (engine.getPathHelper().getComponents(remote).getName().equals(local.getName())) {
|
||||
log.debug("probeDir: {} already exists", remote);
|
||||
return remote;
|
||||
return true;
|
||||
} else {
|
||||
log.debug("probeDir: {} already exists, path adjusted for {}", remote, local.getName());
|
||||
return prepareDir(local, engine.getPathHelper().adjustForParent(remote, local.getName()));
|
||||
throw e;
|
||||
}
|
||||
else
|
||||
throw new IOException(attrs.getMode().getType() + " file already exists at " + remote);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDirectory(final String remote) throws IOException {
|
||||
try {
|
||||
FileAttributes attrs = engine.stat(remote);
|
||||
return attrs.getMode().getType() == FileMode.Type.DIRECTORY;
|
||||
} catch (SFTPException e) {
|
||||
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
|
||||
log.debug("isDir: {} does not exist", remote);
|
||||
return false;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String prepareFile(final LocalSourceFile local, final String remote)
|
||||
@@ -264,8 +310,7 @@ public class SFTPFileTransfer
|
||||
throw e;
|
||||
}
|
||||
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY) {
|
||||
log.debug("probeFile: {} was directory, path adjusted for {}", remote, local.getName());
|
||||
return engine.getPathHelper().adjustForParent(remote, local.getName());
|
||||
throw new IOException("Trying to upload file " + local.getName() + " to path " + remote + " but that is a directory");
|
||||
} else {
|
||||
log.debug("probeFile: {} is a {} file that will be replaced", remote, attrs.getMode().getType());
|
||||
return remote;
|
||||
@@ -281,5 +326,4 @@ public class SFTPFileTransfer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -235,4 +235,4 @@ public interface Transport
|
||||
* @param e The exception that occurred.
|
||||
*/
|
||||
void die(Exception e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,22 +15,17 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import com.hierynomus.sshj.transport.IdentificationStringParser;
|
||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.sshj.AbstractService;
|
||||
import net.schmizz.sshj.Config;
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.Service;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
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.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;
|
||||
@@ -40,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 {
|
||||
@@ -50,6 +45,7 @@ public final class TransportImpl
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class ConnInfo {
|
||||
|
||||
final String host;
|
||||
@@ -65,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;
|
||||
|
||||
@@ -92,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;
|
||||
@@ -104,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;
|
||||
|
||||
@@ -120,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());
|
||||
@@ -135,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 {
|
||||
@@ -170,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()) {
|
||||
@@ -207,37 +219,9 @@ public final class TransportImpl
|
||||
*/
|
||||
private String readIdentification(Buffer.PlainBuffer buffer)
|
||||
throws IOException {
|
||||
String 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)
|
||||
throw new TransportException("Incorrect identification: bad line ending");
|
||||
if (pos >= data.length)
|
||||
throw new TransportException("Incorrect identification: line too long");
|
||||
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");
|
||||
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
|
||||
if (ident.isEmpty()) {
|
||||
return ident;
|
||||
}
|
||||
|
||||
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
|
||||
@@ -427,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -19,8 +19,10 @@ package net.schmizz.sshj.userauth.keyprovider;
|
||||
* @version $Id:$
|
||||
*/
|
||||
public enum KeyFormat {
|
||||
PKCS5,
|
||||
PKCS8,
|
||||
OpenSSH,
|
||||
OpenSSHv1,
|
||||
PuTTY,
|
||||
Unknown
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,8 +25,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/** Support for uploading files over a connected link using SCP. */
|
||||
public final class SCPDownloadClient extends AbstractSCPClient {
|
||||
/** Support for downloading files over a connected link using SCP. */
|
||||
public class SCPDownloadClient extends AbstractSCPClient {
|
||||
|
||||
private boolean recursiveMode = true;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/** Support for uploading files over a connected link using SCP. */
|
||||
public final class SCPUploadClient extends AbstractSCPClient {
|
||||
public class SCPUploadClient extends AbstractSCPClient {
|
||||
|
||||
private LocalFileFilter uploadFilter;
|
||||
|
||||
|
||||
244
src/test/groovy/com/hierynomus/sshj/sftp/SFTPClientSpec.groovy
Normal file
244
src/test/groovy/com/hierynomus/sshj/sftp/SFTPClientSpec.groovy
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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.sftp
|
||||
|
||||
import com.hierynomus.sshj.test.SshFixture
|
||||
import com.hierynomus.sshj.test.util.FileUtil
|
||||
import net.schmizz.sshj.SSHClient
|
||||
import net.schmizz.sshj.sftp.SFTPClient
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
import static org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable
|
||||
|
||||
class SFTPClientSpec extends Specification {
|
||||
|
||||
@Rule
|
||||
public SshFixture fixture = new SshFixture()
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temp = new TemporaryFolder()
|
||||
|
||||
@Unroll
|
||||
def "should copy #sourceType->#targetType if #targetExists with #named name"() {
|
||||
given:
|
||||
File src = source
|
||||
File dest = target
|
||||
|
||||
when:
|
||||
doUpload(src, dest)
|
||||
|
||||
then:
|
||||
exists.each { f ->
|
||||
if (dest.isDirectory()) {
|
||||
assert new File(dest, f).exists()
|
||||
} else {
|
||||
assert dest.exists()
|
||||
}
|
||||
}
|
||||
// Dest is also counted by recursiveCount if it is a dir
|
||||
exists.size() + (dest.isDirectory() ? 1 : 0) == recursiveCount(dest)
|
||||
|
||||
cleanup:
|
||||
// Delete the temp directories
|
||||
recursiveDelete(source.getParentFile())
|
||||
recursiveDelete(target.getParentFile())
|
||||
|
||||
where:
|
||||
source | target || exists
|
||||
sourceTree() | existingTargetDir("dest") || ["toto.txt", "tata.txt", "tutu", "tutu/tutu.txt"]
|
||||
sourceTree() | newTargetDir("dest") || ["toto.txt", "tata.txt", "tutu", "tutu/tutu.txt"]
|
||||
sourceTree() | existingTargetDir("toto") || ["toto.txt", "tata.txt", "tutu", "tutu/tutu.txt"]
|
||||
sourceTree() | newTargetDir("toto") || ["toto.txt", "tata.txt", "tutu", "tutu/tutu.txt"]
|
||||
sourceFile() | existingTargetDir("dest") || ["toto.txt"]
|
||||
sourceFile() | newTargetFile("toto.txt") || ["toto.txt"]
|
||||
sourceFile() | newTargetFile("diff.txt") || ["diff.txt"]
|
||||
|
||||
sourceType = source.isDirectory() ? "dir" : "file"
|
||||
targetType = target.isDirectory() ? "dir" : sourceType
|
||||
targetExists = target.exists() ? "exists" : "not exists"
|
||||
named = (target.name == source.name) ? "same" : "different"
|
||||
}
|
||||
|
||||
|
||||
def "should not throw exception on close before disconnect"() {
|
||||
given:
|
||||
File file = temp.newFile("source.txt")
|
||||
FileUtil.writeToFile(file, "This is the source")
|
||||
|
||||
when:
|
||||
doUpload(file, temp.newFile("dest.txt"))
|
||||
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
//
|
||||
// def "should copy dir->dir if exists and same name"() {
|
||||
// given:
|
||||
// File srcDir = temp.newFolder("toto")
|
||||
// File destDir = temp.newFolder("dest", "toto")
|
||||
// FileUtil.writeToFile(new File(srcDir, "toto.txt"), "Toto file")
|
||||
//
|
||||
// when:
|
||||
// doUpload(srcDir, destDir)
|
||||
//
|
||||
// then:
|
||||
// destDir.exists()
|
||||
// !new File(destDir, "toto").exists()
|
||||
// new File(destDir, "toto.txt").exists()
|
||||
// }
|
||||
//
|
||||
// def "should copy dir->dir if exists and different name"() {
|
||||
// given:
|
||||
// File srcDir = temp.newFolder("toto")
|
||||
// File destDir = temp.newFolder("dest")
|
||||
// FileUtil.writeToFile(new File(srcDir, "toto.txt"), "Toto file")
|
||||
//
|
||||
// when:
|
||||
// doUpload(srcDir, destDir)
|
||||
//
|
||||
// then:
|
||||
// destDir.exists()
|
||||
// new File(destDir, "toto.txt").exists()
|
||||
// }
|
||||
//
|
||||
// def "should copy dir->dir if not exists and same name"() {
|
||||
// given:
|
||||
// File dd = temp.newFolder("dest")
|
||||
// File destDir = new File(dd, "toto")
|
||||
//
|
||||
// when:
|
||||
// doUpload(srcDir, destDir)
|
||||
//
|
||||
// then:
|
||||
// destDir.exists()
|
||||
// new File(destDir, "toto.txt").exists()
|
||||
// }
|
||||
//
|
||||
// def "should copy dir->dir if not exists and different name"() {
|
||||
// given:
|
||||
// File srcDir = temp.newFolder("toto")
|
||||
// File destDir = new File(temp.getRoot(), "dest")
|
||||
// FileUtil.writeToFile(new File(srcDir, "toto.txt"), "Toto file")
|
||||
//
|
||||
// when:
|
||||
// doUpload(srcDir, destDir)
|
||||
//
|
||||
// then:
|
||||
// destDir.exists()
|
||||
// new File(destDir, "toto.txt").exists()
|
||||
// }
|
||||
|
||||
def "should not merge same name subdirs (GH #252)"() {
|
||||
given:
|
||||
File toto = temp.newFolder("toto")
|
||||
File tutu = mkdir(toto, "tutu")
|
||||
File toto2 = mkdir(toto, "toto")
|
||||
File dest = temp.newFolder("dest")
|
||||
FileUtil.writeToFile(new File(toto, "toto.txt"), "Toto file")
|
||||
FileUtil.writeToFile(new File(tutu, "tototutu.txt"), "Toto/Tutu file")
|
||||
FileUtil.writeToFile(new File(toto2, "totototo.txt"), "Toto/Toto file")
|
||||
|
||||
when:
|
||||
doUpload(toto, dest)
|
||||
|
||||
then:
|
||||
new File(dest, "toto").exists()
|
||||
new File(dest, "toto.txt").exists()
|
||||
new File(dest, "tutu").exists()
|
||||
new File(dest, "tutu/tototutu.txt").exists()
|
||||
new File(dest, "toto").exists()
|
||||
new File(dest, "toto/totototo.txt").exists()
|
||||
!new File(dest, "totototo.txt").exists()
|
||||
}
|
||||
|
||||
private void doUpload(File src, File dest) throws IOException {
|
||||
SSHClient sshClient = fixture.setupConnectedDefaultClient()
|
||||
sshClient.authPassword("test", "test")
|
||||
try {
|
||||
withCloseable(sshClient.newSFTPClient()) { SFTPClient sftpClient ->
|
||||
sftpClient.put(src.getPath(), dest.getPath())
|
||||
}
|
||||
} finally {
|
||||
sshClient.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
private File mkdir(File parent, String name) {
|
||||
File file = new File(parent, name)
|
||||
file.mkdirs()
|
||||
return file
|
||||
}
|
||||
|
||||
private def sourceTree() {
|
||||
def tempDir = File.createTempDir()
|
||||
File srcDir = mkdir(tempDir, "toto")
|
||||
FileUtil.writeToFile(new File(srcDir, "toto.txt"), "Toto file")
|
||||
FileUtil.writeToFile(new File(srcDir, "tata.txt"), "Tata file")
|
||||
File tutuDir = mkdir(srcDir, "tutu")
|
||||
FileUtil.writeToFile(new File(tutuDir, "tutu.txt"), "Tutu file")
|
||||
return srcDir
|
||||
}
|
||||
|
||||
private def sourceFile() {
|
||||
def tempDir = File.createTempDir()
|
||||
def totoFile = new File(tempDir, "toto.txt")
|
||||
FileUtil.writeToFile(totoFile, "Bare toto file")
|
||||
return totoFile
|
||||
}
|
||||
|
||||
private def existingTargetDir(String name) {
|
||||
def tempDir = File.createTempDir("sftp", "tmp")
|
||||
tempDir.deleteOnExit()
|
||||
return mkdir(tempDir, name)
|
||||
}
|
||||
|
||||
private def newTargetFile(String name) {
|
||||
def tempDir = File.createTempDir("sftp", "tmp")
|
||||
tempDir.deleteOnExit()
|
||||
return new File(tempDir, name)
|
||||
}
|
||||
|
||||
private def newTargetDir(String name) {
|
||||
def tempDir = File.createTempDir("sftp", "tmp")
|
||||
tempDir.deleteOnExit()
|
||||
return new File(tempDir, name)
|
||||
}
|
||||
|
||||
private int recursiveCount(File file) {
|
||||
if (file.isFile()) {
|
||||
return 1
|
||||
}
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
return 1 + (files.collect({ f -> recursiveCount(f) }).sum() as int)
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
private void recursiveDelete(File file) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File each : files) {
|
||||
recursiveDelete(each);
|
||||
}
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.transport
|
||||
|
||||
import net.schmizz.sshj.common.Buffer
|
||||
import net.schmizz.sshj.transport.TransportException
|
||||
import spock.lang.Specification
|
||||
|
||||
class IdentificationStringParserSpec extends Specification {
|
||||
|
||||
def "should parse simple identification string"() {
|
||||
given:
|
||||
def buffer = new Buffer.PlainBuffer()
|
||||
buffer.putRawBytes("SSH-2.0-OpenSSH-6.13\r\n".bytes)
|
||||
|
||||
when:
|
||||
def ident = new IdentificationStringParser(buffer).parseIdentificationString()
|
||||
|
||||
then:
|
||||
ident == "SSH-2.0-OpenSSH-6.13"
|
||||
}
|
||||
|
||||
def "should leniently parse identification string without carriage return"() {
|
||||
given:
|
||||
def buffer = new Buffer.PlainBuffer()
|
||||
buffer.putRawBytes("SSH-2.0-OpenSSH-6.13\n".bytes)
|
||||
|
||||
when:
|
||||
def ident = new IdentificationStringParser(buffer).parseIdentificationString()
|
||||
|
||||
then:
|
||||
ident == "SSH-2.0-OpenSSH-6.13"
|
||||
}
|
||||
|
||||
def "should not parse header lines as part of ident"() {
|
||||
given:
|
||||
def buffer = new Buffer.PlainBuffer()
|
||||
buffer.putRawBytes("header1\nheader2\r\nSSH-2.0-OpenSSH-6.13\r\n".bytes)
|
||||
|
||||
when:
|
||||
def ident = new IdentificationStringParser(buffer).parseIdentificationString()
|
||||
|
||||
then:
|
||||
ident == "SSH-2.0-OpenSSH-6.13"
|
||||
}
|
||||
|
||||
def "should fail on too long ident string"() {
|
||||
given:
|
||||
def buffer = new Buffer.PlainBuffer()
|
||||
buffer.putRawBytes("SSH-2.0-OpenSSH-6.13 ".bytes)
|
||||
byte[] bs = new byte[255 - buffer.wpos()]
|
||||
Arrays.fill(bs, 'a'.bytes[0])
|
||||
buffer.putRawBytes(bs).putRawBytes("\r\n".bytes)
|
||||
|
||||
when:
|
||||
new IdentificationStringParser(buffer).parseIdentificationString()
|
||||
|
||||
then:
|
||||
thrown(TransportException.class)
|
||||
}
|
||||
|
||||
def "should not fail on too long header line"() {
|
||||
given:
|
||||
def buffer = new Buffer.PlainBuffer()
|
||||
buffer.putRawBytes("header1 ".bytes)
|
||||
byte[] bs = new byte[255 - buffer.wpos()]
|
||||
new Random().nextBytes(bs)
|
||||
buffer.putRawBytes(bs).putRawBytes("\r\n".bytes)
|
||||
buffer.putRawBytes("SSH-2.0-OpenSSH-6.13\r\n".bytes)
|
||||
|
||||
when:
|
||||
def ident = new IdentificationStringParser(buffer).parseIdentificationString()
|
||||
|
||||
then:
|
||||
ident == "SSH-2.0-OpenSSH-6.13"
|
||||
}
|
||||
|
||||
def "should not fail on very short header line"() {
|
||||
given:
|
||||
def buffer = new Buffer.PlainBuffer()
|
||||
buffer.putRawBytes("h1\n".bytes)
|
||||
buffer.putRawBytes("SSH-2.0-OpenSSH-6.13\r\n".bytes)
|
||||
|
||||
when:
|
||||
def ident = new IdentificationStringParser(buffer).parseIdentificationString()
|
||||
|
||||
then:
|
||||
ident == "SSH-2.0-OpenSSH-6.13"
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.sftp;
|
||||
|
||||
import com.hierynomus.sshj.test.SshFixture;
|
||||
import com.hierynomus.sshj.test.util.FileUtil;
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.sftp.SFTPClient;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SFTPClientTest {
|
||||
|
||||
@Rule
|
||||
public SshFixture fixture = new SshFixture();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temp = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void shouldNotThrowExceptionOnCloseBeforeDisconnect() throws IOException {
|
||||
SSHClient sshClient = fixture.setupConnectedDefaultClient();
|
||||
sshClient.authPassword("test", "test");
|
||||
SFTPClient sftpClient = sshClient.newSFTPClient();
|
||||
File file = temp.newFile("source.txt");
|
||||
FileUtil.writeToFile(file, "This is the source");
|
||||
try {
|
||||
try {
|
||||
sftpClient.put(file.getPath(), temp.newFile("dest.txt").getPath());
|
||||
} finally {
|
||||
sftpClient.close();
|
||||
}
|
||||
} finally {
|
||||
sshClient.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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--) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user