Compare commits

...

62 Commits

Author SHA1 Message Date
Jeroen van Erp
8f3d751431 Some cleanup of integration container and add integration test 2021-04-20 11:29:52 +02:00
Jeroen van Erp
9051c8c7e9 Merge branch 'master' into issue-670 2021-04-20 09:28:08 +02:00
Estraysian
e283880e49 Minor improvement for SCP preserve flag: (#680)
- Added an override for copy method, allowing the user to specify whether preserve flag is used in the SCP command.
- Propagated the preserveTime boolean to process method to skip preserveTimeIfPossible when it's not desired
2021-04-13 11:32:30 +02:00
Jeroen van Erp
cf7309c866 Add extra logging to troubleshoot issue #670 2021-03-30 12:00:34 +02:00
Jeroen van Erp
45b2f32b14 Fixed examples for 0.31.0 2021-03-29 11:39:49 +02:00
Jeroen van Erp
1d8eaa7ce2 Release version: 0.31.0 2021-02-08 22:21:35 +01:00
Jeroen van Erp
6eea9a993c VSCode files 2021-02-08 22:21:23 +01:00
Jeroen van Erp
67d2cf72d6 Prepare release notes for 0.31.0 2021-02-08 22:17:42 +01:00
Jeroen van Erp
b8d58389cf Merge branch 'informaticum-master' 2021-01-13 14:30:45 +01:00
Jeroen van Erp
c5f48f9888 Merge branch 'master' of github.com:informaticum/sshj into informaticum-master 2021-01-13 14:30:29 +01:00
Jeroen van Erp
c05c3307b3 Update dependencies
- BouncyCastle 1.68
- asn-one 0.5.0
- slf4j-api 1.7.30
2021-01-13 10:41:01 +01:00
Vladimir Lagunov
9bc9262842 Support ED25519 and ECDSA keys in the PuTTY format (#660)
* Support ED25519 PuTTY keys.

Fix #659

* PuTTYKeyFile: Use net.schmizz.sshj.common.Buffer instead of own KeyReader.

A tiny refactoring made in order to allow usage of other utility methods which require Buffer.

* Support ECDSA PuTTY keys.

* Some code cleanup

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2021-01-08 22:44:19 +01:00
Fabian Bonk
6d7dd741de Bump bouncycastle version (#655) 2020-12-21 13:41:11 +01:00
stefan
7f8328f23f Backdate license to prevent build's "License violations" (#653) 2020-12-12 20:10:32 +01:00
stefan
be18cc6e6a Add license prelude (#653) 2020-12-12 20:03:42 +01:00
stefan
ee68e0a8e6 Adopt project's import order (#653)
- copied by import order of 'SocketStreamCopyMonitorTest'
2020-12-12 20:03:12 +01:00
stefan
9266b6c04a Testing #toString of Parameters (#653) 2020-12-12 20:00:43 +01:00
stefan
9e9797c326 Adding #toString to Parameters (#653) 2020-12-12 19:58:31 +01:00
stefan
ee7a65531f Testing #equals of Parameters (#653) 2020-12-12 19:58:19 +01:00
stefan
8337cce382 Adding #equals to Parameters (#653) 2020-12-12 19:58:05 +01:00
stefan
bc5a119169 Testing #hashCode of Parameters (#653) 2020-12-12 19:57:51 +01:00
stefan
cda04809e1 Adding #hashCode to Parameters (#653) 2020-12-12 19:56:56 +01:00
Henning Poettker
07d624b1df reduced log level for lenient handling of missing CR at end of identification (#647) 2020-11-24 15:03:11 +01:00
Jeroen van Erp
60aa230929 Update release notes for upcoming 0.31.0 2020-11-17 14:47:18 +01:00
Fabian Henneke
2edaf07e71 Improve Android compatibility (#636)
* Loop through security providers to check for BC

Instead of only counting BouncyCastle as being registered if it
is set as the explicit security provider used by SSHJ, count it as
registered if it is available as a provider.

This commit improves Android compatibility, which requires not
specifying an explicit provider.

* Generify BC-specific curve specifiers

The ECNamendCurveGenParameterSpec is a BC-specific workaround for
missing curve tables in Java 1.4 and earlier. For the sake of Android
compatibility, where Conscrypt can't deal with this custom spec class,
replace it with the standard ECGenParameterSpec and update the curve
names to the standard identifiers.
2020-10-20 09:57:51 +02:00
Fabian Henneke
d124607225 Fix PR links in README (#632) 2020-09-10 09:26:08 +02:00
Jeroen van Erp
8c899eb867 Fix warnings 2020-09-09 11:02:28 +02:00
Jeroen van Erp
939a170ee8 Update README 2020-09-09 10:37:56 +02:00
Raymond Lai
143069e3e0 Implement AES-GCM cipher support (#630)
* Implement AES-GCM cipher support

Fixes #217.

A port of AES-GCM cipher support from Apache MINA-SSHD, based on https://github.com/apache/mina-sshd/pull/132.

Included tests for decoding SSH packets sent from Apache MINA-SSHD and OpenSSH (Version 7.9p1 as used by Debian 10).

Manual tests also done on OpenSSH server 7.9p1 running Debian 10 with its available ciphers, including 3des-cbc, aes128-cbc, aes192-cbc, aes256-cbc, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com and aes256-gcm@openssh.com.

* Changes per PR feedback

- Fixed variable/statement whitespaces and add back missing braces per coding standard requirement
- Moved Buffer.putLong() and Buffer.getLong() into GcmCipher.CounterGCMParameterSpec since it's the only user
- Moved BaseCipher.authSize into GcmCipher since it is the only cipher that would return a non-zero. BaseCipher will keep return 0 instead
- Made BaseCipher.cipher protected instead of making it publicly accessible
- Combined the three decoding modes in Decoder.decode() into one single method, to reduce code duplication
- Added integration test for the ciphers, along with the newly implemented AES-GCM ciphers
2020-09-09 09:51:17 +02:00
Jeroen van Erp
4458332cbf Update release notes for 0.30.0 2020-08-25 15:56:15 +02:00
Pavel Dionisev
a0d7b7fd41 Avoid key lleakage. (#627)
In some cases, current code will leak parts or even the whole ssh key if it's slightly malformed.
One example of that malformation will be a key, where all newlines are replaced by other character, thus turning a multiline key to a single big string.
Then that whole line will be leaked to exception message.
2020-08-25 15:23:46 +02:00
Jeroen van Erp
b6213401db Release version: 0.30.0 2020-08-17 15:43:12 +02:00
Jeroen van Erp
19ec6d9d8d Made java 11 default java version 2020-08-17 15:42:58 +02:00
Jeroen van Erp
0ba491c01a Add testcase for reading signed certificate (Fixes #613) 2020-08-17 10:59:03 +02:00
Jeroen van Erp
73d7560e6e Add Reader constructor to OpenSSHKnownHosts (Fixes #626) 2020-08-17 10:23:37 +02:00
Vladimir Lagunov
0e0d730bbf Fix RSA certificate key determination. (#602)
* Fix RSA certificate key determination.

Fixes #599.

* Correct serialization of RSA certificates with unlimited dates.

* The test for connecting with RSA certificate.

* Remove redundant change in TransportImpl.java

* Add forgotten test keys.

* Make net.schmizz.sshj.common.KeyType.CertUtils.epochFromDate readable.

Co-authored-by: Vladimir Lagunov <vladimir.lagunov@jetbrains.com>
Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-07-31 09:43:11 +02:00
Fabian Henneke
6becee176a Fix matching of pubkeys to key algorithms (#607)
* Fix matching of pubkeys to key algorithms

Allow all configured key algorithms for pubkey authentication, even if
these algorithms are not supported as host key algorithms by the
server.

Preference is given to the modern rsa-sha2-* signature algorithms if
the server indicates support for them as host keys signature
algorithms.

* Replace Boolean with primitive boolean

* Add integration tests for ecdsa-sha2-nistp384/521

* Remove redundant import

* Clean up Transport interface

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-07-28 12:30:29 +02:00
Fabian Henneke
4b1619d54f Fix transport encoding of nistp521 signatures (#623)
SignatureECDSA.encode() does not correctly handle signatures longer
than 128 bytes, which affects signatures using the nistp521 curve.

This commits fixes the issue by replacing the ad-hoc ASN.1 DER
parsing with a use of ASN1InputStream.
2020-07-28 11:56:17 +02:00
Fabian Henneke
64f448d438 Send ext-info-c with kex algorithms (#622)
Some SSH servers will not honor the negotiated rsa-sha2-256 algorithms
if the client does not indicate support for SSH_MSG_EXT_INFO messages.
Since we only need to accept these messages, but are free to ignore
their contents, adding support amounts to sending "ext-info-c" with our
kex algorithm proposal.
2020-07-20 15:54:28 +02:00
Moritz Weichert
a5efdf1f0d Bump bonucycastle version (#619)
Co-authored-by: Moritz Weichert <moritz.weichert@check24.de>
2020-07-10 19:02:32 +02:00
Moritz Weichert
588853554a Fix localport for openssh >= 8.0 (#618)
Co-authored-by: Moritz Weichert <moritz.weichert@check24.de>
2020-07-09 11:29:47 +02:00
Fabian Henneke
7bde5c15c1 Clear passphrase bytes after use (#609)
Mimics the behavior of `decrypt()` in `PKCS5KeyFile.java`.
2020-07-01 21:34:31 +02:00
Fabian Henneke
3c85b86915 Always use JCERandom as the default RNG (#606)
The current implementation relies on `VMPCRandomGenerator` for random number generation if BouncyCastle is registered, otherwise it falls back to `SecureRandom`. Nowadays, `SecureRandom` should always be the best available option, whereas `VMPCRandomGenerator` [has known weaknesses](https://books.google.de/books?id=niO6BQAAQBAJ&pg=PA140&lpg=PA140&dq=vmpc+prng&source=bl&ots=QAdZJOT607&sig=ACfU3U0Edqlpm08iRZJLxeWGQNwNQz7WsQ&hl=en&sa=X&ved=2ahUKEwjd2Zyr9pfqAhXWMMAKHT70AioQ6AEwDnoECAoQAQ#v=onepage&q=vmpc%20prng&f=false).
2020-06-23 14:51:55 +02:00
Simon Legner
2ca0fa4732 Use daemon thread to avoid blocking JVM shutdown (#605) 2020-06-23 12:48:11 +02:00
dajoropo
769c896e53 Added method to created Stateful clients ( #268). (#603)
Co-authored-by: Daniel Rodriguez <daniel.rodriguez@elca.ch>
2020-06-22 12:05:11 +02:00
Fabian Henneke
eb19325bc6 Don't specify JcaPEM providers as null (#596)
* Don't specify JcaPEMKeyConverter provider as null

If no provider is set in the `SecurityUtils`, no named provider should be set for the `JcaPEMKeyConverter` as this would cause a `missing provider` exception.

* Don't specify JcePEMDecryptorProviderBuilder provider as null

If no provider is set in the `SecurityUtils`, no named provider should be set for the `JcePEMDecryptorProviderBuilder` as this would cause a missing provider exception. This currently breaks `PKCS8KeyFile` if `SecurityUtils.setSecurityProvider(null)` and `SecurityUtils.setRegisterBouncyCastle(false)` is used.
2020-06-08 09:47:30 +02:00
David Kocher
2d8af5a687 Allow to query for server extensions available. (#591)
Co-authored-by: Jöran Malek <joeran3@gmail.com>
2020-06-05 11:40:58 +02:00
David Kocher
c4fef33d8f Remove length restriction in key reader. (#595) 2020-06-05 11:35:27 +02:00
Jeroen van Erp
ff85e832af Update README.adoc 2020-06-05 11:14:39 +02:00
David Kocher
0dcb4b9a7a Review interface to return collection for allowed authentication methods that allows to query for elements. (#593) 2020-06-05 11:11:50 +02:00
Jeroen van Erp
2baf51bf64 Replace BC ASN.1 dependency with asn-one library 2020-06-02 23:40:08 +02:00
Jeroen van Erp
3194fd9bd0 Refactored KeyAlgorithms slightly 2020-06-02 23:22:55 +02:00
Jeroen van Erp
ab3f0143bd Update release notes 2020-06-02 16:39:56 +02:00
Jeroen van Erp
9671352bda Introduce KeyAlgorithm to separate KeyType from Algorithm (Fixes #588) (#589) 2020-06-02 16:39:22 +02:00
Jeroen van Erp
91105e6a07 Fix integration build 2020-06-02 14:07:31 +02:00
Fabian Henneke
4e802cec86 Make KeyType compatible with Android Keystore (#586)
* Make KeyType compatible with Android Keystore

Android Keystore private keys do not implement PrivateKey since the
raw key material is not available to applications.

With this commit, sshj's KeyType correctly detects the algorithm
associated with Android Keystore keys, which makes them usable for SSH
authentication.

* Extract RSA, DSA, ECDSA and EC into constants

* Fix license lint issue

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-05-28 16:09:43 +02:00
Fabian Henneke
dfdc464e08 Add pwdf retry logic to OpenSSHKeyV1KeyFile (#587)
* Add pwdf retry logic to OpenSSHKeyV1KeyFile

While PKCS8KeyFile uses PasswordFinder's shouldRetry to determine
whether it should call reqPassword again if decryption of they key file
fails, OpenSSHKeyV1KeyFile simply gives up and throws an exception.

With this commit, retry logic similar to that of PKCS8KeyFile is added
to OpenSSHKeyV1KeyFile. The PasswordFinder's reqPassword is called
again if the validation of the "checkint" fails, which indicates an
incorrect passphrase.

* Use new exception to signal incorrect passphrase

* Throw common exception on key decryption failure

* Add test coverage for retry logic

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-05-28 09:25:52 +02:00
Jeroen van Erp
fa7c40cc66 Use java12 for building due to gradle --release flag 2020-05-27 16:41:48 +02:00
Vladimir Lagunov
b1be9258b4 Fix NPE in OpenSSHKnownHosts (#579)
Co-authored-by: Vladimir Lagunov <vladimir.lagunov@jetbrains.com>
2020-05-26 14:56:29 +02:00
Jeroen van Erp
11543b2c00 Release version: 0.29.0 2020-05-12 11:30:41 +02:00
Jeroen van Erp
3526694558 Fix build for Java9 and up, verifying Java7 compat 2020-05-12 11:29:30 +02:00
Jeroen van Erp
d618156ede Fix bintray plugin version 2020-05-11 23:17:02 +02:00
144 changed files with 2600 additions and 607 deletions

View File

@@ -11,43 +11,43 @@ on:
branches: [ master ]
jobs:
java8:
name: Build with Java 8
java12:
name: Build with Java 12
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
- name: Set up JDK 12
uses: actions/setup-java@v1
with:
java-version: 8
java-version: 12
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew check
java9:
name: Build with Java 9
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 9
uses: actions/setup-java@v1
with:
java-version: 9
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew check -xanimalsnifferMain -xanimalsnifferTest
# 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: [java8]
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: 8
java-version: 12
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle

3
.gitignore vendored
View File

@@ -21,3 +21,6 @@ sshj.jar
# MacOS X
.DS_Store
# VSCode
.metals/

1
.java-version Normal file
View File

@@ -0,0 +1 @@
11.0

View File

@@ -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

6
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"java.checkstyle.configuration": "${workspaceFolder}/gradle/config/checkstyle/checkstyle.xml",
"files.watcherExclude": {
"**/target": true
}
}

View File

@@ -1,7 +1,7 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.28.0
:sshj_version: 0.31.0
:source-highlighter: pygments
image:https://api.bintray.com/packages/hierynomus/maven/sshj/images/download.svg[link="https://bintray.com/hierynomus/maven/sshj/_latestVersion"]
@@ -64,7 +64,7 @@ In the `examples` directory, there is a separate Maven project that shows how th
Implementations / adapters for the following algorithms are included:
ciphers::
`aes{128,192,256}-{cbc,ctr}`, `blowfish-{cbc,ctr}`, `3des-{cbc,ctr}`, `twofish{128,192,256}-{cbc,ctr}`, `twofish-cbc`, `serpent{128,192,256}-{cbc,ctr}`, `idea-{cbc,ctr}`, `cast128-{cbc,ctr}`, `arcfour`, `arcfour{128,256}`
`aes{128,192,256}-{cbc,ctr}`, `aes{128,256}-gcm@openssh.com`, `blowfish-{cbc,ctr}`, `3des-{cbc,ctr}`, `twofish{128,192,256}-{cbc,ctr}`, `twofish-cbc`, `serpent{128,192,256}-{cbc,ctr}`, `idea-{cbc,ctr}`, `cast128-{cbc,ctr}`, `arcfour`, `arcfour{128,256}`
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
key exchange::
@@ -78,7 +78,7 @@ key exchange::
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
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`, `ssh-rsa2-256`, `ssh-rsa2-512`
mac::
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`, `hmac-ripemd160`, `hmac-ripemd160@openssh.com`
@@ -105,6 +105,34 @@ Issue tracker: https://github.com/hierynomus/sshj/issues
Fork away!
== Release history
SSHJ 0.31.0 (2021-02-08)::
* Bump dependencies (asn-one 0.5.0, BouncyCastle 1.68, slf4j-api 1.7.30)
* Merged https://github.com/hierynomus/sshj/pull/660[#660]: Support ED25519 and ECDSA keys in PuTTY format
* Merged https://github.com/hierynomus/sshj/pull/655[#655]: Bump BouncyCastle due to CVE
* Merged https://github.com/hierynomus/sshj/pull/653[#653]: Make Parameters class useable as HashMap key
* Merged https://github.com/hierynomus/sshj/pull/647[#647]: Reduce log level for identification parser
* Merged https://github.com/hierynomus/sshj/pull/630[#630]: Add support for `aes128-gcm@openssh.com` and `aes256-gcm@openssh.com` ciphers
* Merged https://github.com/hierynomus/sshj/pull/636[#636]: Improved Android compatibility
* Merged https://github.com/hierynomus/sshj/pull/627[#627]: Prevent key leakage
SSHJ 0.30.0 (2020-08-17)::
* **BREAKING CHANGE**: Removed `setSignatureFactories` and `getSignatureFactories` from the Config and switched them for `getKeyAlgorithms` and `setKeyAlgorithms`
* Fixed https://github.com/hierynomus/sshj/pull/588[#588]: Add support for `ssh-rsa2-256` and `ssh-rsa2-512` signatures
* Merged https://github.com/hierynomus/sshj/pull/579[#579]: Fix NPE in OpenSSHKnownHosts
* Merged https://github.com/hierynomus/sshj/pull/587[#587]: Add passwordfinder retry for OpenSSHKeyV1KeyFile
* Merged https://github.com/hierynomus/sshj/pull/586[#586]: Make KeyType compatible with Android Store
* Merged https://github.com/hierynomus/sshj/pull/593[#593]: Change `UserAuth.getAllowedMethods()` to Collection return type
* Merged https://github.com/hierynomus/sshj/pull/595[#595]: Allow reading arbitrary length keys
* Merged https://github.com/hierynomus/sshj/pull/591[#591]: Allow to query SFTP extensions
* Merged https://github.com/hierynomus/sshj/pull/603[#603]: Add method to create Stateful SFTP client
* Merged https://github.com/hierynomus/sshj/pull/605[#605]: Use Daemon threads to avoid blocking JVM shutdown
* Merged https://github.com/hierynomus/sshj/pull/606[#606]: Always use the JCERandom RNG by default
* Merged https://github.com/hierynomus/sshj/pull/609[#609]: Clear passphrase after use to prevent security issues
* Merged https://github.com/hierynomus/sshj/pull/618[#618]: Fix localport of DirectConnection for use with OpenSSH > 8.0
* Merged https://github.com/hierynomus/sshj/pull/619[#619]: Upgraded BouncyCastle to 1.66
* Merged https://github.com/hierynomus/sshj/pull/622[#622]: Send 'ext-info-c' with KEX algorithms
* Merged https://github.com/hierynomus/sshj/pull/623[#623]: Fix transport encoding of `nistp521` signatures
* Merged https://github.com/hierynomus/sshj/pull/607[#607]: Fix mathing pubkeys to key algorithms
* Merged https://github.com/hierynomus/sshj/pull/602[#602]: Fix RSA certificate key determination
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.
@@ -127,31 +155,31 @@ SSHJ 0.24.0 (2018-04-04)::
* 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)::
* Merged https://github.com/hierynomus/sshj/pulls/372[#372]: Upgrade to 'net.i2p.crypto:eddsa:0.2.0'
* Merged https://github.com/hierynomus/sshj/pull/372[#372]: Upgrade to 'net.i2p.crypto:eddsa:0.2.0'
* Fixed https://github.com/hierynomus/sshj/issues/355[#355] and https://github.com/hierynomus/sshj/issues/354[#354]: Correctly decode signature bytes
* Fixed https://github.com/hierynomus/sshj/issues/365[#365]: Added support for new-style OpenSSH fingerprints of server keys
* Fixed https://github.com/hierynomus/sshj/issues/356[#356]: Fixed key type detection for ECDSA public keys
* Made SSHJ Java9 compatible
SSHJ 0.22.0 (2017-08-24)::
* Fixed https://github.com/hierynomus/sshj/pulls/341[#341]: Fixed path walking during recursive copy
* Merged https://github.com/hierynomus/sshj/pulls/338[#338]: Added ConsolePasswordFinder to read password from stdin
* Merged https://github.com/hierynomus/sshj/pulls/336[#336]: Added support for ecdsa-sha2-nistp384 and ecdsa-sha2-nistp521 signatures
* Fixed https://github.com/hierynomus/sshj/pull/341[#341]: Fixed path walking during recursive copy
* Merged https://github.com/hierynomus/sshj/pull/338[#338]: Added ConsolePasswordFinder to read password from stdin
* Merged https://github.com/hierynomus/sshj/pull/336[#336]: Added support for ecdsa-sha2-nistp384 and ecdsa-sha2-nistp521 signatures
* Fixed https://github.com/hierynomus/sshj/issues/331[#331]: Added support for wildcards in known_hosts file
SSHJ 0.21.1 (2017-04-25)::
* Merged https://github.com/hierynomus/sshj/pulls/322[#322]: Fix regression from 40f956b (invalid length parameter on outputstream)
* Merged https://github.com/hierynomus/sshj/pull/322[#322]: Fix regression from 40f956b (invalid length parameter on outputstream)
SSHJ 0.21.0 (2017-04-14)::
* Merged https://github.com/hierynomus/sshj/pulls/319[#319]: Added support for `ssh-rsa-cert-v01@openssh.com` and `ssh-dsa-cert-v01@openssh.com` certificate key files
* Merged https://github.com/hierynomus/sshj/pull/319[#319]: Added support for `ssh-rsa-cert-v01@openssh.com` and `ssh-dsa-cert-v01@openssh.com` certificate key files
* Upgraded Gradle to 3.4.1
* Merged https://github.com/hierynomus/sshj/pulls/305[#305]: Added support for custom string encoding
* Merged https://github.com/hierynomus/sshj/pull/305[#305]: Added support for custom string encoding
* Fixed https://github.com/hierynomus/sshj/issues/312[#312]: Upgraded BouncyCastle to 1.56
SSHJ 0.20.0 (2017-02-09)::
* Merged https://github.com/hierynomus/sshj/pulls/294[#294]: Reference ED25519 by constant instead of name
* Merged https://github.com/hierynomus/sshj/pulls/293[#293], https://github.com/hierynomus/sshj/pulls/295[#295] and https://github.com/hierynomus/sshj/pulls/301[#301]: Fixed OSGi packaging
* Merged https://github.com/hierynomus/sshj/pull/294[#294]: Reference ED25519 by constant instead of name
* Merged https://github.com/hierynomus/sshj/pull/293[#293], https://github.com/hierynomus/sshj/pull/295[#295] and https://github.com/hierynomus/sshj/pull/301[#301]: Fixed OSGi packaging
* Added new Diffie Hellman groups 15-18 for stronger KeyExchange algorithms
SSHJ 0.19.1 (2016-12-30)::
* Enabled PKCS5 Key files in DefaultConfig
* Merged https://github.com/hierynomus/sshj/pulls/291[#291]: Fixed sshj.properties loading and chained exception messages
* Merged https://github.com/hierynomus/sshj/pulls/284[#284]: Correctly catch interrupt in keepalive thread
* Merged https://github.com/hierynomus/sshj/pull/291[#291]: Fixed sshj.properties loading and chained exception messages
* Merged https://github.com/hierynomus/sshj/pull/284[#284]: Correctly catch interrupt in keepalive thread
* 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
* LocalPortForwarder now correctly interrupts its own thread on close()
@@ -161,11 +189,11 @@ SSHJ 0.19.0 (2016-11-25)::
SSHJ 0.18.0 (2016-09-30)::
* Fixed Android compatibility
* Upgrade to Gradle 3.0
* Merged https://github.com/hierynomus/sshj/pulls/271[#271]: Load known_hosts without requiring BouncyCastle
* Merged https://github.com/hierynomus/sshj/pulls/269[#269]: Brought back Java6 support by popular demand
* Merged https://github.com/hierynomus/sshj/pulls/267[#267]: Added support for per connection logging (Fixes https://github.com/hierynomus/sshj/issues/264[#264])
* Merged https://github.com/hierynomus/sshj/pulls/262[#262], https://github.com/hierynomus/sshj/pulls/265[#265] and https://github.com/hierynomus/sshj/pulls/266[#266]: Added PKCS5 key file support
* Fixed toString of sftp FileAttributes (Fixes https://github.com/hierynomus/sshj/pulls/258[#258])
* Merged https://github.com/hierynomus/sshj/pull/271[#271]: Load known_hosts without requiring BouncyCastle
* Merged https://github.com/hierynomus/sshj/pull/269[#269]: Brought back Java6 support by popular demand
* Merged https://github.com/hierynomus/sshj/pull/267[#267]: Added support for per connection logging (Fixes https://github.com/hierynomus/sshj/issues/264[#264])
* Merged https://github.com/hierynomus/sshj/pull/262[#262], https://github.com/hierynomus/sshj/pull/265[#265] and https://github.com/hierynomus/sshj/pull/266[#266]: Added PKCS5 key file support
* Fixed toString of sftp FileAttributes (Fixes https://github.com/hierynomus/sshj/pull/258[#258])
* Fixed https://github.com/hierynomus/sshj/issues/255[#255]: No longer depending on 'privately marked' classes in `net.i2p.crypto.eddsa.math` package, fixes OSGI dependencies
SSHJ 0.17.2 (2016-07-07)::
* Treating SSH Server identification line ending in '\n' instead of '\r\n' leniently.
@@ -175,7 +203,7 @@ SSHJ 0.17.0 (2016-07-05)::
* *Introduced breaking change in SFTP copy behaviour*: Previously an SFTP copy operation would behave differently if both source and target were folders with different names.
In this case instead of copying the contents of the source into the target directory, the directory itself was copied as a sub directory of the target directory.
This behaviour has been removed in favour of the default behaviour which is to copy the contents of the source into the target. Bringing the behaviour in line with how SCP works.
* Fixed https://github.com/hierynomus/sshj/issues/252[#252] (via: https://github.com/hierynomus/sshj/pulls/253[#253]): Same name subdirs are no longer merged by accident
* Fixed https://github.com/hierynomus/sshj/issues/252[#252] (via: https://github.com/hierynomus/sshj/pull/253[#253]): Same name subdirs are no longer merged by accident
SSHJ 0.16.0 (2016-04-11)::
* Fixed https://github.com/hierynomus/sshj/issues/239[#239]: Remote port forwards did not work if you used the empty string as address, or a catch-all address.
* Fixed https://github.com/hierynomus/sshj/issues/242[#242]: Added OSGI headers to sources jar manifest

View File

@@ -17,7 +17,7 @@ configurations {
pom
}
def bouncycastleVersion = "1.50"
def bouncycastleVersion = "1.67"
dependencies {
compile "org.slf4j:slf4j-api:1.7.7"

View File

@@ -9,13 +9,12 @@ plugins {
id "com.github.blindpirate.osgi" version '0.0.3'
id "maven-publish"
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.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.pom' version '1.0.3'
id 'ru.vyarus.github-info' version '1.1.0'
id 'ru.vyarus.animalsniffer' version '1.5.0'
}
group = "com.hierynomus"
@@ -39,21 +38,17 @@ repositories {
mavenCentral()
}
sourceCompatibility = 1.7
targetCompatibility = 1.7
configurations.compile.transitive = false
def bouncycastleVersion = "1.65"
def bouncycastleVersion = "1.68"
def sshdVersion = "2.1.0"
dependencies {
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
implementation "org.slf4j:slf4j-api:1.7.7"
implementation "org.slf4j:slf4j-api:1.7.30"
implementation "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
implementation "com.jcraft:jzlib:1.1.3"
implementation "com.hierynomus:asn-one:0.5.0"
implementation "net.i2p.crypto:eddsa:0.3.0"
@@ -78,6 +73,10 @@ license {
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
if (JavaVersion.current().isJava8Compatible()) {
tasks.withType(Javadoc) {
@@ -85,6 +84,11 @@ if (JavaVersion.current().isJava8Compatible()) {
}
}
compileJava {
options.compilerArgs.addAll(['--release', '7'])
}
task writeSshjVersionProperties {
doLast {
project.file("${project.buildDir}/resources/main").mkdirs()
@@ -223,7 +227,7 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey
publications = ["maven"]
pkg {
repo = "maven"
name = project.name
name = "${project.name}"
licenses = ["Apache-2.0"]
vcsUrl = "https://github.com/hierynomus/sshj.git"
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
@@ -257,22 +261,23 @@ jacocoTestReport {
task buildItestImage(type: DockerBuildImage) {
inputDir = file('src/itest/docker-image')
tag = 'sshj/sshd-itest'
images.add('sshj/sshd-itest:latest')
}
task createItestContainer(type: DockerCreateContainer) {
dependsOn buildItestImage
targetImageId { buildItestImage.getImageId() }
portBindings = ['2222:22']
targetImageId buildItestImage.getImageId()
hostConfig.portBindings = ['2222:22']
hostConfig.autoRemove = true
}
task startItestContainer(type: DockerStartContainer) {
dependsOn createItestContainer
targetContainerId { createItestContainer.getContainerId() }
targetContainerId createItestContainer.getContainerId()
}
task stopItestContainer(type: DockerStopContainer) {
targetContainerId { createItestContainer.getContainerId() }
targetContainerId createItestContainer.getContainerId()
}
task forkedUploadRelease(type: GradleBuild) {

View File

@@ -55,7 +55,7 @@
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>0.24.0</version>
<version>0.31.0</version>
</dependency>
</dependencies>

View File

@@ -18,7 +18,7 @@ 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.addHostKeyVerifier(new OpenSSHKnownHosts(new InputStreamReader(entry, Charset.defaultCharset())));
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
@@ -28,44 +28,4 @@ public class InMemoryKnownHosts {
}
}
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;
}
}
}

View File

@@ -2,6 +2,7 @@ package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
import net.schmizz.sshj.connection.channel.direct.Parameters;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -28,8 +29,8 @@ public class LocalPF {
* _We_ listen on localhost:8080 and forward all connections on to server, which then forwards it to
* google.com:80
*/
final LocalPortForwarder.Parameters params
= new LocalPortForwarder.Parameters("0.0.0.0", 8080, "google.com", 80);
final Parameters params
= new Parameters("0.0.0.0", 8080, "google.com", 80);
final ServerSocket ss = new ServerSocket();
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress(params.getLocalHost(), params.getLocalPort()));

Binary file not shown.

View File

@@ -1,6 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

51
gradlew vendored
View File

@@ -1,5 +1,21 @@
#!/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.
#
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
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=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
@@ -138,19 +154,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -159,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# 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"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

21
gradlew.bat vendored
View File

@@ -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
@rem ##########################################################################
@rem
@@ -13,8 +29,11 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
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=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

View File

@@ -2,11 +2,14 @@ FROM sickp/alpine-sshd:7.5-r2
ADD authorized_keys /home/sshj/.ssh/authorized_keys
ADD test-container/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key
ADD test-container/ssh_host_dsa_key.pub /etc/ssh/ssh_host_dsa_key.pub
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_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/users_rsa_ca.pub /etc/ssh/users_rsa_ca.pub
RUN apk add --no-cache tini
RUN \
@@ -20,4 +23,4 @@ RUN \
chmod 644 /etc/ssh/ssh_host_ed25519_key.pub && \
chown -R sshj:sshj /home/sshj
ENTRYPOINT ["/sbin/tini", "/entrypoint.sh"]
ENTRYPOINT ["/sbin/tini", "/entrypoint.sh", "-o", "LogLevel=DEBUG2"]

View File

@@ -5,3 +5,5 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8ww4hJG/gHJYdkjTTBDF1GNz+228nuWprPV+NbQauA
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
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMvfRYSe44VQGwxexOMibcM3+fWeUP1jrBofOxFDRRrzRF8dK/vll2svqTPXMRnITnT1UoemEcB5OHtvH4hzfh/HFeDxJ5S7UncYxoClTSa8MeMFG2Zj9CoUZs1SHbwSGg== root@sshj
ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHquUYgkU9wJrcxDWVtdqtfqf6SBDdPDRxwDl7OCohV2UNu2KdjJwSj8j0fsPeMdHjSiv9OCnHYrVilQ+W5WW5q5wGXwk10oIcV0JJscohLA0nS7mKinBrxUwVHnNZbPExFciicnEArcYRb1BuT7HF8hfjuSSpWS0rob6kloSSi/jV7ZA== root@sshj

View File

@@ -0,0 +1,21 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH
NzAAAAgQDGUD3XnEQf3S4iB8LbVAw9diSaTTbaKGRt+D89d6QWHJ75I684F6iehXzr7iKq
PmmVy2Bp3BfSM1m6P4wJftHbVW6dbJSeVW1Ov6SIRA5XEG/hrY1DmlC9aXhksqYKUKffYx
WOEg4p2eCtvGyu/j9EucGrC76BraEboAHpXsojNwAAABUAuEexd+FsMSbNSsJz99HcNO7M
hy8AAACBAL++FvcyX8PZ7VeQj+gu0CkDhZM14IpNGKKOBDFvZTUILHHG7TnkjBR1HJw/bR
lwkvrvYC+155sT/BC2OKpVkiUyCw1LKGVtJBas7UlqmTa4m1fEGjaWX0b1Op2hpwpxCkmj
qS07HlC/nEHsEEMdCa9HxCfbEdMmUdLNSwEVJrPAAAAAgAGb6J24Ra4fz55vlK1vt34u39
0YdMg1RLiCa4dFubc8khEMYKpwBIoyECglJyDdLXnuJQiwVzNgX1azx10G84d5QCoAZPQK
WK1oWjj8rB+4gtEjE1sB7fZkxSXF/BaRJQPiglm0kN9gJU6RZSz4vKZIE8Bk/+htXgcXvv
8R0eShAAAB6EMk1g1DJNYNAAAAB3NzaC1kc3MAAACBAMZQPdecRB/dLiIHwttUDD12JJpN
NtooZG34Pz13pBYcnvkjrzgXqJ6FfOvuIqo+aZXLYGncF9IzWbo/jAl+0dtVbp1slJ5VbU
6/pIhEDlcQb+GtjUOaUL1peGSypgpQp99jFY4SDinZ4K28bK7+P0S5wasLvoGtoRugAele
yiM3AAAAFQC4R7F34WwxJs1KwnP30dw07syHLwAAAIEAv74W9zJfw9ntV5CP6C7QKQOFkz
Xgik0Yoo4EMW9lNQgsccbtOeSMFHUcnD9tGXCS+u9gL7XnmxP8ELY4qlWSJTILDUsoZW0k
FqztSWqZNribV8QaNpZfRvU6naGnCnEKSaOpLTseUL+cQewQQx0Jr0fEJ9sR0yZR0s1LAR
Ums8AAAACAAZvonbhFrh/Pnm+UrW+3fi7f3Rh0yDVEuIJrh0W5tzySEQxgqnAEijIQKCUn
IN0tee4lCLBXM2BfVrPHXQbzh3lAKgBk9ApYrWhaOPysH7iC0SMTWwHt9mTFJcX8FpElA+
KCWbSQ32AlTpFlLPi8pkgTwGT/6G1eBxe+/xHR5KEAAAAUCbVJM+EkJ5K/JWd08304hz+O
Cq8AAAANYWp2YW5lcnBAdGhvcgECAwQF
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1 @@
ssh-dss AAAAB3NzaC1kc3MAAACBAMZQPdecRB/dLiIHwttUDD12JJpNNtooZG34Pz13pBYcnvkjrzgXqJ6FfOvuIqo+aZXLYGncF9IzWbo/jAl+0dtVbp1slJ5VbU6/pIhEDlcQb+GtjUOaUL1peGSypgpQp99jFY4SDinZ4K28bK7+P0S5wasLvoGtoRugAeleyiM3AAAAFQC4R7F34WwxJs1KwnP30dw07syHLwAAAIEAv74W9zJfw9ntV5CP6C7QKQOFkzXgik0Yoo4EMW9lNQgsccbtOeSMFHUcnD9tGXCS+u9gL7XnmxP8ELY4qlWSJTILDUsoZW0kFqztSWqZNribV8QaNpZfRvU6naGnCnEKSaOpLTseUL+cQewQQx0Jr0fEJ9sR0yZR0s1LARUms8AAAACAAZvonbhFrh/Pnm+UrW+3fi7f3Rh0yDVEuIJrh0W5tzySEQxgqnAEijIQKCUnIN0tee4lCLBXM2BfVrPHXQbzh3lAKgBk9ApYrWhaOPysH7iC0SMTWwHt9mTFJcX8FpElA+KCWbSQ32AlTpFlLPi8pkgTwGT/6G1eBxe+/xHR5KE= ajvanerp@thor

View File

@@ -128,5 +128,13 @@ Subsystem sftp /usr/lib/ssh/sftp-server
# PermitTTY no
# 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
TrustedUserCAKeys /etc/ssh/users_rsa_ca.pub
KexAlgorithms +diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1
macs +hmac-ripemd160-etm@openssh.com,hmac-ripemd160,hmac-ripemd160@openssh.com
Ciphers +3des-cbc,blowfish-cbc,aes128-cbc,aes192-cbc,aes256-cbc
PubKeyAcceptedKeyTypes +ssh-dss
HostKeyAlgorithms +ssh-dss
HostbasedAcceptedKeyTypes +ssh-dss

View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDN70b/cYHZQMD1YW0mlncXqC2l++sEWrVYlIUCzNxNhRYjI4UmEVEq3ru1h6K3ZVAJi1DcZuf5ne1ZXtwJ1Uw1JA4wGdKw+9TwAb5Gubn+VEowgt62kLAPeChiPucTXD0FDDhIUOBv3KxytdrJIYAtzZT27STsBiDF1+7Ld3wk/1Dg9NAaI6q40PmuicTEACQRHn5snI1t9+LgZTd3/PPE5pjJM0ow9+r6mlUUM5oHCk5sZ8DBuRR1Ram4sxp/LFQM+9feMmW3ZM2C5AN0JG4A7NXnlwiTKmNVrGI0iFucBBKhjxN1qdgBF11/42cCrerC9UW1auTTi9mqwEIqBGL30VOPy+dCPQQViP+C09CBgyr3wpZciPKP1mvmcOkC5FDzKg9e3v1JBq0fqZgwt+PPG8cGnxRCGEQ+ZMLDuAixkQUEwDWeMskHLkbjUEiVZydViCPSzFczGtKatQiQVZA5Zx0Gn2sUaQjykhWzqKNL8oIbolEdkH9ubOZWNi0brzU= root@sshj

6
src/itest/generate.sh Normal file
View File

@@ -0,0 +1,6 @@
#!/bin/sh
# Don't call it frequently. It's rather a documentation how everything is generated.
ssh-keygen -f resources/users_rsa_ca -t rsa -N ''
mv resources/users_rsa_ca.pub docker-image/test-container
ssh-keygen -f resources/keyfiles/id_rsa2 -t rsa -m pem -N ''
ssh-keygen -s resources/users_rsa_ca -I my_key_id -n sshj resources/keyfiles/id_rsa2.pub

View File

@@ -15,10 +15,14 @@
*/
package com.hierynomus.sshj
import com.hierynomus.sshj.signature.SignatureEdDSA
import com.hierynomus.sshj.key.KeyAlgorithms
import com.hierynomus.sshj.transport.kex.DHGroups
import com.hierynomus.sshj.transport.cipher.BlockCiphers
import com.hierynomus.sshj.transport.mac.Macs
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.signature.SignatureECDSA
import net.schmizz.sshj.transport.TransportException
import net.schmizz.sshj.userauth.UserAuthException
import spock.lang.Unroll
@@ -29,7 +33,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
def "should accept correct key for #signatureName"() {
given:
def config = new DefaultConfig()
config.setSignatureFactories(signatureFactory)
config.setKeyAlgorithms(Collections.singletonList(signatureFactory))
SSHClient sshClient = new SSHClient(config)
sshClient.addHostKeyVerifier(fingerprint) // test-containers/ssh_host_ecdsa_key's fingerprint
@@ -40,7 +44,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
sshClient.isConnected()
where:
signatureFactory << [new SignatureECDSA.Factory256(), new SignatureEdDSA.Factory()]
signatureFactory << [KeyAlgorithms.ECDSASHANistp256(), KeyAlgorithms.EdDSA25519()]
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()
}
@@ -78,6 +82,8 @@ class IntegrationSpec extends IntegrationBaseSpec {
"id_ed25519_opensshv1_protected" | "sshjtest"
"id_rsa" | null
"id_rsa_opensshv1" | null
"id_ecdsa_nistp384_opensshv1" | null
"id_ecdsa_nistp521_opensshv1" | null
}
def "should not authenticate with wrong key"() {
@@ -91,4 +97,22 @@ class IntegrationSpec extends IntegrationBaseSpec {
thrown(UserAuthException.class)
!client.isAuthenticated()
}
def "should correctly connect using parameters in #670"() {
given:
def config = new DefaultConfig()
// config.setKeyAlgorithms(Collections.singletonList(KeyAlgorithms.SSHDSA())) // Can't get openssh to offer ssh-dss
config.setKeyExchangeFactories(DHGroups.Group1SHA1())
config.setCipherFactories(BlockCiphers.TripleDESCBC())
config.setMACFactories(Macs.HMACSHA1())
SSHClient sshClient = new SSHClient(config)
sshClient.addHostKeyVerifier(new PromiscuousVerifier())
when:
sshClient.connect(SERVER_IP, DOCKER_PORT)
then:
sshClient.isConnected()
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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 net.schmizz.sshj.DefaultConfig
import spock.lang.Unroll
class RsaSignatureClientKeySpec extends IntegrationBaseSpec {
@Unroll
def "should correctly connect using publickey auth with RSA key with signature"() {
given:
def client = getConnectedClient(new DefaultConfig())
when:
client.authPublickey(USERNAME, "src/itest/resources/keyfiles/id_rsa2")
then:
client.authenticated
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.signature
import com.hierynomus.sshj.IntegrationBaseSpec
import com.hierynomus.sshj.key.KeyAlgorithms
import net.schmizz.sshj.DefaultConfig
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 << [KeyAlgorithms.SSHRSA(), KeyAlgorithms.RSASHA256(), KeyAlgorithms.RSASHA512()]
sig = sigFactory.name
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.cipher
import com.hierynomus.sshj.IntegrationBaseSpec
import net.schmizz.sshj.DefaultConfig
import spock.lang.Unroll
class CipherSpec extends IntegrationBaseSpec {
@Unroll
def "should correctly connect with #cipher Cipher"() {
given:
def cfg = new DefaultConfig()
cfg.setCipherFactories(cipherFactory)
def client = getConnectedClient(cfg)
when:
client.authPublickey(USERNAME, KEYFILE)
then:
client.authenticated
cleanup:
client.disconnect()
where:
cipherFactory << [BlockCiphers.TripleDESCBC(),
BlockCiphers.BlowfishCBC(),
BlockCiphers.AES128CBC(),
BlockCiphers.AES128CTR(),
BlockCiphers.AES192CBC(),
BlockCiphers.AES192CTR(),
BlockCiphers.AES256CBC(),
BlockCiphers.AES256CTR(),
GcmCiphers.AES128GCM(),
GcmCiphers.AES256GCM()]
cipher = cipherFactory.name
}
}

View File

@@ -17,9 +17,6 @@ package com.hierynomus.sshj.transport.mac
import com.hierynomus.sshj.IntegrationBaseSpec
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.transport.mac.HMACRIPEMD160
import net.schmizz.sshj.transport.mac.HMACSHA2256
import spock.lang.AutoCleanup
import spock.lang.Unroll
class MacSpec extends IntegrationBaseSpec {

View File

@@ -0,0 +1,10 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTL30WEnuOFUBsMXsTjIm3DN/n1nlD9
Y6waHzsRQ0Ua80RfHSv75ZdrL6kz1zEZyE509VKHphHAeTh7bx+Ic34fxxXg8SeUu1J3GM
aApU0mvDHjBRtmY/QqFGbNUh28EhoAAADYHWlHLx1pRy8AAAATZWNkc2Etc2hhMi1uaXN0
cDM4NAAAAAhuaXN0cDM4NAAAAGEEy99FhJ7jhVAbDF7E4yJtwzf59Z5Q/WOsGh87EUNFGv
NEXx0r++WXay+pM9cxGchOdPVSh6YRwHk4e28fiHN+H8cV4PEnlLtSdxjGgKVNJrwx4wUb
ZmP0KhRmzVIdvBIaAAAAMQD3sx28SrtkuhN+Yu06BAoFLMMgneIqguM3jowaz0LWfP1Nhx
Rnh9tNKM6YYvygCggAAAAPZmhlbm5la2VATGFwdG9w
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1,12 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQB6rlGIJFPcCa3MQ1lbXarX6n+kgQ3
Tw0ccA5ezgqIVdlDbtinYycEo/I9H7D3jHR40or/Tgpx2K1YpUPluVluaucBl8JNdKCHFd
CSbHKISwNJ0u5iopwa8VMFR5zWWzxMRXIonJxAK3GEW9Qbk+xxfIX47kkqVktK6G+pJaEk
ov41e2QAAAEQwljQZcJY0GUAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
AAAIUEAeq5RiCRT3AmtzENZW12q1+p/pIEN08NHHAOXs4KiFXZQ27Yp2MnBKPyPR+w94x0
eNKK/04KcditWKVD5blZbmrnAZfCTXSghxXQkmxyiEsDSdLuYqKcGvFTBUec1ls8TEVyKJ
ycQCtxhFvUG5PscXyF+O5JKlZLSuhvqSWhJKL+NXtkAAAAQVXt20tSeLzMU1U2nMv8CEEY
Oyl1WIkGAcRatDBAfsE0+NcJ/eSbPXywWAqCzOElQ5ftNFz9t1kNXwW5qiLaaIBpAAAAD2
ZoZW5uZWtlQExhcHRvcAECAwQ=
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
MIIG5QIBAAKCAYEA0MZ2xduy86/VbzZWnOnzSqUVeU7ajzwc3LF9vR9ZbufZdCB8
pYRzpQ4B9v3umDZ3nX9qPM4f2iMVDfiz241SwmvV5s7WiOHPhy3ij5mUpRXOn3a8
byVIjdraaVawL642p98cLKB9WKEjKaDw633aD7xzSIomqQboVZrMHNjvIanyd3U6
j9mnoydXD1cIuceZx4S/Av8IQru9AiZYJ7rAfZwz3ip9YWcgT0yIohDbBSX7fsX2
7KF6txIfRXJCJg0QO0kOYoMQmLYJtkVApwixdV29Vx2g5PBKXDQ12FwGXTnZG36v
YYZ7ng12PV+xMyC1xPLdXnzrNR/lO4BGtT14GYJbwVM3gjBDrceavaayVW4SjFrq
R5XDLjldKAfKtCHEavLv16Df1eAwnPlGAwmODlRqTHvL5oxBQibvNjDzVpvfqEsF
RqP/d4AbYjARsKCkeu3PzUI+rq5AtwZ52XKgGdg13wURoAkIsNXonKo92avLnomq
skdTYpfBEOUMSZO9AgMBAAECggGAadURhH84mft6kKPVCDo4UJCa8CGe/ZkVcHKx
MNvhdC0nuIx3Y1hfXz5YlKJo/tQtkrNyYVyEHQpHtAts8VEUsOYFSrlzW3RMxVPn
U7AhAAar9X41S4p02yQkL9339lOz9SlOmPjKUdFth77EIjxr/ColrpIJwwlzYWHV
MpJttnz2IsRUaXOGXVil82rFS5f5RoDua1BpGZsd1yck7Q7oYUR7rpWPdX7XjBtZ
7/3naRa2BK/J2m6JTKBtJcEj8zIPK4HXVO7NriQvhMyFAc5FRR+0ZQFp0bNdCELT
k7nbDaHL8QPfzRiH9zLHe/WHJKNZxTcXctU1v/aBBNPAwsT4NHLydrtVagzBZ/YO
ZBB1JKfpdmnQ54vNI1jlkNCbhkMNPZuDCCk3l4qq6JLi3DrYYqi1B8qzVxivw8ps
0eNt4tMHqmOGwMBEzaYcAXfn66Lea84+QyGc5ibwPuBPtuQcNvHdY4BzXFerDqDI
i/rdMH0ELvTPbgpgl3SrIqCWCoMpAoHBAPZcpWDK22owtw0D0QDylO/H3rkf3WX5
5bo9FzoKtZdHNhZJUYFPBC26xHKXJgSKh6OuL9IlXtpQD3u4EctBevnJU8OZxKAM
MJZ/vIXxnAGONuhcDs/kns0Bu9QNgdUOdYW1DomNaePQ669awhXtwaxVeBqwl6AT
v6PnlzyRZK9Di/9xwkh/g0h4258ef0grD2oDUIxDvEnm9Ul9mE8MIqUjQw/dfpuA
D2mW0nh+FK861VbGRrlumhr4y33KNIs5SwKBwQDY8WGyGlPvjPBUD4kHaledNCYu
vMBvlo8UKlk9SpcRpHUqOm5IFmKmmLQdEqYip4PULGZVFjbrfil4LKV9pMxtmn56
HKqteoWP1VuqhC1j2RdnX1KuIqB+sv30BkWsrOsOC6YwaDHl5eLPJv0BbFWApBEm
/g2n2wpoHvGr2uSl0+B2mRw/BFcWw3zLcBB6OP/U4hjpPgEfwHLp2tG8DraT/dOm
7o+DYQQz6ACqz8pzrMyzS/6uaR/YK70Q8RKPihcCgcEAliiH4EQkkkfY+oTN+h7h
KnkPRpSmdEZpgCdGJelrHyaXT+QmWoNXz6ubmyCHWpM480ny+covUy8jEMxzhAiI
NQFCHeF2V/q2DrUSqi1GYihVTTD3Ej2NkPSykCAfd0XV2cYucyaPWPz4+it+SrFc
r3Z0uwfRkw8Waod4xcD0tmKcTPkAawHVefG4IvcKq2kbfwlAKg4LEJxF+yIjGGMU
JsUkVeIyDgNy4W+9HxXx85APglFdwB4qra/hD+2UMxubAoHBAIRUeOtS8/AjYFVm
RIepblgN/1xy9k8a35vFWTnxzcSNNIrVqX9/aB2G+Bbj0UNCOz+o9aLhMg7jnhgX
47qIU8pnes6xvcqj+eSKmKeiiK1nNsdvddeSd6PROucnDEkQETE4Gd9dL1K0r2z8
s0ey9VTKv0uxnFLPYcGxXmkd1GrymvC85GXsF9Ni2zSc3vAu5J7Oa7OahsT/dxj+
yQCVWPlc00X4LsJM42tmEUIgDbYRqauUp31r0mjiBSnYYyH7cQKBwQC+2mw3oRsu
La9Yj11WcwWy6in26JyITYmEnfwydDTdX8tn9XdPva94frC/TJkDmoh9gYSNZc8r
zm9y/l9LpO98jIG+jH9wFRKO7xgCffZl4tbvu1DsGbh86IS7gvRbIwN5mnrKC61g
4c/+PYAJnyC7+0oXhY2W7h2STUWi3/AvT/EnGLv7k41+9H3Hj5oqkWEt+DITgBia
sTT3iRK6YVtXBg+AyG7nbobwTtZu98uFBjHwzGaelSPGGiWsUx4/u2Y=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1 @@
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgUsee85QZv49JUmGGA6cN852mR2cSsEst9KDH7gk3Lq4AAAADAQABAAABgQDQxnbF27Lzr9VvNlac6fNKpRV5TtqPPBzcsX29H1lu59l0IHylhHOlDgH2/e6YNnedf2o8zh/aIxUN+LPbjVLCa9XmztaI4c+HLeKPmZSlFc6fdrxvJUiN2tppVrAvrjan3xwsoH1YoSMpoPDrfdoPvHNIiiapBuhVmswc2O8hqfJ3dTqP2aejJ1cPVwi5x5nHhL8C/whCu70CJlgnusB9nDPeKn1hZyBPTIiiENsFJft+xfbsoXq3Eh9FckImDRA7SQ5igxCYtgm2RUCnCLF1Xb1XHaDk8EpcNDXYXAZdOdkbfq9hhnueDXY9X7EzILXE8t1efOs1H+U7gEa1PXgZglvBUzeCMEOtx5q9prJVbhKMWupHlcMuOV0oB8q0IcRq8u/XoN/V4DCc+UYDCY4OVGpMe8vmjEFCJu82MPNWm9+oSwVGo/93gBtiMBGwoKR67c/NQj6urkC3BnnZcqAZ2DXfBRGgCQiw1eicqj3Zq8ueiaqyR1Nil8EQ5QxJk70AAAAAAAAAAAAAAAEAAAAJbXlfa2V5X2lkAAAACAAAAARzc2hqAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDN70b/cYHZQMD1YW0mlncXqC2l++sEWrVYlIUCzNxNhRYjI4UmEVEq3ru1h6K3ZVAJi1DcZuf5ne1ZXtwJ1Uw1JA4wGdKw+9TwAb5Gubn+VEowgt62kLAPeChiPucTXD0FDDhIUOBv3KxytdrJIYAtzZT27STsBiDF1+7Ld3wk/1Dg9NAaI6q40PmuicTEACQRHn5snI1t9+LgZTd3/PPE5pjJM0ow9+r6mlUUM5oHCk5sZ8DBuRR1Ram4sxp/LFQM+9feMmW3ZM2C5AN0JG4A7NXnlwiTKmNVrGI0iFucBBKhjxN1qdgBF11/42cCrerC9UW1auTTi9mqwEIqBGL30VOPy+dCPQQViP+C09CBgyr3wpZciPKP1mvmcOkC5FDzKg9e3v1JBq0fqZgwt+PPG8cGnxRCGEQ+ZMLDuAixkQUEwDWeMskHLkbjUEiVZydViCPSzFczGtKatQiQVZA5Zx0Gn2sUaQjykhWzqKNL8oIbolEdkH9ubOZWNi0brzUAAAGUAAAADHJzYS1zaGEyLTUxMgAAAYBxPe4yc3IwnjwULBv1tk3uTr35MKqIiDrIuWX9+7w/EJ9hQPjSZjtiOABP/C63F2c43zvN1XUqJ1Yvrh1dqpI5nIxexMLFRgJrawALHNsc5h5uLBL6q9rgyJsUNWXMgl9nOUGDDh+Jg6yM6SUVd44WuDTbgxtIwc5Uq6fE+HFowM35Xt0u9UEOBqOzpyBNYjNEsCMfPxkbzYGcLW9Y2C28ygrizRn+rhXFG3sQxraiCPVAL+d9NMGV3TaFXScEoCuXKRyHab1ch9g613FBL16k2rxZWoHfwI07bAUrpcv5upR5RMeeYrNmE7mrQ5WHemACd6QJtknPxOfxgAKOck486b29dgAt5C6P81T0mL0+52bu0LkbRYxVEExdeDurvhue2ZzGG11PbEJdLpVbgcPJr7dUkPPG2nPW4JLqqnzlulqu7Me07mgJuVK1OnXUKKziQ8BcDqx6lsdBg/CuKvL4PedFkSh32W+7VIQq7e9sVEV+Q5CngWfr2sYFh3cu3kw= lagunov@unit-1381

View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQxnbF27Lzr9VvNlac6fNKpRV5TtqPPBzcsX29H1lu59l0IHylhHOlDgH2/e6YNnedf2o8zh/aIxUN+LPbjVLCa9XmztaI4c+HLeKPmZSlFc6fdrxvJUiN2tppVrAvrjan3xwsoH1YoSMpoPDrfdoPvHNIiiapBuhVmswc2O8hqfJ3dTqP2aejJ1cPVwi5x5nHhL8C/whCu70CJlgnusB9nDPeKn1hZyBPTIiiENsFJft+xfbsoXq3Eh9FckImDRA7SQ5igxCYtgm2RUCnCLF1Xb1XHaDk8EpcNDXYXAZdOdkbfq9hhnueDXY9X7EzILXE8t1efOs1H+U7gEa1PXgZglvBUzeCMEOtx5q9prJVbhKMWupHlcMuOV0oB8q0IcRq8u/XoN/V4DCc+UYDCY4OVGpMe8vmjEFCJu82MPNWm9+oSwVGo/93gBtiMBGwoKR67c/NQj6urkC3BnnZcqAZ2DXfBRGgCQiw1eicqj3Zq8ueiaqyR1Nil8EQ5QxJk70= lagunov@unit-1381

View File

@@ -0,0 +1,38 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAze9G/3GB2UDA9WFtJpZ3F6gtpfvrBFq1WJSFAszcTYUWIyOFJhFR
Kt67tYeit2VQCYtQ3Gbn+Z3tWV7cCdVMNSQOMBnSsPvU8AG+Rrm5/lRKMILetpCwD3goYj
7nE1w9BQw4SFDgb9yscrXaySGALc2U9u0k7AYgxdfuy3d8JP9Q4PTQGiOquND5ronExAAk
ER5+bJyNbffi4GU3d/zzxOaYyTNKMPfq+ppVFDOaBwpObGfAwbkUdUWpuLMafyxUDPvX3j
Jlt2TNguQDdCRuAOzV55cIkypjVaxiNIhbnAQSoY8TdanYARddf+NnAq3qwvVFtWrk04vZ
qsBCKgRi99FTj8vnQj0EFYj/gtPQgYMq98KWXIjyj9Zr5nDpAuRQ8yoPXt79SQatH6mYML
fjzxvHBp8UQhhEPmTCw7gIsZEFBMA1njLJBy5G41BIlWcnVYgj0sxXMxrSmrUIkFWQOWcd
Bp9rFGkI8pIVs6ijS/KCG6JRHZB/bmzmVjYtG681AAAFiJrvSX6a70l+AAAAB3NzaC1yc2
EAAAGBAM3vRv9xgdlAwPVhbSaWdxeoLaX76wRatViUhQLM3E2FFiMjhSYRUSreu7WHordl
UAmLUNxm5/md7Vle3AnVTDUkDjAZ0rD71PABvka5uf5USjCC3raQsA94KGI+5xNcPQUMOE
hQ4G/crHK12skhgC3NlPbtJOwGIMXX7st3fCT/UOD00BojqrjQ+a6JxMQAJBEefmycjW33
4uBlN3f888TmmMkzSjD36vqaVRQzmgcKTmxnwMG5FHVFqbizGn8sVAz7194yZbdkzYLkA3
QkbgDs1eeXCJMqY1WsYjSIW5wEEqGPE3Wp2AEXXX/jZwKt6sL1RbVq5NOL2arAQioEYvfR
U4/L50I9BBWI/4LT0IGDKvfCllyI8o/Wa+Zw6QLkUPMqD17e/UkGrR+pmDC3488bxwafFE
IYRD5kwsO4CLGRBQTANZ4yyQcuRuNQSJVnJ1WII9LMVzMa0pq1CJBVkDlnHQafaxRpCPKS
FbOoo0vyghuiUR2Qf25s5lY2LRuvNQAAAAMBAAEAAAGABvaYR/rmkRoHbESnFC7yR/J/2K
T0BWmryBr9hGK48EYXwYhp8CeVvwVZA4Jaliju0+PKECnKnj4g0GzMs+hqc0GM2UOGREW/
pX3pmSqeh2MCPzGtpi6uRVeixe+qkJUF2y3WmVtiu2WSzy4m/7YKR4I0D0VlgjWS1h2/DV
I0+GtJqNGeV8Ps+eLXDnfKF3aJwapuS+3fOmCvYzcI8R20gGvrrqH1WEKJx3+AcPZttt86
V6AKfIJtlqmMW5pywuoUveYGDLCbg1oajqh7o06eTiFqZuCzbKjJUahzc1IvjsMRmP8rij
WVAVZdUV5NiLYjmPczxS12zcUCmPJo5geDQLpCJdfgFYWCk9nr08MCLXmD973S4egrfw33
+WMblW7R6vkHFUYK1Nfxe+Ume3mag+KbRJc0fYrO4Y+1BrhnsO5lqRHjkvl4jldYkAoqly
jnbk9PwWn4+u/FZ5mUyP7RCEK4INvXah/dG44GwRfY/OyoeQMM4P0BDedp5pGJn73hAAAA
wQDvs3FzKxF8uZPyDBZjEbn61m6oTVXE9PRl1kSzxD2wPqmZUPr1WtK/1ugWSMBoFTk72J
z5wXfaFVifz7U5EzQ23yZkBN2aL6jzu7H5OcxgZZVS3sL2WYfZ/lRphX0kHYBcM7ZChIbV
sDmheqE0wVTdTP809j1NX/sQOBShVg7vj20VTRYQs7SCBzsPEAmXE6a1haWBKfrZfhPN/3
xZJ162n2FA21LQjDcfHLWeSzSHjzdOK/3YSr/8o7EGqK7iTbYAAADBAPodvvOQvHm0k9Ki
7BzTQCCWCog0jR26rKeH5QlCi6fwmWBFR1vVjrqBRzhPUIH0sml6yOlYqpiKi0cNTZZ3dP
QuUU2U63AnyQyuSTAnOWy9mh9aAguWK4IdtuToH+rJCi4lyy4khFaeyzbbb6iD4HDR/sYF
AARMj/rAHypcnUSwkwSRiRoFy2w/h21roIocgBFfrVJUe1RDHTB9tiDFTqQ/WKWJFPfBSV
I4bqpWlFKFAIkbIaCEe2DIKHMp6ywNJwAAAMEA0sd18i4dOzDt0Sx+aYpK5tcx7G1Oen67
8WddT5bO63R4yQ4gCumiH2GQTaEnQIe9iTZ5hMQiwJJcfP9zI60g0RgMabicAX6TmW6ej8
4ZVfZhkQoCU15Ih4uIuCreSbwo/Q1FT48GA6UScqrDuSzaEenDuWKAv0NWap9B0Rro9eCb
ppaSE8QmSoojWAEAnOjaL/ig7j808xaDlNiMXKnmMdV4Lrj2OzyhqE5LpL7fShqhQ5CryY
l4Kj1BWsuKzJJDAAAAEWxhZ3Vub3ZAdW5pdC0xMzgxAQ==
-----END OPENSSH PRIVATE KEY-----

View 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";
}

View 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.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.
*/
@SuppressWarnings("serial")
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);
}
}

View File

@@ -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 class BaseKeyAlgorithm implements KeyAlgorithm {
private final String keyAlgorithm;
private final Factory.Named<Signature> signature;
private final KeyType keyFormat;
public BaseKeyAlgorithm(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();
}
}

View 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();
}

View 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 com.hierynomus.sshj.signature.SignatureEdDSA;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.signature.SignatureDSA;
import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import java.util.Arrays;
import java.util.List;
public class KeyAlgorithms {
public static List<String> SSH_RSA_SHA2_ALGORITHMS = Arrays.asList("rsa-sha2-512", "rsa-sha2-256");
public static Factory SSHRSA() { return new Factory("ssh-rsa", new SignatureRSA.FactorySSHRSA(), KeyType.RSA); }
public static Factory SSHRSACertV01() { return new Factory("ssh-rsa-cert-v01@openssh.com", new SignatureRSA.FactoryCERT(), KeyType.RSA_CERT); }
public static Factory RSASHA256() { return new Factory("rsa-sha2-256", new SignatureRSA.FactoryRSASHA256(), KeyType.RSA); }
public static Factory RSASHA512() { return new Factory("rsa-sha2-512", new SignatureRSA.FactoryRSASHA512(), KeyType.RSA); }
public static Factory SSHDSA() { return new Factory(KeyType.DSA.toString(), new SignatureDSA.Factory(), KeyType.DSA); }
public static Factory SSHDSSCertV01() { return new Factory(KeyType.DSA_CERT.toString(), new SignatureDSA.Factory(), KeyType.DSA_CERT); }
public static Factory ECDSASHANistp256() { return new Factory(KeyType.ECDSA256.toString(), new SignatureECDSA.Factory256(), KeyType.ECDSA256); }
public static Factory ECDSASHANistp384() { return new Factory(KeyType.ECDSA384.toString(), new SignatureECDSA.Factory384(), KeyType.ECDSA384); }
public static Factory ECDSASHANistp521() { return new Factory(KeyType.ECDSA521.toString(), new SignatureECDSA.Factory521(), KeyType.ECDSA521); }
public static Factory EdDSA25519() { return new Factory(KeyType.ED25519.toString(), new SignatureEdDSA.Factory(), KeyType.ED25519); }
public static class Factory implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
private final String algorithmName;
private final Named<Signature> signatureFactory;
private final KeyType keyType;
public Factory(String algorithmName, Named<Signature> signatureFactory, KeyType keyType) {
this.algorithmName = algorithmName;
this.signatureFactory = signatureFactory;
this.keyType = keyType;
}
@Override
public String getName() {
return algorithmName;
}
@Override
public KeyAlgorithm create() {
return new BaseKeyAlgorithm(algorithmName, signatureFactory, keyType);
}
}
}

View File

@@ -27,6 +27,7 @@ import java.util.Arrays;
* Our own extension of the EdDSAPublicKey that comes from ECC-25519, as that class does not implement equality.
* The code uses the equality of the keys as an indicator whether they're the same during host key verification.
*/
@SuppressWarnings("serial")
public class Ed25519PublicKey extends EdDSAPublicKey {
public Ed25519PublicKey(EdDSAPublicKeySpec spec) {

View File

@@ -40,7 +40,7 @@ public class SignatureEdDSA extends AbstractSignature {
}
SignatureEdDSA() {
super(getEngine());
super(getEngine(), KeyType.ED25519.toString());
}
private static EdDSAEngine getEngine() {

View File

@@ -79,11 +79,9 @@ public class IdentificationStringParser {
}
if (bytes[bytes.length - 2] != '\r') {
String ident = new String(bytes, 0, bytes.length - 1);
log.warn("Server identification has bad line ending, was expecting a '\\r\\n' however got: '{}' (hex: {})", (char) (bytes[bytes.length - 2] & 0xFF), Integer.toHexString(bytes[bytes.length - 2] & 0xFF));
log.warn("Will treat the identification of this server '{}' leniently", ident);
log.info("Server identification has bad line ending, was expecting a '\\r\\n' however got: '{}' (hex: {})", (char) (bytes[bytes.length - 2] & 0xFF), Integer.toHexString(bytes[bytes.length - 2] & 0xFF));
log.info("Will treat the identification of this server '{}' leniently", ident);
return ident;
// log.error("Data received up til here was: {}", new String(bytes));
// throw new TransportException("Incorrect identification: bad line ending: " + ByteArrayUtils.toHex(bytes, 0, bytes.length));
}
// Strip off the \r\n

View File

@@ -0,0 +1,164 @@
/*
* 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.cipher;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.transport.cipher.BaseCipher;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
public class GcmCipher extends BaseCipher {
protected int authSize;
protected Mode mode;
protected boolean initialized;
protected CounterGCMParameterSpec parameters;
protected SecretKey secretKey;
public GcmCipher(int ivsize, int authSize, int bsize, String algorithm, String transformation) {
super(ivsize, bsize, algorithm, transformation);
this.authSize = authSize;
}
@Override
public int getAuthenticationTagSize() {
return authSize;
}
protected Cipher getInitializedCipherInstance() throws GeneralSecurityException {
if (!initialized) {
cipher.init(mode == Mode.Encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey, parameters);
initialized = true;
}
return cipher;
}
@Override
protected void initCipher(Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
this.mode = mode;
this.secretKey = getKeySpec(key);
this.parameters = new CounterGCMParameterSpec(getAuthenticationTagSize() * Byte.SIZE, iv);
cipher.init(getMode(mode), secretKey, parameters);
initialized = true;
}
@Override
public void updateAAD(byte[] data, int offset, int length) {
try {
Cipher cipher = getInitializedCipherInstance();
cipher.updateAAD(data, offset, length);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException("Error updating data through cipher", e);
}
}
@Override
public void update(byte[] input, int inputOffset, int inputLen) {
if (mode == Mode.Decrypt) {
inputLen += getAuthenticationTagSize();
}
try {
Cipher cipher = getInitializedCipherInstance();
cipher.doFinal(input, inputOffset, inputLen, input, inputOffset);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException("Error updating data through cipher", e);
}
/*
* As the RFC stated, the IV used in AES-GCM cipher for SSH is a 4-byte constant plus a 8-byte invocation
* counter. After cipher.doFinal() is called, IV invocation counter is increased by 1, and changes to the IV
* requires reinitializing the cipher, so initialized have to be false here, for the next invocation.
*
* Refer to RFC5647, Section 7.1
*/
parameters.incrementCounter();
initialized = false;
}
/**
* Algorithm parameters for AES/GCM that assumes the IV uses an 8-byte counter field as its most significant bytes.
*/
protected static class CounterGCMParameterSpec extends GCMParameterSpec {
protected final byte[] iv;
protected CounterGCMParameterSpec(int tLen, byte[] src) {
super(tLen, src);
if (src.length != 12) {
throw new IllegalArgumentException("GCM nonce must be 12 bytes, but given len=" + src.length);
}
iv = src.clone();
}
protected void incrementCounter() {
int off = iv.length - 8;
long counter = getLong(iv, off, 8);
putLong(addExact(counter, 1L), iv, off, 8);
}
@Override
public byte[] getIV() {
// JCE implementation of GCM will complain if the reference doesn't change between inits
return iv.clone();
}
static long addExact(long var0, long var2) {
long var4 = var0 + var2;
if (((var0 ^ var4) & (var2 ^ var4)) < 0L) {
throw new ArithmeticException("long overflow");
} else {
return var4;
}
}
static long getLong(byte[] buf, int off, int len) {
if (len < 8) {
throw new IllegalArgumentException("Not enough data for a long: required=8, available=" + len);
}
long l = (long) buf[off] << 56;
l |= ((long) buf[off + 1] & 0xff) << 48;
l |= ((long) buf[off + 2] & 0xff) << 40;
l |= ((long) buf[off + 3] & 0xff) << 32;
l |= ((long) buf[off + 4] & 0xff) << 24;
l |= ((long) buf[off + 5] & 0xff) << 16;
l |= ((long) buf[off + 6] & 0xff) << 8;
l |= (long) buf[off + 7] & 0xff;
return l;
}
static int putLong(long value, byte[] buf, int off, int len) {
if (len < 8) {
throw new IllegalArgumentException("Not enough data for a long: required=8, available=" + len);
}
buf[off] = (byte) (value >> 56);
buf[off + 1] = (byte) (value >> 48);
buf[off + 2] = (byte) (value >> 40);
buf[off + 3] = (byte) (value >> 32);
buf[off + 4] = (byte) (value >> 24);
buf[off + 5] = (byte) (value >> 16);
buf[off + 6] = (byte) (value >> 8);
buf[off + 7] = (byte) value;
return 8;
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.cipher;
import net.schmizz.sshj.transport.cipher.Cipher;
public class GcmCiphers {
public static final String GALOIS_COUNTER_MODE = "GCM";
public static Factory AES128GCM() {
return new Factory(12, 16, 128, "aes128-gcm@openssh.com", "AES", GALOIS_COUNTER_MODE);
}
public static Factory AES256GCM() {
return new Factory(12, 16, 256, "aes256-gcm@openssh.com", "AES", GALOIS_COUNTER_MODE);
}
/** Named factory for BlockCipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
private int keysize;
private int authSize;
private String cipher;
private String mode;
private String name;
private int ivsize;
/**
* @param ivsize
* @param keysize The keysize used in bits.
* @param name
* @param cipher
* @param mode
*/
public Factory(int ivsize, int authSize, int keysize, String name, String cipher, String mode) {
this.name = name;
this.keysize = keysize;
this.cipher = cipher;
this.mode = mode;
this.ivsize = ivsize;
this.authSize = authSize;
}
@Override
public Cipher create() {
return new GcmCipher(ivsize, authSize, keysize / 8, cipher, cipher + "/" + mode + "/NoPadding");
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return getName();
}
}
}

View File

@@ -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 com.hierynomus.sshj.transport.kex;
import net.schmizz.sshj.transport.kex.KeyExchange;
/**
* Stub kex algorithm factory that indicates support for SSH2_MSG_EXT_INFO.
* Some servers will not send `rsa-sha2-*` signatures if the client doesn't indicate support.
*
* Note: Since the server sends `ext-info-s` to indicate support, this fake kex algorithm is never negotiated.
*/
public class ExtInfoClientFactory implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
@Override
public String getName() {
return "ext-info-c";
}
@Override
public KeyExchange create() {
return null;
}
}

View File

@@ -15,6 +15,8 @@
*/
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 net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
@@ -111,8 +113,16 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
return readUnencrypted(privateKeyBuffer, publicKey);
} else {
logger.info("Keypair is encrypted with: " + cipherName + ", " + kdfName + ", " + Arrays.toString(kdfOptions));
PlainBuffer decrypted = decryptBuffer(privateKeyBuffer, cipherName, kdfName, kdfOptions);
return readUnencrypted(decrypted, publicKey);
while (true) {
PlainBuffer decryptionBuffer = new PlainBuffer(privateKeyBuffer);
PlainBuffer decrypted = decryptBuffer(decryptionBuffer, cipherName, kdfName, kdfOptions);
try {
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.");
}
}
@@ -133,9 +143,12 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
CharBuffer charBuffer = CharBuffer.wrap(pwdf.reqPassword(null));
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
passphrase = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
Arrays.fill(charBuffer.array(), '\u0000');
Arrays.fill(byteBuffer.array(), (byte) 0);
}
byte[] keyiv = new byte[48];
new BCrypt().pbkdf(passphrase, opts.readBytes(), opts.readUInt32AsInt(), keyiv);
Arrays.fill(passphrase, (byte) 0);
byte[] key = Arrays.copyOfRange(keyiv, 0, 32);
byte[] iv = Arrays.copyOfRange(keyiv, 32, 48);
cipher.init(Cipher.Mode.Decrypt, key, iv);
@@ -184,7 +197,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
int checkInt1 = keyBuffer.readUInt32AsInt(); // uint32 checkint1
int checkInt2 = keyBuffer.readUInt32AsInt(); // uint32 checkint2
if (checkInt1 != checkInt2) {
throw new IOException("The checkInts differed, the key was not correctly decoded.");
throw new KeyDecryptionFailedException();
}
// The private key section contains both the public key and the private key
String keyType = keyBuffer.readString(); // string keytype
@@ -207,7 +220,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
keyBuffer.readMPInt(); // iqmp (q^-1 mod p)
keyBuffer.readMPInt(); // p (Prime 1)
keyBuffer.readMPInt(); // q (Prime 2)
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(n, d)));
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"));
@@ -239,7 +252,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
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("ECDSA").generatePrivate(pks);
return SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks);
}
}

View File

@@ -30,6 +30,7 @@ public abstract class KeepAlive extends Thread {
this.conn = conn;
log = conn.getTransport().getConfig().getLoggerFactory().getLogger(getClass());
setName(name);
setDaemon(true);
}
public synchronized int getKeepAliveInterval() {

View File

@@ -15,14 +15,15 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.signature.SignatureEdDSA;
import com.hierynomus.sshj.key.KeyAlgorithm;
import com.hierynomus.sshj.key.KeyAlgorithms;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.signature.SignatureDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
import java.util.Arrays;
/**
* Registers SpongyCastle as JCE provider.
*/
@@ -33,11 +34,14 @@ public class AndroidConfig
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
}
// don't add ECDSA
protected void initSignatureFactories() {
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
// but add EdDSA
new SignatureEdDSA.Factory());
@Override
protected void initKeyAlgorithms() {
setKeyAlgorithms(Arrays.<Factory.Named<KeyAlgorithm>>asList(
KeyAlgorithms.EdDSA25519(),
KeyAlgorithms.SSHRSA(),
KeyAlgorithms.SSHDSA()
));
}
@Override

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
@@ -77,11 +78,11 @@ public interface Config {
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,
@@ -132,11 +133,11 @@ public interface Config {
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

View File

@@ -15,10 +15,10 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
import net.schmizz.sshj.transport.kex.KeyExchange;
@@ -42,7 +42,7 @@ public class ConfigImpl
private List<Factory.Named<Cipher>> cipherFactories;
private List<Factory.Named<Compression>> compressionFactories;
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 boolean waitForServerIdentBeforeSendingClientIdent = false;
@@ -78,11 +78,6 @@ public class ConfigImpl
return randomFactory;
}
@Override
public List<Factory.Named<Signature>> getSignatureFactories() {
return signatureFactories;
}
@Override
public String getVersion() {
return version;
@@ -138,15 +133,6 @@ public class ConfigImpl
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
public void setVersion(String version) {
this.version = version;
@@ -172,6 +158,16 @@ public class ConfigImpl
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
public LoggerFactory getLoggerFactory() {
return loggerFactory;

View File

@@ -15,10 +15,13 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.signature.SignatureEdDSA;
import com.hierynomus.sshj.key.KeyAlgorithm;
import com.hierynomus.sshj.key.KeyAlgorithms;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import com.hierynomus.sshj.transport.cipher.GcmCiphers;
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
import com.hierynomus.sshj.transport.kex.DHGroups;
import com.hierynomus.sshj.transport.kex.ExtInfoClientFactory;
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
import com.hierynomus.sshj.transport.mac.Macs;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
@@ -26,10 +29,7 @@ import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.signature.SignatureDSA;
import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.cipher.*;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
import net.schmizz.sshj.transport.kex.DHGexSHA1;
@@ -56,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
* 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#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#setFileKeyProviderFactories Key file support}: {@link net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile}*, {@link
* net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile}*</li>
@@ -76,12 +76,12 @@ public class DefaultConfig
setVersion(readVersionFromProperties());
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
initKeyExchangeFactories(bouncyCastleRegistered);
initKeyAlgorithms();
initRandomFactory(bouncyCastleRegistered);
initFileKeyProviderFactories(bouncyCastleRegistered);
initCipherFactories();
initCompressionFactories();
initMACFactories();
initSignatureFactories();
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
}
@@ -99,8 +99,8 @@ public class DefaultConfig
@Override
public void setLoggerFactory(LoggerFactory loggerFactory) {
super.setLoggerFactory(loggerFactory);
log = loggerFactory.getLogger(getClass());
super.setLoggerFactory(loggerFactory);
log = loggerFactory.getLogger(getClass());
}
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
@@ -127,15 +127,29 @@ public class DefaultConfig
ExtendedDHGroups.Group16SHA256(),
ExtendedDHGroups.Group16SHA384AtSSH(),
ExtendedDHGroups.Group16SHA512AtSSH(),
ExtendedDHGroups.Group18SHA512AtSSH());
ExtendedDHGroups.Group18SHA512AtSSH(),
new ExtInfoClientFactory());
} else {
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
}
}
protected void initKeyAlgorithms() {
setKeyAlgorithms(Arrays.<Factory.Named<KeyAlgorithm>>asList(
KeyAlgorithms.EdDSA25519(),
KeyAlgorithms.ECDSASHANistp521(),
KeyAlgorithms.ECDSASHANistp384(),
KeyAlgorithms.ECDSASHANistp256(),
KeyAlgorithms.RSASHA512(),
KeyAlgorithms.RSASHA256(),
KeyAlgorithms.SSHRSACertV01(),
KeyAlgorithms.SSHDSSCertV01(),
KeyAlgorithms.SSHRSA(),
KeyAlgorithms.SSHDSA()));
}
protected void initRandomFactory(boolean bouncyCastleRegistered) {
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered
? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
}
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
@@ -158,6 +172,8 @@ public class DefaultConfig
BlockCiphers.AES192CTR(),
BlockCiphers.AES256CBC(),
BlockCiphers.AES256CTR(),
GcmCiphers.AES128GCM(),
GcmCiphers.AES256GCM(),
BlockCiphers.BlowfishCBC(),
BlockCiphers.BlowfishCTR(),
BlockCiphers.Cast128CBC(),
@@ -207,18 +223,6 @@ public class DefaultConfig
log.debug("Available cipher factories: {}", avail);
}
protected void initSignatureFactories() {
setSignatureFactories(
new SignatureEdDSA.Factory(),
new SignatureECDSA.Factory256(),
new SignatureECDSA.Factory384(),
new SignatureECDSA.Factory521(),
new SignatureRSA.Factory(),
new SignatureRSA.FactoryCERT(),
new SignatureDSA.Factory()
);
}
protected void initMACFactories() {
setMACFactories(
Macs.HMACSHA1(),

View File

@@ -722,6 +722,19 @@ public class SSHClient
return new SFTPClient(new SFTPEngine(this).init());
}
/**
* Stateful FTP client is required in order to connect to Serv-U FTP servers.
* @return Instantiated {@link SFTPClient} implementation.
*
* @throws IOException if there is an error starting the {@code sftp} subsystem
*/
public SFTPClient newStatefulSFTPClient()
throws IOException {
checkConnected();
checkAuthenticated();
return new StatefulSFTPClient(new SFTPEngine(this).init());
}
/**
* Does key re-exchange.
*

View File

@@ -1,5 +1,6 @@
package net.schmizz.sshj.common;
/**
* <p>Encodes and decodes to and from Base64 notation.</p>
* <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
@@ -1359,7 +1360,7 @@ public class Base64
@Override
public Class<?> resolveClass(java.io.ObjectStreamClass streamClass)
throws java.io.IOException, ClassNotFoundException {
Class c = Class.forName(streamClass.getName(), false, loader);
Class<?> c = Class.forName(streamClass.getName(), false, loader);
if( c == null ){
return super.resolveClass(streamClass);
} else {

View File

@@ -21,6 +21,7 @@ import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
@SuppressWarnings("serial")
public class Buffer<T extends Buffer<T>> {
public static class BufferException
@@ -55,10 +56,10 @@ public class Buffer<T extends Buffer<T>> {
public static final int DEFAULT_SIZE = 256;
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
public static final int MAX_SIZE = (1 << 30);
public static final int MAX_SIZE = (1 << 30);
/** Maximum size of a uint64 */
private static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
public static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
.shiftLeft(64)
.subtract(BigInteger.ONE);
@@ -66,7 +67,7 @@ public class Buffer<T extends Buffer<T>> {
int j = 1;
while (j < i) {
j <<= 1;
if (j <= 0) throw new IllegalArgumentException("Cannot get next power of 2; "+i+" is too large");
if (j <= 0) throw new IllegalArgumentException("Cannot get next power of 2; "+i+" is too large");
}
return j;
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.common;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.secg.SecgUtils;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
@@ -87,7 +88,7 @@ class ECDSAVariationsAdapter {
ECPoint p = new ECPoint(bigX, bigY);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
KeyFactory keyFactory = KeyFactory.getInstance(KeyAlgorithm.ECDSA);
return keyFactory.generatePublic(publicKeySpec);
} catch (Exception ex) {
throw new GeneralSecurityException(ex);
@@ -103,7 +104,7 @@ class ECDSAVariationsAdapter {
}
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;
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.common;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.signature.Ed25519PublicKey;
import com.hierynomus.sshj.userauth.certificate.Certificate;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
@@ -30,9 +31,7 @@ import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;
@@ -53,7 +52,7 @@ public enum KeyType {
} catch (Buffer.BufferException 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));
}
@@ -66,7 +65,7 @@ public enum KeyType {
@Override
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) {
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));
}
@@ -99,7 +98,7 @@ public enum KeyType {
@Override
protected boolean isMyType(Key key) {
return (key instanceof DSAPublicKey || key instanceof DSAPrivateKey);
return KeyAlgorithm.DSA.equals(key.getAlgorithm());
}
},
@@ -221,6 +220,11 @@ public enum KeyType {
protected boolean isMyType(Key key) {
return CertUtils.isCertificateOfType(key, RSA);
}
@Override
public KeyType getParent() {
return RSA;
}
},
/** Signed dsa certificate */
@@ -240,6 +244,11 @@ public enum KeyType {
protected boolean isMyType(Key key) {
return CertUtils.isCertificateOfType(key, DSA);
}
@Override
public KeyType getParent() {
return KeyType.DSA;
}
},
/** Unrecognized */
@@ -284,10 +293,24 @@ public enum KeyType {
protected abstract boolean isMyType(Key key);
public static KeyType fromKey(Key key) {
KeyType result = UNKNOWN;
for (KeyType kt : values())
if (kt.isMyType((key)))
return kt;
return UNKNOWN;
if (kt.isMyType((key)) && (result == UNKNOWN || kt.isSubType(result)))
result = kt;
return result;
}
private boolean isSubType(KeyType keyType) {
for (KeyType node = this; node != null; node = node.getParent()) {
if (keyType == node) {
return true;
}
}
return false;
}
public KeyType getParent() {
return null;
}
public static KeyType fromString(String sType) {
@@ -373,8 +396,18 @@ public enum KeyType {
}
}
private static long epochFromDate(Date date) {
return date.getTime() / 1000;
private static BigInteger epochFromDate(Date date) {
long time = date.getTime() / 1000;
if (time >= Long.MAX_VALUE / 1000) {
// Dealing with the signed longs in Java. Since the protocol requires a unix timestamp in milliseconds,
// and since Java can store numbers not bigger than 2^63-1 as `long`, we can't distinguish dates
// after `new Date(Long.MAX_VALUE / 1000)`. It's unlikely that someone uses certificate valid until
// the 10 January of 294247 year. Supposing that such dates are unlimited.
// OpenSSH expects to see 0xFF_FF_FF_FF_FF_FF_FF_FF in such cases.
return Buffer.MAX_UINT64_VALUE;
} else {
return BigInteger.valueOf(time);
}
}
private static String unpackString(byte[] packedString) throws BufferException {

View File

@@ -25,6 +25,7 @@ public enum Message {
DEBUG(4),
SERVICE_REQUEST(5),
SERVICE_ACCEPT(6),
EXT_INFO(7),
KEXINIT(20),
NEWKEYS(21),

View File

@@ -23,6 +23,7 @@ import java.io.IOException;
* Most exceptions in the {@code net.schmizz.sshj} package are instances of this class. An {@link SSHException} is
* itself an {@link IOException} and can be caught like that if this level of granularity is not desired.
*/
@SuppressWarnings("serial")
public class SSHException
extends IOException {

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj.common;
/** Represents unrecoverable exceptions in the {@code net.schmizz.sshj} package. */
@SuppressWarnings("serial")
public class SSHRuntimeException
extends RuntimeException {

View File

@@ -36,6 +36,8 @@ import javax.crypto.NoSuchPaddingException;
import static java.lang.String.format;
import java.lang.reflect.InvocationTargetException;
/**
* Static utility method relating to security facilities.
*/
@@ -53,8 +55,8 @@ public class SecurityUtils {
public static final String SPONGY_CASTLE = "SC";
/*
* Security provider identifier. null = default JCE
*/
* Security provider identifier. null = default JCE
*/
private static String securityProvider = null;
// relate to BC registration (or SpongyCastle on Android)
@@ -65,13 +67,17 @@ public class SecurityUtils {
Provider provider = null;
try {
Class<?> name = Class.forName(providerClassName);
provider = (Provider) name.newInstance();
provider = (Provider) name.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
LOG.info("Security Provider class '{}' not found", providerClassName);
} catch (InstantiationException e) {
LOG.info("Security Provider class '{}' could not be created", providerClassName);
} catch (IllegalAccessException e) {
LOG.info("Security Provider class '{}' could not be accessed", providerClassName);
} catch (InvocationTargetException e) {
LOG.info("Security Provider class '{}' could not be created", providerClassName);
} catch (NoSuchMethodException e) {
LOG.info("Security Provider class '{}' does not have a no-args constructor", providerClassName);
}
if (provider == null) {
@@ -243,7 +249,14 @@ public class SecurityUtils {
*/
public static synchronized boolean isBouncyCastleRegistered() {
register();
return BOUNCY_CASTLE.equals(securityProvider) || SPONGY_CASTLE.equals(securityProvider);
Provider[] providers = Security.getProviders();
for (Provider provider : providers) {
String name = provider.getName();
if (BOUNCY_CASTLE.equals(name) || SPONGY_CASTLE.equals(name)) {
return true;
}
}
return false;
}
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {

View File

@@ -20,7 +20,7 @@ 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 static final int LOCALPORT = 65535;
public DirectConnection(Connection conn, String remoteHost, int remotePort) {
super(conn, new Parameters(LOCALHOST, LOCALPORT, remoteHost, remotePort));

View File

@@ -15,6 +15,8 @@
*/
package net.schmizz.sshj.connection.channel.direct;
import java.util.Objects;
public class Parameters {
private final String localHost;
@@ -45,4 +47,24 @@ public class Parameters {
return localPort;
}
@Override
public int hashCode() {
return Objects.hash(localHost, localPort, remoteHost, remotePort);
}
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (!(obj instanceof Parameters)) { return false; }
Parameters other = (Parameters) obj;
return Objects.equals(localHost, other.localHost) && localPort == other.localPort &&
Objects.equals(remoteHost, other.remoteHost) && remotePort == other.remotePort;
}
@Override
public String toString() {
return "Parameters [localHost=" + localHost + ", localPort=" + localPort + ", "+
"remoteHost=" + remoteHost + ", remotePort=" + remotePort + "]";
}
}

View File

@@ -42,6 +42,7 @@ public class PacketReader extends Thread {
log = engine.getLoggerFactory().getLogger(getClass());
this.in = engine.getSubsystem().getInputStream();
setName("sftp reader");
setDaemon(true);
}
private void readIntoBuffer(byte[] buf, int off, int len)

View File

@@ -108,6 +108,14 @@ public class SFTPEngine
return operativeVersion;
}
public boolean supportsServerExtension(final String extension, final String domain) {
return serverExtensions.containsKey(extension + "@" + domain);
}
public String getServerExtensionData(final String extension, final String domain) {
return serverExtensions.get(extension + "@" + domain);
}
public Request newExtendedRequest(String reqName) {
return newRequest(PacketType.EXTENDED).putString(reqName);
}

View File

@@ -29,18 +29,26 @@ public abstract class AbstractSignature
@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
protected final java.security.Signature signature;
private final String signatureName;
protected AbstractSignature(String algorithm) {
protected AbstractSignature(String algorithm, String signatureName) {
try {
this.signature = SecurityUtils.getSignature(algorithm);
this.signatureName = signatureName;
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
}
}
protected AbstractSignature(@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
java.security.Signature signatureEngine) {
java.security.Signature signatureEngine, String signatureName) {
this.signature = signatureEngine;
this.signatureName = signatureName;
}
@Override
public String getSignatureName() {
return signatureName;
}
@Override

View File

@@ -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. */
public interface Signature {
String getSignatureName();
/**
* Initialize this signature with the given public key for signature verification.
*

View File

@@ -15,15 +15,20 @@
*/
package net.schmizz.sshj.signature;
import com.hierynomus.asn1.encodingrules.der.DEREncoder;
import com.hierynomus.asn1.types.ASN1Object;
import com.hierynomus.asn1.types.constructed.ASN1Sequence;
import com.hierynomus.asn1.types.primitive.ASN1Integer;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import org.bouncycastle.asn1.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* DSA {@link Signature}
@@ -50,7 +55,7 @@ public class SignatureDSA
}
public SignatureDSA() {
super("SHA1withDSA");
super("SHA1withDSA", KeyType.DSA.toString());
}
@Override
@@ -72,8 +77,8 @@ public class SignatureDSA
// 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 s_copylen = (s.length < 20) ? s.length : 20;
int r_copylen = Math.min(r.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(s, s.length - s_copylen, result, 40 - s_copylen, s_copylen);
@@ -97,18 +102,19 @@ public class SignatureDSA
* Encodes the signature as a DER sequence (ASN.1 format).
*/
private byte[] asnEncode(byte[] sigBlob) throws IOException {
byte[] r = new BigInteger(1, Arrays.copyOfRange(sigBlob, 0, 20)).toByteArray();
byte[] s = new BigInteger(1, Arrays.copyOfRange(sigBlob, 20, 40)).toByteArray();
BigInteger r = new BigInteger(1, Arrays.copyOfRange(sigBlob, 0, 20));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(sigBlob, 20, 40));
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(new ASN1Integer(r));
List<ASN1Object> vector = new ArrayList<ASN1Object>();
vector.add(new com.hierynomus.asn1.types.primitive.ASN1Integer(r));
vector.add(new ASN1Integer(s));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
com.hierynomus.asn1.ASN1OutputStream asn1OutputStream = new com.hierynomus.asn1.ASN1OutputStream(new DEREncoder(), baos);
asn1OutputStream.writeObject(new ASN1Sequence(vector));
asn1OutputStream.flush();
asnOS.writeObject(new DERSequence(vector));
asnOS.flush();
return baos.toByteArray();
}

View File

@@ -15,18 +15,23 @@
*/
package net.schmizz.sshj.signature;
import com.hierynomus.asn1.encodingrules.der.DERDecoder;
import com.hierynomus.asn1.encodingrules.der.DEREncoder;
import com.hierynomus.asn1.types.ASN1Object;
import com.hierynomus.asn1.types.constructed.ASN1Sequence;
import com.hierynomus.asn1.types.primitive.ASN1Integer;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.DERSequence;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.List;
/** ECDSA {@link Signature} */
public class SignatureECDSA extends AbstractSignature {
@@ -79,30 +84,26 @@ public class SignatureECDSA extends AbstractSignature {
private String keyTypeName;
public SignatureECDSA(String algorithm, String keyTypeName) {
super(algorithm);
super(algorithm, keyTypeName);
this.keyTypeName = keyTypeName;
}
@Override
public byte[] encode(byte[] sig) {
int rIndex = 3;
int rLen = sig[rIndex++] & 0xff;
byte[] r = new byte[rLen];
System.arraycopy(sig, rIndex, r, 0, r.length);
ByteArrayInputStream bais = new ByteArrayInputStream(sig);
com.hierynomus.asn1.ASN1InputStream asn1InputStream = new com.hierynomus.asn1.ASN1InputStream(new DERDecoder(), bais);
try {
ASN1Sequence sequence = asn1InputStream.readObject();
ASN1Integer r = (ASN1Integer) sequence.get(0);
ASN1Integer s = (ASN1Integer) sequence.get(1);
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
buf.putMPInt(r.getValue());
buf.putMPInt(s.getValue());
int sIndex = rIndex + rLen + 1;
int sLen = sig[sIndex++] & 0xff;
byte[] s = new byte[sLen];
System.arraycopy(sig, sIndex, s, 0, s.length);
System.arraycopy(sig, 4, r, 0, rLen);
System.arraycopy(sig, 6 + rLen, s, 0, sLen);
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
buf.putMPInt(new BigInteger(r));
buf.putMPInt(new BigInteger(s));
return buf.getCompactData();
return buf.getCompactData();
} finally {
IOUtils.closeQuietly(asn1InputStream, bais);
}
}
@Override
@@ -122,19 +123,22 @@ public class SignatureECDSA extends AbstractSignature {
*/
private byte[] asnEncode(byte[] sigBlob) throws IOException {
Buffer.PlainBuffer sigbuf = new Buffer.PlainBuffer(sigBlob);
byte[] r = sigbuf.readBytes();
byte[] s = sigbuf.readBytes();
BigInteger r = sigbuf.readMPInt();
BigInteger s = sigbuf.readMPInt();
ASN1EncodableVector vector = new ASN1EncodableVector();
List<ASN1Object> vector = new ArrayList<ASN1Object>();
vector.add(new ASN1Integer(r));
vector.add(new ASN1Integer(s));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
asnOS.writeObject(new DERSequence(vector));
asnOS.flush();
com.hierynomus.asn1.ASN1OutputStream asn1OutputStream = new com.hierynomus.asn1.ASN1OutputStream(new DEREncoder(), baos);
try {
asn1OutputStream.writeObject(new ASN1Sequence(vector));
asn1OutputStream.flush();
} finally {
IOUtils.closeQuietly(asn1OutputStream);
}
return baos.toByteArray();
}
}

View File

@@ -22,26 +22,53 @@ import net.schmizz.sshj.common.SSHRuntimeException;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.Date;
/** RSA {@link Signature} */
public class SignatureRSA
extends AbstractSignature {
/** A named factory for RSA {@link Signature} */
public static class Factory
public static class FactorySSHRSA
implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureRSA(KeyType.RSA.toString());
return new SignatureRSA("SHA1withRSA", KeyType.RSA, KeyType.RSA.toString());
}
@Override
public String getName() {
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} */
@@ -50,7 +77,7 @@ public class SignatureRSA
@Override
public Signature create() {
return new SignatureRSA(KeyType.RSA_CERT.toString());
return new SignatureRSA("SHA1withRSA", KeyType.RSA_CERT, KeyType.RSA.toString());
}
@Override
@@ -60,19 +87,19 @@ public class SignatureRSA
}
private String keyTypeName;
private KeyType keyType;
public SignatureRSA(String keyTypeName) {
super("SHA1withRSA");
this.keyTypeName = keyTypeName;
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.keyTypeName.equals(KeyType.RSA_CERT.toString()) && publicKey instanceof Certificate) {
if (this.keyType.equals(KeyType.RSA_CERT) && publicKey instanceof Certificate) {
signature.initVerify(((Certificate<PublicKey>) publicKey).getKey());
} else {
signature.initVerify(publicKey);
@@ -89,7 +116,7 @@ public class SignatureRSA
@Override
public boolean verify(byte[] sig) {
sig = extractSig(sig, KeyType.RSA.toString());
sig = extractSig(sig, getSignatureName());
try {
return signature.verify(sig);
} catch (SignatureException e) {

View File

@@ -45,6 +45,7 @@ abstract class Converter {
protected long seq = -1;
protected boolean authed;
protected boolean etm;
protected boolean authMode;
long getSequenceNumber() {
return seq;
@@ -57,7 +58,11 @@ abstract class Converter {
if (compression != null)
compression.init(getCompressionType());
this.cipherSize = cipher.getIVSize();
this.etm = mac.isEtm();
this.etm = this.mac != null && mac.isEtm();
if(cipher.getAuthenticationTagSize() > 0) {
this.cipherSize = cipher.getAuthenticationTagSize();
this.authMode = true;
}
}
void setAuthenticated() {

View File

@@ -70,87 +70,41 @@ final class Decoder
*
* @return number of bytes needed before further decoding possible
*/
private int decode()
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 {
private int decode() throws SSHException {
int need;
/* Decoding loop */
for (; ; )
for(;;) {
if (packetLength == -1) { // Waiting for beginning of packet
assert inputBuffer.rpos() == 0 : "buffer cleared";
need = cipherSize - inputBuffer.available();
if (need <= 0) {
packetLength = decryptLength();
if (authMode) {
packetLength = decryptLengthAAD();
} else if (etm) {
packetLength = inputBuffer.readUInt32AsInt();
checkPacketLength(packetLength);
} else {
packetLength = decryptLength();
}
} else {
// Need more data
break;
}
} else {
assert inputBuffer.rpos() == 4 : "packet length read";
need = packetLength + (mac != null ? mac.getBlockSize() : 0) - inputBuffer.available();
need = (authMode) ? packetLength + cipherSize - inputBuffer.available() : packetLength + (mac != null ? mac.getBlockSize() : 0) - inputBuffer.available();
if (need <= 0) {
decryptBuffer(cipherSize, packetLength + 4 - cipherSize); // Decrypt the rest of the payload
seq = seq + 1 & 0xffffffffL;
if (mac != null) {
if (authMode) {
cipher.update(inputBuffer.array(), 4, packetLength);
} else if (etm) {
checkMAC(inputBuffer.array());
decryptBuffer(4, packetLength);
} else {
decryptBuffer(cipherSize, packetLength + 4 - cipherSize); // Decrypt the rest of the payload
if (mac != null) {
checkMAC(inputBuffer.array());
}
}
// Exclude the padding & MAC
inputBuffer.wpos(packetLength + 4 - inputBuffer.readByte());
final SSHPacket plain = usingCompression() ? decompressed() : inputBuffer;
if (log.isTraceEnabled()) {
@@ -160,16 +114,20 @@ final class Decoder
inputBuffer.clear();
packetLength = -1;
} else {
// Need more data
// Needs more data
break;
}
}
}
return need;
}
private void checkMAC(final byte[] data)
throws TransportException {
if (mac == null) {
return;
}
mac.update(seq); // seq num
mac.update(data, 0, packetLength + 4); // packetLength+4 = entire packet w/o mac
mac.doFinal(macResult, 0); // compute
@@ -186,6 +144,20 @@ final class Decoder
return uncompressBuffer;
}
private int decryptLengthAAD() throws TransportException {
cipher.updateAAD(inputBuffer.array(), 0, 4);
final int len;
try {
len = inputBuffer.readUInt32AsInt();
} catch (Buffer.BufferException be) {
throw new TransportException(be);
}
checkPacketLength(len);
return len;
}
private int decryptLength()
throws TransportException {
decryptBuffer(0, cipherSize);
@@ -237,7 +209,9 @@ final class Decoder
@Override
void setAlgorithms(Cipher cipher, MAC mac, Compression compression) {
super.setAlgorithms(cipher, mac, compression);
macResult = new byte[mac.getBlockSize()];
if (mac != null) {
macResult = new byte[mac.getBlockSize()];
}
}
@Override

View File

@@ -15,6 +15,8 @@
*/
package net.schmizz.sshj.transport;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ByteArrayUtils;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.cipher.Cipher;
@@ -72,7 +74,7 @@ final class Encoder
}
final int payloadSize = buffer.available();
int lengthWithoutPadding;
final 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
@@ -82,16 +84,42 @@ final class Encoder
}
// Compute padding length
int padLen = cipherSize - (lengthWithoutPadding % cipherSize);
if (padLen < 4) {
padLen += cipherSize;
// random padding - Arbitrary-length padding, such that the total length of
// (packet_length || padding_length || payload || random padding)
// is a multiple of the cipher block size or 8, whichever is
// larger. There MUST be at least four bytes of padding. The
// padding SHOULD consist of random bytes. The maximum amount of
// padding is 255 bytes.
final int mod = cipherSize > 8 ? cipherSize : 8;
// (lengthWithoutPadding + padLen) % mod == 0 where 4 <= padLen < 256
int padLen = mod - (lengthWithoutPadding % mod);
if (padLen < 4 || (authMode && padLen < mod)) {
padLen += mod;
}
final int startOfPacket = buffer.rpos() - 5;
int packetLen = 1 + payloadSize + padLen; // packetLength = padLen (1 byte) + payload + padding
if (packetLen < 16) {
padLen += cipherSize;
// The minimum size of a packet is 16 (or the cipher block size,
// whichever is larger) bytes (plus 'mac'). Implementations SHOULD
// decrypt the length after receiving the first 8 (or cipher block size,
// whichever is larger) bytes of a packet.
// if (packetLen < 16) {
// padLen += cipherSize;
// packetLen = 1 + payloadSize + padLen;
// }
while (packetLen < Math.max(16, cipherSize)) {
padLen += mod; // Increment with the mod so that multiple holds
packetLen = 1 + payloadSize + padLen;
}
/*
* In AES-GCM ciphers, they require packets must be a multiple of 16 bytes (which is also block size of AES)
* as mentioned in RFC5647 Section 7.2. So we are calculating the extra padding as necessary here
*/
if (authMode && packetLen % cipherSize != 0) {
padLen += cipherSize - (packetLen % cipherSize);
packetLen = 1 + payloadSize + padLen;
}
@@ -101,6 +129,7 @@ final class Encoder
buffer.wpos(startOfPacket);
buffer.putUInt32(packetLen);
buffer.putByte((byte) padLen);
// Now wpos will mark end of padding
buffer.wpos(endOfPadding);
@@ -109,7 +138,11 @@ final class Encoder
seq = seq + 1 & 0xffffffffL;
if (etm) {
if (authMode) {
int wpos = buffer.wpos();
buffer.wpos(wpos + cipherSize);
aeadOutgoingBuffer(buffer, startOfPacket, packetLen);
} else if (etm) {
cipher.update(buffer.array(), startOfPacket + 4, packetLen);
putMAC(buffer, startOfPacket, endOfPadding);
} else {
@@ -117,16 +150,29 @@ final class Encoder
putMAC(buffer, startOfPacket, endOfPadding);
}
if (log.isTraceEnabled()) {
log.trace("Encrypting packet #{}: {}", seq, ByteArrayUtils.printHex(buffer.array(), startOfPacket, 4 + packetLen));
}
cipher.update(buffer.array(), startOfPacket, 4 + packetLen);
}
buffer.rpos(startOfPacket); // Make ready-to-read
return seq;
} finally {
encodeLock.unlock();
}
}
protected void aeadOutgoingBuffer(Buffer<?> buf, int offset, int len) {
if (cipher == null || cipher.getAuthenticationTagSize() == 0) {
throw new IllegalArgumentException("AEAD mode requires an AEAD cipher");
}
byte[] data = buf.array();
cipher.updateWithAAD(data, offset, 4, len);
}
@Override
void setAlgorithms(Cipher cipher, MAC mac, Compression compression) {
encodeLock.lock();

View File

@@ -30,9 +30,7 @@ import org.slf4j.Logger;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -232,6 +230,10 @@ final class KeyExchanger
}
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
negotiatedAlgs.getKeyExchangeAlgorithm());
transport.setHostKeyAlgorithm(Factory.Named.Util.create(transport.getConfig().getKeyAlgorithms(),
negotiatedAlgs.getSignatureAlgorithm()));
transport.setRSASHA2Support(negotiatedAlgs.getRSASHA2Support());
try {
kex.init(transport,
transport.getServerID(), transport.getClientID(),
@@ -320,13 +322,25 @@ final class KeyExchanger
resizedKey(encryptionKey_S2C, cipher_S2C.getBlockSize(), hash, kex.getK(), kex.getH()),
initialIV_S2C);
final MAC mac_C2S = Factory.Named.Util.create(transport.getConfig().getMACFactories(), negotiatedAlgs
.getClient2ServerMACAlgorithm());
mac_C2S.init(resizedKey(integrityKey_C2S, mac_C2S.getBlockSize(), hash, kex.getK(), kex.getH()));
/*
* For AES-GCM ciphers, MAC will also be AES-GCM, so it is handled by the cipher itself.
* In that case, both s2c and c2s MACs are ignored.
*
* Refer to RFC5647 Section 5.1
*/
MAC mac_C2S = null;
if(cipher_C2S.getAuthenticationTagSize() == 0) {
mac_C2S = Factory.Named.Util.create(transport.getConfig().getMACFactories(), negotiatedAlgs
.getClient2ServerMACAlgorithm());
mac_C2S.init(resizedKey(integrityKey_C2S, mac_C2S.getBlockSize(), hash, kex.getK(), kex.getH()));
}
final MAC mac_S2C = Factory.Named.Util.create(transport.getConfig().getMACFactories(),
negotiatedAlgs.getServer2ClientMACAlgorithm());
mac_S2C.init(resizedKey(integrityKey_S2C, mac_S2C.getBlockSize(), hash, kex.getK(), kex.getH()));
MAC mac_S2C = null;
if(cipher_S2C.getAuthenticationTagSize() == 0) {
mac_S2C = Factory.Named.Util.create(transport.getConfig().getMACFactories(),
negotiatedAlgs.getServer2ClientMACAlgorithm());
mac_S2C.init(resizedKey(integrityKey_S2C, mac_S2C.getBlockSize(), hash, kex.getK(), kex.getH()));
}
final Compression compression_S2C =
Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),

View File

@@ -26,8 +26,10 @@ public final class NegotiatedAlgorithms {
private final String c2sComp;
private final String s2cComp;
private final boolean rsaSHA2Support;
NegotiatedAlgorithms(String kex, String sig, String c2sCipher, String s2cCipher, String c2sMAC, String s2cMAC,
String c2sComp, String s2cComp) {
String c2sComp, String s2cComp, boolean rsaSHA2Support) {
this.kex = kex;
this.sig = sig;
this.c2sCipher = c2sCipher;
@@ -36,6 +38,7 @@ public final class NegotiatedAlgorithms {
this.s2cMAC = s2cMAC;
this.c2sComp = c2sComp;
this.s2cComp = s2cComp;
this.rsaSHA2Support = rsaSHA2Support;
}
public String getKeyExchangeAlgorithm() {
@@ -70,6 +73,10 @@ public final class NegotiatedAlgorithms {
return s2cComp;
}
public boolean getRSASHA2Support() {
return rsaSHA2Support;
}
@Override
public String toString() {
return ("[ " +
@@ -80,7 +87,8 @@ public final class NegotiatedAlgorithms {
"c2sMAC=" + c2sMAC + "; " +
"s2cMAC=" + s2cMAC + "; " +
"c2sComp=" + c2sComp + "; " +
"s2cComp=" + s2cComp +
"s2cComp=" + s2cComp + "; " +
"rsaSHA2Support=" + rsaSHA2Support +
" ]");
}

View File

@@ -15,12 +15,14 @@
*/
package net.schmizz.sshj.transport;
import com.hierynomus.sshj.key.KeyAlgorithms;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -38,7 +40,7 @@ class Proposal {
public Proposal(Config config) {
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());
c2sMAC = s2cMAC = Factory.Named.Util.getNames(config.getMACFactories());
c2sComp = s2cComp = Factory.Named.Util.getNames(config.getCompressionFactories());
@@ -90,7 +92,7 @@ class Proposal {
return kex;
}
public List<String> getSignatureAlgorithms() {
public List<String> getHostKeyAlgorithms() {
return sig;
}
@@ -126,31 +128,49 @@ class Proposal {
throws TransportException {
return new NegotiatedAlgorithms(
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
firstMatch(this.getHostKeyAlgorithms(), other.getHostKeyAlgorithms()),
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()),
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()),
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms())
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms()),
other.getHostKeyAlgorithms().containsAll(KeyAlgorithms.SSH_RSA_SHA2_ALGORITHMS)
);
}
private static String firstMatch(List<String> a, List<String> b)
throws TransportException {
for (String aa : a)
for (String bb : b)
if (aa.equals(bb))
return aa;
for (String aa : a) {
if (b.contains(aa)) {
return aa;
}
}
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) {
StringBuilder sb = new StringBuilder();
int i = 0;
for (String s : sl) {
if (i++ != 0)
if (i++ != 0) {
sb.append(",");
}
sb.append(s);
}
return sb.toString();

View File

@@ -30,6 +30,7 @@ public final class Reader
this.trans = trans;
log = trans.getConfig().getLoggerFactory().getLogger(getClass());
setName("reader");
setDaemon(true);
}
@Override

View File

@@ -15,9 +15,11 @@
*/
package net.schmizz.sshj.transport;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.Service;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
@@ -235,4 +237,7 @@ public interface Transport
* @param e The exception that occurred.
*/
void die(Exception e);
KeyAlgorithm getHostKeyAlgorithm();
KeyAlgorithm getClientKeyAlgorithm(KeyType keyType) throws TransportException;
}

View File

@@ -15,6 +15,8 @@
*/
package net.schmizz.sshj.transport;
import com.hierynomus.sshj.key.KeyAlgorithm;
import com.hierynomus.sshj.key.KeyAlgorithms;
import com.hierynomus.sshj.transport.IdentificationStringParser;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
@@ -30,6 +32,7 @@ import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -39,6 +42,7 @@ import java.util.concurrent.locks.ReentrantLock;
public final class TransportImpl
implements Transport, DisconnectListener {
private static final class NullService
extends AbstractService {
@@ -86,6 +90,10 @@ public final class TransportImpl
private final Decoder decoder;
private KeyAlgorithm hostKeyAlgorithm;
private boolean rsaSHA2Support;
private final Event<TransportException> serviceAccept;
private final Event<TransportException> close;
@@ -524,6 +532,9 @@ public final class TransportImpl
case SERVICE_ACCEPT:
gotServiceAccept();
break;
case EXT_INFO:
log.debug("Received SSH_MSG_EXT_INFO");
break;
case USERAUTH_BANNER:
log.debug("Received USERAUTH_BANNER");
break;
@@ -649,4 +660,30 @@ public final class TransportImpl
return connInfo;
}
public void setHostKeyAlgorithm(KeyAlgorithm keyAlgorithm) {
this.hostKeyAlgorithm = keyAlgorithm;
}
@Override
public KeyAlgorithm getHostKeyAlgorithm() {
return this.hostKeyAlgorithm;
}
public void setRSASHA2Support(boolean rsaSHA2Support) {
this.rsaSHA2Support = rsaSHA2Support;
}
@Override
public KeyAlgorithm getClientKeyAlgorithm(KeyType keyType) throws TransportException {
if (keyType != KeyType.RSA || !rsaSHA2Support) {
return Factory.Named.Util.create(getConfig().getKeyAlgorithms(), keyType.toString());
}
List<Factory.Named<KeyAlgorithm>> factories = getConfig().getKeyAlgorithms();
if (factories != null)
for (Factory.Named<KeyAlgorithm> f : factories)
if (f.getName().equals("ssh-rsa") || KeyAlgorithms.SSH_RSA_SHA2_ALGORITHMS.contains(f.getName()))
return f.create();
throw new TransportException("Cannot find an available KeyAlgorithm for type " + keyType);
}
}

View File

@@ -42,7 +42,7 @@ public abstract class BaseCipher
private final String algorithm;
private final String transformation;
private javax.crypto.Cipher cipher;
protected javax.crypto.Cipher cipher;
public BaseCipher(int ivsize, int bsize, String algorithm, String transformation) {
this.ivsize = ivsize;
@@ -61,6 +61,11 @@ public abstract class BaseCipher
return ivsize;
}
@Override
public int getAuthenticationTagSize() {
return 0;
}
@Override
public void init(Mode mode, byte[] key, byte[] iv) {
key = BaseCipher.resize(key, bsize);
@@ -75,6 +80,7 @@ public abstract class BaseCipher
}
protected abstract void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException;
protected SecretKeySpec getKeySpec(byte[] key) {
return new SecretKeySpec(key, algorithm);
}
@@ -92,4 +98,19 @@ public abstract class BaseCipher
}
}
@Override
public void updateAAD(byte[] data, int offset, int length) {
throw new UnsupportedOperationException(getClass() + " does not support AAD operations");
}
@Override
public void updateAAD(byte[] data) {
updateAAD(data, 0, data.length);
}
@Override
public void updateWithAAD(byte[] input, int offset, int aadLen, int inputLen) {
updateAAD(input, offset, aadLen);
update(input, offset + aadLen, inputLen);
}
}

View File

@@ -29,6 +29,9 @@ public interface Cipher {
/** @return the size of the initialization vector */
int getIVSize();
/** @return Size of the authentication tag (AT) in bytes or 0 if this cipher does not support authentication */
int getAuthenticationTagSize();
/**
* Initialize the cipher for encryption or decryption with the given private key and initialization vector
*
@@ -47,4 +50,32 @@ public interface Cipher {
*/
void update(byte[] input, int inputOffset, int inputLen);
/**
* Adds the provided input data as additional authenticated data during encryption or decryption.
*
* @param data The additional data to authenticate
* @param offset The offset of the additional data in the buffer
* @param length The number of bytes in the buffer to use for authentication
*/
void updateAAD(byte[] data, int offset, int length);
/**
* Adds the provided input data as additional authenticated data during encryption or decryption.
*
* @param data The data to authenticate
*/
void updateAAD(byte[] data);
/**
* Performs in-place authenticated encryption or decryption with additional data (AEAD). Authentication tags are
* implicitly appended after the output ciphertext or implicitly verified after the input ciphertext. Header data
* indicated by the {@code aadLen} parameter are authenticated but not encrypted/decrypted, while payload data
* indicated by the {@code inputLen} parameter are authenticated and encrypted/decrypted.
*
* @param input The input/output bytes
* @param offset The offset of the data in the input buffer
* @param aadLen The number of bytes to use as additional authenticated data - starting at offset
* @param inputLen The number of bytes to update - starting at offset + aadLen
*/
void updateWithAAD(byte[] input, int offset, int aadLen, int inputLen);
}

View File

@@ -44,6 +44,11 @@ public class NoneCipher
return 8;
}
@Override
public int getAuthenticationTagSize() {
return 0;
}
@Override
public void init(Mode mode, byte[] bytes, byte[] bytes1) {
// Nothing to do
@@ -54,4 +59,18 @@ public class NoneCipher
// Nothing to do
}
@Override
public void updateAAD(byte[] data, int offset, int length) {
}
@Override
public void updateAAD(byte[] data) {
}
@Override
public void updateWithAAD(byte[] input, int offset, int aadLen, int inputLen) {
}
}

View File

@@ -29,8 +29,7 @@ import java.security.GeneralSecurityException;
* Base class for DHG key exchange algorithms. Implementations will only have to configure the required data on the
* {@link DH} class in the
*/
public abstract class AbstractDHG extends AbstractDH
implements KeyExchange {
public abstract class AbstractDHG extends AbstractDH {
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -78,8 +77,8 @@ public abstract class AbstractDHG extends AbstractDH
digest.update(buf.array(), buf.rpos(), buf.available());
H = digest.digest();
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
KeyType.fromKey(hostKey).toString());
Signature signature = trans.getHostKeyAlgorithm().newSignature();
signature.initVerify(hostKey);
signature.update(H, 0, H.length);
if (!signature.verify(sig))

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.kex;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.Transport;
@@ -30,9 +31,9 @@ import java.security.GeneralSecurityException;
public abstract class AbstractDHGex extends AbstractDH {
private final Logger log = LoggerFactory.getLogger(getClass());
private int minBits = 1024;
private int maxBits = 8192;
private int preferredBits = 2048;
private final int minBits = 1024;
private final int maxBits = 8192;
private final int preferredBits = 2048;
public AbstractDHGex(Digest digest) {
super(new DH(), digest);
@@ -56,11 +57,12 @@ public abstract class AbstractDHGex extends AbstractDH {
return parseGexGroup(buffer);
case KEX_DH_GEX_REPLY:
return parseGexReply(buffer);
default:
throw new TransportException("Unexpected message " + msg);
}
} catch (Buffer.BufferException be) {
throw new TransportException(be);
}
throw new TransportException("Unexpected message " + msg);
}
private boolean parseGexReply(SSHPacket buffer) throws Buffer.BufferException, GeneralSecurityException, TransportException {
@@ -84,8 +86,8 @@ public abstract class AbstractDHGex extends AbstractDH {
.putMPInt(k);
digest.update(buf.array(), buf.rpos(), buf.available());
H = digest.digest();
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
KeyType.fromKey(hostKey).toString());
KeyAlgorithm keyAlgorithm = trans.getHostKeyAlgorithm();
Signature signature = keyAlgorithm.newSignature();
signature.initVerify(hostKey);
signature.update(H, 0, H.length);
if (!signature.verify(sig))

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.kex;
import com.hierynomus.sshj.common.KeyAlgorithm;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.transport.random.Random;
import org.bouncycastle.asn1.x9.X9ECParameters;
@@ -31,7 +32,7 @@ public class Curve25519DH extends DHBase {
private byte[] secretKey;
public Curve25519DH() {
super("ECDSA", "ECDH");
super(KeyAlgorithm.ECDSA, "ECDH");
}
@Override

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.kex;
import com.hierynomus.sshj.common.KeyAlgorithm;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.random.Random;
@@ -54,7 +55,7 @@ public class ECDH extends DHBase {
@Override
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);
PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
agreement.doPhase(yourPubKey, true);

View File

@@ -19,9 +19,9 @@ import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.SHA256;
import net.schmizz.sshj.transport.digest.SHA384;
import net.schmizz.sshj.transport.digest.SHA512;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
import java.security.GeneralSecurityException;
import java.security.spec.ECGenParameterSpec;
public class ECDHNistP extends AbstractDHG {
@@ -33,7 +33,7 @@ public class ECDHNistP extends AbstractDHG {
@Override
public KeyExchange create() {
return new ECDHNistP("P-521", new SHA512());
return new ECDHNistP("secp521r1", new SHA512());
}
@Override
@@ -48,7 +48,7 @@ public class ECDHNistP extends AbstractDHG {
@Override
public KeyExchange create() {
return new ECDHNistP("P-384", new SHA384());
return new ECDHNistP("secp384r1", new SHA384());
}
@Override
@@ -63,7 +63,7 @@ public class ECDHNistP extends AbstractDHG {
@Override
public KeyExchange create() {
return new ECDHNistP("P-256", new SHA256());
return new ECDHNistP("secp256r1", new SHA256());
}
@Override
@@ -79,7 +79,7 @@ public class ECDHNistP extends AbstractDHG {
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new ECNamedCurveGenParameterSpec(curve), trans.getConfig().getRandomFactory());
dh.init(new ECGenParameterSpec(curve), trans.getConfig().getRandomFactory());
}
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.verification;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.transport.verification.KnownHostMatchers;
import net.schmizz.sshj.common.*;
import org.slf4j.Logger;
@@ -40,6 +41,11 @@ public class OpenSSHKnownHosts
protected final File khFile;
protected final List<KnownHostEntry> entries = new ArrayList<KnownHostEntry>();
public OpenSSHKnownHosts(Reader reader) throws IOException {
this(reader, LoggerFactory.DEFAULT);
}
public OpenSSHKnownHosts(File khFile)
throws IOException {
this(khFile, LoggerFactory.DEFAULT);
@@ -50,29 +56,40 @@ public class OpenSSHKnownHosts
this.khFile = khFile;
log = loggerFactory.getLogger(getClass());
if (khFile.exists()) {
final EntryFactory entryFactory = new EntryFactory();
final BufferedReader br = new BufferedReader(new FileReader(khFile));
try {
// Read in the file, storing each line as an entry
String line;
while ((line = br.readLine()) != null) {
try {
KnownHostEntry entry = entryFactory.parseEntry(line);
if (entry != null) {
entries.add(entry);
}
} catch (SSHException ignore) {
log.debug("Bad line ({}): {} ", ignore.toString(), line);
} catch (SSHRuntimeException ignore) {
log.debug("Failed to process line ({}): {} ", ignore.toString(), line);
}
}
readEntries(br);
} finally {
IOUtils.closeQuietly(br);
}
}
}
public OpenSSHKnownHosts(Reader reader, LoggerFactory loggerFactory) throws IOException {
this.khFile = null;
log = loggerFactory.getLogger(getClass());
BufferedReader br = new BufferedReader(reader);
readEntries(br);
}
private void readEntries(BufferedReader br) throws IOException {
final EntryFactory entryFactory = new EntryFactory();
String line;
while ((line = br.readLine()) != null) {
try {
KnownHostEntry entry = entryFactory.parseEntry(line);
if (entry != null) {
entries.add(entry);
}
} catch (SSHException ignore) {
log.debug("Bad line ({}): {} ", ignore.toString(), line);
} catch (SSHRuntimeException ignore) {
log.debug("Failed to process line ({}): {} ", ignore.toString(), line);
}
}
}
public File getFile() {
return khFile;
}
@@ -239,7 +256,7 @@ public class OpenSSHKnownHosts
final BigInteger e = new BigInteger(split[i++]);
final BigInteger n = new BigInteger(split[i++]);
try {
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
} catch (Exception ex) {
log.error("Error reading entry `{}`, could not create key", line, ex);
@@ -387,7 +404,7 @@ public class OpenSSHKnownHosts
line.append(" ").append(type.toString());
line.append(" ").append(getKeyString(key));
if (!comment.isEmpty()) line.append(" ").append(comment);
if (comment != null && !comment.isEmpty()) line.append(" ").append(comment);
return line.toString();
}

View File

@@ -19,6 +19,8 @@ import net.schmizz.sshj.Service;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.method.AuthMethod;
import java.util.Collection;
/** User authentication API. See RFC 4252. */
public interface UserAuth {
@@ -58,6 +60,6 @@ public interface UserAuth {
boolean hadPartialSuccess();
/** The available authentication methods. This is only defined once an unsuccessful authentication has taken place. */
Iterable<String> getAllowedMethods();
Collection<String> getAllowedMethods();
}

View File

@@ -27,6 +27,7 @@ import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.method.AuthMethod;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -99,7 +100,7 @@ public class UserAuthImpl
}
@Override
public Iterable<String> getAllowedMethods() {
public Collection<String> getAllowedMethods() {
return Collections.unmodifiableList(allowedMethods);
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.ByteArrayUtils;
@@ -98,7 +99,7 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
} else if ("DSS".equals(s)) {
type = KeyType.DSA;
} else {
throw new FormatException("Unrecognized PKCS5 key type: " + s);
throw new FormatException("Unrecognized PKCS5 key type");
}
} else {
throw new FormatException("Bad header; possibly PKCS8 format?");
@@ -108,12 +109,12 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
} else if (type != null) {
if (line.startsWith("Proc-Type: ")) {
if (!"4,ENCRYPTED".equals(line.substring(11))) {
throw new FormatException("Unrecognized Proc-Type: " + line.substring(11));
throw new FormatException("Unrecognized Proc-Type");
}
} else if (line.startsWith("DEK-Info: ")) {
int ptr = line.indexOf(",");
if (ptr == -1) {
throw new FormatException("Unrecognized DEK-Info: " + line.substring(10));
throw new FormatException("Unrecognized DEK-Info");
} else {
String algorithm = line.substring(10, ptr);
if ("DES-EDE3-CBC".equals(algorithm)) {
@@ -140,7 +141,7 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
ASN1Data asn = new ASN1Data(data = decrypt(Base64.decode(sb.toString()), cipher, iv));
switch (type) {
case RSA: {
KeyFactory factory = KeyFactory.getInstance("RSA");
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
asn.readNext();
BigInteger modulus = asn.readNext();
BigInteger pubExp = asn.readNext();
@@ -150,7 +151,7 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
return new KeyPair(pubKey, prvKey);
}
case DSA: {
KeyFactory factory = KeyFactory.getInstance("DSA");
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
asn.readNext();
BigInteger p = asn.readNext();
BigInteger q = asn.readNext();

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.userauth.password.PasswordUtils;
@@ -63,12 +64,16 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
final Object o = r.readObject();
final JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
pemConverter.setProvider(SecurityUtils.getSecurityProvider());
if (SecurityUtils.getSecurityProvider() != null) {
pemConverter.setProvider(SecurityUtils.getSecurityProvider());
}
if (o instanceof PEMEncryptedKeyPair) {
final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) o;
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
decryptorBuilder.setProvider(SecurityUtils.getSecurityProvider());
if (SecurityUtils.getSecurityProvider() != null) {
decryptorBuilder.setProvider(SecurityUtils.getSecurityProvider());
}
try {
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
kp = pemConverter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorBuilder.build(passphrase)));
@@ -85,7 +90,7 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
if (pwdf != null && pwdf.shouldRetry(resource))
continue;
else
throw e;
throw new KeyDecryptionFailedException(e);
} finally {
IOUtils.closeQuietly(r);
}

View File

@@ -15,9 +15,21 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import com.hierynomus.sshj.common.KeyAlgorithm;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.util.encoders.Hex;
import javax.crypto.Cipher;
@@ -100,21 +112,21 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
protected KeyPair readKeyPair() throws IOException {
this.parseKeyPair();
final Buffer.PlainBuffer publicKeyReader = new Buffer.PlainBuffer(publicKey);
final Buffer.PlainBuffer privateKeyReader = new Buffer.PlainBuffer(privateKey);
publicKeyReader.readBytes(); // The first part of the payload is a human-readable key format name.
if (KeyType.RSA.equals(this.getType())) {
final KeyReader publicKeyReader = new KeyReader(publicKey);
publicKeyReader.skip(); // skip this
// public key exponent
BigInteger e = publicKeyReader.readInt();
BigInteger e = publicKeyReader.readMPInt();
// modulus
BigInteger n = publicKeyReader.readInt();
BigInteger n = publicKeyReader.readMPInt();
final KeyReader privateKeyReader = new KeyReader(privateKey);
// private key exponent
BigInteger d = privateKeyReader.readInt();
BigInteger d = privateKeyReader.readMPInt();
final KeyFactory factory;
try {
factory = KeyFactory.getInstance("RSA");
factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
} catch (NoSuchAlgorithmException s) {
throw new IOException(s.getMessage(), s);
}
@@ -128,20 +140,17 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
}
}
if (KeyType.DSA.equals(this.getType())) {
final KeyReader publicKeyReader = new KeyReader(publicKey);
publicKeyReader.skip(); // skip this
BigInteger p = publicKeyReader.readInt();
BigInteger q = publicKeyReader.readInt();
BigInteger g = publicKeyReader.readInt();
BigInteger y = publicKeyReader.readInt();
BigInteger p = publicKeyReader.readMPInt();
BigInteger q = publicKeyReader.readMPInt();
BigInteger g = publicKeyReader.readMPInt();
BigInteger y = publicKeyReader.readMPInt();
final KeyReader privateKeyReader = new KeyReader(privateKey);
// Private exponent from the private key
BigInteger x = privateKeyReader.readInt();
BigInteger x = privateKeyReader.readMPInt();
final KeyFactory factory;
try {
factory = KeyFactory.getInstance("DSA");
factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
} catch (NoSuchAlgorithmException s) {
throw new IOException(s.getMessage(), s);
}
@@ -153,9 +162,42 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
} catch (InvalidKeySpecException e) {
throw new IOException(e.getMessage(), e);
}
} else {
throw new IOException(String.format("Unknown key type %s", this.getType()));
}
if (KeyType.ED25519.equals(this.getType())) {
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(publicKeyReader.readBytes(), ed25519);
EdDSAPrivateKeySpec privateSpec = new EdDSAPrivateKeySpec(privateKeyReader.readBytes(), ed25519);
return new KeyPair(new EdDSAPublicKey(publicSpec), new EdDSAPrivateKey(privateSpec));
}
final String ecdsaCurve;
switch (this.getType()) {
case ECDSA256:
ecdsaCurve = "P-256";
break;
case ECDSA384:
ecdsaCurve = "P-384";
break;
case ECDSA521:
ecdsaCurve = "P-521";
break;
default:
ecdsaCurve = null;
break;
}
if (ecdsaCurve != null) {
BigInteger s = new BigInteger(1, privateKeyReader.readBytes());
X9ECParameters ecParams = NISTNamedCurves.getByName(ecdsaCurve);
ECNamedCurveSpec ecCurveSpec =
new ECNamedCurveSpec(ecdsaCurve, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec);
try {
PrivateKey privateKey = SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks);
return new KeyPair(getType().readPubKeyFromBuffer(publicKeyReader), privateKey);
} catch (GeneralSecurityException e) {
throw new IOException(e.getMessage(), e);
}
}
throw new IOException(String.format("Unknown key type %s", this.getType()));
}
protected void parseKeyPair() throws IOException {
@@ -296,43 +338,4 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
throw new IOException(e.getMessage(), e);
}
}
/**
* Parses the putty key bit vector, which is an encoded sequence
* of {@link java.math.BigInteger}s.
*/
private final static class KeyReader {
private final DataInput di;
public KeyReader(byte[] key) {
this.di = new DataInputStream(new ByteArrayInputStream(key));
}
/**
* Skips an integer without reading it.
*/
public void skip() throws IOException {
final int read = di.readInt();
if (read != di.skipBytes(read)) {
throw new IOException(String.format("Failed to skip %d bytes", read));
}
}
private byte[] read() throws IOException {
int len = di.readInt();
if (len <= 0 || len > 513) {
throw new IOException(String.format("Invalid length %d", len));
}
byte[] r = new byte[len];
di.readFully(r);
return r;
}
/**
* Reads the next integer.
*/
public BigInteger readInt() throws IOException {
return new BigInteger(read());
}
}
}

View File

@@ -15,11 +15,12 @@
*/
package net.schmizz.sshj.userauth.method;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.UserAuthException;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
@@ -47,9 +48,15 @@ public abstract class KeyedAuthMethod
}
// public key as 2 strings: [ key type | key blob ]
reqBuf.putString(KeyType.fromKey(key).toString())
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
return reqBuf;
KeyType keyType = KeyType.fromKey(key);
try {
KeyAlgorithm ka = params.getTransport().getClientKeyAlgorithm(keyType);
reqBuf.putString(ka.getKeyAlgorithm())
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
return reqBuf;
} catch (IOException ioe) {
throw new UserAuthException("No KeyAlgorithm configured for key " + keyType);
}
}
protected SSHPacket putSig(SSHPacket reqBuf)
@@ -61,17 +68,20 @@ public abstract class KeyedAuthMethod
throw new UserAuthException("Problem getting private key from " + kProv, ioe);
}
final String kt = KeyType.fromKey(key).toString();
Signature signature = Factory.Named.Util.create(params.getTransport().getConfig().getSignatureFactories(), kt);
if (signature == null)
throw new UserAuthException("Could not create signature instance for " + kt + " key");
final KeyType kt = KeyType.fromKey(key);
Signature signature;
try {
signature = params.getTransport().getClientKeyAlgorithm(kt).newSignature();
} catch (TransportException e) {
throw new UserAuthException("No KeyAlgorithm configured for key " + kt);
}
signature.initSign(key);
signature.update(new Buffer.PlainBuffer()
.putString(params.getTransport().getSessionID())
.putBuffer(reqBuf) // & rest of the data for sig
.getCompactData());
reqBuf.putSignature(kt, signature.encode(signature.sign()));
reqBuf.putSignature(signature.getSignatureName(), signature.encode(signature.sign()));
return reqBuf;
}

View File

@@ -43,11 +43,15 @@ public class SCPUploadClient extends AbstractSCPClient {
return copy(sourceFile, remotePath, ScpCommandLine.EscapeMode.SingleQuote);
}
public synchronized int copy(LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
public synchronized int copy (LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode) throws IOException {
return copy(sourceFile, remotePath, escapeMode, true);
}
public synchronized int copy(LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode, boolean preserveTimes)
throws IOException {
engine.cleanSlate();
try {
startCopy(sourceFile, remotePath, escapeMode);
startCopy(sourceFile, remotePath, escapeMode, preserveTimes);
} finally {
engine.exit();
}
@@ -58,40 +62,44 @@ public class SCPUploadClient extends AbstractSCPClient {
this.uploadFilter = uploadFilter;
}
private void startCopy(LocalSourceFile sourceFile, String targetPath, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
private void startCopy(LocalSourceFile sourceFile, String targetPath, ScpCommandLine.EscapeMode escapeMode, boolean preserveTimes)
throws IOException {
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SINK)
.and(ScpCommandLine.Arg.RECURSIVE)
.and(ScpCommandLine.Arg.PRESERVE_TIMES, sourceFile.providesAtimeMtime())
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
.and(ScpCommandLine.Arg.RECURSIVE)
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
if (preserveTimes) {
commandLine.and(ScpCommandLine.Arg.PRESERVE_TIMES, sourceFile.providesAtimeMtime());
}
commandLine.withPath(targetPath, escapeMode);
engine.execSCPWith(commandLine);
engine.check("Start status OK");
process(engine.getTransferListener(), sourceFile);
process(engine.getTransferListener(), sourceFile, preserveTimes);
}
private void process(TransferListener listener, LocalSourceFile f)
throws IOException {
private void process(TransferListener listener, LocalSourceFile f, boolean preserveTimes)
throws IOException {
if (f.isDirectory()) {
sendDirectory(listener.directory(f.getName()), f);
sendDirectory(listener.directory(f.getName()), f, preserveTimes);
} else if (f.isFile()) {
sendFile(listener.file(f.getName(), f.getLength()), f);
sendFile(listener.file(f.getName(), f.getLength()), f, preserveTimes);
} else
throw new IOException(f + " is not a regular file or directory");
}
private void sendDirectory(TransferListener listener, LocalSourceFile f)
throws IOException {
private void sendDirectory(TransferListener listener, LocalSourceFile f, boolean preserveTimes)
throws IOException {
preserveTimeIfPossible(f);
engine.sendMessage("D0" + getPermString(f) + " 0 " + f.getName());
for (LocalSourceFile child : f.getChildren(uploadFilter))
process(listener, child);
process(listener, child, preserveTimes);
engine.sendMessage("E");
}
private void sendFile(StreamCopier.Listener listener, LocalSourceFile f)
throws IOException {
preserveTimeIfPossible(f);
private void sendFile(StreamCopier.Listener listener, LocalSourceFile f, boolean preserveTimes)
throws IOException {
if(preserveTimes) {
preserveTimeIfPossible(f);
}
final InputStream src = f.getInputStream();
try {
engine.sendMessage("C0" + getPermString(f) + " " + f.getLength() + " " + f.getName());

View File

@@ -15,6 +15,9 @@
*/
package com.hierynomus.sshj.common
import com.hierynomus.sshj.userauth.certificate.Certificate
import net.schmizz.sshj.common.Base64
import net.schmizz.sshj.common.Buffer
import net.schmizz.sshj.common.KeyType
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile
import spock.lang.Specification
@@ -30,16 +33,88 @@ class KeyTypeSpec extends Specification {
expect:
KeyType.fromKey(kf.getPublic()) == type
KeyType.fromKey(kf.getPrivate()) == type
KeyType.fromKey(kf.getPrivate()) == privateType
where:
privKey << ["""-----BEGIN EC PRIVATE KEY-----
privKey << [
"""-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIGhcvG8anyHew/xZJfozh5XIc1kmZZs6o2f0l3KFs4jgoAoGCCqGSM49
AwEHoUQDQgAEDUA1JYVD7URSoOGdwPxjea+ETD6IABMD9CWfk3NVTNkdu/Ksn7uX
cLTQhx4N16z1IgW2bRbSbsmM++UKXmeWyg==
-----END EC PRIVATE KEY-----"""]
pubKey << ["""ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBA1ANSWFQ+1EUqDhncD8Y3mvhEw+iAATA/Qln5NzVUzZHbvyrJ+7l3C00IceDdes9SIFtm0W0m7JjPvlCl5nlso= SSH Key"""]
type << [KeyType.ECDSA256]
-----END EC PRIVATE KEY-----""",
// ssh-keygen -f ca_key -N '' -t rsa -b 1024 \
// && ssh-keygen -f id_rsa_test -N '' -t rsa -b 1024 -m pem \
// && cat id_rsa_test
"""-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDNBlUYU5KebH8PBf5i58zFI7xW2CLwUrzKA3949+0wnA9JzrX1
XWiVJG3gNgQEgUZIIrPfPAMk9x6hVdBKL9gi3tRUfGTQfQZ7JN9rMFmlbLmEFHd4
RY86OHe2h7SO+xfYBieR7aL0DsbwcBZIWm0xlotHzR6d3QodMLYZh0jleQIDAQAB
AoGAdzHIRRVJN0tSbxSH+U5T8QS+mSqc3WTslvGDqXtR7SG9jaZciOKeS57bNi+R
FGFnz8ZFFnJYTaRRrXArYQYBu/Fp5EtMJQ52G04gGI5mov+4vACaJ3J98wieBMZh
miQXZUXjmCIkE+3/SxIIE1Cf2J8Epa012FZ5vcu+kfjOZZECQQDwTm2Hdp8PTpTm
BX802Z7zO/ZfW+oGK4LIqvcUqf/2NnA3zpgjAwl9FtxpSzzq4lvcNMTANk3ZkJIg
VZsn5Ar9AkEA2moMF2AvWuW7LN5aDKpyPqJJv9fxM1HOkrYWu6aYyl/r5/RaIwSp
Bl/5W1Elg5xgYbnms1wtgLIpipfAofoDLQJAG0XrbGpsFwKmJ40MKOViAt0VUzFN
WDHr//ZXYIMCx+DZz5uk7KRVmVrU3SZq3YWfQ1jB08bWAxFDZGQS3e4lyQJBANki
bkza6ZkjJFax4rIOzS7pZgob4wWTAZum/KinMeSXQc7ChM2ld2gIB705yeKylrrw
9qI/NFlqRZQr02z0QS0CQQDVnk224ebhcFlUG6AFcQW/b3O9yqKh6Qj7q9AK5CbV
8T8wEvB6yg929UbNSzqtKFHD2+oq/VQF7sym8BTCA9jp
-----END RSA PRIVATE KEY-----""",
// ssh-keygen -f ca_key -N '' -t rsa -b 1024 \
// && ssh-keygen -f id_dsa_test -N '' -t dsa -b 1024 -m pem \
// && cat id_dsa_test
"""-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQDCY3/O8Locj4ByO/tFiGi83rQNFlCeD44ZBiZgCMCh687H49OC
r1G8Z6Ga55uLp87DXhdEjq8HXgMhDXQakqF5huOyTkPibdsOlv86l3sw8NBBGGMF
OE4t6Qj2pVA2YrvUKY7UiyZpOdLVzrXAcouE/32259vDIbX6Y84F+TzZKwIVAOR3
zWOxnbR8wR9xKdcMczNlxOtBAoGAVBDn7jKB76oTzcmCnDyoBNISNNiknuhyM0xp
hfVMGMgUw72WLWE9k9LYBZMRWBLAeI3C5PuV0cJuSXOz1maY9B+NHr9z7AmMfPF0
eK3Fr7IHyr1DcZNZ3yE7UX4UPB/9blgxj/m1/Kvgvl/l9F+wDIH/uAJ+Awuv5a8w
jVAaZPcCgYB/vvvduepAMBYSHB3/J7a9GeEAhIlu69fGhiwUmrD1hTWKa4skVKDT
PcJ/j1urFglMdh/hKB3DdjT5Fs+95VJYvBBuOwhZ/XwQCQcFLlSXi/CvbaV63f8d
f26VSnEypH3G3cmPYfpVcXL63bCb0E4sNJwENM4tQGZa5YGz3CxMdgIVAJUv4z9+
2AE1NF07cGZ4Zs9euh9y
-----END DSA PRIVATE KEY-----""",
]
pubKey << [
"""ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBA1ANSWFQ+1EUqDhncD8Y3mvhEw+iAATA/Qln5NzVUzZHbvyrJ+7l3C00IceDdes9SIFtm0W0m7JjPvlCl5nlso= SSH Key""",
// ssh-keygen -s ca_key -I sshj_test -n user -V +240m id_rsa_test.pub \
// && cat id_rsa_test-cert.pub
"""ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgKXsDoXKTlSaN3N/z6OwYQwCWMAf6N5CidrRJMe+IUOwAAAADAQABAAAAgQDNBlUYU5KebH8PBf5i58zFI7xW2CLwUrzKA3949+0wnA9JzrX1XWiVJG3gNgQEgUZIIrPfPAMk9x6hVdBKL9gi3tRUfGTQfQZ7JN9rMFmlbLmEFHd4RY86OHe2h7SO+xfYBieR7aL0DsbwcBZIWm0xlotHzR6d3QodMLYZh0jleQAAAAAAAAAAAAAAAQAAAAlzc2hqX3Rlc3QAAAAIAAAABHVzZXIAAAAAXuczLAAAAABe52vUAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAN4tykUwkwobduBrqTJW7HNJP1Z6bAhzNNA1P2KY+0aO0iBhISHcaWS0vKsgQ4BYbbVfTkrc0rEuRnajl9PhhEyZgJs8oR7sGndxAlDY6UJH4wSL3j7A+2bogoSbpOfWZiPp/tJohPBvmsN6Uv89Axs0L7V338yH3UE4bX6eIQDZVjKjL7evOYz7sOdV7pUYvgzErqzVxxmF7t/yd61rNOOLm0PY4O1HpW2ZYGGtG8YIC8asFYY2EouLy8OsP2z7U8DLFokTuMsTA6ADN5tYbzBqxUCMsuUPqdGz4xnlTja7jm4FjBFa91KmpQSOWhveaQeb91dwMeomyplYH8W6tkDuXR7acncxYAjB955ws/T9qWrACWFryskUGI1oupM5QVXveO793oWoSqtDi6BOw763Z+7oekmScOOSpeRgsNvSPN+RMyDDuT0Sf4Tc/aLABow6t5UfULAwv9xqLg9QH6CS9ZSkIWNiEOkhCa1uH2srSF6e9m2XN0f5EawZ81EYBQAAAZQAAAAMcnNhLXNoYTItNTEyAAABgFrJp0zHjElwO+PV/hd5zXydySLLKI6A/UzCnCcceSY46oO94Uzn3Bc8UKdxMctlMOG1LGeiuYE/CMMGwnuo1TLWrfY1anVigPx0CrHzjE7ZT4qhCJsVmuwpU3qcZOrom+bDbco2N30/K81rrU5KlmVG6zGHNOpwRr2zRIusXGQ3/e+dOzsMh1cmO/XntGmxWXYPIHs4TUXE1d5C2hZFN1jkBkvlG6n6ZMq7Z+6V5oVc2bqETJv6zxypt1II7l5ObZJP26x75yb4VEbycHLENYSgSVBoN7pyc7QqRYbTnfdxJBKyjDmjOXAMlAzFch5BVHazw1PNS+BkKLZaDb/dlTfeKjDqIywb97zT4XJ70HUTZB3P/q6OwiTtrKOW9no5C9Wka2EkOliNwvsJyxYxNmS8SEzT/Ezk7vhxzS8C17+uBX55o0nJ1bjPw0E02mAoIu5o0sXm/J+3dW+s1TG1XzXdksUthG8EozQfr5M0MuOeONkwebtAo/JzSF73rlGBlA== sshj@example.com""",
// ssh-keygen -s ca_key -I sshj_test -n user -V +240m id_dsa_test.pub \
// && cat id_dsa_test-cert.pub
"""ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgOjflXUJk5PKmdg2F4lLYyluqOR+xNAGNoc3SzPBuUKMAAACBAMJjf87wuhyPgHI7+0WIaLzetA0WUJ4PjhkGJmAIwKHrzsfj04KvUbxnoZrnm4unzsNeF0SOrwdeAyENdBqSoXmG47JOQ+Jt2w6W/zqXezDw0EEYYwU4Ti3pCPalUDZiu9QpjtSLJmk50tXOtcByi4T/fbbn28MhtfpjzgX5PNkrAAAAFQDkd81jsZ20fMEfcSnXDHMzZcTrQQAAAIBUEOfuMoHvqhPNyYKcPKgE0hI02KSe6HIzTGmF9UwYyBTDvZYtYT2T0tgFkxFYEsB4jcLk+5XRwm5Jc7PWZpj0H40ev3PsCYx88XR4rcWvsgfKvUNxk1nfITtRfhQ8H/1uWDGP+bX8q+C+X+X0X7AMgf+4An4DC6/lrzCNUBpk9wAAAIB/vvvduepAMBYSHB3/J7a9GeEAhIlu69fGhiwUmrD1hTWKa4skVKDTPcJ/j1urFglMdh/hKB3DdjT5Fs+95VJYvBBuOwhZ/XwQCQcFLlSXi/CvbaV63f8df26VSnEypH3G3cmPYfpVcXL63bCb0E4sNJwENM4tQGZa5YGz3CxMdgAAAAAAAAAAAAAAAQAAAAlzc2hqX3Rlc3QAAAAIAAAABHVzZXIAAAAAXuc3ZAAAAABe52/0AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAN4tykUwkwobduBrqTJW7HNJP1Z6bAhzNNA1P2KY+0aO0iBhISHcaWS0vKsgQ4BYbbVfTkrc0rEuRnajl9PhhEyZgJs8oR7sGndxAlDY6UJH4wSL3j7A+2bogoSbpOfWZiPp/tJohPBvmsN6Uv89Axs0L7V338yH3UE4bX6eIQDZVjKjL7evOYz7sOdV7pUYvgzErqzVxxmF7t/yd61rNOOLm0PY4O1HpW2ZYGGtG8YIC8asFYY2EouLy8OsP2z7U8DLFokTuMsTA6ADN5tYbzBqxUCMsuUPqdGz4xnlTja7jm4FjBFa91KmpQSOWhveaQeb91dwMeomyplYH8W6tkDuXR7acncxYAjB955ws/T9qWrACWFryskUGI1oupM5QVXveO793oWoSqtDi6BOw763Z+7oekmScOOSpeRgsNvSPN+RMyDDuT0Sf4Tc/aLABow6t5UfULAwv9xqLg9QH6CS9ZSkIWNiEOkhCa1uH2srSF6e9m2XN0f5EawZ81EYBQAAAZQAAAAMcnNhLXNoYTItNTEyAAABgKlle1p8BfukO4xZAdLNfaH7iPqxvPd34tPeaK8esXlui5ZicjcsXNm92k74VHhFH4vTNmYdF8lqwoiHK6af8eja1W/yvj5lYkZe84K2XGIOZ8UefBT8w9ms1WPgdPtGbznr/uTOhgJr7LrHQDJiGv8wrsaJe3Md59zqIhFrhq/aSkmd/7lpsiPSgxtz/PyxEjquBp+d0qVpWxAqng+rofYMFIau+Ucc6J6JX8xrkDZJ7JBUrzFjNWWrkp3ZJVcxlBnqRtfkrU2t+LpFZEwGUmjmejUz5Ydc0n5GfCe29rhICwAlNStVR/Y/WgTJRWJsaza1ZkitryBozEL/vZNrVB4eQ+G8fUqhdflPzMH1MxQREt97dtZPxbyIxX8mOFYbiIVVH9Ar0h+SLapTc9u6/bw9N4lft7Rkp7yehhvlKd6u+Rls8KgGNcn9SMf4kBSCnfFro1lZLc7z1e87EIdrgoBMc07eAvviqYctXqrz69y90+x5bqEr67V0/MPqA+pM1Q== sshj@example.com""",
]
type << [
KeyType.ECDSA256,
KeyType.RSA_CERT,
KeyType.DSA_CERT,
]
privateType << [
KeyType.ECDSA256,
KeyType.RSA,
KeyType.DSA,
]
}
def "should read signed certificate (public)"() {
given:
def key = """ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg10JjXqWoxeMUyik45jr0cIZ/hFmYZQO27ilcjFHDVpgAAAADAQABAAAAgQCbFJRT+YwxvPCuHjWfEzeMs/Xkp6ay/N9J4XgSgHcyB6yeYw0KMW4Rs6OWJZcvj5ejuUonS9Kbwyf0YaylZxlfxt/K06rtHgKjwY3SSAQz4Y6woo4QI+pNP6SciJ/XymnE8+ZC6bJxwF5VtX1ivPn8o5am6LiDUk7kQoPAkBfs5/IE+w5UvO4zAAAAAQAAAE52YXVsdC1hcHByb2xlLWRiZDY5ZGExNmNiOTYwYjRhYTJkMzdlODcwZTA5OTdkN2M3ZjhhMjVlZTcyYmEwNTU0NTQ0NjBmNDA1YWU3ZjIAAAAPAAAAC2JyZWFrLWdsYXNzAAAAAF7B1hkAAAAAXsHdPwAAAAAAAAASAAAACnBlcm1pdC1wdHkAAAAAAAAAAAAAAhcAAAAHc3NoLXJzYQAAAAMBAAEAAAIBAOZSr+o8qpoAxBByB3dB0uRbaP9TMKU7W/j/RTIrVINeMJFzzkrqX6Bwwvn6+cAUjuLMz7L8H9Wqjivp48XWWPyyYbJ64bPdlPFzAzWbdCfY4Nd2akFLDTKqd5i0+iu+06suMEmvKVMQraNvjPccshrtcQYHTB0qmq6G1PjFaUxMeeSdv+U8+AqKIVMcuvnVfidUnaaVXn3ie9R616l3JwtvW/XS1SeAqrMqovvMMmZrIB9Jq9WPyV5hriYeWwFPbZUhsQsSj78IMxLdp0P318+6LUkuLuGZ9QGrQxWeOM2Y1/4PA3CHUBIRiA7+UGafkwn2y3L9XtxHXdQvcotV1zWq805YDw7frQJTXEI028+NPE0sE0aLbNM8/RibXgukiL8nP9M9ZfSlB8tUDO5Kuq5nWEUQ6hODW+RLf3qQFlCCF1mLy4HnwjY70jyLankVThIK2ZMoUqujVlBo5Fh8ptwzQ+VvJ9EUcz9rnoGtP0qIxutcTROKvUMcCKNimu7K+PP5+U6GJK4UbPqJSKGO7z85BBsNoqU50S2DP7lEhqd/9Ty+zXICu/Fjtio2uM4GfFIFChqTR2QuJ25dw9+v9yCVcqwozWeb0MOLm3i/bpxNqbici8ol1ZNBDyoI9gyi3vuxPkzerse7IplqUraINvXXQxrqR7/WNehayruAwYcNAAACDwAAAAdzc2gtcnNhAAACAMoIDDhDC2TCOEYE46tKL/lD4eODb4MxVJBWxBPcdUtDf24L7TuOoeRAoOIpKtv5JKU2du3ElUMnheyX+3IWuh4H8AuHGU4AyFc+cXvyGNAE7mppUb637cPNCOVgfUdImHtPRiP6MdILFeMAHNPpfkh8i+qqszum2QuukexF+7wieuaaOtlBNeuZ0/2oi6I2KGOzEnL54w0PKXgQY2AVWgeOQ2S7c+XFjxI0ospE0XVjut/Kn1VSK3cO3sI4W14gb9agaF/XTUegtrpHuPvW4Q3OQbbGHG9J/8/WGQB/HlexDkXh2GaNupC4+vwXb4//P1OV9bMUYPqMH6j2DAs8cSMdfh14utmmwrIWIgoKD3Htiby6XDkNs0EQdzPHeVgE6wbL0hO2oV3VRgcQ2CUxD+Z8j/S/q/mWS+PrfMkVBTPFojdrmxy0fJeZnkqhqK/N5+zkcP2RCwPiy2DQ/NOSrpJFqwxSctse7kbrwESdtT4cUIuofN1UOceRBhk2DoSJgLag9JflYeWez6rBW7GzJEtTLbObkRGWacWNZsyVHZlQFd1rhA2+6Yn42FFJQm9E6Eh+Higd2u7HmSrAVKiqZuwdGLCuUut+QhHIShBN0WBQAZTKtvv8/IQGk84jHy7UZeVrmNFM57AsSLIw3kTcebrRP+vFLYPun/KzbnTP3yrC"""
def parts = key.split("\\s+")
def keyType = KeyType.fromString(parts[0])
when:
def pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey()
then:
KeyType.fromKey(pubKey) == keyType
pubKey.getClass() == Certificate.class
((Certificate)pubKey).getSignature().length > 0
}
}

View File

@@ -30,6 +30,7 @@
*/
package net.schmizz.sshj.signature
import com.hierynomus.sshj.common.KeyAlgorithm
import spock.lang.Unroll;
import java.math.BigInteger;
@@ -47,7 +48,7 @@ import spock.lang.Specification
class SignatureDSASpec extends Specification {
def keyFactory = KeyFactory.getInstance("DSA")
def keyFactory = KeyFactory.getInstance(KeyAlgorithm.DSA)
private PublicKey createPublicKey(final byte[] y, final byte[] p, final byte[] q, final byte[] g) throws Exception {
final BigInteger publicKey = new BigInteger(y);

View File

@@ -16,17 +16,14 @@
package com.hierynomus.sshj.transport;
import com.hierynomus.sshj.test.SshFixture;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.transport.DisconnectListener;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.theories.suppliers.TestedOn;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport;
import com.hierynomus.sshj.transport.cipher.GcmCiphers;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.transport.cipher.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.BeforeClass;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import java.security.Security;
import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals;
/**
* Unit test to decrypt SSH traffic with OpenSSH and Apache Mina SSHD (master) using AES-GCM ciphers, for verifying
* cipher behaviour.
*/
@RunWith(Theories.class)
public class GcmCipherDecryptSshPacketTest {
@DataPoints
public static final String sets[][] = new String[][]{{"mina-sshd", "3"}, {"openssh", "4"}};
@BeforeClass
public static void setupBeforeClass() {
Security.addProvider(new BouncyCastleProvider());
}
@Theory
public void testDecryptPacket(String[] args) throws Exception {
ClassLoader classLoader = getClass().getClassLoader();
byte[] iv = IOUtils.readFully(classLoader.getResourceAsStream("ssh-packets/gcm/"+args[0]+"/s2c.iv.bin")).toByteArray();
byte[] key = IOUtils.readFully(classLoader.getResourceAsStream("ssh-packets/gcm/"+args[0]+"/s2c.key.bin")).toByteArray();
Cipher cipher = GcmCiphers.AES128GCM().create();
cipher.init(Cipher.Mode.Decrypt, key, iv);
for(int i=1; i<=Integer.parseInt(args[1]); i++) {
byte[] data = IOUtils.readFully(classLoader.getResourceAsStream("ssh-packets/gcm/"+args[0]+"/client.receive."+i+".bin")).toByteArray();
SSHPacket inputBuffer = new SSHPacket(data);
cipher.updateAAD(inputBuffer.array(), 0, 4);
int size = inputBuffer.readUInt32AsInt();
cipher.update(inputBuffer.array(), 4, size);
byte[] expected = IOUtils.readFully(classLoader.getResourceAsStream("ssh-packets/gcm/"+args[0]+"/client.decrypted."+i+".bin")).toByteArray();
assertArrayEquals(Arrays.copyOfRange(expected, 0, size+4),
Arrays.copyOfRange(inputBuffer.array(), 0, size+4));
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport;
import com.hierynomus.sshj.transport.cipher.GcmCiphers;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.transport.cipher.Cipher;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import javax.crypto.AEADBadTagException;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.*;
@RunWith(Theories.class)
public class GcmCipherTest {
public static final @DataPoints
GcmCiphers.Factory[] cipherFactories = { GcmCiphers.AES128GCM(), GcmCiphers.AES256GCM() };
@Theory
public void testEncryptDecrypt(GcmCiphers.Factory factory) throws Exception {
Cipher enc = factory.create();
byte[] key = new byte[enc.getBlockSize()];
byte[] iv = new byte[enc.getIVSize()];
enc.init(Cipher.Mode.Encrypt, key, iv);
byte[] aad = getClass().getName().getBytes(StandardCharsets.UTF_8);
enc.updateAAD(aad);
String plaintext = "[Secret authenticated message using AES-GCM";
byte[] ptBytes = plaintext.getBytes(StandardCharsets.UTF_8);
byte[] output = new byte[ptBytes.length + enc.getAuthenticationTagSize()];
System.arraycopy(ptBytes, 0, output, 0, ptBytes.length);
enc.update(output, 0, ptBytes.length);
Cipher dec = factory.create();
dec.init(Cipher.Mode.Decrypt, key, iv);
dec.updateAAD(aad);
byte[] input = output.clone();
dec.update(input, 0, ptBytes.length);
assertEquals(getClass().getName(), new String(aad, StandardCharsets.UTF_8));
assertEquals(plaintext, new String(input, 0, ptBytes.length, StandardCharsets.UTF_8));
byte[] corrupted = output.clone();
corrupted[corrupted.length - 1] += 1;
Cipher failingDec = factory.create();
failingDec.init(Cipher.Mode.Decrypt, key, iv);
try {
failingDec.updateAAD(aad.clone());
failingDec.update(corrupted, 0, ptBytes.length);
fail("Modified authentication tag should not validate");
} catch (SSHRuntimeException e) {
assertNotNull(e);
assertEquals(AEADBadTagException.class, e.getCause().getClass());
}
}
}

View File

@@ -19,7 +19,6 @@ import com.hierynomus.sshj.test.BaseAlgorithmTest;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
import net.schmizz.sshj.transport.kex.DHGexSHA1;
import net.schmizz.sshj.transport.kex.DHGexSHA256;
import net.schmizz.sshj.transport.kex.ECDHNistP;

View File

@@ -20,8 +20,6 @@ import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import static org.junit.Assert.fail;
public class LoadsOfConnects {
@@ -46,4 +44,4 @@ public class LoadsOfConnects {
}
}
}

Some files were not shown because too many files have changed in this diff Show More