mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fedf8c410c | ||
|
|
8b0d1ca03c | ||
|
|
91105e6a07 | ||
|
|
4e802cec86 | ||
|
|
dfdc464e08 | ||
|
|
fa7c40cc66 | ||
|
|
b1be9258b4 | ||
|
|
11543b2c00 | ||
|
|
3526694558 | ||
|
|
d618156ede | ||
|
|
98063680bc | ||
|
|
17754a65fe | ||
|
|
2bb52fcf7d | ||
|
|
1a70023e2d | ||
|
|
5e25c017bf | ||
|
|
27a5039831 | ||
|
|
c2d25a9d62 | ||
|
|
2a22809de2 | ||
|
|
9d1f6d9d83 | ||
|
|
4542d94440 | ||
|
|
46a0cbac9e | ||
|
|
f470ddf219 | ||
|
|
d09276fe01 | ||
|
|
241c355e20 | ||
|
|
56ef6c1223 | ||
|
|
989fb8cde6 | ||
|
|
d10a33ec59 | ||
|
|
327a4c4c5b | ||
|
|
d5c045defd | ||
|
|
02b70ef427 | ||
|
|
fdf08ef3c9 | ||
|
|
633b42fec8 | ||
|
|
3c594d9a1c | ||
|
|
c2b9c0266d | ||
|
|
0e784dd171 | ||
|
|
f322a4b060 | ||
|
|
0cd19284ee | ||
|
|
a5017d55c8 | ||
|
|
2f7b181306 | ||
|
|
20223d3614 | ||
|
|
cac340dd43 | ||
|
|
00cd335f47 | ||
|
|
e14fb2f695 | ||
|
|
b0dee02bf9 | ||
|
|
17c09eb471 | ||
|
|
0301d4537f | ||
|
|
f71d34e106 | ||
|
|
254f739ac1 | ||
|
|
aa201fa08c | ||
|
|
8721269d0f | ||
|
|
971ccf6273 | ||
|
|
813469646e | ||
|
|
17c368f9c2 | ||
|
|
4de9f8ab9f | ||
|
|
deff097170 | ||
|
|
7556a7f6f6 | ||
|
|
c5792fe4a8 | ||
|
|
a96fbfcf2f | ||
|
|
15e6924fc4 | ||
|
|
9e8bef24c5 |
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
* @hierynomus
|
||||||
|
|
||||||
55
.github/workflows/gradle.yml
vendored
Normal file
55
.github/workflows/gradle.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# This workflow will build a Java project with Gradle
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||||
|
|
||||||
|
name: Build SSHJ
|
||||||
|
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
java12:
|
||||||
|
name: Build with Java 12
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 12
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 12
|
||||||
|
- name: Grant execute permission for gradlew
|
||||||
|
run: chmod +x gradlew
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew check
|
||||||
|
# java10:
|
||||||
|
# name: Build with Java 10
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v2
|
||||||
|
# - name: Set up JDK 10
|
||||||
|
# uses: actions/setup-java@v1
|
||||||
|
# with:
|
||||||
|
# java-version: 10
|
||||||
|
# - name: Grant execute permission for gradlew
|
||||||
|
# run: chmod +x gradlew
|
||||||
|
# - name: Build with Gradle
|
||||||
|
# run: ./gradlew check -xanimalsnifferMain -xanimalsnifferTest
|
||||||
|
|
||||||
|
integration:
|
||||||
|
name: Integration test
|
||||||
|
needs: [java12]
|
||||||
|
runs-on: [ubuntu-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||||
|
- uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 12
|
||||||
|
- name: Grant execute permission for gradlew
|
||||||
|
run: chmod +x gradlew
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew integrationTest
|
||||||
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,6 +13,7 @@
|
|||||||
out/
|
out/
|
||||||
target/
|
target/
|
||||||
classes/
|
classes/
|
||||||
|
bin/
|
||||||
build/
|
build/
|
||||||
docs/
|
docs/
|
||||||
.gradle/
|
.gradle/
|
||||||
|
|||||||
1
.java-version
Normal file
1
.java-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
9
|
||||||
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
|||||||
language: java
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
jdk:
|
|
||||||
- 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
|
|
||||||
2
NOTICE
2
NOTICE
@@ -15,7 +15,7 @@ The Apache Software Foundation (http://www.apache.org/):
|
|||||||
== in this case for the SSHD distribution. ==
|
== in this case for the SSHD distribution. ==
|
||||||
=========================================================================
|
=========================================================================
|
||||||
|
|
||||||
This product contains software developped by JCraft,Inc. and subject to
|
This product contains software developed by JCraft,Inc. and subject to
|
||||||
the following license:
|
the following license:
|
||||||
|
|
||||||
Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc.
|
Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc.
|
||||||
|
|||||||
36
README.adoc
36
README.adoc
@@ -1,7 +1,7 @@
|
|||||||
= 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.26.0
|
:sshj_version: 0.29.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://api.bintray.com/packages/hierynomus/maven/sshj/images/download.svg[link="https://bintray.com/hierynomus/maven/sshj/_latestVersion"]
|
||||||
@@ -10,7 +10,6 @@ image:https://api.codacy.com/project/badge/Grade/14a0a316bb9149739b5ea26dbfa8da8
|
|||||||
image:https://codecov.io/gh/hierynomus/sshj/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/hierynomus/sshj"]
|
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: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"]
|
|
||||||
|
|
||||||
To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :)
|
To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :)
|
||||||
|
|
||||||
@@ -73,7 +72,8 @@ key exchange::
|
|||||||
`diffie-hellman-group14-sha256`, `diffie-hellman-group15-sha512`, `diffie-hellman-group16-sha512`, `diffie-hellman-group17-sha512`, `diffie-hellman-group18-sha512`
|
`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`,
|
`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:
|
|
||||||
|
SSHJ also supports the following extended (non official) key exchange algorithms:
|
||||||
`diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`,
|
`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`
|
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
|
||||||
|
|
||||||
@@ -81,7 +81,8 @@ signatures::
|
|||||||
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `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-ripemd160`
|
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`, `hmac-ripemd160`, `hmac-ripemd160@openssh.com`
|
||||||
|
`hmac-md5-etm@openssh.com`, `hmac-md5-96-etm@openssh.com`, `hmac-sha1-etm@openssh.com`, `hmac-sha1-96-etm@openssh.com`, `hmac-sha2-256-etm@openssh.com`, `hmac-sha2-512-etm@openssh.com`, `hmac-ripemd160-etm@openssh.com`
|
||||||
|
|
||||||
compression::
|
compression::
|
||||||
`zlib` and `zlib@openssh.com` (delayed zlib)
|
`zlib` and `zlib@openssh.com` (delayed zlib)
|
||||||
@@ -100,16 +101,31 @@ Java 6+. http://www.slf4j.org/download.html[slf4j] is required. http://www.bounc
|
|||||||
== Reporting bugs
|
== Reporting bugs
|
||||||
Issue tracker: https://github.com/hierynomus/sshj/issues
|
Issue tracker: https://github.com/hierynomus/sshj/issues
|
||||||
|
|
||||||
== Discussion
|
|
||||||
Google Group: http://groups.google.com/group/sshj-users
|
|
||||||
|
|
||||||
== Contributing
|
== Contributing
|
||||||
Fork away!
|
Fork away!
|
||||||
|
|
||||||
== Release history
|
== Release history
|
||||||
SSHJ 0.24.0 (2018-??-??)::
|
SSHJ 0.27.0 (2019-01-24)::
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/415[#415]: Fixed wrongly prefixed '/' to path in SFTPClient.mkdirs
|
||||||
|
* Added support for ETM (Encrypt-then-Mac) MAC algorithms.
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/454[#454]: Added missing capacity check for Buffer.putUint64
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/466[#466]: Added lock timeout for remote action to prevent hanging
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/470[#470]: Made EdDSA the default (first) signature factory
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/467[#467]: Added AES256-CBC as cipher mode in openssh-key-v1 support
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/464[#464]: Enabled curve25519-sha256@openssh.org in DefaultConfig
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/472[#472]: Handle server initiated global requests
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/485[#485]: Added support for all keytypes to openssh-key-v1 keyfiles.
|
||||||
|
SSHJ 0.26.0 (2018-07-24)::
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/413[#413]: Use UTF-8 for PrivateKeyFileResource
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/427[#427]: Support encrypted ed25519 openssh-key-v1 files
|
||||||
|
* Upgraded BouncyCastle to 1.60
|
||||||
|
* Added support for hmac-ripemd160@openssh.com MAC
|
||||||
|
SSHJ 0.24.0 (2018-04-04)::
|
||||||
* Added support for hmac-ripemd160
|
* Added support for hmac-ripemd160
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/382[#382]: Fixed escaping in WildcardHostmatcher
|
||||||
|
* Added integration testsuite using Docker against OpenSSH
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/187[#187]: Fixed length bug in Buffer.putString
|
||||||
|
* Fixed https://github.com/hierynomus/sshj/issues/405[#405]: Continue host verification if first hostkey does not match.
|
||||||
SSHJ 0.23.0 (2017-10-13)::
|
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'
|
* 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/355[#355] and https://github.com/hierynomus/sshj/issues/354[#354]: Correctly decode signature bytes
|
||||||
@@ -136,7 +152,7 @@ SSHJ 0.19.1 (2016-12-30)::
|
|||||||
* Enabled PKCS5 Key files in DefaultConfig
|
* 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/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
|
* 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/292[#292]: Pass the configured RandomFactory to Diffie Hellman KEX
|
||||||
* Fixed https://github.com/hierynomus/sshj/issues/256[#256]: SSHJ now builds if no git repository present
|
* 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()
|
* LocalPortForwarder now correctly interrupts its own thread on close()
|
||||||
SSHJ 0.19.0 (2016-11-25)::
|
SSHJ 0.19.0 (2016-11-25)::
|
||||||
|
|||||||
72
build.gradle
72
build.gradle
@@ -6,16 +6,15 @@ plugins {
|
|||||||
id "java"
|
id "java"
|
||||||
id "groovy"
|
id "groovy"
|
||||||
id "jacoco"
|
id "jacoco"
|
||||||
id "osgi"
|
id "com.github.blindpirate.osgi" version '0.0.3'
|
||||||
id "maven-publish"
|
id "maven-publish"
|
||||||
id 'pl.allegro.tech.build.axion-release' version '1.9.2'
|
id 'pl.allegro.tech.build.axion-release' version '1.11.0'
|
||||||
id "com.bmuschko.docker-remote-api" version "3.2.1"
|
id "com.bmuschko.docker-remote-api" version "6.4.0"
|
||||||
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.8.5"
|
||||||
id 'ru.vyarus.java-lib' version '1.0.5'
|
id 'ru.vyarus.java-lib' version '1.0.5'
|
||||||
// id 'ru.vyarus.pom' version '1.0.3'
|
// 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"
|
||||||
@@ -37,35 +36,30 @@ defaultTasks "build"
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
|
||||||
url "https://dl.bintray.com/mockito/maven/"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sourceCompatibility = 1.6
|
|
||||||
targetCompatibility = 1.6
|
|
||||||
|
|
||||||
configurations.compile.transitive = false
|
configurations.compile.transitive = false
|
||||||
|
|
||||||
def bouncycastleVersion = "1.60"
|
def bouncycastleVersion = "1.65"
|
||||||
|
def sshdVersion = "2.1.0"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
implementation "org.slf4j:slf4j-api:1.7.7"
|
||||||
|
implementation "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
||||||
|
implementation "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
||||||
|
implementation "com.jcraft:jzlib:1.1.3"
|
||||||
|
|
||||||
compile "org.slf4j:slf4j-api:1.7.7"
|
implementation "net.i2p.crypto:eddsa:0.3.0"
|
||||||
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
|
||||||
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
|
||||||
compile "com.jcraft:jzlib:1.1.3"
|
|
||||||
|
|
||||||
compile "net.i2p.crypto:eddsa:0.2.0"
|
testImplementation "junit:junit:4.12"
|
||||||
|
testImplementation 'org.spockframework:spock-core:1.3-groovy-2.4'
|
||||||
testCompile "junit:junit:4.11"
|
testImplementation "org.mockito:mockito-core:2.28.2"
|
||||||
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
testImplementation "org.apache.sshd:sshd-core:$sshdVersion"
|
||||||
testCompile "org.mockito:mockito-core:2.9.2"
|
testImplementation "org.apache.sshd:sshd-sftp:$sshdVersion"
|
||||||
testCompile "org.apache.sshd:sshd-core:1.2.0"
|
testImplementation "org.apache.sshd:sshd-scp:$sshdVersion"
|
||||||
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
testRuntimeOnly "ch.qos.logback:logback-classic:1.2.3"
|
||||||
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
testImplementation 'org.glassfish.grizzly:grizzly-http-server:2.4.4'
|
||||||
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
|
testImplementation 'org.apache.httpcomponents:httpclient:4.5.9'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +72,10 @@ license {
|
|||||||
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java', '**/org/mindrot/jbcrypt/*.java'])
|
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java', '**/org/mindrot/jbcrypt/*.java'])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!JavaVersion.current().isJava9Compatible()) {
|
||||||
|
throw new GradleScriptException("Minimum compilation version is Java 9")
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -85,6 +83,11 @@ if (JavaVersion.current().isJava8Compatible()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.compilerArgs.addAll(['--release', '7'])
|
||||||
|
}
|
||||||
|
|
||||||
task writeSshjVersionProperties {
|
task writeSshjVersionProperties {
|
||||||
doLast {
|
doLast {
|
||||||
project.file("${project.buildDir}/resources/main").mkdirs()
|
project.file("${project.buildDir}/resources/main").mkdirs()
|
||||||
@@ -129,8 +132,8 @@ sourcesJar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestImplementation.extendsFrom testImplementation
|
||||||
integrationTestRuntime.extendsFrom testRuntime
|
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -223,7 +226,7 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey
|
|||||||
publications = ["maven"]
|
publications = ["maven"]
|
||||||
pkg {
|
pkg {
|
||||||
repo = "maven"
|
repo = "maven"
|
||||||
name = project.name
|
name = "${project.name}"
|
||||||
licenses = ["Apache-2.0"]
|
licenses = ["Apache-2.0"]
|
||||||
vcsUrl = "https://github.com/hierynomus/sshj.git"
|
vcsUrl = "https://github.com/hierynomus/sshj.git"
|
||||||
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
|
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
|
||||||
@@ -257,22 +260,23 @@ jacocoTestReport {
|
|||||||
|
|
||||||
task buildItestImage(type: DockerBuildImage) {
|
task buildItestImage(type: DockerBuildImage) {
|
||||||
inputDir = file('src/itest/docker-image')
|
inputDir = file('src/itest/docker-image')
|
||||||
tag = 'sshj/sshd-itest'
|
images.add('sshj/sshd-itest:latest')
|
||||||
}
|
}
|
||||||
|
|
||||||
task createItestContainer(type: DockerCreateContainer) {
|
task createItestContainer(type: DockerCreateContainer) {
|
||||||
dependsOn buildItestImage
|
dependsOn buildItestImage
|
||||||
targetImageId { buildItestImage.getImageId() }
|
targetImageId buildItestImage.getImageId()
|
||||||
portBindings = ['2222:22']
|
hostConfig.portBindings = ['2222:22']
|
||||||
|
hostConfig.autoRemove = true
|
||||||
}
|
}
|
||||||
|
|
||||||
task startItestContainer(type: DockerStartContainer) {
|
task startItestContainer(type: DockerStartContainer) {
|
||||||
dependsOn createItestContainer
|
dependsOn createItestContainer
|
||||||
targetContainerId { createItestContainer.getContainerId() }
|
targetContainerId createItestContainer.getContainerId()
|
||||||
}
|
}
|
||||||
|
|
||||||
task stopItestContainer(type: DockerStopContainer) {
|
task stopItestContainer(type: DockerStopContainer) {
|
||||||
targetContainerId { createItestContainer.getContainerId() }
|
targetContainerId createItestContainer.getContainerId()
|
||||||
}
|
}
|
||||||
|
|
||||||
task forkedUploadRelease(type: GradleBuild) {
|
task forkedUploadRelease(type: GradleBuild) {
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package net.schmizz.sshj.examples;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
|
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||||
|
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
||||||
|
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** This examples demonstrates how to configure {@link net.schmizz.sshj.SSHClient} client with an in-memory known_hosts file */
|
||||||
|
public class InMemoryKnownHosts {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
InputStream entry = new ByteArrayInputStream("localhost ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPmhSBtMctNa4hsZt8QGlsYSE5/gMkjeand69Vj4ir13".getBytes(Charset.defaultCharset()));
|
||||||
|
SSHClient ssh = new SSHClient();
|
||||||
|
ssh.addHostKeyVerifier(new InMemoryHostKeyVerifier(entry, Charset.defaultCharset()));
|
||||||
|
ssh.connect("localhost");
|
||||||
|
try {
|
||||||
|
ssh.authPublickey(System.getProperty("user.name"));
|
||||||
|
ssh.newSCPFileTransfer().download("test_file", new FileSystemFile("/tmp/"));
|
||||||
|
} finally {
|
||||||
|
ssh.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InMemoryHostKeyVerifier implements HostKeyVerifier {
|
||||||
|
|
||||||
|
private final List<OpenSSHKnownHosts.KnownHostEntry> entries = new ArrayList<OpenSSHKnownHosts.KnownHostEntry>();
|
||||||
|
|
||||||
|
public InMemoryHostKeyVerifier(InputStream inputStream, Charset charset) throws IOException {
|
||||||
|
final OpenSSHKnownHosts.EntryFactory entryFactory = new OpenSSHKnownHosts.EntryFactory();
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset));
|
||||||
|
while(reader.ready()) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
try {
|
||||||
|
OpenSSHKnownHosts.KnownHostEntry entry = entryFactory.parseEntry(line);
|
||||||
|
if (entry != null) {
|
||||||
|
entries.add(entry);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
//log error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(String hostname, int port, PublicKey key) {
|
||||||
|
final KeyType type = KeyType.fromKey(key);
|
||||||
|
if (type == KeyType.UNKNOWN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (OpenSSHKnownHosts.KnownHostEntry e : entries) {
|
||||||
|
try {
|
||||||
|
if (e.appliesTo(type, hostname) && e.verify(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
//log error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
50
examples/src/main/java/net/schmizz/sshj/examples/Jump.java
Normal file
50
examples/src/main/java/net/schmizz/sshj/examples/Jump.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package net.schmizz.sshj.examples;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
import net.schmizz.sshj.connection.channel.direct.DirectConnection;
|
||||||
|
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This example demonstrates connecting via an intermediate "jump" server using a direct TCP/IP channel.
|
||||||
|
*/
|
||||||
|
public class Jump {
|
||||||
|
public static void main(String... args)
|
||||||
|
throws IOException {
|
||||||
|
SSHClient firstHop = new SSHClient();
|
||||||
|
|
||||||
|
firstHop.loadKnownHosts();
|
||||||
|
|
||||||
|
firstHop.connect("localhost");
|
||||||
|
try {
|
||||||
|
|
||||||
|
firstHop.authPublickey(System.getProperty("user.name"));
|
||||||
|
|
||||||
|
DirectConnection tunnel = firstHop.newDirectConnection("localhost", 22);
|
||||||
|
|
||||||
|
SSHClient ssh = new SSHClient();
|
||||||
|
try {
|
||||||
|
ssh.loadKnownHosts();
|
||||||
|
ssh.connectVia(tunnel);
|
||||||
|
ssh.authPublickey(System.getProperty("user.name"));
|
||||||
|
|
||||||
|
final Session session = ssh.startSession();
|
||||||
|
try {
|
||||||
|
final Session.Command cmd = session.exec("ping -c 1 google.com");
|
||||||
|
System.out.println(IOUtils.readFully(cmd.getInputStream()).toString());
|
||||||
|
cmd.join(5, TimeUnit.SECONDS);
|
||||||
|
System.out.println("\n** exit status: " + cmd.getExitStatus());
|
||||||
|
} finally {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
ssh.disconnect();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
firstHop.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
|||||||
#Fri Mar 18 11:26:35 CET 2016
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip
|
|
||||||
|
|||||||
111
gradlew
vendored
111
gradlew
vendored
@@ -1,4 +1,20 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# https://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.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
@@ -6,12 +22,30 @@
|
|||||||
##
|
##
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Attempt to set APP_HOME
|
||||||
DEFAULT_JVM_OPTS=""
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
|
|
||||||
@@ -30,6 +64,7 @@ die ( ) {
|
|||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
case "`uname`" in
|
case "`uname`" in
|
||||||
CYGWIN* )
|
CYGWIN* )
|
||||||
cygwin=true
|
cygwin=true
|
||||||
@@ -40,31 +75,11 @@ case "`uname`" in
|
|||||||
MINGW* )
|
MINGW* )
|
||||||
msys=true
|
msys=true
|
||||||
;;
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
|
||||||
if $cygwin ; then
|
|
||||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
|
||||||
# Resolve links: $0 may be a link
|
|
||||||
PRG="$0"
|
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
|
||||||
ls=`ls -ld "$PRG"`
|
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
|
||||||
PRG="$link"
|
|
||||||
else
|
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >&-
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >&-
|
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
@@ -90,7 +105,7 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
if [ $? -eq 0 ] ; then
|
if [ $? -eq 0 ] ; then
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
@@ -110,10 +125,11 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
@@ -138,27 +154,30 @@ if $cygwin ; then
|
|||||||
else
|
else
|
||||||
eval `echo args$i`="\"$arg\""
|
eval `echo args$i`="\"$arg\""
|
||||||
fi
|
fi
|
||||||
i=$((i+1))
|
i=`expr $i + 1`
|
||||||
done
|
done
|
||||||
case $i in
|
case $i in
|
||||||
(0) set -- ;;
|
0) set -- ;;
|
||||||
(1) set -- "$args0" ;;
|
1) set -- "$args0" ;;
|
||||||
(2) set -- "$args0" "$args1" ;;
|
2) set -- "$args0" "$args1" ;;
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
# Escape application args
|
||||||
function splitJvmOpts() {
|
save () {
|
||||||
JVM_OPTS=("$@")
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
}
|
}
|
||||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
APP_ARGS=`save "$@"`
|
||||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
|
||||||
|
|
||||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
33
gradlew.bat
vendored
33
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@@ -8,14 +24,17 @@
|
|||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables with windows NT shell
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
set DEFAULT_JVM_OPTS=
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
@@ -46,10 +65,9 @@ echo location of your Java installation.
|
|||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
:init
|
||||||
@rem Get command-line arguments, handling Windowz variants
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
|
||||||
|
|
||||||
:win9xME_args
|
:win9xME_args
|
||||||
@rem Slurp the command line arguments.
|
@rem Slurp the command line arguments.
|
||||||
@@ -60,11 +78,6 @@ set _SKIP=2
|
|||||||
if "x%~1" == "x" goto execute
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
set CMD_LINE_ARGS=%*
|
||||||
goto execute
|
|
||||||
|
|
||||||
:4NT_args
|
|
||||||
@rem Get arguments from the 4NT Shell from JP Software
|
|
||||||
set CMD_LINE_ARGS=%$
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
FROM sickp/alpine-sshd:7.5
|
FROM sickp/alpine-sshd:7.5-r2
|
||||||
|
|
||||||
ADD id_rsa.pub /home/sshj/.ssh/authorized_keys
|
ADD authorized_keys /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 /etc/ssh/ssh_host_ecdsa_key
|
||||||
ADD test-container/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ecdsa_key.pub
|
ADD test-container/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ecdsa_key.pub
|
||||||
|
ADD test-container/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key
|
||||||
|
ADD test-container/ssh_host_ed25519_key.pub /etc/ssh/ssh_host_ed25519_key.pub
|
||||||
ADD test-container/sshd_config /etc/ssh/sshd_config
|
ADD test-container/sshd_config /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
RUN apk add --no-cache tini
|
||||||
RUN \
|
RUN \
|
||||||
echo "root:smile" | chpasswd && \
|
echo "root:smile" | chpasswd && \
|
||||||
adduser -D -s /bin/ash sshj && \
|
adduser -D -s /bin/ash sshj && \
|
||||||
@@ -13,5 +16,8 @@ RUN \
|
|||||||
chmod 600 /home/sshj/.ssh/authorized_keys && \
|
chmod 600 /home/sshj/.ssh/authorized_keys && \
|
||||||
chmod 600 /etc/ssh/ssh_host_ecdsa_key && \
|
chmod 600 /etc/ssh/ssh_host_ecdsa_key && \
|
||||||
chmod 644 /etc/ssh/ssh_host_ecdsa_key.pub && \
|
chmod 644 /etc/ssh/ssh_host_ecdsa_key.pub && \
|
||||||
|
chmod 600 /etc/ssh/ssh_host_ed25519_key && \
|
||||||
|
chmod 644 /etc/ssh/ssh_host_ed25519_key.pub && \
|
||||||
chown -R sshj:sshj /home/sshj
|
chown -R sshj:sshj /home/sshj
|
||||||
|
|
||||||
|
ENTRYPOINT ["/sbin/tini", "/entrypoint.sh"]
|
||||||
7
src/itest/docker-image/authorized_keys
Normal file
7
src/itest/docker-image/authorized_keys
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOEQcvowiV3igdRO7rKPrZrao1hCQrnC4tgsxqSJdQCbABI+vHrdbJRfWZNuSk48aAtARJzJVmkn/r63EPJgkh8= root@itgcpkerberosstack-cbgateway-0-20151117031915
|
||||||
|
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHQiZm0wBbmI8gohA/N9ir1O+egikL6S9FjZS8GHbx4rTHI1V+vbXxx2O9bFWtep1PFb4iowtZkxf6gvRjGkL6M= ajvanerp@Heimdall.local
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDAdJiRkkBM8yC8seTEoAn2PfwbLKrkcahZ0xxPoWICJ root@sshj
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8ww4hJG/gHJYdkjTTBDF1GNz+228nuWprPV+NbQauA ajvanerp@Heimdall.local
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOaWrwt3drIOjeBq2LSHRavxAT7ja2f+5soOUJl/zKSI ajvanerp@Heimdall.xebialabs.com
|
||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQmTIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmuhA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPCliztspKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmLGGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQ== no-passphrase
|
||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKRyZAtOJJfAhPU6xE6ZXY564vwErAI3n3Yn4lTHL9bxev9Ily6eCqPLcV0WbSV04pztngFn9MjT7yb8mcXheHpIaWEH569sMpmpOtyfn4p68SceuXBGyyPGMIcfOTknkASd1JYSD4EPkd9rZmCzcx3vEnLu8ChnA/G221xSVQ5VC/jD/c/CgNUayhQ+xbn57qHKKtZwfTa21QmwIabGYJNwlVjlKTCdddeVnZfKqKrG7cxHQApsxd21rhM9IT/C/f4Y/Tx3WUUVeam0iZ265oiPHoPALqJIWSQIUheRYAxYAQqJwSQ0Or9MM8XXun2Iy3RUSGk6eIvrCsFbNURsHNs7Pu0UnpYv6FZ3vCkFep/1pAT6fQvY7pDOOWDHKXArD4watc9gIWaQBH73wDW/KgBcnMRSoGWgQjsYqIamP4oV1+HqUI3lRAsXZaX+eiBGt3+3A5KebP27UJ1YUwhwlzs7wzTKaCu0OaL+hOsP1F2AxAa995bgFksMd23645ux3YCJKXG4sGpJ1Z/Hs49K72gv+QjLZVxXqY623c8+3OUhlixqoEFd4iG7UMc5a552ch/VA+jaspmLZoFhPz99aBRVb1oCSPxSwLw+Q/wxv6pZmT+14rqTzY2farjU53hM+CsUPh7dnWXhGG7RuA5wCdeOXOYjuksfzAoHIZhPqTgQ== ajvanerp@Heimdall.local
|
||||||
@@ -1 +0,0 @@
|
|||||||
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQmTIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmuhA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPCliztspKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmLGGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQ== no-passphrase
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||||
|
QyNTUxOQAAACBFG9PKAq8FtH0me+LHUE6YaVANCMqy/Znkffzief1W/gAAAKCyyoBkssqA
|
||||||
|
ZAAAAAtzc2gtZWQyNTUxOQAAACBFG9PKAq8FtH0me+LHUE6YaVANCMqy/Znkffzief1W/g
|
||||||
|
AAAED+Yfza2xk5LqP9pN6TpvhWYP0L60zOQJpHhbEuiS3LLkUb08oCrwW0fSZ74sdQTphp
|
||||||
|
UA0IyrL9meR9/OJ5/Vb+AAAAF2FqdmFuZXJwQEhlaW1kYWxsLmxvY2FsAQIDBAUG
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEUb08oCrwW0fSZ74sdQTphpUA0IyrL9meR9/OJ5/Vb+ ajvanerp@Heimdall.local
|
||||||
@@ -128,5 +128,5 @@ Subsystem sftp /usr/lib/ssh/sftp-server
|
|||||||
# PermitTTY no
|
# PermitTTY no
|
||||||
# ForceCommand cvs server
|
# ForceCommand cvs server
|
||||||
|
|
||||||
|
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1
|
||||||
macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-ripemd160-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com
|
macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-ripemd160-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import spock.lang.Specification
|
|||||||
class IntegrationBaseSpec extends Specification {
|
class IntegrationBaseSpec extends Specification {
|
||||||
protected static final int DOCKER_PORT = 2222
|
protected static final int DOCKER_PORT = 2222
|
||||||
protected static final String USERNAME = "sshj"
|
protected static final String USERNAME = "sshj"
|
||||||
protected static final String KEYFILE = "src/test/resources/id_rsa"
|
protected static final String KEYFILE = "src/itest/resources/keyfiles/id_rsa"
|
||||||
protected final static String SERVER_IP = System.getProperty("serverIP", "127.0.0.1")
|
protected final static String SERVER_IP = System.getProperty("serverIP", "127.0.0.1")
|
||||||
|
|
||||||
protected static SSHClient getConnectedClient(Config config) {
|
protected static SSHClient getConnectedClient(Config config) {
|
||||||
|
|||||||
@@ -15,23 +15,36 @@
|
|||||||
*/
|
*/
|
||||||
package com.hierynomus.sshj
|
package com.hierynomus.sshj
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.ECDSAKeyAlgorithm
|
||||||
|
import com.hierynomus.sshj.key.EdDSAKeyAlgorithm
|
||||||
|
import com.hierynomus.sshj.signature.SignatureEdDSA
|
||||||
import net.schmizz.sshj.DefaultConfig
|
import net.schmizz.sshj.DefaultConfig
|
||||||
import net.schmizz.sshj.SSHClient
|
import net.schmizz.sshj.SSHClient
|
||||||
|
import net.schmizz.sshj.signature.SignatureECDSA
|
||||||
import net.schmizz.sshj.transport.TransportException
|
import net.schmizz.sshj.transport.TransportException
|
||||||
import net.schmizz.sshj.userauth.UserAuthException
|
import net.schmizz.sshj.userauth.UserAuthException
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
class IntegrationSpec extends IntegrationBaseSpec {
|
class IntegrationSpec extends IntegrationBaseSpec {
|
||||||
|
|
||||||
def "should accept correct key"() {
|
@Unroll
|
||||||
|
def "should accept correct key for #signatureName"() {
|
||||||
given:
|
given:
|
||||||
SSHClient sshClient = new SSHClient(new DefaultConfig())
|
def config = 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
|
config.setKeyAlgorithms(Collections.singletonList(signatureFactory))
|
||||||
|
SSHClient sshClient = new SSHClient(config)
|
||||||
|
sshClient.addHostKeyVerifier(fingerprint) // test-containers/ssh_host_ecdsa_key's fingerprint
|
||||||
|
|
||||||
when:
|
when:
|
||||||
sshClient.connect(SERVER_IP, DOCKER_PORT)
|
sshClient.connect(SERVER_IP, DOCKER_PORT)
|
||||||
|
|
||||||
then:
|
then:
|
||||||
sshClient.isConnected()
|
sshClient.isConnected()
|
||||||
|
|
||||||
|
where:
|
||||||
|
signatureFactory << [new ECDSAKeyAlgorithm.Factory256(), new EdDSAKeyAlgorithm.Factory()]
|
||||||
|
fingerprint << ["d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3", "dc:68:38:ce:fc:6f:2c:d6:6d:6b:34:eb:5c:f0:41:6a"]
|
||||||
|
signatureName = signatureFactory.getName()
|
||||||
}
|
}
|
||||||
|
|
||||||
def "should decline wrong key"() throws IOException {
|
def "should decline wrong key"() throws IOException {
|
||||||
@@ -46,15 +59,27 @@ class IntegrationSpec extends IntegrationBaseSpec {
|
|||||||
thrown(TransportException.class)
|
thrown(TransportException.class)
|
||||||
}
|
}
|
||||||
|
|
||||||
def "should authenticate"() {
|
@Unroll
|
||||||
|
def "should authenticate with key #key"() {
|
||||||
given:
|
given:
|
||||||
SSHClient client = getConnectedClient()
|
SSHClient client = getConnectedClient()
|
||||||
|
|
||||||
when:
|
when:
|
||||||
client.authPublickey(USERNAME, KEYFILE)
|
def keyProvider = passphrase != null ? client.loadKeys("src/itest/resources/keyfiles/$key", passphrase) : client.loadKeys("src/itest/resources/keyfiles/$key")
|
||||||
|
client.authPublickey(USERNAME, keyProvider)
|
||||||
|
|
||||||
then:
|
then:
|
||||||
client.isAuthenticated()
|
client.isAuthenticated()
|
||||||
|
|
||||||
|
where:
|
||||||
|
key | passphrase
|
||||||
|
// "id_ecdsa_nistp256" | null // TODO: Need to improve PKCS8 key support.
|
||||||
|
"id_ecdsa_opensshv1" | null
|
||||||
|
"id_ed25519_opensshv1" | null
|
||||||
|
"id_ed25519_opensshv1_aes256cbc.pem" | "foobar"
|
||||||
|
"id_ed25519_opensshv1_protected" | "sshjtest"
|
||||||
|
"id_rsa" | null
|
||||||
|
"id_rsa_opensshv1" | null
|
||||||
}
|
}
|
||||||
|
|
||||||
def "should not authenticate with wrong key"() {
|
def "should not authenticate with wrong key"() {
|
||||||
@@ -62,7 +87,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
|
|||||||
SSHClient client = getConnectedClient()
|
SSHClient client = getConnectedClient()
|
||||||
|
|
||||||
when:
|
when:
|
||||||
client.authPublickey("sshj", "src/test/resources/id_dsa")
|
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_unknown_key")
|
||||||
|
|
||||||
then:
|
then:
|
||||||
thrown(UserAuthException.class)
|
thrown(UserAuthException.class)
|
||||||
|
|||||||
@@ -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.signature
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||||
|
import com.hierynomus.sshj.key.RSAKeyAlgorithm
|
||||||
|
import net.schmizz.sshj.DefaultConfig
|
||||||
|
import net.schmizz.sshj.signature.SignatureRSA
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
class SignatureSpec extends IntegrationBaseSpec {
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "should correctly connect with #sig Signature"() {
|
||||||
|
given:
|
||||||
|
def cfg = new DefaultConfig()
|
||||||
|
cfg.setKeyAlgorithms(Collections.singletonList(sigFactory))
|
||||||
|
def client = getConnectedClient(cfg)
|
||||||
|
|
||||||
|
when:
|
||||||
|
client.authPublickey(USERNAME, KEYFILE)
|
||||||
|
|
||||||
|
then:
|
||||||
|
client.authenticated
|
||||||
|
|
||||||
|
where:
|
||||||
|
sigFactory << [new RSAKeyAlgorithm.FactorySSHRSA(), new RSAKeyAlgorithm.FactoryRSASHA256(), new RSAKeyAlgorithm.FactoryRSASHA512()]
|
||||||
|
sig = sigFactory.name
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.hierynomus.sshj.IntegrationBaseSpec
|
||||||
|
import com.hierynomus.sshj.transport.mac.Macs
|
||||||
|
import net.schmizz.sshj.DefaultConfig
|
||||||
|
import net.schmizz.sshj.transport.kex.Curve25519DH
|
||||||
|
import net.schmizz.sshj.transport.kex.Curve25519SHA256
|
||||||
|
import net.schmizz.sshj.transport.kex.DH
|
||||||
|
import net.schmizz.sshj.transport.kex.DHGexSHA1
|
||||||
|
import net.schmizz.sshj.transport.kex.DHGexSHA256
|
||||||
|
import net.schmizz.sshj.transport.kex.ECDH
|
||||||
|
import net.schmizz.sshj.transport.kex.ECDHNistP
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
class KexSpec extends IntegrationBaseSpec {
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "should correctly connect with #kex Key Exchange"() {
|
||||||
|
given:
|
||||||
|
def cfg = new DefaultConfig()
|
||||||
|
cfg.setKeyExchangeFactories(kexFactory)
|
||||||
|
def client = getConnectedClient(cfg)
|
||||||
|
|
||||||
|
when:
|
||||||
|
client.authPublickey(USERNAME, KEYFILE)
|
||||||
|
|
||||||
|
then:
|
||||||
|
client.authenticated
|
||||||
|
|
||||||
|
where:
|
||||||
|
kexFactory << [DHGroups.Group1SHA1(),
|
||||||
|
DHGroups.Group14SHA1(),
|
||||||
|
DHGroups.Group14SHA256(),
|
||||||
|
DHGroups.Group16SHA512(),
|
||||||
|
DHGroups.Group18SHA512(),
|
||||||
|
new DHGexSHA1.Factory(),
|
||||||
|
new DHGexSHA256.Factory(),
|
||||||
|
new Curve25519SHA256.Factory(),
|
||||||
|
new Curve25519SHA256.FactoryLibSsh(),
|
||||||
|
new ECDHNistP.Factory256(),
|
||||||
|
new ECDHNistP.Factory384(),
|
||||||
|
new ECDHNistP.Factory521()]
|
||||||
|
kex = kexFactory.name
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,8 +17,6 @@ package com.hierynomus.sshj.transport.mac
|
|||||||
|
|
||||||
import com.hierynomus.sshj.IntegrationBaseSpec
|
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||||
import net.schmizz.sshj.DefaultConfig
|
import net.schmizz.sshj.DefaultConfig
|
||||||
import net.schmizz.sshj.transport.mac.HMACRIPEMD160
|
|
||||||
import net.schmizz.sshj.transport.mac.HMACSHA2256
|
|
||||||
import spock.lang.Unroll
|
import spock.lang.Unroll
|
||||||
|
|
||||||
class MacSpec extends IntegrationBaseSpec {
|
class MacSpec extends IntegrationBaseSpec {
|
||||||
@@ -36,8 +34,32 @@ class MacSpec extends IntegrationBaseSpec {
|
|||||||
then:
|
then:
|
||||||
client.authenticated
|
client.authenticated
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
client.disconnect()
|
||||||
|
|
||||||
where:
|
where:
|
||||||
macFactory << [Macs.HMACRIPEMD160(), Macs.HMACRIPEMD160OpenSsh(), Macs.HMACSHA2256(), Macs.HMACSHA2512()]
|
macFactory << [Macs.HMACRIPEMD160(), Macs.HMACRIPEMD160OpenSsh(), Macs.HMACSHA2256(), Macs.HMACSHA2512()]
|
||||||
mac = macFactory.name
|
mac = macFactory.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "should correctly connect with Encrypt-Then-Mac #mac MAC"() {
|
||||||
|
given:
|
||||||
|
def cfg = new DefaultConfig()
|
||||||
|
cfg.setMACFactories(macFactory)
|
||||||
|
def client = getConnectedClient(cfg)
|
||||||
|
|
||||||
|
when:
|
||||||
|
client.authPublickey(USERNAME, KEYFILE)
|
||||||
|
|
||||||
|
then:
|
||||||
|
client.authenticated
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
client.disconnect()
|
||||||
|
|
||||||
|
where:
|
||||||
|
macFactory << [Macs.HMACRIPEMD160Etm(), Macs.HMACSHA2256Etm(), Macs.HMACSHA2512Etm()]
|
||||||
|
mac = macFactory.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/itest/resources/keyfiles/id_ecdsa_nistp256
Normal file
5
src/itest/resources/keyfiles/id_ecdsa_nistp256
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIJUMlsSlXqCZmCjlN4kV7hzP+p9pu0fwJ8r4m1qle58SoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAE4RBy+jCJXeKB1E7uso+tmtqjWEJCucLi2CzGpIl1AJsAEj68et1s
|
||||||
|
lF9Zk25KTjxoC0BEnMlWaSf+vrcQ8mCSHw==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
9
src/itest/resources/keyfiles/id_ecdsa_opensshv1
Normal file
9
src/itest/resources/keyfiles/id_ecdsa_opensshv1
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
|
||||||
|
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQR0ImZtMAW5iPIKIQPzfYq9TvnoIpC+
|
||||||
|
kvRY2UvBh28eK0xyNVfr218cdjvWxVrXqdTxW+IqMLWZMX+oL0YxpC+jAAAAsD+6Oow/uj
|
||||||
|
qMAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHQiZm0wBbmI8goh
|
||||||
|
A/N9ir1O+egikL6S9FjZS8GHbx4rTHI1V+vbXxx2O9bFWtep1PFb4iowtZkxf6gvRjGkL6
|
||||||
|
MAAAAgXNC11pInVAOd3xNphiHMoISeitf6h1IKbDM+niLrL5kAAAAXYWp2YW5lcnBASGVp
|
||||||
|
bWRhbGwubG9jYWwB
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
7
src/itest/resources/keyfiles/id_ed25519_opensshv1
Normal file
7
src/itest/resources/keyfiles/id_ed25519_opensshv1
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||||
|
QyNTUxOQAAACAwHSYkZJATPMgvLHkxKAJ9j38Gyyq5HGoWdMcT6FiAiQAAAJDimgR84poE
|
||||||
|
fAAAAAtzc2gtZWQyNTUxOQAAACAwHSYkZJATPMgvLHkxKAJ9j38Gyyq5HGoWdMcT6FiAiQ
|
||||||
|
AAAECmsckQycWnfGQK6XtQpaMGODbAkMQOdJNK6XJSipB7dDAdJiRkkBM8yC8seTEoAn2P
|
||||||
|
fwbLKrkcahZ0xxPoWICJAAAACXJvb3RAc3NoagECAwQ=
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABBLQVXV9f
|
||||||
|
Wpw8AL9RTpAr//AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIJ8ww4hJG/gHJYdk
|
||||||
|
jTTBDF1GNz+228nuWprPV+NbQauAAAAAoGHEO7x3fSRBohvrIR52U4XD3uqRnhrPYm01k1
|
||||||
|
f4HHNNv46m92Zw6JKIB9Trrvp0sdMI8MVb79bN45rbn6mvpABtWl6T5TOTyMnKzDfAOx9c
|
||||||
|
FTaasWFmgtgkXOsu5pLrYBAQgCHWbzjjz6KoV1DmD4SAn9Ojf9Oh+YdAEKZcsvklgpu+Kj
|
||||||
|
nzN/DR0jt7Nzep2kNCLAS24QEkvQeATVSDiL8=
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB/aWL0WG
|
||||||
|
iYPOTxGlFwvaCNAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIOaWrwt3drIOjeBq
|
||||||
|
2LSHRavxAT7ja2f+5soOUJl/zKSIAAAAsKplAiFbOhzcOJYFYBYm8sqYbvhPF8jKdQFkbo
|
||||||
|
LAOeq+vQ0YBV9XUWQQM2tmL+RPjykPJZ2thcHLpVp3PfUEgo4bImCt939b3Ji3cEwD3QuK
|
||||||
|
MIhjhx1KvSJNF/uhjwPJnttwHG+ld8F5Gv7LpTOUmOzXKGLIgYRuwonhs5ezdNv5ERs+Cq
|
||||||
|
M9p/SW5ehL5KPJhGa5a+ZQXRojwEH7J4Q5xztH1gviTdIEpFWWQBH8rX6y
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
27
src/itest/resources/keyfiles/id_rsa
Normal file
27
src/itest/resources/keyfiles/id_rsa
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEoAIBAAKCAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQm
|
||||||
|
TIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6
|
||||||
|
RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmu
|
||||||
|
hA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPClizt
|
||||||
|
spKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmL
|
||||||
|
GGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQIBIwKCAQBXvO4uJlbrLJQDPYAt
|
||||||
|
1i1ybGcF+rrR/Q34a2dgCpZDEwFiDTlcv1hxx689OXTf5cMPXGDZXX5udd9p7Wxa
|
||||||
|
NqnIrvVUtQWLdqcZuEeO+gitHr8IyMJf5Lm8C/u5vl1PYOYhO0qxwmrTP1u6fZPh
|
||||||
|
zWX2X1p5626/sy+TCisCRDeLRyes+Dtfs3bDjUq+zF3D/DmeYY55LUx0XS27uXNS
|
||||||
|
QuUDMSnymFyj4o+jPK0q/j5w4bB+0rbsij+EP7S//jOFrSEcZgBhhIj0rHA5fo6w
|
||||||
|
NrgtgRKD3HKFBM3b4VM8TdMbHsmf+nT9DjiDqcs+IxXMGlb1XTjtQFIN2eyRtNLd
|
||||||
|
eQ0bAoGBAMwgv3rGytRjVbR4TT77eF81svzitOJWRdfXuKB5gqM3jsPR08f1MEtZ
|
||||||
|
44kaI5ByJ3mBDt/EwNgLRdmBddPrLu3so9VLdRmWKI+KNGxwkcxzJv1xXdicgw+w
|
||||||
|
S5WgigJryuUbtdylXQTlRArLUKsXULk/MndhGiD+a4fZ3dUtktF9AoGBAMqxh6tr
|
||||||
|
S0ao0rN4hc9I92gwPubD1+XQr9TJQEEqGv3g5O3BdfDrTvizfaeNArahrIqyO5SK
|
||||||
|
7iDg0xcHqdxmVmmCJ8CkIWBPXLU6erQ1QNlBJmnzYn5lR0Ksx2h/myjeXztvJKEM
|
||||||
|
q4xUjAEzWjmwxxU3Y6l3FokvgIU4kOVoE4JdAoGARfyZa+xizHnUPeAae/5yaclE
|
||||||
|
rnmdGma43En2KGQsyj7vHpEVaSDdW6nKWuRj9wKRMPkMafpQvxnOzjsD06EXZ4RV
|
||||||
|
bbN4mw7pVcj8B+wUuyAqoAmchMfya8dqXy+6SfkSXS4Sd4knNODkIPVAOqjoegcJ
|
||||||
|
/QtZamXbuYyGkjuCy3sCgYBLSUEFJ9ohjymwX/cvvAQfYmCBmTLu9b2mzmhSt94j
|
||||||
|
yI+Lgl8BtnxrANbmdjQ1NLxuB6+61IRVWtIP3kZnzj1aY4sbqq1PqHLkOkrVOFnq
|
||||||
|
S2YKGJJMND8KIur67ZFm84J1KUgej61uow9uKQRBUEnx8AByJOsdAwPZteys+sVq
|
||||||
|
7wKBgAc3BL6ttDVYlL8U8fgvTCGuIajItvOQQj1n8RKyRLblYXBKAtY+pbULzmF+
|
||||||
|
HscRgJMcwEIosEbTzzBNEVQm6dS6R/Q534C00Fpsw1z/PFTI8AOdUzTROGjuN8Fg
|
||||||
|
YZoqMQLhk/PB8V4l7yJmPrE971RmJBBDlLDt6hZwOYEI2yF4
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
49
src/itest/resources/keyfiles/id_rsa_opensshv1
Normal file
49
src/itest/resources/keyfiles/id_rsa_opensshv1
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
|
||||||
|
NhAAAAAwEAAQAAAgEAykcmQLTiSXwIT1OsROmV2OeuL8BKwCN592J+JUxy/W8Xr/SJcung
|
||||||
|
qjy3FdFm0ldOKc7Z4BZ/TI0+8m/JnF4Xh6SGlhB+evbDKZqTrcn5+KevEnHrlwRssjxjCH
|
||||||
|
Hzk5J5AEndSWEg+BD5Hfa2Zgs3Md7xJy7vAoZwPxtttcUlUOVQv4w/3PwoDVGsoUPsW5+e
|
||||||
|
6hyirWcH02ttUJsCGmxmCTcJVY5SkwnXXXlZ2Xyqiqxu3MR0AKbMXdta4TPSE/wv3+GP08
|
||||||
|
d1lFFXmptImduuaIjx6DwC6iSFkkCFIXkWAMWAEKicEkNDq/TDPF17p9iMt0VEhpOniL6w
|
||||||
|
rBWzVEbBzbOz7tFJ6WL+hWd7wpBXqf9aQE+n0L2O6QzjlgxylwKw+MGrXPYCFmkAR+98A1
|
||||||
|
vyoAXJzEUqBloEI7GKiGpj+KFdfh6lCN5UQLF2Wl/nogRrd/twOSnmz9u1CdWFMIcJc7O8
|
||||||
|
M0ymgrtDmi/oTrD9RdgMQGvfeW4BZLDHdt+uObsd2AiSlxuLBqSdWfx7OPSu9oL/kIy2Vc
|
||||||
|
V6mOtt3PPtzlIZYsaqBBXeIhu1DHOWuednIf1QPo2rKZi2aBYT8/fWgUVW9aAkj8UsC8Pk
|
||||||
|
P8Mb+qWZk/teK6k82Nn2q41Od4TPgrFD4e3Z1l4Rhu0bgOcAnXjlzmI7pLH8wKByGYT6k4
|
||||||
|
EAAAdQ3L5mFty+ZhYAAAAHc3NoLXJzYQAAAgEAykcmQLTiSXwIT1OsROmV2OeuL8BKwCN5
|
||||||
|
92J+JUxy/W8Xr/SJcungqjy3FdFm0ldOKc7Z4BZ/TI0+8m/JnF4Xh6SGlhB+evbDKZqTrc
|
||||||
|
n5+KevEnHrlwRssjxjCHHzk5J5AEndSWEg+BD5Hfa2Zgs3Md7xJy7vAoZwPxtttcUlUOVQ
|
||||||
|
v4w/3PwoDVGsoUPsW5+e6hyirWcH02ttUJsCGmxmCTcJVY5SkwnXXXlZ2Xyqiqxu3MR0AK
|
||||||
|
bMXdta4TPSE/wv3+GP08d1lFFXmptImduuaIjx6DwC6iSFkkCFIXkWAMWAEKicEkNDq/TD
|
||||||
|
PF17p9iMt0VEhpOniL6wrBWzVEbBzbOz7tFJ6WL+hWd7wpBXqf9aQE+n0L2O6Qzjlgxylw
|
||||||
|
Kw+MGrXPYCFmkAR+98A1vyoAXJzEUqBloEI7GKiGpj+KFdfh6lCN5UQLF2Wl/nogRrd/tw
|
||||||
|
OSnmz9u1CdWFMIcJc7O8M0ymgrtDmi/oTrD9RdgMQGvfeW4BZLDHdt+uObsd2AiSlxuLBq
|
||||||
|
SdWfx7OPSu9oL/kIy2VcV6mOtt3PPtzlIZYsaqBBXeIhu1DHOWuednIf1QPo2rKZi2aBYT
|
||||||
|
8/fWgUVW9aAkj8UsC8PkP8Mb+qWZk/teK6k82Nn2q41Od4TPgrFD4e3Z1l4Rhu0bgOcAnX
|
||||||
|
jlzmI7pLH8wKByGYT6k4EAAAADAQABAAACAEeOg+nAE40LY6UsZHS8bVYeH3ClBcySwELT
|
||||||
|
hOyM7uDYu/hy+Wy9b8zJTbtaKJWgbPY9RrYPP1lFXk9FXH0EjC5f9XyAuT2mrcO5+yQvn0
|
||||||
|
5ng3dy9XSnDAzBcAc8yH4cAtInTzD2O0OGPZpr/Hp83Tm3NHg4EjVCedLZUSZMZ7cGaFpa
|
||||||
|
svzp9wE/M2KZNLP087K+Do5pNEuGZVVugH/4eOApqBOsFWoOwTFADJjzkSEdftp6ZM8WMp
|
||||||
|
XBU5T3UAnh3M3GbartlJqza9o1tKk5Ham9SFZvZFiQMvBaAr6kpzP+qh86hnuvb/EU1Tw1
|
||||||
|
ldj6skzjJCq3cTzeuIEn7BiUL1qEECjpjx7OG6L9/lWyOy27nU/tiQ1MCUDMs/u/Kpnm8X
|
||||||
|
1kvEYzq1iEQVjqEaKHQBA35GB5krXzVLK2XNMYzZDM4+bGbffX04t4CpVbJHY7mFrbO584
|
||||||
|
JlqsCxKvhDDJpNuWhT4HUrAMPCJRvFC57n12+wjLrDsBKMOGRwo1NqnaR75QOR5gtboav+
|
||||||
|
6P/o35U/gATyiePDF3HD/213gJcOwRXpH9oFleRStqiU2OsfcULlrjD6gIXCAQOOrt4l15
|
||||||
|
uEh8fnUdFbgmFfuTapKHHm6nVGs6K0JWpqlqlsiwsJxSBwRhRFO3G/iAsmxKDvWaq1yBIJ
|
||||||
|
yhDRTeA7fyCw8k+wsBAAABAGeNiRwkVyn/iUA61drLC5Y/0Gd+a540VrLMwyV3LGlDZPH3
|
||||||
|
RQFHH+HldhLqmp2ikHZWFq36vjTDr/ubCuwQNlJo4TAo5/RQk1/ChBqXj2IdT+vBysH+bK
|
||||||
|
IuZQoWXsfISMfQ7o+F5vv7WdItS9w44HpXayH12Q8D1Qr4Qnt0CeMIhrrV7MPsGVTIOpOU
|
||||||
|
FxH4xu9ovBWDnyloC4rWkBmeAzLCFtO1V1iGN7Six/OXvnxnbif+BsfdQt+OxHIYBOue5G
|
||||||
|
+Dkss+1xR8l8xrZsOpN2uY1QFIaE6UyElFleAEhtYL2vvuXTrL3EJKqRtIcWenL/wxYlkt
|
||||||
|
X1CJQS02JW+PtNUAAAEBAPWFstL1hWK4Fp5ipJSGSkDNvGGuzamAYEgqr6n5Zzb1R1HPyE
|
||||||
|
x6uEMB7ELQjOG4FENIQYBBnBRnMOWWFJp0V5UjFKDft1FabLiozqBtLCRnHnIGllFIWJK+
|
||||||
|
u/h9OL4OWXGUJx2Em4XdJBPqp0g56VI237AsnTbTGS0tGLOErLWbQY7npZeBFct/501RTP
|
||||||
|
M5i7F0QEDLjEDZbDxvCz8a5tjfvyP1awK2SyluiE4VPeYr5Op1JNPTJMz5U3YFsIZxdZHJ
|
||||||
|
AK5mX8hNzTHpTApkS7o0DvExn5DVB8OHOQFdc+BjBIqQwa953f3FaAw9V3o6Dt1zXe9OJR
|
||||||
|
tBUiBeurvDFk0AAAEBANLpAv9NDf3c8k0PAYhwu+SWoo0OclOWQSZes/93UeB0ch57LD+v
|
||||||
|
KVPR3hw2AzAsgZn/PcMbIC3SPLNnAlNftfTa98avQOEfmYqrH499zVPuMb7fieS/eQZ4LF
|
||||||
|
rsZ0o+W4CDVmzImgOe1hENWcfSeUKajEwpgtj440mJlBls37G/zHMMe5DkA2TAxKldiHOR
|
||||||
|
fmHOtFKT3fnKFa6PWbwlLFnWIod4zheWrwV7E1coBhh+CA5SlgQANRFs7J8zxPznOtIK2O
|
||||||
|
cF2+/92cM0TijlBk8u5jnN44cUmk4V1nYboCVb0JD2B7yF9ZYP6IB03jt5GEZSfHHCrZP8
|
||||||
|
vCxMmXDxtAUAAAAXYWp2YW5lcnBASGVpbWRhbGwubG9jYWwBAgME
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
15
src/itest/resources/keyfiles/id_unknown_key
Normal file
15
src/itest/resources/keyfiles/id_unknown_key
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN DSA PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: DES-EDE3-CBC,9B6744BB12A8EA8F
|
||||||
|
|
||||||
|
pzZw5s3zDVHYdejZxdTpaRx00Yd1grbJe6mIJGvZRB0Jm0hKXoOX71PUI814mc5+
|
||||||
|
a5pzbyO98aciL/Eat5m3P692WQ0yOPMuphRnklsM3s4qrCjp2aRRbWvbyV/QV9bp
|
||||||
|
Xz2yYvNqU3WJC3UJIaFFMvRo/lC/Wsz9OvHSSl3LnsXXhiOCeaE32etoOYdlk9ro
|
||||||
|
N9NqDdaw28t9//iiHhuQK4afK6TZkU6DatFljJHILCC416Xh9+DDK9E+CDKzmlcw
|
||||||
|
jSwtzgFKEhgrT0XKoZR9LJZDolT1YpFy7M3cFRYIuYvJfuLcjxVEldJE900QlaJS
|
||||||
|
ybb6RxV6SRVwQYXTbIClcXes+oNJMv59DivAfajxECQC5sAynW/FnY1sz0igmz6D
|
||||||
|
scclJuJIbawqiuV/Lv6bvgzMa/ZXL4b9JeJPuQELa7tCpvj4fpNk1IiftYISlwoT
|
||||||
|
iG5pL8yLhPL4/fxGnKJzUPCA9mbwiloW2cAZZxTd+Utqmhemcl9gF0JGNR2XeYwS
|
||||||
|
3obEjbkqMF0WR3AcVZU9B5d9SKUaAzTp4vu5yZtNVEIaiVlnI3hMwWMs2Jgahswo
|
||||||
|
QF9MCPsRYsxLs7/u4a4qoQ==
|
||||||
|
-----END DSA PRIVATE KEY-----
|
||||||
31
src/main/java/com/hierynomus/sshj/common/KeyAlgorithm.java
Normal file
31
src/main/java/com/hierynomus/sshj/common/KeyAlgorithm.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
public class KeyAlgorithm {
|
||||||
|
|
||||||
|
public static final String RSA = "RSA";
|
||||||
|
public static final String DSA = "DSA";
|
||||||
|
|
||||||
|
/** Elliptic curve signature key algorithm for use with BouncyCastle **/
|
||||||
|
public static final String ECDSA = "ECDSA";
|
||||||
|
|
||||||
|
/** General elliptic curve algorithm identifier for use with BouncyCastle **/
|
||||||
|
public static final String EC_BC = "EC";
|
||||||
|
|
||||||
|
/** General elliptic curve algorithm identifier for use with the Android Keystore **/
|
||||||
|
public static final String EC_KEYSTORE = "EC";
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.common;
|
||||||
|
|
||||||
|
import org.bouncycastle.openssl.EncryptionException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a key file could not be decrypted correctly, e.g. if its checkInts differed in the case of an OpenSSH
|
||||||
|
* key file.
|
||||||
|
*/
|
||||||
|
public class KeyDecryptionFailedException extends IOException {
|
||||||
|
|
||||||
|
public static final String MESSAGE = "Decryption of the key failed. A supplied passphrase may be incorrect.";
|
||||||
|
|
||||||
|
public KeyDecryptionFailedException() {
|
||||||
|
super(MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyDecryptionFailedException(EncryptionException cause) {
|
||||||
|
super(MESSAGE, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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.key;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Buffer;
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
|
import net.schmizz.sshj.signature.Signature;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
public abstract class AbstractKeyAlgorithm implements KeyAlgorithm {
|
||||||
|
private final String keyAlgorithm;
|
||||||
|
private final Factory.Named<Signature> signature;
|
||||||
|
private final KeyType keyFormat;
|
||||||
|
|
||||||
|
public AbstractKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
|
||||||
|
this.keyAlgorithm = keyAlgorithm;
|
||||||
|
this.signature = signature;
|
||||||
|
this.keyFormat = keyFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||||
|
keyFormat.putPubKeyIntoBuffer(pk, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException {
|
||||||
|
return keyFormat.readPubKeyFromBuffer(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeyAlgorithm() {
|
||||||
|
return keyAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyType getKeyFormat() {
|
||||||
|
return keyFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Signature newSignature() {
|
||||||
|
return this.signature.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/main/java/com/hierynomus/sshj/key/DSAKeyAlgorithm.java
Normal file
65
src/main/java/com/hierynomus/sshj/key/DSAKeyAlgorithm.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.key;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
|
import net.schmizz.sshj.signature.Signature;
|
||||||
|
import net.schmizz.sshj.signature.SignatureDSA;
|
||||||
|
|
||||||
|
public class DSAKeyAlgorithm extends AbstractKeyAlgorithm {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named factory for the SSH-DSA key algorithm.
|
||||||
|
*/
|
||||||
|
public static class FactorySSHDSA
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new DSAKeyAlgorithm(KeyType.DSA.toString(), new SignatureDSA.Factory(), KeyType.DSA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.DSA.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named factory for the SSH-DSS-CERT key algorithm
|
||||||
|
*/
|
||||||
|
public static class FactorySSHDSSCert
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new DSAKeyAlgorithm(KeyType.DSA_CERT.toString(), new SignatureDSA.Factory(), KeyType.DSA_CERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.DSA_CERT.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
|
||||||
|
super(keyAlgorithm, signature, KeyType.DSA);
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/main/java/com/hierynomus/sshj/key/ECDSAKeyAlgorithm.java
Normal file
72
src/main/java/com/hierynomus/sshj/key/ECDSAKeyAlgorithm.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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.key;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
|
import net.schmizz.sshj.signature.Signature;
|
||||||
|
import net.schmizz.sshj.signature.SignatureECDSA;
|
||||||
|
|
||||||
|
public class ECDSAKeyAlgorithm extends AbstractKeyAlgorithm {
|
||||||
|
/** A named factory for ECDSA-256 signature */
|
||||||
|
public static class Factory256 implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new ECDSAKeyAlgorithm(KeyType.ECDSA256.toString(), new SignatureECDSA.Factory256(), KeyType.ECDSA256);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.ECDSA256.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A named factory for ECDSA-384 signature */
|
||||||
|
public static class Factory384 implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new ECDSAKeyAlgorithm(KeyType.ECDSA384.toString(), new SignatureECDSA.Factory384(), KeyType.ECDSA384);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new ECDSAKeyAlgorithm(KeyType.ECDSA521.toString(), new SignatureECDSA.Factory384(), KeyType.ECDSA521);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.ECDSA521.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECDSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
|
||||||
|
super(keyAlgorithm, signature, keyFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/main/java/com/hierynomus/sshj/key/EdDSAKeyAlgorithm.java
Normal file
39
src/main/java/com/hierynomus/sshj/key/EdDSAKeyAlgorithm.java
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.key;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
|
import net.schmizz.sshj.signature.Signature;
|
||||||
|
|
||||||
|
public class EdDSAKeyAlgorithm extends AbstractKeyAlgorithm {
|
||||||
|
public static class Factory implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.ED25519.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new EdDSAKeyAlgorithm(KeyType.ED25519.toString(), new SignatureEdDSA.Factory(), KeyType.ED25519);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EdDSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
|
||||||
|
super(keyAlgorithm, signature, keyFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/main/java/com/hierynomus/sshj/key/KeyAlgorithm.java
Normal file
45
src/main/java/com/hierynomus/sshj/key/KeyAlgorithm.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.key;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Buffer;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
|
import net.schmizz.sshj.signature.Signature;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In [RFC4252], the concept "public key algorithm" is used to establish
|
||||||
|
* a relationship between one algorithm name, and:
|
||||||
|
* <p>
|
||||||
|
* A. procedures used to generate and validate a private/public
|
||||||
|
* keypair;
|
||||||
|
* B. a format used to encode a public key; and
|
||||||
|
* C. procedures used to calculate, encode, and verify a signature.
|
||||||
|
*/
|
||||||
|
public interface KeyAlgorithm {
|
||||||
|
|
||||||
|
PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException;
|
||||||
|
|
||||||
|
void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||||
|
|
||||||
|
String getKeyAlgorithm();
|
||||||
|
|
||||||
|
KeyType getKeyFormat();
|
||||||
|
|
||||||
|
Signature newSignature();
|
||||||
|
}
|
||||||
96
src/main/java/com/hierynomus/sshj/key/RSAKeyAlgorithm.java
Normal file
96
src/main/java/com/hierynomus/sshj/key/RSAKeyAlgorithm.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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.key;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.Factory;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
|
import net.schmizz.sshj.signature.Signature;
|
||||||
|
import net.schmizz.sshj.signature.SignatureRSA;
|
||||||
|
|
||||||
|
public class RSAKeyAlgorithm extends AbstractKeyAlgorithm {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named factory for the SSH-RSA (SHA1) public key algorithm
|
||||||
|
*/
|
||||||
|
public static class FactorySSHRSA
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new RSAKeyAlgorithm("ssh-rsa", new SignatureRSA.FactorySSHRSA(), KeyType.RSA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "ssh-rsa";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named factory for the ssh-rsa-cert-v01@openssh.com (SHA1) public key algorithm
|
||||||
|
*/
|
||||||
|
public static class FactorySSHRSACert
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new RSAKeyAlgorithm("ssh-rsa-cert-v01@openssh.com", new SignatureRSA.FactoryCERT(), KeyType.RSA_CERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "ssh-rsa-cert-v01@openssh.com";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named factory for the RSA-SHA2-256 public key algorithm
|
||||||
|
*/
|
||||||
|
public static class FactoryRSASHA256
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new RSAKeyAlgorithm("rsa-sha2-256", new SignatureRSA.FactoryRSASHA256(), KeyType.RSA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "rsa-sha2-256";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named factory for the RSA-SHA2-512 public key algorithm
|
||||||
|
*/
|
||||||
|
public static class FactoryRSASHA512
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm create() {
|
||||||
|
return new RSAKeyAlgorithm("rsa-sha2-512", new SignatureRSA.FactoryRSASHA512(), KeyType.RSA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "rsa-sha2-512";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
|
||||||
|
super(keyAlgorithm, signature, keyFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ public class SignatureEdDSA extends AbstractSignature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SignatureEdDSA() {
|
SignatureEdDSA() {
|
||||||
super(getEngine());
|
super(getEngine(), KeyType.ED25519.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EdDSAEngine getEngine() {
|
private static EdDSAEngine getEngine() {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import java.math.BigInteger;
|
|||||||
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups
|
* Factory methods for Diffie Hellman KEX algorithms based on MODP groups / Oakley Groups
|
||||||
*
|
*
|
||||||
* - https://tools.ietf.org/html/rfc4253
|
* - https://tools.ietf.org/html/rfc4253
|
||||||
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01
|
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01
|
||||||
|
|||||||
@@ -16,48 +16,73 @@
|
|||||||
package com.hierynomus.sshj.transport.mac;
|
package com.hierynomus.sshj.transport.mac;
|
||||||
|
|
||||||
import net.schmizz.sshj.transport.mac.BaseMAC;
|
import net.schmizz.sshj.transport.mac.BaseMAC;
|
||||||
|
import net.schmizz.sshj.transport.mac.MAC;
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||||
public class Macs {
|
public class Macs {
|
||||||
public static Factory HMACMD5() {
|
public static Factory HMACMD5() {
|
||||||
return new Factory("hmac-md5", "HmacMD5", 16, 16);
|
return new Factory("hmac-md5", "HmacMD5", 16, 16, false);
|
||||||
}
|
}
|
||||||
public static Factory HMACMD596() {
|
public static Factory HMACMD596() {
|
||||||
return new Factory("hmac-md5-96", "HmacMD5", 12, 16);
|
return new Factory("hmac-md5-96", "HmacMD5", 12, 16, false);
|
||||||
|
}
|
||||||
|
public static Factory HMACMD5Etm() {
|
||||||
|
return new Factory("hmac-md5-etm@openssh.com", "HmacMD5", 16, 16, true);
|
||||||
|
}
|
||||||
|
public static Factory HMACMD596Etm() {
|
||||||
|
return new Factory("hmac-md5-96-etm@openssh.com", "HmacMD5", 12, 16, true);
|
||||||
}
|
}
|
||||||
public static Factory HMACRIPEMD160() {
|
public static Factory HMACRIPEMD160() {
|
||||||
return new Factory("hmac-ripemd160", "HMACRIPEMD160", 20, 20);
|
return new Factory("hmac-ripemd160", "HMACRIPEMD160", 20, 20, false);
|
||||||
}
|
}
|
||||||
public static Factory HMACRIPEMD16096() {
|
public static Factory HMACRIPEMD16096() {
|
||||||
return new Factory("hmac-ripemd160-96", "HMACRIPEMD160", 12, 20);
|
return new Factory("hmac-ripemd160-96", "HMACRIPEMD160", 12, 20, false);
|
||||||
|
}
|
||||||
|
public static Factory HMACRIPEMD160Etm() {
|
||||||
|
return new Factory("hmac-ripemd160-etm@openssh.com", "HMACRIPEMD160", 20, 20, true);
|
||||||
}
|
}
|
||||||
public static Factory HMACRIPEMD160OpenSsh() {
|
public static Factory HMACRIPEMD160OpenSsh() {
|
||||||
return new Factory("hmac-ripemd160@openssh.com", "HMACRIPEMD160", 20, 20);
|
return new Factory("hmac-ripemd160@openssh.com", "HMACRIPEMD160", 20, 20, false);
|
||||||
}
|
}
|
||||||
public static Factory HMACSHA1() {
|
public static Factory HMACSHA1() {
|
||||||
return new Factory("hmac-sha1", "HmacSHA1", 20, 20);
|
return new Factory("hmac-sha1", "HmacSHA1", 20, 20, false);
|
||||||
}
|
}
|
||||||
public static Factory HMACSHA196() {
|
public static Factory HMACSHA196() {
|
||||||
return new Factory("hmac-sha1-96", "HmacSHA1", 12, 20);
|
return new Factory("hmac-sha1-96", "HmacSHA1", 12, 20, false);
|
||||||
|
}
|
||||||
|
public static Factory HMACSHA1Etm() {
|
||||||
|
return new Factory("hmac-sha1-etm@openssh.com", "HmacSHA1", 20, 20, true);
|
||||||
|
}
|
||||||
|
public static Factory HMACSHA196Etm() {
|
||||||
|
return new Factory("hmac-sha1-96@openssh.com", "HmacSHA1", 12, 20, true);
|
||||||
}
|
}
|
||||||
public static Factory HMACSHA2256() {
|
public static Factory HMACSHA2256() {
|
||||||
return new Factory("hmac-sha2-256", "HmacSHA256", 32, 32);
|
return new Factory("hmac-sha2-256", "HmacSHA256", 32, 32, false);
|
||||||
|
}
|
||||||
|
public static Factory HMACSHA2256Etm() {
|
||||||
|
return new Factory("hmac-sha2-256-etm@openssh.com", "HmacSHA256", 32, 32, true);
|
||||||
}
|
}
|
||||||
public static Factory HMACSHA2512() {
|
public static Factory HMACSHA2512() {
|
||||||
return new Factory("hmac-sha2-512", "HmacSHA512", 64, 64);
|
return new Factory("hmac-sha2-512", "HmacSHA512", 64, 64, false);
|
||||||
|
}
|
||||||
|
public static Factory HMACSHA2512Etm() {
|
||||||
|
return new Factory("hmac-sha2-512-etm@openssh.com", "HmacSHA512", 64, 64, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Factory implements net.schmizz.sshj.common.Factory.Named<BaseMAC> {
|
public static class Factory implements net.schmizz.sshj.common.Factory.Named<MAC> {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private String algorithm;
|
private String algorithm;
|
||||||
private int bSize;
|
private int bSize;
|
||||||
private int defBSize;
|
private int defBSize;
|
||||||
|
private final boolean etm;
|
||||||
|
|
||||||
public Factory(String name, String algorithm, int bSize, int defBSize) {
|
public Factory(String name, String algorithm, int bSize, int defBSize, boolean etm) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.algorithm = algorithm;
|
this.algorithm = algorithm;
|
||||||
this.bSize = bSize;
|
this.bSize = bSize;
|
||||||
this.defBSize = defBSize;
|
this.defBSize = defBSize;
|
||||||
|
this.etm = etm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,7 +92,7 @@ public class Macs {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseMAC create() {
|
public BaseMAC create() {
|
||||||
return new BaseMAC(algorithm, bSize, defBSize);
|
return new BaseMAC(algorithm, bSize, defBSize, etm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.hierynomus.sshj.transport.verification;
|
|||||||
import net.schmizz.sshj.common.Base64;
|
import net.schmizz.sshj.common.Base64;
|
||||||
import net.schmizz.sshj.common.IOUtils;
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
import net.schmizz.sshj.common.SSHException;
|
import net.schmizz.sshj.common.SSHException;
|
||||||
import net.schmizz.sshj.transport.mac.HMACSHA1;
|
|
||||||
import net.schmizz.sshj.transport.mac.MAC;
|
import net.schmizz.sshj.transport.mac.MAC;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -26,6 +25,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.transport.mac.Macs;
|
||||||
|
|
||||||
public class KnownHostMatchers {
|
public class KnownHostMatchers {
|
||||||
|
|
||||||
public static HostMatcher createMatcher(String hostEntry) throws SSHException {
|
public static HostMatcher createMatcher(String hostEntry) throws SSHException {
|
||||||
@@ -63,7 +64,7 @@ public class KnownHostMatchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class HashedHostMatcher implements HostMatcher {
|
private static class HashedHostMatcher implements HostMatcher {
|
||||||
private final MAC sha1 = new HMACSHA1();
|
private final MAC sha1 = Macs.HMACSHA1().create();
|
||||||
private final String hash;
|
private final String hash;
|
||||||
private final String salt;
|
private final String salt;
|
||||||
private byte[] saltyBytes;
|
private byte[] saltyBytes;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.hierynomus.sshj.userauth.keyprovider;
|
package com.hierynomus.sshj.userauth.keyprovider;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||||
|
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
|
||||||
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||||
@@ -25,18 +27,22 @@ import net.schmizz.sshj.transport.cipher.Cipher;
|
|||||||
import net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider;
|
import net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
||||||
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
|
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
|
||||||
|
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
|
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
||||||
import org.mindrot.jbcrypt.BCrypt;
|
import org.mindrot.jbcrypt.BCrypt;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.*;
|
||||||
import java.security.KeyPair;
|
import java.security.spec.ECPrivateKeySpec;
|
||||||
import java.security.PublicKey;
|
import java.security.spec.RSAPrivateKeySpec;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,9 +112,17 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
|||||||
logger.debug("Reading unencrypted keypair");
|
logger.debug("Reading unencrypted keypair");
|
||||||
return readUnencrypted(privateKeyBuffer, publicKey);
|
return readUnencrypted(privateKeyBuffer, publicKey);
|
||||||
} else {
|
} else {
|
||||||
logger.info("Keypair is encrypted with: " + cipherName + ", " + kdfName + ", " + kdfOptions);
|
logger.info("Keypair is encrypted with: " + cipherName + ", " + kdfName + ", " + Arrays.toString(kdfOptions));
|
||||||
PlainBuffer decrypted = decryptBuffer(privateKeyBuffer, cipherName, kdfName, kdfOptions);
|
while (true) {
|
||||||
|
PlainBuffer decryptionBuffer = new PlainBuffer(privateKeyBuffer);
|
||||||
|
PlainBuffer decrypted = decryptBuffer(decryptionBuffer, cipherName, kdfName, kdfOptions);
|
||||||
|
try {
|
||||||
return readUnencrypted(decrypted, publicKey);
|
return readUnencrypted(decrypted, publicKey);
|
||||||
|
} catch (KeyDecryptionFailedException e) {
|
||||||
|
if (pwdf == null || !pwdf.shouldRetry(resource))
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
// throw new IOException("Cannot read encrypted keypair with " + cipherName + " yet.");
|
// throw new IOException("Cannot read encrypted keypair with " + cipherName + " yet.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,10 +138,12 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
|||||||
private void initializeCipher(String kdfName, byte[] kdfOptions, Cipher cipher) throws Buffer.BufferException {
|
private void initializeCipher(String kdfName, byte[] kdfOptions, Cipher cipher) throws Buffer.BufferException {
|
||||||
if (kdfName.equals(BCRYPT)) {
|
if (kdfName.equals(BCRYPT)) {
|
||||||
PlainBuffer opts = new PlainBuffer(kdfOptions);
|
PlainBuffer opts = new PlainBuffer(kdfOptions);
|
||||||
|
byte[] passphrase = new byte[0];
|
||||||
|
if (pwdf != null) {
|
||||||
CharBuffer charBuffer = CharBuffer.wrap(pwdf.reqPassword(null));
|
CharBuffer charBuffer = CharBuffer.wrap(pwdf.reqPassword(null));
|
||||||
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
|
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
|
||||||
byte[] passphrase = Arrays.copyOfRange(byteBuffer.array(),
|
passphrase = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
|
||||||
byteBuffer.position(), byteBuffer.limit());
|
}
|
||||||
byte[] keyiv = new byte[48];
|
byte[] keyiv = new byte[48];
|
||||||
new BCrypt().pbkdf(passphrase, opts.readBytes(), opts.readUInt32AsInt(), keyiv);
|
new BCrypt().pbkdf(passphrase, opts.readBytes(), opts.readUInt32AsInt(), keyiv);
|
||||||
byte[] key = Arrays.copyOfRange(keyiv, 0, 32);
|
byte[] key = Arrays.copyOfRange(keyiv, 0, 32);
|
||||||
@@ -141,6 +157,8 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
|||||||
private Cipher createCipher(String cipherName) {
|
private Cipher createCipher(String cipherName) {
|
||||||
if (cipherName.equals(BlockCiphers.AES256CTR().getName())) {
|
if (cipherName.equals(BlockCiphers.AES256CTR().getName())) {
|
||||||
return BlockCiphers.AES256CTR().create();
|
return BlockCiphers.AES256CTR().create();
|
||||||
|
} else if (cipherName.equals(BlockCiphers.AES256CBC().getName())) {
|
||||||
|
return BlockCiphers.AES256CBC().create();
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("Cipher '" + cipherName + "' not currently implemented for openssh-key-v1 format");
|
throw new IllegalStateException("Cipher '" + cipherName + "' not currently implemented for openssh-key-v1 format");
|
||||||
}
|
}
|
||||||
@@ -176,18 +194,45 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
|||||||
int checkInt1 = keyBuffer.readUInt32AsInt(); // uint32 checkint1
|
int checkInt1 = keyBuffer.readUInt32AsInt(); // uint32 checkint1
|
||||||
int checkInt2 = keyBuffer.readUInt32AsInt(); // uint32 checkint2
|
int checkInt2 = keyBuffer.readUInt32AsInt(); // uint32 checkint2
|
||||||
if (checkInt1 != checkInt2) {
|
if (checkInt1 != checkInt2) {
|
||||||
throw new IOException("The checkInts differed, the key was not correctly decoded.");
|
throw new KeyDecryptionFailedException();
|
||||||
}
|
}
|
||||||
// The private key section contains both the public key and the private key
|
// The private key section contains both the public key and the private key
|
||||||
String keyType = keyBuffer.readString(); // string keytype
|
String keyType = keyBuffer.readString(); // string keytype
|
||||||
logger.info("Read key type: {}", keyType);
|
KeyType kt = KeyType.fromString(keyType);
|
||||||
|
logger.info("Read key type: {}", keyType, kt);
|
||||||
byte[] pubKey = keyBuffer.readBytes(); // string publickey (again...)
|
KeyPair kp;
|
||||||
keyBuffer.readUInt32();
|
switch (kt) {
|
||||||
|
case ED25519:
|
||||||
|
keyBuffer.readBytes(); // string publickey (again...)
|
||||||
|
keyBuffer.readUInt32(); // length of privatekey+publickey
|
||||||
byte[] privKey = new byte[32];
|
byte[] privKey = new byte[32];
|
||||||
keyBuffer.readRawBytes(privKey); // string privatekey
|
keyBuffer.readRawBytes(privKey); // string privatekey
|
||||||
keyBuffer.readRawBytes(new byte[32]); // string publickey (again...)
|
keyBuffer.readRawBytes(new byte[32]); // string publickey (again...)
|
||||||
String comment = keyBuffer.readString(); // string comment
|
kp = new KeyPair(publicKey, new EdDSAPrivateKey(new EdDSAPrivateKeySpec(privKey, EdDSANamedCurveTable.getByName("Ed25519"))));
|
||||||
|
break;
|
||||||
|
case RSA:
|
||||||
|
BigInteger n = keyBuffer.readMPInt(); // Modulus
|
||||||
|
keyBuffer.readMPInt(); // Public Exponent
|
||||||
|
BigInteger d = keyBuffer.readMPInt(); // Private Exponent
|
||||||
|
keyBuffer.readMPInt(); // iqmp (q^-1 mod p)
|
||||||
|
keyBuffer.readMPInt(); // p (Prime 1)
|
||||||
|
keyBuffer.readMPInt(); // q (Prime 2)
|
||||||
|
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(new RSAPrivateKeySpec(n, d)));
|
||||||
|
break;
|
||||||
|
case ECDSA256:
|
||||||
|
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-256"));
|
||||||
|
break;
|
||||||
|
case ECDSA384:
|
||||||
|
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-384"));
|
||||||
|
break;
|
||||||
|
case ECDSA521:
|
||||||
|
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-521"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IOException("Cannot decode keytype " + keyType + " in openssh-key-v1 files (yet).");
|
||||||
|
}
|
||||||
|
keyBuffer.readString(); // string comment
|
||||||
byte[] padding = new byte[keyBuffer.available()];
|
byte[] padding = new byte[keyBuffer.available()];
|
||||||
keyBuffer.readRawBytes(padding); // char[] padding
|
keyBuffer.readRawBytes(padding); // char[] padding
|
||||||
for (int i = 0; i < padding.length; i++) {
|
for (int i = 0; i < padding.length; i++) {
|
||||||
@@ -195,6 +240,16 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
|||||||
throw new IOException("Padding of key format contained wrong byte at position: " + i);
|
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"))));
|
return kp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivateKey createECDSAPrivateKey(KeyType kt, PlainBuffer buffer, String name) throws GeneralSecurityException, Buffer.BufferException {
|
||||||
|
kt.readPubKeyFromBuffer(buffer); // Public key
|
||||||
|
BigInteger s = new BigInteger(1, buffer.readBytes());
|
||||||
|
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
|
||||||
|
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
|
||||||
|
ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec);
|
||||||
|
return SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,23 +19,23 @@ import java.util.Collection;
|
|||||||
|
|
||||||
public class ErrorDeliveryUtil {
|
public class ErrorDeliveryUtil {
|
||||||
|
|
||||||
public static void alertPromises(Throwable x, Promise... promises) {
|
public static void alertPromises(Throwable x, Promise<?, ?>... promises) {
|
||||||
for (Promise p : promises)
|
for (Promise<?, ?> p : promises)
|
||||||
p.deliverError(x);
|
p.deliverError(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void alertPromises(Throwable x, Collection<? extends Promise> promises) {
|
public static void alertPromises(Throwable x, Collection<? extends Promise<?, ?>> promises) {
|
||||||
for (Promise p : promises)
|
for (Promise<?, ?> p : promises)
|
||||||
p.deliverError(x);
|
p.deliverError(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void alertEvents(Throwable x, Event... events) {
|
public static void alertEvents(Throwable x, Event<?>... events) {
|
||||||
for (Event e : events)
|
for (Event<?> e : events)
|
||||||
e.deliverError(x);
|
e.deliverError(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void alertEvents(Throwable x, Collection<? extends Event> events) {
|
public static void alertEvents(Throwable x, Collection<? extends Event<?>> events) {
|
||||||
for (Event e : events)
|
for (Event<?> e : events)
|
||||||
e.deliverError(x);
|
e.deliverError(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ package net.schmizz.concurrent;
|
|||||||
* if (t instanceof SomeException)
|
* if (t instanceof SomeException)
|
||||||
* return (SomeException) t;
|
* return (SomeException) t;
|
||||||
* else
|
* else
|
||||||
* return new SomeExcepion(t);
|
* return new SomeException(t);
|
||||||
* }
|
* }
|
||||||
* };
|
* };
|
||||||
* </pre>
|
* </pre>
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.DSAKeyAlgorithm;
|
||||||
|
import com.hierynomus.sshj.key.EdDSAKeyAlgorithm;
|
||||||
|
import com.hierynomus.sshj.key.RSAKeyAlgorithm;
|
||||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
@@ -23,6 +26,8 @@ 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;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers SpongyCastle as JCE provider.
|
* Registers SpongyCastle as JCE provider.
|
||||||
*/
|
*/
|
||||||
@@ -33,11 +38,14 @@ public class AndroidConfig
|
|||||||
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
|
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't add ECDSA
|
|
||||||
protected void initSignatureFactories() {
|
@Override
|
||||||
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
|
protected void initKeyAlgorithms() {
|
||||||
// but add EdDSA
|
setKeyAlgorithms(Arrays.asList(
|
||||||
new SignatureEdDSA.Factory());
|
new EdDSAKeyAlgorithm.Factory(),
|
||||||
|
new RSAKeyAlgorithm.FactorySSHRSA(),
|
||||||
|
new DSAKeyAlgorithm.FactorySSHDSA()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||||
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;
|
||||||
@@ -77,11 +78,11 @@ public interface Config {
|
|||||||
Factory<Random> getRandomFactory();
|
Factory<Random> getRandomFactory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the list of named factories for {@link Signature}
|
* Retrieve the list of named factories for {@link com.hierynomus.sshj.key.KeyAlgorithm}
|
||||||
*
|
*
|
||||||
* @return a list of named {@link Signature} factories
|
* @return a list of named {@link com.hierynomus.sshj.key.KeyAlgorithm} factories
|
||||||
*/
|
*/
|
||||||
List<Factory.Named<Signature>> getSignatureFactories();
|
List<Factory.Named<KeyAlgorithm>> getKeyAlgorithms();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the software version information for identification during SSH connection initialization. For example,
|
* Returns the software version information for identification during SSH connection initialization. For example,
|
||||||
@@ -132,11 +133,11 @@ public interface Config {
|
|||||||
void setRandomFactory(Factory<Random> randomFactory);
|
void setRandomFactory(Factory<Random> randomFactory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the named factories for {@link Signature}.
|
* Set the named factories for {@link KeyAlgorithm}.
|
||||||
*
|
*
|
||||||
* @param signatureFactories a list of named factories
|
* @param keyAlgorithms a list of named factories
|
||||||
*/
|
*/
|
||||||
void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories);
|
void setKeyAlgorithms(List<Factory.Named<KeyAlgorithm>> keyAlgorithms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the software version information for identification during SSH connection initialization. For example, {@code
|
* Set the software version information for identification during SSH connection initialization. For example, {@code
|
||||||
@@ -160,7 +161,7 @@ public interface Config {
|
|||||||
/**
|
/**
|
||||||
* Gets whether the client should first wait for a received server ident, before sending the client ident.
|
* Gets whether the client should first wait for a received server ident, before sending the client ident.
|
||||||
* <p/>
|
* <p/>
|
||||||
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
* <strong>NB:</strong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||||
*
|
*
|
||||||
* The default value is set to false.
|
* The default value is set to false.
|
||||||
*
|
*
|
||||||
@@ -171,7 +172,7 @@ public interface Config {
|
|||||||
/**
|
/**
|
||||||
* Sets whether the SSH client should wait for a received server ident, before sending the client ident.
|
* Sets whether the SSH client should wait for a received server ident, before sending the client ident.
|
||||||
* <p/>
|
* <p/>
|
||||||
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
* <strong>NB:</strong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||||
|
|
||||||
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
|
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||||
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;
|
||||||
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;
|
||||||
import net.schmizz.sshj.transport.kex.KeyExchange;
|
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||||
@@ -42,7 +42,7 @@ public class ConfigImpl
|
|||||||
private List<Factory.Named<Cipher>> cipherFactories;
|
private List<Factory.Named<Cipher>> cipherFactories;
|
||||||
private List<Factory.Named<Compression>> compressionFactories;
|
private List<Factory.Named<Compression>> compressionFactories;
|
||||||
private List<Factory.Named<MAC>> macFactories;
|
private List<Factory.Named<MAC>> macFactories;
|
||||||
private List<Factory.Named<Signature>> signatureFactories;
|
private List<Factory.Named<KeyAlgorithm>> keyAlgorithms;
|
||||||
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
|
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
|
||||||
|
|
||||||
private boolean waitForServerIdentBeforeSendingClientIdent = false;
|
private boolean waitForServerIdentBeforeSendingClientIdent = false;
|
||||||
@@ -78,11 +78,6 @@ public class ConfigImpl
|
|||||||
return randomFactory;
|
return randomFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Factory.Named<Signature>> getSignatureFactories() {
|
|
||||||
return signatureFactories;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getVersion() {
|
public String getVersion() {
|
||||||
return version;
|
return version;
|
||||||
@@ -138,15 +133,6 @@ public class ConfigImpl
|
|||||||
this.randomFactory = randomFactory;
|
this.randomFactory = randomFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSignatureFactories(Factory.Named<Signature>... signatureFactories) {
|
|
||||||
setSignatureFactories(Arrays.asList(signatureFactories));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories) {
|
|
||||||
this.signatureFactories = signatureFactories;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVersion(String version) {
|
public void setVersion(String version) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
@@ -172,6 +158,16 @@ public class ConfigImpl
|
|||||||
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
|
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Factory.Named<KeyAlgorithm>> getKeyAlgorithms() {
|
||||||
|
return keyAlgorithms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setKeyAlgorithms(List<Factory.Named<KeyAlgorithm>> keyAlgorithms) {
|
||||||
|
this.keyAlgorithms = keyAlgorithms;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoggerFactory getLoggerFactory() {
|
public LoggerFactory getLoggerFactory() {
|
||||||
return loggerFactory;
|
return loggerFactory;
|
||||||
|
|||||||
@@ -15,26 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
import com.hierynomus.sshj.key.DSAKeyAlgorithm;
|
||||||
|
import com.hierynomus.sshj.key.ECDSAKeyAlgorithm;
|
||||||
|
import com.hierynomus.sshj.key.EdDSAKeyAlgorithm;
|
||||||
|
import com.hierynomus.sshj.key.RSAKeyAlgorithm;
|
||||||
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.DHGroups;
|
||||||
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
|
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
|
||||||
|
import com.hierynomus.sshj.transport.mac.Macs;
|
||||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
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;
|
||||||
import net.schmizz.sshj.common.SecurityUtils;
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
import net.schmizz.sshj.signature.SignatureDSA;
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
import net.schmizz.sshj.signature.SignatureECDSA;
|
|
||||||
import net.schmizz.sshj.signature.SignatureRSA;
|
|
||||||
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.Curve25519SHA256;
|
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
|
||||||
import net.schmizz.sshj.transport.kex.DHGexSHA1;
|
import net.schmizz.sshj.transport.kex.DHGexSHA1;
|
||||||
import net.schmizz.sshj.transport.kex.DHGexSHA256;
|
import net.schmizz.sshj.transport.kex.DHGexSHA256;
|
||||||
import net.schmizz.sshj.transport.kex.ECDHNistP;
|
import net.schmizz.sshj.transport.kex.ECDHNistP;
|
||||||
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;
|
||||||
@@ -44,7 +44,6 @@ 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.io.IOException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +56,7 @@ import java.util.*;
|
|||||||
* <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>
|
||||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setSignatureFactories Signature}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
|
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyAlgorithms KeyAlgorithm}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
|
||||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory PRNG}: {@link net.schmizz.sshj.transport.random.BouncyCastleRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
|
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory PRNG}: {@link net.schmizz.sshj.transport.random.BouncyCastleRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
|
||||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setFileKeyProviderFactories Key file support}: {@link net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile}*, {@link
|
* <li>{@link net.schmizz.sshj.ConfigImpl#setFileKeyProviderFactories Key file support}: {@link net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile}*, {@link
|
||||||
* net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile}*</li>
|
* net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile}*</li>
|
||||||
@@ -77,12 +76,12 @@ public class DefaultConfig
|
|||||||
setVersion(readVersionFromProperties());
|
setVersion(readVersionFromProperties());
|
||||||
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
|
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
|
||||||
initKeyExchangeFactories(bouncyCastleRegistered);
|
initKeyExchangeFactories(bouncyCastleRegistered);
|
||||||
|
initKeyAlgorithms();
|
||||||
initRandomFactory(bouncyCastleRegistered);
|
initRandomFactory(bouncyCastleRegistered);
|
||||||
initFileKeyProviderFactories(bouncyCastleRegistered);
|
initFileKeyProviderFactories(bouncyCastleRegistered);
|
||||||
initCipherFactories();
|
initCipherFactories();
|
||||||
initCompressionFactories();
|
initCompressionFactories();
|
||||||
initMACFactories();
|
initMACFactories();
|
||||||
initSignatureFactories();
|
|
||||||
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
|
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +105,9 @@ public class DefaultConfig
|
|||||||
|
|
||||||
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
|
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
|
||||||
if (bouncyCastleRegistered) {
|
if (bouncyCastleRegistered) {
|
||||||
setKeyExchangeFactories(new Curve25519SHA256.Factory(),
|
setKeyExchangeFactories(
|
||||||
|
new Curve25519SHA256.Factory(),
|
||||||
|
new Curve25519SHA256.FactoryLibSsh(),
|
||||||
new DHGexSHA256.Factory(),
|
new DHGexSHA256.Factory(),
|
||||||
new ECDHNistP.Factory521(),
|
new ECDHNistP.Factory521(),
|
||||||
new ECDHNistP.Factory384(),
|
new ECDHNistP.Factory384(),
|
||||||
@@ -132,6 +133,20 @@ public class DefaultConfig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void initKeyAlgorithms() {
|
||||||
|
setKeyAlgorithms(Arrays.asList(
|
||||||
|
new EdDSAKeyAlgorithm.Factory(),
|
||||||
|
new ECDSAKeyAlgorithm.Factory521(),
|
||||||
|
new ECDSAKeyAlgorithm.Factory384(),
|
||||||
|
new ECDSAKeyAlgorithm.Factory256(),
|
||||||
|
new RSAKeyAlgorithm.FactoryRSASHA512(),
|
||||||
|
new RSAKeyAlgorithm.FactoryRSASHA256(),
|
||||||
|
new RSAKeyAlgorithm.FactorySSHRSACert(),
|
||||||
|
new DSAKeyAlgorithm.FactorySSHDSSCert(),
|
||||||
|
new RSAKeyAlgorithm.FactorySSHRSA(),
|
||||||
|
new DSAKeyAlgorithm.FactorySSHDSA()));
|
||||||
|
}
|
||||||
|
|
||||||
protected void initRandomFactory(boolean bouncyCastleRegistered) {
|
protected void initRandomFactory(boolean bouncyCastleRegistered) {
|
||||||
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered
|
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered
|
||||||
? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
|
? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
|
||||||
@@ -206,25 +221,24 @@ public class DefaultConfig
|
|||||||
log.debug("Available cipher factories: {}", avail);
|
log.debug("Available cipher factories: {}", avail);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initSignatureFactories() {
|
|
||||||
setSignatureFactories(
|
|
||||||
new SignatureECDSA.Factory256(),
|
|
||||||
new SignatureECDSA.Factory384(),
|
|
||||||
new SignatureECDSA.Factory521(),
|
|
||||||
new SignatureRSA.Factory(),
|
|
||||||
new SignatureDSA.Factory(),
|
|
||||||
new SignatureEdDSA.Factory()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initMACFactories() {
|
protected void initMACFactories() {
|
||||||
setMACFactories(
|
setMACFactories(
|
||||||
new HMACSHA1.Factory(),
|
Macs.HMACSHA1(),
|
||||||
new HMACSHA196.Factory(),
|
Macs.HMACSHA1Etm(),
|
||||||
new HMACMD5.Factory(),
|
Macs.HMACSHA196(),
|
||||||
new HMACMD596.Factory(),
|
Macs.HMACSHA196Etm(),
|
||||||
new HMACSHA2256.Factory(),
|
Macs.HMACMD5(),
|
||||||
new HMACSHA2512.Factory()
|
Macs.HMACMD5Etm(),
|
||||||
|
Macs.HMACMD596(),
|
||||||
|
Macs.HMACMD596Etm(),
|
||||||
|
Macs.HMACSHA2256(),
|
||||||
|
Macs.HMACSHA2256Etm(),
|
||||||
|
Macs.HMACSHA2512(),
|
||||||
|
Macs.HMACSHA2512Etm(),
|
||||||
|
Macs.HMACRIPEMD160(),
|
||||||
|
Macs.HMACRIPEMD160Etm(),
|
||||||
|
Macs.HMACRIPEMD16096(),
|
||||||
|
Macs.HMACRIPEMD160OpenSsh()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,7 @@ import net.schmizz.sshj.common.*;
|
|||||||
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;
|
||||||
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
|
import net.schmizz.sshj.connection.channel.direct.*;
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
|
||||||
import net.schmizz.sshj.connection.channel.direct.SessionChannel;
|
|
||||||
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
|
|
||||||
import net.schmizz.sshj.connection.channel.forwarded.ConnectListener;
|
import net.schmizz.sshj.connection.channel.forwarded.ConnectListener;
|
||||||
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder;
|
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder;
|
||||||
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.ForwardedTCPIPChannel;
|
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.ForwardedTCPIPChannel;
|
||||||
@@ -61,7 +58,6 @@ import java.io.IOException;
|
|||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -360,8 +356,7 @@ public class SSHClient
|
|||||||
* @throws TransportException if there was a transport-layer error
|
* @throws TransportException if there was a transport-layer error
|
||||||
*/
|
*/
|
||||||
public void authPublickey(String username, KeyProvider... keyProviders)
|
public void authPublickey(String username, KeyProvider... keyProviders)
|
||||||
throws UserAuthException,
|
throws UserAuthException, TransportException {
|
||||||
TransportException {
|
|
||||||
authPublickey(username, Arrays.<KeyProvider>asList(keyProviders));
|
authPublickey(username, Arrays.<KeyProvider>asList(keyProviders));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +515,7 @@ public class SSHClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function for createing a {@link KeyProvider} instance from given location on the file system. Creates a
|
* Utility function for creating a {@link KeyProvider} instance from given location on the file system. Creates a
|
||||||
* one-off {@link PasswordFinder} using {@link PasswordUtils#createOneOff(char[])}, and calls {@link
|
* one-off {@link PasswordFinder} using {@link PasswordUtils#createOneOff(char[])}, and calls {@link
|
||||||
* #loadKeys(String, PasswordFinder)}.
|
* #loadKeys(String, PasswordFinder)}.
|
||||||
*
|
*
|
||||||
@@ -666,13 +661,27 @@ public class SSHClient
|
|||||||
*
|
*
|
||||||
* @return a {@link LocalPortForwarder}
|
* @return a {@link LocalPortForwarder}
|
||||||
*/
|
*/
|
||||||
public LocalPortForwarder newLocalPortForwarder(LocalPortForwarder.Parameters parameters,
|
public LocalPortForwarder newLocalPortForwarder(Parameters parameters,
|
||||||
ServerSocket serverSocket) {
|
ServerSocket serverSocket) {
|
||||||
LocalPortForwarder forwarder = new LocalPortForwarder(conn, parameters, serverSocket, loggerFactory);
|
LocalPortForwarder forwarder = new LocalPortForwarder(conn, parameters, serverSocket, loggerFactory);
|
||||||
forwarders.add(forwarder);
|
forwarders.add(forwarder);
|
||||||
return forwarder;
|
return forwarder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Create a {@link DirectConnection} channel that connects to a remote address from the server.
|
||||||
|
*
|
||||||
|
* This can be used to open a tunnel to, for example, an HTTP server that is only
|
||||||
|
* accessible from the SSH server, or opening an SSH connection via a 'jump' server.
|
||||||
|
*
|
||||||
|
* @param hostname name of the host to connect to from the server.
|
||||||
|
* @param port remote port number.
|
||||||
|
*/
|
||||||
|
public DirectConnection newDirectConnection(String hostname, int port) throws IOException {
|
||||||
|
DirectConnection tunnel = new DirectConnection(conn, hostname, port);
|
||||||
|
tunnel.open();
|
||||||
|
return tunnel;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a {@code listener} for handling forwarded X11 channels. Without having done this, an incoming X11
|
* Register a {@code listener} for handling forwarded X11 channels. Without having done this, an incoming X11
|
||||||
* forwarding will be summarily rejected.
|
* forwarding will be summarily rejected.
|
||||||
@@ -745,7 +754,7 @@ public class SSHClient
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds {@code zlib} compression to preferred compression algorithms. There is no guarantee that it will be
|
* Adds {@code zlib} compression to preferred compression algorithms. There is no guarantee that it will be
|
||||||
* successfully negotiatied.
|
* successfully negotiated.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If the client is already connected renegotiation is done; otherwise this method simply returns (and compression
|
* If the client is already connected renegotiation is done; otherwise this method simply returns (and compression
|
||||||
* will be negotiated during connection establishment).
|
* will be negotiated during connection establishment).
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ package net.schmizz.sshj;
|
|||||||
|
|
||||||
import com.hierynomus.sshj.backport.JavaVersion;
|
import com.hierynomus.sshj.backport.JavaVersion;
|
||||||
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
|
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
|
||||||
|
import net.schmizz.sshj.connection.channel.Channel;
|
||||||
|
import net.schmizz.sshj.connection.channel.direct.DirectConnection;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -43,11 +45,18 @@ public abstract class SocketClient {
|
|||||||
private int timeout = 0;
|
private int timeout = 0;
|
||||||
|
|
||||||
private String hostname;
|
private String hostname;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
private boolean tunneled = false;
|
||||||
|
|
||||||
SocketClient(int defaultPort) {
|
SocketClient(int defaultPort) {
|
||||||
this.defaultPort = defaultPort;
|
this.defaultPort = defaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected InetSocketAddress makeInetSocketAddress(String hostname, int port) {
|
||||||
|
return new InetSocketAddress(hostname, port);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a host via a proxy.
|
* Connect to a host via a proxy.
|
||||||
* @param hostname The host name to connect to.
|
* @param hostname The host name to connect to.
|
||||||
@@ -71,13 +80,14 @@ public abstract class SocketClient {
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public void connect(String hostname, int port, Proxy proxy) throws IOException {
|
public void connect(String hostname, int port, Proxy proxy) throws IOException {
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
|
this.port = port;
|
||||||
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
|
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
|
||||||
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
|
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
|
||||||
socket = new Jdk7HttpProxySocket(proxy);
|
socket = new Jdk7HttpProxySocket(proxy);
|
||||||
} else {
|
} else {
|
||||||
socket = new Socket(proxy);
|
socket = new Socket(proxy);
|
||||||
}
|
}
|
||||||
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
|
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
|
||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +113,7 @@ public abstract class SocketClient {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void connect(InetAddress host, int port, Proxy proxy) throws IOException {
|
public void connect(InetAddress host, int port, Proxy proxy) throws IOException {
|
||||||
|
this.port = port;
|
||||||
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
|
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
|
||||||
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
|
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
|
||||||
socket = new Jdk7HttpProxySocket(proxy);
|
socket = new Jdk7HttpProxySocket(proxy);
|
||||||
@@ -122,8 +133,9 @@ public abstract class SocketClient {
|
|||||||
connect(InetAddress.getByName(null), port);
|
connect(InetAddress.getByName(null), port);
|
||||||
} else {
|
} else {
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
|
this.port = port;
|
||||||
socket = socketFactory.createSocket();
|
socket = socketFactory.createSocket();
|
||||||
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
|
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
|
||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,18 +145,34 @@ public abstract class SocketClient {
|
|||||||
connect(InetAddress.getByName(null), port, localAddr, localPort);
|
connect(InetAddress.getByName(null), port, localAddr, localPort);
|
||||||
} else {
|
} else {
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
|
this.port = port;
|
||||||
socket = socketFactory.createSocket();
|
socket = socketFactory.createSocket();
|
||||||
socket.bind(new InetSocketAddress(localAddr, localPort));
|
socket.bind(new InetSocketAddress(localAddr, localPort));
|
||||||
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
|
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
|
||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void connectVia(Channel channel, String hostname, int port) throws IOException {
|
||||||
|
this.hostname = hostname;
|
||||||
|
this.port = port;
|
||||||
|
this.input = channel.getInputStream();
|
||||||
|
this.output = channel.getOutputStream();
|
||||||
|
this.tunneled = true;
|
||||||
|
onConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Connect to a remote address via a direct TCP/IP connection from the server. */
|
||||||
|
public void connectVia(DirectConnection directConnection) throws IOException {
|
||||||
|
connectVia(directConnection, directConnection.getRemoteHost(), directConnection.getRemotePort());
|
||||||
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host) throws IOException {
|
public void connect(InetAddress host) throws IOException {
|
||||||
connect(host, defaultPort);
|
connect(host, defaultPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host, int port) throws IOException {
|
public void connect(InetAddress host, int port) throws IOException {
|
||||||
|
this.port = port;
|
||||||
socket = socketFactory.createSocket();
|
socket = socketFactory.createSocket();
|
||||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||||
onConnect();
|
onConnect();
|
||||||
@@ -152,6 +180,7 @@ public abstract class SocketClient {
|
|||||||
|
|
||||||
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
|
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
this.port = port;
|
||||||
socket = socketFactory.createSocket();
|
socket = socketFactory.createSocket();
|
||||||
socket.bind(new InetSocketAddress(localAddr, localPort));
|
socket.bind(new InetSocketAddress(localAddr, localPort));
|
||||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||||
@@ -174,15 +203,15 @@ public abstract class SocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return (socket != null) && socket.isConnected();
|
return tunneled || ((socket != null) && socket.isConnected());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLocalPort() {
|
public int getLocalPort() {
|
||||||
return socket.getLocalPort();
|
return tunneled ? 65536 : socket.getLocalPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InetAddress getLocalAddress() {
|
public InetAddress getLocalAddress() {
|
||||||
return socket.getLocalAddress();
|
return socket == null ? null : socket.getLocalAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRemoteHostname() {
|
public String getRemoteHostname() {
|
||||||
@@ -190,11 +219,11 @@ public abstract class SocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getRemotePort() {
|
public int getRemotePort() {
|
||||||
return socket.getPort();
|
return socket == null ? this.port : socket.getPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InetAddress getRemoteAddress() {
|
public InetAddress getRemoteAddress() {
|
||||||
return socket.getInetAddress();
|
return socket == null ? null : socket.getInetAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSocketFactory(SocketFactory factory) {
|
public void setSocketFactory(SocketFactory factory) {
|
||||||
@@ -238,9 +267,11 @@ public abstract class SocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onConnect() throws IOException {
|
void onConnect() throws IOException {
|
||||||
|
if (socket != null) {
|
||||||
socket.setSoTimeout(timeout);
|
socket.setSoTimeout(timeout);
|
||||||
input = socket.getInputStream();
|
input = socket.getInputStream();
|
||||||
output = socket.getOutputStream();
|
output = socket.getOutputStream();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ package net.schmizz.sshj.common;
|
|||||||
* <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li>
|
* <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li>
|
||||||
* <li><em>Throws exceptions instead of returning null values.</em> Because some operations
|
* <li><em>Throws exceptions instead of returning null values.</em> Because some operations
|
||||||
* (especially those that may permit the GZIP option) use IO streams, there
|
* (especially those that may permit the GZIP option) use IO streams, there
|
||||||
* is a possiblity of an java.io.IOException being thrown. After some discussion and
|
* is a possibility of an java.io.IOException being thrown. After some discussion and
|
||||||
* thought, I've changed the behavior of the methods to throw java.io.IOExceptions
|
* thought, I've changed the behavior of the methods to throw java.io.IOExceptions
|
||||||
* rather than return null if ever there's an error. I think this is more
|
* rather than return null if ever there's an error. I think this is more
|
||||||
* appropriate, though it will require some changes to your code. Sorry,
|
* appropriate, though it will require some changes to your code. Sorry,
|
||||||
@@ -476,7 +476,7 @@ public class Base64
|
|||||||
* anywhere along their length by specifying
|
* anywhere along their length by specifying
|
||||||
* <var>srcOffset</var> and <var>destOffset</var>.
|
* <var>srcOffset</var> and <var>destOffset</var>.
|
||||||
* This method does not check to make sure your arrays
|
* This method does not check to make sure your arrays
|
||||||
* are large enough to accomodate <var>srcOffset</var> + 3 for
|
* are large enough to accommodate <var>srcOffset</var> + 3 for
|
||||||
* the <var>source</var> array or <var>destOffset</var> + 4 for
|
* the <var>source</var> array or <var>destOffset</var> + 4 for
|
||||||
* the <var>destination</var> array.
|
* the <var>destination</var> array.
|
||||||
* The actual number of significant bytes in your array is
|
* The actual number of significant bytes in your array is
|
||||||
@@ -1007,7 +1007,7 @@ public class Base64
|
|||||||
* anywhere along their length by specifying
|
* anywhere along their length by specifying
|
||||||
* <var>srcOffset</var> and <var>destOffset</var>.
|
* <var>srcOffset</var> and <var>destOffset</var>.
|
||||||
* This method does not check to make sure your arrays
|
* This method does not check to make sure your arrays
|
||||||
* are large enough to accomodate <var>srcOffset</var> + 4 for
|
* are large enough to accommodate <var>srcOffset</var> + 4 for
|
||||||
* the <var>source</var> array or <var>destOffset</var> + 3 for
|
* the <var>source</var> array or <var>destOffset</var> + 3 for
|
||||||
* the <var>destination</var> array.
|
* the <var>destination</var> array.
|
||||||
* This method returns the actual number of bytes that
|
* This method returns the actual number of bytes that
|
||||||
@@ -1928,7 +1928,7 @@ public class Base64
|
|||||||
if( suspendEncoding ) {
|
if( suspendEncoding ) {
|
||||||
this.out.write( theByte );
|
this.out.write( theByte );
|
||||||
return;
|
return;
|
||||||
} // end if: supsended
|
} // end if: suspended
|
||||||
|
|
||||||
// Encode?
|
// Encode?
|
||||||
if( encode ) {
|
if( encode ) {
|
||||||
@@ -1983,7 +1983,7 @@ public class Base64
|
|||||||
if( suspendEncoding ) {
|
if( suspendEncoding ) {
|
||||||
this.out.write( theBytes, off, len );
|
this.out.write( theBytes, off, len );
|
||||||
return;
|
return;
|
||||||
} // end if: supsended
|
} // end if: suspended
|
||||||
|
|
||||||
for( int i = 0; i < len; i++ ) {
|
for( int i = 0; i < len; i++ ) {
|
||||||
write( theBytes[ off + i ] );
|
write( theBytes[ off + i ] );
|
||||||
|
|||||||
@@ -372,6 +372,7 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private T putUInt64Unchecked(long uint64) {
|
private T putUInt64Unchecked(long uint64) {
|
||||||
|
ensureCapacity(8);
|
||||||
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);
|
||||||
@@ -452,11 +453,14 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
public T putSensitiveString(char[] str) {
|
public T putSensitiveString(char[] str) {
|
||||||
if (str == null)
|
if (str == null)
|
||||||
return putString("");
|
return putString("");
|
||||||
putUInt32(str.length);
|
// RFC 4252, Section 8 says: passwords should be encoded as UTF-8.
|
||||||
ensureCapacity(str.length);
|
// RFC 4256, Section 3.4 says: keyboard-interactive information responses should be encoded as UTF-8.
|
||||||
for (char c : str)
|
byte[] utf8 = ByteArrayUtils.encodeSensitiveStringToUtf8(str);
|
||||||
data[wpos++] = (byte) c;
|
putUInt32(utf8.length);
|
||||||
Arrays.fill(str, ' ');
|
ensureCapacity(utf8.length);
|
||||||
|
for (byte c : utf8)
|
||||||
|
data[wpos++] = c;
|
||||||
|
Arrays.fill(utf8, (byte) 0);
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharsetEncoder;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** Utility functions for byte arrays. */
|
/** Utility functions for byte arrays. */
|
||||||
public class ByteArrayUtils {
|
public class ByteArrayUtils {
|
||||||
|
|
||||||
@@ -124,4 +130,26 @@ public class ByteArrayUtils {
|
|||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Digit '" + c + "' out of bounds [0-9a-fA-F]");
|
throw new IllegalArgumentException("Digit '" + c + "' out of bounds [0-9a-fA-F]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a char-array to UTF-8 byte-array and then blanks out source array and all intermediate arrays.
|
||||||
|
* <p/>
|
||||||
|
* This is useful when a plaintext password needs to be encoded as UTF-8.
|
||||||
|
*
|
||||||
|
* @param str A not-null string as a character array.
|
||||||
|
*
|
||||||
|
* @return UTF-8 bytes of the string
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSensitiveStringToUtf8(char[] str) {
|
||||||
|
CharsetEncoder charsetEncoder = Charset.forName("UTF-8").newEncoder();
|
||||||
|
ByteBuffer utf8Buffer = ByteBuffer.allocate((int) (str.length * charsetEncoder.maxBytesPerChar()));
|
||||||
|
assert utf8Buffer.hasArray();
|
||||||
|
charsetEncoder.encode(CharBuffer.wrap(str), utf8Buffer, true);
|
||||||
|
Arrays.fill(str, ' ');
|
||||||
|
|
||||||
|
byte[] utf8Bytes = new byte[utf8Buffer.position()];
|
||||||
|
System.arraycopy(utf8Buffer.array(), 0, utf8Bytes, 0, utf8Bytes.length);
|
||||||
|
Arrays.fill(utf8Buffer.array(), (byte) 0);
|
||||||
|
return utf8Bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||||
import com.hierynomus.sshj.secg.SecgUtils;
|
import com.hierynomus.sshj.secg.SecgUtils;
|
||||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
@@ -87,7 +88,7 @@ class ECDSAVariationsAdapter {
|
|||||||
ECPoint p = new ECPoint(bigX, bigY);
|
ECPoint p = new ECPoint(bigX, bigY);
|
||||||
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
|
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
|
||||||
|
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
|
KeyFactory keyFactory = KeyFactory.getInstance(KeyAlgorithm.ECDSA);
|
||||||
return keyFactory.generatePublic(publicKeySpec);
|
return keyFactory.generatePublic(publicKeySpec);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new GeneralSecurityException(ex);
|
throw new GeneralSecurityException(ex);
|
||||||
@@ -103,7 +104,7 @@ class ECDSAVariationsAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean isECKeyWithFieldSize(Key key, int fieldSize) {
|
static boolean isECKeyWithFieldSize(Key key, int fieldSize) {
|
||||||
return "ECDSA".equals(key.getAlgorithm())
|
return (KeyAlgorithm.ECDSA.equals(key.getAlgorithm()) || KeyAlgorithm.EC_KEYSTORE.equals(key.getAlgorithm()))
|
||||||
&& fieldSizeFromKey((ECKey) key) == fieldSize;
|
&& fieldSizeFromKey((ECKey) key) == fieldSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import java.util.List;
|
|||||||
public interface Factory<T> {
|
public interface Factory<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inteface for a named factory. Named factories are simply factories that are identified by a name. Such names are
|
* Interface for a named factory. Named factories are simply factories that are identified by a name. Such names are
|
||||||
* used mainly in SSH algorithm negotiation.
|
* used mainly in SSH algorithm negotiation.
|
||||||
*
|
*
|
||||||
* @param <T> type of object created by this factory
|
* @param <T> type of object created by this factory
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||||
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
||||||
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
@@ -30,9 +31,7 @@ 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.DSAPrivateKey;
|
|
||||||
import java.security.interfaces.DSAPublicKey;
|
import java.security.interfaces.DSAPublicKey;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
|
||||||
import java.security.interfaces.RSAPublicKey;
|
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;
|
||||||
@@ -53,7 +52,7 @@ public enum KeyType {
|
|||||||
} catch (Buffer.BufferException be) {
|
} catch (Buffer.BufferException be) {
|
||||||
throw new GeneralSecurityException(be);
|
throw new GeneralSecurityException(be);
|
||||||
}
|
}
|
||||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
|
||||||
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +65,7 @@ public enum KeyType {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isMyType(Key key) {
|
protected boolean isMyType(Key key) {
|
||||||
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
|
return KeyAlgorithm.RSA.equals(key.getAlgorithm());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -84,7 +83,7 @@ public enum KeyType {
|
|||||||
} catch (Buffer.BufferException be) {
|
} catch (Buffer.BufferException be) {
|
||||||
throw new GeneralSecurityException(be);
|
throw new GeneralSecurityException(be);
|
||||||
}
|
}
|
||||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
|
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.DSA);
|
||||||
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
|
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +98,7 @@ public enum KeyType {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isMyType(Key key) {
|
protected boolean isMyType(Key key) {
|
||||||
return (key instanceof DSAPublicKey || key instanceof DSAPrivateKey);
|
return KeyAlgorithm.DSA.equals(key.getAlgorithm());
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -315,8 +314,8 @@ public enum KeyType {
|
|||||||
builder.type(buf.readUInt32());
|
builder.type(buf.readUInt32());
|
||||||
builder.id(buf.readString());
|
builder.id(buf.readString());
|
||||||
builder.validPrincipals(unpackList(buf.readBytes()));
|
builder.validPrincipals(unpackList(buf.readBytes()));
|
||||||
builder.validAfter(dateFromEpoch(buf.readUInt64()));
|
builder.validAfter(dateFromEpoch(buf.readUInt64AsBigInteger()));
|
||||||
builder.validBefore(dateFromEpoch(buf.readUInt64()));
|
builder.validBefore(dateFromEpoch(buf.readUInt64AsBigInteger()));
|
||||||
builder.critOptions(unpackMap(buf.readBytes()));
|
builder.critOptions(unpackMap(buf.readBytes()));
|
||||||
builder.extensions(unpackMap(buf.readBytes()));
|
builder.extensions(unpackMap(buf.readBytes()));
|
||||||
buf.readString(); // reserved
|
buf.readString(); // reserved
|
||||||
@@ -364,8 +363,13 @@ public enum KeyType {
|
|||||||
return ((Certificate<PublicKey>) key);
|
return ((Certificate<PublicKey>) key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Date dateFromEpoch(long seconds) {
|
private static Date dateFromEpoch(BigInteger seconds) {
|
||||||
return new Date(seconds * 1000);
|
BigInteger maxValue = BigInteger.valueOf(Long.MAX_VALUE / 1000);
|
||||||
|
if (seconds.compareTo(maxValue) > 0) {
|
||||||
|
return new Date(maxValue.longValue() * 1000);
|
||||||
|
} else {
|
||||||
|
return new Date(seconds.longValue() * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long epochFromDate(Date date) {
|
private static long epochFromDate(Date date) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.slf4j.Logger;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class StreamCopier {
|
public class StreamCopier {
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ public class StreamCopier {
|
|||||||
long count = 0;
|
long count = 0;
|
||||||
int read = 0;
|
int read = 0;
|
||||||
|
|
||||||
final long startTime = System.currentTimeMillis();
|
final long startTime = System.nanoTime();
|
||||||
|
|
||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
while ((read = in.read(buf)) != -1) {
|
while ((read = in.read(buf)) != -1) {
|
||||||
@@ -140,7 +141,7 @@ public class StreamCopier {
|
|||||||
if (!keepFlushing)
|
if (!keepFlushing)
|
||||||
out.flush();
|
out.flush();
|
||||||
|
|
||||||
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
|
final double timeSeconds = TimeUnit.NANOSECONDS.toMillis (System.nanoTime() - startTime) / 1000.0;
|
||||||
final double sizeKiB = count / 1024.0;
|
final double sizeKiB = count / 1024.0;
|
||||||
log.debug(String.format("%1$,.1f KiB transferred in %2$,.1f seconds (%3$,.2f KiB/s)", sizeKiB, timeSeconds, (sizeKiB / timeSeconds)));
|
log.debug(String.format("%1$,.1f KiB transferred in %2$,.1f seconds (%3$,.2f KiB/s)", sizeKiB, timeSeconds, (sizeKiB / timeSeconds)));
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ public class ConnectionImpl
|
|||||||
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 GLOBAL_REQUEST:
|
||||||
|
gotGlobalRequest(buf);
|
||||||
|
break;
|
||||||
case REQUEST_SUCCESS:
|
case REQUEST_SUCCESS:
|
||||||
gotGlobalReqResponse(buf);
|
gotGlobalReqResponse(buf);
|
||||||
break;
|
break;
|
||||||
@@ -259,6 +262,20 @@ public class ConnectionImpl
|
|||||||
channels.clear();
|
channels.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void gotGlobalRequest(SSHPacket buf)
|
||||||
|
throws ConnectionException, TransportException {
|
||||||
|
try {
|
||||||
|
final String requestName = buf.readString();
|
||||||
|
boolean wantReply = buf.readBoolean();
|
||||||
|
log.debug("Received GLOBAL_REQUEST `{}`; want reply: {}", requestName, wantReply);
|
||||||
|
if (wantReply) {
|
||||||
|
trans.write(new SSHPacket(Message.REQUEST_FAILURE));
|
||||||
|
}
|
||||||
|
} catch (Buffer.BufferException be) {
|
||||||
|
throw new ConnectionException(be);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTimeoutMs(int timeoutMs) {
|
public void setTimeoutMs(int timeoutMs) {
|
||||||
this.timeoutMs = timeoutMs;
|
this.timeoutMs = timeoutMs;
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ public abstract class AbstractChannel
|
|||||||
|
|
||||||
protected void init(int recipient, long remoteWinSize, long remoteMaxPacketSize) {
|
protected void init(int recipient, long remoteWinSize, long remoteMaxPacketSize) {
|
||||||
this.recipient = recipient;
|
this.recipient = recipient;
|
||||||
rwin = new Window.Remote(remoteWinSize, (int) Math.min(remoteMaxPacketSize, REMOTE_MAX_PACKET_SIZE_CEILING), loggerFactory);
|
rwin = new Window.Remote(remoteWinSize, (int) Math.min(remoteMaxPacketSize, REMOTE_MAX_PACKET_SIZE_CEILING),
|
||||||
|
conn.getTimeoutMs(), loggerFactory);
|
||||||
out = new ChannelOutputStream(this, trans, rwin);
|
out = new ChannelOutputStream(this, trans, rwin);
|
||||||
log.debug("Initialized - {}", this);
|
log.debug("Initialized - {}", this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkClose() throws SSHException {
|
private void checkClose() throws SSHException {
|
||||||
if (closed) {
|
// Check whether either the Stream is closed, or the underlying channel is closed
|
||||||
|
if (closed || !chan.isOpen()) {
|
||||||
if (error != null)
|
if (error != null)
|
||||||
throw error;
|
throw error;
|
||||||
else
|
else
|
||||||
@@ -160,7 +161,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() throws IOException {
|
public synchronized void close() throws IOException {
|
||||||
if (!closed) {
|
// Not closed yet, and underlying channel is open to flush the data to.
|
||||||
|
if (!closed && chan.isOpen()) {
|
||||||
try {
|
try {
|
||||||
buffer.flush(false);
|
buffer.flush(false);
|
||||||
// trans.write(new SSHPacket(Message.CHANNEL_EOF).putUInt32(chan.getRecipient()));
|
// trans.write(new SSHPacket(Message.CHANNEL_EOF).putUInt32(chan.getRecipient()));
|
||||||
|
|||||||
@@ -39,15 +39,21 @@ public class SocketStreamCopyMonitor
|
|||||||
new SocketStreamCopyMonitor(new Runnable() {
|
new SocketStreamCopyMonitor(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
for (Event<IOException> ev = x;
|
await(x);
|
||||||
!ev.tryAwait(frequency, unit);
|
await(y);
|
||||||
ev = (ev == x) ? y : x) {
|
|
||||||
}
|
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(channel, asCloseable(socket));
|
IOUtils.closeQuietly(channel, asCloseable(socket));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void await(final Event<IOException> event) throws IOException {
|
||||||
|
while(true){
|
||||||
|
if(event.tryAwait(frequency, unit)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import net.schmizz.sshj.common.SSHRuntimeException;
|
|||||||
import net.schmizz.sshj.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public abstract class Window {
|
public abstract class Window {
|
||||||
|
|
||||||
protected final Logger log;
|
protected final Logger log;
|
||||||
@@ -73,17 +75,23 @@ public abstract class Window {
|
|||||||
/** Controls how much data we can send before an adjustment notification from remote end is required. */
|
/** Controls how much data we can send before an adjustment notification from remote end is required. */
|
||||||
public static final class Remote
|
public static final class Remote
|
||||||
extends Window {
|
extends Window {
|
||||||
|
private final long timeoutMs;
|
||||||
|
|
||||||
public Remote(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
|
public Remote(long initialWinSize, int maxPacketSize, long timeoutMs, LoggerFactory loggerFactory) {
|
||||||
super(initialWinSize, maxPacketSize, loggerFactory);
|
super(initialWinSize, maxPacketSize, loggerFactory);
|
||||||
|
this.timeoutMs = timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long awaitExpansion(long was) throws ConnectionException {
|
public long awaitExpansion(long was) throws ConnectionException {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
long end = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
|
||||||
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);
|
||||||
try {
|
try {
|
||||||
lock.wait();
|
lock.wait(timeoutMs);
|
||||||
|
if ((size <= was) && ((System.nanoTime() - end) > 0)) {
|
||||||
|
throw new ConnectionException("Timeout when trying to expand the window size");
|
||||||
|
}
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
throw new ConnectionException(ie);
|
throw new ConnectionException(ie);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import net.schmizz.sshj.transport.TransportException;
|
|||||||
import java.nio.charset.Charset;
|
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 initiated by the client. */
|
||||||
public abstract class AbstractDirectChannel
|
public abstract class AbstractDirectChannel
|
||||||
extends AbstractChannel
|
extends AbstractChannel
|
||||||
implements Channel.Direct {
|
implements Channel.Direct {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.connection.channel.direct;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.connection.Connection;
|
||||||
|
|
||||||
|
/** A channel for creating a direct TCP/IP connection from the server to a remote address. */
|
||||||
|
public class DirectConnection extends DirectTCPIPChannel {
|
||||||
|
public static final String LOCALHOST = "localhost";
|
||||||
|
public static final int LOCALPORT = 65536;
|
||||||
|
|
||||||
|
public DirectConnection(Connection conn, String remoteHost, int remotePort) {
|
||||||
|
super(conn, new Parameters(LOCALHOST, LOCALPORT, remoteHost, remotePort));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemoteHost() {
|
||||||
|
return parameters.getRemoteHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemotePort() {
|
||||||
|
return parameters.getRemotePort();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.connection.channel.direct;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.SSHPacket;
|
||||||
|
import net.schmizz.sshj.connection.Connection;
|
||||||
|
|
||||||
|
public class DirectTCPIPChannel extends AbstractDirectChannel {
|
||||||
|
protected final Parameters parameters;
|
||||||
|
|
||||||
|
protected DirectTCPIPChannel(Connection conn, Parameters parameters) {
|
||||||
|
super(conn, "direct-tcpip");
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SSHPacket buildOpenReq() {
|
||||||
|
return super.buildOpenReq()
|
||||||
|
.putString(parameters.getRemoteHost())
|
||||||
|
.putUInt32(parameters.getRemotePort())
|
||||||
|
.putString(parameters.getLocalHost())
|
||||||
|
.putUInt32(parameters.getLocalPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,6 @@ package net.schmizz.sshj.connection.channel.direct;
|
|||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.common.IOUtils;
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
import net.schmizz.sshj.common.LoggerFactory;
|
import net.schmizz.sshj.common.LoggerFactory;
|
||||||
import net.schmizz.sshj.common.SSHPacket;
|
|
||||||
import net.schmizz.sshj.common.StreamCopier;
|
import net.schmizz.sshj.common.StreamCopier;
|
||||||
import net.schmizz.sshj.connection.Connection;
|
import net.schmizz.sshj.connection.Connection;
|
||||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||||
@@ -34,48 +33,14 @@ import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
|||||||
|
|
||||||
public class LocalPortForwarder {
|
public class LocalPortForwarder {
|
||||||
|
|
||||||
public static class Parameters {
|
public static class ForwardedChannel
|
||||||
|
extends DirectTCPIPChannel {
|
||||||
private final String localHost;
|
|
||||||
private final int localPort;
|
|
||||||
private final String remoteHost;
|
|
||||||
private final int remotePort;
|
|
||||||
|
|
||||||
public Parameters(String localHost, int localPort, String remoteHost, int remotePort) {
|
|
||||||
this.localHost = localHost;
|
|
||||||
this.localPort = localPort;
|
|
||||||
this.remoteHost = remoteHost;
|
|
||||||
this.remotePort = remotePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRemoteHost() {
|
|
||||||
return remoteHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemotePort() {
|
|
||||||
return remotePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLocalHost() {
|
|
||||||
return localHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLocalPort() {
|
|
||||||
return localPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DirectTCPIPChannel
|
|
||||||
extends AbstractDirectChannel {
|
|
||||||
|
|
||||||
protected final Socket socket;
|
protected final Socket socket;
|
||||||
protected final Parameters parameters;
|
|
||||||
|
|
||||||
public DirectTCPIPChannel(Connection conn, Socket socket, Parameters parameters) {
|
public ForwardedChannel(Connection conn, Socket socket, Parameters parameters) {
|
||||||
super(conn, "direct-tcpip");
|
super(conn, parameters);
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.parameters = parameters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void start()
|
protected void start()
|
||||||
@@ -90,16 +55,6 @@ public class LocalPortForwarder {
|
|||||||
.spawnDaemon("chan2soc");
|
.spawnDaemon("chan2soc");
|
||||||
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
|
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SSHPacket buildOpenReq() {
|
|
||||||
return super.buildOpenReq()
|
|
||||||
.putString(parameters.getRemoteHost())
|
|
||||||
.putUInt32(parameters.getRemotePort())
|
|
||||||
.putString(parameters.getLocalHost())
|
|
||||||
.putUInt32(parameters.getLocalPort());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final LoggerFactory loggerFactory;
|
private final LoggerFactory loggerFactory;
|
||||||
@@ -118,7 +73,7 @@ public class LocalPortForwarder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startChannel(Socket socket) throws IOException {
|
private void startChannel(Socket socket) throws IOException {
|
||||||
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
|
ForwardedChannel chan = new ForwardedChannel(conn, socket, parameters);
|
||||||
try {
|
try {
|
||||||
chan.open();
|
chan.open();
|
||||||
chan.start();
|
chan.start();
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.connection.channel.direct;
|
||||||
|
|
||||||
|
public class Parameters {
|
||||||
|
|
||||||
|
private final String localHost;
|
||||||
|
private final int localPort;
|
||||||
|
private final String remoteHost;
|
||||||
|
private final int remotePort;
|
||||||
|
|
||||||
|
public Parameters(String localHost, int localPort, String remoteHost, int remotePort) {
|
||||||
|
this.localHost = localHost;
|
||||||
|
this.localPort = localPort;
|
||||||
|
this.remoteHost = remoteHost;
|
||||||
|
this.remotePort = remotePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemoteHost() {
|
||||||
|
return remoteHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemotePort() {
|
||||||
|
return remotePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocalHost() {
|
||||||
|
return localHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLocalPort() {
|
||||||
|
return localPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -191,7 +191,7 @@ public interface Session
|
|||||||
TransportException;
|
TransportException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an enviornment variable.
|
* Set an environment variable.
|
||||||
*
|
*
|
||||||
* @param name name of the variable
|
* @param name name of the variable
|
||||||
* @param value value to set
|
* @param value value to set
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public interface ForwardedChannelOpener {
|
|||||||
/**
|
/**
|
||||||
* Delegates a {@code SSH_MSG_CHANNEL_OPEN} request for the channel type claimed by this opener.
|
* Delegates a {@code SSH_MSG_CHANNEL_OPEN} request for the channel type claimed by this opener.
|
||||||
*
|
*
|
||||||
* @param buf {@link SSHPacket} containg the request except for the message identifier and channel type field
|
* @param buf {@link SSHPacket} containing the request except for the message identifier and channel type field
|
||||||
*/
|
*/
|
||||||
void handleOpen(SSHPacket buf)
|
void handleOpen(SSHPacket buf)
|
||||||
throws ConnectionException, TransportException;
|
throws ConnectionException, TransportException;
|
||||||
|
|||||||
@@ -18,8 +18,14 @@ package net.schmizz.sshj.sftp;
|
|||||||
public class PathComponents {
|
public class PathComponents {
|
||||||
|
|
||||||
static String adjustForParent(String parent, String path, String pathSep) {
|
static String adjustForParent(String parent, String path, String pathSep) {
|
||||||
return (path.startsWith(pathSep)) ? path // Absolute path, nothing to adjust
|
if (path.startsWith(pathSep)) {
|
||||||
: (parent + (parent.endsWith(pathSep) ? "" : pathSep) + path); // Relative path
|
return path; // Absolute path, nothing to adjust
|
||||||
|
} else if (parent.endsWith(pathSep)) {
|
||||||
|
return parent + path; // Relative path, parent endsWith '/'
|
||||||
|
} else if (parent.isEmpty()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return parent + pathSep + path; // Relative path
|
||||||
}
|
}
|
||||||
|
|
||||||
static String trimTrailingSeparator(String somePath, String pathSep) {
|
static String trimTrailingSeparator(String somePath, String pathSep) {
|
||||||
@@ -33,7 +39,8 @@ public class PathComponents {
|
|||||||
public PathComponents(String parent, String name, String pathSep) {
|
public PathComponents(String parent, String name, String pathSep) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.path = trimTrailingSeparator(adjustForParent(parent, name, pathSep), pathSep);
|
String adjusted = adjustForParent(parent, name, pathSep);
|
||||||
|
this.path = !pathSep.equals(adjusted) ? trimTrailingSeparator(adjusted, pathSep) : adjusted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getParent() {
|
public String getParent() {
|
||||||
|
|||||||
@@ -70,16 +70,25 @@ public class PathHelper {
|
|||||||
*/
|
*/
|
||||||
public PathComponents getComponents(final String path)
|
public PathComponents getComponents(final String path)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (path.equals(pathSep))
|
if (path.equals(pathSep)) {
|
||||||
return getComponents("", "");
|
return getComponents("", "/");
|
||||||
|
}
|
||||||
|
|
||||||
if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path))
|
if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path)) {
|
||||||
return getComponents(getDotDir());
|
return getComponents(getDotDir());
|
||||||
|
}
|
||||||
|
|
||||||
final String withoutTrailSep = trimTrailingSeparator(path);
|
final String withoutTrailSep = trimTrailingSeparator(path);
|
||||||
final int lastSep = withoutTrailSep.lastIndexOf(pathSep);
|
final int lastSep = withoutTrailSep.lastIndexOf(pathSep);
|
||||||
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep);
|
String parent;
|
||||||
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length());
|
String name;
|
||||||
|
if (lastSep == -1) {
|
||||||
|
parent = "";
|
||||||
|
name = withoutTrailSep;
|
||||||
|
} else {
|
||||||
|
parent = lastSep == 0 ? "/" : withoutTrailSep.substring(0, lastSep);
|
||||||
|
name = withoutTrailSep.substring(lastSep + pathSep.length());
|
||||||
|
}
|
||||||
|
|
||||||
if (".".equals(name) || "..".equals(name)) {
|
if (".".equals(name) || "..".equals(name)) {
|
||||||
return getComponents(canonicalizer.canonicalize(path));
|
return getComponents(canonicalizer.canonicalize(path));
|
||||||
@@ -87,5 +96,4 @@ public class PathHelper {
|
|||||||
return getComponents(parent, name);
|
return getComponents(parent, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -68,6 +68,9 @@ public final class Response
|
|||||||
this.code = code;
|
this.code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int protocolVersion;
|
private final int protocolVersion;
|
||||||
|
|||||||
@@ -29,18 +29,26 @@ public abstract class AbstractSignature
|
|||||||
|
|
||||||
@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
||||||
protected final java.security.Signature signature;
|
protected final java.security.Signature signature;
|
||||||
|
private final String signatureName;
|
||||||
|
|
||||||
protected AbstractSignature(String algorithm) {
|
protected AbstractSignature(String algorithm, String signatureName) {
|
||||||
try {
|
try {
|
||||||
this.signature = SecurityUtils.getSignature(algorithm);
|
this.signature = SecurityUtils.getSignature(algorithm);
|
||||||
|
this.signatureName = signatureName;
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AbstractSignature(@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
protected AbstractSignature(@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
||||||
java.security.Signature signatureEngine) {
|
java.security.Signature signatureEngine, String signatureName) {
|
||||||
this.signature = signatureEngine;
|
this.signature = signatureEngine;
|
||||||
|
this.signatureName = signatureName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSignatureName() {
|
||||||
|
return signatureName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import java.security.PublicKey;
|
|||||||
/** Signature interface for SSH used to sign or verify data. Usually wraps a {@code javax.crypto.Signature} object. */
|
/** Signature interface for SSH used to sign or verify data. Usually wraps a {@code javax.crypto.Signature} object. */
|
||||||
public interface Signature {
|
public interface Signature {
|
||||||
|
|
||||||
|
String getSignatureName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize this signature with the given public key for signature verification.
|
* Initialize this signature with the given public key for signature verification.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class SignatureDSA
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SignatureDSA() {
|
public SignatureDSA() {
|
||||||
super("SHA1withDSA");
|
super("SHA1withDSA", KeyType.DSA.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -72,8 +72,8 @@ public class SignatureDSA
|
|||||||
|
|
||||||
// result must be 40 bytes, but length of r and s may not be 20 bytes
|
// result must be 40 bytes, but length of r and s may not be 20 bytes
|
||||||
|
|
||||||
int r_copylen = (r.length < 20) ? r.length : 20;
|
int r_copylen = Math.min(r.length, 20);
|
||||||
int s_copylen = (s.length < 20) ? s.length : 20;
|
int s_copylen = Math.min(s.length, 20);
|
||||||
|
|
||||||
System.arraycopy(r, r.length - r_copylen, result, 20 - r_copylen, r_copylen);
|
System.arraycopy(r, r.length - r_copylen, result, 20 - r_copylen, r_copylen);
|
||||||
System.arraycopy(s, s.length - s_copylen, result, 40 - s_copylen, s_copylen);
|
System.arraycopy(s, s.length - s_copylen, result, 40 - s_copylen, s_copylen);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class SignatureECDSA extends AbstractSignature {
|
|||||||
private String keyTypeName;
|
private String keyTypeName;
|
||||||
|
|
||||||
public SignatureECDSA(String algorithm, String keyTypeName) {
|
public SignatureECDSA(String algorithm, String keyTypeName) {
|
||||||
super(algorithm);
|
super(algorithm, keyTypeName);
|
||||||
this.keyTypeName = keyTypeName;
|
this.keyTypeName = keyTypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.signature;
|
package net.schmizz.sshj.signature;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||||
import net.schmizz.sshj.common.KeyType;
|
import net.schmizz.sshj.common.KeyType;
|
||||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
|
||||||
/** RSA {@link Signature} */
|
/** RSA {@link Signature} */
|
||||||
@@ -25,23 +28,85 @@ public class SignatureRSA
|
|||||||
extends AbstractSignature {
|
extends AbstractSignature {
|
||||||
|
|
||||||
/** A named factory for RSA {@link Signature} */
|
/** A named factory for RSA {@link Signature} */
|
||||||
public static class Factory
|
public static class FactorySSHRSA
|
||||||
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 SignatureRSA();
|
return new SignatureRSA("SHA1withRSA", KeyType.RSA, KeyType.RSA.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return KeyType.RSA.toString();
|
return KeyType.RSA.toString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A named factory for RSA {@link Signature} */
|
||||||
|
public static class FactoryRSASHA256
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Signature create() {
|
||||||
|
return new SignatureRSA("SHA256withRSA", KeyType.RSA, "rsa-sha2-256");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "rsa-sha2-256";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** A named factory for RSA {@link Signature} */
|
||||||
|
public static class FactoryRSASHA512
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Signature create() {
|
||||||
|
return new SignatureRSA("SHA512withRSA", KeyType.RSA, "rsa-sha2-512");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "rsa-sha2-512";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A named factory for RSA {@link Signature} */
|
||||||
|
public static class FactoryCERT
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Signature create() {
|
||||||
|
return new SignatureRSA("SHA1withRSA", KeyType.RSA_CERT, KeyType.RSA.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return KeyType.RSA_CERT.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignatureRSA() {
|
private KeyType keyType;
|
||||||
super("SHA1withRSA");
|
|
||||||
|
|
||||||
|
public SignatureRSA(String algorithm, KeyType keyType, String name) {
|
||||||
|
super(algorithm, name);
|
||||||
|
this.keyType = keyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void initVerify(PublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
if (this.keyType.equals(KeyType.RSA_CERT) && publicKey instanceof Certificate) {
|
||||||
|
signature.initVerify(((Certificate<PublicKey>) publicKey).getKey());
|
||||||
|
} else {
|
||||||
|
signature.initVerify(publicKey);
|
||||||
|
}
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -51,12 +116,11 @@ public class SignatureRSA
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(byte[] sig) {
|
public boolean verify(byte[] sig) {
|
||||||
sig = extractSig(sig, "ssh-rsa");
|
sig = extractSig(sig, getSignatureName());
|
||||||
try {
|
try {
|
||||||
return signature.verify(sig);
|
return signature.verify(sig);
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ abstract class Converter {
|
|||||||
protected int cipherSize = 8;
|
protected int cipherSize = 8;
|
||||||
protected long seq = -1;
|
protected long seq = -1;
|
||||||
protected boolean authed;
|
protected boolean authed;
|
||||||
|
protected boolean etm;
|
||||||
|
|
||||||
long getSequenceNumber() {
|
long getSequenceNumber() {
|
||||||
return seq;
|
return seq;
|
||||||
@@ -56,6 +57,7 @@ abstract class Converter {
|
|||||||
if (compression != null)
|
if (compression != null)
|
||||||
compression.init(getCompressionType());
|
compression.init(getCompressionType());
|
||||||
this.cipherSize = cipher.getIVSize();
|
this.cipherSize = cipher.getIVSize();
|
||||||
|
this.etm = mac.isEtm();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAuthenticated() {
|
void setAuthenticated() {
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ 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;
|
||||||
|
|
||||||
/** 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
|
||||||
extends Converter {
|
extends Converter {
|
||||||
|
|
||||||
@@ -29,16 +31,26 @@ final class Decoder
|
|||||||
|
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
|
|
||||||
/** What we pass decoded packets to */
|
/**
|
||||||
|
* What we pass decoded packets to
|
||||||
|
*/
|
||||||
private final SSHPacketHandler packetHandler;
|
private final SSHPacketHandler packetHandler;
|
||||||
/** Buffer where as-yet undecoded data lives */
|
/**
|
||||||
|
* Buffer where as-yet undecoded data lives
|
||||||
|
*/
|
||||||
private final SSHPacket inputBuffer = new SSHPacket();
|
private final SSHPacket inputBuffer = new SSHPacket();
|
||||||
/** Used in case compression is active to store the uncompressed data */
|
/**
|
||||||
|
* Used in case compression is active to store the uncompressed data
|
||||||
|
*/
|
||||||
private final SSHPacket uncompressBuffer = new SSHPacket();
|
private final SSHPacket uncompressBuffer = new SSHPacket();
|
||||||
/** MAC result is stored here */
|
/**
|
||||||
|
* MAC result is stored here
|
||||||
|
*/
|
||||||
private byte[] macResult;
|
private byte[] macResult;
|
||||||
|
|
||||||
/** -1 if packet length not yet been decoded, else the packet length */
|
/**
|
||||||
|
* -1 if packet length not yet been decoded, else the packet length
|
||||||
|
*/
|
||||||
private int packetLength = -1;
|
private int packetLength = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,54 +72,98 @@ final class Decoder
|
|||||||
*/
|
*/
|
||||||
private int decode()
|
private int decode()
|
||||||
throws SSHException {
|
throws SSHException {
|
||||||
|
|
||||||
|
if (etm) {
|
||||||
|
return decodeEtm();
|
||||||
|
} else {
|
||||||
|
return decodeMte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode an Encrypt-Then-Mac packet.
|
||||||
|
*/
|
||||||
|
private int decodeEtm() throws SSHException {
|
||||||
|
int bytesNeeded;
|
||||||
|
while (true) {
|
||||||
|
if (packetLength == -1) {
|
||||||
|
assert inputBuffer.rpos() == 0 : "buffer cleared";
|
||||||
|
bytesNeeded = 4 - inputBuffer.available();
|
||||||
|
if (bytesNeeded <= 0) {
|
||||||
|
// In Encrypt-Then-Mac, the packetlength is sent unencrypted.
|
||||||
|
packetLength = inputBuffer.readUInt32AsInt();
|
||||||
|
checkPacketLength(packetLength);
|
||||||
|
} else {
|
||||||
|
// Needs more data
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert inputBuffer.rpos() == 4 : "packet length read";
|
||||||
|
bytesNeeded = packetLength + mac.getBlockSize() - inputBuffer.available();
|
||||||
|
if (bytesNeeded <= 0) {
|
||||||
|
seq = seq + 1 & 0xffffffffL;
|
||||||
|
checkMAC(inputBuffer.array());
|
||||||
|
decryptBuffer(4, packetLength);
|
||||||
|
inputBuffer.wpos(packetLength + 4 - inputBuffer.readByte());
|
||||||
|
final SSHPacket plain = usingCompression() ? decompressed() : inputBuffer;
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("Received packet #{}: {}", seq, plain.printHex());
|
||||||
|
}
|
||||||
|
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet
|
||||||
|
inputBuffer.clear();
|
||||||
|
packetLength = -1;
|
||||||
|
} else {
|
||||||
|
// Needs more data
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytesNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a Mac-Then-Encrypt packet
|
||||||
|
* @return
|
||||||
|
* @throws SSHException
|
||||||
|
*/
|
||||||
|
private int decodeMte() throws SSHException {
|
||||||
int need;
|
int need;
|
||||||
|
|
||||||
/* Decoding loop */
|
/* Decoding loop */
|
||||||
for (; ; )
|
for (; ; )
|
||||||
|
|
||||||
if (packetLength == -1) // Waiting for beginning of packet
|
if (packetLength == -1) { // Waiting for beginning of packet
|
||||||
{
|
|
||||||
|
|
||||||
assert inputBuffer.rpos() == 0 : "buffer cleared";
|
assert inputBuffer.rpos() == 0 : "buffer cleared";
|
||||||
|
|
||||||
need = cipherSize - inputBuffer.available();
|
need = cipherSize - inputBuffer.available();
|
||||||
if (need <= 0)
|
if (need <= 0) {
|
||||||
packetLength = decryptLength();
|
packetLength = decryptLength();
|
||||||
else
|
} else {
|
||||||
// Need more data
|
// Need more data
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
assert inputBuffer.rpos() == 4 : "packet length read";
|
assert inputBuffer.rpos() == 4 : "packet length read";
|
||||||
|
|
||||||
need = packetLength + (mac != null ? mac.getBlockSize() : 0) - inputBuffer.available();
|
need = packetLength + (mac != null ? mac.getBlockSize() : 0) - inputBuffer.available();
|
||||||
if (need <= 0) {
|
if (need <= 0) {
|
||||||
|
decryptBuffer(cipherSize, packetLength + 4 - cipherSize); // Decrypt the rest of the payload
|
||||||
decryptPayload(inputBuffer.array());
|
|
||||||
|
|
||||||
seq = seq + 1 & 0xffffffffL;
|
seq = seq + 1 & 0xffffffffL;
|
||||||
|
if (mac != null) {
|
||||||
if (mac != null)
|
|
||||||
checkMAC(inputBuffer.array());
|
checkMAC(inputBuffer.array());
|
||||||
|
}
|
||||||
// Exclude the padding & MAC
|
// Exclude the padding & MAC
|
||||||
inputBuffer.wpos(packetLength + 4 - inputBuffer.readByte());
|
inputBuffer.wpos(packetLength + 4 - inputBuffer.readByte());
|
||||||
|
|
||||||
final SSHPacket plain = usingCompression() ? decompressed() : inputBuffer;
|
final SSHPacket plain = usingCompression() ? decompressed() : inputBuffer;
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
if (log.isTraceEnabled())
|
|
||||||
log.trace("Received packet #{}: {}", seq, plain.printHex());
|
log.trace("Received packet #{}: {}", seq, plain.printHex());
|
||||||
|
}
|
||||||
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet
|
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet
|
||||||
|
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
packetLength = -1;
|
packetLength = -1;
|
||||||
|
} else {
|
||||||
} else
|
|
||||||
// Need more data
|
// Need more data
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return need;
|
return need;
|
||||||
}
|
}
|
||||||
@@ -118,9 +174,10 @@ final class Decoder
|
|||||||
mac.update(data, 0, packetLength + 4); // packetLength+4 = entire packet w/o mac
|
mac.update(data, 0, packetLength + 4); // packetLength+4 = entire packet w/o mac
|
||||||
mac.doFinal(macResult, 0); // compute
|
mac.doFinal(macResult, 0); // compute
|
||||||
// Check against the received MAC
|
// Check against the received MAC
|
||||||
if (!ByteArrayUtils.equals(macResult, 0, data, packetLength + 4, mac.getBlockSize()))
|
if (!ByteArrayUtils.equals(macResult, 0, data, packetLength + 4, mac.getBlockSize())) {
|
||||||
throw new TransportException(DisconnectReason.MAC_ERROR, "MAC Error");
|
throw new TransportException(DisconnectReason.MAC_ERROR, "MAC Error");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SSHPacket decompressed()
|
private SSHPacket decompressed()
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
@@ -131,7 +188,7 @@ final class Decoder
|
|||||||
|
|
||||||
private int decryptLength()
|
private int decryptLength()
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
cipher.update(inputBuffer.array(), 0, cipherSize);
|
decryptBuffer(0, cipherSize);
|
||||||
|
|
||||||
final int len; // Read packet length
|
final int len; // Read packet length
|
||||||
try {
|
try {
|
||||||
@@ -140,21 +197,25 @@ final class Decoder
|
|||||||
throw new TransportException(be);
|
throw new TransportException(be);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInvalidPacketLength(len)) { // Check packet length validity
|
checkPacketLength(len);
|
||||||
log.error("Error decoding packet (invalid length) {}", inputBuffer.printHex());
|
|
||||||
throw new TransportException(DisconnectReason.PROTOCOL_ERROR, "invalid packet length: " + len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isInvalidPacketLength(int len) {
|
private void decryptBuffer(int offset, int length) {
|
||||||
return len < 5 || len > MAX_PACKET_LEN;
|
cipher.update(inputBuffer.array(), offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptPayload(final byte[] data) {
|
private void checkPacketLength(int len) throws TransportException {
|
||||||
cipher.update(data, cipherSize, packetLength + 4 - cipherSize);
|
if (len < 5 || len > MAX_PACKET_LEN) { // Check packet length validity
|
||||||
|
log.error("Error decoding packet (invalid length) {}", inputBuffer.printHex());
|
||||||
|
throw new TransportException(DisconnectReason.PROTOCOL_ERROR, "invalid packet length: " + len);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void decryptPayload(final byte[] data, int offset, int length) {
|
||||||
|
// cipher.update(data, cipherSize, packetLength + 4 - cipherSize);
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds {@code len} bytes from {@code b} to the decoder buffer. When a packet has been successfully decoded, hooks
|
* Adds {@code len} bytes from {@code b} to the decoder buffer. When a packet has been successfully decoded, hooks
|
||||||
|
|||||||
@@ -67,36 +67,58 @@ final class Encoder
|
|||||||
log.trace("Encoding packet #{}: {}", seq + 1, buffer.printHex());
|
log.trace("Encoding packet #{}: {}", seq + 1, buffer.printHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usingCompression())
|
if (usingCompression()) {
|
||||||
compress(buffer);
|
compress(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
final int payloadSize = buffer.available();
|
final int payloadSize = buffer.available();
|
||||||
|
int lengthWithoutPadding;
|
||||||
|
if (etm) {
|
||||||
|
// in Encrypt-Then-Mac mode, the length field is not encrypted, so we should keep it out of the
|
||||||
|
// padding length calculation
|
||||||
|
lengthWithoutPadding = 1 + payloadSize; // padLength (1 byte) + payload
|
||||||
|
} else {
|
||||||
|
lengthWithoutPadding = 4 + 1 + payloadSize; // packetLength (4 bytes) + padLength (1 byte) + payload
|
||||||
|
}
|
||||||
|
|
||||||
// Compute padding length
|
// Compute padding length
|
||||||
int padLen = -(payloadSize + 5) & cipherSize - 1;
|
int padLen = cipherSize - (lengthWithoutPadding % cipherSize);
|
||||||
if (padLen < cipherSize)
|
if (padLen < 4) {
|
||||||
padLen += cipherSize;
|
padLen += cipherSize;
|
||||||
|
}
|
||||||
|
|
||||||
final int startOfPacket = buffer.rpos() - 5;
|
final int startOfPacket = buffer.rpos() - 5;
|
||||||
final int packetLen = payloadSize + 1 + padLen;
|
int packetLen = 1 + payloadSize + padLen; // packetLength = padLen (1 byte) + payload + padding
|
||||||
|
|
||||||
|
if (packetLen < 16) {
|
||||||
|
padLen += cipherSize;
|
||||||
|
packetLen = 1 + payloadSize + padLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int endOfPadding = startOfPacket + 4 + packetLen;
|
||||||
|
|
||||||
// Put packet header
|
// Put packet header
|
||||||
buffer.wpos(startOfPacket);
|
buffer.wpos(startOfPacket);
|
||||||
buffer.putUInt32(packetLen);
|
buffer.putUInt32(packetLen);
|
||||||
buffer.putByte((byte) padLen);
|
buffer.putByte((byte) padLen);
|
||||||
|
|
||||||
// Now wpos will mark end of padding
|
// Now wpos will mark end of padding
|
||||||
buffer.wpos(startOfPacket + 5 + payloadSize + padLen);
|
buffer.wpos(endOfPadding);
|
||||||
|
|
||||||
// Fill padding
|
// Fill padding
|
||||||
prng.fill(buffer.array(), buffer.wpos() - padLen, padLen);
|
prng.fill(buffer.array(), endOfPadding - padLen, padLen);
|
||||||
|
|
||||||
seq = seq + 1 & 0xffffffffL;
|
seq = seq + 1 & 0xffffffffL;
|
||||||
|
|
||||||
if (mac != null)
|
if (etm) {
|
||||||
putMAC(buffer, startOfPacket, buffer.wpos());
|
cipher.update(buffer.array(), startOfPacket + 4, packetLen);
|
||||||
|
putMAC(buffer, startOfPacket, endOfPadding);
|
||||||
|
} else {
|
||||||
|
if (mac != null) {
|
||||||
|
putMAC(buffer, startOfPacket, endOfPadding);
|
||||||
|
}
|
||||||
|
|
||||||
cipher.update(buffer.array(), startOfPacket, 4 + packetLen);
|
cipher.update(buffer.array(), startOfPacket, 4 + packetLen);
|
||||||
|
}
|
||||||
buffer.rpos(startOfPacket); // Make ready-to-read
|
buffer.rpos(startOfPacket); // Make ready-to-read
|
||||||
|
|
||||||
return seq;
|
return seq;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.common.*;
|
import net.schmizz.sshj.common.*;
|
||||||
@@ -30,9 +31,7 @@ import org.slf4j.Logger;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ final class KeyExchanger
|
|||||||
* Add a callback for host key verification.
|
* Add a callback for host key verification.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Any of the {@link HostKeyVerifier} implementations added this way can deem a host key to be acceptable, allowing
|
* Any of the {@link HostKeyVerifier} implementations added this way can deem a host key to be acceptable, allowing
|
||||||
* key exchange to successfuly complete. Otherwise, a {@link TransportException} will result during key exchange.
|
* key exchange to successfully complete. Otherwise, a {@link TransportException} will result during key exchange.
|
||||||
*
|
*
|
||||||
* @param hkv object whose {@link HostKeyVerifier#verify} method will be invoked
|
* @param hkv object whose {@link HostKeyVerifier#verify} method will be invoked
|
||||||
*/
|
*/
|
||||||
@@ -232,6 +231,13 @@ final class KeyExchanger
|
|||||||
}
|
}
|
||||||
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
|
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
|
||||||
negotiatedAlgs.getKeyExchangeAlgorithm());
|
negotiatedAlgs.getKeyExchangeAlgorithm());
|
||||||
|
|
||||||
|
List<KeyAlgorithm> keyAlgorithms = new ArrayList<KeyAlgorithm>();
|
||||||
|
for (String signatureAlgorithm : negotiatedAlgs.getSignatureAlgorithms()) {
|
||||||
|
keyAlgorithms.add(Factory.Named.Util.create(transport.getConfig().getKeyAlgorithms(), signatureAlgorithm));
|
||||||
|
}
|
||||||
|
transport.setKeyAlgorithms(keyAlgorithms);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
kex.init(transport,
|
kex.init(transport,
|
||||||
transport.getServerID(), transport.getClientID(),
|
transport.getServerID(), transport.getClientID(),
|
||||||
|
|||||||
@@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class NegotiatedAlgorithms {
|
public final class NegotiatedAlgorithms {
|
||||||
|
|
||||||
private final String kex;
|
private final String kex;
|
||||||
private final String sig;
|
private final List<String> availableSigs;
|
||||||
private final String c2sCipher;
|
private final String c2sCipher;
|
||||||
private final String s2cCipher;
|
private final String s2cCipher;
|
||||||
private final String c2sMAC;
|
private final String c2sMAC;
|
||||||
@@ -26,10 +28,10 @@ public final class NegotiatedAlgorithms {
|
|||||||
private final String c2sComp;
|
private final String c2sComp;
|
||||||
private final String s2cComp;
|
private final String s2cComp;
|
||||||
|
|
||||||
NegotiatedAlgorithms(String kex, String sig, String c2sCipher, String s2cCipher, String c2sMAC, String s2cMAC,
|
NegotiatedAlgorithms(String kex, List<String> availableSigs, String c2sCipher, String s2cCipher, String c2sMAC, String s2cMAC,
|
||||||
String c2sComp, String s2cComp) {
|
String c2sComp, String s2cComp) {
|
||||||
this.kex = kex;
|
this.kex = kex;
|
||||||
this.sig = sig;
|
this.availableSigs = availableSigs;
|
||||||
this.c2sCipher = c2sCipher;
|
this.c2sCipher = c2sCipher;
|
||||||
this.s2cCipher = s2cCipher;
|
this.s2cCipher = s2cCipher;
|
||||||
this.c2sMAC = c2sMAC;
|
this.c2sMAC = c2sMAC;
|
||||||
@@ -42,8 +44,8 @@ public final class NegotiatedAlgorithms {
|
|||||||
return kex;
|
return kex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSignatureAlgorithm() {
|
public List<String> getSignatureAlgorithms() {
|
||||||
return sig;
|
return availableSigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getClient2ServerCipherAlgorithm() {
|
public String getClient2ServerCipherAlgorithm() {
|
||||||
@@ -74,7 +76,7 @@ public final class NegotiatedAlgorithms {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return ("[ " +
|
return ("[ " +
|
||||||
"kex=" + kex + "; " +
|
"kex=" + kex + "; " +
|
||||||
"sig=" + sig + "; " +
|
"availableSigs=" + availableSigs + "; " +
|
||||||
"c2sCipher=" + c2sCipher + "; " +
|
"c2sCipher=" + c2sCipher + "; " +
|
||||||
"s2cCipher=" + s2cCipher + "; " +
|
"s2cCipher=" + s2cCipher + "; " +
|
||||||
"c2sMAC=" + c2sMAC + "; " +
|
"c2sMAC=" + c2sMAC + "; " +
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import net.schmizz.sshj.common.Factory;
|
|||||||
import net.schmizz.sshj.common.Message;
|
import net.schmizz.sshj.common.Message;
|
||||||
import net.schmizz.sshj.common.SSHPacket;
|
import net.schmizz.sshj.common.SSHPacket;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ class Proposal {
|
|||||||
|
|
||||||
public Proposal(Config config) {
|
public Proposal(Config config) {
|
||||||
kex = Factory.Named.Util.getNames(config.getKeyExchangeFactories());
|
kex = Factory.Named.Util.getNames(config.getKeyExchangeFactories());
|
||||||
sig = Factory.Named.Util.getNames(config.getSignatureFactories());
|
sig = Factory.Named.Util.getNames(config.getKeyAlgorithms());
|
||||||
c2sCipher = s2cCipher = Factory.Named.Util.getNames(config.getCipherFactories());
|
c2sCipher = s2cCipher = Factory.Named.Util.getNames(config.getCipherFactories());
|
||||||
c2sMAC = s2cMAC = Factory.Named.Util.getNames(config.getMACFactories());
|
c2sMAC = s2cMAC = Factory.Named.Util.getNames(config.getMACFactories());
|
||||||
c2sComp = s2cComp = Factory.Named.Util.getNames(config.getCompressionFactories());
|
c2sComp = s2cComp = Factory.Named.Util.getNames(config.getCompressionFactories());
|
||||||
@@ -126,7 +127,7 @@ class Proposal {
|
|||||||
throws TransportException {
|
throws TransportException {
|
||||||
return new NegotiatedAlgorithms(
|
return new NegotiatedAlgorithms(
|
||||||
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
|
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
|
||||||
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
|
allMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
|
||||||
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
|
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
|
||||||
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
|
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
|
||||||
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
|
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
|
||||||
@@ -138,19 +139,36 @@ class Proposal {
|
|||||||
|
|
||||||
private static String firstMatch(List<String> a, List<String> b)
|
private static String firstMatch(List<String> a, List<String> b)
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
for (String aa : a)
|
for (String aa : a) {
|
||||||
for (String bb : b)
|
if (b.contains(aa)) {
|
||||||
if (aa.equals(bb))
|
|
||||||
return aa;
|
return aa;
|
||||||
|
}
|
||||||
|
}
|
||||||
throw new TransportException("Unable to reach a settlement: " + a + " and " + b);
|
throw new TransportException("Unable to reach a settlement: " + a + " and " + b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<String> allMatch(List<String> a, List<String> b) throws TransportException {
|
||||||
|
List<String> res = new ArrayList<String>();
|
||||||
|
for (String aa : a) {
|
||||||
|
if (b.contains(aa)) {
|
||||||
|
res.add(aa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.isEmpty()) {
|
||||||
|
throw new TransportException("Unable to reach a settlement: " + a + " and " + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private static String toCommaString(List<String> sl) {
|
private static String toCommaString(List<String> sl) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (String s : sl) {
|
for (String s : sl) {
|
||||||
if (i++ != 0)
|
if (i++ != 0) {
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
|
}
|
||||||
sb.append(s);
|
sb.append(s);
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|||||||
@@ -15,9 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||||
import net.schmizz.sshj.Config;
|
import net.schmizz.sshj.Config;
|
||||||
import net.schmizz.sshj.Service;
|
import net.schmizz.sshj.Service;
|
||||||
import net.schmizz.sshj.common.DisconnectReason;
|
import net.schmizz.sshj.common.DisconnectReason;
|
||||||
|
import net.schmizz.sshj.common.KeyType;
|
||||||
import net.schmizz.sshj.common.SSHPacket;
|
import net.schmizz.sshj.common.SSHPacket;
|
||||||
import net.schmizz.sshj.common.SSHPacketHandler;
|
import net.schmizz.sshj.common.SSHPacketHandler;
|
||||||
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
||||||
@@ -92,7 +94,7 @@ public interface Transport
|
|||||||
int getHeartbeatInterval();
|
int getHeartbeatInterval();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param interval the interval in seconds, {@code 0} means no hearbeat
|
* @param interval the interval in seconds, {@code 0} means no heartbeat
|
||||||
* @deprecated Moved to {@link net.schmizz.keepalive.KeepAlive#getKeepAliveInterval()}. This is accessible through the {@link net.schmizz.sshj.connection.Connection}.
|
* @deprecated Moved to {@link net.schmizz.keepalive.KeepAlive#getKeepAliveInterval()}. This is accessible through the {@link net.schmizz.sshj.connection.Connection}.
|
||||||
* Scheduled to be removed in 0.12.0
|
* Scheduled to be removed in 0.12.0
|
||||||
*/
|
*/
|
||||||
@@ -156,7 +158,7 @@ public interface Transport
|
|||||||
*
|
*
|
||||||
* @return the sequence number of the packet sent
|
* @return the sequence number of the packet sent
|
||||||
*
|
*
|
||||||
* @throws TransportException if an error occured sending the packet
|
* @throws TransportException if an error occurred sending the packet
|
||||||
*/
|
*/
|
||||||
long sendUnimplemented()
|
long sendUnimplemented()
|
||||||
throws TransportException;
|
throws TransportException;
|
||||||
@@ -235,4 +237,6 @@ public interface Transport
|
|||||||
* @param e The exception that occurred.
|
* @param e The exception that occurred.
|
||||||
*/
|
*/
|
||||||
void die(Exception e);
|
void die(Exception e);
|
||||||
|
|
||||||
|
KeyAlgorithm getKeyAlgorithm(KeyType keyType) throws TransportException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||||
import com.hierynomus.sshj.transport.IdentificationStringParser;
|
import com.hierynomus.sshj.transport.IdentificationStringParser;
|
||||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
@@ -30,6 +31,7 @@ import org.slf4j.Logger;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
public final class TransportImpl
|
public final class TransportImpl
|
||||||
implements Transport, DisconnectListener {
|
implements Transport, DisconnectListener {
|
||||||
|
|
||||||
|
|
||||||
private static final class NullService
|
private static final class NullService
|
||||||
extends AbstractService {
|
extends AbstractService {
|
||||||
|
|
||||||
@@ -86,6 +89,8 @@ public final class TransportImpl
|
|||||||
|
|
||||||
private final Decoder decoder;
|
private final Decoder decoder;
|
||||||
|
|
||||||
|
private List<KeyAlgorithm> keyAlgorithms;
|
||||||
|
|
||||||
private final Event<TransportException> serviceAccept;
|
private final Event<TransportException> serviceAccept;
|
||||||
|
|
||||||
private final Event<TransportException> close;
|
private final Event<TransportException> close;
|
||||||
@@ -104,6 +109,11 @@ public final class TransportImpl
|
|||||||
*/
|
*/
|
||||||
private volatile Service service;
|
private volatile Service service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The next service that will be activated, only set when sending an SSH_MSG_SERVICE_REQUEST
|
||||||
|
*/
|
||||||
|
private volatile Service nextService;
|
||||||
|
|
||||||
private DisconnectListener disconnectListener;
|
private DisconnectListener disconnectListener;
|
||||||
|
|
||||||
private ConnInfo connInfo;
|
private ConnInfo connInfo;
|
||||||
@@ -324,8 +334,9 @@ public final class TransportImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setService(Service service) {
|
public synchronized void setService(Service service) {
|
||||||
if (service == null)
|
if (service == null) {
|
||||||
service = nullService;
|
service = nullService;
|
||||||
|
}
|
||||||
|
|
||||||
log.debug("Setting active service to {}", service.getName());
|
log.debug("Setting active service to {}", service.getName());
|
||||||
this.service = service;
|
this.service = service;
|
||||||
@@ -337,11 +348,12 @@ public final class TransportImpl
|
|||||||
serviceAccept.lock();
|
serviceAccept.lock();
|
||||||
try {
|
try {
|
||||||
serviceAccept.clear();
|
serviceAccept.clear();
|
||||||
|
this.nextService = service;
|
||||||
sendServiceRequest(service.getName());
|
sendServiceRequest(service.getName());
|
||||||
serviceAccept.await(timeoutMs, TimeUnit.MILLISECONDS);
|
serviceAccept.await(timeoutMs, TimeUnit.MILLISECONDS);
|
||||||
setService(service);
|
|
||||||
} finally {
|
} finally {
|
||||||
serviceAccept.unlock();
|
serviceAccept.unlock();
|
||||||
|
this.nextService = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,8 +497,8 @@ public final class TransportImpl
|
|||||||
* This method is called in the context of the {@link #reader} thread via {@link Decoder#received} when a full
|
* This method is called in the context of the {@link #reader} thread via {@link Decoder#received} when a full
|
||||||
* packet has been decoded.
|
* packet has been decoded.
|
||||||
*
|
*
|
||||||
* @param msg the message identifer
|
* @param msg the message identifier
|
||||||
* @param buf buffer containg rest of the packet
|
* @param buf buffer containing rest of the packet
|
||||||
* @throws SSHException if an error occurs during handling (unrecoverable)
|
* @throws SSHException if an error occurs during handling (unrecoverable)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -496,13 +508,11 @@ public final class TransportImpl
|
|||||||
|
|
||||||
log.trace("Received packet {}", msg);
|
log.trace("Received packet {}", msg);
|
||||||
|
|
||||||
if (msg.geq(50)) // not a transport layer packet
|
if (msg.geq(50)) { // not a transport layer packet
|
||||||
service.handle(msg, buf);
|
service.handle(msg, buf);
|
||||||
|
} else if (msg.in(20, 21) || msg.in(30, 49)) { // kex packet
|
||||||
else if (msg.in(20, 21) || msg.in(30, 49)) // kex packet
|
|
||||||
kexer.handle(msg, buf);
|
kexer.handle(msg, buf);
|
||||||
|
} else {
|
||||||
else
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case DISCONNECT:
|
case DISCONNECT:
|
||||||
gotDisconnect(buf);
|
gotDisconnect(buf);
|
||||||
@@ -527,6 +537,7 @@ public final class TransportImpl
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void gotDebug(SSHPacket buf)
|
private void gotDebug(SSHPacket buf)
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
@@ -558,6 +569,8 @@ public final class TransportImpl
|
|||||||
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");
|
||||||
|
// Immediately switch to next service to prevent race condition mentioned in #559
|
||||||
|
setService(nextService);
|
||||||
serviceAccept.set();
|
serviceAccept.set();
|
||||||
} finally {
|
} finally {
|
||||||
serviceAccept.unlock();
|
serviceAccept.unlock();
|
||||||
@@ -641,4 +654,18 @@ public final class TransportImpl
|
|||||||
return connInfo;
|
return connInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyAlgorithm getKeyAlgorithm(KeyType keyType) throws TransportException {
|
||||||
|
for (KeyAlgorithm ka : keyAlgorithms) {
|
||||||
|
if (ka.getKeyFormat().equals(keyType)) {
|
||||||
|
return ka;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TransportException("Cannot find an available KeyAlgorithm for type " + keyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyAlgorithms(List<KeyAlgorithm> keyAlgorithms) {
|
||||||
|
this.keyAlgorithms = keyAlgorithms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ public abstract class AbstractDHG extends AbstractDH
|
|||||||
digest.update(buf.array(), buf.rpos(), buf.available());
|
digest.update(buf.array(), buf.rpos(), buf.available());
|
||||||
H = digest.digest();
|
H = digest.digest();
|
||||||
|
|
||||||
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
|
||||||
KeyType.fromKey(hostKey).toString());
|
Signature signature = trans.getKeyAlgorithm(KeyType.fromKey(hostKey)).newSignature();
|
||||||
signature.initVerify(hostKey);
|
signature.initVerify(hostKey);
|
||||||
signature.update(H, 0, H.length);
|
signature.update(H, 0, H.length);
|
||||||
if (!signature.verify(sig))
|
if (!signature.verify(sig))
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||||
import net.schmizz.sshj.common.*;
|
import net.schmizz.sshj.common.*;
|
||||||
import net.schmizz.sshj.signature.Signature;
|
import net.schmizz.sshj.signature.Signature;
|
||||||
import net.schmizz.sshj.transport.Transport;
|
import net.schmizz.sshj.transport.Transport;
|
||||||
@@ -30,9 +31,9 @@ import java.security.GeneralSecurityException;
|
|||||||
public abstract class AbstractDHGex extends AbstractDH {
|
public abstract class AbstractDHGex extends AbstractDH {
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
private int minBits = 1024;
|
private final int minBits = 1024;
|
||||||
private int maxBits = 8192;
|
private final int maxBits = 8192;
|
||||||
private int preferredBits = 2048;
|
private final int preferredBits = 2048;
|
||||||
|
|
||||||
public AbstractDHGex(Digest digest) {
|
public AbstractDHGex(Digest digest) {
|
||||||
super(new DH(), digest);
|
super(new DH(), digest);
|
||||||
@@ -84,8 +85,8 @@ public abstract class AbstractDHGex extends AbstractDH {
|
|||||||
.putMPInt(k);
|
.putMPInt(k);
|
||||||
digest.update(buf.array(), buf.rpos(), buf.available());
|
digest.update(buf.array(), buf.rpos(), buf.available());
|
||||||
H = digest.digest();
|
H = digest.digest();
|
||||||
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
KeyAlgorithm keyAlgorithm = trans.getKeyAlgorithm(KeyType.fromKey(hostKey));
|
||||||
KeyType.fromKey(hostKey).toString());
|
Signature signature = keyAlgorithm.newSignature();
|
||||||
signature.initVerify(hostKey);
|
signature.initVerify(hostKey);
|
||||||
signature.update(H, 0, H.length);
|
signature.update(H, 0, H.length);
|
||||||
if (!signature.verify(sig))
|
if (!signature.verify(sig))
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||||
import net.schmizz.sshj.common.Factory;
|
import net.schmizz.sshj.common.Factory;
|
||||||
import net.schmizz.sshj.transport.random.Random;
|
import net.schmizz.sshj.transport.random.Random;
|
||||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
@@ -31,7 +32,7 @@ public class Curve25519DH extends DHBase {
|
|||||||
private byte[] secretKey;
|
private byte[] secretKey;
|
||||||
|
|
||||||
public Curve25519DH() {
|
public Curve25519DH() {
|
||||||
super("ECDSA", "ECDH");
|
super(KeyAlgorithm.ECDSA, "ECDH");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import java.security.GeneralSecurityException;
|
|||||||
|
|
||||||
public class Curve25519SHA256 extends AbstractDHG {
|
public class Curve25519SHA256 extends AbstractDHG {
|
||||||
/** Named factory for Curve25519SHA256 key exchange */
|
/** Named factory for Curve25519SHA256 key exchange */
|
||||||
public static class Factory
|
public static class FactoryLibSsh
|
||||||
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -35,6 +35,21 @@ public class Curve25519SHA256 extends AbstractDHG {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Named factory for Curve25519SHA256 key exchange */
|
||||||
|
public static class Factory
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyExchange create() {
|
||||||
|
return new Curve25519SHA256();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "curve25519-sha256";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Curve25519SHA256() {
|
public Curve25519SHA256() {
|
||||||
super(new Curve25519DH(), new SHA256());
|
super(new Curve25519DH(), new SHA256());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||||
import net.schmizz.sshj.common.Factory;
|
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 net.schmizz.sshj.transport.random.Random;
|
||||||
@@ -54,7 +55,7 @@ public class ECDH extends DHBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void computeK(byte[] f) throws GeneralSecurityException {
|
public void computeK(byte[] f) throws GeneralSecurityException {
|
||||||
KeyFactory keyFactory = SecurityUtils.getKeyFactory("EC");
|
KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.EC_BC);
|
||||||
ECPublicKeySpec keySpec = new ECPublicKeySpec(getDecoded(f, ecParameterSpec.getCurve()), ecParameterSpec);
|
ECPublicKeySpec keySpec = new ECPublicKeySpec(getDecoded(f, ecParameterSpec.getCurve()), ecParameterSpec);
|
||||||
PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
|
PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
|
||||||
agreement.doPhase(yourPubKey, true);
|
agreement.doPhase(yourPubKey, true);
|
||||||
|
|||||||
@@ -30,12 +30,18 @@ public class BaseMAC
|
|||||||
private final int defbsize;
|
private final int defbsize;
|
||||||
private final int bsize;
|
private final int bsize;
|
||||||
private final byte[] tmp;
|
private final byte[] tmp;
|
||||||
|
private final boolean etm;
|
||||||
private javax.crypto.Mac mac;
|
private javax.crypto.Mac mac;
|
||||||
|
|
||||||
public BaseMAC(String algorithm, int bsize, int defbsize) {
|
public BaseMAC(String algorithm, int bsize, int defbsize) {
|
||||||
|
this(algorithm, bsize, defbsize, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseMAC(String algorithm, int bsize, int defbsize, boolean isEtm) {
|
||||||
this.algorithm = algorithm;
|
this.algorithm = algorithm;
|
||||||
this.bsize = bsize;
|
this.bsize = bsize;
|
||||||
this.defbsize = defbsize;
|
this.defbsize = defbsize;
|
||||||
|
this.etm = isEtm;
|
||||||
tmp = new byte[defbsize];
|
tmp = new byte[defbsize];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,4 +118,8 @@ public class BaseMAC
|
|||||||
update(tmp, 0, 4);
|
update(tmp, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEtm() {
|
||||||
|
return etm;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.mac;
|
package net.schmizz.sshj.transport.mac;
|
||||||
|
|
||||||
/** Message Authentication Code for use in SSH. It usually wraps a javax.crypto.Mac class. */
|
/**
|
||||||
|
* Message Authentication Code for use in SSH. It usually wraps a javax.crypto.Mac class.
|
||||||
|
*/
|
||||||
public interface MAC {
|
public interface MAC {
|
||||||
|
|
||||||
byte[] doFinal();
|
byte[] doFinal();
|
||||||
@@ -33,4 +35,40 @@ public interface MAC {
|
|||||||
void update(byte[] foo, int start, int len);
|
void update(byte[] foo, int start, int len);
|
||||||
|
|
||||||
void update(long foo);
|
void update(long foo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that an Encrypt-Then-Mac algorithm was selected.
|
||||||
|
* <p>
|
||||||
|
* This has the following implementation details.
|
||||||
|
* 1.5 transport: Protocol 2 Encrypt-then-MAC MAC algorithms
|
||||||
|
* <p>
|
||||||
|
* OpenSSH supports MAC algorithms, whose names contain "-etm", that
|
||||||
|
* perform the calculations in a different order to that defined in RFC
|
||||||
|
* 4253. These variants use the so-called "encrypt then MAC" ordering,
|
||||||
|
* calculating the MAC over the packet ciphertext rather than the
|
||||||
|
* plaintext. This ordering closes a security flaw in the SSH transport
|
||||||
|
* protocol, where decryption of unauthenticated ciphertext provided a
|
||||||
|
* "decryption oracle" that could, in conjunction with cipher flaws, reveal
|
||||||
|
* session plaintext.
|
||||||
|
* <p>
|
||||||
|
* Specifically, the "-etm" MAC algorithms modify the transport protocol
|
||||||
|
* to calculate the MAC over the packet ciphertext and to send the packet
|
||||||
|
* length unencrypted. This is necessary for the transport to obtain the
|
||||||
|
* length of the packet and location of the MAC tag so that it may be
|
||||||
|
* verified without decrypting unauthenticated data.
|
||||||
|
* <p>
|
||||||
|
* As such, the MAC covers:
|
||||||
|
* <p>
|
||||||
|
* mac = MAC(key, sequence_number || packet_length || encrypted_packet)
|
||||||
|
* <p>
|
||||||
|
* where "packet_length" is encoded as a uint32 and "encrypted_packet"
|
||||||
|
* contains:
|
||||||
|
* <p>
|
||||||
|
* byte padding_length
|
||||||
|
* byte[n1] payload; n1 = packet_length - padding_length - 1
|
||||||
|
* byte[n2] random padding; n2 = padding_length
|
||||||
|
*
|
||||||
|
* @return Whether the MAC algorithm is an Encrypt-Then-Mac algorithm
|
||||||
|
*/
|
||||||
|
boolean isEtm();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import net.schmizz.sshj.common.Base64;
|
|||||||
import net.schmizz.sshj.common.Buffer;
|
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 net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
|
||||||
|
|
||||||
public class FingerprintVerifier implements HostKeyVerifier {
|
public class FingerprintVerifier implements HostKeyVerifier {
|
||||||
private static final Pattern MD5_FINGERPRINT_PATTERN = Pattern.compile("[0-9a-f]{2}+(:[0-9a-f]{2}+){15}+");
|
private static final Pattern MD5_FINGERPRINT_PATTERN = Pattern.compile("[0-9a-f]{2}+(:[0-9a-f]{2}+){15}+");
|
||||||
@@ -121,4 +120,8 @@ public class FingerprintVerifier implements HostKeyVerifier {
|
|||||||
return Arrays.equals(fingerprintData, digestData);
|
return Arrays.equals(fingerprintData, digestData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "FingerprintVerifier{digestAlgorithm='" + digestAlgorithm + "'}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.verification;
|
package net.schmizz.sshj.transport.verification;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||||
import com.hierynomus.sshj.transport.verification.KnownHostMatchers;
|
import com.hierynomus.sshj.transport.verification.KnownHostMatchers;
|
||||||
import net.schmizz.sshj.common.*;
|
import net.schmizz.sshj.common.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -190,7 +191,7 @@ public class OpenSSHKnownHosts
|
|||||||
* Lines starting with `#' and empty lines are ignored as comments.
|
* Lines starting with `#' and empty lines are ignored as comments.
|
||||||
*/
|
*/
|
||||||
public class EntryFactory {
|
public class EntryFactory {
|
||||||
EntryFactory() {
|
public EntryFactory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public KnownHostEntry parseEntry(String line)
|
public KnownHostEntry parseEntry(String line)
|
||||||
@@ -199,17 +200,22 @@ public class OpenSSHKnownHosts
|
|||||||
return new CommentEntry(line);
|
return new CommentEntry(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String[] split = line.split(" ");
|
final String trimmed = line.trim();
|
||||||
|
int minComponents = 3;
|
||||||
|
if (trimmed.startsWith("@")) {
|
||||||
|
minComponents = 4;
|
||||||
|
}
|
||||||
|
String[] split = trimmed.split("\\s+", minComponents + 1); // Add 1 for optional comments
|
||||||
|
if(split.length < minComponents) {
|
||||||
|
log.error("Error reading entry `{}`", line);
|
||||||
|
return new BadHostEntry(line);
|
||||||
|
}
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
final Marker marker = Marker.fromString(split[i]);
|
final Marker marker = Marker.fromString(split[i]);
|
||||||
if (marker != null) {
|
if (marker != null) {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if(split.length < 3) {
|
|
||||||
log.error("Error reading entry `{}`", line);
|
|
||||||
return new BadHostEntry(line);
|
|
||||||
}
|
|
||||||
final String hostnames = split[i++];
|
final String hostnames = split[i++];
|
||||||
final String sType = split[i++];
|
final String sType = split[i++];
|
||||||
|
|
||||||
@@ -227,11 +233,14 @@ public class OpenSSHKnownHosts
|
|||||||
}
|
}
|
||||||
} else if (isBits(sType)) {
|
} else if (isBits(sType)) {
|
||||||
type = KeyType.RSA;
|
type = KeyType.RSA;
|
||||||
|
minComponents += 1;
|
||||||
|
// re-split
|
||||||
|
split = trimmed.split("\\s+", minComponents + 1); // Add 1 for optional comments
|
||||||
// int bits = Integer.valueOf(sType);
|
// int bits = Integer.valueOf(sType);
|
||||||
final BigInteger e = new BigInteger(split[i++]);
|
final BigInteger e = new BigInteger(split[i++]);
|
||||||
final BigInteger n = new BigInteger(split[i++]);
|
final BigInteger n = new BigInteger(split[i++]);
|
||||||
try {
|
try {
|
||||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
|
||||||
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("Error reading entry `{}`, could not create key", line, ex);
|
log.error("Error reading entry `{}`, could not create key", line, ex);
|
||||||
@@ -242,7 +251,13 @@ public class OpenSSHKnownHosts
|
|||||||
return new BadHostEntry(line);
|
return new BadHostEntry(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HostEntry(marker, hostnames, type, key);
|
final String comment;
|
||||||
|
if (i < split.length) {
|
||||||
|
comment = split[i++];
|
||||||
|
} else {
|
||||||
|
comment = null;
|
||||||
|
}
|
||||||
|
return new HostEntry(marker, hostnames, type, key, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isBits(String type) {
|
private boolean isBits(String type) {
|
||||||
@@ -323,13 +338,19 @@ public class OpenSSHKnownHosts
|
|||||||
private final String hostPart;
|
private final String hostPart;
|
||||||
protected final KeyType type;
|
protected final KeyType type;
|
||||||
protected final PublicKey key;
|
protected final PublicKey key;
|
||||||
|
private final String comment;
|
||||||
private final KnownHostMatchers.HostMatcher matcher;
|
private final KnownHostMatchers.HostMatcher matcher;
|
||||||
|
|
||||||
public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException {
|
public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException {
|
||||||
|
this(marker, hostPart, type, key, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key, String comment) throws SSHException {
|
||||||
this.marker = marker;
|
this.marker = marker;
|
||||||
this.hostPart = hostPart;
|
this.hostPart = hostPart;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
this.comment = comment;
|
||||||
this.matcher = KnownHostMatchers.createMatcher(hostPart);
|
this.matcher = KnownHostMatchers.createMatcher(hostPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,6 +387,9 @@ public class OpenSSHKnownHosts
|
|||||||
line.append(getHostPart());
|
line.append(getHostPart());
|
||||||
line.append(" ").append(type.toString());
|
line.append(" ").append(type.toString());
|
||||||
line.append(" ").append(getKeyString(key));
|
line.append(" ").append(getKeyString(key));
|
||||||
|
|
||||||
|
if (comment != null && !comment.isEmpty()) line.append(" ").append(comment);
|
||||||
|
|
||||||
return line.toString();
|
return line.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,6 +401,10 @@ public class OpenSSHKnownHosts
|
|||||||
protected String getHostPart() {
|
protected String getHostPart() {
|
||||||
return hostPart;
|
return hostPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BadHostEntry implements KnownHostEntry {
|
public static class BadHostEntry implements KnownHostEntry {
|
||||||
@@ -440,4 +468,10 @@ public class OpenSSHKnownHosts
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "OpenSSHKnownHosts{khFile='" + khFile + "'}";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class KeyProviderUtil {
|
|||||||
|
|
||||||
private static KeyFormat keyFormatFromHeader(String header, boolean separatePubKey) {
|
private static KeyFormat keyFormatFromHeader(String header, boolean separatePubKey) {
|
||||||
if (header.startsWith("-----BEGIN") && header.endsWith("PRIVATE KEY-----")) {
|
if (header.startsWith("-----BEGIN") && header.endsWith("PRIVATE KEY-----")) {
|
||||||
if (separatePubKey && header.contains(OpenSSHKeyV1KeyFile.OPENSSH_PRIVATE_KEY)) {
|
if (header.contains(OpenSSHKeyV1KeyFile.OPENSSH_PRIVATE_KEY)) {
|
||||||
return KeyFormat.OpenSSHv1;
|
return KeyFormat.OpenSSHv1;
|
||||||
} else if (separatePubKey) {
|
} else if (separatePubKey) {
|
||||||
// Can delay asking for password since have unencrypted pubkey
|
// Can delay asking for password since have unencrypted pubkey
|
||||||
|
|||||||
@@ -93,13 +93,21 @@ public class OpenSSHKeyFile
|
|||||||
private void initPubKey(Reader publicKey) throws IOException {
|
private void initPubKey(Reader publicKey) throws IOException {
|
||||||
final BufferedReader br = new BufferedReader(publicKey);
|
final BufferedReader br = new BufferedReader(publicKey);
|
||||||
try {
|
try {
|
||||||
final String keydata = br.readLine();
|
String keydata;
|
||||||
if (keydata != null) {
|
while ((keydata = br.readLine()) != null) {
|
||||||
String[] parts = keydata.trim().split(" ");
|
keydata = keydata.trim();
|
||||||
assert parts.length >= 2;
|
if (!keydata.isEmpty()) {
|
||||||
|
String[] parts = keydata.trim().split("\\s+");
|
||||||
|
if (parts.length >= 2) {
|
||||||
type = KeyType.fromString(parts[0]);
|
type = KeyType.fromString(parts[0]);
|
||||||
pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey();
|
pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey();
|
||||||
|
} else {
|
||||||
|
throw new IOException("Got line with only one column");
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IOException("Public key file is blank");
|
||||||
} finally {
|
} finally {
|
||||||
br.close();
|
br.close();
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user