mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 07:10:53 +03:00
Compare commits
81 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 |
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
|
||||
|
||||
19
README.adoc
19
README.adoc
@@ -1,10 +1,11 @@
|
||||
= sshj - SSHv2 library for Java
|
||||
Jeroen van Erp
|
||||
:sshj_groupid: com.hierynomus
|
||||
:sshj_version: 0.16.0
|
||||
:sshj_version: 0.19.0
|
||||
:source-highlighter: pygments
|
||||
|
||||
image:https://travis-ci.org/hierynomus/sshj.svg?branch=master[link="https://travis-ci.org/hierynomus/sshj"]
|
||||
image:https://api.codacy.com/project/badge/Grade/14a0a316bb9149739b5ea26dbfa8da8a["Codacy code quality", link="https://www.codacy.com/app/jeroen_2/sshj?utm_source=github.com&utm_medium=referral&utm_content=hierynomus/sshj&utm_campaign=Badge_Grade"]
|
||||
image:https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj/badge.svg["Maven Central",link="https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj"]
|
||||
image:https://javadoc-emblem.rhcloud.com/doc/com.hierynomus/sshj/badge.svg["Javadoc",link="http://www.javadoc.io/doc/com.hierynomus/sshj"]
|
||||
|
||||
@@ -98,6 +99,22 @@ 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.
|
||||
|
||||
352
build.gradle
352
build.gradle
@@ -1,210 +1,218 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
id "groovy"
|
||||
id "maven"
|
||||
id "idea"
|
||||
id "signing"
|
||||
id "osgi"
|
||||
id "org.ajoberstar.release-opinion" version "1.4.2"
|
||||
id "com.github.hierynomus.license" version "0.12.1"
|
||||
id "java"
|
||||
id "groovy"
|
||||
id "osgi"
|
||||
id "maven-publish"
|
||||
id "org.ajoberstar.release-opinion" version "1.4.2"
|
||||
id "com.github.hierynomus.license" version "0.12.1"
|
||||
id "com.jfrog.bintray" version "1.7"
|
||||
id 'ru.vyarus.pom' version '1.0.3'
|
||||
id 'ru.vyarus.github-info' version '1.1.0'
|
||||
}
|
||||
|
||||
group = "com.hierynomus"
|
||||
defaultTasks "build"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
targetCompatibility = 1.7
|
||||
sourceCompatibility = 1.6
|
||||
targetCompatibility = 1.6
|
||||
|
||||
configurations.compile.transitive = false
|
||||
|
||||
idea {
|
||||
module {
|
||||
downloadJavadoc = true
|
||||
downloadSources = true
|
||||
}
|
||||
}
|
||||
|
||||
license {
|
||||
mapping {
|
||||
java = 'SLASHSTAR_STYLE'
|
||||
}
|
||||
header rootProject.file('LICENSE_HEADER')
|
||||
strictCheck true
|
||||
}
|
||||
|
||||
release {
|
||||
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
include "**/*Test.*"
|
||||
if (!project.hasProperty("allTests")) {
|
||||
useJUnit {
|
||||
excludeCategories 'com.hierynomus.sshj.test.SlowTests'
|
||||
excludeCategories 'com.hierynomus.sshj.test.KnownFailingTests'
|
||||
}
|
||||
}
|
||||
|
||||
afterSuite { descriptor, result ->
|
||||
if (descriptor.className != null) {
|
||||
def indicator = "\u001B[32m✓\u001b[0m"
|
||||
if (result.failedTestCount > 0) {
|
||||
indicator = "\u001B[31m✘\u001b[0m"
|
||||
}
|
||||
logger.lifecycle("$indicator Test ${descriptor.name}; Executed: ${result.testCount}/\u001B[32m${result.successfulTestCount}\u001B[0m/\u001B[31m${result.failedTestCount}\u001B[0m")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def bouncycastleVersion = "1.51"
|
||||
|
||||
dependencies {
|
||||
compile "org.slf4j:slf4j-api:1.7.7"
|
||||
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
||||
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
||||
compile "com.jcraft:jzlib:1.1.3"
|
||||
compile "org.slf4j:slf4j-api:1.7.7"
|
||||
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
||||
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
||||
compile "com.jcraft:jzlib:1.1.3"
|
||||
|
||||
compile "net.vrallev.ecc:ecc-25519-java:1.0.1"
|
||||
compile "net.i2p.crypto:eddsa:0.1.0"
|
||||
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
||||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
testCompile "org.apache.sshd:sshd-core:1.1.0"
|
||||
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
||||
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
||||
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
||||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
testCompile "org.apache.sshd:sshd-core:1.1.0"
|
||||
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
||||
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
||||
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
|
||||
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
instruction "Bundle-Description", "SSHv2 library for Java"
|
||||
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
instruction "Import-Package", "!net.schmizz.*"
|
||||
instruction "Import-Package", "javax.crypto*"
|
||||
instruction "Import-Package", "net.i2p*"
|
||||
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
|
||||
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
|
||||
instruction "Import-Package", "org.bouncycastle*"
|
||||
instruction "Import-Package", "*"
|
||||
instruction "Export-Package", "net.schmizz.*"
|
||||
}
|
||||
license {
|
||||
header rootProject.file('LICENSE_HEADER')
|
||||
strictCheck true
|
||||
mapping {
|
||||
java = 'SLASHSTAR_STYLE'
|
||||
}
|
||||
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java'])
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
classifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
manifest {
|
||||
attributes(
|
||||
// Add the needed OSGI attributes
|
||||
"Bundle-ManifestVersion": "2",
|
||||
"Bundle-Name": "${project.jar.manifest.name} Source",
|
||||
"Bundle-Version": project.jar.manifest.version,
|
||||
"Eclipse-SourceBundle": "${project.jar.manifest.symbolicName};version=\"${project.jar.manifest.version}\";roots:=\".\"",
|
||||
"Bundle-SymbolicName": "${project.jar.manifest.symbolicName}.source"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar, sourcesJar
|
||||
}
|
||||
|
||||
signing {
|
||||
required { !version.toString().contains("SNAPSHOT") && gradle.taskGraph.hasTask("uploadArchives") }
|
||||
sign configurations.archives
|
||||
release {
|
||||
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
|
||||
}
|
||||
|
||||
// This disables the pedantic doclint feature of JDK8
|
||||
if (JavaVersion.current().isJava8Compatible()) {
|
||||
tasks.withType(Javadoc) {
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
}
|
||||
tasks.withType(Javadoc) {
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
}
|
||||
}
|
||||
|
||||
uploadArchives {
|
||||
if (project.hasProperty('sonatypeUsername')) {
|
||||
repositories.mavenDeployer {
|
||||
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
||||
task writeSshjVersionProperties << {
|
||||
project.file("${project.buildDir}/resources/main").mkdirs()
|
||||
project.file("${project.buildDir}/resources/main/sshj.properties").withWriter { w ->
|
||||
w.append("sshj.version=${version}")
|
||||
}
|
||||
}
|
||||
|
||||
configuration = configurations.archives
|
||||
jar.dependsOn writeSshjVersionProperties
|
||||
|
||||
repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') {
|
||||
authentication(userName: sonatypeUsername, password: sonatypePassword)
|
||||
}
|
||||
snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') {
|
||||
authentication(userName: sonatypeUsername, password: sonatypePassword)
|
||||
}
|
||||
jar {
|
||||
manifest {
|
||||
instruction "Bundle-Description", "SSHv2 library for Java"
|
||||
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
instruction "Import-Package", "!net.schmizz.*"
|
||||
instruction "Import-Package", "javax.crypto*"
|
||||
instruction "Import-Package", "net.i2p*"
|
||||
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
|
||||
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
|
||||
instruction "Import-Package", "org.bouncycastle*"
|
||||
instruction "Import-Package", "*"
|
||||
instruction "Export-Package", "net.schmizz.*"
|
||||
}
|
||||
}
|
||||
|
||||
pom.project {
|
||||
name "sshj"
|
||||
description "SSHv2 library for Java"
|
||||
url "https://github.com/hierynomus/sshj"
|
||||
inceptionYear "2009"
|
||||
task javadocJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system "github"
|
||||
url "https://github.com/hierynomus/sshj/issues"
|
||||
}
|
||||
task sourcesJar(type: Jar) {
|
||||
classifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
manifest {
|
||||
attributes(
|
||||
// Add the needed OSGI attributes
|
||||
"Bundle-ManifestVersion": "2",
|
||||
"Bundle-Name": "${project.jar.manifest.name} Source",
|
||||
"Bundle-Version": project.jar.manifest.version,
|
||||
"Eclipse-SourceBundle": "${project.jar.manifest.symbolicName};version=\"${project.jar.manifest.version}\";roots:=\".\"",
|
||||
"Bundle-SymbolicName": "${project.jar.manifest.symbolicName}.source"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection "scm:git:git://github.com/hierynomus/sshj.git"
|
||||
developerConnection "scm:git:git@github.com:hierynomus/sshj.git"
|
||||
url "https://github.com/hierynomus/sshj.git"
|
||||
}
|
||||
tasks.withType(Test) {
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
include "**/*Test.*"
|
||||
include "**/*Spec.*"
|
||||
if (!project.hasProperty("allTests")) {
|
||||
useJUnit {
|
||||
excludeCategories 'com.hierynomus.sshj.test.SlowTests'
|
||||
excludeCategories 'com.hierynomus.sshj.test.KnownFailingTests'
|
||||
}
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name "Apache 2"
|
||||
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
distribution "repo"
|
||||
}
|
||||
}
|
||||
afterSuite { descriptor, result ->
|
||||
if (descriptor.className != null) {
|
||||
def indicator = "\u001B[32m✓\u001b[0m"
|
||||
if (result.failedTestCount > 0) {
|
||||
indicator = "\u001B[31m✘\u001b[0m"
|
||||
}
|
||||
logger.lifecycle("$indicator Test ${descriptor.name}; Executed: ${result.testCount}/\u001B[32m${result.successfulTestCount}\u001B[0m/\u001B[31m${result.failedTestCount}\u001B[0m")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id "hierynomus"
|
||||
name "Jeroen van Erp"
|
||||
email "jeroen@javadude.nl"
|
||||
roles {
|
||||
role "Lead developer"
|
||||
}
|
||||
}
|
||||
developer {
|
||||
id "shikhar"
|
||||
name "Shikhar Bhushan"
|
||||
email "shikhar@schmizz.net"
|
||||
url "http://schmizz.net"
|
||||
roles {
|
||||
role "Previous lead developer"
|
||||
}
|
||||
}
|
||||
developer {
|
||||
id "iterate"
|
||||
name "David Kocher"
|
||||
email "dkocher@iterate.ch"
|
||||
organization "iterage GmbH"
|
||||
organizationUrl "https://iterate.ch"
|
||||
roles {
|
||||
role "Developer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
project.tasks.compileGroovy.onlyIf { false }
|
||||
|
||||
github {
|
||||
user 'hierynomus'
|
||||
license 'Apache'
|
||||
}
|
||||
|
||||
pom {
|
||||
description "SSHv2 library for Java"
|
||||
url "https://github.com/hierynomus/sshj"
|
||||
inceptionYear "2009"
|
||||
developers {
|
||||
developer {
|
||||
id "hierynomus"
|
||||
name "Jeroen van Erp"
|
||||
email "jeroen@javadude.nl"
|
||||
roles {
|
||||
role "Lead developer"
|
||||
}
|
||||
}
|
||||
developer {
|
||||
id "shikhar"
|
||||
name "Shikhar Bhushan"
|
||||
email "shikhar@schmizz.net"
|
||||
url "http://schmizz.net"
|
||||
roles {
|
||||
role "Previous lead developer"
|
||||
}
|
||||
}
|
||||
developer {
|
||||
id "iterate"
|
||||
name "David Kocher"
|
||||
email "dkocher@iterate.ch"
|
||||
organization "iterage GmbH"
|
||||
organizationUrl "https://iterate.ch"
|
||||
roles {
|
||||
role "Developer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing.publications {
|
||||
Sshj(MavenPublication) {
|
||||
from components.java
|
||||
artifact sourcesJar
|
||||
artifact javadocJar
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey")) {
|
||||
bintray {
|
||||
user = project.property("bintrayUsername")
|
||||
key = project.property("bintrayApiKey")
|
||||
publish = true
|
||||
publications = ["Sshj"]
|
||||
pkg {
|
||||
repo = "maven"
|
||||
name = project.name
|
||||
licenses = ["Apache-2.0"]
|
||||
vcsUrl = "https://github.com/hierynomus/sshj.git"
|
||||
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
|
||||
githubRepo = "hierynomus/sshj"
|
||||
version {
|
||||
name = project.version.toString()
|
||||
vcsTag = "v${project.version}"
|
||||
released = new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ').format(new Date())
|
||||
gpg {
|
||||
sign = true
|
||||
passphrase = project.property("signing.password")
|
||||
}
|
||||
mavenCentralSync {
|
||||
sync = true
|
||||
user = project.property("sonatypeUsername")
|
||||
password = project.property("sonatypePassword")
|
||||
close = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.compileGroovy.onlyIf { false }
|
||||
tasks.release.dependsOn 'build', 'uploadArchives'
|
||||
project.tasks.release.dependsOn([project.tasks.build, project.tasks.bintrayUpload])
|
||||
|
||||
@@ -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_16_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,19 +17,12 @@ package net.schmizz.sshj.sftp;
|
||||
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.sftp.Response.StatusCode;
|
||||
import net.schmizz.sshj.xfer.AbstractFileTransfer;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
import net.schmizz.sshj.xfer.FileTransfer;
|
||||
import net.schmizz.sshj.xfer.LocalDestFile;
|
||||
import net.schmizz.sshj.xfer.LocalFileFilter;
|
||||
import net.schmizz.sshj.xfer.LocalSourceFile;
|
||||
import net.schmizz.sshj.xfer.TransferListener;
|
||||
import net.schmizz.sshj.xfer.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class SFTPFileTransfer
|
||||
extends AbstractFileTransfer
|
||||
@@ -42,6 +35,7 @@ public class SFTPFileTransfer
|
||||
private volatile boolean preserveAttributes = true;
|
||||
|
||||
public SFTPFileTransfer(SFTPEngine engine) {
|
||||
super(engine.getLoggerFactory());
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
@@ -145,7 +139,7 @@ public class SFTPFileTransfer
|
||||
final RemoteFile.ReadAheadRemoteFileInputStream rfis = rf.new ReadAheadRemoteFileInputStream(16);
|
||||
final OutputStream os = adjusted.getOutputStream();
|
||||
try {
|
||||
new StreamCopier(rfis, os)
|
||||
new StreamCopier(rfis, os, engine.getLoggerFactory())
|
||||
.bufSize(engine.getSubsystem().getLocalMaxPacketSize())
|
||||
.keepFlushing(false)
|
||||
.listener(listener)
|
||||
@@ -235,14 +229,36 @@ public class SFTPFileTransfer
|
||||
final String remote)
|
||||
throws IOException {
|
||||
final String adjusted = prepareFile(local, remote);
|
||||
try (RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC))) {
|
||||
try (InputStream fis = local.getInputStream();
|
||||
RemoteFile.RemoteFileOutputStream rfos = rf.new RemoteFileOutputStream(0, 16)) {
|
||||
new StreamCopier(fis, rfos)
|
||||
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
|
||||
.keepFlushing(false)
|
||||
.listener(listener)
|
||||
.copy();
|
||||
RemoteFile rf = null;
|
||||
InputStream fis = null;
|
||||
RemoteFile.RemoteFileOutputStream rfos = null;
|
||||
try {
|
||||
rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC));
|
||||
fis = local.getInputStream();
|
||||
rfos = rf.new RemoteFileOutputStream(0, 16);
|
||||
new StreamCopier(fis, rfos, engine.getLoggerFactory())
|
||||
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
|
||||
.keepFlushing(false)
|
||||
.listener(listener)
|
||||
.copy();
|
||||
} finally {
|
||||
if (rf != null) {
|
||||
try {
|
||||
rf.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
if (rfos != null) {
|
||||
try {
|
||||
rfos.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return adjusted;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,7 +26,7 @@ import spock.lang.Unroll
|
||||
|
||||
import static org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable
|
||||
|
||||
class SFTPClientTest extends Specification {
|
||||
class SFTPClientSpec extends Specification {
|
||||
|
||||
@Rule
|
||||
public SshFixture fixture = new SshFixture()
|
||||
@@ -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;
|
||||
|
||||
@@ -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--) {
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer.PlainBuffer;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class BufferTest {
|
||||
|
||||
// Issue 72: previously, it entered an infinite loop trying to establish the buffer size
|
||||
|
||||
@@ -15,15 +15,14 @@
|
||||
*/
|
||||
package net.schmizz.sshj.keyprovider;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class KeyProviderUtilTest {
|
||||
|
||||
@@ -35,6 +34,12 @@ public class KeyProviderUtilTest {
|
||||
assertEquals(KeyFormat.OpenSSH, format);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPkcs5() throws IOException {
|
||||
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs5"));
|
||||
assertEquals(KeyFormat.PKCS5, format);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPkcs8() throws IOException {
|
||||
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs8"));
|
||||
|
||||
@@ -18,6 +18,7 @@ package net.schmizz.sshj.keyprovider;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
||||
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
import net.schmizz.sshj.userauth.password.Resource;
|
||||
@@ -30,11 +31,13 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Scanner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -142,7 +145,7 @@ public class OpenSSHKeyFileTest {
|
||||
|
||||
@Test
|
||||
public void shouldHaveCorrectFingerprintForED25519() throws IOException {
|
||||
OpenSSHKeyFile keyFile = new OpenSSHKeyFile();
|
||||
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
|
||||
keyFile.init(new File("src/test/resources/keytypes/test_ed25519"));
|
||||
String expected = "256 MD5:d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32 root@sshj (ED25519)\n";
|
||||
PublicKey aPublic = keyFile.getPublic();
|
||||
@@ -150,6 +153,14 @@ public class OpenSSHKeyFileTest {
|
||||
assertThat(expected, containsString(sshjFingerprintSshjKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLoadED25519PrivateKey() throws IOException {
|
||||
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
|
||||
keyFile.init(new File("src/test/resources/keytypes/test_ed25519"));
|
||||
PrivateKey aPrivate = keyFile.getPrivate();
|
||||
assertThat(aPrivate.getAlgorithm(), equalTo("EdDSA"));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
@@ -171,4 +182,4 @@ public class OpenSSHKeyFileTest {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user