mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 23:30:55 +03:00
Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5f0d4c9fb | ||
|
|
c10cb7f138 | ||
|
|
81e26f4a7f | ||
|
|
aa53effce8 | ||
|
|
76e6e572b4 | ||
|
|
2003a9f8c9 | ||
|
|
84a7677a62 | ||
|
|
3bcd3530cf | ||
|
|
a63f9ee8fd | ||
|
|
4be5a98ea3 | ||
|
|
26df2f3c23 | ||
|
|
39b72eed62 | ||
|
|
d55eb6d02e | ||
|
|
265e9d2916 | ||
|
|
0b6552654b | ||
|
|
dabe43dfdc | ||
|
|
0f67fa2541 | ||
|
|
54018a4a81 | ||
|
|
ca81c2eea4 | ||
|
|
048f84b42a | ||
|
|
8ca6451d5d | ||
|
|
5e1be8b1b0 | ||
|
|
bc4da2ea8e | ||
|
|
09fb2b9dc2 | ||
|
|
4045d5a7ef | ||
|
|
d0daa2c12f | ||
|
|
64a2a4f779 | ||
|
|
7cb1f8b11c | ||
|
|
73bc785ab4 | ||
|
|
9d697ede12 | ||
|
|
2b62492caf | ||
|
|
a0f1aa7e2c | ||
|
|
0e981f7656 | ||
|
|
a014567c9e | ||
|
|
8454cf1a0c | ||
|
|
663f118d0f | ||
|
|
47d73a9381 | ||
|
|
c4552d5f3d | ||
|
|
7a884d0938 | ||
|
|
661f63eab7 | ||
|
|
a71a7d7d33 | ||
|
|
d2e0f50d0c | ||
|
|
b41f0acd19 | ||
|
|
a1f501a027 | ||
|
|
fef9cfaf79 | ||
|
|
c67ae242f2 | ||
|
|
823f1e5759 | ||
|
|
f046a41750 | ||
|
|
c161fe26f6 | ||
|
|
ec46a7a489 | ||
|
|
762d088388 | ||
|
|
99c85672b8 | ||
|
|
28d57840ab | ||
|
|
2984291d84 | ||
|
|
bdbd9d7eb5 | ||
|
|
9ac55de26c | ||
|
|
a9928c2882 | ||
|
|
c6c9a3f6a8 | ||
|
|
0918bc626f | ||
|
|
aa7748395d | ||
|
|
cf077e2a4f | ||
|
|
c58c7c7c60 | ||
|
|
0b548d9d13 | ||
|
|
eb1629f250 | ||
|
|
8856aaea61 | ||
|
|
1f6615b57a | ||
|
|
e5084ed8db | ||
|
|
3729119e23 | ||
|
|
aed3decf1d | ||
|
|
303c03061c | ||
|
|
5e3a08a637 | ||
|
|
d0800058e8 | ||
|
|
ad9c2d5411 | ||
|
|
ed65176b68 | ||
|
|
28f3280a84 | ||
|
|
d69f722908 | ||
|
|
1d7cb8c2c6 | ||
|
|
6ad6242ed1 | ||
|
|
3310530d42 | ||
|
|
3685f9dc36 | ||
|
|
f8cad120a6 | ||
|
|
56dd4e4af4 | ||
|
|
9f8cf1f298 | ||
|
|
a51270791d | ||
|
|
d43fc4551e | ||
|
|
93bf6c0089 | ||
|
|
7b535a8db3 | ||
|
|
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 |
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
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,7 +10,9 @@
|
|||||||
.settings/
|
.settings/
|
||||||
|
|
||||||
# Output dirs
|
# Output dirs
|
||||||
|
out/
|
||||||
target/
|
target/
|
||||||
|
classes/
|
||||||
build/
|
build/
|
||||||
docs/
|
docs/
|
||||||
.gradle/
|
.gradle/
|
||||||
|
|||||||
29
.travis.yml
29
.travis.yml
@@ -1,5 +1,30 @@
|
|||||||
language: java
|
language: java
|
||||||
sudo: false
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk7
|
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
|
- openjdk8
|
||||||
|
- oraclejdk9
|
||||||
|
|
||||||
|
before_cache:
|
||||||
|
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.gradle/caches/
|
||||||
|
- $HOME/.gradle/wrapper/
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- pip install --user codecov
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./gradlew check
|
||||||
|
- ./gradlew integrationTest
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- codecov
|
||||||
|
|||||||
55
README.adoc
55
README.adoc
@@ -1,11 +1,14 @@
|
|||||||
= 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.18.0
|
:sshj_version: 0.25.0
|
||||||
:source-highlighter: pygments
|
:source-highlighter: pygments
|
||||||
|
|
||||||
|
image:https://api.bintray.com/packages/hierynomus/maven/sshj/images/download.svg[link="https://bintray.com/hierynomus/maven/sshj/_latestVersion"]
|
||||||
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://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://codecov.io/gh/hierynomus/sshj/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/hierynomus/sshj"]
|
||||||
|
image:http://www.javadoc.io/badge/com.hierynomus/sshj.svg?color=blue["JavaDocs", link="http://www.javadoc.io/doc/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://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"]
|
||||||
|
|
||||||
@@ -37,7 +40,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`.
|
||||||
|
|
||||||
@@ -66,20 +69,25 @@ 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`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519`
|
||||||
|
|
||||||
mac::
|
mac::
|
||||||
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`
|
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`, `hmac-ripemd160`
|
||||||
|
|
||||||
compression::
|
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!)
|
||||||
|
|
||||||
@@ -99,6 +107,41 @@ Google Group: http://groups.google.com/group/sshj-users
|
|||||||
Fork away!
|
Fork away!
|
||||||
|
|
||||||
== Release history
|
== Release history
|
||||||
|
SSHJ 0.24.0 (2018-??-??)::
|
||||||
|
* Added support for hmac-ripemd160
|
||||||
|
|
||||||
|
SSHJ 0.23.0 (2017-10-13)::
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/372[#372]: Upgrade to 'net.i2p.crypto:eddsa:0.2.0'
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/355[#355] and https://github.com/hierynomus/sshj/issues/354[#354]: Correctly decode signature bytes
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/365[#365]: Added support for new-style OpenSSH fingerprints of server keys
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/356[#356]: Fixed key type detection for ECDSA public keys
|
||||||
|
* Made SSHJ Java9 compatible
|
||||||
|
SSHJ 0.22.0 (2017-08-24)::
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/pulls/341[#341]: Fixed path walking during recursive copy
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/338[#338]: Added ConsolePasswordFinder to read password from stdin
|
||||||
|
* Merged https://github.com/hierynomus/sshj/pulls/336[#336]: Added support for ecdsa-sha2-nistp384 and ecdsa-sha2-nistp521 signatures
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/331[#331]: Added support for wildcards in known_hosts file
|
||||||
|
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)::
|
SSHJ 0.18.0 (2016-09-30)::
|
||||||
* Fixed Android compatibility
|
* Fixed Android compatibility
|
||||||
* Upgrade to Gradle 3.0
|
* Upgrade to Gradle 3.0
|
||||||
|
|||||||
157
build.gradle
157
build.gradle
@@ -1,22 +1,45 @@
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import com.bmuschko.gradle.docker.tasks.container.*
|
||||||
|
import com.bmuschko.gradle.docker.tasks.image.*
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
id 'pl.allegro.tech.build.axion-release' version '1.9.0'
|
||||||
id "java"
|
id "java"
|
||||||
id "groovy"
|
id "groovy"
|
||||||
|
id "jacoco"
|
||||||
id "osgi"
|
id "osgi"
|
||||||
id "maven-publish"
|
id "maven-publish"
|
||||||
id "org.ajoberstar.release-opinion" version "1.4.2"
|
id "com.bmuschko.docker-remote-api" version "3.2.1"
|
||||||
id "com.github.hierynomus.license" version "0.12.1"
|
id "com.github.hierynomus.license" version "0.12.1"
|
||||||
id "com.jfrog.bintray" version "1.7"
|
id "com.jfrog.bintray" version "1.7"
|
||||||
id 'ru.vyarus.pom' version '1.0.3'
|
id 'ru.vyarus.java-lib' version '1.0.5'
|
||||||
|
// id 'ru.vyarus.pom' version '1.0.3'
|
||||||
id 'ru.vyarus.github-info' version '1.1.0'
|
id 'ru.vyarus.github-info' version '1.1.0'
|
||||||
|
id 'ru.vyarus.animalsniffer' version '1.4.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.hierynomus"
|
group = "com.hierynomus"
|
||||||
|
|
||||||
|
scmVersion {
|
||||||
|
tag {
|
||||||
|
prefix = 'v'
|
||||||
|
versionSeparator = ''
|
||||||
|
}
|
||||||
|
hooks {
|
||||||
|
pre 'fileUpdate', [file: 'README.adoc', pattern: { v, c -> /:sshj_version: .*/}, replacement: { v, c -> ":sshj_version: $v" }]
|
||||||
|
pre 'commit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.version = scmVersion.version
|
||||||
|
|
||||||
defaultTasks "build"
|
defaultTasks "build"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url "https://dl.bintray.com/mockito/maven/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = 1.6
|
sourceCompatibility = 1.6
|
||||||
@@ -24,20 +47,22 @@ targetCompatibility = 1.6
|
|||||||
|
|
||||||
configurations.compile.transitive = false
|
configurations.compile.transitive = false
|
||||||
|
|
||||||
def bouncycastleVersion = "1.51"
|
def bouncycastleVersion = "1.57"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
||||||
|
|
||||||
compile "org.slf4j:slf4j-api:1.7.7"
|
compile "org.slf4j:slf4j-api:1.7.7"
|
||||||
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
||||||
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
||||||
compile "com.jcraft:jzlib:1.1.3"
|
compile "com.jcraft:jzlib:1.1.3"
|
||||||
|
|
||||||
compile "net.i2p.crypto:eddsa:0.1.0"
|
compile "net.i2p.crypto:eddsa:0.2.0"
|
||||||
|
|
||||||
testCompile "junit:junit:4.11"
|
testCompile "junit:junit:4.11"
|
||||||
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
||||||
testCompile "org.mockito:mockito-core:1.9.5"
|
testCompile "org.mockito:mockito-core:2.9.2"
|
||||||
testCompile "org.apache.sshd:sshd-core:1.1.0"
|
testCompile "org.apache.sshd:sshd-core:1.2.0"
|
||||||
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
||||||
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
||||||
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
|
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
|
||||||
@@ -53,10 +78,6 @@ license {
|
|||||||
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java'])
|
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java'])
|
||||||
}
|
}
|
||||||
|
|
||||||
release {
|
|
||||||
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -64,29 +85,37 @@ if (JavaVersion.current().isJava8Compatible()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
task writeSshjVersionProperties {
|
||||||
manifest {
|
doLast {
|
||||||
instruction "Bundle-Description", "SSHv2 library for Java"
|
project.file("${project.buildDir}/resources/main").mkdirs()
|
||||||
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
project.file("${project.buildDir}/resources/main/sshj.properties").withWriter { w ->
|
||||||
instruction "Import-Package", "!net.schmizz.*"
|
w.append("sshj.version=${version}")
|
||||||
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) {
|
jar.dependsOn writeSshjVersionProperties
|
||||||
classifier = 'javadoc'
|
jar {
|
||||||
from javadoc
|
manifest {
|
||||||
|
// please see http://bnd.bndtools.org/chapters/390-wrapping.html
|
||||||
|
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", "!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}\""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task sourcesJar(type: Jar) {
|
sourcesJar {
|
||||||
classifier = 'sources'
|
|
||||||
from sourceSets.main.allSource
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes(
|
attributes(
|
||||||
// Add the needed OSGI attributes
|
// Add the needed OSGI attributes
|
||||||
@@ -99,6 +128,27 @@ task sourcesJar(type: Jar) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
integrationTestCompile.extendsFrom testCompile
|
||||||
|
integrationTestRuntime.extendsFrom testRuntime
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
integrationTest {
|
||||||
|
groovy {
|
||||||
|
compileClasspath += sourceSets.main.output + sourceSets.test.output
|
||||||
|
runtimeClasspath += sourceSets.main.output + sourceSets.test.output
|
||||||
|
srcDir file('src/itest/groovy')
|
||||||
|
}
|
||||||
|
resources.srcDir file('src/itest/resources')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task integrationTest(type: Test) {
|
||||||
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
tasks.withType(Test) {
|
tasks.withType(Test) {
|
||||||
testLogging {
|
testLogging {
|
||||||
exceptionFormat = 'full'
|
exceptionFormat = 'full'
|
||||||
@@ -165,21 +215,12 @@ pom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing.publications {
|
|
||||||
Sshj(MavenPublication) {
|
|
||||||
from components.java
|
|
||||||
artifact sourcesJar
|
|
||||||
artifact javadocJar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey")) {
|
if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey")) {
|
||||||
bintray {
|
bintray {
|
||||||
user = project.property("bintrayUsername")
|
user = project.property("bintrayUsername")
|
||||||
key = project.property("bintrayApiKey")
|
key = project.property("bintrayApiKey")
|
||||||
publish = true
|
publish = true
|
||||||
publications = ["Sshj"]
|
publications = ["maven"]
|
||||||
pkg {
|
pkg {
|
||||||
repo = "maven"
|
repo = "maven"
|
||||||
name = project.name
|
name = project.name
|
||||||
@@ -188,8 +229,8 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey
|
|||||||
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
|
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
|
||||||
githubRepo = "hierynomus/sshj"
|
githubRepo = "hierynomus/sshj"
|
||||||
version {
|
version {
|
||||||
name = project.version.toString()
|
name = "${->project.version}"
|
||||||
vcsTag = "v${project.version}"
|
vcsTag = "v${->project.version}"
|
||||||
released = new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ').format(new Date())
|
released = new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ').format(new Date())
|
||||||
gpg {
|
gpg {
|
||||||
sign = true
|
sign = true
|
||||||
@@ -206,4 +247,38 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
project.tasks.release.dependsOn([project.tasks.build, project.tasks.bintrayUpload])
|
jacocoTestReport {
|
||||||
|
reports {
|
||||||
|
xml.enabled true
|
||||||
|
html.enabled true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
task buildItestImage(type: DockerBuildImage) {
|
||||||
|
inputDir = file('src/itest/docker-image')
|
||||||
|
tag = 'sshj/sshd-itest'
|
||||||
|
}
|
||||||
|
|
||||||
|
task createItestContainer(type: DockerCreateContainer) {
|
||||||
|
dependsOn buildItestImage
|
||||||
|
targetImageId { buildItestImage.getImageId() }
|
||||||
|
portBindings = ['2222:22']
|
||||||
|
}
|
||||||
|
|
||||||
|
task startItestContainer(type: DockerStartContainer) {
|
||||||
|
dependsOn createItestContainer
|
||||||
|
targetContainerId { createItestContainer.getContainerId() }
|
||||||
|
}
|
||||||
|
|
||||||
|
task stopItestContainer(type: DockerStopContainer) {
|
||||||
|
targetContainerId { createItestContainer.getContainerId() }
|
||||||
|
}
|
||||||
|
|
||||||
|
project.tasks.integrationTest.dependsOn(startItestContainer)
|
||||||
|
project.tasks.integrationTest.finalizedBy(stopItestContainer)
|
||||||
|
|
||||||
|
project.tasks.release.dependsOn([project.tasks.integrationTest, project.tasks.build])
|
||||||
|
project.tasks.release.finalizedBy(project.tasks.bintrayUpload)
|
||||||
|
project.tasks.jacocoTestReport.dependsOn(project.tasks.test)
|
||||||
|
project.tasks.check.dependsOn(project.tasks.jacocoTestReport)
|
||||||
|
|||||||
@@ -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-3.0-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip
|
||||||
|
|||||||
17
src/itest/docker-image/Dockerfile
Normal file
17
src/itest/docker-image/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
FROM sickp/alpine-sshd:7.5
|
||||||
|
|
||||||
|
ADD id_rsa.pub /home/sshj/.ssh/authorized_keys
|
||||||
|
|
||||||
|
ADD test-container/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key
|
||||||
|
ADD test-container/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ecdsa_key.pub
|
||||||
|
ADD test-container/sshd_config /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
echo "root:smile" | chpasswd && \
|
||||||
|
adduser -D -s /bin/ash sshj && \
|
||||||
|
passwd -u sshj && \
|
||||||
|
chmod 600 /home/sshj/.ssh/authorized_keys && \
|
||||||
|
chmod 600 /etc/ssh/ssh_host_ecdsa_key && \
|
||||||
|
chmod 644 /etc/ssh/ssh_host_ecdsa_key.pub && \
|
||||||
|
chown -R sshj:sshj /home/sshj
|
||||||
|
|
||||||
1
src/itest/docker-image/id_rsa.pub
Normal file
1
src/itest/docker-image/id_rsa.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQmTIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmuhA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPCliztspKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmLGGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQ== no-passphrase
|
||||||
5
src/itest/docker-image/test-container/ssh_host_ecdsa_key
Normal file
5
src/itest/docker-image/test-container/ssh_host_ecdsa_key
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIOpOBFjqe0hjK/hs4WZ3dZqnzanq1L3/JbvV1TCkbe4ToAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEVzkrS7Yj0nXML7A3mE08YDthfBR/ZbyYJDIq1vTzcqs6KTaCT529
|
||||||
|
swNXWLHO+mbHviZcRiI57ULXHZ1emom/Jw==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFc5K0u2I9J1zC+wN5hNPGA7YXwUf2W8mCQyKtb083KrOik2gk+dvbMDV1ixzvpmx74mXEYiOe1C1x2dXpqJvyc= root@404b27be2bf4
|
||||||
132
src/itest/docker-image/test-container/sshd_config
Normal file
132
src/itest/docker-image/test-container/sshd_config
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# $OpenBSD: sshd_config,v 1.101 2017/03/14 07:19:07 djm Exp $
|
||||||
|
|
||||||
|
# This is the sshd server system-wide configuration file. See
|
||||||
|
# sshd_config(5) for more information.
|
||||||
|
|
||||||
|
# This sshd was compiled with PATH=/bin:/usr/bin:/sbin:/usr/sbin
|
||||||
|
|
||||||
|
# The strategy used for options in the default sshd_config shipped with
|
||||||
|
# OpenSSH is to specify options with their default value where
|
||||||
|
# possible, but leave them commented. Uncommented options override the
|
||||||
|
# default value.
|
||||||
|
|
||||||
|
#Port 22
|
||||||
|
#AddressFamily any
|
||||||
|
#ListenAddress 0.0.0.0
|
||||||
|
#ListenAddress ::
|
||||||
|
|
||||||
|
#HostKey /etc/ssh/ssh_host_rsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_dsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ed25519_key
|
||||||
|
|
||||||
|
# Ciphers and keying
|
||||||
|
#RekeyLimit default none
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
#SyslogFacility AUTH
|
||||||
|
#LogLevel INFO
|
||||||
|
|
||||||
|
# Authentication:
|
||||||
|
|
||||||
|
#LoginGraceTime 2m
|
||||||
|
PermitRootLogin yes
|
||||||
|
#StrictModes yes
|
||||||
|
#MaxAuthTries 6
|
||||||
|
#MaxSessions 10
|
||||||
|
|
||||||
|
#PubkeyAuthentication yes
|
||||||
|
|
||||||
|
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
|
||||||
|
# but this is overridden so installations will only check .ssh/authorized_keys
|
||||||
|
AuthorizedKeysFile .ssh/authorized_keys
|
||||||
|
|
||||||
|
#AuthorizedPrincipalsFile none
|
||||||
|
|
||||||
|
#AuthorizedKeysCommand none
|
||||||
|
#AuthorizedKeysCommandUser nobody
|
||||||
|
|
||||||
|
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
|
||||||
|
#HostbasedAuthentication no
|
||||||
|
# Change to yes if you don't trust ~/.ssh/known_hosts for
|
||||||
|
# HostbasedAuthentication
|
||||||
|
#IgnoreUserKnownHosts no
|
||||||
|
# Don't read the user's ~/.rhosts and ~/.shosts files
|
||||||
|
#IgnoreRhosts yes
|
||||||
|
|
||||||
|
# To disable tunneled clear text passwords, change to no here!
|
||||||
|
#PasswordAuthentication yes
|
||||||
|
#PermitEmptyPasswords no
|
||||||
|
|
||||||
|
# Change to no to disable s/key passwords
|
||||||
|
#ChallengeResponseAuthentication yes
|
||||||
|
|
||||||
|
# Kerberos options
|
||||||
|
#KerberosAuthentication no
|
||||||
|
#KerberosOrLocalPasswd yes
|
||||||
|
#KerberosTicketCleanup yes
|
||||||
|
#KerberosGetAFSToken no
|
||||||
|
|
||||||
|
# GSSAPI options
|
||||||
|
#GSSAPIAuthentication no
|
||||||
|
#GSSAPICleanupCredentials yes
|
||||||
|
|
||||||
|
# Set this to 'yes' to enable PAM authentication, account processing,
|
||||||
|
# and session processing. If this is enabled, PAM authentication will
|
||||||
|
# be allowed through the ChallengeResponseAuthentication and
|
||||||
|
# PasswordAuthentication. Depending on your PAM configuration,
|
||||||
|
# PAM authentication via ChallengeResponseAuthentication may bypass
|
||||||
|
# the setting of "PermitRootLogin without-password".
|
||||||
|
# If you just want the PAM account and session checks to run without
|
||||||
|
# PAM authentication, then enable this but set PasswordAuthentication
|
||||||
|
# and ChallengeResponseAuthentication to 'no'.
|
||||||
|
#UsePAM no
|
||||||
|
|
||||||
|
#AllowAgentForwarding yes
|
||||||
|
#AllowTcpForwarding yes
|
||||||
|
#GatewayPorts no
|
||||||
|
#X11Forwarding no
|
||||||
|
#X11DisplayOffset 10
|
||||||
|
#X11UseLocalhost yes
|
||||||
|
#PermitTTY yes
|
||||||
|
#PrintMotd yes
|
||||||
|
#PrintLastLog yes
|
||||||
|
#TCPKeepAlive yes
|
||||||
|
#UseLogin no
|
||||||
|
#PermitUserEnvironment no
|
||||||
|
#Compression delayed
|
||||||
|
#ClientAliveInterval 0
|
||||||
|
#ClientAliveCountMax 3
|
||||||
|
#UseDNS no
|
||||||
|
#PidFile /run/sshd.pid
|
||||||
|
#MaxStartups 10:30:100
|
||||||
|
#PermitTunnel no
|
||||||
|
#ChrootDirectory none
|
||||||
|
#VersionAddendum none
|
||||||
|
|
||||||
|
# no default banner path
|
||||||
|
#Banner none
|
||||||
|
|
||||||
|
# override default of no subsystems
|
||||||
|
Subsystem sftp /usr/lib/ssh/sftp-server
|
||||||
|
|
||||||
|
# the following are HPN related configuration options
|
||||||
|
# tcp receive buffer polling. disable in non autotuning kernels
|
||||||
|
#TcpRcvBufPoll yes
|
||||||
|
|
||||||
|
# disable hpn performance boosts
|
||||||
|
#HPNDisabled no
|
||||||
|
|
||||||
|
# buffer size for hpn to non-hpn connections
|
||||||
|
#HPNBufferSize 2048
|
||||||
|
|
||||||
|
|
||||||
|
# Example of overriding settings on a per-user basis
|
||||||
|
#Match User anoncvs
|
||||||
|
# X11Forwarding no
|
||||||
|
# AllowTcpForwarding no
|
||||||
|
# PermitTTY no
|
||||||
|
# ForceCommand cvs server
|
||||||
|
|
||||||
|
|
||||||
|
macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config
|
||||||
|
import net.schmizz.sshj.DefaultConfig
|
||||||
|
import net.schmizz.sshj.SSHClient
|
||||||
|
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
class IntegrationBaseSpec extends Specification {
|
||||||
|
protected static final int DOCKER_PORT = 2222
|
||||||
|
protected static final String USERNAME = "sshj"
|
||||||
|
protected static final String KEYFILE = "src/test/resources/id_rsa"
|
||||||
|
protected final static String SERVER_IP = System.getProperty("serverIP", "127.0.0.1")
|
||||||
|
|
||||||
|
protected static SSHClient getConnectedClient(Config config) {
|
||||||
|
SSHClient sshClient = new SSHClient(config)
|
||||||
|
sshClient.addHostKeyVerifier(new PromiscuousVerifier())
|
||||||
|
sshClient.connect(SERVER_IP, DOCKER_PORT)
|
||||||
|
|
||||||
|
return sshClient
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static SSHClient getConnectedClient() throws IOException {
|
||||||
|
return getConnectedClient(new DefaultConfig())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
src/itest/groovy/com/hierynomus/sshj/IntegrationSpec.groovy
Normal file
71
src/itest/groovy/com/hierynomus/sshj/IntegrationSpec.groovy
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import net.schmizz.sshj.DefaultConfig
|
||||||
|
import net.schmizz.sshj.SSHClient
|
||||||
|
import net.schmizz.sshj.transport.TransportException
|
||||||
|
import net.schmizz.sshj.userauth.UserAuthException
|
||||||
|
|
||||||
|
class IntegrationSpec extends IntegrationBaseSpec {
|
||||||
|
|
||||||
|
def "should accept correct key"() {
|
||||||
|
given:
|
||||||
|
SSHClient sshClient = new SSHClient(new DefaultConfig())
|
||||||
|
sshClient.addHostKeyVerifier("d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3") // test-containers/ssh_host_ecdsa_key's fingerprint
|
||||||
|
|
||||||
|
when:
|
||||||
|
sshClient.connect(SERVER_IP, DOCKER_PORT)
|
||||||
|
|
||||||
|
then:
|
||||||
|
sshClient.isConnected()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "should decline wrong key"() throws IOException {
|
||||||
|
given:
|
||||||
|
SSHClient sshClient = new SSHClient(new DefaultConfig())
|
||||||
|
sshClient.addHostKeyVerifier("d4:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3")
|
||||||
|
|
||||||
|
when:
|
||||||
|
sshClient.connect(SERVER_IP, DOCKER_PORT)
|
||||||
|
|
||||||
|
then:
|
||||||
|
thrown(TransportException.class)
|
||||||
|
}
|
||||||
|
|
||||||
|
def "should authenticate"() {
|
||||||
|
given:
|
||||||
|
SSHClient client = getConnectedClient()
|
||||||
|
|
||||||
|
when:
|
||||||
|
client.authPublickey(USERNAME, KEYFILE)
|
||||||
|
|
||||||
|
then:
|
||||||
|
client.isAuthenticated()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "should not authenticate with wrong key"() {
|
||||||
|
given:
|
||||||
|
SSHClient client = getConnectedClient()
|
||||||
|
|
||||||
|
when:
|
||||||
|
client.authPublickey("sshj", "src/test/resources/id_dsa")
|
||||||
|
|
||||||
|
then:
|
||||||
|
thrown(UserAuthException.class)
|
||||||
|
!client.isAuthenticated()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.sftp
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||||
|
import net.schmizz.sshj.SSHClient
|
||||||
|
import net.schmizz.sshj.sftp.OpenMode
|
||||||
|
import net.schmizz.sshj.sftp.RemoteFile
|
||||||
|
import net.schmizz.sshj.sftp.SFTPClient
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
import static org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable
|
||||||
|
|
||||||
|
class FileWriteSpec extends IntegrationBaseSpec {
|
||||||
|
|
||||||
|
def "should append to file (GH issue #390)"() {
|
||||||
|
given:
|
||||||
|
SSHClient client = getConnectedClient()
|
||||||
|
client.authPublickey("sshj", "src/test/resources/id_rsa")
|
||||||
|
SFTPClient sftp = client.newSFTPClient()
|
||||||
|
def file = "/home/sshj/test.txt"
|
||||||
|
def initialText = "This is the initial text.\n".getBytes(StandardCharsets.UTF_16)
|
||||||
|
def appendText = "And here's the appended text.\n".getBytes(StandardCharsets.UTF_16)
|
||||||
|
|
||||||
|
when:
|
||||||
|
withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT))) { RemoteFile initial ->
|
||||||
|
initial.write(0, initialText, 0, initialText.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read ->
|
||||||
|
def bytes = new byte[initialText.length]
|
||||||
|
read.read(0, bytes, 0, bytes.length)
|
||||||
|
bytes == initialText
|
||||||
|
}
|
||||||
|
|
||||||
|
when:
|
||||||
|
withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.APPEND))) { RemoteFile append ->
|
||||||
|
append.write(0, appendText, 0, appendText.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read ->
|
||||||
|
def bytes = new byte[initialText.length + appendText.length]
|
||||||
|
read.read(0, bytes, 0, bytes.length)
|
||||||
|
Arrays.copyOfRange(bytes, 0, initialText.length) == initialText
|
||||||
|
Arrays.copyOfRange(bytes, initialText.length, initialText.length + appendText.length) == appendText
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
sftp.close()
|
||||||
|
client.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.mac
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||||
|
import net.schmizz.sshj.DefaultConfig
|
||||||
|
import net.schmizz.sshj.transport.mac.HMACRIPEMD160
|
||||||
|
import net.schmizz.sshj.transport.mac.HMACSHA2256
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
class MacSpec extends IntegrationBaseSpec {
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "should correctly connect with #mac MAC"() {
|
||||||
|
given:
|
||||||
|
def cfg = new DefaultConfig()
|
||||||
|
cfg.setMACFactories(macFactory)
|
||||||
|
def client = getConnectedClient(cfg)
|
||||||
|
|
||||||
|
when:
|
||||||
|
client.authPublickey(USERNAME, KEYFILE)
|
||||||
|
|
||||||
|
then:
|
||||||
|
client.authenticated
|
||||||
|
|
||||||
|
where:
|
||||||
|
macFactory << [new HMACSHA2256.Factory(), new HMACRIPEMD160.Factory()]
|
||||||
|
mac = macFactory.name
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.hierynomus.sshj.backport;
|
package com.hierynomus.sshj.backport;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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")) {
|
||||||
|
|||||||
@@ -32,7 +32,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("Ed25519");
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,16 @@
|
|||||||
package com.hierynomus.sshj.signature;
|
package com.hierynomus.sshj.signature;
|
||||||
|
|
||||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
import net.i2p.crypto.eddsa.EdDSAEngine;
|
||||||
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 net.schmizz.sshj.signature.AbstractSignature;
|
||||||
import net.schmizz.sshj.signature.Signature;
|
import net.schmizz.sshj.signature.Signature;
|
||||||
|
|
||||||
import java.security.*;
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
public class SignatureEdDSA implements Signature {
|
public class SignatureEdDSA extends AbstractSignature {
|
||||||
public static class Factory implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
public static class Factory implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -37,54 +39,18 @@ public class SignatureEdDSA implements Signature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final EdDSAEngine engine;
|
SignatureEdDSA() {
|
||||||
|
super(getEngine());
|
||||||
|
}
|
||||||
|
|
||||||
protected SignatureEdDSA() {
|
private static EdDSAEngine getEngine() {
|
||||||
try {
|
try {
|
||||||
engine = new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
|
return new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(PublicKey pubkey, PrivateKey prvkey) {
|
|
||||||
try {
|
|
||||||
if (pubkey != null) {
|
|
||||||
engine.initVerify(pubkey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prvkey != null) {
|
|
||||||
engine.initSign(prvkey);
|
|
||||||
}
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new SSHRuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte[] H) {
|
|
||||||
update(H, 0, H.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte[] H, int off, int len) {
|
|
||||||
try {
|
|
||||||
engine.update(H, off, len);
|
|
||||||
} catch (SignatureException e) {
|
|
||||||
throw new SSHRuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] sign() {
|
|
||||||
try {
|
|
||||||
return engine.sign();
|
|
||||||
} catch (SignatureException e) {
|
|
||||||
throw new SSHRuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encode(byte[] signature) {
|
public byte[] encode(byte[] signature) {
|
||||||
return signature;
|
return signature;
|
||||||
@@ -93,17 +59,9 @@ public class SignatureEdDSA implements Signature {
|
|||||||
@Override
|
@Override
|
||||||
public boolean verify(byte[] sig) {
|
public boolean verify(byte[] sig) {
|
||||||
try {
|
try {
|
||||||
Buffer.PlainBuffer plainBuffer = new Buffer.PlainBuffer(sig);
|
return signature.verify(extractSig(sig, "ssh-ed25519"));
|
||||||
String algo = plainBuffer.readString();
|
|
||||||
if (!"ssh-ed25519".equals(algo)) {
|
|
||||||
throw new SSHRuntimeException("Expected 'ssh-ed25519' key algorithm, but was: " + algo);
|
|
||||||
}
|
|
||||||
byte[] bytes = plainBuffer.readBytes();
|
|
||||||
return engine.verify(bytes);
|
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
} catch (Buffer.BufferException e) {
|
|
||||||
throw new SSHRuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,8 +62,11 @@ public class IdentificationStringParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) {
|
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 {
|
private String readIdentification(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException, TransportException {
|
||||||
|
|||||||
@@ -19,23 +19,46 @@ import net.schmizz.sshj.transport.cipher.BlockCipher;
|
|||||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All BlockCiphers supported by SSH according to the following RFCs
|
* All BlockCiphers supported by SSH according to the following RFCs:
|
||||||
*
|
*
|
||||||
* - https://tools.ietf.org/html/rfc4344#section-3.1
|
* <ul>
|
||||||
* - https://tools.ietf.org/html/rfc4253#section-6.3
|
* <li>https://tools.ietf.org/html/rfc4344#section-3.1</li>
|
||||||
|
* <li>https://tools.ietf.org/html/rfc4253#section-6.3</li>
|
||||||
|
* <li>TODO: https://tools.ietf.org/html/rfc5647</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* TODO: https://tools.ietf.org/html/rfc5647
|
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are deprecated and scheduled to be removed.
|
||||||
*
|
|
||||||
* 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";
|
||||||
public static final String CIPHER_BLOCK_CHAINING_MODE = "CBC";
|
public static final String CIPHER_BLOCK_CHAINING_MODE = "CBC";
|
||||||
|
|
||||||
|
public static Factory AES128CTR() {
|
||||||
|
return new Factory(16, 128, "aes128-ctr", "AES", COUNTER_MODE);
|
||||||
|
}
|
||||||
|
public static Factory AES192CTR() {
|
||||||
|
return new Factory(16, 192, "aes192-ctr", "AES", COUNTER_MODE);
|
||||||
|
}
|
||||||
|
public static Factory AES256CTR() {
|
||||||
|
return new Factory(16, 256, "aes256-ctr", "AES", COUNTER_MODE);
|
||||||
|
}
|
||||||
|
public static Factory AES128CBC() {
|
||||||
|
return new Factory(16, 128, "aes128-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE);
|
||||||
|
}
|
||||||
|
public static Factory AES192CBC() {
|
||||||
|
return new Factory(16, 192, "aes192-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE);
|
||||||
|
}
|
||||||
|
public static Factory AES256CBC() {
|
||||||
|
return new Factory(16, 256, "aes256-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE);
|
||||||
|
}
|
||||||
public static Factory BlowfishCTR() {
|
public static Factory BlowfishCTR() {
|
||||||
return new Factory(8, 256, "blowfish-ctr", "Blowfish", COUNTER_MODE);
|
return new Factory(8, 256, "blowfish-ctr", "Blowfish", COUNTER_MODE);
|
||||||
}
|
}
|
||||||
|
public static Factory BlowfishCBC() {
|
||||||
|
return new Factory(8, 128, "blowfish-cbc", "Blowfish", CIPHER_BLOCK_CHAINING_MODE);
|
||||||
|
}
|
||||||
public static Factory Twofish128CTR() {
|
public static Factory Twofish128CTR() {
|
||||||
return new Factory(16, 128, "twofish128-ctr", "Twofish", COUNTER_MODE);
|
return new Factory(16, 128, "twofish128-ctr", "Twofish", COUNTER_MODE);
|
||||||
}
|
}
|
||||||
@@ -90,6 +113,9 @@ public class BlockCiphers {
|
|||||||
public static Factory TripleDESCTR() {
|
public static Factory TripleDESCTR() {
|
||||||
return new Factory(8, 192, "3des-ctr", "DESede", COUNTER_MODE);
|
return new Factory(8, 192, "3des-ctr", "DESede", COUNTER_MODE);
|
||||||
}
|
}
|
||||||
|
public static Factory TripleDESCBC() {
|
||||||
|
return new Factory(8, 192, "3des-cbc", "DESede", CIPHER_BLOCK_CHAINING_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
/** Named factory for BlockCipher */
|
/** Named factory for BlockCipher */
|
||||||
public static class Factory
|
public static class Factory
|
||||||
|
|||||||
@@ -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,94 @@
|
|||||||
|
/*
|
||||||
|
* 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.digest.SHA1;
|
||||||
|
import net.schmizz.sshj.transport.digest.SHA256;
|
||||||
|
import net.schmizz.sshj.transport.digest.SHA512;
|
||||||
|
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,151 @@
|
|||||||
|
/*
|
||||||
|
* 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.verification;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Base64;
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
import net.schmizz.sshj.common.SSHException;
|
||||||
|
import net.schmizz.sshj.transport.mac.HMACSHA1;
|
||||||
|
import net.schmizz.sshj.transport.mac.MAC;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class KnownHostMatchers {
|
||||||
|
|
||||||
|
public static HostMatcher createMatcher(String hostEntry) throws SSHException {
|
||||||
|
if (hostEntry.contains(",")) {
|
||||||
|
return new AnyHostMatcher(hostEntry);
|
||||||
|
}
|
||||||
|
if (hostEntry.startsWith("!")) {
|
||||||
|
return new NegateHostMatcher(hostEntry);
|
||||||
|
}
|
||||||
|
if (hostEntry.startsWith("|1|")) {
|
||||||
|
return new HashedHostMatcher(hostEntry);
|
||||||
|
}
|
||||||
|
if (hostEntry.contains("*") || hostEntry.contains("?")) {
|
||||||
|
return new WildcardHostMatcher(hostEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EquiHostMatcher(hostEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface HostMatcher {
|
||||||
|
boolean match(String hostname) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EquiHostMatcher implements HostMatcher {
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
public EquiHostMatcher(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(String hostname) {
|
||||||
|
return host.equals(hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HashedHostMatcher implements HostMatcher {
|
||||||
|
private final MAC sha1 = new HMACSHA1();
|
||||||
|
private final String hash;
|
||||||
|
private final String salt;
|
||||||
|
private byte[] saltyBytes;
|
||||||
|
|
||||||
|
HashedHostMatcher(String hash) throws SSHException {
|
||||||
|
this.hash = hash;
|
||||||
|
final String[] hostParts = hash.split("\\|");
|
||||||
|
if (hostParts.length != 4) {
|
||||||
|
throw new SSHException("Unrecognized format for hashed hostname");
|
||||||
|
}
|
||||||
|
salt = hostParts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(String hostname) throws IOException {
|
||||||
|
return hash.equals(hashHost(hostname));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String hashHost(String host) throws IOException {
|
||||||
|
sha1.init(getSaltyBytes());
|
||||||
|
return "|1|" + salt + "|" + Base64.encodeBytes(sha1.doFinal(host.getBytes(IOUtils.UTF8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getSaltyBytes() throws IOException {
|
||||||
|
if (saltyBytes == null) {
|
||||||
|
saltyBytes = Base64.decode(salt);
|
||||||
|
}
|
||||||
|
return saltyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AnyHostMatcher implements HostMatcher {
|
||||||
|
private final List<HostMatcher> matchers;
|
||||||
|
|
||||||
|
AnyHostMatcher(String hostEntry) throws SSHException {
|
||||||
|
matchers = new ArrayList<HostMatcher>();
|
||||||
|
for (String subEntry : hostEntry.split(",")) {
|
||||||
|
matchers.add(KnownHostMatchers.createMatcher(subEntry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(String hostname) throws IOException {
|
||||||
|
for (HostMatcher matcher : matchers) {
|
||||||
|
if (matcher.match(hostname)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NegateHostMatcher implements HostMatcher {
|
||||||
|
private final HostMatcher matcher;
|
||||||
|
|
||||||
|
NegateHostMatcher(String hostEntry) throws SSHException {
|
||||||
|
this.matcher = createMatcher(hostEntry.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(String hostname) throws IOException {
|
||||||
|
return !matcher.match(hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WildcardHostMatcher implements HostMatcher {
|
||||||
|
private final Pattern pattern;
|
||||||
|
|
||||||
|
public WildcardHostMatcher(String hostEntry) {
|
||||||
|
this.pattern = Pattern.compile("^" + hostEntry.replace("[", "\\[").replace("]", "\\]").replace(".", "\\.").replace("*", ".*").replace("?", ".") + "$");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(String hostname) throws IOException {
|
||||||
|
return pattern.matcher(hostname).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "WildcardHostMatcher[" + pattern + ']';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,158 @@
|
|||||||
|
/*
|
||||||
|
* 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 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 org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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("Ed25519"))));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.concurrent;
|
package net.schmizz.concurrent;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
|
|
||||||
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}.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.concurrent;
|
package net.schmizz.concurrent;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -22,8 +23,6 @@ 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}.
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ 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;
|
protected final Logger log;
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -15,12 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||||
|
|
||||||
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.SignatureRSA;
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers SpongyCastle as JCE provider.
|
||||||
|
*/
|
||||||
public class AndroidConfig
|
public class AndroidConfig
|
||||||
extends DefaultConfig {
|
extends DefaultConfig {
|
||||||
|
|
||||||
@@ -30,7 +35,9 @@ public class AndroidConfig
|
|||||||
|
|
||||||
// don't add ECDSA
|
// don't add ECDSA
|
||||||
protected void initSignatureFactories() {
|
protected void initSignatureFactories() {
|
||||||
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory());
|
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
|
||||||
|
// but add EdDSA
|
||||||
|
new SignatureEdDSA.Factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
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.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;
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ 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.LoggerFactory;
|
||||||
@@ -27,20 +30,22 @@ import net.schmizz.sshj.signature.SignatureECDSA;
|
|||||||
import net.schmizz.sshj.signature.SignatureRSA;
|
import net.schmizz.sshj.signature.SignatureRSA;
|
||||||
import net.schmizz.sshj.transport.cipher.*;
|
import net.schmizz.sshj.transport.cipher.*;
|
||||||
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.kex.DHGexSHA1;
|
||||||
|
import net.schmizz.sshj.transport.kex.DHGexSHA256;
|
||||||
|
import net.schmizz.sshj.transport.kex.ECDHNistP;
|
||||||
import net.schmizz.sshj.transport.mac.*;
|
import net.schmizz.sshj.transport.mac.*;
|
||||||
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 java.util.Arrays;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.*;
|
||||||
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
|
||||||
@@ -48,9 +53,7 @@ import java.util.List;
|
|||||||
* <p/>
|
* <p/>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyExchangeFactories Key exchange}: {@link net.schmizz.sshj.transport.kex.DHG14}*, {@link net.schmizz.sshj.transport.kex.DHG1}</li>
|
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyExchangeFactories Key exchange}: {@link net.schmizz.sshj.transport.kex.DHG14}*, {@link net.schmizz.sshj.transport.kex.DHG1}</li>
|
||||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setCipherFactories Ciphers} [1]: {@link net.schmizz.sshj.transport.cipher.AES128CTR}, {@link net.schmizz.sshj.transport.cipher.AES192CTR}, {@link net.schmizz.sshj.transport.cipher.AES256CTR},
|
* <li>{@link net.schmizz.sshj.ConfigImpl#setCipherFactories Ciphers}: {@link BlockCiphers}, {@link StreamCiphers} [1]</li>
|
||||||
* {@link
|
|
||||||
* net.schmizz.sshj.transport.cipher.AES128CBC}, {@link net.schmizz.sshj.transport.cipher.AES192CBC}, {@link net.schmizz.sshj.transport.cipher.AES256CBC}, {@link net.schmizz.sshj.transport.cipher.AES192CBC}, {@link net.schmizz.sshj.transport.cipher.TripleDESCBC}, {@link net.schmizz.sshj.transport.cipher.BlowfishCBC}</li>
|
|
||||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setMACFactories MAC}: {@link net.schmizz.sshj.transport.mac.HMACSHA1}, {@link net.schmizz.sshj.transport.mac.HMACSHA196}, {@link net.schmizz.sshj.transport.mac.HMACMD5}, {@link
|
* <li>{@link net.schmizz.sshj.ConfigImpl#setMACFactories MAC}: {@link net.schmizz.sshj.transport.mac.HMACSHA1}, {@link net.schmizz.sshj.transport.mac.HMACSHA196}, {@link net.schmizz.sshj.transport.mac.HMACMD5}, {@link
|
||||||
* net.schmizz.sshj.transport.mac.HMACMD596}</li>
|
* net.schmizz.sshj.transport.mac.HMACMD596}</li>
|
||||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setCompressionFactories Compression}: {@link net.schmizz.sshj.transport.compression.NoneCompression}</li>
|
* <li>{@link net.schmizz.sshj.ConfigImpl#setCompressionFactories Compression}: {@link net.schmizz.sshj.transport.compression.NoneCompression}</li>
|
||||||
@@ -67,13 +70,11 @@ import java.util.List;
|
|||||||
public class DefaultConfig
|
public class DefaultConfig
|
||||||
extends ConfigImpl {
|
extends ConfigImpl {
|
||||||
|
|
||||||
private static final String VERSION = "SSHJ_0_17_2";
|
|
||||||
|
|
||||||
private Logger log;
|
private Logger log;
|
||||||
|
|
||||||
public DefaultConfig() {
|
public DefaultConfig() {
|
||||||
setLoggerFactory(LoggerFactory.DEFAULT);
|
setLoggerFactory(LoggerFactory.DEFAULT);
|
||||||
setVersion(VERSION);
|
setVersion(readVersionFromProperties());
|
||||||
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
|
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
|
||||||
initKeyExchangeFactories(bouncyCastleRegistered);
|
initKeyExchangeFactories(bouncyCastleRegistered);
|
||||||
initRandomFactory(bouncyCastleRegistered);
|
initRandomFactory(bouncyCastleRegistered);
|
||||||
@@ -85,6 +86,18 @@ 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 (Exception e) {
|
||||||
|
log.error("Could not read the sshj.properties file, returning an 'unknown' version as fallback.");
|
||||||
|
return "SSHJ_VERSION_UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLoggerFactory(LoggerFactory loggerFactory) {
|
public void setLoggerFactory(LoggerFactory loggerFactory) {
|
||||||
super.setLoggerFactory(loggerFactory);
|
super.setLoggerFactory(loggerFactory);
|
||||||
@@ -99,10 +112,23 @@ public class DefaultConfig
|
|||||||
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(),
|
||||||
|
DHGroups.Group14SHA256(),
|
||||||
|
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 {
|
} else {
|
||||||
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
|
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,21 +139,25 @@ 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void initCipherFactories() {
|
protected void initCipherFactories() {
|
||||||
List<Factory.Named<Cipher>> avail = new LinkedList<Factory.Named<Cipher>>(Arrays.<Factory.Named<Cipher>>asList(
|
List<Factory.Named<Cipher>> avail = new LinkedList<Factory.Named<Cipher>>(Arrays.<Factory.Named<Cipher>>asList(
|
||||||
new AES128CTR.Factory(),
|
BlockCiphers.AES128CBC(),
|
||||||
new AES192CTR.Factory(),
|
BlockCiphers.AES128CTR(),
|
||||||
new AES256CTR.Factory(),
|
BlockCiphers.AES192CBC(),
|
||||||
new AES128CBC.Factory(),
|
BlockCiphers.AES192CTR(),
|
||||||
new AES192CBC.Factory(),
|
BlockCiphers.AES256CBC(),
|
||||||
new AES256CBC.Factory(),
|
BlockCiphers.AES256CTR(),
|
||||||
new TripleDESCBC.Factory(),
|
BlockCiphers.BlowfishCBC(),
|
||||||
new BlowfishCBC.Factory(),
|
|
||||||
BlockCiphers.BlowfishCTR(),
|
BlockCiphers.BlowfishCTR(),
|
||||||
BlockCiphers.Cast128CBC(),
|
BlockCiphers.Cast128CBC(),
|
||||||
BlockCiphers.Cast128CTR(),
|
BlockCiphers.Cast128CTR(),
|
||||||
@@ -139,6 +169,7 @@ public class DefaultConfig
|
|||||||
BlockCiphers.Serpent192CTR(),
|
BlockCiphers.Serpent192CTR(),
|
||||||
BlockCiphers.Serpent256CBC(),
|
BlockCiphers.Serpent256CBC(),
|
||||||
BlockCiphers.Serpent256CTR(),
|
BlockCiphers.Serpent256CTR(),
|
||||||
|
BlockCiphers.TripleDESCBC(),
|
||||||
BlockCiphers.TripleDESCTR(),
|
BlockCiphers.TripleDESCTR(),
|
||||||
BlockCiphers.Twofish128CBC(),
|
BlockCiphers.Twofish128CBC(),
|
||||||
BlockCiphers.Twofish128CTR(),
|
BlockCiphers.Twofish128CTR(),
|
||||||
@@ -177,7 +208,9 @@ public class DefaultConfig
|
|||||||
|
|
||||||
protected void initSignatureFactories() {
|
protected void initSignatureFactories() {
|
||||||
setSignatureFactories(
|
setSignatureFactories(
|
||||||
new SignatureECDSA.Factory(),
|
new SignatureECDSA.Factory256(),
|
||||||
|
new SignatureECDSA.Factory384(),
|
||||||
|
new SignatureECDSA.Factory521(),
|
||||||
new SignatureRSA.Factory(),
|
new SignatureRSA.Factory(),
|
||||||
new SignatureDSA.Factory(),
|
new SignatureDSA.Factory(),
|
||||||
new SignatureEdDSA.Factory()
|
new SignatureEdDSA.Factory()
|
||||||
|
|||||||
@@ -15,10 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.Factory;
|
import net.schmizz.sshj.common.*;
|
||||||
import net.schmizz.sshj.common.LoggerFactory;
|
|
||||||
import net.schmizz.sshj.common.SSHException;
|
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
|
||||||
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.ConnectionImpl;
|
import net.schmizz.sshj.connection.ConnectionImpl;
|
||||||
@@ -41,6 +38,7 @@ import net.schmizz.sshj.transport.compression.DelayedZlibCompression;
|
|||||||
import net.schmizz.sshj.transport.compression.NoneCompression;
|
import net.schmizz.sshj.transport.compression.NoneCompression;
|
||||||
import net.schmizz.sshj.transport.compression.ZlibCompression;
|
import net.schmizz.sshj.transport.compression.ZlibCompression;
|
||||||
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
||||||
|
import net.schmizz.sshj.transport.verification.FingerprintVerifier;
|
||||||
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
||||||
import net.schmizz.sshj.userauth.UserAuth;
|
import net.schmizz.sshj.userauth.UserAuth;
|
||||||
@@ -61,6 +59,7 @@ 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.*;
|
import java.util.*;
|
||||||
@@ -128,6 +127,9 @@ public class SSHClient
|
|||||||
|
|
||||||
private final List<LocalPortForwarder> forwarders = new ArrayList<LocalPortForwarder>();
|
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());
|
||||||
@@ -168,19 +170,23 @@ public class SSHClient
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a {@link HostKeyVerifier} that will verify any host that's able to claim a host key with the given {@code
|
* Add a {@link HostKeyVerifier} that will verify any host that's able to claim a host key with the given {@code
|
||||||
* fingerprint}, e.g. {@code "4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"}
|
* fingerprint}.
|
||||||
|
*
|
||||||
|
* The fingerprint can be specified in either an MD5 colon-delimited format (16 hexadecimal octets, delimited by a colon),
|
||||||
|
* or in a Base64 encoded format for SHA-1 or SHA-256 fingerprints.
|
||||||
|
* Valid examples are:
|
||||||
|
*
|
||||||
|
* <ul><li>"SHA1:2Fo8c/96zv32xc8GZWbOGYOlRak="</li>
|
||||||
|
* <li>"SHA256:oQGbQTujGeNIgh0ONthcEpA/BHxtt3rcYY+NxXTxQjs="</li>
|
||||||
|
* <li>"MD5:d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32"</li>
|
||||||
|
* <li>"d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32"</li></ul>
|
||||||
*
|
*
|
||||||
* @param fingerprint expected fingerprint in colon-delimited format (16 octets in hex delimited by a colon)
|
* @param fingerprint expected fingerprint in colon-delimited format (16 octets in hex delimited by a colon)
|
||||||
*
|
*
|
||||||
* @see SecurityUtils#getFingerprint
|
* @see SecurityUtils#getFingerprint
|
||||||
*/
|
*/
|
||||||
public void addHostKeyVerifier(final String fingerprint) {
|
public void addHostKeyVerifier(final String fingerprint) {
|
||||||
addHostKeyVerifier(new HostKeyVerifier() {
|
addHostKeyVerifier(FingerprintVerifier.getInstance(fingerprint));
|
||||||
@Override
|
|
||||||
public boolean verify(String h, int p, PublicKey k) {
|
|
||||||
return SecurityUtils.getFingerprint(k).equals(fingerprint);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: there are way too many auth... overrides. Better API needed.
|
// FIXME: there are way too many auth... overrides. Better API needed.
|
||||||
@@ -316,7 +322,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -440,6 +446,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 +539,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
|
||||||
@@ -703,12 +723,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;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||||
@@ -241,7 +246,7 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public T putBytes(byte[] b, int off, int len) {
|
public T putBytes(byte[] b, int off, int len) {
|
||||||
return putUInt32(len - off).putRawBytes(b, off, len);
|
return putUInt32(len).putRawBytes(b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readRawBytes(byte[] buf)
|
public void readRawBytes(byte[] buf)
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -425,10 +460,13 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
|
|
||||||
public PublicKey readPublicKey()
|
public PublicKey readPublicKey()
|
||||||
throws BufferException {
|
throws BufferException {
|
||||||
|
KeyType keyType = KeyType.fromString(readString());
|
||||||
try {
|
try {
|
||||||
return KeyType.fromString(readString()).readPubKeyFromBuffer(this);
|
return keyType.readPubKeyFromBuffer(this);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
|
} catch (UnsupportedOperationException uoe) {
|
||||||
|
throw new BufferException("Could not decode keytype " + keyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,4 +94,34 @@ public class ByteArrayUtils {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] parseHex(String hex) {
|
||||||
|
if (hex == null) {
|
||||||
|
throw new IllegalArgumentException("Hex string is null");
|
||||||
|
}
|
||||||
|
if (hex.length() % 2 != 0) {
|
||||||
|
throw new IllegalArgumentException("Hex string '" + hex + "' should have even length.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] result = new byte[hex.length() / 2];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
int hi = parseHexDigit(hex.charAt(i * 2)) << 4;
|
||||||
|
int lo = parseHexDigit(hex.charAt(i * 2 + 1));
|
||||||
|
result[i] = (byte) (hi + lo);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseHexDigit(char c) {
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
if (c >= 'a' && c <= 'f') {
|
||||||
|
return c - 'a' + 10;
|
||||||
|
}
|
||||||
|
if (c >= 'A' && c <= 'F') {
|
||||||
|
return c - 'A' + 10;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Digit '" + c + "' out of bounds [0-9a-fA-F]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.hierynomus.sshj.secg.SecgUtils;
|
||||||
|
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
|
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.interfaces.ECKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.spec.ECPoint;
|
||||||
|
import java.security.spec.ECPublicKeySpec;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class ECDSAVariationsAdapter {
|
||||||
|
|
||||||
|
private final static String BASE_ALGORITHM_NAME = "ecdsa-sha2-nistp";
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(ECDSAVariationsAdapter.class);
|
||||||
|
|
||||||
|
public final static Map<String, String> SUPPORTED_CURVES = new HashMap<String, String>();
|
||||||
|
public final static Map<String, String> NIST_CURVES_NAMES = new HashMap<String, String>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
NIST_CURVES_NAMES.put("256", "p-256");
|
||||||
|
NIST_CURVES_NAMES.put("384", "p-384");
|
||||||
|
NIST_CURVES_NAMES.put("521", "p-521");
|
||||||
|
|
||||||
|
SUPPORTED_CURVES.put("256", "nistp256");
|
||||||
|
SUPPORTED_CURVES.put("384", "nistp384");
|
||||||
|
SUPPORTED_CURVES.put("521", "nistp521");
|
||||||
|
}
|
||||||
|
|
||||||
|
static PublicKey readPubKeyFromBuffer(Buffer<?> buf, String variation) throws GeneralSecurityException {
|
||||||
|
String algorithm = BASE_ALGORITHM_NAME + variation;
|
||||||
|
if (!SecurityUtils.isBouncyCastleRegistered()) {
|
||||||
|
throw new GeneralSecurityException("BouncyCastle is required to read a key of type " + algorithm);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// final String algo = buf.readString(); it has been already read
|
||||||
|
final String curveName = buf.readString();
|
||||||
|
final int keyLen = buf.readUInt32AsInt();
|
||||||
|
final byte x04 = buf.readByte(); // it must be 0x04, but don't think
|
||||||
|
// we need that check
|
||||||
|
final byte[] x = new byte[(keyLen - 1) / 2];
|
||||||
|
final byte[] y = new byte[(keyLen - 1) / 2];
|
||||||
|
buf.readRawBytes(x);
|
||||||
|
buf.readRawBytes(y);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
|
||||||
|
algorithm, curveName, keyLen, x04, Arrays.toString(x), Arrays.toString(y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SUPPORTED_CURVES.values().contains(curveName)) {
|
||||||
|
throw new GeneralSecurityException(String.format("Unknown curve %s", curveName));
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger bigX = new BigInteger(1, x);
|
||||||
|
BigInteger bigY = new BigInteger(1, y);
|
||||||
|
|
||||||
|
String name = NIST_CURVES_NAMES.get(variation);
|
||||||
|
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
|
||||||
|
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
|
||||||
|
ECPoint p = new ECPoint(bigX, bigY);
|
||||||
|
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
|
||||||
|
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
|
||||||
|
return keyFactory.generatePublic(publicKeySpec);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new GeneralSecurityException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
final ECPublicKey ecdsa = (ECPublicKey) pk;
|
||||||
|
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
|
||||||
|
|
||||||
|
buf.putString("nistp" + Integer.toString(fieldSizeFromKey(ecdsa)))
|
||||||
|
.putBytes(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isECKeyWithFieldSize(Key key, int fieldSize) {
|
||||||
|
return "ECDSA".equals(key.getAlgorithm())
|
||||||
|
&& fieldSizeFromKey((ECKey) key) == fieldSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fieldSizeFromKey(ECKey ecPublicKey) {
|
||||||
|
return ecPublicKey.getParams().getCurve().getField().getFieldSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -15,17 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import com.hierynomus.sshj.secg.SecgUtils;
|
|
||||||
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
||||||
|
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
import net.schmizz.sshj.common.Buffer.BufferException;
|
||||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
|
||||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
|
||||||
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
|
||||||
import org.bouncycastle.math.ec.ECPoint;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -34,15 +30,17 @@ import java.security.GeneralSecurityException;
|
|||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.interfaces.*;
|
import java.security.interfaces.DSAPrivateKey;
|
||||||
|
import java.security.interfaces.DSAPublicKey;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
import java.security.spec.DSAPublicKeySpec;
|
import java.security.spec.DSAPublicKeySpec;
|
||||||
import java.security.spec.RSAPublicKeySpec;
|
import java.security.spec.RSAPublicKeySpec;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
|
|
||||||
/** 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
|
||||||
@@ -60,18 +58,16 @@ 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 */
|
||||||
@@ -93,13 +89,12 @@ 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,70 +104,66 @@ public enum KeyType {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** SSH identifier for ECDSA keys */
|
/** SSH identifier for ECDSA-256 keys */
|
||||||
ECDSA("ecdsa-sha2-nistp256") {
|
ECDSA256("ecdsa-sha2-nistp256") {
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (!SecurityUtils.isBouncyCastleRegistered()) {
|
return ECDSAVariationsAdapter.readPubKeyFromBuffer(buf, "256");
|
||||||
throw new GeneralSecurityException("BouncyCastle is required to read a key of type " + sType);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// final String algo = buf.readString(); it has been already read
|
|
||||||
final String curveName = buf.readString();
|
|
||||||
final int keyLen = buf.readUInt32AsInt();
|
|
||||||
final byte x04 = buf.readByte(); // it must be 0x04, but don't think we need that check
|
|
||||||
final byte[] x = new byte[(keyLen - 1) / 2];
|
|
||||||
final byte[] y = new byte[(keyLen - 1) / 2];
|
|
||||||
buf.readRawBytes(x);
|
|
||||||
buf.readRawBytes(y);
|
|
||||||
if(log.isDebugEnabled()) {
|
|
||||||
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
|
|
||||||
sType,
|
|
||||||
curveName,
|
|
||||||
keyLen,
|
|
||||||
x04,
|
|
||||||
Arrays.toString(x),
|
|
||||||
Arrays.toString(y))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NISTP_CURVE.equals(curveName)) {
|
|
||||||
throw new GeneralSecurityException(String.format("Unknown curve %s", curveName));
|
|
||||||
}
|
|
||||||
|
|
||||||
BigInteger bigX = new BigInteger(1, x);
|
|
||||||
BigInteger bigY = new BigInteger(1, y);
|
|
||||||
|
|
||||||
X9ECParameters ecParams = NISTNamedCurves.getByName("p-256");
|
|
||||||
ECPoint pPublicPoint = ecParams.getCurve().createPoint(bigX, bigY);
|
|
||||||
ECParameterSpec spec = new ECParameterSpec(ecParams.getCurve(),
|
|
||||||
ecParams.getG(), ecParams.getN());
|
|
||||||
ECPublicKeySpec publicSpec = new ECPublicKeySpec(pPublicPoint, spec);
|
|
||||||
|
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
|
|
||||||
return keyFactory.generatePublic(publicSpec);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new GeneralSecurityException(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
final ECPublicKey ecdsa = (ECPublicKey) pk;
|
ECDSAVariationsAdapter.writePubKeyContentsIntoBuffer(pk, buf);
|
||||||
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
|
|
||||||
|
|
||||||
buf.putString(sType)
|
|
||||||
.putString(NISTP_CURVE)
|
|
||||||
.putBytes(encoded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isMyType(Key key) {
|
protected boolean isMyType(Key key) {
|
||||||
return ("ECDSA".equals(key.getAlgorithm()));
|
return ECDSAVariationsAdapter.isECKeyWithFieldSize(key, 256);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** SSH identifier for ECDSA-384 keys */
|
||||||
|
ECDSA384("ecdsa-sha2-nistp384") {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return ECDSAVariationsAdapter.readPubKeyFromBuffer(buf, "384");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
ECDSAVariationsAdapter.writePubKeyContentsIntoBuffer(pk, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isMyType(Key key) {
|
||||||
|
return ECDSAVariationsAdapter.isECKeyWithFieldSize(key, 384);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** SSH identifier for ECDSA-521 keys */
|
||||||
|
ECDSA521("ecdsa-sha2-nistp521") {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return ECDSAVariationsAdapter.readPubKeyFromBuffer(buf, "521");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
ECDSAVariationsAdapter.writePubKeyContentsIntoBuffer(pk, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isMyType(Key key) {
|
||||||
|
return ECDSAVariationsAdapter.isECKeyWithFieldSize(key, 521);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -192,7 +183,7 @@ public enum KeyType {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
|
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||||
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(p, ed25519);
|
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(p, ed25519);
|
||||||
return new Ed25519PublicKey(publicSpec);
|
return new Ed25519PublicKey(publicSpec);
|
||||||
|
|
||||||
@@ -202,9 +193,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
|
||||||
@@ -213,6 +204,44 @@ 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
|
||||||
@@ -226,15 +255,17 @@ 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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
private static final String NISTP_CURVE = "nistp256";
|
|
||||||
|
|
||||||
protected final String sType;
|
protected final String sType;
|
||||||
|
|
||||||
private KeyType(String type) {
|
private KeyType(String type) {
|
||||||
@@ -244,7 +275,11 @@ public enum KeyType {
|
|||||||
public abstract PublicKey readPubKeyFromBuffer(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 +301,129 @@ public enum KeyType {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class SSHRuntimeException
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SSHRuntimeException(Throwable cause) {
|
public SSHRuntimeException(Throwable cause) {
|
||||||
this(null, cause);
|
this(cause.getMessage(), cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import java.security.*;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.Signature;
|
||||||
|
|
||||||
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 org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
|
|
||||||
@@ -36,12 +47,17 @@ public class SecurityUtils {
|
|||||||
*/
|
*/
|
||||||
public static final String BOUNCY_CASTLE = "BC";
|
public static final String BOUNCY_CASTLE = "BC";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier for the BouncyCastle JCE provider
|
||||||
|
*/
|
||||||
|
public static final String SPONGY_CASTLE = "SC";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Security provider identifier. null = default JCE
|
* Security provider identifier. null = default JCE
|
||||||
*/
|
*/
|
||||||
private static String securityProvider = null;
|
private static String securityProvider = null;
|
||||||
|
|
||||||
// relate to BC registration
|
// relate to BC registration (or SpongyCastle on Android)
|
||||||
private static Boolean registerBouncyCastle;
|
private static Boolean registerBouncyCastle;
|
||||||
private static boolean registrationDone;
|
private static boolean registrationDone;
|
||||||
|
|
||||||
@@ -68,19 +84,21 @@ public class SecurityUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (securityProvider == null) {
|
if (securityProvider == null) {
|
||||||
MessageDigest.getInstance("MD5", provider.getName());
|
MessageDigest.getInstance("MD5", provider);
|
||||||
KeyAgreement.getInstance("DH", provider.getName());
|
KeyAgreement.getInstance("DH", provider);
|
||||||
setSecurityProvider(provider.getName());
|
setSecurityProvider(provider.getName());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
LOG.info(format("Security Provider '%s' does not support necessary algorithm", providerClassName), e);
|
LOG.info(format("Security Provider '%s' does not support necessary algorithm", providerClassName), e);
|
||||||
} catch (NoSuchProviderException e) {
|
} catch (Exception e) {
|
||||||
LOG.info("Registration of Security Provider '{}' unexpectedly failed", providerClassName);
|
LOG.info(format("Registration of Security Provider '%s' unexpectedly failed", providerClassName), e);
|
||||||
}
|
}
|
||||||
return false;
|
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();
|
||||||
@@ -221,11 +239,11 @@ public class SecurityUtils {
|
|||||||
* Attempts registering BouncyCastle as security provider if it has not been previously attempted and returns
|
* Attempts registering BouncyCastle as security provider if it has not been previously attempted and returns
|
||||||
* whether the registration succeeded.
|
* whether the registration succeeded.
|
||||||
*
|
*
|
||||||
* @return whether BC registered
|
* @return whether BC (or SC on Android) registered
|
||||||
*/
|
*/
|
||||||
public static synchronized boolean isBouncyCastleRegistered() {
|
public static synchronized boolean isBouncyCastleRegistered() {
|
||||||
register();
|
register();
|
||||||
return BOUNCY_CASTLE.equals(securityProvider);
|
return BOUNCY_CASTLE.equals(securityProvider) || SPONGY_CASTLE.equals(securityProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
|
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
|
||||||
@@ -254,7 +272,7 @@ public class SecurityUtils {
|
|||||||
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut JCE provider");
|
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut JCE provider");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
registrationDone = true;
|
||||||
}
|
}
|
||||||
registrationDone = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.LoggerFactory;
|
|
||||||
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;
|
||||||
@@ -68,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,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)
|
||||||
@@ -146,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,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);
|
||||||
@@ -142,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
|
||||||
@@ -174,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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
|
|||||||
|
|
||||||
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;
|
||||||
@@ -51,6 +52,8 @@ 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 boolean eof = false;
|
||||||
|
|
||||||
@@ -78,12 +81,16 @@ 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.loggerFactory = conn.getTransport().getConfig().getLoggerFactory();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.log = loggerFactory.getLogger(getClass());
|
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(), loggerFactory);
|
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize(), loggerFactory);
|
||||||
@@ -135,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();
|
||||||
@@ -189,7 +201,7 @@ public abstract class AbstractChannel
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
gotUnknown(msg, buf);
|
gotUnknown(msg, buf);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,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)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,6 +124,9 @@ public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
|
|||||||
*/
|
*/
|
||||||
int getRecipient();
|
int getRecipient();
|
||||||
|
|
||||||
|
/** @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.
|
* @return the maximum packet size as specified by the remote end.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ 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;
|
||||||
|
|||||||
@@ -27,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;
|
||||||
@@ -56,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);
|
||||||
@@ -69,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) {
|
||||||
@@ -91,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);
|
||||||
@@ -99,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);
|
||||||
}
|
}
|
||||||
@@ -115,7 +110,7 @@ public final class ChannelOutputStream
|
|||||||
leftOvers.clear();
|
leftOvers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferSize = leftOverBytes;
|
dataLeft = leftOverBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -140,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,8 +149,7 @@ public final class ChannelOutputStream
|
|||||||
this.error = error;
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkClose()
|
private void checkClose() throws SSHException {
|
||||||
throws SSHException {
|
|
||||||
if (closed) {
|
if (closed) {
|
||||||
if (error != null)
|
if (error != null)
|
||||||
throw error;
|
throw error;
|
||||||
@@ -163,8 +159,7 @@ public final class ChannelOutputStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close()
|
public synchronized void close() throws IOException {
|
||||||
throws IOException {
|
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
try {
|
try {
|
||||||
buffer.flush(false);
|
buffer.flush(false);
|
||||||
@@ -182,8 +177,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ public abstract class Window {
|
|||||||
super(initialWinSize, maxPacketSize, loggerFactory);
|
super(initialWinSize, maxPacketSize, loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long awaitExpansion(long was)
|
public long awaitExpansion(long was) throws ConnectionException {
|
||||||
throws ConnectionException {
|
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
while (size <= was) {
|
while (size <= was) {
|
||||||
log.debug("Waiting, need size to grow from {} bytes", was);
|
log.debug("Waiting, need size to grow from {} bytes", was);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ public class LocalPortForwarder {
|
|||||||
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, LoggerFactory loggerFactory) {
|
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket, LoggerFactory loggerFactory) {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
@@ -132,10 +133,29 @@ public class LocalPortForwarder {
|
|||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void listen()
|
public void listen() throws IOException {
|
||||||
throws IOException {
|
listen(Thread.currentThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this listener is running (ie. whether a thread is attached to it).
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isRunning() {
|
||||||
|
return this.runningThread != null && !serverSocket.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (!runningThread.isInterrupted()) {
|
||||||
try {
|
try {
|
||||||
final Socket socket = serverSocket.accept();
|
final Socket socket = serverSocket.accept();
|
||||||
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
|
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
|
||||||
@@ -162,6 +182,7 @@ public class LocalPortForwarder {
|
|||||||
if (!serverSocket.isClosed()) {
|
if (!serverSocket.isClosed()) {
|
||||||
log.info("Closing listener on {}", serverSocket.getLocalSocketAddress());
|
log.info("Closing listener on {}", serverSocket.getLocalSocketAddress());
|
||||||
serverSocket.close();
|
serverSocket.close();
|
||||||
|
runningThread.interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ 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;
|
||||||
@@ -47,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 {
|
||||||
@@ -93,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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,17 @@ 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 */
|
/**
|
||||||
|
* Logger
|
||||||
|
*/
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
@@ -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,7 +100,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class RemoteDirectory
|
|||||||
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;
|
||||||
|
|
||||||
|
|||||||
@@ -81,10 +81,8 @@ 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
|
.putString(data, off, len)
|
||||||
.putUInt32(len)
|
|
||||||
.putRawBytes(data, off, len)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,10 +192,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 +339,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;
|
||||||
|
|||||||
@@ -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),
|
||||||
@@ -122,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,7 +19,6 @@ 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;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
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.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHException;
|
import net.schmizz.sshj.common.SSHException;
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||||
@@ -25,6 +26,7 @@ import org.slf4j.Logger;
|
|||||||
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;
|
||||||
@@ -137,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);
|
||||||
}
|
}
|
||||||
@@ -155,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);
|
||||||
}
|
}
|
||||||
@@ -163,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,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)
|
||||||
@@ -192,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,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) {
|
||||||
@@ -258,20 +260,32 @@ public class SFTPEngine
|
|||||||
|
|
||||||
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();
|
||||||
|
|||||||
@@ -60,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);
|
||||||
@@ -91,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:
|
||||||
@@ -104,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");
|
||||||
|
|||||||
@@ -15,34 +15,48 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.signature;
|
package net.schmizz.sshj.signature;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Buffer;
|
||||||
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 java.security.GeneralSecurityException;
|
import java.security.*;
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
|
|
||||||
/** An abstract class for {@link Signature} that implements common functionality. */
|
/**
|
||||||
|
* An abstract class for {@link Signature} that implements common functionality.
|
||||||
|
*/
|
||||||
public abstract class AbstractSignature
|
public abstract class AbstractSignature
|
||||||
implements Signature {
|
implements Signature {
|
||||||
|
|
||||||
protected final String algorithm;
|
@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
||||||
protected java.security.Signature signature;
|
protected final java.security.Signature signature;
|
||||||
|
|
||||||
protected AbstractSignature(String algorithm) {
|
protected AbstractSignature(String algorithm) {
|
||||||
this.algorithm = algorithm;
|
try {
|
||||||
|
this.signature = SecurityUtils.getSignature(algorithm);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractSignature(@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
||||||
|
java.security.Signature signatureEngine) {
|
||||||
|
this.signature = signatureEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(PublicKey publicKey, PrivateKey privateKey) {
|
public void initVerify(PublicKey publicKey) {
|
||||||
try {
|
try {
|
||||||
signature = SecurityUtils.getSignature(algorithm);
|
signature.initVerify(publicKey);
|
||||||
if (publicKey != null)
|
} catch (InvalidKeyException e) {
|
||||||
signature.initVerify(publicKey);
|
throw new SSHRuntimeException(e);
|
||||||
if (privateKey != null)
|
}
|
||||||
signature.initSign(privateKey);
|
}
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
|
@Override
|
||||||
|
public void initSign(PrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,23 +84,24 @@ public abstract class AbstractSignature
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] extractSig(byte[] sig) {
|
/**
|
||||||
if (sig[0] == 0 && sig[1] == 0 && sig[2] == 0) {
|
* Check whether the signature is generated using the expected algorithm, and if so, return the signature blob
|
||||||
int i = 0;
|
*
|
||||||
int j = sig[i++] << 24 & 0xff000000
|
* @param sig The full signature
|
||||||
| sig[i++] << 16 & 0x00ff0000
|
* @param expectedKeyAlgorithm The expected key algorithm
|
||||||
| sig[i++] << 8 & 0x0000ff00
|
* @return The blob part of the signature
|
||||||
| sig[i++] & 0x000000ff;
|
*/
|
||||||
i += j;
|
protected byte[] extractSig(byte[] sig, String expectedKeyAlgorithm) {
|
||||||
j = sig[i++] << 24 & 0xff000000
|
Buffer.PlainBuffer buffer = new Buffer.PlainBuffer(sig);
|
||||||
| sig[i++] << 16 & 0x00ff0000
|
try {
|
||||||
| sig[i++] << 8 & 0x0000ff00
|
String algo = buffer.readString();
|
||||||
| sig[i++] & 0x000000ff;
|
if (!expectedKeyAlgorithm.equals(algo)) {
|
||||||
byte[] newSig = new byte[j];
|
throw new SSHRuntimeException("Expected '" + expectedKeyAlgorithm + "' key algorithm, but got: " + algo);
|
||||||
System.arraycopy(sig, i, newSig, 0, j);
|
}
|
||||||
sig = newSig;
|
return buffer.readBytes();
|
||||||
|
} catch (Buffer.BufferException e) {
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
return sig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,24 @@ import java.security.PublicKey;
|
|||||||
public interface Signature {
|
public interface Signature {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize this signature with the given public key and private key. If the private key is null, only signature
|
* Initialize this signature with the given public key for signature verification.
|
||||||
* verification can be performed.
|
|
||||||
*
|
*
|
||||||
* @param pubkey (null-ok) specify in case verification is needed
|
* Note that subsequent calls to either {@link #initVerify(PublicKey)} or {@link #initSign(PrivateKey)} will
|
||||||
* @param prvkey (null-ok) specify in case signing is needed
|
* overwrite prior initialization.
|
||||||
|
*
|
||||||
|
* @param pubkey the public key to use for signature verification
|
||||||
*/
|
*/
|
||||||
void init(PublicKey pubkey, PrivateKey prvkey);
|
void initVerify(PublicKey pubkey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize this signature with the given private key for signing.
|
||||||
|
*
|
||||||
|
* Note that subsequent calls to either {@link #initVerify(PublicKey)} or {@link #initSign(PrivateKey)} will
|
||||||
|
* overwrite prior initialization.
|
||||||
|
*
|
||||||
|
* @param prvkey the private key to use for signing
|
||||||
|
*/
|
||||||
|
void initSign(PrivateKey prvkey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method, same as calling {@link #update(byte[], int, int)} with offset as {@code 0} and {@code
|
* Convenience method, same as calling {@link #update(byte[], int, int)} with offset as {@code 0} and {@code
|
||||||
|
|||||||
@@ -17,14 +17,23 @@ package net.schmizz.sshj.signature;
|
|||||||
|
|
||||||
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 org.bouncycastle.asn1.*;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** DSA {@link Signature} */
|
/**
|
||||||
|
* DSA {@link Signature}
|
||||||
|
*/
|
||||||
public class SignatureDSA
|
public class SignatureDSA
|
||||||
extends AbstractSignature {
|
extends AbstractSignature {
|
||||||
|
|
||||||
/** A named factory for DSA signature */
|
/**
|
||||||
|
* A named factory for DSA signature
|
||||||
|
*/
|
||||||
public static class Factory
|
public static class Factory
|
||||||
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
|
|
||||||
@@ -74,33 +83,33 @@ public class SignatureDSA
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(byte[] sig) {
|
public boolean verify(byte[] sig) {
|
||||||
sig = extractSig(sig);
|
|
||||||
|
|
||||||
// ASN.1
|
|
||||||
int frst = (sig[0] & 0x80) != 0 ? 1 : 0;
|
|
||||||
int scnd = (sig[20] & 0x80) != 0 ? 1 : 0;
|
|
||||||
|
|
||||||
int length = sig.length + 6 + frst + scnd;
|
|
||||||
byte[] tmp = new byte[length];
|
|
||||||
tmp[0] = (byte) 0x30;
|
|
||||||
tmp[1] = (byte) 0x2c;
|
|
||||||
tmp[1] += frst;
|
|
||||||
tmp[1] += scnd;
|
|
||||||
tmp[2] = (byte) 0x02;
|
|
||||||
tmp[3] = (byte) 0x14;
|
|
||||||
tmp[3] += frst;
|
|
||||||
System.arraycopy(sig, 0, tmp, 4 + frst, 20);
|
|
||||||
tmp[4 + tmp[3]] = (byte) 0x02;
|
|
||||||
tmp[5 + tmp[3]] = (byte) 0x14;
|
|
||||||
tmp[5 + tmp[3]] += scnd;
|
|
||||||
System.arraycopy(sig, 20, tmp, 6 + tmp[3] + scnd, 20);
|
|
||||||
sig = tmp;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return signature.verify(sig);
|
byte[] sigBlob = extractSig(sig, "ssh-dss");
|
||||||
|
return signature.verify(asnEncode(sigBlob));
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the signature as a DER sequence (ASN.1 format).
|
||||||
|
*/
|
||||||
|
private byte[] asnEncode(byte[] sigBlob) throws IOException {
|
||||||
|
byte[] r = new BigInteger(1, Arrays.copyOfRange(sigBlob, 0, 20)).toByteArray();
|
||||||
|
byte[] s = new BigInteger(1, Arrays.copyOfRange(sigBlob, 20, 40)).toByteArray();
|
||||||
|
|
||||||
|
ASN1EncodableVector vector = new ASN1EncodableVector();
|
||||||
|
vector.add(new ASN1Integer(r));
|
||||||
|
vector.add(new ASN1Integer(s));
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
|
||||||
|
|
||||||
|
asnOS.writeObject(new DERSequence(vector));
|
||||||
|
asnOS.flush();
|
||||||
|
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,32 +18,69 @@ package net.schmizz.sshj.signature;
|
|||||||
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 org.bouncycastle.asn1.ASN1EncodableVector;
|
||||||
|
import org.bouncycastle.asn1.ASN1Integer;
|
||||||
|
import org.bouncycastle.asn1.ASN1OutputStream;
|
||||||
|
import org.bouncycastle.asn1.DERSequence;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
|
||||||
/** ECDSA {@link Signature} */
|
/** ECDSA {@link Signature} */
|
||||||
public class SignatureECDSA
|
public class SignatureECDSA extends AbstractSignature {
|
||||||
extends AbstractSignature {
|
|
||||||
|
|
||||||
/** A named factory for ECDSA signature */
|
/** A named factory for ECDSA-256 signature */
|
||||||
public static class Factory
|
public static class Factory256 implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Signature create() {
|
public Signature create() {
|
||||||
return new SignatureECDSA();
|
return new SignatureECDSA("SHA256withECDSA", KeyType.ECDSA256.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return KeyType.ECDSA.toString();
|
return KeyType.ECDSA256.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignatureECDSA() {
|
/** A named factory for ECDSA-384 signature */
|
||||||
super("SHA256withECDSA");
|
public static class Factory384 implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Signature create() {
|
||||||
|
return new SignatureECDSA("SHA384withECDSA", KeyType.ECDSA384.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.ECDSA384.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A named factory for ECDSA-521 signature */
|
||||||
|
public static class Factory521 implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Signature create() {
|
||||||
|
return new SignatureECDSA("SHA512withECDSA", KeyType.ECDSA521.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.ECDSA521.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String keyTypeName;
|
||||||
|
|
||||||
|
public SignatureECDSA(String algorithm, String keyTypeName) {
|
||||||
|
super(algorithm);
|
||||||
|
this.keyTypeName = keyTypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +98,7 @@ public class SignatureECDSA
|
|||||||
System.arraycopy(sig, 4, r, 0, rLen);
|
System.arraycopy(sig, 4, r, 0, rLen);
|
||||||
System.arraycopy(sig, 6 + rLen, s, 0, sLen);
|
System.arraycopy(sig, 6 + rLen, s, 0, sLen);
|
||||||
|
|
||||||
Buffer buf = new Buffer.PlainBuffer();
|
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||||
buf.putMPInt(new BigInteger(r));
|
buf.putMPInt(new BigInteger(r));
|
||||||
buf.putMPInt(new BigInteger(s));
|
buf.putMPInt(new BigInteger(s));
|
||||||
|
|
||||||
@@ -70,68 +107,34 @@ public class SignatureECDSA
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(byte[] sig) {
|
public boolean verify(byte[] sig) {
|
||||||
byte[] r;
|
|
||||||
byte[] s;
|
|
||||||
try {
|
try {
|
||||||
Buffer sigbuf = new Buffer.PlainBuffer(sig);
|
byte[] sigBlob = extractSig(sig, keyTypeName);
|
||||||
final String algo = new String(sigbuf.readBytes());
|
return signature.verify(asnEncode(sigBlob));
|
||||||
if (!"ecdsa-sha2-nistp256".equals(algo)) {
|
|
||||||
throw new SSHRuntimeException(String.format("Signature :: ecdsa-sha2-nistp256 expected, got %s", algo));
|
|
||||||
}
|
|
||||||
final int rsLen = sigbuf.readUInt32AsInt();
|
|
||||||
if (!(sigbuf.available() == rsLen)) {
|
|
||||||
throw new SSHRuntimeException("Invalid key length");
|
|
||||||
}
|
|
||||||
r = sigbuf.readBytes();
|
|
||||||
s = sigbuf.readBytes();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new SSHRuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rLen = r.length;
|
|
||||||
int sLen = s.length;
|
|
||||||
|
|
||||||
/* We can't have the high bit set, so add an extra zero at the beginning if so. */
|
|
||||||
if ((r[0] & 0x80) != 0) {
|
|
||||||
rLen++;
|
|
||||||
}
|
|
||||||
if ((s[0] & 0x80) != 0) {
|
|
||||||
sLen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate total output length */
|
|
||||||
int length = 6 + rLen + sLen;
|
|
||||||
byte[] asn1 = new byte[length];
|
|
||||||
|
|
||||||
/* ASN.1 SEQUENCE tag */
|
|
||||||
asn1[0] = (byte) 0x30;
|
|
||||||
|
|
||||||
/* Size of SEQUENCE */
|
|
||||||
asn1[1] = (byte) (4 + rLen + sLen);
|
|
||||||
|
|
||||||
/* ASN.1 INTEGER tag */
|
|
||||||
asn1[2] = (byte) 0x02;
|
|
||||||
|
|
||||||
/* "r" INTEGER length */
|
|
||||||
asn1[3] = (byte) rLen;
|
|
||||||
|
|
||||||
/* Copy in the "r" INTEGER */
|
|
||||||
System.arraycopy(r, 0, asn1, 4, rLen);
|
|
||||||
|
|
||||||
/* ASN.1 INTEGER tag */
|
|
||||||
asn1[rLen + 4] = (byte) 0x02;
|
|
||||||
|
|
||||||
/* "s" INTEGER length */
|
|
||||||
asn1[rLen + 5] = (byte) sLen;
|
|
||||||
|
|
||||||
/* Copy in the "s" INTEGER */
|
|
||||||
System.arraycopy(s, 0, asn1, (6 + rLen), sLen);
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
return signature.verify(asn1);
|
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the signature as a DER sequence (ASN.1 format).
|
||||||
|
*/
|
||||||
|
private byte[] asnEncode(byte[] sigBlob) throws IOException {
|
||||||
|
Buffer.PlainBuffer sigbuf = new Buffer.PlainBuffer(sigBlob);
|
||||||
|
byte[] r = sigbuf.readBytes();
|
||||||
|
byte[] s = sigbuf.readBytes();
|
||||||
|
|
||||||
|
ASN1EncodableVector vector = new ASN1EncodableVector();
|
||||||
|
vector.add(new ASN1Integer(r));
|
||||||
|
vector.add(new ASN1Integer(s));
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
|
||||||
|
|
||||||
|
asnOS.writeObject(new DERSequence(vector));
|
||||||
|
asnOS.flush();
|
||||||
|
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class SignatureRSA
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(byte[] sig) {
|
public boolean verify(byte[] sig) {
|
||||||
sig = extractSig(sig);
|
sig = extractSig(sig, "ssh-rsa");
|
||||||
try {
|
try {
|
||||||
return signature.verify(sig);
|
return signature.verify(sig);
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
|
|||||||
@@ -16,12 +16,10 @@
|
|||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.*;
|
import net.schmizz.sshj.common.*;
|
||||||
import net.schmizz.sshj.transport.Transport;
|
|
||||||
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
|
||||||
|
|||||||
@@ -39,19 +39,6 @@ final class Encoder
|
|||||||
log = loggerFactory.getLogger(getClass());
|
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) {
|
||||||
compression.compress(buffer);
|
compression.compress(buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import net.schmizz.sshj.transport.mac.MAC;
|
|||||||
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.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
@@ -158,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)
|
||||||
@@ -197,6 +197,12 @@ final class KeyExchanger
|
|||||||
if (hkv.verify(transport.getRemoteHost(), transport.getRemotePort(), key))
|
if (hkv.verify(transport.getRemoteHost(), transport.getRemotePort(), key))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
log.error("Disconnecting because none of the configured Host key verifiers ({}) could verify '{}' host key with fingerprint {} for {}:{}",
|
||||||
|
hostVerifiers,
|
||||||
|
KeyType.fromKey(key),
|
||||||
|
SecurityUtils.getFingerprint(key),
|
||||||
|
transport.getRemoteHost(),
|
||||||
|
transport.getRemotePort());
|
||||||
|
|
||||||
throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE,
|
throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE,
|
||||||
"Could not verify `" + KeyType.fromKey(key)
|
"Could not verify `" + KeyType.fromKey(key)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ 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, DisconnectListener {
|
implements Transport, DisconnectListener {
|
||||||
|
|
||||||
@@ -49,11 +51,11 @@ 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;
|
||||||
@@ -88,24 +90,32 @@ public final class TransportImpl
|
|||||||
|
|
||||||
private final Event<TransportException> close;
|
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 */
|
/**
|
||||||
|
* Currently active service e.g. UserAuthService, ConnectionService
|
||||||
|
*/
|
||||||
private volatile Service service;
|
private volatile Service service;
|
||||||
|
|
||||||
private DisconnectListener disconnectListener;
|
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();
|
||||||
@@ -115,9 +125,9 @@ public final class TransportImpl
|
|||||||
this.loggerFactory = config.getLoggerFactory();
|
this.loggerFactory = config.getLoggerFactory();
|
||||||
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
|
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
|
||||||
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
|
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
|
||||||
this.nullService = new NullService(this);
|
this.nullService = new NullService(this);
|
||||||
this.service = nullService;
|
this.service = nullService;
|
||||||
this.log = loggerFactory.getLogger(getClass());
|
this.log = loggerFactory.getLogger(getClass());
|
||||||
this.disconnectListener = this;
|
this.disconnectListener = this;
|
||||||
this.reader = new Reader(this);
|
this.reader = new Reader(this);
|
||||||
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock, loggerFactory);
|
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock, loggerFactory);
|
||||||
@@ -138,7 +148,7 @@ public final class TransportImpl
|
|||||||
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
|
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
|
||||||
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
|
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
|
||||||
this.log = loggerFactory.getLogger(getClass());
|
this.log = loggerFactory.getLogger(getClass());
|
||||||
this.nullService = new NullService(this);
|
this.nullService = new NullService(this);
|
||||||
this.service = nullService;
|
this.service = nullService;
|
||||||
this.disconnectListener = this;
|
this.disconnectListener = this;
|
||||||
this.reader = new Reader(this);
|
this.reader = new Reader(this);
|
||||||
@@ -186,14 +196,18 @@ public final class TransportImpl
|
|||||||
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 {
|
||||||
@@ -212,9 +226,7 @@ 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)
|
||||||
@@ -226,7 +238,7 @@ public final class TransportImpl
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -337,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)
|
||||||
@@ -456,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());
|
||||||
}
|
}
|
||||||
@@ -476,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
|
||||||
@@ -494,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,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();
|
||||||
@@ -563,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)
|
||||||
@@ -586,7 +590,7 @@ public final class TransportImpl
|
|||||||
try {
|
try {
|
||||||
if (!close.isSet()) {
|
if (!close.isSet()) {
|
||||||
|
|
||||||
log.error("Dying because - {}", ex);
|
log.error("Dying because - {}", ex.getMessage(), ex);
|
||||||
|
|
||||||
final SSHException causeOfDeath = SSHException.chainer.chain(ex);
|
final SSHException causeOfDeath = SSHException.chainer.chain(ex);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code aes128-cbc} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code aes128-cbc} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#AES128CBC()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AES128CBC
|
public class AES128CBC
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class AES128CBC
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "aes128-cbc";
|
return "aes128-cbc";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AES128CBC() {
|
public AES128CBC() {
|
||||||
|
|||||||
@@ -15,11 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code aes128-ctr} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code aes128-ctr} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#AES128CTR()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AES128CTR
|
public class AES128CTR
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
/** Named factory for AES128CBC Cipher */
|
/** Named factory for AES128CTR Cipher */
|
||||||
public static class Factory
|
public static class Factory
|
||||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class AES128CTR
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "aes128-ctr";
|
return "aes128-ctr";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AES128CTR() {
|
public AES128CTR() {
|
||||||
|
|||||||
@@ -15,7 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code aes192-cbc} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code aes192-cbc} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#AES192CBC()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AES192CBC
|
public class AES192CBC
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class AES192CBC
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "aes192-cbc";
|
return "aes192-cbc";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AES192CBC() {
|
public AES192CBC() {
|
||||||
|
|||||||
@@ -15,7 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code aes192-ctr} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code aes192-ctr} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#AES192CTR()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AES192CTR
|
public class AES192CTR
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class AES192CTR
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "aes192-ctr";
|
return "aes192-ctr";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AES192CTR() {
|
public AES192CTR() {
|
||||||
|
|||||||
@@ -15,7 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code aes256-ctr} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code aes256-cbc} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#AES256CBC()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AES256CBC
|
public class AES256CBC
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class AES256CBC
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "aes256-cbc";
|
return "aes256-cbc";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AES256CBC() {
|
public AES256CBC() {
|
||||||
|
|||||||
@@ -15,11 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code aes256-ctr} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code aes256-ctr} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#AES256CTR()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AES256CTR
|
public class AES256CTR
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
/** Named factory for AES256CBC Cipher */
|
/** Named factory for AES256CTR Cipher */
|
||||||
public static class Factory
|
public static class Factory
|
||||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class AES256CTR
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "aes256-ctr";
|
return "aes256-ctr";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AES256CTR() {
|
public AES256CTR() {
|
||||||
|
|||||||
@@ -15,7 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code blowfish-ctr} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code blowfish-bcb} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#BlowfishCBC()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class BlowfishCBC
|
public class BlowfishCBC
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class BlowfishCBC
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "blowfish-cbc";
|
return "blowfish-cbc";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlowfishCBC() {
|
public BlowfishCBC() {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.cipher;
|
package net.schmizz.sshj.transport.cipher;
|
||||||
|
|
||||||
/** {@code 3des-cbc} cipher */
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code 3des-cbc} cipher
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link BlockCiphers#TripleDESCBC()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class TripleDESCBC
|
public class TripleDESCBC
|
||||||
extends BlockCipher {
|
extends BlockCipher {
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ public class TripleDESCBC
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "3des-cbc";
|
return "3des-cbc";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TripleDESCBC() {
|
public TripleDESCBC() {
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.compression;
|
package net.schmizz.sshj.transport.compression;
|
||||||
|
|
||||||
|
import com.jcraft.jzlib.Deflater;
|
||||||
|
import com.jcraft.jzlib.GZIPException;
|
||||||
|
import com.jcraft.jzlib.Inflater;
|
||||||
import com.jcraft.jzlib.JZlib;
|
import com.jcraft.jzlib.JZlib;
|
||||||
import com.jcraft.jzlib.ZStream;
|
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.Buffer;
|
||||||
import net.schmizz.sshj.common.DisconnectReason;
|
import net.schmizz.sshj.common.DisconnectReason;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
@@ -45,20 +47,24 @@ public class ZlibCompression
|
|||||||
|
|
||||||
private final byte[] tempBuf = new byte[BUF_SIZE];
|
private final byte[] tempBuf = new byte[BUF_SIZE];
|
||||||
|
|
||||||
private ZStream stream;
|
private Deflater deflater;
|
||||||
|
private Inflater inflater;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Mode mode) {
|
public void init(Mode mode) {
|
||||||
stream = new ZStream();
|
try {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case DEFLATE:
|
case DEFLATE:
|
||||||
stream.deflateInit(JZlib.Z_DEFAULT_COMPRESSION);
|
deflater = new Deflater(JZlib.Z_DEFAULT_COMPRESSION);
|
||||||
break;
|
break;
|
||||||
case INFLATE:
|
case INFLATE:
|
||||||
stream.inflateInit();
|
inflater = new Inflater();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert false;
|
assert false;
|
||||||
|
}
|
||||||
|
} catch (GZIPException gze) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,44 +75,43 @@ public class ZlibCompression
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compress(Buffer buffer) {
|
public void compress(Buffer buffer) {
|
||||||
stream.next_in = buffer.array();
|
deflater.setNextIn(buffer.array());
|
||||||
stream.next_in_index = buffer.rpos();
|
deflater.setNextInIndex(buffer.rpos());
|
||||||
stream.avail_in = buffer.available();
|
deflater.setAvailIn(buffer.available());
|
||||||
buffer.wpos(buffer.rpos());
|
buffer.wpos(buffer.rpos());
|
||||||
do {
|
do {
|
||||||
stream.next_out = tempBuf;
|
deflater.setNextOut(tempBuf);
|
||||||
stream.next_out_index = 0;
|
deflater.setNextOutIndex(0);
|
||||||
stream.avail_out = BUF_SIZE;
|
deflater.setAvailOut(BUF_SIZE);
|
||||||
final int status = stream.deflate(JZlib.Z_PARTIAL_FLUSH);
|
final int status = deflater.deflate(JZlib.Z_PARTIAL_FLUSH);
|
||||||
if (status == JZlib.Z_OK) {
|
if (status == JZlib.Z_OK) {
|
||||||
buffer.putRawBytes(tempBuf, 0, BUF_SIZE - stream.avail_out);
|
buffer.putRawBytes(tempBuf, 0, BUF_SIZE - deflater.getAvailOut());
|
||||||
} else {
|
} else {
|
||||||
throw new SSHRuntimeException("compress: deflate returned " + status);
|
throw new SSHRuntimeException("compress: deflate returned " + status);
|
||||||
}
|
}
|
||||||
} while (stream.avail_out == 0);
|
} while (deflater.getAvailOut() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uncompress(Buffer from, Buffer to)
|
public void uncompress(Buffer from, Buffer to)
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
stream.next_in = from.array();
|
inflater.setNextIn(from.array());
|
||||||
stream.next_in_index = from.rpos();
|
inflater.setNextInIndex(from.rpos());
|
||||||
stream.avail_in = from.available();
|
inflater.setAvailIn(from.available());
|
||||||
while (true) {
|
while (true) {
|
||||||
stream.next_out = tempBuf;
|
inflater.setNextOut(tempBuf);
|
||||||
stream.next_out_index = 0;
|
inflater.setNextOutIndex(0);
|
||||||
stream.avail_out = BUF_SIZE;
|
inflater.setAvailOut(BUF_SIZE);
|
||||||
final int status = stream.inflate(JZlib.Z_PARTIAL_FLUSH);
|
final int status = inflater.inflate(JZlib.Z_PARTIAL_FLUSH);
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case JZlib.Z_OK:
|
case JZlib.Z_OK:
|
||||||
to.putRawBytes(tempBuf, 0, BUF_SIZE - stream.avail_out);
|
to.putRawBytes(tempBuf, 0, BUF_SIZE - inflater.getAvailOut());
|
||||||
break;
|
break;
|
||||||
case JZlib.Z_BUF_ERROR:
|
case JZlib.Z_BUF_ERROR:
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
throw new TransportException(DisconnectReason.COMPRESSION_ERROR, "uncompress: inflate returned "
|
throw new TransportException(DisconnectReason.COMPRESSION_ERROR, "uncompress: inflate returned " + status);
|
||||||
+ status);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public abstract class AbstractDHG extends AbstractDH
|
|||||||
|
|
||||||
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
||||||
KeyType.fromKey(hostKey).toString());
|
KeyType.fromKey(hostKey).toString());
|
||||||
signature.init(hostKey, null);
|
signature.initVerify(hostKey);
|
||||||
signature.update(H, 0, H.length);
|
signature.update(H, 0, H.length);
|
||||||
if (!signature.verify(sig))
|
if (!signature.verify(sig))
|
||||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED,
|
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED,
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public abstract class AbstractDHGex extends AbstractDH {
|
|||||||
H = digest.digest();
|
H = digest.digest();
|
||||||
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
||||||
KeyType.fromKey(hostKey).toString());
|
KeyType.fromKey(hostKey).toString());
|
||||||
signature.init(hostKey, null);
|
signature.initVerify(hostKey);
|
||||||
signature.update(H, 0, H.length);
|
signature.update(H, 0, H.length);
|
||||||
if (!signature.verify(sig))
|
if (!signature.verify(sig))
|
||||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED,
|
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED,
|
||||||
@@ -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,19 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.transport.random.Random;
|
||||||
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 java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class Curve25519DH extends DHBase {
|
public class Curve25519DH extends DHBase {
|
||||||
|
|
||||||
|
|
||||||
private byte[] secretKey;
|
private byte[] secretKey;
|
||||||
|
|
||||||
public Curve25519DH() {
|
public Curve25519DH() {
|
||||||
@@ -42,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);
|
||||||
|
|||||||
@@ -16,14 +16,10 @@
|
|||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
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> {
|
||||||
@@ -45,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;
|
||||||
@@ -39,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user