mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-08 16:18:05 +03:00
Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d4f8fc46a | ||
|
|
2b21ec6032 | ||
|
|
8e15a8bd7d | ||
|
|
531eb97767 | ||
|
|
e36fd0fb3d | ||
|
|
382321deca | ||
|
|
7b75fb3d53 | ||
|
|
4d84d3f67c | ||
|
|
8eb7d1a2ad | ||
|
|
a03fa9ac63 | ||
|
|
bcb15e6ccd | ||
|
|
d85b22fe8d | ||
|
|
f4b71941a3 | ||
|
|
636f896850 | ||
|
|
56c0baf814 | ||
|
|
edfb069f2a | ||
|
|
65b3003e72 | ||
|
|
fbee0b3956 | ||
|
|
fd60139b98 | ||
|
|
0b397bc3d7 | ||
|
|
40f956b4b6 | ||
|
|
ef3f7a2eaf | ||
|
|
8134113510 | ||
|
|
c883c87963 | ||
|
|
920537dac9 | ||
|
|
356ec9ed08 | ||
|
|
aa47b0c5f7 | ||
|
|
d3ed3cfe0f | ||
|
|
786734ce26 | ||
|
|
9cb5bf4e10 | ||
|
|
0e3f7c2bbf | ||
|
|
66d4b34eba | ||
|
|
aafb9942a3 | ||
|
|
d1dff550ce | ||
|
|
ac2720becd | ||
|
|
48dd1fdc41 | ||
|
|
9826a71d2b | ||
|
|
936eb26e9e | ||
|
|
9438157b93 | ||
|
|
7d326e5ae4 | ||
|
|
f038b5ce2b | ||
|
|
20879a4aa5 | ||
|
|
516abb0282 | ||
|
|
0ad51709c2 | ||
|
|
c9c68f019e | ||
|
|
fc75f9796c | ||
|
|
61af500c3e | ||
|
|
56553ea086 | ||
|
|
86e6631b1e | ||
|
|
b6f437a932 | ||
|
|
9e3b9f7c24 | ||
|
|
766ab916ee | ||
|
|
cdca43a848 | ||
|
|
3ce7c2ebfb | ||
|
|
ca4e0bf2d7 | ||
|
|
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 |
8
.bettercodehub.yml
Normal file
8
.bettercodehub.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
exclude:
|
||||||
|
- /build-publishing.gradle
|
||||||
|
- /build.gradle
|
||||||
|
- /settings.gradle
|
||||||
|
component_depth: 1
|
||||||
|
languages:
|
||||||
|
- groovy
|
||||||
|
- java
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -11,7 +11,11 @@
|
|||||||
|
|
||||||
# Output dirs
|
# Output dirs
|
||||||
target/
|
target/
|
||||||
|
classes/
|
||||||
build/
|
build/
|
||||||
|
docs/
|
||||||
.gradle/
|
.gradle/
|
||||||
|
sshj.jar
|
||||||
|
|
||||||
|
# MacOS X
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
language: java
|
language: java
|
||||||
sudo: false
|
sudo: false
|
||||||
|
jdk:
|
||||||
|
- oraclejdk7
|
||||||
|
- oraclejdk8
|
||||||
|
|||||||
48
README.adoc
48
README.adoc
@@ -1,10 +1,11 @@
|
|||||||
= sshj - SSHv2 library for Java
|
= sshj - SSHv2 library for Java
|
||||||
Jeroen van Erp
|
Jeroen van Erp
|
||||||
:sshj_groupid: com.hierynomus
|
:sshj_groupid: com.hierynomus
|
||||||
:sshj_version: 0.16.0
|
:sshj_version: 0.21.1
|
||||||
:source-highlighter: pygments
|
:source-highlighter: pygments
|
||||||
|
|
||||||
image:https://travis-ci.org/hierynomus/sshj.svg?branch=master[link="https://travis-ci.org/hierynomus/sshj"]
|
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://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"]
|
image:https://javadoc-emblem.rhcloud.com/doc/com.hierynomus/sshj/badge.svg["Javadoc",link="http://www.javadoc.io/doc/com.hierynomus/sshj"]
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ If you're building your project using Maven, you can add the following dependenc
|
|||||||
If your project is built using another build tool that uses the Maven Central repository, translate this dependency into the format used by your build tool.
|
If your project is built using another build tool that uses the Maven Central repository, translate this dependency into the format used by your build tool.
|
||||||
|
|
||||||
== Building SSHJ
|
== Building SSHJ
|
||||||
. Clone the Overthere repository.
|
. Clone the SSHJ repository.
|
||||||
. Ensure you have Java6 installed with the http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html[Unlimited strength Java Cryptography Extensions (JCE)].
|
. Ensure you have Java6 installed with the http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html[Unlimited strength Java Cryptography Extensions (JCE)].
|
||||||
. Run the command `./gradlew clean build`.
|
. Run the command `./gradlew clean build`.
|
||||||
|
|
||||||
@@ -65,8 +66,13 @@ ciphers::
|
|||||||
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
|
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
|
||||||
|
|
||||||
key exchange::
|
key exchange::
|
||||||
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`, `diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
|
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`,
|
||||||
|
`diffie-hellman-group14-sha256`, `diffie-hellman-group15-sha512`, `diffie-hellman-group16-sha512`, `diffie-hellman-group17-sha512`, `diffie-hellman-group18-sha512`
|
||||||
|
`diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
|
||||||
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
|
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
|
||||||
|
SSHJ also supports the following extended (non official) key exchange algoriths:
|
||||||
|
`diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`,
|
||||||
|
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
|
||||||
|
|
||||||
signatures::
|
signatures::
|
||||||
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519`
|
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519`
|
||||||
@@ -78,7 +84,7 @@ compression::
|
|||||||
`zlib` and `zlib@openssh.com` (delayed zlib)
|
`zlib` and `zlib@openssh.com` (delayed zlib)
|
||||||
|
|
||||||
private key files::
|
private key files::
|
||||||
`pkcs8` encoded (what openssh uses)
|
`pkcs5`, `pkcs8`, `openssh-key-v1`, `ssh-rsa-cert-v01@openssh.com`, `ssh-dsa-cert-v01@openssh.com`
|
||||||
|
|
||||||
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
|
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
|
||||||
|
|
||||||
@@ -98,6 +104,40 @@ Google Group: http://groups.google.com/group/sshj-users
|
|||||||
Fork away!
|
Fork away!
|
||||||
|
|
||||||
== Release history
|
== Release history
|
||||||
|
SSHJ 0.21.1 (2017-04-25)::
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/322[#322]: Fix regression from 40f956b (invalid length parameter on outputstream)
|
||||||
|
SSHJ 0.21.0 (2017-04-14)::
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/319[#319]: Added support for `ssh-rsa-cert-v01@openssh.com` and `ssh-dsa-cert-v01@openssh.com` certificate key files
|
||||||
|
* Upgraded Gradle to 3.4.1
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/305[#305]: Added support for custom string encoding
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/312[#312]: Upgraded BouncyCastle to 1.56
|
||||||
|
SSHJ 0.20.0 (2017-02-09)::
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/294[#294]: Reference ED25519 by constant instead of name
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/293[#293], https://github.com/hierynomus/sshj/pulls/295[#295] and https://github.com/hierynomus/sshj/pulls/301[#301]: Fixed OSGi packaging
|
||||||
|
* Added new Diffie Hellman groups 15-18 for stronger KeyExchange algorithms
|
||||||
|
SSHJ 0.19.1 (2016-12-30)::
|
||||||
|
* Enabled PKCS5 Key files in DefaultConfig
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/291[#291]: Fixed sshj.properties loading and chained exception messages
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/284[#284]: Correctly catch interrupt in keepalive thread
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/292[#292]: Pass the configured RandomFactory to Diffie Hellmann KEX
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/256[#256]: SSHJ now builds if no git repository present
|
||||||
|
* LocalPortForwarder now correctly interrupts its own thread on close()
|
||||||
|
SSHJ 0.19.0 (2016-11-25)::
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/276[#276]: Add support for ed-25519 and new OpenSSH key format
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/280[#280]: Read version from a generated sshj.properties file to correctly output version during negotiation
|
||||||
|
SSHJ 0.18.0 (2016-09-30)::
|
||||||
|
* Fixed Android compatibility
|
||||||
|
* Upgrade to Gradle 3.0
|
||||||
|
* 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)::
|
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.
|
* *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.
|
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.
|
||||||
|
|||||||
371
build.gradle
371
build.gradle
@@ -1,210 +1,229 @@
|
|||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
id "groovy"
|
id "groovy"
|
||||||
id "maven"
|
id "osgi"
|
||||||
id "idea"
|
id "maven-publish"
|
||||||
id "signing"
|
id "org.ajoberstar.release-opinion" version "1.4.2"
|
||||||
id "osgi"
|
id "com.github.hierynomus.license" version "0.12.1"
|
||||||
id "org.ajoberstar.release-opinion" version "1.4.2"
|
id "com.jfrog.bintray" version "1.7"
|
||||||
id "com.github.hierynomus.license" version "0.12.1"
|
id 'ru.vyarus.pom' version '1.0.3'
|
||||||
|
id 'ru.vyarus.github-info' version '1.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.hierynomus"
|
group = "com.hierynomus"
|
||||||
|
defaultTasks "build"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = 1.7
|
sourceCompatibility = 1.6
|
||||||
targetCompatibility = 1.7
|
targetCompatibility = 1.6
|
||||||
|
|
||||||
configurations.compile.transitive = false
|
configurations.compile.transitive = false
|
||||||
|
|
||||||
idea {
|
def bouncycastleVersion = "1.56"
|
||||||
module {
|
|
||||||
downloadJavadoc = true
|
dependencies {
|
||||||
downloadSources = true
|
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.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.2.0"
|
||||||
|
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
||||||
|
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
||||||
|
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
license {
|
license {
|
||||||
mapping {
|
header rootProject.file('LICENSE_HEADER')
|
||||||
java = 'SLASHSTAR_STYLE'
|
strictCheck true
|
||||||
}
|
mapping {
|
||||||
header rootProject.file('LICENSE_HEADER')
|
java = 'SLASHSTAR_STYLE'
|
||||||
strictCheck true
|
}
|
||||||
|
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java'])
|
||||||
}
|
}
|
||||||
|
|
||||||
release {
|
if (project.file('.git').isDirectory()) {
|
||||||
|
release {
|
||||||
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
|
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
test {
|
version = "0.0.0-no.git"
|
||||||
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 "net.vrallev.ecc:ecc-25519-java:1.0.1"
|
|
||||||
|
|
||||||
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.*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This disables the pedantic doclint feature of JDK8
|
// This disables the pedantic doclint feature of JDK8
|
||||||
if (JavaVersion.current().isJava8Compatible()) {
|
if (JavaVersion.current().isJava8Compatible()) {
|
||||||
tasks.withType(Javadoc) {
|
tasks.withType(Javadoc) {
|
||||||
options.addStringOption('Xdoclint:none', '-quiet')
|
options.addStringOption('Xdoclint:none', '-quiet')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadArchives {
|
task writeSshjVersionProperties {
|
||||||
if (project.hasProperty('sonatypeUsername')) {
|
doLast {
|
||||||
repositories.mavenDeployer {
|
project.file("${project.buildDir}/resources/main").mkdirs()
|
||||||
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
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') {
|
jar {
|
||||||
authentication(userName: sonatypeUsername, password: sonatypePassword)
|
manifest {
|
||||||
}
|
// please see http://bnd.bndtools.org/chapters/390-wrapping.html
|
||||||
snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') {
|
instruction "Bundle-Description", "SSHv2 library for Java"
|
||||||
authentication(userName: sonatypeUsername, password: sonatypePassword)
|
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
}
|
instruction "Import-Package", "!net.schmizz.*"
|
||||||
|
instruction "Import-Package", "!com.hierynomus.sshj.*"
|
||||||
|
instruction "Import-Package", "javax.crypto*"
|
||||||
|
instruction "Import-Package", "!net.i2p.crypto.eddsa.math"
|
||||||
|
instruction "Import-Package", "net.i2p*"
|
||||||
|
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
|
||||||
|
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
|
||||||
|
instruction "Import-Package", "org.bouncycastle*;resolution:=optional"
|
||||||
|
instruction "Import-Package", "org.bouncycastle.jce.provider;resolution:=optional"
|
||||||
|
instruction "Import-Package", "*"
|
||||||
|
instruction "Export-Package", "com.hierynomus.sshj.*;version=\"${project.jar.manifest.version}\""
|
||||||
|
instruction "Export-Package", "net.schmizz.*;version=\"${project.jar.manifest.version}\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pom.project {
|
task javadocJar(type: Jar) {
|
||||||
name "sshj"
|
classifier = 'javadoc'
|
||||||
description "SSHv2 library for Java"
|
from javadoc
|
||||||
url "https://github.com/hierynomus/sshj"
|
}
|
||||||
inceptionYear "2009"
|
|
||||||
|
|
||||||
issueManagement {
|
task sourcesJar(type: Jar) {
|
||||||
system "github"
|
classifier = 'sources'
|
||||||
url "https://github.com/hierynomus/sshj/issues"
|
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 {
|
tasks.withType(Test) {
|
||||||
connection "scm:git:git://github.com/hierynomus/sshj.git"
|
testLogging {
|
||||||
developerConnection "scm:git:git@github.com:hierynomus/sshj.git"
|
exceptionFormat = 'full'
|
||||||
url "https://github.com/hierynomus/sshj.git"
|
}
|
||||||
}
|
include "**/*Test.*"
|
||||||
|
include "**/*Spec.*"
|
||||||
|
if (!project.hasProperty("allTests")) {
|
||||||
|
useJUnit {
|
||||||
|
excludeCategories 'com.hierynomus.sshj.test.SlowTests'
|
||||||
|
excludeCategories 'com.hierynomus.sshj.test.KnownFailingTests'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
licenses {
|
afterSuite { descriptor, result ->
|
||||||
license {
|
if (descriptor.className != null) {
|
||||||
name "Apache 2"
|
def indicator = "\u001B[32m✓\u001b[0m"
|
||||||
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
if (result.failedTestCount > 0) {
|
||||||
distribution "repo"
|
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 {
|
project.tasks.compileGroovy.onlyIf { false }
|
||||||
developer {
|
|
||||||
id "hierynomus"
|
github {
|
||||||
name "Jeroen van Erp"
|
user 'hierynomus'
|
||||||
email "jeroen@javadude.nl"
|
license 'Apache'
|
||||||
roles {
|
}
|
||||||
role "Lead developer"
|
|
||||||
}
|
pom {
|
||||||
}
|
description "SSHv2 library for Java"
|
||||||
developer {
|
url "https://github.com/hierynomus/sshj"
|
||||||
id "shikhar"
|
inceptionYear "2009"
|
||||||
name "Shikhar Bhushan"
|
developers {
|
||||||
email "shikhar@schmizz.net"
|
developer {
|
||||||
url "http://schmizz.net"
|
id "hierynomus"
|
||||||
roles {
|
name "Jeroen van Erp"
|
||||||
role "Previous lead developer"
|
email "jeroen@javadude.nl"
|
||||||
}
|
roles {
|
||||||
}
|
role "Lead developer"
|
||||||
developer {
|
}
|
||||||
id "iterate"
|
}
|
||||||
name "David Kocher"
|
developer {
|
||||||
email "dkocher@iterate.ch"
|
id "shikhar"
|
||||||
organization "iterage GmbH"
|
name "Shikhar Bhushan"
|
||||||
organizationUrl "https://iterate.ch"
|
email "shikhar@schmizz.net"
|
||||||
roles {
|
url "http://schmizz.net"
|
||||||
role "Developer"
|
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 }
|
project.tasks.release.dependsOn([project.tasks.build, project.tasks.bintrayUpload])
|
||||||
tasks.release.dependsOn 'build', 'uploadArchives'
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<groupId>com.hierynomus</groupId>
|
<groupId>com.hierynomus</groupId>
|
||||||
<artifactId>sshj-examples</artifactId>
|
<artifactId>sshj-examples</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>0.14.0</version>
|
<version>0.19.1</version>
|
||||||
|
|
||||||
<name>sshj-examples</name>
|
<name>sshj-examples</name>
|
||||||
<description>Examples for SSHv2 library for Java</description>
|
<description>Examples for SSHv2 library for Java</description>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.hierynomus</groupId>
|
<groupId>com.hierynomus</groupId>
|
||||||
<artifactId>sshj</artifactId>
|
<artifactId>sshj</artifactId>
|
||||||
<version>0.15.0</version>
|
<version>0.19.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,11 @@ package net.schmizz.sshj.examples;
|
|||||||
import net.schmizz.keepalive.KeepAliveProvider;
|
import net.schmizz.keepalive.KeepAliveProvider;
|
||||||
import net.schmizz.sshj.DefaultConfig;
|
import net.schmizz.sshj.DefaultConfig;
|
||||||
import net.schmizz.sshj.SSHClient;
|
import net.schmizz.sshj.SSHClient;
|
||||||
import net.schmizz.sshj.common.IOUtils;
|
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
|
||||||
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/** This examples demonstrates how to setup keep-alive to detect connection dropping. */
|
/** This examples demonstrates how to setup keep-alive to detect connection dropping. */
|
||||||
public class KeepAlive {
|
public class KeepAlive {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
|
|
||||||
/** A very rudimentary psuedo-terminal based on console I/O. */
|
/** A very rudimentary psuedo-terminal based on console I/O. */
|
||||||
class RudimentaryPTY {
|
class RudimentaryPTY {
|
||||||
@@ -33,18 +34,18 @@ class RudimentaryPTY {
|
|||||||
|
|
||||||
final Shell shell = session.startShell();
|
final Shell shell = session.startShell();
|
||||||
|
|
||||||
new StreamCopier(shell.getInputStream(), System.out)
|
new StreamCopier(shell.getInputStream(), System.out, LoggerFactory.DEFAULT)
|
||||||
.bufSize(shell.getLocalMaxPacketSize())
|
.bufSize(shell.getLocalMaxPacketSize())
|
||||||
.spawn("stdout");
|
.spawn("stdout");
|
||||||
|
|
||||||
new StreamCopier(shell.getErrorStream(), System.err)
|
new StreamCopier(shell.getErrorStream(), System.err, LoggerFactory.DEFAULT)
|
||||||
.bufSize(shell.getLocalMaxPacketSize())
|
.bufSize(shell.getLocalMaxPacketSize())
|
||||||
.spawn("stderr");
|
.spawn("stderr");
|
||||||
|
|
||||||
// Now make System.in act as stdin. To exit, hit Ctrl+D (since that results in an EOF on System.in)
|
// 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
|
// 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
|
// 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())
|
.bufSize(shell.getRemoteMaxPacketSize())
|
||||||
.copy();
|
.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;
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
||||||
import net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectListener;
|
import net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectListener;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@@ -42,8 +43,8 @@ public class X11 {
|
|||||||
|
|
||||||
final Command cmd = sess.exec("/usr/X11/bin/xcalc");
|
final Command cmd = sess.exec("/usr/X11/bin/xcalc");
|
||||||
|
|
||||||
new StreamCopier(cmd.getInputStream(), System.out).spawn("stdout");
|
new StreamCopier(cmd.getInputStream(), System.out, LoggerFactory.DEFAULT).spawn("stdout");
|
||||||
new StreamCopier(cmd.getErrorStream(), System.err).spawn("stderr");
|
new StreamCopier(cmd.getErrorStream(), System.err, LoggerFactory.DEFAULT).spawn("stderr");
|
||||||
|
|
||||||
// Wait for session & X11 channel to get closed
|
// Wait for session & X11 channel to get closed
|
||||||
ssh.getConnection().join();
|
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
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ package com.hierynomus.sshj.backport;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
|
||||||
public class Jdk7HttpProxySocket extends Socket {
|
public class Jdk7HttpProxySocket extends Socket {
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ public class Jdk7HttpProxySocket extends Socket {
|
|||||||
}
|
}
|
||||||
InetSocketAddress isa = (InetSocketAddress) endpoint;
|
InetSocketAddress isa = (InetSocketAddress) endpoint;
|
||||||
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
|
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
|
||||||
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
|
getOutputStream().write(httpConnect.getBytes(IOUtils.UTF8));
|
||||||
checkAndFlushProxyResponse();
|
checkAndFlushProxyResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ public class Jdk7HttpProxySocket extends Socket {
|
|||||||
throw new SocketException("Empty response from proxy");
|
throw new SocketException("Empty response from proxy");
|
||||||
}
|
}
|
||||||
|
|
||||||
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
|
String proxyResponse = new String(tmpBuffer, 0, len, IOUtils.UTF8);
|
||||||
|
|
||||||
// Expecting HTTP/1.x 200 OK
|
// Expecting HTTP/1.x 200 OK
|
||||||
if (proxyResponse.contains("200")) {
|
if (proxyResponse.contains("200")) {
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import net.schmizz.sshj.common.SSHRuntimeException;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
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.
|
* 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.
|
* 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) {
|
public Ed25519PublicKey(EdDSAPublicKeySpec spec) {
|
||||||
super(spec);
|
super(spec);
|
||||||
|
|
||||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
|
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
|
||||||
if (!spec.getParams().getCurve().equals(ed25519.getCurve())) {
|
if (!spec.getParams().getCurve().equals(ed25519.getCurve())) {
|
||||||
throw new SSHRuntimeException("Cannot create Ed25519 Public Key from wrong spec");
|
throw new SSHRuntimeException("Cannot create Ed25519 Public Key from wrong spec");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.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) throws Buffer.BufferException {
|
||||||
|
byte[] bytes = new byte[lineBuffer.available()];
|
||||||
|
lineBuffer.readRawBytes(bytes);
|
||||||
|
String header = new String(bytes, 0, bytes.length - 1);
|
||||||
|
log.debug("Received header: {}", header);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readIdentification(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException, TransportException {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import net.schmizz.sshj.transport.cipher.Cipher;
|
|||||||
*
|
*
|
||||||
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here.
|
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||||
public class BlockCiphers {
|
public class BlockCiphers {
|
||||||
|
|
||||||
public static final String COUNTER_MODE = "CTR";
|
public static final String COUNTER_MODE = "CTR";
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static com.hierynomus.sshj.transport.cipher.BlockCiphers.COUNTER_MODE;
|
|||||||
*
|
*
|
||||||
* - http://tools.ietf.org/id/draft-kanno-secsh-camellia-01.txt
|
* - http://tools.ietf.org/id/draft-kanno-secsh-camellia-01.txt
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||||
public class ExtendedBlockCiphers {
|
public class ExtendedBlockCiphers {
|
||||||
public static BlockCiphers.Factory Camellia128CTR() {
|
public static BlockCiphers.Factory Camellia128CTR() {
|
||||||
return new BlockCiphers.Factory(16, 128, "camellia128-ctr", "Camellia", COUNTER_MODE);
|
return new BlockCiphers.Factory(16, 128, "camellia128-ctr", "Camellia", COUNTER_MODE);
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import net.schmizz.sshj.transport.cipher.BaseCipher;
|
|||||||
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.SecureRandom;
|
|
||||||
|
|
||||||
public class StreamCipher extends BaseCipher {
|
public class StreamCipher extends BaseCipher {
|
||||||
|
|
||||||
@@ -29,6 +28,6 @@ public class StreamCipher extends BaseCipher {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
|
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||||
cipher.init(getMode(mode), getKeySpec(key), new SecureRandom());
|
cipher.init(getMode(mode), getKeySpec(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import net.schmizz.sshj.transport.cipher.Cipher;
|
|||||||
* - https://tools.ietf.org/html/rfc4253#section-6.3
|
* - https://tools.ietf.org/html/rfc4253#section-6.3
|
||||||
* - https://tools.ietf.org/html/rfc4345
|
* - https://tools.ietf.org/html/rfc4345
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||||
public class StreamCiphers {
|
public class StreamCiphers {
|
||||||
|
|
||||||
public static Factory Arcfour() {
|
public static Factory Arcfour() {
|
||||||
|
|||||||
44
src/main/java/com/hierynomus/sshj/transport/kex/DHG.java
Normal file
44
src/main/java/com/hierynomus/sshj/transport/kex/DHG.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.transport.digest.Digest;
|
||||||
|
import net.schmizz.sshj.transport.kex.AbstractDHG;
|
||||||
|
import net.schmizz.sshj.transport.kex.DH;
|
||||||
|
import net.schmizz.sshj.transport.kex.DHBase;
|
||||||
|
|
||||||
|
import javax.crypto.spec.DHParameterSpec;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DHG extends AbstractDHG {
|
||||||
|
private BigInteger group;
|
||||||
|
private BigInteger generator;
|
||||||
|
|
||||||
|
public DHG(BigInteger group, BigInteger generator, Digest digest) {
|
||||||
|
super(new DH(), digest);
|
||||||
|
this.group = group;
|
||||||
|
this.generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
||||||
|
dh.init(new DHParameterSpec(group, generator), trans.getConfig().getRandomFactory());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.transport.digest.*;
|
||||||
|
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
||||||
|
import static net.schmizz.sshj.transport.kex.DHGroupData.P16;
|
||||||
|
import static net.schmizz.sshj.transport.kex.DHGroupData.P18;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups
|
||||||
|
*
|
||||||
|
* - https://tools.ietf.org/html/rfc4253
|
||||||
|
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||||
|
public class DHGroups {
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group1SHA1() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group1-sha1", P1, G, new SHA1.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group14SHA1() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group14-sha1", P14, G, new SHA1.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group14SHA256() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group14-sha256", P14, G, new SHA256.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group15SHA512() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group15-sha512", P15, G, new SHA512.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group16SHA512() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group16-sha512", P16, G, new SHA512.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group17SHA512() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group17-sha512", P17, G, new SHA512.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group18SHA512() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group18-sha512", P18, G, new SHA512.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Named factory for DHG1 key exchange
|
||||||
|
*/
|
||||||
|
public static class Factory
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private BigInteger group;
|
||||||
|
private BigInteger generator;
|
||||||
|
private Factory.Named<Digest> digestFactory;
|
||||||
|
|
||||||
|
public Factory(String name, BigInteger group, BigInteger generator, Named<Digest> digestFactory) {
|
||||||
|
this.name = name;
|
||||||
|
this.group = group;
|
||||||
|
this.generator = generator;
|
||||||
|
this.digestFactory = digestFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyExchange create() {
|
||||||
|
return new DHG(group, generator, digestFactory.create());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.transport.digest.SHA256;
|
||||||
|
import net.schmizz.sshj.transport.digest.SHA384;
|
||||||
|
import net.schmizz.sshj.transport.digest.SHA512;
|
||||||
|
|
||||||
|
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of KEX methods that are not in official RFCs but are supported by some SSH servers.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||||
|
public class ExtendedDHGroups {
|
||||||
|
public static DHGroups.Factory Group14SHA256AtSSH() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group14-sha256@ssh.com", P14, G, new SHA256.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group15SHA256() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group15-sha256", P15, G, new SHA256.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group15SHA256AtSSH() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group15-sha256@ssh.com", P15, G, new SHA256.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group15SHA384AtSSH() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group15-sha384@ssh.com", P15, G, new SHA384.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group16SHA256() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group16-sha256", P16, G, new SHA256.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group16SHA384AtSSH() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group16-sha384@ssh.com", P16, G, new SHA384.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group16SHA512AtSSH() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group16-sha512@ssh.com", P16, G, new SHA512.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DHGroups.Factory Group18SHA512AtSSH() {
|
||||||
|
return new DHGroups.Factory("diffie-hellman-group18-sha512@ssh.com", P18, G, new SHA512.Factory());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
package com.hierynomus.sshj.userauth.certificate;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Certificate wrapper for public keys, created to help implement
|
||||||
|
* protocol described here:
|
||||||
|
*
|
||||||
|
* https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
|
||||||
|
*
|
||||||
|
* Consumed primarily by net.shmizz.sshj.common.KeyType
|
||||||
|
*
|
||||||
|
* @param <T> inner public key type
|
||||||
|
*/
|
||||||
|
public class Certificate<T extends PublicKey> implements PublicKey {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final T publicKey;
|
||||||
|
private final byte[] nonce;
|
||||||
|
private final BigInteger serial;
|
||||||
|
private final long type;
|
||||||
|
private final String id;
|
||||||
|
private final List<String> validPrincipals;
|
||||||
|
private final Date validAfter;
|
||||||
|
private final Date validBefore;
|
||||||
|
private final Map<String, String> critOptions;
|
||||||
|
private final Map<String, String> extensions;
|
||||||
|
private final byte[] signatureKey;
|
||||||
|
private final byte[] signature;
|
||||||
|
|
||||||
|
Certificate(Builder<T> builder) {
|
||||||
|
this.publicKey = builder.getPublicKey();
|
||||||
|
this.nonce = builder.getNonce();
|
||||||
|
this.serial = builder.getSerial();
|
||||||
|
this.type = builder.getType();
|
||||||
|
this.id = builder.getId();
|
||||||
|
this.validPrincipals = builder.getValidPrincipals();
|
||||||
|
this.validAfter = builder.getValidAfter();
|
||||||
|
this.validBefore = builder.getValidBefore();
|
||||||
|
this.critOptions = builder.getCritOptions();
|
||||||
|
this.extensions = builder.getExtensions();
|
||||||
|
this.signatureKey = builder.getSignatureKey();
|
||||||
|
this.signature = builder.getSignature();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <P extends PublicKey> Builder<P> getBuilder() {
|
||||||
|
return new Builder<P>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getNonce() {
|
||||||
|
return nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger getSerial() {
|
||||||
|
return serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getValidPrincipals() {
|
||||||
|
return validPrincipals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getValidAfter() {
|
||||||
|
return validAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getValidBefore() {
|
||||||
|
return validBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getCritOptions() {
|
||||||
|
return critOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getExtensions() {
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignatureKey() {
|
||||||
|
return signatureKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
return publicKey.getEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return publicKey.getAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFormat() {
|
||||||
|
return publicKey.getFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder<T extends PublicKey> {
|
||||||
|
private T publicKey;
|
||||||
|
private byte[] nonce;
|
||||||
|
private BigInteger serial;
|
||||||
|
private long type;
|
||||||
|
private String id;
|
||||||
|
private List<String> validPrincipals;
|
||||||
|
private Date validAfter;
|
||||||
|
private Date validBefore;
|
||||||
|
private Map<String, String> critOptions;
|
||||||
|
private Map<String, String> extensions;
|
||||||
|
private byte[] signatureKey;
|
||||||
|
private byte[] signature;
|
||||||
|
|
||||||
|
public Certificate<T> build() {
|
||||||
|
return new Certificate<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getPublicKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> publicKey(T publicKey) {
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getNonce() {
|
||||||
|
return nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> nonce(byte[] nonce) {
|
||||||
|
this.nonce = nonce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger getSerial() {
|
||||||
|
return serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> serial(BigInteger serial) {
|
||||||
|
this.serial = serial;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> type(long type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> id(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getValidPrincipals() {
|
||||||
|
return validPrincipals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> validPrincipals(List<String> validPrincipals) {
|
||||||
|
this.validPrincipals = validPrincipals;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getValidAfter() {
|
||||||
|
return validAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> validAfter(Date validAfter) {
|
||||||
|
this.validAfter = validAfter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getValidBefore() {
|
||||||
|
return validBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> validBefore(Date validBefore) {
|
||||||
|
this.validBefore = validBefore;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getCritOptions() {
|
||||||
|
return critOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> critOptions(Map<String, String> critOptions) {
|
||||||
|
this.critOptions = critOptions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getExtensions() {
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> extensions(Map<String, String> extensions) {
|
||||||
|
this.extensions = extensions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignatureKey() {
|
||||||
|
return signatureKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> signatureKey(byte[] signatureKey) {
|
||||||
|
this.signatureKey = signatureKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> signature(byte[] signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.TimeUnit;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
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
|
* 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}.
|
* 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 name name of this event
|
||||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||||
*/
|
*/
|
||||||
public Event(String name, ExceptionChainer<T> chainer) {
|
public Event(String name, ExceptionChainer<T> chainer, LoggerFactory loggerFactory) {
|
||||||
promise = new Promise<Object, T>(name, chainer);
|
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 chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||||
* @param lock lock to use
|
* @param lock lock to use
|
||||||
*/
|
*/
|
||||||
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock, LoggerFactory loggerFactory) {
|
||||||
promise = new Promise<Object, T>(name, chainer, lock);
|
promise = new Promise<Object, T>(name, chainer, lock, loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets this event to be {@code true}. Short for {@code set(true)}. */
|
/** Sets this event to be {@code true}. Short for {@code set(true)}. */
|
||||||
|
|||||||
@@ -16,13 +16,14 @@
|
|||||||
package net.schmizz.concurrent;
|
package net.schmizz.concurrent;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.locks.Condition;
|
import java.util.concurrent.locks.Condition;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
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
|
* 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}.
|
* 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> {
|
public class Promise<V, T extends Throwable> {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log;
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final ExceptionChainer<T> chainer;
|
private final ExceptionChainer<T> chainer;
|
||||||
private final ReentrantLock lock;
|
private final ReentrantLock lock;
|
||||||
@@ -49,8 +49,8 @@ public class Promise<V, T extends Throwable> {
|
|||||||
* @param name name of this promise
|
* @param name name of this promise
|
||||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||||
*/
|
*/
|
||||||
public Promise(String name, ExceptionChainer<T> chainer) {
|
public Promise(String name, ExceptionChainer<T> chainer, LoggerFactory loggerFactory) {
|
||||||
this(name, chainer, null);
|
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 chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||||
* @param lock lock to use
|
* @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.name = name;
|
||||||
this.chainer = chainer;
|
this.chainer = chainer;
|
||||||
this.lock = lock == null ? new ReentrantLock() : lock;
|
this.lock = lock == null ? new ReentrantLock() : lock;
|
||||||
|
this.log = loggerFactory.getLogger(getClass());
|
||||||
this.cond = this.lock.newCondition();
|
this.cond = this.lock.newCondition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,16 @@ import net.schmizz.sshj.connection.ConnectionException;
|
|||||||
import net.schmizz.sshj.connection.ConnectionImpl;
|
import net.schmizz.sshj.connection.ConnectionImpl;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public abstract class KeepAlive extends Thread {
|
public abstract class KeepAlive extends Thread {
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final Logger log;
|
||||||
|
|
||||||
protected final ConnectionImpl conn;
|
protected final ConnectionImpl conn;
|
||||||
|
|
||||||
protected int keepAliveInterval = 0;
|
protected int keepAliveInterval = 0;
|
||||||
|
|
||||||
protected KeepAlive(ConnectionImpl conn, String name) {
|
protected KeepAlive(ConnectionImpl conn, String name) {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
|
log = conn.getTransport().getConfig().getLoggerFactory().getLogger(getClass());
|
||||||
setName(name);
|
setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +64,8 @@ public abstract class KeepAlive extends Thread {
|
|||||||
}
|
}
|
||||||
Thread.sleep(hi * 1000);
|
Thread.sleep(hi * 1000);
|
||||||
}
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Interrupt signal may be catched when sleeping.
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If we weren't interrupted, kill the transport, then this exception was unexpected.
|
// If we weren't interrupted, kill the transport, then this exception was unexpected.
|
||||||
// Else we're in shutdown-mode already, so don't forcibly kill the transport.
|
// Else we're in shutdown-mode already, so don't forcibly kill the transport.
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ import net.schmizz.sshj.common.SSHPacket;
|
|||||||
import net.schmizz.sshj.transport.Transport;
|
import net.schmizz.sshj.transport.Transport;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/** An abstract class for {@link Service} that implements common or default functionality. */
|
/** An abstract class for {@link Service} that implements common or default functionality. */
|
||||||
public abstract class AbstractService
|
public abstract class AbstractService
|
||||||
implements Service {
|
implements Service {
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final Logger log;
|
||||||
|
|
||||||
/** Assigned name of this service */
|
/** Assigned name of this service */
|
||||||
protected final String name;
|
protected final String name;
|
||||||
@@ -39,6 +38,7 @@ public abstract class AbstractService
|
|||||||
public AbstractService(String name, Transport trans) {
|
public AbstractService(String name, Transport trans) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.trans = trans;
|
this.trans = trans;
|
||||||
|
log = trans.getConfig().getLoggerFactory().getLogger(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,12 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
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.JCERandom;
|
||||||
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
||||||
|
|
||||||
public class AndroidConfig
|
public class AndroidConfig
|
||||||
extends DefaultConfig {
|
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
|
@Override
|
||||||
protected void initRandomFactory(boolean ignored) {
|
protected void initRandomFactory(boolean ignored) {
|
||||||
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
|
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package net.schmizz.sshj;
|
|||||||
|
|
||||||
import net.schmizz.keepalive.KeepAliveProvider;
|
import net.schmizz.keepalive.KeepAliveProvider;
|
||||||
import net.schmizz.sshj.common.Factory;
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.signature.Signature;
|
import net.schmizz.sshj.signature.Signature;
|
||||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
import net.schmizz.sshj.transport.compression.Compression;
|
import net.schmizz.sshj.transport.compression.Compression;
|
||||||
@@ -175,4 +176,14 @@ public interface Config {
|
|||||||
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
|
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
|
||||||
*/
|
*/
|
||||||
void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent);
|
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;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
import net.schmizz.keepalive.KeepAliveProvider;
|
import net.schmizz.keepalive.KeepAliveProvider;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.Factory;
|
import net.schmizz.sshj.common.Factory;
|
||||||
import net.schmizz.sshj.signature.Signature;
|
import net.schmizz.sshj.signature.Signature;
|
||||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
@@ -45,6 +46,7 @@ public class ConfigImpl
|
|||||||
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
|
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
|
||||||
|
|
||||||
private boolean waitForServerIdentBeforeSendingClientIdent = false;
|
private boolean waitForServerIdentBeforeSendingClientIdent = false;
|
||||||
|
private LoggerFactory loggerFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Factory.Named<Cipher>> getCipherFactories() {
|
public List<Factory.Named<Cipher>> getCipherFactories() {
|
||||||
@@ -169,4 +171,14 @@ public class ConfigImpl
|
|||||||
public void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent) {
|
public void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent) {
|
||||||
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
|
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggerFactory getLoggerFactory() {
|
||||||
|
return loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLoggerFactory(LoggerFactory loggerFactory) {
|
||||||
|
this.loggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,44 +18,34 @@ package net.schmizz.sshj;
|
|||||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||||
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
|
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
|
||||||
|
import com.hierynomus.sshj.transport.kex.DHGroups;
|
||||||
|
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
|
||||||
|
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||||
import net.schmizz.keepalive.KeepAliveProvider;
|
import net.schmizz.keepalive.KeepAliveProvider;
|
||||||
import net.schmizz.sshj.common.Factory;
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
import net.schmizz.sshj.signature.SignatureDSA;
|
import net.schmizz.sshj.signature.SignatureDSA;
|
||||||
import net.schmizz.sshj.signature.SignatureECDSA;
|
import net.schmizz.sshj.signature.SignatureECDSA;
|
||||||
import net.schmizz.sshj.signature.SignatureRSA;
|
import net.schmizz.sshj.signature.SignatureRSA;
|
||||||
import net.schmizz.sshj.transport.cipher.AES128CBC;
|
import net.schmizz.sshj.transport.cipher.*;
|
||||||
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.compression.NoneCompression;
|
import net.schmizz.sshj.transport.compression.NoneCompression;
|
||||||
import net.schmizz.sshj.transport.kex.*;
|
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
|
||||||
import net.schmizz.sshj.transport.mac.HMACMD5;
|
import net.schmizz.sshj.transport.kex.DHGexSHA1;
|
||||||
import net.schmizz.sshj.transport.mac.HMACMD596;
|
import net.schmizz.sshj.transport.kex.DHGexSHA256;
|
||||||
import net.schmizz.sshj.transport.mac.HMACSHA1;
|
import net.schmizz.sshj.transport.kex.ECDHNistP;
|
||||||
import net.schmizz.sshj.transport.mac.HMACSHA196;
|
import net.schmizz.sshj.transport.mac.*;
|
||||||
import net.schmizz.sshj.transport.mac.HMACSHA2256;
|
|
||||||
import net.schmizz.sshj.transport.mac.HMACSHA2512;
|
|
||||||
import net.schmizz.sshj.transport.random.BouncyCastleRandom;
|
import net.schmizz.sshj.transport.random.BouncyCastleRandom;
|
||||||
import net.schmizz.sshj.transport.random.JCERandom;
|
import net.schmizz.sshj.transport.random.JCERandom;
|
||||||
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
||||||
|
import net.schmizz.sshj.userauth.keyprovider.PKCS5KeyFile;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
|
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
|
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.awt.image.ByteLookupTable;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link net.schmizz.sshj.Config} that is initialized as follows. Items marked with an asterisk are added to the config only if
|
* 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 +72,11 @@ import java.util.List;
|
|||||||
public class DefaultConfig
|
public class DefaultConfig
|
||||||
extends ConfigImpl {
|
extends ConfigImpl {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private Logger log;
|
||||||
|
|
||||||
private static final String VERSION = "SSHJ_0_16_0";
|
|
||||||
|
|
||||||
public DefaultConfig() {
|
public DefaultConfig() {
|
||||||
setVersion(VERSION);
|
setLoggerFactory(LoggerFactory.DEFAULT);
|
||||||
|
setVersion(readVersionFromProperties());
|
||||||
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
|
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
|
||||||
initKeyExchangeFactories(bouncyCastleRegistered);
|
initKeyExchangeFactories(bouncyCastleRegistered);
|
||||||
initRandomFactory(bouncyCastleRegistered);
|
initRandomFactory(bouncyCastleRegistered);
|
||||||
@@ -99,18 +88,50 @@ public class DefaultConfig
|
|||||||
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
|
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String readVersionFromProperties() {
|
||||||
|
try {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.load(DefaultConfig.class.getClassLoader().getResourceAsStream("sshj.properties"));
|
||||||
|
String property = properties.getProperty("sshj.version");
|
||||||
|
return "SSHJ_" + property.replace('-', '_'); // '-' is a disallowed character, see RFC-4253#section-4.2
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Could not read the sshj.properties file, returning an 'unknown' version as fallback.");
|
||||||
|
return "SSHJ_VERSION_UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLoggerFactory(LoggerFactory loggerFactory) {
|
||||||
|
super.setLoggerFactory(loggerFactory);
|
||||||
|
log = loggerFactory.getLogger(getClass());
|
||||||
|
}
|
||||||
|
|
||||||
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
|
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
|
||||||
if (bouncyCastleRegistered)
|
if (bouncyCastleRegistered) {
|
||||||
setKeyExchangeFactories(new Curve25519SHA256.Factory(),
|
setKeyExchangeFactories(new Curve25519SHA256.Factory(),
|
||||||
new DHGexSHA256.Factory(),
|
new DHGexSHA256.Factory(),
|
||||||
new ECDHNistP.Factory521(),
|
new ECDHNistP.Factory521(),
|
||||||
new ECDHNistP.Factory384(),
|
new ECDHNistP.Factory384(),
|
||||||
new ECDHNistP.Factory256(),
|
new ECDHNistP.Factory256(),
|
||||||
new DHGexSHA1.Factory(),
|
new DHGexSHA1.Factory(),
|
||||||
new DHG14.Factory(),
|
DHGroups.Group1SHA1(),
|
||||||
new DHG1.Factory());
|
DHGroups.Group14SHA1(),
|
||||||
else
|
DHGroups.Group14SHA256(),
|
||||||
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
|
DHGroups.Group15SHA512(),
|
||||||
|
DHGroups.Group16SHA512(),
|
||||||
|
DHGroups.Group17SHA512(),
|
||||||
|
DHGroups.Group18SHA512(),
|
||||||
|
ExtendedDHGroups.Group14SHA256AtSSH(),
|
||||||
|
ExtendedDHGroups.Group15SHA256(),
|
||||||
|
ExtendedDHGroups.Group15SHA256AtSSH(),
|
||||||
|
ExtendedDHGroups.Group15SHA384AtSSH(),
|
||||||
|
ExtendedDHGroups.Group16SHA256(),
|
||||||
|
ExtendedDHGroups.Group16SHA384AtSSH(),
|
||||||
|
ExtendedDHGroups.Group16SHA512AtSSH(),
|
||||||
|
ExtendedDHGroups.Group18SHA512AtSSH());
|
||||||
|
} else {
|
||||||
|
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initRandomFactory(boolean bouncyCastleRegistered) {
|
protected void initRandomFactory(boolean bouncyCastleRegistered) {
|
||||||
@@ -120,7 +141,12 @@ public class DefaultConfig
|
|||||||
|
|
||||||
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
|
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
|
||||||
if (bouncyCastleRegistered) {
|
if (bouncyCastleRegistered) {
|
||||||
setFileKeyProviderFactories(new PKCS8KeyFile.Factory(), new OpenSSHKeyFile.Factory(), new PuTTYKeyFile.Factory());
|
setFileKeyProviderFactories(
|
||||||
|
new OpenSSHKeyV1KeyFile.Factory(),
|
||||||
|
new PKCS8KeyFile.Factory(),
|
||||||
|
new PKCS5KeyFile.Factory(),
|
||||||
|
new OpenSSHKeyFile.Factory(),
|
||||||
|
new PuTTYKeyFile.Factory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +182,8 @@ public class DefaultConfig
|
|||||||
BlockCiphers.TwofishCBC(),
|
BlockCiphers.TwofishCBC(),
|
||||||
StreamCiphers.Arcfour(),
|
StreamCiphers.Arcfour(),
|
||||||
StreamCiphers.Arcfour128(),
|
StreamCiphers.Arcfour128(),
|
||||||
StreamCiphers.Arcfour256()));
|
StreamCiphers.Arcfour256())
|
||||||
|
);
|
||||||
|
|
||||||
boolean warn = false;
|
boolean warn = false;
|
||||||
// Ref. https://issues.apache.org/jira/browse/SSHD-24
|
// Ref. https://issues.apache.org/jira/browse/SSHD-24
|
||||||
@@ -182,17 +209,26 @@ public class DefaultConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void initSignatureFactories() {
|
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() {
|
protected void initMACFactories() {
|
||||||
setMACFactories(new HMACSHA1.Factory(), new HMACSHA196.Factory(), new HMACMD5.Factory(),
|
setMACFactories(
|
||||||
new HMACMD596.Factory(), new HMACSHA2256.Factory(), new HMACSHA2512.Factory());
|
new HMACSHA1.Factory(),
|
||||||
|
new HMACSHA196.Factory(),
|
||||||
|
new HMACMD5.Factory(),
|
||||||
|
new HMACMD596.Factory(),
|
||||||
|
new HMACSHA2256.Factory(),
|
||||||
|
new HMACSHA2512.Factory()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initCompressionFactories() {
|
protected void initCompressionFactories() {
|
||||||
setCompressionFactories(new NoneCompression.Factory());
|
setCompressionFactories(new NoneCompression.Factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Factory;
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHException;
|
import net.schmizz.sshj.common.SSHException;
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
import net.schmizz.sshj.connection.Connection;
|
import net.schmizz.sshj.connection.Connection;
|
||||||
@@ -45,17 +47,8 @@ import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
|||||||
import net.schmizz.sshj.userauth.UserAuth;
|
import net.schmizz.sshj.userauth.UserAuth;
|
||||||
import net.schmizz.sshj.userauth.UserAuthException;
|
import net.schmizz.sshj.userauth.UserAuthException;
|
||||||
import net.schmizz.sshj.userauth.UserAuthImpl;
|
import net.schmizz.sshj.userauth.UserAuthImpl;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
import net.schmizz.sshj.userauth.keyprovider.*;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
|
import net.schmizz.sshj.userauth.method.*;
|
||||||
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.password.PasswordFinder;
|
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||||
import net.schmizz.sshj.userauth.password.PasswordUpdateProvider;
|
import net.schmizz.sshj.userauth.password.PasswordUpdateProvider;
|
||||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||||
@@ -63,21 +56,16 @@ import net.schmizz.sshj.userauth.password.Resource;
|
|||||||
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
|
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
|
||||||
import org.ietf.jgss.Oid;
|
import org.ietf.jgss.Oid;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.security.auth.login.LoginContext;
|
import javax.security.auth.login.LoginContext;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Secure SHell client API.
|
* Secure SHell client API.
|
||||||
@@ -128,7 +116,8 @@ public class SSHClient
|
|||||||
public static final int DEFAULT_PORT = 22;
|
public static final int DEFAULT_PORT = 22;
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final LoggerFactory loggerFactory;
|
||||||
|
protected final Logger log;
|
||||||
|
|
||||||
/** Transport layer */
|
/** Transport layer */
|
||||||
protected final Transport trans;
|
protected final Transport trans;
|
||||||
@@ -139,6 +128,11 @@ public class SSHClient
|
|||||||
/** {@code ssh-connection} service */
|
/** {@code ssh-connection} service */
|
||||||
protected final Connection conn;
|
protected final Connection conn;
|
||||||
|
|
||||||
|
private final List<LocalPortForwarder> forwarders = new ArrayList<LocalPortForwarder>();
|
||||||
|
|
||||||
|
/** character set of the remote machine */
|
||||||
|
protected Charset remoteCharset = IOUtils.UTF8;
|
||||||
|
|
||||||
/** Default constructor. Initializes this object using {@link DefaultConfig}. */
|
/** Default constructor. Initializes this object using {@link DefaultConfig}. */
|
||||||
public SSHClient() {
|
public SSHClient() {
|
||||||
this(new DefaultConfig());
|
this(new DefaultConfig());
|
||||||
@@ -151,6 +145,8 @@ public class SSHClient
|
|||||||
*/
|
*/
|
||||||
public SSHClient(Config config) {
|
public SSHClient(Config config) {
|
||||||
super(DEFAULT_PORT);
|
super(DEFAULT_PORT);
|
||||||
|
loggerFactory = config.getLoggerFactory();
|
||||||
|
log = loggerFactory.getLogger(getClass());
|
||||||
this.trans = new TransportImpl(config, this);
|
this.trans = new TransportImpl(config, this);
|
||||||
this.auth = new UserAuthImpl(trans);
|
this.auth = new UserAuthImpl(trans);
|
||||||
this.conn = new ConnectionImpl(trans, config.getKeepAliveProvider());
|
this.conn = new ConnectionImpl(trans, config.getKeepAliveProvider());
|
||||||
@@ -221,8 +217,9 @@ public class SSHClient
|
|||||||
public void auth(String username, Iterable<AuthMethod> methods)
|
public void auth(String username, Iterable<AuthMethod> methods)
|
||||||
throws UserAuthException, TransportException {
|
throws UserAuthException, TransportException {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
final Deque<UserAuthException> savedEx = new LinkedList<>();
|
final Deque<UserAuthException> savedEx = new LinkedList<UserAuthException>();
|
||||||
for (AuthMethod method: methods) {
|
for (AuthMethod method: methods) {
|
||||||
|
method.setLoggerFactory(loggerFactory);
|
||||||
try {
|
try {
|
||||||
if (auth.authenticate(username, (Service) conn, method, trans.getTimeoutMs()))
|
if (auth.authenticate(username, (Service) conn, method, trans.getTimeoutMs()))
|
||||||
return;
|
return;
|
||||||
@@ -324,7 +321,7 @@ public class SSHClient
|
|||||||
public void authPublickey(String username)
|
public void authPublickey(String username)
|
||||||
throws UserAuthException, TransportException {
|
throws UserAuthException, TransportException {
|
||||||
final String base = System.getProperty("user.home") + File.separator + ".ssh" + File.separator;
|
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 +339,7 @@ public class SSHClient
|
|||||||
*/
|
*/
|
||||||
public void authPublickey(String username, Iterable<KeyProvider> keyProviders)
|
public void authPublickey(String username, Iterable<KeyProvider> keyProviders)
|
||||||
throws UserAuthException, TransportException {
|
throws UserAuthException, TransportException {
|
||||||
final List<AuthMethod> am = new LinkedList<>();
|
final List<AuthMethod> am = new LinkedList<AuthMethod>();
|
||||||
for (KeyProvider kp : keyProviders)
|
for (KeyProvider kp : keyProviders)
|
||||||
am.add(new AuthPublickey(kp));
|
am.add(new AuthPublickey(kp));
|
||||||
auth(username, am);
|
auth(username, am);
|
||||||
@@ -385,7 +382,7 @@ public class SSHClient
|
|||||||
*/
|
*/
|
||||||
public void authPublickey(String username, String... locations)
|
public void authPublickey(String username, String... locations)
|
||||||
throws UserAuthException, TransportException {
|
throws UserAuthException, TransportException {
|
||||||
final List<KeyProvider> keyProviders = new LinkedList<>();
|
final List<KeyProvider> keyProviders = new LinkedList<KeyProvider>();
|
||||||
for (String loc : locations) {
|
for (String loc : locations) {
|
||||||
try {
|
try {
|
||||||
log.debug("Attempting to load key from: {}", loc);
|
log.debug("Attempting to load key from: {}", loc);
|
||||||
@@ -415,7 +412,7 @@ public class SSHClient
|
|||||||
public void authGssApiWithMic(String username, LoginContext context, Oid supportedOid, Oid... supportedOids)
|
public void authGssApiWithMic(String username, LoginContext context, Oid supportedOid, Oid... supportedOids)
|
||||||
throws UserAuthException, TransportException {
|
throws UserAuthException, TransportException {
|
||||||
// insert supportedOid to the front of the list since ordering matters
|
// 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);
|
oids.add(0, supportedOid);
|
||||||
|
|
||||||
auth(username, new AuthGssApiWithMic(context, oids));
|
auth(username, new AuthGssApiWithMic(context, oids));
|
||||||
@@ -431,6 +428,14 @@ public class SSHClient
|
|||||||
@Override
|
@Override
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
for (LocalPortForwarder forwarder : forwarders) {
|
||||||
|
try {
|
||||||
|
forwarder.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Error closing forwarder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
forwarders.clear();
|
||||||
trans.disconnect();
|
trans.disconnect();
|
||||||
super.disconnect();
|
super.disconnect();
|
||||||
}
|
}
|
||||||
@@ -440,6 +445,15 @@ public class SSHClient
|
|||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the character set used to communicate with the remote machine for certain strings (like paths).
|
||||||
|
*
|
||||||
|
* @return remote character set
|
||||||
|
*/
|
||||||
|
public Charset getRemoteCharset() {
|
||||||
|
return remoteCharset;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return a {@link RemotePortForwarder} that allows requesting remote forwarding over this connection. */
|
/** @return a {@link RemotePortForwarder} that allows requesting remote forwarding over this connection. */
|
||||||
public RemotePortForwarder getRemotePortForwarder() {
|
public RemotePortForwarder getRemotePortForwarder() {
|
||||||
synchronized (conn) {
|
synchronized (conn) {
|
||||||
@@ -524,8 +538,13 @@ public class SSHClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link KeyProvider} instance from given location on the file system. Currently only PKCS8 format
|
* Creates a {@link KeyProvider} instance from given location on the file system. Currently the following private key files are supported:
|
||||||
* private key files are supported (OpenSSH uses this format).
|
* <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/>
|
* <p/>
|
||||||
*
|
*
|
||||||
* @param location the location of the key file
|
* @param location the location of the key file
|
||||||
@@ -630,7 +649,7 @@ public class SSHClient
|
|||||||
*/
|
*/
|
||||||
public void loadKnownHosts(File location)
|
public void loadKnownHosts(File location)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
addHostKeyVerifier(new OpenSSHKnownHosts(location));
|
addHostKeyVerifier(new OpenSSHKnownHosts(location, loggerFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -648,7 +667,9 @@ public class SSHClient
|
|||||||
*/
|
*/
|
||||||
public LocalPortForwarder newLocalPortForwarder(LocalPortForwarder.Parameters parameters,
|
public LocalPortForwarder newLocalPortForwarder(LocalPortForwarder.Parameters parameters,
|
||||||
ServerSocket serverSocket) {
|
ServerSocket serverSocket) {
|
||||||
return new LocalPortForwarder(conn, parameters, serverSocket);
|
LocalPortForwarder forwarder = new LocalPortForwarder(conn, parameters, serverSocket, loggerFactory);
|
||||||
|
forwarders.add(forwarder);
|
||||||
|
return forwarder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -675,7 +696,7 @@ public class SSHClient
|
|||||||
public SCPFileTransfer newSCPFileTransfer() {
|
public SCPFileTransfer newSCPFileTransfer() {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
checkAuthenticated();
|
checkAuthenticated();
|
||||||
return new SCPFileTransfer(this);
|
return new SCPFileTransfer(this, loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -701,12 +722,22 @@ public class SSHClient
|
|||||||
doKex();
|
doKex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the character set used to communicate with the remote machine for certain strings (like paths)
|
||||||
|
*
|
||||||
|
* @param remoteCharset
|
||||||
|
* remote character set or {@code null} for default
|
||||||
|
*/
|
||||||
|
public void setRemoteCharset(Charset remoteCharset) {
|
||||||
|
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Session startSession()
|
public Session startSession()
|
||||||
throws ConnectionException, TransportException {
|
throws ConnectionException, TransportException {
|
||||||
checkConnected();
|
checkConnected();
|
||||||
checkAuthenticated();
|
checkAuthenticated();
|
||||||
final SessionChannel sess = new SessionChannel(conn);
|
final SessionChannel sess = new SessionChannel(conn, remoteCharset);
|
||||||
sess.open();
|
sess.open();
|
||||||
return sess;
|
return sess;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,12 +48,50 @@ public abstract class SocketClient {
|
|||||||
this.defaultPort = defaultPort;
|
this.defaultPort = defaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host, int port) throws IOException {
|
/**
|
||||||
socket = socketFactory.createSocket();
|
* Connect to a host via a proxy.
|
||||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
* @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();
|
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.
|
* Connect to a host via a proxy.
|
||||||
@@ -75,23 +113,41 @@ public abstract class SocketClient {
|
|||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String hostname, int port) throws IOException {
|
public void connect(String hostname) throws IOException {
|
||||||
this.hostname = hostname;
|
connect(hostname, defaultPort);
|
||||||
connect(InetAddress.getByName(hostname), port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void connect(String hostname, int port) throws IOException {
|
||||||
* Connect to a host via a proxy.
|
if (hostname == null) {
|
||||||
* @param hostname The host name to connect to.
|
connect(InetAddress.getByName(null), port);
|
||||||
* @param port The port to connect to.
|
} else {
|
||||||
* @param proxy The proxy to connect via.
|
this.hostname = hostname;
|
||||||
* @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}
|
socket = socketFactory.createSocket();
|
||||||
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
|
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
|
||||||
*/
|
onConnect();
|
||||||
@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, 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)
|
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
|
||||||
@@ -102,43 +158,6 @@ public abstract class SocketClient {
|
|||||||
onConnect();
|
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 {
|
public void disconnect() throws IOException {
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
socket.close();
|
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;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
|
|
||||||
@@ -1742,4 +1727,4 @@ public class Base64 {
|
|||||||
private Base64() {
|
private Base64() {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end class Base64
|
} // end class Base64
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -57,6 +57,11 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
|
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
|
||||||
public static final int MAX_SIZE = (1 << 30);
|
public static final int MAX_SIZE = (1 << 30);
|
||||||
|
|
||||||
|
/** Maximum size of a uint64 */
|
||||||
|
private static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
|
||||||
|
.shiftLeft(64)
|
||||||
|
.subtract(BigInteger.ONE);
|
||||||
|
|
||||||
protected static int getNextPowerOf2(int i) {
|
protected static int getNextPowerOf2(int i) {
|
||||||
int j = 1;
|
int j = 1;
|
||||||
while (j < i) {
|
while (j < i) {
|
||||||
@@ -311,7 +316,7 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
public T putUInt32(long uint32) {
|
public T putUInt32(long uint32) {
|
||||||
ensureCapacity(4);
|
ensureCapacity(4);
|
||||||
if (uint32 < 0 || uint32 > 0xffffffffL)
|
if (uint32 < 0 || uint32 > 0xffffffffL)
|
||||||
throw new RuntimeException("Invalid value: " + uint32);
|
throw new IllegalArgumentException("Invalid value: " + uint32);
|
||||||
data[wpos++] = (byte) (uint32 >> 24);
|
data[wpos++] = (byte) (uint32 >> 24);
|
||||||
data[wpos++] = (byte) (uint32 >> 16);
|
data[wpos++] = (byte) (uint32 >> 16);
|
||||||
data[wpos++] = (byte) (uint32 >> 8);
|
data[wpos++] = (byte) (uint32 >> 8);
|
||||||
@@ -343,10 +348,29 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
return uint64;
|
return uint64;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
public BigInteger readUInt64AsBigInteger()
|
||||||
|
throws BufferException {
|
||||||
|
byte[] magnitude = new byte[8];
|
||||||
|
readRawBytes(magnitude);
|
||||||
|
return new BigInteger(1, magnitude);
|
||||||
|
}
|
||||||
|
|
||||||
public T putUInt64(long uint64) {
|
public T putUInt64(long uint64) {
|
||||||
if (uint64 < 0)
|
if (uint64 < 0)
|
||||||
throw new RuntimeException("Invalid value: " + uint64);
|
throw new IllegalArgumentException("Invalid value: " + uint64);
|
||||||
|
return putUInt64Unchecked(uint64);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T putUInt64(BigInteger uint64) {
|
||||||
|
if (uint64.compareTo(MAX_UINT64_VALUE) > 0 ||
|
||||||
|
uint64.compareTo(BigInteger.ZERO) < 0) {
|
||||||
|
throw new IllegalArgumentException("Invalid value: " + uint64);
|
||||||
|
}
|
||||||
|
return putUInt64Unchecked(uint64.longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private T putUInt64Unchecked(long uint64) {
|
||||||
data[wpos++] = (byte) (uint64 >> 56);
|
data[wpos++] = (byte) (uint64 >> 56);
|
||||||
data[wpos++] = (byte) (uint64 >> 48);
|
data[wpos++] = (byte) (uint64 >> 48);
|
||||||
data[wpos++] = (byte) (uint64 >> 40);
|
data[wpos++] = (byte) (uint64 >> 40);
|
||||||
@@ -361,24 +385,31 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
/**
|
/**
|
||||||
* Reads an SSH string
|
* Reads an SSH string
|
||||||
*
|
*
|
||||||
|
* @param cs the charset to use for decoding
|
||||||
|
*
|
||||||
* @return the string as a Java {@code String}
|
* @return the string as a Java {@code String}
|
||||||
*/
|
*/
|
||||||
public String readString()
|
public String readString(Charset cs)
|
||||||
throws BufferException {
|
throws BufferException {
|
||||||
int len = readUInt32AsInt();
|
int len = readUInt32AsInt();
|
||||||
if (len < 0 || len > 32768)
|
if (len < 0 || len > 32768)
|
||||||
throw new BufferException("Bad item length: " + len);
|
throw new BufferException("Bad item length: " + len);
|
||||||
ensureAvailable(len);
|
ensureAvailable(len);
|
||||||
String s;
|
String s = new String(data, rpos, len, cs);
|
||||||
try {
|
|
||||||
s = new String(data, rpos, len, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new SSHRuntimeException(e);
|
|
||||||
}
|
|
||||||
rpos += len;
|
rpos += len;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads an SSH string using {@code UTF8}
|
||||||
|
*
|
||||||
|
* @return the string as a Java {@code String}
|
||||||
|
*/
|
||||||
|
public String readString()
|
||||||
|
throws BufferException {
|
||||||
|
return readString(IOUtils.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an SSH string
|
* Reads an SSH string
|
||||||
*
|
*
|
||||||
@@ -397,8 +428,12 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
return putBytes(str, offset, len);
|
return putBytes(str, offset, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T putString(String string, Charset cs) {
|
||||||
|
return putString(string.getBytes(cs));
|
||||||
|
}
|
||||||
|
|
||||||
public T putString(String string) {
|
public T putString(String string) {
|
||||||
return putString(string.getBytes(IOUtils.UTF8));
|
return putString(string, IOUtils.UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -426,8 +461,7 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
public PublicKey readPublicKey()
|
public PublicKey readPublicKey()
|
||||||
throws BufferException {
|
throws BufferException {
|
||||||
try {
|
try {
|
||||||
final String type = readString();
|
return KeyType.fromString(readString()).readPubKeyFromBuffer(this);
|
||||||
return KeyType.fromString(type).readPubKeyFromBuffer(type, this);
|
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -26,25 +23,33 @@ import java.nio.charset.Charset;
|
|||||||
|
|
||||||
public class IOUtils {
|
public class IOUtils {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(IOUtils.class);
|
|
||||||
|
|
||||||
public static final Charset UTF8 = Charset.forName("UTF-8");
|
public static final Charset UTF8 = Charset.forName("UTF-8");
|
||||||
|
|
||||||
public static void closeQuietly(Closeable... closeables) {
|
public static void closeQuietly(Closeable... closeables) {
|
||||||
for (Closeable c : closeables)
|
closeQuietly(LoggerFactory.DEFAULT, closeables);
|
||||||
try {
|
|
||||||
if (c != null)
|
|
||||||
c.close();
|
|
||||||
} catch (IOException logged) {
|
|
||||||
LOG.warn("Error closing {} - {}", c, logged);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteArrayOutputStream readFully(InputStream stream)
|
public static ByteArrayOutputStream readFully(InputStream stream)
|
||||||
throws IOException {
|
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();
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
new StreamCopier(stream, baos).copy();
|
new StreamCopier(stream, baos, loggerFactory).copy();
|
||||||
return baos;
|
return baos;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import com.hierynomus.sshj.secg.SecgUtils;
|
import java.math.BigInteger;
|
||||||
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
import java.security.GeneralSecurityException;
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
import java.security.Key;
|
||||||
import net.i2p.crypto.eddsa.math.GroupElement;
|
import java.security.KeyFactory;
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
import java.security.PublicKey;
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
import java.security.interfaces.DSAPrivateKey;
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
import java.security.interfaces.DSAPublicKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.DSAPublicKeySpec;
|
||||||
|
import java.security.spec.RSAPublicKeySpec;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||||
@@ -30,24 +43,23 @@ import org.bouncycastle.math.ec.ECPoint;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import com.hierynomus.sshj.secg.SecgUtils;
|
||||||
import java.security.GeneralSecurityException;
|
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
||||||
import java.security.Key;
|
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.PublicKey;
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
import java.security.interfaces.*;
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||||
import java.security.spec.DSAPublicKeySpec;
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||||
import java.security.spec.RSAPublicKeySpec;
|
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||||
import java.util.Arrays;
|
import net.schmizz.sshj.common.Buffer.BufferException;
|
||||||
|
|
||||||
/** Type of key e.g. rsa, dsa */
|
/** Type of key e.g. rsa, dsa */
|
||||||
public enum KeyType {
|
public enum KeyType {
|
||||||
|
|
||||||
|
|
||||||
/** SSH identifier for RSA keys */
|
/** SSH identifier for RSA keys */
|
||||||
RSA("ssh-rsa") {
|
RSA("ssh-rsa") {
|
||||||
@Override
|
@Override
|
||||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
final BigInteger e, n;
|
final BigInteger e, n;
|
||||||
try {
|
try {
|
||||||
@@ -61,24 +73,22 @@ public enum KeyType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
final RSAPublicKey rsaKey = (RSAPublicKey) pk;
|
final RSAPublicKey rsaKey = (RSAPublicKey) pk;
|
||||||
buf.putString(sType)
|
buf.putMPInt(rsaKey.getPublicExponent()) // e
|
||||||
.putMPInt(rsaKey.getPublicExponent()) // e
|
.putMPInt(rsaKey.getModulus()); // n
|
||||||
.putMPInt(rsaKey.getModulus()); // n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isMyType(Key key) {
|
protected boolean isMyType(Key key) {
|
||||||
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
|
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** SSH identifier for DSA keys */
|
/** SSH identifier for DSA keys */
|
||||||
DSA("ssh-dss") {
|
DSA("ssh-dss") {
|
||||||
@Override
|
@Override
|
||||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
BigInteger p, q, g, y;
|
BigInteger p, q, g, y;
|
||||||
try {
|
try {
|
||||||
@@ -94,10 +104,9 @@ public enum KeyType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
final DSAPublicKey dsaKey = (DSAPublicKey) pk;
|
final DSAPublicKey dsaKey = (DSAPublicKey) pk;
|
||||||
buf.putString(sType)
|
buf.putMPInt(dsaKey.getParams().getP()) // p
|
||||||
.putMPInt(dsaKey.getParams().getP()) // p
|
|
||||||
.putMPInt(dsaKey.getParams().getQ()) // q
|
.putMPInt(dsaKey.getParams().getQ()) // q
|
||||||
.putMPInt(dsaKey.getParams().getG()) // g
|
.putMPInt(dsaKey.getParams().getG()) // g
|
||||||
.putMPInt(dsaKey.getY()); // y
|
.putMPInt(dsaKey.getY()); // y
|
||||||
@@ -115,8 +124,11 @@ public enum KeyType {
|
|||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
|
if (!SecurityUtils.isBouncyCastleRegistered()) {
|
||||||
|
throw new GeneralSecurityException("BouncyCastle is required to read a key of type " + sType);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// final String algo = buf.readString(); it has been already read
|
// final String algo = buf.readString(); it has been already read
|
||||||
final String curveName = buf.readString();
|
final String curveName = buf.readString();
|
||||||
@@ -128,7 +140,7 @@ public enum KeyType {
|
|||||||
buf.readRawBytes(y);
|
buf.readRawBytes(y);
|
||||||
if(log.isDebugEnabled()) {
|
if(log.isDebugEnabled()) {
|
||||||
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
|
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
|
||||||
type,
|
sType,
|
||||||
curveName,
|
curveName,
|
||||||
keyLen,
|
keyLen,
|
||||||
x04,
|
x04,
|
||||||
@@ -159,12 +171,11 @@ public enum KeyType {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
final ECPublicKey ecdsa = (ECPublicKey) pk;
|
final ECPublicKey ecdsa = (ECPublicKey) pk;
|
||||||
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
|
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
|
||||||
|
|
||||||
buf.putString(sType)
|
buf.putString(NISTP_CURVE)
|
||||||
.putString(NISTP_CURVE)
|
|
||||||
.putBytes(encoded);
|
.putBytes(encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,24 +186,23 @@ public enum KeyType {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ED25519("ssh-ed25519") {
|
ED25519("ssh-ed25519") {
|
||||||
private final Logger logger = LoggerFactory.getLogger(KeyType.class);
|
private final Logger log = LoggerFactory.getLogger(KeyType.class);
|
||||||
@Override
|
@Override
|
||||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf) throws GeneralSecurityException {
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException {
|
||||||
try {
|
try {
|
||||||
final int keyLen = buf.readUInt32AsInt();
|
final int keyLen = buf.readUInt32AsInt();
|
||||||
final byte[] p = new byte[keyLen];
|
final byte[] p = new byte[keyLen];
|
||||||
buf.readRawBytes(p);
|
buf.readRawBytes(p);
|
||||||
if (logger.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Key algo: %s, Key curve: 25519, Key Len: %s\np: %s",
|
log.debug(String.format("Key algo: %s, Key curve: 25519, Key Len: %s\np: %s",
|
||||||
type,
|
sType,
|
||||||
keyLen,
|
keyLen,
|
||||||
Arrays.toString(p))
|
Arrays.toString(p))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
|
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512);
|
||||||
GroupElement point = ed25519.getCurve().createPoint(p, true);
|
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(p, ed25519);
|
||||||
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(point, ed25519);
|
|
||||||
return new Ed25519PublicKey(publicSpec);
|
return new Ed25519PublicKey(publicSpec);
|
||||||
|
|
||||||
} catch (Buffer.BufferException be) {
|
} catch (Buffer.BufferException be) {
|
||||||
@@ -201,9 +211,9 @@ public enum KeyType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
EdDSAPublicKey key = (EdDSAPublicKey) pk;
|
EdDSAPublicKey key = (EdDSAPublicKey) pk;
|
||||||
buf.putString(sType).putBytes(key.getAbyte());
|
buf.putBytes(key.getAbyte());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -212,12 +222,50 @@ public enum KeyType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Signed rsa certificate */
|
||||||
|
RSA_CERT("ssh-rsa-cert-v01@openssh.com") {
|
||||||
|
@Override
|
||||||
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return CertUtils.readPubKey(buf, RSA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
CertUtils.writePubKeyContentsIntoBuffer(pk, RSA, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isMyType(Key key) {
|
||||||
|
return CertUtils.isCertificateOfType(key, RSA);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Signed dsa certificate */
|
||||||
|
DSA_CERT("ssh-dss-cert-v01@openssh.com") {
|
||||||
|
@Override
|
||||||
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return CertUtils.readPubKey(buf, DSA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
CertUtils.writePubKeyContentsIntoBuffer(pk, DSA, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isMyType(Key key) {
|
||||||
|
return CertUtils.isCertificateOfType(key, DSA);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/** Unrecognized */
|
/** Unrecognized */
|
||||||
UNKNOWN("unknown") {
|
UNKNOWN("unknown") {
|
||||||
@Override
|
@Override
|
||||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
throws GeneralSecurityException {
|
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
|
@Override
|
||||||
@@ -225,6 +273,11 @@ public enum KeyType {
|
|||||||
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
|
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isMyType(Key key) {
|
protected boolean isMyType(Key key) {
|
||||||
return false;
|
return false;
|
||||||
@@ -240,10 +293,14 @@ public enum KeyType {
|
|||||||
this.sType = type;
|
this.sType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
public abstract PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
|
protected abstract void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||||
|
|
||||||
|
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
writePubKeyContentsIntoBuffer(pk, buf.putString(sType));
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract boolean isMyType(Key key);
|
protected abstract boolean isMyType(Key key);
|
||||||
|
|
||||||
@@ -266,4 +323,128 @@ public enum KeyType {
|
|||||||
return sType;
|
return sType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CertUtils {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T extends PublicKey> Certificate<T> readPubKey(Buffer<?> buf, KeyType innerKeyType) throws GeneralSecurityException {
|
||||||
|
Certificate.Builder<T> builder = Certificate.getBuilder();
|
||||||
|
|
||||||
|
try {
|
||||||
|
builder.nonce(buf.readBytes());
|
||||||
|
builder.publicKey((T) innerKeyType.readPubKeyFromBuffer(buf));
|
||||||
|
builder.serial(buf.readUInt64AsBigInteger());
|
||||||
|
builder.type(buf.readUInt32());
|
||||||
|
builder.id(buf.readString());
|
||||||
|
builder.validPrincipals(unpackList(buf.readBytes()));
|
||||||
|
builder.validAfter(dateFromEpoch(buf.readUInt64()));
|
||||||
|
builder.validBefore(dateFromEpoch(buf.readUInt64()));
|
||||||
|
builder.critOptions(unpackMap(buf.readBytes()));
|
||||||
|
builder.extensions(unpackMap(buf.readBytes()));
|
||||||
|
buf.readString(); // reserved
|
||||||
|
builder.signatureKey(buf.readBytes());
|
||||||
|
builder.signature(buf.readBytes());
|
||||||
|
} catch (Buffer.BufferException be) {
|
||||||
|
throw new GeneralSecurityException(be);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writePubKeyContentsIntoBuffer(PublicKey publicKey, KeyType innerKeyType, Buffer<?> buf) {
|
||||||
|
Certificate<PublicKey> certificate = toCertificate(publicKey);
|
||||||
|
buf.putBytes(certificate.getNonce());
|
||||||
|
innerKeyType.writePubKeyContentsIntoBuffer(certificate.getKey(), buf);
|
||||||
|
buf.putUInt64(certificate.getSerial())
|
||||||
|
.putUInt32(certificate.getType())
|
||||||
|
.putString(certificate.getId())
|
||||||
|
.putBytes(packList(certificate.getValidPrincipals()))
|
||||||
|
.putUInt64(epochFromDate(certificate.getValidAfter()))
|
||||||
|
.putUInt64(epochFromDate(certificate.getValidBefore()))
|
||||||
|
.putBytes(packMap(certificate.getCritOptions()))
|
||||||
|
.putBytes(packMap(certificate.getExtensions()))
|
||||||
|
.putString("") // reserved
|
||||||
|
.putBytes(certificate.getSignatureKey())
|
||||||
|
.putBytes(certificate.getSignature());
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isCertificateOfType(Key key, KeyType innerKeyType) {
|
||||||
|
if (!(key instanceof Certificate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Key innerKey = ((Certificate<PublicKey>) key).getKey();
|
||||||
|
return innerKeyType.isMyType(innerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static Certificate<PublicKey> toCertificate(PublicKey key) {
|
||||||
|
if (!(key instanceof Certificate)) {
|
||||||
|
throw new UnsupportedOperationException("Can't convert non-certificate key " +
|
||||||
|
key.getAlgorithm() + " to certificate");
|
||||||
|
}
|
||||||
|
return ((Certificate<PublicKey>) key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Date dateFromEpoch(long seconds) {
|
||||||
|
return new Date(seconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long epochFromDate(Date date) {
|
||||||
|
return date.getTime() / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String unpackString(byte[] packedString) throws BufferException {
|
||||||
|
if (packedString.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return new Buffer.PlainBuffer(packedString).readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> unpackList(byte[] packedString) throws BufferException {
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
Buffer<?> buf = new Buffer.PlainBuffer(packedString);
|
||||||
|
while (buf.available() > 0) {
|
||||||
|
list.add(buf.readString());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> unpackMap(byte[] packedString) throws BufferException {
|
||||||
|
Map<String, String> map = new LinkedHashMap<String, String>();
|
||||||
|
Buffer<?> buf = new Buffer.PlainBuffer(packedString);
|
||||||
|
while (buf.available() > 0) {
|
||||||
|
String name = buf.readString();
|
||||||
|
String data = unpackString(buf.readStringAsBytes());
|
||||||
|
map.put(name, data);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] packString(String data) {
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
return "".getBytes();
|
||||||
|
}
|
||||||
|
return new Buffer.PlainBuffer().putString(data).getCompactData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] packList(Iterable<String> strings) {
|
||||||
|
Buffer<?> buf = new Buffer.PlainBuffer();
|
||||||
|
for (String string : strings) {
|
||||||
|
buf.putString(string);
|
||||||
|
}
|
||||||
|
return buf.getCompactData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] packMap(Map<String, String> map) {
|
||||||
|
Buffer<?> buf = new Buffer.PlainBuffer();
|
||||||
|
List<String> keys = new ArrayList<String>(map.keySet());
|
||||||
|
Collections.sort(keys);
|
||||||
|
for (String key : keys) {
|
||||||
|
buf.putString(key);
|
||||||
|
String value = map.get(key);
|
||||||
|
buf.putString(packString(value));
|
||||||
|
}
|
||||||
|
return buf.getCompactData();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ public class SSHRuntimeException
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SSHRuntimeException(Throwable cause) {
|
public SSHRuntimeException(Throwable cause) {
|
||||||
this(null, cause);
|
this(cause.getMessage(), cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,47 +15,25 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import java.security.*;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.KeyAgreement;
|
import javax.crypto.KeyAgreement;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import java.security.GeneralSecurityException;
|
import org.slf4j.Logger;
|
||||||
import java.security.KeyFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import java.security.KeyPairGenerator;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.Signature;
|
|
||||||
|
|
||||||
// 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 {
|
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);
|
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";
|
public static final String BOUNCY_CASTLE = "BC";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -67,6 +45,42 @@ public class SecurityUtils {
|
|||||||
private static Boolean registerBouncyCastle;
|
private static Boolean registerBouncyCastle;
|
||||||
private static boolean registrationDone;
|
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)
|
public static synchronized Cipher getCipher(String transformation)
|
||||||
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
|
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
|
||||||
register();
|
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"
|
* 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
|
* @param key the public key
|
||||||
*
|
|
||||||
* @return the fingerprint
|
* @return the fingerprint
|
||||||
*
|
|
||||||
* @see <a href="http://tools.ietf.org/html/draft-friedl-secsh-fingerprint-00">specification</a>
|
* @see <a href="http://tools.ietf.org/html/draft-friedl-secsh-fingerprint-00">specification</a>
|
||||||
*/
|
*/
|
||||||
public static String getFingerprint(PublicKey key) {
|
public static String getFingerprint(PublicKey key) {
|
||||||
@@ -105,9 +117,7 @@ public class SecurityUtils {
|
|||||||
* Creates a new instance of {@link KeyAgreement} with the given algorithm.
|
* Creates a new instance of {@link KeyAgreement} with the given algorithm.
|
||||||
*
|
*
|
||||||
* @param algorithm key agreement algorithm
|
* @param algorithm key agreement algorithm
|
||||||
*
|
|
||||||
* @return new instance
|
* @return new instance
|
||||||
*
|
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
*/
|
*/
|
||||||
@@ -124,9 +134,7 @@ public class SecurityUtils {
|
|||||||
* Creates a new instance of {@link KeyFactory} with the given algorithm.
|
* Creates a new instance of {@link KeyFactory} with the given algorithm.
|
||||||
*
|
*
|
||||||
* @param algorithm key factory algorithm e.g. RSA, DSA
|
* @param algorithm key factory algorithm e.g. RSA, DSA
|
||||||
*
|
|
||||||
* @return new instance
|
* @return new instance
|
||||||
*
|
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
*/
|
*/
|
||||||
@@ -143,9 +151,7 @@ public class SecurityUtils {
|
|||||||
* Creates a new instance of {@link KeyPairGenerator} with the given algorithm.
|
* Creates a new instance of {@link KeyPairGenerator} with the given algorithm.
|
||||||
*
|
*
|
||||||
* @param algorithm key pair generator algorithm
|
* @param algorithm key pair generator algorithm
|
||||||
*
|
|
||||||
* @return new instance
|
* @return new instance
|
||||||
*
|
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
*/
|
*/
|
||||||
@@ -162,9 +168,7 @@ public class SecurityUtils {
|
|||||||
* Create a new instance of {@link Mac} with the given algorithm.
|
* Create a new instance of {@link Mac} with the given algorithm.
|
||||||
*
|
*
|
||||||
* @param algorithm MAC algorithm
|
* @param algorithm MAC algorithm
|
||||||
*
|
|
||||||
* @return new instance
|
* @return new instance
|
||||||
*
|
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
*/
|
*/
|
||||||
@@ -181,9 +185,7 @@ public class SecurityUtils {
|
|||||||
* Create a new instance of {@link MessageDigest} with the given algorithm.
|
* Create a new instance of {@link MessageDigest} with the given algorithm.
|
||||||
*
|
*
|
||||||
* @param algorithm MessageDigest algorithm name
|
* @param algorithm MessageDigest algorithm name
|
||||||
*
|
|
||||||
* @return new instance
|
* @return new instance
|
||||||
*
|
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
*/
|
*/
|
||||||
@@ -243,20 +245,16 @@ public class SecurityUtils {
|
|||||||
|
|
||||||
private static void register() {
|
private static void register() {
|
||||||
if (!registrationDone) {
|
if (!registrationDone) {
|
||||||
if (securityProvider == null && (registerBouncyCastle == null || registerBouncyCastle))
|
if (securityProvider == null && (registerBouncyCastle == null || registerBouncyCastle)) {
|
||||||
// Use an inner class to avoid a strong dependency on BouncyCastle
|
registerSecurityProvider("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
||||||
try {
|
if (securityProvider == null && registerBouncyCastle == null) {
|
||||||
new BouncyCastleRegistration().run();
|
LOG.info("BouncyCastle not registered, using the default JCE provider");
|
||||||
} catch (Throwable t) {
|
} else if (securityProvider == null) {
|
||||||
if (registerBouncyCastle == null)
|
LOG.error("Failed to register BouncyCastle as the defaut JCE provider");
|
||||||
LOG.info("BouncyCastle not registered, using the default JCE provider");
|
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
registrationDone = true;
|
registrationDone = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package net.schmizz.sshj.common;
|
|||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.concurrent.ExceptionChainer;
|
import net.schmizz.concurrent.ExceptionChainer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -39,8 +38,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 InputStream in;
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
|
|
||||||
@@ -50,9 +49,11 @@ public class StreamCopier {
|
|||||||
private boolean keepFlushing = true;
|
private boolean keepFlushing = true;
|
||||||
private long length = -1;
|
private long length = -1;
|
||||||
|
|
||||||
public StreamCopier(InputStream in, OutputStream out) {
|
public StreamCopier(InputStream in, OutputStream out, LoggerFactory loggerFactory) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.out = out;
|
this.out = out;
|
||||||
|
this.loggerFactory = loggerFactory;
|
||||||
|
this.log = loggerFactory.getLogger(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamCopier bufSize(int bufSize) {
|
public StreamCopier bufSize(int bufSize) {
|
||||||
@@ -66,8 +67,11 @@ public class StreamCopier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public StreamCopier listener(Listener listener) {
|
public StreamCopier listener(Listener listener) {
|
||||||
if (listener == null) listener = NULL_LISTENER;
|
if (listener == null) {
|
||||||
this.listener = listener;
|
this.listener = NULL_LISTENER;
|
||||||
|
} else {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +95,7 @@ public class StreamCopier {
|
|||||||
public IOException chain(Throwable t) {
|
public IOException chain(Throwable t) {
|
||||||
return (t instanceof IOException) ? (IOException) t : new IOException(t);
|
return (t instanceof IOException) ? (IOException) t : new IOException(t);
|
||||||
}
|
}
|
||||||
});
|
}, loggerFactory);
|
||||||
|
|
||||||
new Thread() {
|
new Thread() {
|
||||||
{
|
{
|
||||||
@@ -107,7 +111,7 @@ public class StreamCopier {
|
|||||||
log.debug("Done copying from {}", in);
|
log.debug("Done copying from {}", in);
|
||||||
doneEvent.set();
|
doneEvent.set();
|
||||||
} catch (IOException ioe) {
|
} 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);
|
doneEvent.deliverError(ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,11 +128,13 @@ public class StreamCopier {
|
|||||||
final long startTime = System.currentTimeMillis();
|
final long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
while ((read = in.read(buf)) != -1)
|
while ((read = in.read(buf)) != -1) {
|
||||||
count = write(buf, count, read);
|
count += write(buf, count, read);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1)
|
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1) {
|
||||||
count = write(buf, count, read);
|
count += write(buf, count, read);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keepFlushing)
|
if (!keepFlushing)
|
||||||
@@ -136,7 +142,7 @@ public class StreamCopier {
|
|||||||
|
|
||||||
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
|
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
|
||||||
final double sizeKiB = count / 1024.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)
|
if (length != -1 && read == -1)
|
||||||
throw new IOException("Encountered EOF, could not transfer " + length + " bytes");
|
throw new IOException("Encountered EOF, could not transfer " + length + " bytes");
|
||||||
@@ -144,14 +150,13 @@ public class StreamCopier {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long write(byte[] buf, long count, int read)
|
private long write(byte[] buf, long curPos, int len)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
out.write(buf, 0, read);
|
out.write(buf, 0, len);
|
||||||
count += read;
|
|
||||||
if (keepFlushing)
|
if (keepFlushing)
|
||||||
out.flush();
|
out.flush();
|
||||||
listener.reportProgress(count);
|
listener.reportProgress(curPos + len);
|
||||||
return count;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,4 +156,4 @@ public interface Connection {
|
|||||||
* @return The configured {@link net.schmizz.keepalive.KeepAlive} mechanism.
|
* @return The configured {@link net.schmizz.keepalive.KeepAlive} mechanism.
|
||||||
*/
|
*/
|
||||||
KeepAlive getKeepAlive();
|
KeepAlive getKeepAlive();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,7 @@ import net.schmizz.concurrent.Promise;
|
|||||||
import net.schmizz.keepalive.KeepAlive;
|
import net.schmizz.keepalive.KeepAlive;
|
||||||
import net.schmizz.keepalive.KeepAliveProvider;
|
import net.schmizz.keepalive.KeepAliveProvider;
|
||||||
import net.schmizz.sshj.AbstractService;
|
import net.schmizz.sshj.AbstractService;
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.connection.channel.Channel;
|
import net.schmizz.sshj.connection.channel.Channel;
|
||||||
import net.schmizz.sshj.connection.channel.OpenFailException.Reason;
|
import net.schmizz.sshj.connection.channel.OpenFailException.Reason;
|
||||||
import net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener;
|
import net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener;
|
||||||
@@ -131,10 +126,9 @@ public class ConnectionImpl
|
|||||||
@Override
|
@Override
|
||||||
public void handle(Message msg, SSHPacket buf)
|
public void handle(Message msg, SSHPacket buf)
|
||||||
throws SSHException {
|
throws SSHException {
|
||||||
if (msg.in(91, 100))
|
if (msg.in(91, 100)) {
|
||||||
getChannel(buf).handle(msg, buf);
|
getChannel(buf).handle(msg, buf);
|
||||||
|
} else if (msg.in(80, 90)) {
|
||||||
else if (msg.in(80, 90))
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case REQUEST_SUCCESS:
|
case REQUEST_SUCCESS:
|
||||||
gotGlobalReqResponse(buf);
|
gotGlobalReqResponse(buf);
|
||||||
@@ -147,10 +141,11 @@ public class ConnectionImpl
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.handle(msg, buf);
|
super.handle(msg, buf);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
else
|
|
||||||
super.handle(msg, buf);
|
super.handle(msg, buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -179,11 +174,11 @@ public class ConnectionImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void join()
|
public void join() throws InterruptedException {
|
||||||
throws InterruptedException {
|
|
||||||
synchronized (internalSynchronizer) {
|
synchronized (internalSynchronizer) {
|
||||||
while (!channels.isEmpty())
|
while (!channels.isEmpty()) {
|
||||||
internalSynchronizer.wait();
|
internalSynchronizer.wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +199,7 @@ public class ConnectionImpl
|
|||||||
|
|
||||||
Promise<SSHPacket, ConnectionException> promise = null;
|
Promise<SSHPacket, ConnectionException> promise = null;
|
||||||
if (wantReply) {
|
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);
|
globalReqPromises.add(promise);
|
||||||
}
|
}
|
||||||
return promise;
|
return promise;
|
||||||
|
|||||||
@@ -17,22 +17,16 @@ package net.schmizz.sshj.connection.channel;
|
|||||||
|
|
||||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.connection.Connection;
|
import net.schmizz.sshj.connection.Connection;
|
||||||
import net.schmizz.sshj.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import net.schmizz.sshj.transport.Transport;
|
import net.schmizz.sshj.transport.Transport;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -44,7 +38,8 @@ public abstract class AbstractChannel
|
|||||||
private static final int REMOTE_MAX_PACKET_SIZE_CEILING = 1024 * 1024;
|
private static final int REMOTE_MAX_PACKET_SIZE_CEILING = 1024 * 1024;
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final LoggerFactory loggerFactory;
|
||||||
|
protected final Logger log;
|
||||||
|
|
||||||
/** Transport layer */
|
/** Transport layer */
|
||||||
protected final Transport trans;
|
protected final Transport trans;
|
||||||
@@ -57,6 +52,10 @@ public abstract class AbstractChannel
|
|||||||
private final int id;
|
private final int id;
|
||||||
/** Remote recipient ID */
|
/** Remote recipient ID */
|
||||||
private int recipient;
|
private int recipient;
|
||||||
|
/** Remote character set */
|
||||||
|
private final Charset remoteCharset;
|
||||||
|
|
||||||
|
private boolean eof = false;
|
||||||
|
|
||||||
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
|
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
|
||||||
|
|
||||||
@@ -82,22 +81,28 @@ public abstract class AbstractChannel
|
|||||||
private volatile boolean autoExpand = false;
|
private volatile boolean autoExpand = false;
|
||||||
|
|
||||||
protected AbstractChannel(Connection conn, String type) {
|
protected AbstractChannel(Connection conn, String type) {
|
||||||
|
this(conn, type, null);
|
||||||
|
}
|
||||||
|
protected AbstractChannel(Connection conn, String type, Charset remoteCharset) {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
|
this.loggerFactory = conn.getTransport().getConfig().getLoggerFactory();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.log = loggerFactory.getLogger(getClass());
|
||||||
this.trans = conn.getTransport();
|
this.trans = conn.getTransport();
|
||||||
|
|
||||||
|
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
|
||||||
id = conn.nextID();
|
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);
|
in = new ChannelInputStream(this, trans, lwin);
|
||||||
|
|
||||||
openEvent = new Event<ConnectionException>("chan#" + id + " / " + "open", ConnectionException.chainer, openCloseLock);
|
openEvent = new Event<ConnectionException>("chan#" + id + " / " + "open", ConnectionException.chainer, openCloseLock, loggerFactory);
|
||||||
closeEvent = new Event<ConnectionException>("chan#" + id + " / " + "close", ConnectionException.chainer, openCloseLock);
|
closeEvent = new Event<ConnectionException>("chan#" + id + " / " + "close", ConnectionException.chainer, openCloseLock, loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init(int recipient, long remoteWinSize, long remoteMaxPacketSize) {
|
protected void init(int recipient, long remoteWinSize, long remoteMaxPacketSize) {
|
||||||
this.recipient = recipient;
|
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);
|
out = new ChannelOutputStream(this, trans, rwin);
|
||||||
log.debug("Initialized - {}", this);
|
log.debug("Initialized - {}", this);
|
||||||
}
|
}
|
||||||
@@ -137,6 +142,11 @@ public abstract class AbstractChannel
|
|||||||
return recipient;
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Charset getRemoteCharset() {
|
||||||
|
return remoteCharset;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRemoteMaxPacketSize() {
|
public int getRemoteMaxPacketSize() {
|
||||||
return rwin.getMaxPacketSize();
|
return rwin.getMaxPacketSize();
|
||||||
@@ -191,10 +201,20 @@ public abstract class AbstractChannel
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
gotUnknown(msg, buf);
|
gotUnknown(msg, buf);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEOF() {
|
||||||
|
return eof;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggerFactory getLoggerFactory() {
|
||||||
|
return loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
private void gotClose()
|
private void gotClose()
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
log.debug("Got close");
|
log.debug("Got close");
|
||||||
@@ -321,6 +341,8 @@ public abstract class AbstractChannel
|
|||||||
|
|
||||||
protected void gotUnknown(Message msg, SSHPacket buf)
|
protected void gotUnknown(Message msg, SSHPacket buf)
|
||||||
throws ConnectionException, TransportException {
|
throws ConnectionException, TransportException {
|
||||||
|
log.warn("Got unknown packet with type {}", msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleRequest(String reqType, SSHPacket buf)
|
protected void handleRequest(String reqType, SSHPacket buf)
|
||||||
@@ -362,7 +384,7 @@ public abstract class AbstractChannel
|
|||||||
Event<ConnectionException> responseEvent = null;
|
Event<ConnectionException> responseEvent = null;
|
||||||
if (wantReply) {
|
if (wantReply) {
|
||||||
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType,
|
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType,
|
||||||
ConnectionException.chainer);
|
ConnectionException.chainer, loggerFactory);
|
||||||
chanReqResponseEvents.add(responseEvent);
|
chanReqResponseEvents.add(responseEvent);
|
||||||
}
|
}
|
||||||
return responseEvent;
|
return responseEvent;
|
||||||
@@ -393,6 +415,7 @@ public abstract class AbstractChannel
|
|||||||
/** Called when EOF has been received. Subclasses can override but must call super. */
|
/** Called when EOF has been received. Subclasses can override but must call super. */
|
||||||
protected void eofInputStreams() {
|
protected void eofInputStreams() {
|
||||||
in.eof();
|
in.eof();
|
||||||
|
eof = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -402,4 +425,4 @@ public abstract class AbstractChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package net.schmizz.sshj.connection.channel;
|
package net.schmizz.sshj.connection.channel;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.ErrorNotifiable;
|
import net.schmizz.sshj.common.ErrorNotifiable;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHPacketHandler;
|
import net.schmizz.sshj.common.SSHPacketHandler;
|
||||||
import net.schmizz.sshj.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
@@ -23,15 +24,18 @@ import net.schmizz.sshj.transport.TransportException;
|
|||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/** A channel is the basic medium for application-layer data on top of an SSH transport. */
|
/**
|
||||||
public interface Channel
|
* A channel is the basic medium for application-layer data on top of an SSH transport.
|
||||||
extends Closeable, SSHPacketHandler, ErrorNotifiable {
|
*/
|
||||||
|
public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
|
||||||
|
|
||||||
/** Direct channels are those that are initiated by us. */
|
/**
|
||||||
interface Direct
|
* Direct channels are those that are initiated by us.
|
||||||
extends Channel {
|
*/
|
||||||
|
interface Direct extends Channel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request opening this channel from remote end.
|
* Request opening this channel from remote end.
|
||||||
@@ -40,27 +44,30 @@ public interface Channel
|
|||||||
* @throws ConnectionException other connection-layer error
|
* @throws ConnectionException other connection-layer error
|
||||||
* @throws TransportException error writing packets etc.
|
* @throws TransportException error writing packets etc.
|
||||||
*/
|
*/
|
||||||
void open()
|
void open() throws ConnectionException, TransportException;
|
||||||
throws ConnectionException, TransportException;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Forwarded channels are those that are initiated by the server. */
|
/**
|
||||||
interface Forwarded
|
* Forwarded channels are those that are initiated by the server.
|
||||||
extends Channel {
|
*/
|
||||||
|
interface Forwarded extends Channel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm {@code CHANNEL_OPEN} request.
|
* Confirm {@code CHANNEL_OPEN} request.
|
||||||
*
|
*
|
||||||
* @throws TransportException error sending confirmation packet
|
* @throws TransportException error sending confirmation packet
|
||||||
*/
|
*/
|
||||||
void confirm()
|
void confirm() throws TransportException;
|
||||||
throws TransportException;
|
|
||||||
|
|
||||||
/** @return the IP of where the forwarded connection originates. */
|
/**
|
||||||
|
* @return the IP of where the forwarded connection originates.
|
||||||
|
*/
|
||||||
String getOriginatorIP();
|
String getOriginatorIP();
|
||||||
|
|
||||||
/** @return port from which the forwarded connection originates. */
|
/**
|
||||||
|
* @return port from which the forwarded connection originates.
|
||||||
|
*/
|
||||||
int getOriginatorPort();
|
int getOriginatorPort();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,55 +75,76 @@ public interface Channel
|
|||||||
*
|
*
|
||||||
* @param reason indicate {@link OpenFailException.Reason reason} for rejection of the request
|
* @param reason indicate {@link OpenFailException.Reason reason} for rejection of the request
|
||||||
* @param message indicate a message for why the request is rejected
|
* @param message indicate a message for why the request is rejected
|
||||||
*
|
|
||||||
* @throws TransportException error sending rejection packet
|
* @throws TransportException error sending rejection packet
|
||||||
*/
|
*/
|
||||||
void reject(OpenFailException.Reason reason, String message)
|
void reject(OpenFailException.Reason reason, String message) throws TransportException;
|
||||||
throws TransportException;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Close this channel. */
|
/**
|
||||||
|
* Close this channel.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
void close()
|
void close() throws TransportException, ConnectionException;
|
||||||
throws TransportException, ConnectionException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether auto-expansion of local window is set.
|
* @return whether auto-expansion of local window is set.
|
||||||
*
|
|
||||||
* @see #setAutoExpand(boolean)
|
* @see #setAutoExpand(boolean)
|
||||||
*/
|
*/
|
||||||
boolean getAutoExpand();
|
boolean getAutoExpand();
|
||||||
|
|
||||||
/** @return the channel ID */
|
/**
|
||||||
|
* @return the channel ID
|
||||||
|
*/
|
||||||
int getID();
|
int getID();
|
||||||
|
|
||||||
/** @return the {@code InputStream} for this channel. */
|
/**
|
||||||
|
* @return the {@code InputStream} for this channel.
|
||||||
|
*/
|
||||||
InputStream getInputStream();
|
InputStream getInputStream();
|
||||||
|
|
||||||
/** @return the maximum packet size that we have specified. */
|
/**
|
||||||
|
* @return the maximum packet size that we have specified.
|
||||||
|
*/
|
||||||
int getLocalMaxPacketSize();
|
int getLocalMaxPacketSize();
|
||||||
|
|
||||||
/** @return the current local window size. */
|
/**
|
||||||
|
* @return the current local window size.
|
||||||
|
*/
|
||||||
long getLocalWinSize();
|
long getLocalWinSize();
|
||||||
|
|
||||||
/** @return an {@code OutputStream} for this channel. */
|
/**
|
||||||
|
* @return an {@code OutputStream} for this channel.
|
||||||
|
*/
|
||||||
OutputStream getOutputStream();
|
OutputStream getOutputStream();
|
||||||
|
|
||||||
/** @return the channel ID at the remote end. */
|
/**
|
||||||
|
* @return the channel ID at the remote end.
|
||||||
|
*/
|
||||||
int getRecipient();
|
int getRecipient();
|
||||||
|
|
||||||
/** @return the maximum packet size as specified by the remote end. */
|
/** @return the character set used to communicate with the remote machine for certain strings (like paths). */
|
||||||
|
Charset getRemoteCharset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum packet size as specified by the remote end.
|
||||||
|
*/
|
||||||
int getRemoteMaxPacketSize();
|
int getRemoteMaxPacketSize();
|
||||||
|
|
||||||
/** @return the current remote window size. */
|
/**
|
||||||
|
* @return the current remote window size.
|
||||||
|
*/
|
||||||
long getRemoteWinSize();
|
long getRemoteWinSize();
|
||||||
|
|
||||||
/** @return the channel type identifier. */
|
/**
|
||||||
|
* @return the channel type identifier.
|
||||||
|
*/
|
||||||
String getType();
|
String getType();
|
||||||
|
|
||||||
/** @return whether the channel is open. */
|
/**
|
||||||
|
* @return whether the channel is open.
|
||||||
|
*/
|
||||||
boolean isOpen();
|
boolean isOpen();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,10 +156,17 @@ public interface Channel
|
|||||||
*/
|
*/
|
||||||
void setAutoExpand(boolean autoExpand);
|
void setAutoExpand(boolean autoExpand);
|
||||||
|
|
||||||
void join()
|
void join() throws ConnectionException;
|
||||||
throws ConnectionException;
|
|
||||||
|
|
||||||
void join(long timeout, TimeUnit unit)
|
void join(long timeout, TimeUnit unit) throws ConnectionException;
|
||||||
throws ConnectionException;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether EOF has been received.
|
||||||
|
*/
|
||||||
|
boolean isEOF();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the LoggerFactory associated with the SSH client.
|
||||||
|
*/
|
||||||
|
LoggerFactory getLoggerFactory();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,16 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.connection.channel;
|
package net.schmizz.sshj.connection.channel;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import net.schmizz.sshj.transport.Transport;
|
import net.schmizz.sshj.transport.Transport;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -38,7 +33,7 @@ public final class ChannelInputStream
|
|||||||
extends InputStream
|
extends InputStream
|
||||||
implements ErrorNotifiable {
|
implements ErrorNotifiable {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log;
|
||||||
|
|
||||||
private final Channel chan;
|
private final Channel chan;
|
||||||
private final Transport trans;
|
private final Transport trans;
|
||||||
@@ -51,6 +46,7 @@ public final class ChannelInputStream
|
|||||||
|
|
||||||
public ChannelInputStream(Channel chan, Transport trans, Window.Local win) {
|
public ChannelInputStream(Channel chan, Transport trans, Window.Local win) {
|
||||||
this.chan = chan;
|
this.chan = chan;
|
||||||
|
log = chan.getLoggerFactory().getLogger(getClass());
|
||||||
this.trans = trans;
|
this.trans = trans;
|
||||||
this.win = win;
|
this.win = win;
|
||||||
buf = new Buffer.PlainBuffer(chan.getLocalMaxPacketSize());
|
buf = new Buffer.PlainBuffer(chan.getLocalMaxPacketSize());
|
||||||
|
|||||||
@@ -15,11 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.connection.channel;
|
package net.schmizz.sshj.connection.channel;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import net.schmizz.sshj.transport.Transport;
|
import net.schmizz.sshj.transport.Transport;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
@@ -31,9 +27,7 @@ import java.io.OutputStream;
|
|||||||
* {@link OutputStream} for channels. Buffers data upto the remote window's maximum packet size. Data can also be
|
* {@link OutputStream} for channels. Buffers data upto the remote window's maximum packet size. Data can also be
|
||||||
* flushed via {@link #flush()} and is also flushed on {@link #close()}.
|
* flushed via {@link #flush()} and is also flushed on {@link #close()}.
|
||||||
*/
|
*/
|
||||||
public final class ChannelOutputStream
|
public final class ChannelOutputStream extends OutputStream implements ErrorNotifiable {
|
||||||
extends OutputStream
|
|
||||||
implements ErrorNotifiable {
|
|
||||||
|
|
||||||
private final Channel chan;
|
private final Channel chan;
|
||||||
private final Transport trans;
|
private final Transport trans;
|
||||||
@@ -60,8 +54,7 @@ public final class ChannelOutputStream
|
|||||||
dataOffset = packet.wpos();
|
dataOffset = packet.wpos();
|
||||||
}
|
}
|
||||||
|
|
||||||
int write(byte[] data, int off, int len)
|
int write(byte[] data, int off, int len) throws TransportException, ConnectionException {
|
||||||
throws TransportException, ConnectionException {
|
|
||||||
final int bufferSize = packet.wpos() - dataOffset;
|
final int bufferSize = packet.wpos() - dataOffset;
|
||||||
if (bufferSize >= win.getMaxPacketSize()) {
|
if (bufferSize >= win.getMaxPacketSize()) {
|
||||||
flush(bufferSize, true);
|
flush(bufferSize, true);
|
||||||
@@ -73,15 +66,13 @@ public final class ChannelOutputStream
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean flush(boolean canAwaitExpansion)
|
boolean flush(boolean canAwaitExpansion) throws TransportException, ConnectionException {
|
||||||
throws TransportException, ConnectionException {
|
|
||||||
return flush(packet.wpos() - dataOffset, canAwaitExpansion);
|
return flush(packet.wpos() - dataOffset, canAwaitExpansion);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean flush(int bufferSize, boolean canAwaitExpansion)
|
boolean flush(int bufferSize, boolean canAwaitExpansion) throws TransportException, ConnectionException {
|
||||||
throws TransportException, ConnectionException {
|
int dataLeft = bufferSize;
|
||||||
while (bufferSize > 0) {
|
while (dataLeft > 0) {
|
||||||
|
|
||||||
long remoteWindowSize = win.getSize();
|
long remoteWindowSize = win.getSize();
|
||||||
if (remoteWindowSize == 0) {
|
if (remoteWindowSize == 0) {
|
||||||
if (canAwaitExpansion) {
|
if (canAwaitExpansion) {
|
||||||
@@ -95,7 +86,7 @@ public final class ChannelOutputStream
|
|||||||
// a) how much data we have
|
// a) how much data we have
|
||||||
// b) the max packet size
|
// b) the max packet size
|
||||||
// c) what the current window size will allow
|
// c) what the current window size will allow
|
||||||
final int writeNow = Math.min(bufferSize, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
|
final int writeNow = Math.min(dataLeft, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
|
||||||
|
|
||||||
packet.wpos(headerOffset);
|
packet.wpos(headerOffset);
|
||||||
packet.putMessageID(Message.CHANNEL_DATA);
|
packet.putMessageID(Message.CHANNEL_DATA);
|
||||||
@@ -103,7 +94,7 @@ public final class ChannelOutputStream
|
|||||||
packet.putUInt32(writeNow);
|
packet.putUInt32(writeNow);
|
||||||
packet.wpos(dataOffset + writeNow);
|
packet.wpos(dataOffset + writeNow);
|
||||||
|
|
||||||
final int leftOverBytes = bufferSize - writeNow;
|
final int leftOverBytes = dataLeft - writeNow;
|
||||||
if (leftOverBytes > 0) {
|
if (leftOverBytes > 0) {
|
||||||
leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes);
|
leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes);
|
||||||
}
|
}
|
||||||
@@ -119,7 +110,7 @@ public final class ChannelOutputStream
|
|||||||
leftOvers.clear();
|
leftOvers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferSize = leftOverBytes;
|
dataLeft = leftOverBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -144,10 +135,12 @@ public final class ChannelOutputStream
|
|||||||
public synchronized void write(final byte[] data, int off, int len)
|
public synchronized void write(final byte[] data, int off, int len)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
checkClose();
|
checkClose();
|
||||||
while (len > 0) {
|
int length = len;
|
||||||
final int n = buffer.write(data, off, len);
|
int offset = off;
|
||||||
off += n;
|
while (length > 0) {
|
||||||
len -= n;
|
final int n = buffer.write(data, offset, length);
|
||||||
|
offset += n;
|
||||||
|
length -= n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,8 +179,7 @@ public final class ChannelOutputStream
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void flush()
|
public synchronized void flush() throws IOException {
|
||||||
throws IOException {
|
|
||||||
checkClose();
|
checkClose();
|
||||||
buffer.flush(true);
|
buffer.flush(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.connection.channel;
|
package net.schmizz.sshj.connection.channel;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
import net.schmizz.sshj.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public abstract class Window {
|
public abstract class Window {
|
||||||
|
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final Logger log;
|
||||||
|
|
||||||
protected final Object lock = new Object();
|
protected final Object lock = new Object();
|
||||||
|
|
||||||
@@ -30,9 +30,10 @@ public abstract class Window {
|
|||||||
|
|
||||||
protected long size;
|
protected long size;
|
||||||
|
|
||||||
public Window(long initialWinSize, int maxPacketSize) {
|
public Window(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
|
||||||
size = initialWinSize;
|
size = initialWinSize;
|
||||||
this.maxPacketSize = maxPacketSize;
|
this.maxPacketSize = maxPacketSize;
|
||||||
|
log = loggerFactory.getLogger(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expand(long inc) {
|
public void expand(long inc) {
|
||||||
@@ -72,8 +73,8 @@ public abstract class Window {
|
|||||||
public static final class Remote
|
public static final class Remote
|
||||||
extends Window {
|
extends Window {
|
||||||
|
|
||||||
public Remote(long initialWinSize, int maxPacketSize) {
|
public Remote(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
|
||||||
super(initialWinSize, maxPacketSize);
|
super(initialWinSize, maxPacketSize, loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long awaitExpansion(long was)
|
public long awaitExpansion(long was)
|
||||||
@@ -108,8 +109,8 @@ public abstract class Window {
|
|||||||
private final long initialSize;
|
private final long initialSize;
|
||||||
private final long threshold;
|
private final long threshold;
|
||||||
|
|
||||||
public Local(long initialWinSize, int maxPacketSize) {
|
public Local(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
|
||||||
super(initialWinSize, maxPacketSize);
|
super(initialWinSize, maxPacketSize, loggerFactory);
|
||||||
this.initialSize = initialWinSize;
|
this.initialSize = initialWinSize;
|
||||||
threshold = Math.min(maxPacketSize * 20, initialSize / 4);
|
threshold = Math.min(maxPacketSize * 20, initialSize / 4);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import net.schmizz.sshj.connection.channel.Channel;
|
|||||||
import net.schmizz.sshj.connection.channel.OpenFailException;
|
import net.schmizz.sshj.connection.channel.OpenFailException;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/** Base class for direct channels whose open is initated by the client. */
|
/** Base class for direct channels whose open is initated by the client. */
|
||||||
@@ -41,6 +42,15 @@ public abstract class AbstractDirectChannel
|
|||||||
conn.attach(this);
|
conn.attach(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AbstractDirectChannel(Connection conn, String type, Charset remoteCharset) {
|
||||||
|
super(conn, type, remoteCharset);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We expect to receive channel open confirmation/rejection and want to be able to next this packet.
|
||||||
|
*/
|
||||||
|
conn.attach(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open()
|
public void open()
|
||||||
throws ConnectionException, TransportException {
|
throws ConnectionException, TransportException {
|
||||||
@@ -94,4 +104,4 @@ public abstract class AbstractDirectChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ package net.schmizz.sshj.connection.channel.direct;
|
|||||||
|
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.common.IOUtils;
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHPacket;
|
import net.schmizz.sshj.common.SSHPacket;
|
||||||
import net.schmizz.sshj.common.StreamCopier;
|
import net.schmizz.sshj.common.StreamCopier;
|
||||||
import net.schmizz.sshj.connection.Connection;
|
import net.schmizz.sshj.connection.Connection;
|
||||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
||||||
@@ -82,10 +82,10 @@ public class LocalPortForwarder {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
socket.setSendBufferSize(getLocalMaxPacketSize());
|
socket.setSendBufferSize(getLocalMaxPacketSize());
|
||||||
socket.setReceiveBufferSize(getRemoteMaxPacketSize());
|
socket.setReceiveBufferSize(getRemoteMaxPacketSize());
|
||||||
final Event<IOException> soc2chan = new StreamCopier(socket.getInputStream(), getOutputStream())
|
final Event<IOException> soc2chan = new StreamCopier(socket.getInputStream(), getOutputStream(), loggerFactory)
|
||||||
.bufSize(getRemoteMaxPacketSize())
|
.bufSize(getRemoteMaxPacketSize())
|
||||||
.spawnDaemon("soc2chan");
|
.spawnDaemon("soc2chan");
|
||||||
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), socket.getOutputStream())
|
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), socket.getOutputStream(), loggerFactory)
|
||||||
.bufSize(getLocalMaxPacketSize())
|
.bufSize(getLocalMaxPacketSize())
|
||||||
.spawnDaemon("chan2soc");
|
.spawnDaemon("chan2soc");
|
||||||
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
|
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
|
||||||
@@ -102,16 +102,19 @@ 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 Connection conn;
|
||||||
private final Parameters parameters;
|
private final Parameters parameters;
|
||||||
private final ServerSocket serverSocket;
|
private final ServerSocket serverSocket;
|
||||||
|
private Thread runningThread;
|
||||||
|
|
||||||
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket) {
|
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket, LoggerFactory loggerFactory) {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
this.serverSocket = serverSocket;
|
this.serverSocket = serverSocket;
|
||||||
|
this.loggerFactory = loggerFactory;
|
||||||
|
this.log = loggerFactory.getLogger(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startChannel(Socket socket) throws IOException {
|
private void startChannel(Socket socket) throws IOException {
|
||||||
@@ -130,15 +133,48 @@ public class LocalPortForwarder {
|
|||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void listen()
|
public void listen() throws IOException {
|
||||||
throws IOException {
|
listen(Thread.currentThread());
|
||||||
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
|
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
|
||||||
final Socket socket = serverSocket.accept();
|
|
||||||
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
|
|
||||||
startChannel(socket);
|
|
||||||
}
|
|
||||||
log.debug("Interrupted!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Start listening for incoming connections and forward to remote host as a channel and ensure that the thread is registered.
|
||||||
|
* This is useful if for instance {@link #close() is called from another thread}
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void listen(Thread runningThread) throws IOException {
|
||||||
|
this.runningThread = runningThread;
|
||||||
|
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
|
||||||
|
while (!runningThread.isInterrupted()) {
|
||||||
|
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!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
runningThread.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,18 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.connection.channel.direct;
|
package net.schmizz.sshj.connection.channel.direct;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.connection.Connection;
|
import net.schmizz.sshj.connection.Connection;
|
||||||
import net.schmizz.sshj.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import net.schmizz.sshj.connection.channel.ChannelInputStream;
|
import net.schmizz.sshj.connection.channel.ChannelInputStream;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -52,6 +48,10 @@ public class SessionChannel
|
|||||||
super(conn, "session");
|
super(conn, "session");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SessionChannel(Connection conn, Charset remoteCharset) {
|
||||||
|
super(conn, "session", remoteCharset);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void allocateDefaultPTY()
|
public void allocateDefaultPTY()
|
||||||
throws ConnectionException, TransportException {
|
throws ConnectionException, TransportException {
|
||||||
@@ -98,7 +98,7 @@ public class SessionChannel
|
|||||||
throws ConnectionException, TransportException {
|
throws ConnectionException, TransportException {
|
||||||
checkReuse();
|
checkReuse();
|
||||||
log.debug("Will request to exec `{}`", command);
|
log.debug("Will request to exec `{}`", command);
|
||||||
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
|
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command, getRemoteCharset()))
|
||||||
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
|
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
|
||||||
usedUp = true;
|
usedUp = true;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -73,4 +73,4 @@ public abstract class AbstractForwardedChannel
|
|||||||
return origPort;
|
return origPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import net.schmizz.sshj.connection.channel.Channel;
|
|||||||
import net.schmizz.sshj.connection.channel.OpenFailException;
|
import net.schmizz.sshj.connection.channel.OpenFailException;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -29,14 +28,14 @@ import java.io.IOException;
|
|||||||
public abstract class AbstractForwardedChannelOpener
|
public abstract class AbstractForwardedChannelOpener
|
||||||
implements ForwardedChannelOpener {
|
implements ForwardedChannelOpener {
|
||||||
|
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final Logger log;
|
||||||
|
|
||||||
protected final String chanType;
|
protected final String chanType;
|
||||||
protected final Connection conn;
|
protected final Connection conn;
|
||||||
|
|
||||||
protected AbstractForwardedChannelOpener(String chanType, Connection conn) {
|
protected AbstractForwardedChannelOpener(String chanType, Connection conn) {
|
||||||
this.chanType = chanType;
|
this.chanType = chanType;
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
|
log = conn.getTransport().getConfig().getLoggerFactory().getLogger(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -72,4 +71,4 @@ public abstract class AbstractForwardedChannelOpener
|
|||||||
}.start();
|
}.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ public class RemotePortForwarder
|
|||||||
// Addresses match up
|
// Addresses match up
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ("localhost".equals(address) && (channelForward.address.equals("127.0.0.1") || channelForward.address.equals("::1"))) {
|
if ("localhost".equals(address) && ("127.0.0.1".equals(channelForward.address) || "::1".equals(channelForward.address))) {
|
||||||
// Localhost special case.
|
// Localhost special case.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ import net.schmizz.concurrent.Event;
|
|||||||
import net.schmizz.sshj.common.StreamCopier;
|
import net.schmizz.sshj.common.StreamCopier;
|
||||||
import net.schmizz.sshj.connection.channel.Channel;
|
import net.schmizz.sshj.connection.channel.Channel;
|
||||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
@@ -31,8 +29,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class SocketForwardingConnectListener
|
public class SocketForwardingConnectListener
|
||||||
implements ConnectListener {
|
implements ConnectListener {
|
||||||
|
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
|
||||||
|
|
||||||
protected final SocketAddress addr;
|
protected final SocketAddress addr;
|
||||||
|
|
||||||
/** Create with a {@link SocketAddress} this listener will forward to. */
|
/** Create with a {@link SocketAddress} this listener will forward to. */
|
||||||
@@ -44,7 +40,7 @@ public class SocketForwardingConnectListener
|
|||||||
@Override
|
@Override
|
||||||
public void gotConnect(Channel.Forwarded chan)
|
public void gotConnect(Channel.Forwarded chan)
|
||||||
throws IOException {
|
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();
|
final Socket sock = new Socket();
|
||||||
sock.setSendBufferSize(chan.getLocalMaxPacketSize());
|
sock.setSendBufferSize(chan.getLocalMaxPacketSize());
|
||||||
@@ -55,11 +51,11 @@ public class SocketForwardingConnectListener
|
|||||||
// ok so far -- could connect, let's confirm the channel
|
// ok so far -- could connect, let's confirm the channel
|
||||||
chan.confirm();
|
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())
|
.bufSize(chan.getRemoteMaxPacketSize())
|
||||||
.spawnDaemon("soc2chan");
|
.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())
|
.bufSize(chan.getLocalMaxPacketSize())
|
||||||
.spawnDaemon("chan2soc");
|
.spawnDaemon("chan2soc");
|
||||||
|
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ public final class FileAttributes {
|
|||||||
sb.append("size=").append(size).append(";");
|
sb.append("size=").append(size).append(";");
|
||||||
|
|
||||||
if (has(Flag.UIDGID))
|
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))
|
if (has(Flag.MODE))
|
||||||
sb.append("mode=").append(mode.toString()).append(";");
|
sb.append("mode=").append(mode.toString()).append(";");
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class FileMode {
|
|||||||
/** directory */
|
/** directory */
|
||||||
DIRECTORY(0040000),
|
DIRECTORY(0040000),
|
||||||
/** symbolic link */
|
/** symbolic link */
|
||||||
SYMKLINK(0120000),
|
SYMLINK(0120000),
|
||||||
/** unknown */
|
/** unknown */
|
||||||
UNKNOWN(0);
|
UNKNOWN(0);
|
||||||
|
|
||||||
|
|||||||
@@ -17,20 +17,19 @@ package net.schmizz.sshj.sftp;
|
|||||||
|
|
||||||
import net.schmizz.concurrent.Promise;
|
import net.schmizz.concurrent.Promise;
|
||||||
import net.schmizz.sshj.common.SSHException;
|
import net.schmizz.sshj.common.SSHException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class PacketReader
|
public class PacketReader extends Thread {
|
||||||
extends Thread {
|
|
||||||
|
|
||||||
/** Logger */
|
/**
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
* Logger
|
||||||
|
*/
|
||||||
|
private final Logger log;
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private final Map<Long, Promise<Response, SFTPException>> promises = new ConcurrentHashMap<Long, Promise<Response, SFTPException>>();
|
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) {
|
public PacketReader(SFTPEngine engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
log = engine.getLoggerFactory().getLogger(getClass());
|
||||||
this.in = engine.getSubsystem().getInputStream();
|
this.in = engine.getSubsystem().getInputStream();
|
||||||
setName("sftp reader");
|
setName("sftp reader");
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ public class PacketReader
|
|||||||
| lenBuf[3] & 0x000000ffL);
|
| lenBuf[3] & 0x000000ffL);
|
||||||
|
|
||||||
if (len > SFTPPacket.MAX_SIZE) {
|
if (len > SFTPPacket.MAX_SIZE) {
|
||||||
throw new SSHException(String.format("Indicated packet length %d too large", len));
|
throw new SSHException(String.format("Indicated packet length %d too large", len));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int) len;
|
return (int) len;
|
||||||
@@ -100,14 +100,14 @@ public class PacketReader
|
|||||||
log.debug("Received {} packet", resp.getType());
|
log.debug("Received {} packet", resp.getType());
|
||||||
if (promise == null)
|
if (promise == null)
|
||||||
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
|
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
|
||||||
+ ", no such request was made");
|
+ ", no such request was made");
|
||||||
else
|
else
|
||||||
promise.deliver(resp);
|
promise.deliver(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Promise<Response, SFTPException> expectResponseTo(long requestId) {
|
public Promise<Response, SFTPException> expectResponseTo(long requestId) {
|
||||||
final Promise<Response, SFTPException> promise
|
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);
|
promises.put(requestId, promise);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class PathHelper {
|
|||||||
if (path.equals(pathSep))
|
if (path.equals(pathSep))
|
||||||
return getComponents("", "");
|
return getComponents("", "");
|
||||||
|
|
||||||
if (path.isEmpty() || path.equals(".") || path.equals("." + pathSep))
|
if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path))
|
||||||
return getComponents(getDotDir());
|
return getComponents(getDotDir());
|
||||||
|
|
||||||
final String withoutTrailSep = trimTrailingSeparator(path);
|
final String withoutTrailSep = trimTrailingSeparator(path);
|
||||||
@@ -81,7 +81,7 @@ public class PathHelper {
|
|||||||
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep);
|
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep);
|
||||||
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length());
|
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length());
|
||||||
|
|
||||||
if (name.equals(".") || name.equals("..")) {
|
if (".".equals(name) || "..".equals(name)) {
|
||||||
return getComponents(canonicalizer.canonicalize(path));
|
return getComponents(canonicalizer.canonicalize(path));
|
||||||
} else {
|
} else {
|
||||||
return getComponents(parent, name);
|
return getComponents(parent, name);
|
||||||
|
|||||||
@@ -15,12 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.sftp;
|
package net.schmizz.sshj.sftp;
|
||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.*;
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutput;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class RandomAccessRemoteFile
|
public class RandomAccessRemoteFile
|
||||||
implements DataInput, DataOutput {
|
implements DataInput, DataOutput {
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class RemoteDirectory
|
public class RemoteDirectory
|
||||||
extends RemoteResource {
|
extends RemoteResource {
|
||||||
|
|
||||||
public RemoteDirectory(Requester requester, String path, byte[] handle) {
|
public RemoteDirectory(SFTPEngine requester, String path, byte[] handle) {
|
||||||
super(requester, path, handle);
|
super(requester, path, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RemoteResourceInfo> scan(RemoteResourceFilter filter)
|
public List<RemoteResourceInfo> scan(RemoteResourceFilter filter)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
|
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
|
||||||
|
// TODO: Remove GOTO!
|
||||||
loop:
|
loop:
|
||||||
for (; ; ) {
|
for (; ; ) {
|
||||||
final Response res = requester.request(newRequest(PacketType.READDIR))
|
final Response res = requester.request(newRequest(PacketType.READDIR))
|
||||||
@@ -41,13 +42,14 @@ public class RemoteDirectory
|
|||||||
case NAME:
|
case NAME:
|
||||||
final int count = res.readUInt32AsInt();
|
final int count = res.readUInt32AsInt();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
final String name = res.readString();
|
final String name = res.readString(requester.sub.getRemoteCharset());
|
||||||
res.readString(); // long name - IGNORED - shdve never been in the protocol
|
res.readString(); // long name - IGNORED - shdve never been in the protocol
|
||||||
final FileAttributes attrs = res.readFileAttributes();
|
final FileAttributes attrs = res.readFileAttributes();
|
||||||
final PathComponents comps = requester.getPathHelper().getComponents(path, name);
|
final PathComponents comps = requester.getPathHelper().getComponents(path, name);
|
||||||
final RemoteResourceInfo inf = new RemoteResourceInfo(comps, attrs);
|
final RemoteResourceInfo inf = new RemoteResourceInfo(comps, attrs);
|
||||||
if (!(name.equals(".") || name.equals("..")) && (filter == null || filter.accept(inf)))
|
if (!(".".equals(name) || "..".equals(name)) && (filter == null || filter.accept(inf))) {
|
||||||
rri.add(inf);
|
rri.add(inf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class RemoteFile
|
public class RemoteFile
|
||||||
extends RemoteResource {
|
extends RemoteResource {
|
||||||
|
|
||||||
public RemoteFile(Requester requester, String path, byte[] handle) {
|
public RemoteFile(SFTPEngine requester, String path, byte[] handle) {
|
||||||
super(requester, path, handle);
|
super(requester, path, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,10 +81,10 @@ public class RemoteFile
|
|||||||
protected Promise<Response, SFTPException> asyncWrite(long fileOffset, byte[] data, int off, int len)
|
protected Promise<Response, SFTPException> asyncWrite(long fileOffset, byte[] data, int off, int len)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return requester.request(newRequest(PacketType.WRITE)
|
return requester.request(newRequest(PacketType.WRITE)
|
||||||
.putUInt64(fileOffset)
|
.putUInt64(fileOffset)
|
||||||
// TODO The SFTP spec claims this field is unneeded...? See #187
|
// TODO The SFTP spec claims this field is unneeded...? See #187
|
||||||
.putUInt32(len)
|
.putUInt32(len)
|
||||||
.putRawBytes(data, off, len)
|
.putRawBytes(data, off, len)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,10 +194,10 @@ public class RemoteFile
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long skip(long n) throws IOException {
|
public long skip(long n) throws IOException {
|
||||||
final long fileLength = length();
|
final long fileLength = length();
|
||||||
final Long previousFileOffset = fileOffset;
|
final Long previousFileOffset = fileOffset;
|
||||||
fileOffset = Math.min(fileOffset + n, fileLength);
|
fileOffset = Math.min(fileOffset + n, fileLength);
|
||||||
return fileOffset - previousFileOffset;
|
return fileOffset - previousFileOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -341,7 +341,7 @@ public class RemoteFile
|
|||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
boolean lastRead = true;
|
boolean lastRead = true;
|
||||||
while (!eof && (pending.available() <= 0) && lastRead) {
|
while (!eof && (pending.available() <= 0) && lastRead) {
|
||||||
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
|
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
|
||||||
}
|
}
|
||||||
return pending.available();
|
return pending.available();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
package net.schmizz.sshj.sftp;
|
package net.schmizz.sshj.sftp;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -26,14 +25,15 @@ public abstract class RemoteResource
|
|||||||
implements Closeable {
|
implements Closeable {
|
||||||
|
|
||||||
/** Logger */
|
/** 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 String path;
|
||||||
protected final byte[] handle;
|
protected final byte[] handle;
|
||||||
|
|
||||||
protected RemoteResource(Requester requester, String path, byte[] handle) {
|
protected RemoteResource(SFTPEngine requester, String path, byte[] handle) {
|
||||||
this.requester = requester;
|
this.requester = requester;
|
||||||
|
log = requester.getLoggerFactory().getLogger(getClass());
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import net.schmizz.sshj.common.Buffer;
|
|||||||
public final class Response
|
public final class Response
|
||||||
extends SFTPPacket<Response> {
|
extends SFTPPacket<Response> {
|
||||||
|
|
||||||
public static enum StatusCode {
|
public enum StatusCode {
|
||||||
UNKNOWN(-1),
|
UNKNOWN(-1),
|
||||||
OK(0),
|
OK(0),
|
||||||
EOF(1),
|
EOF(1),
|
||||||
@@ -30,7 +30,30 @@ public final class Response
|
|||||||
BAD_MESSAGE(5),
|
BAD_MESSAGE(5),
|
||||||
NO_CONNECTION(6),
|
NO_CONNECTION(6),
|
||||||
CONNECITON_LOST(7),
|
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;
|
private final int code;
|
||||||
|
|
||||||
@@ -99,6 +122,7 @@ public final class Response
|
|||||||
return ensurePacketTypeIs(PacketType.STATUS).ensureStatusIs(StatusCode.OK);
|
return ensurePacketTypeIs(PacketType.STATUS).ensureStatusIs(StatusCode.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
||||||
public Response ensureStatusIs(StatusCode acceptable)
|
public Response ensureStatusIs(StatusCode acceptable)
|
||||||
throws SFTPException {
|
throws SFTPException {
|
||||||
final StatusCode sc = readStatusCode();
|
final StatusCode sc = readStatusCode();
|
||||||
|
|||||||
@@ -19,27 +19,23 @@ import net.schmizz.sshj.xfer.FilePermission;
|
|||||||
import net.schmizz.sshj.xfer.LocalDestFile;
|
import net.schmizz.sshj.xfer.LocalDestFile;
|
||||||
import net.schmizz.sshj.xfer.LocalSourceFile;
|
import net.schmizz.sshj.xfer.LocalSourceFile;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Deque;
|
import java.util.*;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class SFTPClient
|
public class SFTPClient
|
||||||
implements Closeable {
|
implements Closeable {
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final Logger log;
|
||||||
|
|
||||||
protected final SFTPEngine engine;
|
protected final SFTPEngine engine;
|
||||||
protected final SFTPFileTransfer xfer;
|
protected final SFTPFileTransfer xfer;
|
||||||
|
|
||||||
public SFTPClient(SFTPEngine engine) {
|
public SFTPClient(SFTPEngine engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
log = engine.getLoggerFactory().getLogger(getClass());
|
||||||
this.xfer = new SFTPFileTransfer(engine);
|
this.xfer = new SFTPFileTransfer(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +85,7 @@ public class SFTPClient
|
|||||||
|
|
||||||
public void mkdirs(String path)
|
public void mkdirs(String path)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final Deque<String> dirsToMake = new LinkedList<>();
|
final Deque<String> dirsToMake = new LinkedList<String>();
|
||||||
for (PathComponents current = engine.getPathHelper().getComponents(path); ;
|
for (PathComponents current = engine.getPathHelper().getComponents(path); ;
|
||||||
current = engine.getPathHelper().getComponents(current.getParent())) {
|
current = engine.getPathHelper().getComponents(current.getParent())) {
|
||||||
final FileAttributes attrs = statExistence(current.getPath());
|
final FileAttributes attrs = statExistence(current.getPath());
|
||||||
|
|||||||
@@ -16,15 +16,17 @@
|
|||||||
package net.schmizz.sshj.sftp;
|
package net.schmizz.sshj.sftp;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Promise;
|
import net.schmizz.concurrent.Promise;
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHException;
|
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 net.schmizz.sshj.connection.channel.direct.SessionFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -38,13 +40,14 @@ public class SFTPEngine
|
|||||||
public static final int DEFAULT_TIMEOUT_MS = 30 * 1000; // way too long, but it was the original default
|
public static final int DEFAULT_TIMEOUT_MS = 30 * 1000; // way too long, but it was the original default
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
protected final LoggerFactory loggerFactory;
|
||||||
|
protected final Logger log;
|
||||||
|
|
||||||
protected volatile int timeoutMs = DEFAULT_TIMEOUT_MS;
|
protected volatile int timeoutMs = DEFAULT_TIMEOUT_MS;
|
||||||
|
|
||||||
protected final PathHelper pathHelper;
|
protected final PathHelper pathHelper;
|
||||||
|
|
||||||
protected final Subsystem sub;
|
protected final Session.Subsystem sub;
|
||||||
protected final PacketReader reader;
|
protected final PacketReader reader;
|
||||||
protected final OutputStream out;
|
protected final OutputStream out;
|
||||||
|
|
||||||
@@ -59,7 +62,10 @@ public class SFTPEngine
|
|||||||
|
|
||||||
public SFTPEngine(SessionFactory ssh, String pathSep)
|
public SFTPEngine(SessionFactory ssh, String pathSep)
|
||||||
throws SSHException {
|
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();
|
out = sub.getOutputStream();
|
||||||
reader = new PacketReader(this);
|
reader = new PacketReader(this);
|
||||||
pathHelper = new PathHelper(new PathHelper.Canonicalizer() {
|
pathHelper = new PathHelper(new PathHelper.Canonicalizer() {
|
||||||
@@ -94,7 +100,7 @@ public class SFTPEngine
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Subsystem getSubsystem() {
|
public Session.Subsystem getSubsystem() {
|
||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +139,7 @@ public class SFTPEngine
|
|||||||
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
|
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final byte[] handle = doRequest(
|
final byte[] handle = doRequest(
|
||||||
newRequest(PacketType.OPEN).putString(path).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
|
newRequest(PacketType.OPEN).putString(path, sub.getRemoteCharset()).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
|
||||||
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
|
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
|
||||||
return new RemoteFile(this, path, handle);
|
return new RemoteFile(this, path, handle);
|
||||||
}
|
}
|
||||||
@@ -151,7 +157,7 @@ public class SFTPEngine
|
|||||||
public RemoteDirectory openDir(String path)
|
public RemoteDirectory openDir(String path)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final byte[] handle = doRequest(
|
final byte[] handle = doRequest(
|
||||||
newRequest(PacketType.OPENDIR).putString(path)
|
newRequest(PacketType.OPENDIR).putString(path, sub.getRemoteCharset())
|
||||||
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
|
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
|
||||||
return new RemoteDirectory(this, path, handle);
|
return new RemoteDirectory(this, path, handle);
|
||||||
}
|
}
|
||||||
@@ -159,7 +165,7 @@ public class SFTPEngine
|
|||||||
public void setAttributes(String path, FileAttributes attrs)
|
public void setAttributes(String path, FileAttributes attrs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
doRequest(
|
doRequest(
|
||||||
newRequest(PacketType.SETSTAT).putString(path).putFileAttributes(attrs)
|
newRequest(PacketType.SETSTAT).putString(path, sub.getRemoteCharset()).putFileAttributes(attrs)
|
||||||
).ensureStatusPacketIsOK();
|
).ensureStatusPacketIsOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,13 +175,13 @@ public class SFTPEngine
|
|||||||
throw new SFTPException("READLINK is not supported in SFTPv" + operativeVersion);
|
throw new SFTPException("READLINK is not supported in SFTPv" + operativeVersion);
|
||||||
return readSingleName(
|
return readSingleName(
|
||||||
doRequest(
|
doRequest(
|
||||||
newRequest(PacketType.READLINK).putString(path)
|
newRequest(PacketType.READLINK).putString(path, sub.getRemoteCharset())
|
||||||
));
|
), sub.getRemoteCharset());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeDir(String path, FileAttributes attrs)
|
public void makeDir(String path, FileAttributes attrs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
doRequest(newRequest(PacketType.MKDIR).putString(path).putFileAttributes(attrs)).ensureStatusPacketIsOK();
|
doRequest(newRequest(PacketType.MKDIR).putString(path, sub.getRemoteCharset()).putFileAttributes(attrs)).ensureStatusPacketIsOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeDir(String path)
|
public void makeDir(String path)
|
||||||
@@ -188,21 +194,21 @@ public class SFTPEngine
|
|||||||
if (operativeVersion < 3)
|
if (operativeVersion < 3)
|
||||||
throw new SFTPException("SYMLINK is not supported in SFTPv" + operativeVersion);
|
throw new SFTPException("SYMLINK is not supported in SFTPv" + operativeVersion);
|
||||||
doRequest(
|
doRequest(
|
||||||
newRequest(PacketType.SYMLINK).putString(linkpath).putString(targetpath)
|
newRequest(PacketType.SYMLINK).putString(linkpath, sub.getRemoteCharset()).putString(targetpath, sub.getRemoteCharset())
|
||||||
).ensureStatusPacketIsOK();
|
).ensureStatusPacketIsOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(String filename)
|
public void remove(String filename)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
doRequest(
|
doRequest(
|
||||||
newRequest(PacketType.REMOVE).putString(filename)
|
newRequest(PacketType.REMOVE).putString(filename, sub.getRemoteCharset())
|
||||||
).ensureStatusPacketIsOK();
|
).ensureStatusPacketIsOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDir(String path)
|
public void removeDir(String path)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
doRequest(
|
doRequest(
|
||||||
newRequest(PacketType.RMDIR).putString(path)
|
newRequest(PacketType.RMDIR).putString(path, sub.getRemoteCharset())
|
||||||
).ensureStatusIs(Response.StatusCode.OK);
|
).ensureStatusIs(Response.StatusCode.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +227,7 @@ public class SFTPEngine
|
|||||||
if (operativeVersion < 1)
|
if (operativeVersion < 1)
|
||||||
throw new SFTPException("RENAME is not supported in SFTPv" + operativeVersion);
|
throw new SFTPException("RENAME is not supported in SFTPv" + operativeVersion);
|
||||||
doRequest(
|
doRequest(
|
||||||
newRequest(PacketType.RENAME).putString(oldPath).putString(newPath)
|
newRequest(PacketType.RENAME).putString(oldPath, sub.getRemoteCharset()).putString(newPath, sub.getRemoteCharset())
|
||||||
).ensureStatusPacketIsOK();
|
).ensureStatusPacketIsOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,8 +235,8 @@ public class SFTPEngine
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
return readSingleName(
|
return readSingleName(
|
||||||
doRequest(
|
doRequest(
|
||||||
newRequest(PacketType.REALPATH).putString(path)
|
newRequest(PacketType.REALPATH).putString(path, sub.getRemoteCharset())
|
||||||
));
|
), sub.getRemoteCharset());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeoutMs(int timeoutMs) {
|
public void setTimeoutMs(int timeoutMs) {
|
||||||
@@ -248,22 +254,38 @@ public class SFTPEngine
|
|||||||
reader.interrupt();
|
reader.interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected LoggerFactory getLoggerFactory() {
|
||||||
|
return loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
protected FileAttributes stat(PacketType pt, String path)
|
protected FileAttributes stat(PacketType pt, String path)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return doRequest(newRequest(pt).putString(path))
|
return doRequest(newRequest(pt).putString(path, sub.getRemoteCharset()))
|
||||||
.ensurePacketTypeIs(PacketType.ATTRS)
|
.ensurePacketTypeIs(PacketType.ATTRS)
|
||||||
.readFileAttributes();
|
.readFileAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static String readSingleName(Response res)
|
private static byte[] readSingleNameAsBytes(Response res)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
res.ensurePacketTypeIs(PacketType.NAME);
|
res.ensurePacketTypeIs(PacketType.NAME);
|
||||||
if (res.readUInt32AsInt() == 1)
|
if (res.readUInt32AsInt() == 1)
|
||||||
return res.readString();
|
return res.readStringAsBytes();
|
||||||
else
|
else
|
||||||
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
|
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Using UTF-8 */
|
||||||
|
protected static String readSingleName(Response res)
|
||||||
|
throws IOException {
|
||||||
|
return readSingleName(res, IOUtils.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Using any character set */
|
||||||
|
protected static String readSingleName(Response res, Charset charset)
|
||||||
|
throws IOException {
|
||||||
|
return new String(readSingleNameAsBytes(res), charset);
|
||||||
|
}
|
||||||
|
|
||||||
protected synchronized void transmit(SFTPPacket<Request> payload)
|
protected synchronized void transmit(SFTPPacket<Request> payload)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final int len = payload.available();
|
final int len = payload.available();
|
||||||
|
|||||||
@@ -17,19 +17,12 @@ package net.schmizz.sshj.sftp;
|
|||||||
|
|
||||||
import net.schmizz.sshj.common.StreamCopier;
|
import net.schmizz.sshj.common.StreamCopier;
|
||||||
import net.schmizz.sshj.sftp.Response.StatusCode;
|
import net.schmizz.sshj.sftp.Response.StatusCode;
|
||||||
import net.schmizz.sshj.xfer.AbstractFileTransfer;
|
import net.schmizz.sshj.xfer.*;
|
||||||
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 java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SFTPFileTransfer
|
public class SFTPFileTransfer
|
||||||
extends AbstractFileTransfer
|
extends AbstractFileTransfer
|
||||||
@@ -42,6 +35,7 @@ public class SFTPFileTransfer
|
|||||||
private volatile boolean preserveAttributes = true;
|
private volatile boolean preserveAttributes = true;
|
||||||
|
|
||||||
public SFTPFileTransfer(SFTPEngine engine) {
|
public SFTPFileTransfer(SFTPEngine engine) {
|
||||||
|
super(engine.getLoggerFactory());
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,14 +60,12 @@ public class SFTPFileTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void upload(LocalSourceFile localFile, String remotePath)
|
public void upload(LocalSourceFile localFile, String remotePath) throws IOException {
|
||||||
throws IOException {
|
|
||||||
new Uploader(localFile, remotePath).upload(getTransferListener());
|
new Uploader(localFile, remotePath).upload(getTransferListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void download(String source, LocalDestFile dest)
|
public void download(String source, LocalDestFile dest) throws IOException {
|
||||||
throws IOException {
|
|
||||||
final PathComponents pathComponents = engine.getPathHelper().getComponents(source);
|
final PathComponents pathComponents = engine.getPathHelper().getComponents(source);
|
||||||
final FileAttributes attributes = engine.stat(source);
|
final FileAttributes attributes = engine.stat(source);
|
||||||
new Downloader().download(getTransferListener(), new RemoteResourceInfo(pathComponents, attributes), dest);
|
new Downloader().download(getTransferListener(), new RemoteResourceInfo(pathComponents, attributes), dest);
|
||||||
@@ -97,10 +89,10 @@ public class SFTPFileTransfer
|
|||||||
|
|
||||||
private class Downloader {
|
private class Downloader {
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.MissingBreakInSwitch")
|
||||||
private void download(final TransferListener listener,
|
private void download(final TransferListener listener,
|
||||||
final RemoteResourceInfo remote,
|
final RemoteResourceInfo remote,
|
||||||
final LocalDestFile local)
|
final LocalDestFile local) throws IOException {
|
||||||
throws IOException {
|
|
||||||
final LocalDestFile adjustedFile;
|
final LocalDestFile adjustedFile;
|
||||||
switch (remote.getAttributes().getType()) {
|
switch (remote.getAttributes().getType()) {
|
||||||
case DIRECTORY:
|
case DIRECTORY:
|
||||||
@@ -110,8 +102,7 @@ public class SFTPFileTransfer
|
|||||||
log.warn("Server did not supply information about the type of file at `{}` " +
|
log.warn("Server did not supply information about the type of file at `{}` " +
|
||||||
"-- assuming it is a regular file!", remote.getPath());
|
"-- assuming it is a regular file!", remote.getPath());
|
||||||
case REGULAR:
|
case REGULAR:
|
||||||
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()),
|
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()), remote, local);
|
||||||
remote, local);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IOException(remote + " is not a regular file or directory");
|
throw new IOException(remote + " is not a regular file or directory");
|
||||||
@@ -145,7 +136,7 @@ public class SFTPFileTransfer
|
|||||||
final RemoteFile.ReadAheadRemoteFileInputStream rfis = rf.new ReadAheadRemoteFileInputStream(16);
|
final RemoteFile.ReadAheadRemoteFileInputStream rfis = rf.new ReadAheadRemoteFileInputStream(16);
|
||||||
final OutputStream os = adjusted.getOutputStream();
|
final OutputStream os = adjusted.getOutputStream();
|
||||||
try {
|
try {
|
||||||
new StreamCopier(rfis, os)
|
new StreamCopier(rfis, os, engine.getLoggerFactory())
|
||||||
.bufSize(engine.getSubsystem().getLocalMaxPacketSize())
|
.bufSize(engine.getSubsystem().getLocalMaxPacketSize())
|
||||||
.keepFlushing(false)
|
.keepFlushing(false)
|
||||||
.listener(listener)
|
.listener(listener)
|
||||||
@@ -235,14 +226,36 @@ public class SFTPFileTransfer
|
|||||||
final String remote)
|
final String remote)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final String adjusted = prepareFile(local, remote);
|
final String adjusted = prepareFile(local, remote);
|
||||||
try (RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC))) {
|
RemoteFile rf = null;
|
||||||
try (InputStream fis = local.getInputStream();
|
InputStream fis = null;
|
||||||
RemoteFile.RemoteFileOutputStream rfos = rf.new RemoteFileOutputStream(0, 16)) {
|
RemoteFile.RemoteFileOutputStream rfos = null;
|
||||||
new StreamCopier(fis, rfos)
|
try {
|
||||||
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
|
rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC));
|
||||||
.keepFlushing(false)
|
fis = local.getInputStream();
|
||||||
.listener(listener)
|
rfos = rf.new RemoteFileOutputStream(0, 16);
|
||||||
.copy();
|
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;
|
return adjusted;
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public abstract class AbstractSignature
|
|||||||
| sig[i++] & 0x000000ff;
|
| sig[i++] & 0x000000ff;
|
||||||
byte[] newSig = new byte[j];
|
byte[] newSig = new byte[j];
|
||||||
System.arraycopy(sig, i, newSig, 0, j);
|
System.arraycopy(sig, i, newSig, 0, j);
|
||||||
sig = newSig;
|
return newSig;
|
||||||
}
|
}
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.signature;
|
package net.schmizz.sshj.signature;
|
||||||
|
|
||||||
import java.security.SignatureException;
|
|
||||||
|
|
||||||
import net.schmizz.sshj.common.KeyType;
|
import net.schmizz.sshj.common.KeyType;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
|
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
/** DSA {@link Signature} */
|
/** DSA {@link Signature} */
|
||||||
public class SignatureDSA
|
public class SignatureDSA
|
||||||
extends AbstractSignature {
|
extends AbstractSignature {
|
||||||
|
|||||||
@@ -15,13 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.signature;
|
package net.schmizz.sshj.signature;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.Buffer;
|
||||||
import net.schmizz.sshj.common.KeyType;
|
import net.schmizz.sshj.common.KeyType;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
/** ECDSA {@link Signature} */
|
/** ECDSA {@link Signature} */
|
||||||
public class SignatureECDSA
|
public class SignatureECDSA
|
||||||
extends AbstractSignature {
|
extends AbstractSignature {
|
||||||
@@ -79,7 +79,7 @@ public class SignatureECDSA
|
|||||||
throw new SSHRuntimeException(String.format("Signature :: ecdsa-sha2-nistp256 expected, got %s", algo));
|
throw new SSHRuntimeException(String.format("Signature :: ecdsa-sha2-nistp256 expected, got %s", algo));
|
||||||
}
|
}
|
||||||
final int rsLen = sigbuf.readUInt32AsInt();
|
final int rsLen = sigbuf.readUInt32AsInt();
|
||||||
if (!(sigbuf.available() == rsLen)) {
|
if (sigbuf.available() != rsLen) {
|
||||||
throw new SSHRuntimeException("Invalid key length");
|
throw new SSHRuntimeException("Invalid key length");
|
||||||
}
|
}
|
||||||
r = sigbuf.readBytes();
|
r = sigbuf.readBytes();
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.signature;
|
package net.schmizz.sshj.signature;
|
||||||
|
|
||||||
import java.security.SignatureException;
|
|
||||||
|
|
||||||
import net.schmizz.sshj.common.KeyType;
|
import net.schmizz.sshj.common.KeyType;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
|
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
/** RSA {@link Signature} */
|
/** RSA {@link Signature} */
|
||||||
public class SignatureRSA
|
public class SignatureRSA
|
||||||
extends AbstractSignature {
|
extends AbstractSignature {
|
||||||
|
|||||||
@@ -15,17 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.transport.cipher.Cipher;
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
import net.schmizz.sshj.transport.compression.Compression;
|
import net.schmizz.sshj.transport.compression.Compression;
|
||||||
import net.schmizz.sshj.transport.mac.MAC;
|
import net.schmizz.sshj.transport.mac.MAC;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/** Decodes packets from the SSH binary protocol per the current algorithms. */
|
/** Decodes packets from the SSH binary protocol per the current algorithms. */
|
||||||
final class Decoder
|
final class Decoder
|
||||||
@@ -33,7 +27,7 @@ final class Decoder
|
|||||||
|
|
||||||
private static final int MAX_PACKET_LEN = 256 * 1024;
|
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 */
|
/** What we pass decoded packets to */
|
||||||
private final SSHPacketHandler packetHandler;
|
private final SSHPacketHandler packetHandler;
|
||||||
@@ -53,8 +47,9 @@ final class Decoder
|
|||||||
*/
|
*/
|
||||||
private int needed = 8;
|
private int needed = 8;
|
||||||
|
|
||||||
Decoder(SSHPacketHandler packetHandler) {
|
Decoder(Transport packetHandler) {
|
||||||
this.packetHandler = packetHandler;
|
this.packetHandler = packetHandler;
|
||||||
|
log = packetHandler.getConfig().getLoggerFactory().getLogger(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,4 +188,4 @@ final class Decoder
|
|||||||
return MAX_PACKET_LEN;
|
return MAX_PACKET_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHPacket;
|
import net.schmizz.sshj.common.SSHPacket;
|
||||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
import net.schmizz.sshj.transport.compression.Compression;
|
import net.schmizz.sshj.transport.compression.Compression;
|
||||||
import net.schmizz.sshj.transport.mac.MAC;
|
import net.schmizz.sshj.transport.mac.MAC;
|
||||||
import net.schmizz.sshj.transport.random.Random;
|
import net.schmizz.sshj.transport.random.Random;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
@@ -29,28 +29,14 @@ import java.util.concurrent.locks.Lock;
|
|||||||
final class Encoder
|
final class Encoder
|
||||||
extends Converter {
|
extends Converter {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log;
|
||||||
|
|
||||||
private final Random prng;
|
private final Random prng;
|
||||||
|
|
||||||
private final Lock encodeLock;
|
private final Lock encodeLock;
|
||||||
|
|
||||||
Encoder(Random prng, Lock encodeLock) {
|
Encoder(Random prng, Lock encodeLock, LoggerFactory loggerFactory) {
|
||||||
this.prng = prng;
|
this.prng = prng;
|
||||||
this.encodeLock = encodeLock;
|
this.encodeLock = encodeLock;
|
||||||
}
|
log = loggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
private SSHPacket checkHeaderSpace(SSHPacket buffer) {
|
|
||||||
if (buffer.rpos() < 5) {
|
|
||||||
log.warn("Performance cost: when sending a packet, ensure that "
|
|
||||||
+ "5 bytes are available in front of the buffer");
|
|
||||||
SSHPacket nb = new SSHPacket(buffer.available() + 5);
|
|
||||||
nb.rpos(5);
|
|
||||||
nb.wpos(5);
|
|
||||||
nb.putBuffer(buffer);
|
|
||||||
buffer = nb;
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compress(SSHPacket buffer) {
|
private void compress(SSHPacket buffer) {
|
||||||
@@ -142,4 +128,4 @@ final class Encoder
|
|||||||
return Compression.Mode.DEFLATE;
|
return Compression.Mode.DEFLATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,26 +17,15 @@ package net.schmizz.sshj.transport;
|
|||||||
|
|
||||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.transport.cipher.Cipher;
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
import net.schmizz.sshj.transport.compression.Compression;
|
import net.schmizz.sshj.transport.compression.Compression;
|
||||||
import net.schmizz.sshj.transport.digest.Digest;
|
import net.schmizz.sshj.transport.digest.Digest;
|
||||||
import net.schmizz.sshj.transport.kex.KeyExchange;
|
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||||
import net.schmizz.sshj.transport.mac.MAC;
|
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.AlgorithmsVerifier;
|
||||||
|
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
@@ -60,8 +49,7 @@ final class KeyExchanger
|
|||||||
NEWKEYS,
|
NEWKEYS,
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log;
|
||||||
|
|
||||||
private final TransportImpl transport;
|
private final TransportImpl transport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,18 +74,20 @@ final class KeyExchanger
|
|||||||
private Proposal clientProposal;
|
private Proposal clientProposal;
|
||||||
private NegotiatedAlgorithms negotiatedAlgs;
|
private NegotiatedAlgorithms negotiatedAlgs;
|
||||||
|
|
||||||
private final Event<TransportException> kexInitSent =
|
private final Event<TransportException> kexInitSent;
|
||||||
new Event<TransportException>("kexinit sent", TransportException.chainer);
|
|
||||||
|
|
||||||
private final Event<TransportException> done;
|
private final Event<TransportException> done;
|
||||||
|
|
||||||
KeyExchanger(TransportImpl trans) {
|
KeyExchanger(TransportImpl trans) {
|
||||||
this.transport = 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
|
* Use TransportImpl's writeLock, since TransportImpl.write() may wait on this event and the lock should
|
||||||
* be released while waiting.
|
* 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,6 +157,7 @@ final class KeyExchanger
|
|||||||
"Key exchange packet received when key exchange was not ongoing");
|
"Key exchange packet received when key exchange was not ongoing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
||||||
private static void ensureReceivedMatchesExpected(Message got, Message expected)
|
private static void ensureReceivedMatchesExpected(Message got, Message expected)
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
if (got != expected)
|
if (got != expected)
|
||||||
@@ -395,4 +386,4 @@ final class KeyExchanger
|
|||||||
ErrorDeliveryUtil.alertEvents(error, kexInitSent, done);
|
ErrorDeliveryUtil.alertEvents(error, kexInitSent, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
@@ -24,12 +23,12 @@ import java.net.SocketTimeoutException;
|
|||||||
public final class Reader
|
public final class Reader
|
||||||
extends Thread {
|
extends Thread {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log;
|
||||||
|
|
||||||
private final TransportImpl trans;
|
private final TransportImpl trans;
|
||||||
|
|
||||||
public Reader(TransportImpl trans) {
|
public Reader(TransportImpl trans) {
|
||||||
this.trans = trans;
|
this.trans = trans;
|
||||||
|
log = trans.getConfig().getLoggerFactory().getLogger(getClass());
|
||||||
setName("reader");
|
setName("reader");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,4 +235,4 @@ public interface Transport
|
|||||||
* @param e The exception that occurred.
|
* @param e The exception that occurred.
|
||||||
*/
|
*/
|
||||||
void die(Exception e);
|
void die(Exception e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,22 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.transport.IdentificationStringParser;
|
||||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.AbstractService;
|
import net.schmizz.sshj.AbstractService;
|
||||||
import net.schmizz.sshj.Config;
|
import net.schmizz.sshj.Config;
|
||||||
import net.schmizz.sshj.SSHClient;
|
import net.schmizz.sshj.SSHClient;
|
||||||
import net.schmizz.sshj.Service;
|
import net.schmizz.sshj.Service;
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.transport.verification.AlgorithmsVerifier;
|
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
||||||
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -38,9 +33,11 @@ import java.io.OutputStream;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/** A thread-safe {@link Transport} implementation. */
|
/**
|
||||||
|
* A thread-safe {@link Transport} implementation.
|
||||||
|
*/
|
||||||
public final class TransportImpl
|
public final class TransportImpl
|
||||||
implements Transport {
|
implements Transport, DisconnectListener {
|
||||||
|
|
||||||
private static final class NullService
|
private static final class NullService
|
||||||
extends AbstractService {
|
extends AbstractService {
|
||||||
@@ -50,14 +47,15 @@ public final class TransportImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class ConnInfo {
|
static final class ConnInfo {
|
||||||
|
|
||||||
final String host;
|
final String host;
|
||||||
|
|
||||||
final int port;
|
final int port;
|
||||||
final InputStream in;
|
final InputStream in;
|
||||||
final OutputStream out;
|
final OutputStream out;
|
||||||
public ConnInfo(String host, int port, InputStream in, OutputStream out) {
|
|
||||||
|
ConnInfo(String host, int port, InputStream in, OutputStream out) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.in = in;
|
this.in = in;
|
||||||
@@ -65,16 +63,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() {
|
private final Logger log;
|
||||||
@Override
|
|
||||||
public void notifyDisconnect(DisconnectReason reason, String message) {
|
private final Service nullService;
|
||||||
log.info("Disconnected - {}", reason);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Config config;
|
private final Config config;
|
||||||
|
|
||||||
@@ -92,36 +86,51 @@ public final class TransportImpl
|
|||||||
|
|
||||||
private final Decoder decoder;
|
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 */
|
/**
|
||||||
|
* Client version identification string
|
||||||
|
*/
|
||||||
private final String clientID;
|
private final String clientID;
|
||||||
|
|
||||||
private volatile int timeoutMs = 30 * 1000; // Crazy long, but it was the original default
|
private volatile int timeoutMs = 30 * 1000; // Crazy long, but it was the original default
|
||||||
|
|
||||||
private volatile boolean authed = false;
|
private volatile boolean authed = false;
|
||||||
|
|
||||||
/** Currently active service e.g. UserAuthService, ConnectionService */
|
/**
|
||||||
private volatile Service service = nullService;
|
* Currently active service e.g. UserAuthService, ConnectionService
|
||||||
|
*/
|
||||||
|
private volatile Service service;
|
||||||
|
|
||||||
private DisconnectListener disconnectListener = nullDisconnectListener;
|
private DisconnectListener disconnectListener;
|
||||||
|
|
||||||
private ConnInfo connInfo;
|
private ConnInfo connInfo;
|
||||||
|
|
||||||
/** Server version identification string */
|
/**
|
||||||
|
* Server version identification string
|
||||||
|
*/
|
||||||
private String serverID;
|
private String serverID;
|
||||||
|
|
||||||
/** Message identifier of last packet received */
|
/**
|
||||||
|
* Message identifier of last packet received
|
||||||
|
*/
|
||||||
private Message msg;
|
private Message msg;
|
||||||
|
|
||||||
private final ReentrantLock writeLock = new ReentrantLock();
|
private final ReentrantLock writeLock = new ReentrantLock();
|
||||||
|
|
||||||
public TransportImpl(Config config) {
|
public TransportImpl(Config config) {
|
||||||
this.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.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.decoder = new Decoder(this);
|
||||||
this.kexer = new KeyExchanger(this);
|
this.kexer = new KeyExchanger(this);
|
||||||
this.clientID = String.format("SSH-2.0-%s", config.getVersion());
|
this.clientID = String.format("SSH-2.0-%s", config.getVersion());
|
||||||
@@ -135,16 +144,21 @@ public final class TransportImpl
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public TransportImpl(Config config, SSHClient sshClient) {
|
public TransportImpl(Config config, SSHClient sshClient) {
|
||||||
this.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.log = loggerFactory.getLogger(getClass());
|
||||||
|
this.nullService = new NullService(this);
|
||||||
|
this.service = nullService;
|
||||||
|
this.disconnectListener = this;
|
||||||
this.reader = new Reader(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.decoder = new Decoder(this);
|
||||||
this.kexer = new KeyExchanger(this);
|
this.kexer = new KeyExchanger(this);
|
||||||
this.clientID = String.format("SSH-2.0-%s", config.getVersion());
|
this.clientID = String.format("SSH-2.0-%s", config.getVersion());
|
||||||
this.sshClient = sshClient;
|
this.sshClient = sshClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(String remoteHost, int remotePort, InputStream in, OutputStream out)
|
public void init(String remoteHost, int remotePort, InputStream in, OutputStream out)
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
@@ -170,18 +184,30 @@ public final class TransportImpl
|
|||||||
reader.start();
|
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 {
|
private void receiveServerIdent() throws IOException {
|
||||||
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||||
while ((serverID = readIdentification(buf)).isEmpty()) {
|
while ((serverID = readIdentification(buf)).isEmpty()) {
|
||||||
int b = connInfo.in.read();
|
int b = connInfo.in.read();
|
||||||
if (b == -1)
|
if (b == -1) {
|
||||||
|
log.error("Received end of connection, but no identification received. ");
|
||||||
throw new TransportException("Server closed connection during identification exchange");
|
throw new TransportException("Server closed connection during identification exchange");
|
||||||
|
|
||||||
|
}
|
||||||
buf.putByte((byte) b);
|
buf.putByte((byte) b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive the server identification string.
|
* Receive the server identification string.
|
||||||
|
*
|
||||||
* @throws IOException If there was an error writing to the outputstream.
|
* @throws IOException If there was an error writing to the outputstream.
|
||||||
*/
|
*/
|
||||||
private void sendClientIdent() throws IOException {
|
private void sendClientIdent() throws IOException {
|
||||||
@@ -200,49 +226,19 @@ public final class TransportImpl
|
|||||||
* This is not efficient but is only done once.
|
* This is not efficient but is only done once.
|
||||||
*
|
*
|
||||||
* @param buffer The buffer to read from.
|
* @param buffer The buffer to read from.
|
||||||
*
|
|
||||||
* @return empty string if full ident string has not yet been received
|
* @return empty string if full ident string has not yet been received
|
||||||
*
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private String readIdentification(Buffer.PlainBuffer buffer)
|
private String readIdentification(Buffer.PlainBuffer buffer)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String ident;
|
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
|
||||||
|
if (ident.isEmpty()) {
|
||||||
byte[] data = new byte[256];
|
return ident;
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
|
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
|
||||||
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
|
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
|
||||||
"Server does not support SSHv2, identified as: " + ident);
|
"Server does not support SSHv2, identified as: " + ident);
|
||||||
|
|
||||||
return ident;
|
return ident;
|
||||||
}
|
}
|
||||||
@@ -353,7 +349,6 @@ public final class TransportImpl
|
|||||||
* Sends a service request for the specified service
|
* Sends a service request for the specified service
|
||||||
*
|
*
|
||||||
* @param serviceName name of the service being requested
|
* @param serviceName name of the service being requested
|
||||||
*
|
|
||||||
* @throws TransportException if there is an error while sending the request
|
* @throws TransportException if there is an error while sending the request
|
||||||
*/
|
*/
|
||||||
private void sendServiceRequest(String serviceName)
|
private void sendServiceRequest(String serviceName)
|
||||||
@@ -427,7 +422,7 @@ public final class TransportImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDisconnectListener(DisconnectListener listener) {
|
public void setDisconnectListener(DisconnectListener listener) {
|
||||||
this.disconnectListener = listener == null ? nullDisconnectListener : listener;
|
this.disconnectListener = listener == null ? this : listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -472,9 +467,9 @@ public final class TransportImpl
|
|||||||
log.debug("Sending SSH_MSG_DISCONNECT: reason=[{}], msg=[{}]", reason, message);
|
log.debug("Sending SSH_MSG_DISCONNECT: reason=[{}], msg=[{}]", reason, message);
|
||||||
try {
|
try {
|
||||||
write(new SSHPacket(Message.DISCONNECT)
|
write(new SSHPacket(Message.DISCONNECT)
|
||||||
.putUInt32(reason.toInt())
|
.putUInt32(reason.toInt())
|
||||||
.putString(message)
|
.putString(message)
|
||||||
.putString(""));
|
.putString(""));
|
||||||
} catch (IOException worthless) {
|
} catch (IOException worthless) {
|
||||||
log.debug("Error writing packet: {}", worthless.toString());
|
log.debug("Error writing packet: {}", worthless.toString());
|
||||||
}
|
}
|
||||||
@@ -492,7 +487,6 @@ public final class TransportImpl
|
|||||||
*
|
*
|
||||||
* @param msg the message identifer
|
* @param msg the message identifer
|
||||||
* @param buf buffer containg rest of the packet
|
* @param buf buffer containg rest of the packet
|
||||||
*
|
|
||||||
* @throws SSHException if an error occurs during handling (unrecoverable)
|
* @throws SSHException if an error occurs during handling (unrecoverable)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -510,32 +504,27 @@ public final class TransportImpl
|
|||||||
|
|
||||||
else
|
else
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case DISCONNECT: {
|
case DISCONNECT:
|
||||||
gotDisconnect(buf);
|
gotDisconnect(buf);
|
||||||
break;
|
break;
|
||||||
}
|
case IGNORE:
|
||||||
case IGNORE: {
|
|
||||||
log.debug("Received SSH_MSG_IGNORE");
|
log.debug("Received SSH_MSG_IGNORE");
|
||||||
break;
|
break;
|
||||||
}
|
case UNIMPLEMENTED:
|
||||||
case UNIMPLEMENTED: {
|
|
||||||
gotUnimplemented(buf);
|
gotUnimplemented(buf);
|
||||||
break;
|
break;
|
||||||
}
|
case DEBUG:
|
||||||
case DEBUG: {
|
|
||||||
gotDebug(buf);
|
gotDebug(buf);
|
||||||
break;
|
break;
|
||||||
}
|
case SERVICE_ACCEPT:
|
||||||
case SERVICE_ACCEPT: {
|
|
||||||
gotServiceAccept();
|
gotServiceAccept();
|
||||||
break;
|
break;
|
||||||
}
|
case USERAUTH_BANNER:
|
||||||
case USERAUTH_BANNER: {
|
|
||||||
log.debug("Received USERAUTH_BANNER");
|
log.debug("Received USERAUTH_BANNER");
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
sendUnimplemented();
|
sendUnimplemented();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,7 +557,7 @@ public final class TransportImpl
|
|||||||
try {
|
try {
|
||||||
if (!serviceAccept.hasWaiters())
|
if (!serviceAccept.hasWaiters())
|
||||||
throw new TransportException(DisconnectReason.PROTOCOL_ERROR,
|
throw new TransportException(DisconnectReason.PROTOCOL_ERROR,
|
||||||
"Got a service accept notification when none was awaited");
|
"Got a service accept notification when none was awaited");
|
||||||
serviceAccept.set();
|
serviceAccept.set();
|
||||||
} finally {
|
} finally {
|
||||||
serviceAccept.unlock();
|
serviceAccept.unlock();
|
||||||
@@ -579,7 +568,6 @@ public final class TransportImpl
|
|||||||
* Got an SSH_MSG_UNIMPLEMENTED, so lets see where we're at and act accordingly.
|
* Got an SSH_MSG_UNIMPLEMENTED, so lets see where we're at and act accordingly.
|
||||||
*
|
*
|
||||||
* @param packet The 'unimplemented' packet received
|
* @param packet The 'unimplemented' packet received
|
||||||
*
|
|
||||||
* @throws TransportException
|
* @throws TransportException
|
||||||
*/
|
*/
|
||||||
private void gotUnimplemented(SSHPacket packet)
|
private void gotUnimplemented(SSHPacket packet)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import net.schmizz.sshj.common.SSHRuntimeException;
|
|||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
|
|
||||||
import javax.crypto.ShortBufferException;
|
import javax.crypto.ShortBufferException;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
|||||||
@@ -46,10 +46,12 @@ public class NoneCipher
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Mode mode, byte[] bytes, byte[] bytes1) {
|
public void init(Mode mode, byte[] bytes, byte[] bytes1) {
|
||||||
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(byte[] input, int inputOffset, int inputLen) {
|
public void update(byte[] input, int inputOffset, int inputLen) {
|
||||||
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,22 +16,26 @@
|
|||||||
package net.schmizz.sshj.transport.digest;
|
package net.schmizz.sshj.transport.digest;
|
||||||
|
|
||||||
public class SHA384 extends BaseDigest {
|
public class SHA384 extends BaseDigest {
|
||||||
/** Named factory for SHA384 digest */
|
/**
|
||||||
public static class Factory
|
* Named factory for SHA384 digest
|
||||||
implements net.schmizz.sshj.common.Factory.Named<Digest> {
|
*/
|
||||||
|
public static class Factory
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<Digest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Digest create() {
|
public Digest create() {
|
||||||
return new SHA384();
|
return new SHA384();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "sha384";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String getName() {
|
* Create a new instance of a SHA384 digest
|
||||||
return "sha384";
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create a new instance of a SHA384 digest */
|
|
||||||
public SHA384() {
|
public SHA384() {
|
||||||
super("SHA-384", 48);
|
super("SHA-384", 48);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,22 +16,26 @@
|
|||||||
package net.schmizz.sshj.transport.digest;
|
package net.schmizz.sshj.transport.digest;
|
||||||
|
|
||||||
public class SHA512 extends BaseDigest {
|
public class SHA512 extends BaseDigest {
|
||||||
/** Named factory for SHA384 digest */
|
/**
|
||||||
public static class Factory
|
* Named factory for SHA384 digest
|
||||||
implements net.schmizz.sshj.common.Factory.Named<Digest> {
|
*/
|
||||||
|
public static class Factory
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<Digest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Digest create() {
|
public Digest create() {
|
||||||
return new SHA512();
|
return new SHA512();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "sha512";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String getName() {
|
* Create a new instance of a SHA384 digest
|
||||||
return "sha512";
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create a new instance of a SHA384 digest */
|
|
||||||
public SHA512() {
|
public SHA512() {
|
||||||
super("SHA-512", 64);
|
super("SHA-512", 64);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ package net.schmizz.sshj.transport.kex;
|
|||||||
import net.schmizz.sshj.transport.digest.Digest;
|
import net.schmizz.sshj.transport.digest.Digest;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public abstract class AbstractDH extends KeyExchangeBase {
|
public abstract class AbstractDH extends KeyExchangeBase {
|
||||||
protected final DHBase dh;
|
protected final DHBase dh;
|
||||||
|
|||||||
@@ -15,24 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.*;
|
||||||
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.signature.Signature;
|
import net.schmizz.sshj.signature.Signature;
|
||||||
import net.schmizz.sshj.transport.Transport;
|
import net.schmizz.sshj.transport.Transport;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
import net.schmizz.sshj.transport.digest.Digest;
|
import net.schmizz.sshj.transport.digest.Digest;
|
||||||
import net.schmizz.sshj.transport.digest.SHA1;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.security.GeneralSecurityException;
|
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
|
* Base class for DHG key exchange algorithms. Implementations will only have to configure the required data on the
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public abstract class AbstractDHGex extends AbstractDH {
|
|||||||
throw new GeneralSecurityException("Server generated gex p is out of range (" + bitLength + " bits)");
|
throw new GeneralSecurityException("Server generated gex p is out of range (" + bitLength + " bits)");
|
||||||
}
|
}
|
||||||
log.debug("Received server p bitlength {}", bitLength);
|
log.debug("Received server p bitlength {}", bitLength);
|
||||||
dh.init(new DHParameterSpec(p, g));
|
dh.init(new DHParameterSpec(p, g), trans.getConfig().getRandomFactory());
|
||||||
log.debug("Sending {}", Message.KEX_DH_GEX_INIT);
|
log.debug("Sending {}", Message.KEX_DH_GEX_INIT);
|
||||||
trans.write(new SSHPacket(Message.KEX_DH_GEX_INIT).putBytes(dh.getE()));
|
trans.write(new SSHPacket(Message.KEX_DH_GEX_INIT).putBytes(dh.getE()));
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -15,25 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import java.math.BigInteger;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
|
import java.util.Arrays;
|
||||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
import org.bouncycastle.crypto.ec.CustomNamedCurves;
|
import org.bouncycastle.crypto.ec.CustomNamedCurves;
|
||||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
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 net.schmizz.sshj.common.Factory;
|
||||||
import java.security.*;
|
import net.schmizz.sshj.transport.random.Random;
|
||||||
import java.security.interfaces.ECPublicKey;
|
|
||||||
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 {
|
public class Curve25519DH extends DHBase {
|
||||||
|
|
||||||
|
|
||||||
private byte[] secretKey;
|
private byte[] secretKey;
|
||||||
|
|
||||||
public Curve25519DH() {
|
public Curve25519DH() {
|
||||||
@@ -48,10 +42,10 @@ public class Curve25519DH extends DHBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
|
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
|
||||||
SecureRandom secureRandom = new SecureRandom();
|
Random random = randomFactory.create();
|
||||||
byte[] secretBytes = new byte[32];
|
byte[] secretBytes = new byte[32];
|
||||||
secureRandom.nextBytes(secretBytes);
|
random.fill(secretBytes);
|
||||||
byte[] publicBytes = new byte[32];
|
byte[] publicBytes = new byte[32];
|
||||||
djb.Curve25519.keygen(publicBytes, null, secretBytes);
|
djb.Curve25519.keygen(publicBytes, null, secretBytes);
|
||||||
this.secretKey = Arrays.copyOf(secretBytes, secretBytes.length);
|
this.secretKey = Arrays.copyOf(secretBytes, secretBytes.length);
|
||||||
|
|||||||
@@ -15,18 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
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 net.schmizz.sshj.transport.digest.SHA256;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
public class Curve25519SHA256 extends AbstractDHG {
|
public class Curve25519SHA256 extends AbstractDHG {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Curve25519SHA256.class);
|
|
||||||
|
|
||||||
/** Named factory for Curve25519SHA256 key exchange */
|
/** Named factory for Curve25519SHA256 key exchange */
|
||||||
public static class Factory
|
public static class Factory
|
||||||
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||||
@@ -48,6 +41,6 @@ public class Curve25519SHA256 extends AbstractDHG {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
||||||
dh.init(Curve25519DH.getCurve25519Params());
|
dh.init(Curve25519DH.getCurve25519Params(), trans.getConfig().getRandomFactory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
|
import net.schmizz.sshj.transport.random.Random;
|
||||||
|
|
||||||
import javax.crypto.spec.DHParameterSpec;
|
import javax.crypto.spec.DHParameterSpec;
|
||||||
import javax.crypto.spec.DHPublicKeySpec;
|
import javax.crypto.spec.DHPublicKeySpec;
|
||||||
@@ -38,7 +40,7 @@ public class DH extends DHBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
|
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
|
||||||
if (!(params instanceof DHParameterSpec)) {
|
if (!(params instanceof DHParameterSpec)) {
|
||||||
throw new SSHRuntimeException("Wrong algorithm parameters for Diffie Hellman");
|
throw new SSHRuntimeException("Wrong algorithm parameters for Diffie Hellman");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
|
import net.schmizz.sshj.transport.random.Random;
|
||||||
|
|
||||||
import javax.crypto.KeyAgreement;
|
import javax.crypto.KeyAgreement;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
@@ -24,7 +26,7 @@ import java.security.GeneralSecurityException;
|
|||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
|
|
||||||
abstract class DHBase {
|
public abstract class DHBase {
|
||||||
protected final KeyPairGenerator generator;
|
protected final KeyPairGenerator generator;
|
||||||
protected final KeyAgreement agreement;
|
protected final KeyAgreement agreement;
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ abstract class DHBase {
|
|||||||
|
|
||||||
abstract void computeK(byte[] f) throws GeneralSecurityException;
|
abstract void computeK(byte[] f) throws GeneralSecurityException;
|
||||||
|
|
||||||
protected abstract void init(AlgorithmParameterSpec params) throws GeneralSecurityException;
|
public abstract void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException;
|
||||||
|
|
||||||
void setE(byte[] e) {
|
void setE(byte[] e) {
|
||||||
this.e = e;
|
this.e = e;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import java.security.GeneralSecurityException;
|
|||||||
* @see <a href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</a>
|
* @see <a href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</a>
|
||||||
*
|
*
|
||||||
* TODO refactor away the (unneeded) class
|
* TODO refactor away the (unneeded) class
|
||||||
|
* @deprecated Replaced by {@link com.hierynomus.sshj.transport.kex.DHG} with {@link com.hierynomus.sshj.transport.kex.DHGroups}
|
||||||
*/
|
*/
|
||||||
public class DHG1
|
public class DHG1
|
||||||
extends AbstractDHG {
|
extends AbstractDHG {
|
||||||
@@ -51,6 +52,6 @@ public class DHG1
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
||||||
dh.init(new DHParameterSpec(DHGroupData.P1, DHGroupData.G));
|
dh.init(new DHParameterSpec(DHGroupData.P1, DHGroupData.G), trans.getConfig().getRandomFactory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import java.security.GeneralSecurityException;
|
|||||||
* <p/>
|
* <p/>
|
||||||
* DHG14 does not work with the default JCE implementation provided by Sun because it does not support 2048 bits
|
* DHG14 does not work with the default JCE implementation provided by Sun because it does not support 2048 bits
|
||||||
* encryption. It requires BouncyCastle to be used.
|
* encryption. It requires BouncyCastle to be used.
|
||||||
|
*
|
||||||
|
* @deprecated Replaced by {@link com.hierynomus.sshj.transport.kex.DHG} with {@link com.hierynomus.sshj.transport.kex.DHGroups}
|
||||||
*/
|
*/
|
||||||
public class DHG14
|
public class DHG14
|
||||||
extends AbstractDHG {
|
extends AbstractDHG {
|
||||||
@@ -51,6 +53,6 @@ public class DHG14
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
||||||
dh.init(new DHParameterSpec(DHGroupData.P14, DHGroupData.G));
|
dh.init(new DHParameterSpec(DHGroupData.P14, DHGroupData.G), trans.getConfig().getRandomFactory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,26 +17,139 @@ package net.schmizz.sshj.transport.kex;
|
|||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
/** Simple class holding the data for DH group key exchanges. */
|
/**
|
||||||
|
* Simple class holding the data for DH group key exchanges.
|
||||||
|
*/
|
||||||
public final class DHGroupData {
|
public final class DHGroupData {
|
||||||
|
|
||||||
public static final BigInteger G =
|
public static final BigInteger G =
|
||||||
new BigInteger("2");
|
new BigInteger("2");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First Oakley Group (https://tools.ietf.org/html/rfc2409) - P1
|
||||||
|
* prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
|
||||||
|
*/
|
||||||
public static final BigInteger P1 =
|
public static final BigInteger P1 =
|
||||||
new BigInteger("1797693134862315907708391567937874531978602960487560117064444236841971802161585193" +
|
new BigInteger("1797693134862315907708391567937874531978602960487560117064444236841971802161585193" +
|
||||||
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
|
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
|
||||||
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
|
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
|
||||||
"727629299387668709205606050270810842907692932019128194467627007");
|
"727629299387668709205606050270810842907692932019128194467627007");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2048-bit MODP Group - P14 (https://tools.ietf.org/html/rfc3526#section-3)
|
||||||
|
* prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
|
||||||
|
*/
|
||||||
public static final BigInteger P14 =
|
public static final BigInteger P14 =
|
||||||
new BigInteger("3231700607131100730033891392642382824881794124114023911284200975140074170663435422" +
|
new BigInteger("3231700607131100730033891392642382824881794124114023911284200975140074170663435422" +
|
||||||
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
|
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
|
||||||
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
|
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
|
||||||
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
|
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
|
||||||
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
|
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
|
||||||
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
|
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
|
||||||
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
|
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
|
||||||
"9448652468262416972035911852507045361090559");
|
"9448652468262416972035911852507045361090559");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3072-bit MODP Group - P15 (https://tools.ietf.org/html/rfc3526#section-4)
|
||||||
|
* prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
|
||||||
|
*/
|
||||||
|
public static final BigInteger P15 =
|
||||||
|
new BigInteger("5809605995369958062791915965639201402176612226902900533702900882779736177890990861" +
|
||||||
|
"4720947744773395811473734101856463783280437298007504700982109244878669350591643715" +
|
||||||
|
"8816804754094398164451663275506750162643455639819318662899007124866081936120511979" +
|
||||||
|
"3693985433297036118232914410171876807536457391277857011849897410207519105333355801" +
|
||||||
|
"1211093568974594262718454713979526759594407934930716283941227805101246184882326024" +
|
||||||
|
"6464987685045886124578424092925842628769970531258450962541951346360515542801716571" +
|
||||||
|
"4465363094021609290561084025893662561222573202082865797821865270991145082200656978" +
|
||||||
|
"1771928270245389902399691755461907706456858934380117144304264093386763147435711545" +
|
||||||
|
"3714203157300427642870143303638180170530865983075119035294602548205993130657100472" +
|
||||||
|
"7362479688415574702596946457770284148435989129632853918392117997472632693078113129" +
|
||||||
|
"8864873993477969827727846158652326212896569442842168246113187097645351525073541163" +
|
||||||
|
"44703769998514148343807");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4096-bit MODP Group - P16 (https://tools.ietf.org/html/rfc3526#section-5)
|
||||||
|
* prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
|
||||||
|
*/
|
||||||
|
public static final BigInteger P16 =
|
||||||
|
new BigInteger("10443888814131525066796027198465295458312690609921350090225887564443381720223226907" +
|
||||||
|
"10444046669809783930111585737890362691860127079270495454517218673016928427459146001" +
|
||||||
|
"86688577976298222932119236830334623520436805101030915567415569746034717694639407653" +
|
||||||
|
"51572849948952848216337009218117167389724518349794558970103063334685907513583651387" +
|
||||||
|
"82250372269117968985194322444535687415522007151638638141456178420621277822674995027" +
|
||||||
|
"99027867345862954439173691976629900551150544617766815444623488266596168079657690319" +
|
||||||
|
"91160893476349471877789065280080047566925716669229641225661745827767073324523710012" +
|
||||||
|
"72163776841229318324903125740713574141005124561965913888899753461735347970011693256" +
|
||||||
|
"31675166067895083002751025580484610558346505544661509044430958305077580850929704003" +
|
||||||
|
"96800574353422539265662408981958636315888889363641299200593084556694540340103914782" +
|
||||||
|
"38784189888594672336242763795138176353222845524644040094258962433613354036104643881" +
|
||||||
|
"92523848922401019419308891166616558422942466816544168892779046060826486420423771700" +
|
||||||
|
"20547443379889419746612146996897065215430062626045358909981257522759426087721743761" +
|
||||||
|
"07314217749233048217904944409836238235772306749874396760463376480215133461333478395" +
|
||||||
|
"682746608242585133953883882226786118030184028136755970045385534758453247");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 6144-bit MODP Group - P17 (https://tools.ietf.org/html/rfc3526#section-6)
|
||||||
|
* prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
|
||||||
|
*/
|
||||||
|
public static final BigInteger P17 =
|
||||||
|
new BigInteger("3375152182143856118451852315996741233006489780574184654817389047442942990132667244" +
|
||||||
|
"5203235101919165483964194359460994881062089387893762814044257438204432573941083014" +
|
||||||
|
"8270060902589258751610180963277323358005958319159760142088223040073278481327349332" +
|
||||||
|
"9788580321367526156496260334045722077682632250005809131096725397661997398803366366" +
|
||||||
|
"6385188155212656268079501726223369693427999804134467810120772356498596945532366527" +
|
||||||
|
"4005175754719693358549052745041195095923660137119541482588848792245999152034563158" +
|
||||||
|
"8103477655308367699571833559858639559116999957082451503501754353335269752528775333" +
|
||||||
|
"2500527176569576894926734950469293596134095086603716860086302051544539652689091299" +
|
||||||
|
"0997845889190523834630577894405654606814419024423999564190605216296046973478790246" +
|
||||||
|
"5431380018607831652696452928806274087901103517592005919217856147319900620589671943" +
|
||||||
|
"5014765345518490882366607110905303449152556221163232127426440691921134648766635695" +
|
||||||
|
"8502392313045917442156109850296368954067188807663082492273159842675422662594896843" +
|
||||||
|
"7222391644541101590050623941926790971632033120898897818086898743162371034761799235" +
|
||||||
|
"6201449023892203230133009421463914291201346063125219636964261683591541014344239275" +
|
||||||
|
"3407356909977322220697587739633908763605465157552805170421605254873028981223116697" +
|
||||||
|
"9967944753045360039934269703271445854959128593945394903498124811432232236723864504" +
|
||||||
|
"2515984447890788917823576330019151696568654314153058547592091366014550143819685170" +
|
||||||
|
"0683437001046776090411663697600809334136054989623820777788455998349074759534307874" +
|
||||||
|
"4620138456732853067527579296235488377080690082718368571835346957473168052062194454" +
|
||||||
|
"0947734619035177180057973022652571032196598229259194875709994709721793154158686515" +
|
||||||
|
"7485072742241813169487971046010682120152329216914824963468544136987197501906011027" +
|
||||||
|
"0527448105054323981513068607360107630451228454921845984604608225359676243382741906" +
|
||||||
|
"0089029417044871218316020923109988915707117567");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 8192-bit MODP Group - P18 (https://tools.ietf.org/html/rfc3526#section-7)
|
||||||
|
* prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
|
||||||
|
*/
|
||||||
|
public static final BigInteger P18 =
|
||||||
|
new BigInteger("10907481356194159294502949293597845003481551249531722117741011069661501689227856390" +
|
||||||
|
"28532473848836817769712164169076432969224698752674677662739994265785437233596157045" +
|
||||||
|
"97092233804069810050786103304731233182398243527947570019986097161273254052879655450" +
|
||||||
|
"28679197467769837593914759871425213158787195775191488118308799194269399584870875409" +
|
||||||
|
"65716419167467499326156226529675209172277001377591248147563782880558861083327174154" +
|
||||||
|
"01497513489312511601577631889029596069801161415772128252753946881651931933333750311" +
|
||||||
|
"47771923604122817210189558343776154804684792527488673203623853555966017951228067562" +
|
||||||
|
"17713579819870634321561907813255153703950795271232652404894983869492174481652303803" +
|
||||||
|
"49888136621050864726366837651413103110233683748899977574404673365182723939535354034" +
|
||||||
|
"84148728546397192946943234501868841898225445406472269872921606931847346549419069366" +
|
||||||
|
"46576130260972193280317171696418971553954161446191759093719524951116705577362073481" +
|
||||||
|
"31929604120128351615426904438925772770028968411946028348045230620413002491387998113" +
|
||||||
|
"59080269838682059693181678196808509986496944169079527129049624049377757896989172073" +
|
||||||
|
"56355227455066183815847669135530549755439819480321732925869069136146085326382334628" +
|
||||||
|
"74545639807160305805163420938670870330654590319960852382451372962513665912822110096" +
|
||||||
|
"77354505199524042481982628138310973742616503800172779169753241348465746813073370173" +
|
||||||
|
"80830353680623216336949471306191686438249305686413380231046096450953594089375540285" +
|
||||||
|
"03729247092939511402830554745258496207430943815182543790297601289174935519867842060" +
|
||||||
|
"37220349003113648930464957614043339386861400378480309162925432736845336400326376391" +
|
||||||
|
"00774502371542479302473698388692892420946478947733800387782741417786484770190108867" +
|
||||||
|
"87977899163321862864053398261932246615488301145229189025233648723608665439609385389" +
|
||||||
|
"86288058131775591620763631544364944775078712941198416378677017221666098312018454840" +
|
||||||
|
"78070518041336869808398454625586921201308185638888082699408686536045192649569198110" +
|
||||||
|
"35365994311180230063610650986502394366182943642656300791728205089442938884174888539" +
|
||||||
|
"82907077430529736053592775157496197308237732158947551217614678878653277071155738042" +
|
||||||
|
"64519206349215850195195364813387526811742474131549802130246506341207020335797706780" +
|
||||||
|
"70540694527543880626597851620970679570257924407538049023174103086261496878330620786" +
|
||||||
|
"96878681084236399719832090776247580804999882755913927872676271824428928096468742282" +
|
||||||
|
"63172435642368588260139161962836121481966092745325488641054238839295138992979335446" +
|
||||||
|
"110090325230955276870524611359124918392740353154294858383359");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
|
import net.schmizz.sshj.transport.random.Random;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
@@ -23,7 +25,10 @@ import java.security.KeyFactory;
|
|||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.interfaces.ECPublicKey;
|
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.getDecoded;
|
||||||
import static com.hierynomus.sshj.secg.SecgUtils.getEncoded;
|
import static com.hierynomus.sshj.secg.SecgUtils.getEncoded;
|
||||||
@@ -36,7 +41,7 @@ public class ECDH extends DHBase {
|
|||||||
super("EC", "ECDH");
|
super("EC", "ECDH");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init(AlgorithmParameterSpec params) throws GeneralSecurityException {
|
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
|
||||||
generator.initialize(params);
|
generator.initialize(params);
|
||||||
KeyPair keyPair = generator.generateKeyPair();
|
KeyPair keyPair = generator.generateKeyPair();
|
||||||
agreement.init(keyPair.getPrivate());
|
agreement.init(keyPair.getPrivate());
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class ECDHNistP extends AbstractDHG {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
||||||
dh.init(new ECNamedCurveGenParameterSpec(curve));
|
dh.init(new ECNamedCurveGenParameterSpec(curve), trans.getConfig().getRandomFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ package net.schmizz.sshj.transport.random;
|
|||||||
|
|
||||||
import org.bouncycastle.crypto.prng.RandomGenerator;
|
import org.bouncycastle.crypto.prng.RandomGenerator;
|
||||||
import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
|
import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
@@ -27,6 +29,8 @@ import java.security.SecureRandom;
|
|||||||
public class BouncyCastleRandom
|
public class BouncyCastleRandom
|
||||||
implements Random {
|
implements Random {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(BouncyCastleRandom.class);
|
||||||
|
|
||||||
/** Named factory for the BouncyCastle <code>Random</code> */
|
/** Named factory for the BouncyCastle <code>Random</code> */
|
||||||
public static class Factory
|
public static class Factory
|
||||||
implements net.schmizz.sshj.common.Factory<Random> {
|
implements net.schmizz.sshj.common.Factory<Random> {
|
||||||
@@ -41,7 +45,10 @@ public class BouncyCastleRandom
|
|||||||
private final RandomGenerator random = new VMPCRandomGenerator();
|
private final RandomGenerator random = new VMPCRandomGenerator();
|
||||||
|
|
||||||
public BouncyCastleRandom() {
|
public BouncyCastleRandom() {
|
||||||
|
logger.info("Generating random seed from SecureRandom.");
|
||||||
|
long t = System.currentTimeMillis();
|
||||||
byte[] seed = new SecureRandom().generateSeed(8);
|
byte[] seed = new SecureRandom().generateSeed(8);
|
||||||
|
logger.debug("Creating random seed took {} ms", System.currentTimeMillis() - t);
|
||||||
random.addSeedMaterial(seed);
|
random.addSeedMaterial(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,4 +57,9 @@ public class BouncyCastleRandom
|
|||||||
random.nextBytes(bytes, start, len);
|
random.nextBytes(bytes, start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill(byte[] bytes) {
|
||||||
|
random.nextBytes(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,13 @@
|
|||||||
package net.schmizz.sshj.transport.random;
|
package net.schmizz.sshj.transport.random;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/** A {@link Random} implementation using the built-in {@link SecureRandom} PRNG. */
|
/** A {@link Random} implementation using the built-in {@link SecureRandom} PRNG. */
|
||||||
public class JCERandom
|
public class JCERandom
|
||||||
implements Random {
|
implements Random {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(JCERandom.class);
|
||||||
|
|
||||||
/** Named factory for the JCE {@link Random} */
|
/** Named factory for the JCE {@link Random} */
|
||||||
public static class Factory
|
public static class Factory
|
||||||
@@ -38,7 +41,14 @@ public class JCERandom
|
|||||||
}
|
}
|
||||||
|
|
||||||
private byte[] tmp = new byte[16];
|
private byte[] tmp = new byte[16];
|
||||||
private final SecureRandom random = new SecureRandom();
|
private final SecureRandom random;
|
||||||
|
|
||||||
|
JCERandom() {
|
||||||
|
logger.info("Creating new SecureRandom.");
|
||||||
|
long t = System.currentTimeMillis();
|
||||||
|
random = new SecureRandom();
|
||||||
|
logger.debug("Random creation took {} ms", System.currentTimeMillis() - t);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill the given byte-array with random bytes from this PRNG.
|
* Fill the given byte-array with random bytes from this PRNG.
|
||||||
@@ -49,15 +59,20 @@ public class JCERandom
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void fill(byte[] foo, int start, int len) {
|
public synchronized void fill(byte[] foo, int start, int len) {
|
||||||
if (start == 0 && len == foo.length)
|
if (start == 0 && len == foo.length) {
|
||||||
random.nextBytes(foo);
|
random.nextBytes(foo);
|
||||||
else
|
} else {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (len > tmp.length)
|
if (len > tmp.length)
|
||||||
tmp = new byte[len];
|
tmp = new byte[len];
|
||||||
random.nextBytes(tmp);
|
random.nextBytes(tmp);
|
||||||
System.arraycopy(tmp, 0, foo, start, len);
|
System.arraycopy(tmp, 0, foo, start, len);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill(final byte[] bytes) {
|
||||||
|
random.nextBytes(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ package net.schmizz.sshj.transport.random;
|
|||||||
/** A pseudo random number generator. */
|
/** A pseudo random number generator. */
|
||||||
public interface Random {
|
public interface Random {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill the array of bytes with random values.
|
||||||
|
*
|
||||||
|
* @param bytes byte array to be filled.
|
||||||
|
*/
|
||||||
|
void fill(byte[] bytes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill part of bytes with random values.
|
* Fill part of bytes with random values.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -37,4 +37,8 @@ public class SingletonRandomFactory
|
|||||||
random.fill(bytes, start, len);
|
random.fill(bytes, start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill(final byte[] bytes) {
|
||||||
|
random.fill(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,25 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.verification;
|
package net.schmizz.sshj.transport.verification;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Base64;
|
import net.schmizz.sshj.common.*;
|
||||||
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.transport.mac.HMACSHA1;
|
import net.schmizz.sshj.transport.mac.HMACSHA1;
|
||||||
import net.schmizz.sshj.transport.mac.MAC;
|
import net.schmizz.sshj.transport.mac.MAC;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.*;
|
||||||
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.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
@@ -50,29 +37,38 @@ import java.util.List;
|
|||||||
public class OpenSSHKnownHosts
|
public class OpenSSHKnownHosts
|
||||||
implements HostKeyVerifier {
|
implements HostKeyVerifier {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(OpenSSHKnownHosts.class);
|
protected final Logger log;
|
||||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
|
||||||
|
|
||||||
protected final File khFile;
|
protected final File khFile;
|
||||||
protected final List<HostEntry> entries = new ArrayList<HostEntry>();
|
protected final List<HostEntry> entries = new ArrayList<HostEntry>();
|
||||||
|
|
||||||
public OpenSSHKnownHosts(File khFile)
|
public OpenSSHKnownHosts(File khFile)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
this(khFile, LoggerFactory.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenSSHKnownHosts(File khFile, LoggerFactory loggerFactory)
|
||||||
|
throws IOException {
|
||||||
this.khFile = khFile;
|
this.khFile = khFile;
|
||||||
|
log = loggerFactory.getLogger(getClass());
|
||||||
if (khFile.exists()) {
|
if (khFile.exists()) {
|
||||||
|
final EntryFactory entryFactory = new EntryFactory();
|
||||||
final BufferedReader br = new BufferedReader(new FileReader(khFile));
|
final BufferedReader br = new BufferedReader(new FileReader(khFile));
|
||||||
try {
|
try {
|
||||||
// Read in the file, storing each line as an entry
|
// Read in the file, storing each line as an entry
|
||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null)
|
while ((line = br.readLine()) != null) {
|
||||||
try {
|
try {
|
||||||
HostEntry entry = EntryFactory.parseEntry(line);
|
HostEntry entry = entryFactory.parseEntry(line);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
entries.add(entry);
|
entries.add(entry);
|
||||||
}
|
}
|
||||||
} catch (SSHException ignore) {
|
} catch (SSHException ignore) {
|
||||||
log.debug("Bad line ({}): {} ", ignore.toString(), line);
|
log.debug("Bad line ({}): {} ", ignore.toString(), line);
|
||||||
|
} catch (SSHRuntimeException ignore) {
|
||||||
|
log.debug("Failed to process line ({}): {} ", ignore.toString(), line);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(br);
|
IOUtils.closeQuietly(br);
|
||||||
}
|
}
|
||||||
@@ -185,9 +181,11 @@ public class OpenSSHKnownHosts
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Lines starting with `#' and empty lines are ignored as comments.
|
* 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 {
|
throws IOException {
|
||||||
if (isComment(line)) {
|
if (isComment(line)) {
|
||||||
return new CommentEntry(line);
|
return new CommentEntry(line);
|
||||||
@@ -201,7 +199,7 @@ public class OpenSSHKnownHosts
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if(split.length < 3) {
|
if(split.length < 3) {
|
||||||
LOG.error("Error reading entry `{}`", line);
|
log.error("Error reading entry `{}`", line);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final String hostnames = split[i++];
|
final String hostnames = split[i++];
|
||||||
@@ -212,7 +210,7 @@ public class OpenSSHKnownHosts
|
|||||||
|
|
||||||
if (type != KeyType.UNKNOWN) {
|
if (type != KeyType.UNKNOWN) {
|
||||||
final String sKey = split[i++];
|
final String sKey = split[i++];
|
||||||
key = getKey(sKey);
|
key = new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
|
||||||
} else if (isBits(sType)) {
|
} else if (isBits(sType)) {
|
||||||
type = KeyType.RSA;
|
type = KeyType.RSA;
|
||||||
// int bits = Integer.valueOf(sType);
|
// int bits = Integer.valueOf(sType);
|
||||||
@@ -222,11 +220,11 @@ public class OpenSSHKnownHosts
|
|||||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
||||||
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||||
} catch (Exception ex) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Error reading entry `{}`, could not determine type", line);
|
log.error("Error reading entry `{}`, could not determine type", line);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,12 +235,7 @@ public class OpenSSHKnownHosts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PublicKey getKey(String sKey)
|
private boolean isBits(String type) {
|
||||||
throws IOException {
|
|
||||||
return new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isBits(String type) {
|
|
||||||
try {
|
try {
|
||||||
Integer.parseInt(type);
|
Integer.parseInt(type);
|
||||||
return true;
|
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("#");
|
return line.isEmpty() || line.startsWith("#");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isHashed(String line) {
|
public boolean isHashed(String line) {
|
||||||
return line.startsWith("|1|");
|
return line.startsWith("|1|");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface HostEntry {
|
public interface HostEntry {
|
||||||
|
KeyType getType();
|
||||||
|
|
||||||
|
String getFingerprint();
|
||||||
|
|
||||||
|
boolean appliesTo(String host)
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
boolean appliesTo(KeyType type, String host)
|
boolean appliesTo(KeyType type, String host)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
@@ -279,6 +279,22 @@ public class OpenSSHKnownHosts
|
|||||||
this.comment = comment;
|
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
|
@Override
|
||||||
public boolean appliesTo(KeyType type, String host) {
|
public boolean appliesTo(KeyType type, String host) {
|
||||||
return false;
|
return false;
|
||||||
@@ -308,6 +324,16 @@ public class OpenSSHKnownHosts
|
|||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFingerprint() {
|
||||||
|
return SecurityUtils.getFingerprint(key);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(PublicKey key)
|
public boolean verify(PublicKey key)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@@ -349,6 +375,12 @@ public class OpenSSHKnownHosts
|
|||||||
return hostnames;
|
return hostnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean appliesTo(String host)
|
||||||
|
throws IOException {
|
||||||
|
return hosts.contains(host);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean appliesTo(KeyType type, String host)
|
public boolean appliesTo(KeyType type, String host)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@@ -377,6 +409,12 @@ public class OpenSSHKnownHosts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean appliesTo(String host)
|
||||||
|
throws IOException {
|
||||||
|
return hashedHost.equals(hashHost(host));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean appliesTo(KeyType type, String host)
|
public boolean appliesTo(KeyType type, String host)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@@ -416,14 +454,14 @@ public class OpenSSHKnownHosts
|
|||||||
public String getMarkerString() {
|
public String getMarkerString() {
|
||||||
return sMarker;
|
return sMarker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Marker fromString(String str) {
|
public static Marker fromString(String str) {
|
||||||
for (Marker m: values())
|
for (Marker m: values())
|
||||||
if (m.sMarker.equals(str))
|
if (m.sMarker.equals(str))
|
||||||
return m;
|
return m;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,14 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/** {@link UserAuth} implementation. */
|
/**
|
||||||
|
* {@link UserAuth} implementation.
|
||||||
|
*/
|
||||||
public class UserAuthImpl
|
public class UserAuthImpl
|
||||||
extends AbstractService
|
extends AbstractService
|
||||||
implements UserAuth {
|
implements UserAuth {
|
||||||
|
|
||||||
private final Promise<Boolean, UserAuthException> authenticated
|
private final Promise<Boolean, UserAuthException> authenticated;
|
||||||
= new Promise<Boolean, UserAuthException>("authenticated", UserAuthException.chainer);
|
|
||||||
|
|
||||||
// Externally available
|
// Externally available
|
||||||
private volatile String banner = "";
|
private volatile String banner = "";
|
||||||
@@ -51,6 +52,7 @@ public class UserAuthImpl
|
|||||||
|
|
||||||
public UserAuthImpl(Transport trans) {
|
public UserAuthImpl(Transport trans) {
|
||||||
super("ssh-userauth", trans);
|
super("ssh-userauth", trans);
|
||||||
|
authenticated = new Promise<Boolean, UserAuthException>("authenticated", UserAuthException.chainer, trans.getConfig().getLoggerFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -111,22 +113,20 @@ public class UserAuthImpl
|
|||||||
try {
|
try {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
|
|
||||||
case USERAUTH_BANNER: {
|
case USERAUTH_BANNER:
|
||||||
banner = buf.readString();
|
banner = buf.readString();
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case USERAUTH_SUCCESS: {
|
case USERAUTH_SUCCESS:
|
||||||
// In order to prevent race conditions, we immediately set the authenticated flag on the transport
|
// In order to prevent race conditions, we immediately set the authenticated flag on the transport
|
||||||
// And change the service before delivering the authenticated promise.
|
// And change the service before delivering the authenticated promise.
|
||||||
// Should fix https://github.com/hierynomus/sshj/issues/237
|
// Should fix https://github.com/hierynomus/sshj/issues/237
|
||||||
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
|
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
|
||||||
trans.setService(nextService); // We aren't in charge anymore, next service is
|
trans.setService(nextService); // We aren't in charge anymore, next service is
|
||||||
authenticated.deliver(true);
|
authenticated.deliver(true);
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case USERAUTH_FAILURE: {
|
case USERAUTH_FAILURE:
|
||||||
allowedMethods = Arrays.asList(buf.readString().split(","));
|
allowedMethods = Arrays.asList(buf.readString().split(","));
|
||||||
partialSuccess |= buf.readBoolean();
|
partialSuccess |= buf.readBoolean();
|
||||||
if (allowedMethods.contains(currentMethod.getName()) && currentMethod.shouldRetry()) {
|
if (allowedMethods.contains(currentMethod.getName()) && currentMethod.shouldRetry()) {
|
||||||
@@ -134,18 +134,16 @@ public class UserAuthImpl
|
|||||||
} else {
|
} else {
|
||||||
authenticated.deliver(false);
|
authenticated.deliver(false);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
default: {
|
default:
|
||||||
log.debug("Asking `{}` method to handle {} packet", currentMethod.getName(), msg);
|
log.debug("Asking `{}` method to handle {} packet", currentMethod.getName(), msg);
|
||||||
try {
|
try {
|
||||||
currentMethod.handle(msg, buf);
|
currentMethod.handle(msg, buf);
|
||||||
} catch (UserAuthException e) {
|
} catch (UserAuthException e) {
|
||||||
authenticated.deliverError(e);
|
authenticated.deliverError(e);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
authenticated.unlock();
|
authenticated.unlock();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user