Compare commits

...

186 Commits

Author SHA1 Message Date
Jeroen van Erp
2f7b181306 Release version: 0.27.0 2019-01-24 13:21:11 +01:00
Jeroen van Erp
20223d3614 Added release notes 2019-01-24 13:19:02 +01:00
Jeroen van Erp
cac340dd43 Add support for other keytypes to openssh-key-v1 keyfiles (#485)
* Added support for RSA to openssh-key-v1 keyfile

* Fixed exception

* Added ECDSA support to openssh-key-v1

* Added integration tests for different keytypes
2019-01-17 13:01:49 +01:00
Jeroen van Erp
00cd335f47 Moved tests to spock 2018-11-27 11:27:45 +01:00
Andremoniy
e14fb2f695 Expose the numeric code of the Response.StatusCode #473 (#474)
* Expose the numeric code of the Response.StatusCode #473

* Expose the numeric code of the Response.StatusCode #473
2018-11-27 10:22:00 +01:00
Pepijn Van Eeckhoudt
b0dee02bf9 Handle server initiated global requests (#472)
* Handle server initiated global requests

* Code layout
2018-11-26 15:16:43 +01:00
Jeroen van Erp
17c09eb471 Fixed integration test 2018-11-16 12:29:45 +01:00
Jeroen van Erp
0301d4537f Enable 'curve25519-sha256@libssh.org' in DefaultConfig (Fixes #464) 2018-11-16 11:48:15 +01:00
Jeroen van Erp
f71d34e106 Ignore bin/ directory 2018-11-16 11:13:09 +01:00
Jeroen van Erp
254f739ac1 Upgraded sshd to 2.1.0 2018-11-16 11:12:58 +01:00
Jeroen van Erp
aa201fa08c Add AES256-CBC to OpenSSHKeyV1KeyFile (Fixes #467) 2018-11-16 10:39:20 +01:00
Jeroen van Erp
8721269d0f Added EdDSA as first signature factory (Fixed #470) 2018-11-16 10:07:32 +01:00
Ben Manes
971ccf6273 Add lock timeout for remote action (fixes #466) (#468)
When the remove window size is expanded, a condition is waited on until
the remote server acknowledges and completes the action. If the server
does not respond, e.g. due to a connectivity issue, then this blocks the
client indefinitely. Instead the client waits up to the connection's
timeout (500 min default) and fails. This allows users to set a reasonable
timeout, fail their operations, and retry accordingly.
2018-11-16 09:33:48 +01:00
Andremoniy
813469646e Improving logging for KeyExchanger (#458) 2018-10-23 10:47:34 +02:00
OlivierSalasc
17c368f9c2 add Buffer capacity check for type UInt64 (#454) 2018-09-27 14:49:25 +02:00
Jeroen van Erp
4de9f8ab9f Add support for Encrypt-then-MAC MAC Algorithms (#450) 2018-08-28 13:22:31 +02:00
Jeroen van Erp
deff097170 Fix SFTPClient.mkdirs to not inadvertently prefix with '/' (#415) 2018-08-02 13:11:09 +02:00
Jeroen van Erp
7556a7f6f6 Updated license header 2018-07-25 12:59:25 +02:00
Jeroen van Erp
c5792fe4a8 Added Kex integration test 2018-07-25 10:34:52 +02:00
Jeroen van Erp
02cfeb9a6a Release version: 0.26.0 2018-07-24 14:27:54 +02:00
Jeroen van Erp
5e771382fe Updated license header 2018-07-24 14:24:28 +02:00
Jeroen van Erp
59e68f1ed7 Updated release plugin 2018-07-24 14:20:10 +02:00
Jeroen van Erp
7d07c1bb46 Refactored Macs and added hmac-ripemd1600@openssh.com 2018-07-24 14:18:02 +02:00
Jeroen van Erp
5bebe044aa Code cleanup, add { to single-line if 2018-07-12 14:55:03 +02:00
Jeroen van Erp
d2a16385da Upgraded BouncyCastle to 1.60 (Fixes #436) 2018-07-10 16:33:33 +02:00
Jeroen van Erp
adc0451b3f Cleanup OpenSSHKeyFile and add Disconnection test 2018-07-10 16:29:09 +02:00
Jeroen van Erp
df5e73f1e8 Close before interrupt LocalPortForwarder (Fixes #426) 2018-07-10 16:28:28 +02:00
Jeroen van Erp
eeeba57c73 Code formatting improvements. 2018-07-10 16:15:37 +02:00
Jeroen van Erp
db48ff85c0 Add support for encrypted ed25519 openssh-key-v1 files (Fixes #427) (#429) 2018-06-19 11:53:32 +02:00
Jeroen van Erp
49a450fb53 Fixed some codacy issues 2018-06-18 14:27:45 +02:00
Tom Caflisch
80d93ae8e7 Remove unnecessary nested try/finally (#417)
* Remove unnecessary nested try/finally

* This handles the case of your concern.

An even better solution would be to have SSHClient and Session implement Auto-Closable so then you don't have to worry about doing anything in the finally block!
2018-06-11 09:54:26 +02:00
Jeroen van Erp
42c52e4fe6 Fixed logging of Encoder to log correct sequence number 2018-04-30 09:30:28 +02:00
Jeroen van Erp
329966ecc4 Using UTF-8 encoding for PrivateKeyFileResource (Fixes #413) 2018-04-30 09:30:00 +02:00
Jeroen van Erp
10918f3201 Using forked gradle process for upload 2018-04-04 13:15:54 +02:00
Jeroen van Erp
b5f0d4c9fb Release version: 0.25.0 2018-04-04 13:05:02 +02:00
Jeroen van Erp
c10cb7f138 Fix release plugin? 2018-04-04 13:03:56 +02:00
Jeroen van Erp
81e26f4a7f Release version: 0.24.0 2018-04-04 12:00:30 +02:00
Jeroen van Erp
aa53effce8 Merge branch 'issue-358' 2018-03-22 22:50:51 +01:00
Jeroen van Erp
76e6e572b4 Merge branch 'master' into issue-358 2018-03-22 22:50:37 +01:00
Jeroen van Erp
2003a9f8c9 Add support for verifying multiple host entries (Fixes #405) 2018-03-21 23:54:25 +01:00
Jeroen van Erp
84a7677a62 Add support for hmac-ripemd-160 2018-03-05 13:00:41 +01:00
Jeroen van Erp
3bcd3530cf Renamed test to spec 2018-03-05 13:00:41 +01:00
Jeroen van Erp
a63f9ee8fd New version Base64 class 2018-03-05 13:00:41 +01:00
Jeroen van Erp
4be5a98ea3 Merge pull request #400 from maxd/public_constructor_of_host_entry
HostEntry constructor must be public
2018-02-20 23:16:41 +01:00
Maxim Dobryakov
26df2f3c23 HostEntry constructor must be public
Reasons:

1) SimpleEntry (was replaced to HostEntry) class had public constructor
2) HostEntry class can be used outside of sshj library to add entries to .known_hosts file (i.e. for implementation of interactive HostKeyVerifier)
2018-02-20 22:59:36 +03:00
Michael Prankl
39b72eed62 Android Compability, again ;-) (#392)
* Rework SecurityUtils and PKCS8KeyFile for usage with SpongyCastle.

* Specifying providerName for registration is unneccessary.

* Update AndroidConfig, fix imports.

* Workaround for Android 5.0 bug when SpongyCastle is the default JCE provider.

On Android 5.0 reading the version from the jar does throw a SecurityException due to a bug in Android (see https://issuetracker.google.com/issues/36993752). Including that Exception in the catch provides a workaround for that issue.
2018-01-30 16:22:05 +01:00
Jeroen van Erp
d55eb6d02e Fix build for windows 2018-01-24 21:34:29 +01:00
Jeroen van Erp
265e9d2916 Add extra logging in OpenSSHKnownHosts and extra test 2018-01-24 15:53:12 +01:00
Jeroen van Erp
0b6552654b Fix 'key spec not recognized' exception with ECDSA keys 2018-01-23 19:58:04 +01:00
Jeroen van Erp
dabe43dfdc Fixed headers 2017-12-28 13:18:30 +01:00
Jeroen van Erp
0f67fa2541 Added integration test for append scenario (Fixes #390) 2017-12-28 13:00:49 +01:00
Michael Prankl
54018a4a81 Update AndroidConfig (#389)
* Add EdDSA signature for AndroidConfig.

* Initialize KeyExchange- and FileKeyProviderFactories with registered "bouncyCastle" (in fact, SpongyCastle is registered).

See #308 for discussion.
2017-12-28 11:55:36 +01:00
Jeroen van Erp
ca81c2eea4 Added integration test to travis 2017-12-28 10:13:56 +01:00
Jeroen van Erp
048f84b42a Removed docker from travis yml as it is included in gradle build now 2017-12-28 10:10:46 +01:00
Jeroen van Erp
8ca6451d5d Fixed length bug in putString (Fixes #187) 2017-12-27 23:02:41 +01:00
Jeroen van Erp
5e1be8b1b0 Separated out integration tests 2017-12-27 23:01:59 +01:00
Jeroen van Erp
bc4da2ea8e Upgraded gradle to cope with java9 2017-12-27 15:02:42 +01:00
Jeroen van Erp
09fb2b9dc2 Merge pull request #385 from Igerly/ssh-with-docker-tests
Integration test(s) with OpenSSH server in Docker
2017-12-04 00:23:44 +01:00
Iger
4045d5a7ef - One more time 2017-12-03 23:10:56 +02:00
Iger
d0daa2c12f - desperation 2017-12-03 23:00:40 +02:00
Iger
64a2a4f779 - orly? 2017-12-03 22:55:18 +02:00
Iger
7cb1f8b11c - switch username back 2017-12-03 22:49:29 +02:00
Iger
73bc785ab4 - eh? 2017-12-03 22:40:41 +02:00
Iger
9d697ede12 - minor improvements 2017-12-03 22:28:02 +02:00
Iger
2b62492caf - grr, ip 2017-12-03 22:11:29 +02:00
Iger
a0f1aa7e2c - Fixed server keys
- Use sshj branding
2017-12-03 22:08:06 +02:00
Iger
0e981f7656 - try common format 2017-12-03 20:25:26 +02:00
Iger
a014567c9e - still -d 2017-12-03 20:05:26 +02:00
Iger
8454cf1a0c - double before_install 2017-12-03 19:44:05 +02:00
Iger
663f118d0f - yaml-yaml 2017-12-03 19:36:20 +02:00
Iger
47d73a9381 - account for different working dir 2017-12-03 19:31:31 +02:00
Iger
c4552d5f3d - fix ip for online testing 2017-12-03 19:18:21 +02:00
Iger
7a884d0938 - Experimenting with travis 2017-12-03 19:10:08 +02:00
Jeroen van Erp
661f63eab7 Updated builds to include CodeCov 2017-11-30 11:33:13 +01:00
Jeroen van Erp
a71a7d7d33 Fix escaping in WildcardHostMatcher (#382)
* Escape '[' and ']' in WildcardHostMatcher

* Anchoring regex to match entire string (Fixes #381)
2017-11-13 15:49:48 +01:00
Jeroen van Erp
d2e0f50d0c Updated build plugins 2017-11-09 15:22:34 +01:00
Jeroen van Erp
b41f0acd19 Using new release plugin 2017-10-16 12:38:55 +02:00
Jeroen van Erp
a1f501a027 Updated README for v0.23.0 release 2017-10-13 16:19:27 +02:00
Jeroen van Erp
fef9cfaf79 Merge pull request #369 from charlesrgould/migrate-block-ciphers
Migrate remaining block ciphers
2017-10-11 23:50:02 +02:00
Charles Gould
c67ae242f2 Migrate remaining block ciphers 2017-10-11 17:34:18 -04:00
charlesrgould
823f1e5759 Log security provider registration failures (#374) 2017-10-11 23:21:49 +02:00
paladox
f046a41750 Update net.i2p.crypto:eddsa to 0.2.0 (#372)
* Update net.i2p.crypto:eddsa to 0.2.0

* Update net.i2p.crypto.eddsa to 0.2.0

* Update net.i2p.crypto.eddsa to 0.2.0

* Update net.i2p.crypto.eddsa to 0.2.0
2017-10-11 21:47:51 +02:00
charlesrgould
c161fe26f6 Extracted ASN.1/DER encoding to method (#368) 2017-10-04 11:06:37 +02:00
Jeroen van Erp
ec46a7a489 Fix decoding signature bytes (Fixes #355, #354) (#361)
* Fix for signature verify in DSA

* Cleaned up signature verification

* Fixed import

* Ignored erroneous pmd warnings

* Updated JavaDoc
2017-09-29 13:23:21 +02:00
Jeroen van Erp
762d088388 Added support for new-style fingerprints (#365)
* Added support for new-style fingerprints

* Fixed codacy warnings
2017-09-28 14:01:04 +02:00
Jeroen van Erp
99c85672b8 Added 'out/' to gitignore 2017-09-19 17:23:26 -04:00
Jeroen van Erp
28d57840ab Organised imports 2017-09-19 17:22:55 -04:00
Charles Gould
2984291d84 Removed deprecated method 2017-09-07 23:18:46 +02:00
Charles Gould
bdbd9d7eb5 Disambiguated signature initialization 2017-09-07 23:18:46 +02:00
Jeroen van Erp
9ac55de26c Fixed Java9 build? 2017-09-07 21:54:42 +02:00
Jeroen van Erp
a9928c2882 fixed build 2017-09-05 15:58:10 +02:00
Jeroen van Erp
c6c9a3f6a8 Correctly determine KeyType for ECDSA public key (Fixes #356) 2017-09-05 15:23:47 +02:00
Jeroen van Erp
0918bc626f Improved test stability 2017-08-24 13:59:58 +02:00
Jeroen van Erp
aa7748395d Removed build of broken openJDK7 in favour of using animal-sniffer to detect java 1.6 compatibility 2017-08-24 13:18:27 +02:00
Jeroen van Erp
cf077e2a4f Removed use of DataTypeConverter as that is no longer in default JDK9 2017-08-24 11:20:35 +02:00
Jeroen van Erp
c58c7c7c60 Added gradle caching to travis config 2017-08-24 09:32:24 +02:00
Jeroen van Erp
0b548d9d13 Removed oraclejdk7 as that is no longer supported on trusty, added openjdk 2017-08-24 09:30:03 +02:00
Jeroen van Erp
eb1629f250 Updated README release notes 2017-08-24 09:11:58 +02:00
Jeroen van Erp
8856aaea61 Fixed codacy 2017-08-22 19:32:45 +02:00
Jeroen van Erp
1f6615b57a Check whether filename is a child of the current file (Fixes #341) 2017-08-22 19:32:45 +02:00
Matt Dailey
e5084ed8db Removed Builder, and fixed call to checkFormatString 2017-07-10 09:30:10 +02:00
Matt Dailey
3729119e23 Added assertions to testPromptFormat 2017-07-10 09:30:10 +02:00
Matt Dailey
aed3decf1d Upgraded Mockito, and added message and retries to ConsolePasswordFinder
* Upgraded Mockito to 2.8.47 (latest)
* Added extension to allow mocking final classes
* ConsolePasswordFinder allows custom message and number of retries
* Added builder for ConsolePasswordFinder
* Added more unit tests
2017-07-10 09:30:10 +02:00
Matt Dailey
303c03061c Add ConsolePasswordFinder to read from Console
* There was no example `PasswordFinder` to prompt
a user for their password
2017-07-10 09:30:10 +02:00
Iger
5e3a08a637 - boggle 2017-07-04 10:02:00 +02:00
Iger
d0800058e8 - Test ECDSA signature verifications 2017-07-04 10:02:00 +02:00
Iger
ad9c2d5411 - Test ECDSA fingerprints 2017-07-04 10:02:00 +02:00
Iger
ed65176b68 - Incorrect key format during write 2017-07-04 10:02:00 +02:00
Iger
28f3280a84 - license header 2017-07-04 10:02:00 +02:00
Iger
d69f722908 - Some more indentation fixes 2017-07-04 10:02:00 +02:00
Iger
1d7cb8c2c6 - Some more indentation fixes 2017-07-04 10:02:00 +02:00
Iger
6ad6242ed1 - Ident in spaces 2017-07-04 10:02:00 +02:00
Iger
3310530d42 - cleanup 2017-07-04 10:02:00 +02:00
Iger
3685f9dc36 - Formal generation of ASN.1 encoding for the ecdsa signature
- Support ecdsa-sha2-nistp521
2017-07-04 10:02:00 +02:00
Iger
f8cad120a6 - Pretty honed up implementation of -384 2017-07-04 10:02:00 +02:00
Iger
56dd4e4af4 - A separate enum members take with lots of code duplication 2017-07-04 10:02:00 +02:00
Jeroen van Erp
9f8cf1f298 Upgrade to gradle 4.0 2017-06-26 10:01:19 +02:00
Jeroen van Erp
a51270791d Remove deprecated ZLib usage 2017-06-26 09:56:37 +02:00
Jeroen van Erp
d43fc4551e Minor reformatting 2017-06-26 09:51:40 +02:00
Jan Peter Stotz
93bf6c0089 Fixed small exception logging problem 2017-06-09 11:58:24 +02:00
Jeroen van Erp
7b535a8db3 Added support for wildcard host entries in known_hosts (Fixes #331) 2017-05-22 14:43:30 +02:00
Jeroen van Erp
9d4f8fc46a Updated release notes 2017-04-25 15:32:44 +02:00
David Kocher
2b21ec6032 Fix regression from 40f956b. Invalid length parameter. (#322)
Also Fixes #306
2017-04-25 15:30:20 +02:00
Jeroen van Erp
8e15a8bd7d Updated README with release notes 2017-04-14 09:54:00 +02:00
Jeroen van Erp
531eb97767 Added log message for early identification termination 2017-04-14 09:14:52 +02:00
chqr
e36fd0fb3d Add support for authentication with DSA & RSA user certificates (#153) (#319)
* Add support for authentication with DSA & RSA user certificates (#153)

Updates:

- KeyType.java - add support for two certificate key types
    ssh-rsa-cert-v01@openssh.com
    ssh-dsa-cert-v01@openssh.com

- Buffer.java - allow uint64s that overflow Long.MAX_VALUE, otherwise
  we break on certificates with serial numbers greater Long.MAX_VALUE

- OpenSSHKeyFile, KeyProviderUtil - prefer public key files that end
  "-cert.pub" if they exist

Added new class Certificate, which represents certificate key

Reference:

https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD

* Use BigInteger for certificate serial numbers, address Codacy issues

* Address code review concerns
2017-04-14 08:49:16 +02:00
Jeroen van Erp
382321deca Updated README 2017-04-10 10:13:47 +02:00
Jeroen van Erp
7b75fb3d53 Upgraded Gradle to 3.4.1 2017-04-10 10:13:35 +02:00
Jeroen van Erp
4d84d3f67c Upgraded BouncyCastle (Fixes #312) 2017-04-10 10:08:09 +02:00
Jeroen van Erp
8eb7d1a2ad Update README to point to right repository (Fixes #309) 2017-03-23 14:05:32 +01:00
ISQ-GTT
a03fa9ac63 Added charset support, centralized UTF-8 usage (#305)
* Added charset support, centralized UTF-8 usage

* Code style, buffer methods with charsets

* assure remote charset isn't null
2017-03-09 13:58:51 +01:00
Jeroen van Erp
bcb15e6ccd Changed deprecated build notation 2017-03-01 21:53:42 +01:00
Jeroen van Erp
d85b22fe8d Upgrade to Gradle 3.4 2017-03-01 21:51:29 +01:00
Jeroen van Erp
f4b71941a3 Updated release notes for 0.20.0 2017-02-10 10:38:43 +01:00
Jeroen van Erp
636f896850 Cleaned up some code 2017-02-10 09:43:12 +01:00
Jeroen van Erp
56c0baf814 Merge pull request #301 from adagios/master
adds current version to exported classes
2017-02-06 10:16:45 +01:00
João Nelas
edfb069f2a adds current version to exported classes 2017-02-03 17:33:07 +00:00
Jeroen van Erp
65b3003e72 Merge pull request #295 from adagios/master
converts bouncycastle OSGi dep from Import-Bundle to Import-Package
2017-01-24 09:20:15 +01:00
Jeroen van Erp
fbee0b3956 Update build.gradle 2017-01-23 22:56:23 +01:00
Jeroen van Erp
fd60139b98 Merge branch 'master' into master 2017-01-23 22:54:15 +01:00
Jeroen van Erp
0b397bc3d7 Added TODO 2017-01-23 15:33:47 +01:00
Jeroen van Erp
40f956b4b6 More code cleanup 2017-01-23 15:33:22 +01:00
Jeroen van Erp
ef3f7a2eaf Fixed more warnings 2017-01-23 14:40:33 +01:00
Jeroen van Erp
8134113510 Cleaned up more PMD/Codacy warnings 2017-01-23 10:46:01 +01:00
Jeroen van Erp
c883c87963 Fixed a few warnings 2017-01-23 09:55:04 +01:00
Jeroen van Erp
920537dac9 More cleaning up 2017-01-20 16:41:16 +01:00
Jeroen van Erp
356ec9ed08 Upgraded to mina sshd 1.2.0 2017-01-20 16:30:01 +01:00
Jeroen van Erp
aa47b0c5f7 Cleaning up unused code 2017-01-20 16:29:37 +01:00
Jeroen van Erp
d3ed3cfe0f Merge pull request #296 from hierynomus/new-dhgroups
Added new DH groups
2017-01-20 16:17:06 +01:00
Jeroen van Erp
786734ce26 Suppressing Method naming conventions for Factory classes 2017-01-20 15:43:36 +01:00
Jeroen van Erp
9cb5bf4e10 Organized imports 2017-01-20 15:26:21 +01:00
Jeroen van Erp
0e3f7c2bbf Added new DH groups 2017-01-20 15:05:18 +01:00
João Nelas
66d4b34eba re-adding Export-Package of com.hierynomus.sshj.* 2017-01-12 14:37:02 +00:00
João Nelas
aafb9942a3 converts bouncycastle OSGi dep from Import-Bundle to Import-Package
Import-Bundle is generally discouraged by OSGi best practices.

See http://stackoverflow.com/questions/1865819/when-should-i-use-import-package-and-when-should-i-use-require-bundle for more info.
2017-01-12 14:26:59 +00:00
Jeroen van Erp
d1dff550ce Updated OSGi bundling (Fixes #255 again) 2017-01-11 23:14:43 +01:00
Jeroen van Erp
ac2720becd Added bettercodehub config 2017-01-10 17:45:37 +01:00
Jeroen van Erp
48dd1fdc41 Merge pull request #294 from joval/master
Reference the EdDSANamedCurveTable constant instead of its value...
2017-01-09 11:21:13 +01:00
Jeroen van Erp
9826a71d2b Merge pull request #293 from adagios/osgi-fix
excludes net.i2p.crypto.eddsa.math from OSGI Import-Package
2017-01-09 11:20:48 +01:00
David A. Solin
936eb26e9e Reference the EdDSANamedCurveTable constant instead of its value, for forward-compatibility with the ed25519-java project on Github.
See: abdee146c3
2017-01-06 12:58:04 -06:00
João Nelas
9438157b93 excludes net.i2p.crypto.eddsa.math from OSGI Import-Package 2017-01-06 15:12:44 +00:00
Jeroen van Erp
7d326e5ae4 Updated example build 2016-12-30 09:26:43 +01:00
Jeroen van Erp
f038b5ce2b Updated Release notes 2016-12-30 09:26:00 +01:00
Jeroen van Erp
20879a4aa5 LocalPortForwarder interrupts its thread on close() 2016-12-29 16:06:57 +01:00
Jeroen van Erp
516abb0282 Disable version inference when no git history present (Fixes #256) 2016-12-28 10:09:48 +01:00
Jeroen van Erp
0ad51709c2 Use the configured Random factory in DH KEX (Fixes #292) 2016-12-28 10:00:24 +01:00
Jeroen van Erp
c9c68f019e StreamCipher can use the constructor without random. 2016-12-28 09:52:58 +01:00
Jeroen van Erp
fc75f9796c Added logging to trace time creation of Randoms 2016-12-28 09:52:20 +01:00
Jeroen van Erp
61af500c3e Added 'classes' directory to ignore 2016-12-28 09:51:03 +01:00
Jeroen van Erp
56553ea086 Prepared release notes 2016-12-23 11:58:49 +01:00
Jeroen van Erp
86e6631b1e Enabled PKCS5 in DefaultConfig 2016-12-23 11:54:43 +01:00
Jeroen van Erp
b6f437a932 Merge pull request #291 from joval/master
Two minor changes
2016-12-21 21:51:23 +01:00
David Solin
9e3b9f7c24 Obtain the sshj.properties resource from the ClassLoader that loaded the DefaultConfig class, not from the context classloader. 2016-12-21 10:04:56 -06:00
David Solin
766ab916ee Use cause message when initializing an SSHRuntimeException from a Throwable. 2016-12-21 07:48:02 -06:00
Jeroen van Erp
cdca43a848 Merge pull request #284 from gatesking/patch-1
Catch interrupt signal in keepalive thread
2016-11-30 09:42:29 +01:00
gatesking
3ce7c2ebfb Catch interrupt signal in keepalive thread
Interrupt signal may be catched when keepalive thread is sleeping. As a result, it will still go into conn.getTransport().die(e).
2016-11-30 09:30:57 +08:00
Jeroen van Erp
ca4e0bf2d7 Update README.adoc 2016-11-25 13:27:20 +01:00
Jeroen van Erp
2ca2bbd633 Prepared for next release 2016-11-25 13:14:48 +01:00
Jeroen van Erp
256e65dea4 Build jar with correct version string automatically (Fixes #280) 2016-11-23 13:30:37 +01:00
Jeroen van Erp
1feb7fe9a6 Update README.adoc 2016-10-31 09:57:12 +01:00
Jeroen van Erp
d95b4db930 Merge pull request #279 from hierynomus/issue-276
Support for OpenSSH new key file format (fixes #276)
2016-10-31 09:55:47 +01:00
Jeroen van Erp
677f482a69 Fixed text 2016-10-28 16:31:23 +02:00
Jeroen van Erp
179b30ef4e Fixed some codacy warnings 2016-10-28 14:50:37 +02:00
Jeroen van Erp
f59bbccc5f Fixed ed-25519 and openssh-key-v1 formats 2016-10-28 14:45:16 +02:00
Jeroen van Erp
bf34072c3a Reading first part of the new openssh key format 2016-10-24 09:49:45 +02:00
Jeroen van Erp
771751ca4c Fixed license header 2016-10-19 14:48:06 +01:00
Jeroen van Erp
968d4284a0 Extracted common key file methods into an abstract base class 2016-10-19 12:08:51 +01:00
208 changed files with 8710 additions and 2953 deletions

8
.bettercodehub.yml Normal file
View File

@@ -0,0 +1,8 @@
exclude:
- /build-publishing.gradle
- /build.gradle
- /settings.gradle
component_depth: 1
languages:
- groovy
- java

3
.gitignore vendored
View File

@@ -10,7 +10,10 @@
.settings/
# Output dirs
out/
target/
classes/
bin/
build/
docs/
.gradle/

View File

@@ -1,5 +1,30 @@
language: java
sudo: false
dist: trusty
sudo: required
services:
- docker
jdk:
- oraclejdk7
- 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

View File

@@ -1,13 +1,15 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.18.0
:sshj_version: 0.27.0
:source-highlighter: pygments
image:https://api.bintray.com/packages/hierynomus/maven/sshj/images/download.svg[link="https://bintray.com/hierynomus/maven/sshj/_latestVersion"]
image:https://travis-ci.org/hierynomus/sshj.svg?branch=master[link="https://travis-ci.org/hierynomus/sshj"]
image:https://api.codacy.com/project/badge/Grade/14a0a316bb9149739b5ea26dbfa8da8a["Codacy code quality", link="https://www.codacy.com/app/jeroen_2/sshj?utm_source=github.com&utm_medium=referral&utm_content=hierynomus/sshj&utm_campaign=Badge_Grade"]
image:https://codecov.io/gh/hierynomus/sshj/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/hierynomus/sshj"]
image:http://www.javadoc.io/badge/com.hierynomus/sshj.svg?color=blue["JavaDocs", link="http://www.javadoc.io/doc/com.hierynomus/sshj"]
image:https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj/badge.svg["Maven Central",link="https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj"]
image:https://javadoc-emblem.rhcloud.com/doc/com.hierynomus/sshj/badge.svg["Javadoc",link="http://www.javadoc.io/doc/com.hierynomus/sshj"]
To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :)
@@ -37,7 +39,7 @@ If you're building your project using Maven, you can add the following dependenc
If your project is built using another build tool that uses the Maven Central repository, translate this dependency into the format used by your build tool.
== Building SSHJ
. Clone the Overthere repository.
. Clone the SSHJ repository.
. Ensure you have Java6 installed with the http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html[Unlimited strength Java Cryptography Extensions (JCE)].
. Run the command `./gradlew clean build`.
@@ -66,20 +68,27 @@ ciphers::
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::
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`, `diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`,
`diffie-hellman-group14-sha256`, `diffie-hellman-group15-sha512`, `diffie-hellman-group16-sha512`, `diffie-hellman-group17-sha512`, `diffie-hellman-group18-sha512`
`diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
SSHJ also supports the following extended (non official) key exchange algoriths:
`diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`,
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
signatures::
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519`
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519`
mac::
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`, `hmac-ripemd160`, `hmac-ripemd160@openssh.com`
`hmac-md5-etm@openssh.com`, `hmac-md5-96-etm@openssh.com`, `hmac-sha1-etm@openssh.com`, `hmac-sha1-96-etm@openssh.com`, `hmac-sha2-256-etm@openssh.com`, `hmac-sha2-512-etm@openssh.com`, `hmac-ripemd160-etm@openssh.com`
compression::
`zlib` and `zlib@openssh.com` (delayed zlib)
private key files::
`pkcs8` encoded (what openssh uses)
`pkcs5`, `pkcs8`, `openssh-key-v1`, `ssh-rsa-cert-v01@openssh.com`, `ssh-dsa-cert-v01@openssh.com`
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
@@ -92,13 +101,63 @@ Java 6+. http://www.slf4j.org/download.html[slf4j] is required. http://www.bounc
== Reporting bugs
Issue tracker: https://github.com/hierynomus/sshj/issues
== Discussion
Google Group: http://groups.google.com/group/sshj-users
== Contributing
Fork away!
== Release history
SSHJ 0.27.0 (2019-01-24)::
* Fixed https://github.com/hierynomus/sshj/issues/415[#415]: Fixed wrongly prefixed '/' to path in SFTPClient.mkdirs
* Added support for ETM (Encrypt-then-Mac) MAC algorithms.
* Fixed https://github.com/hierynomus/sshj/issues/454[#454]: Added missing capacity check for Buffer.putUint64
* Fixed https://github.com/hierynomus/sshj/issues/466[#466]: Added lock timeout for remote action to prevent hanging
* Fixed https://github.com/hierynomus/sshj/issues/470[#470]: Made EdDSA the default (first) signature factory
* Fixed https://github.com/hierynomus/sshj/issues/467[#467]: Added AES256-CBC as cipher mode in openssh-key-v1 support
* Fixed https://github.com/hierynomus/sshj/issues/464[#464]: Enabled curve25519-sha256@openssh.org in DefaultConfig
* Fixed https://github.com/hierynomus/sshj/issues/472[#472]: Handle server initiated global requests
* Fixed https://github.com/hierynomus/sshj/issues/485[#485]: Added support for all keytypes to openssh-key-v1 keyfiles.
SSHJ 0.26.0 (2018-07-24)::
* Fixed https://github.com/hierynomus/sshj/issues/413[#413]: Use UTF-8 for PrivateKeyFileResource
* Fixed https://github.com/hierynomus/sshj/issues/427[#427]: Support encrypted ed25519 openssh-key-v1 files
* Upgraded BouncyCastle to 1.60
* Added support for hmac-ripemd160@openssh.com MAC
SSHJ 0.24.0 (2018-04-04)::
* Added support for hmac-ripemd160
* Fixed https://github.com/hierynomus/sshj/issues/382[#382]: Fixed escaping in WildcardHostmatcher
* Added integration testsuite using Docker against OpenSSH
* Fixed https://github.com/hierynomus/sshj/issues/187[#187]: Fixed length bug in Buffer.putString
* Fixed https://github.com/hierynomus/sshj/issues/405[#405]: Continue host verification if first hostkey does not match.
SSHJ 0.23.0 (2017-10-13)::
* Merged https://github.com/hierynomus/sshj/pulls/372[#372]: Upgrade to 'net.i2p.crypto:eddsa:0.2.0'
* Fixed https://github.com/hierynomus/sshj/issues/355[#355] and https://github.com/hierynomus/sshj/issues/354[#354]: Correctly decode signature bytes
* Fixed https://github.com/hierynomus/sshj/issues/365[#365]: Added support for new-style OpenSSH fingerprints of server keys
* Fixed https://github.com/hierynomus/sshj/issues/356[#356]: Fixed key type detection for ECDSA public keys
* Made SSHJ Java9 compatible
SSHJ 0.22.0 (2017-08-24)::
* Fixed https://github.com/hierynomus/sshj/pulls/341[#341]: Fixed path walking during recursive copy
* Merged https://github.com/hierynomus/sshj/pulls/338[#338]: Added ConsolePasswordFinder to read password from stdin
* Merged https://github.com/hierynomus/sshj/pulls/336[#336]: Added support for ecdsa-sha2-nistp384 and ecdsa-sha2-nistp521 signatures
* Fixed https://github.com/hierynomus/sshj/issues/331[#331]: Added support for wildcards in known_hosts file
SSHJ 0.21.1 (2017-04-25)::
* Merged https://github.com/hierynomus/sshj/pulls/322[#322]: Fix regression from 40f956b (invalid length parameter on outputstream)
SSHJ 0.21.0 (2017-04-14)::
* Merged https://github.com/hierynomus/sshj/pulls/319[#319]: Added support for `ssh-rsa-cert-v01@openssh.com` and `ssh-dsa-cert-v01@openssh.com` certificate key files
* Upgraded Gradle to 3.4.1
* Merged https://github.com/hierynomus/sshj/pulls/305[#305]: Added support for custom string encoding
* Fixed https://github.com/hierynomus/sshj/issues/312[#312]: Upgraded BouncyCastle to 1.56
SSHJ 0.20.0 (2017-02-09)::
* Merged https://github.com/hierynomus/sshj/pulls/294[#294]: Reference ED25519 by constant instead of name
* Merged https://github.com/hierynomus/sshj/pulls/293[#293], https://github.com/hierynomus/sshj/pulls/295[#295] and https://github.com/hierynomus/sshj/pulls/301[#301]: Fixed OSGi packaging
* Added new Diffie Hellman groups 15-18 for stronger KeyExchange algorithms
SSHJ 0.19.1 (2016-12-30)::
* Enabled PKCS5 Key files in DefaultConfig
* Merged https://github.com/hierynomus/sshj/pulls/291[#291]: Fixed sshj.properties loading and chained exception messages
* Merged https://github.com/hierynomus/sshj/pulls/284[#284]: Correctly catch interrupt in keepalive thread
* Fixed https://github.com/hierynomus/sshj/issues/292[#292]: Pass the configured RandomFactory to Diffie Hellmann KEX
* Fixed https://github.com/hierynomus/sshj/issues/256[#256]: SSHJ now builds if no git repository present
* LocalPortForwarder now correctly interrupts its own thread on close()
SSHJ 0.19.0 (2016-11-25)::
* Fixed https://github.com/hierynomus/sshj/issues/276[#276]: Add support for ed-25519 and new OpenSSH key format
* Fixed https://github.com/hierynomus/sshj/issues/280[#280]: Read version from a generated sshj.properties file to correctly output version during negotiation
SSHJ 0.18.0 (2016-09-30)::
* Fixed Android compatibility
* Upgrade to Gradle 3.0

View File

@@ -1,22 +1,45 @@
import java.text.SimpleDateFormat
import com.bmuschko.gradle.docker.tasks.container.*
import com.bmuschko.gradle.docker.tasks.image.*
plugins {
id "java"
id "groovy"
id "jacoco"
id "osgi"
id "maven-publish"
id "org.ajoberstar.release-opinion" version "1.4.2"
id 'pl.allegro.tech.build.axion-release' version '1.9.2'
id "com.bmuschko.docker-remote-api" version "3.2.1"
id "com.github.hierynomus.license" version "0.12.1"
id "com.jfrog.bintray" version "1.7"
id 'ru.vyarus.pom' version '1.0.3'
id 'ru.vyarus.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.4.2'
}
group = "com.hierynomus"
scmVersion {
tag {
prefix = 'v'
versionSeparator = ''
}
hooks {
pre 'fileUpdate', [file: 'README.adoc', pattern: { v, c -> /:sshj_version: .*/}, replacement: { v, c -> ":sshj_version: $v" }]
pre 'commit'
}
}
project.version = scmVersion.version
defaultTasks "build"
repositories {
mavenCentral()
maven {
url "https://dl.bintray.com/mockito/maven/"
}
}
sourceCompatibility = 1.6
@@ -24,20 +47,25 @@ targetCompatibility = 1.6
configurations.compile.transitive = false
def bouncycastleVersion = "1.51"
def bouncycastleVersion = "1.60"
def sshdVersion = "2.1.0"
dependencies {
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
compile "org.slf4j:slf4j-api:1.7.7"
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
compile "com.jcraft:jzlib:1.1.3"
compile "net.i2p.crypto:eddsa:0.1.0"
compile "net.i2p.crypto:eddsa:0.2.0"
testCompile "junit:junit:4.11"
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile "org.mockito:mockito-core:1.9.5"
testCompile "org.apache.sshd:sshd-core:1.1.0"
testCompile "org.mockito:mockito-core:2.9.2"
testCompile "org.apache.sshd:sshd-core:$sshdVersion"
testCompile "org.apache.sshd:sshd-sftp:$sshdVersion"
testCompile "org.apache.sshd:sshd-scp:$sshdVersion"
testRuntime "ch.qos.logback:logback-classic:1.1.2"
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
@@ -50,11 +78,7 @@ license {
mapping {
java = 'SLASHSTAR_STYLE'
}
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java'])
}
release {
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java', '**/org/mindrot/jbcrypt/*.java'])
}
// This disables the pedantic doclint feature of JDK8
@@ -64,29 +88,37 @@ if (JavaVersion.current().isJava8Compatible()) {
}
}
jar {
manifest {
instruction "Bundle-Description", "SSHv2 library for Java"
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
instruction "Import-Package", "!net.schmizz.*"
instruction "Import-Package", "javax.crypto*"
instruction "Import-Package", "net.i2p*"
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
instruction "Import-Package", "org.bouncycastle*"
instruction "Import-Package", "*"
instruction "Export-Package", "net.schmizz.*"
task writeSshjVersionProperties {
doLast {
project.file("${project.buildDir}/resources/main").mkdirs()
project.file("${project.buildDir}/resources/main/sshj.properties").withWriter { w ->
w.append("sshj.version=${version}")
}
}
}
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
jar.dependsOn writeSshjVersionProperties
jar {
manifest {
// please see http://bnd.bndtools.org/chapters/390-wrapping.html
instruction "Bundle-Description", "SSHv2 library for Java"
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
instruction "Import-Package", "!net.schmizz.*"
instruction "Import-Package", "!com.hierynomus.sshj.*"
instruction "Import-Package", "javax.crypto*"
instruction "Import-Package", "!net.i2p.crypto.eddsa.math"
instruction "Import-Package", "net.i2p*"
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
instruction "Import-Package", "org.bouncycastle*;resolution:=optional"
instruction "Import-Package", "org.bouncycastle.jce.provider;resolution:=optional"
instruction "Import-Package", "*"
instruction "Export-Package", "com.hierynomus.sshj.*;version=\"${project.jar.manifest.version}\""
instruction "Export-Package", "net.schmizz.*;version=\"${project.jar.manifest.version}\""
}
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
sourcesJar {
manifest {
attributes(
// Add the needed OSGI attributes
@@ -99,6 +131,27 @@ task sourcesJar(type: Jar) {
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
sourceSets {
integrationTest {
groovy {
compileClasspath += sourceSets.main.output + sourceSets.test.output
runtimeClasspath += sourceSets.main.output + sourceSets.test.output
srcDir file('src/itest/groovy')
}
resources.srcDir file('src/itest/resources')
}
}
task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
tasks.withType(Test) {
testLogging {
exceptionFormat = 'full'
@@ -165,21 +218,12 @@ pom {
}
}
publishing.publications {
Sshj(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
}
}
if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey")) {
bintray {
user = project.property("bintrayUsername")
key = project.property("bintrayApiKey")
publish = true
publications = ["Sshj"]
publications = ["maven"]
pkg {
repo = "maven"
name = project.name
@@ -188,7 +232,7 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
githubRepo = "hierynomus/sshj"
version {
name = project.version.toString()
name = "${project.version}"
vcsTag = "v${project.version}"
released = new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ').format(new Date())
gpg {
@@ -206,4 +250,43 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey
}
}
project.tasks.release.dependsOn([project.tasks.build, project.tasks.bintrayUpload])
jacocoTestReport {
reports {
xml.enabled true
html.enabled true
}
}
task buildItestImage(type: DockerBuildImage) {
inputDir = file('src/itest/docker-image')
tag = 'sshj/sshd-itest'
}
task createItestContainer(type: DockerCreateContainer) {
dependsOn buildItestImage
targetImageId { buildItestImage.getImageId() }
portBindings = ['2222:22']
}
task startItestContainer(type: DockerStartContainer) {
dependsOn createItestContainer
targetContainerId { createItestContainer.getContainerId() }
}
task stopItestContainer(type: DockerStopContainer) {
targetContainerId { createItestContainer.getContainerId() }
}
task forkedUploadRelease(type: GradleBuild) {
buildFile = project.buildFile
tasks = ["bintrayUpload"]
}
project.tasks.integrationTest.dependsOn(startItestContainer)
project.tasks.integrationTest.finalizedBy(stopItestContainer)
project.tasks.release.dependsOn([project.tasks.integrationTest, project.tasks.build])
project.tasks.release.finalizedBy(project.tasks.forkedUploadRelease)
project.tasks.jacocoTestReport.dependsOn(project.tasks.test)
project.tasks.check.dependsOn(project.tasks.jacocoTestReport)

View File

@@ -24,7 +24,7 @@
<groupId>com.hierynomus</groupId>
<artifactId>sshj-examples</artifactId>
<packaging>jar</packaging>
<version>0.14.0</version>
<version>0.19.1</version>
<name>sshj-examples</name>
<description>Examples for SSHv2 library for Java</description>
@@ -55,7 +55,7 @@
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>0.15.0</version>
<version>0.24.0</version>
</dependency>
</dependencies>

View File

@@ -5,30 +5,36 @@ import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import java.io.Console;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/** This examples demonstrates how a remote command can be executed. */
public class Exec {
private static final Console con = System.console();
public static void main(String... args)
throws IOException {
final SSHClient ssh = new SSHClient();
ssh.loadKnownHosts();
ssh.connect("localhost");
Session session = null;
try {
ssh.authPublickey(System.getProperty("user.name"));
final Session session = ssh.startSession();
try {
final Command cmd = session.exec("ping -c 1 google.com");
System.out.println(IOUtils.readFully(cmd.getInputStream()).toString());
cmd.join(5, TimeUnit.SECONDS);
System.out.println("\n** exit status: " + cmd.getExitStatus());
} finally {
session.close();
}
session = ssh.startSession();
final Command cmd = session.exec("ping -c 1 google.com");
con.writer().print(IOUtils.readFully(cmd.getInputStream()).toString());
cmd.join(5, TimeUnit.SECONDS);
con.writer().print("\n** exit status: " + cmd.getExitStatus());
} finally {
try {
if (session != null) {
session.close();
}
} catch (IOException e) {
// Do Nothing
}
ssh.disconnect();
}
}

View File

@@ -3,14 +3,11 @@ package net.schmizz.sshj.examples;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** This examples demonstrates how to setup keep-alive to detect connection dropping. */
public class KeepAlive {

View File

@@ -9,6 +9,7 @@ import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import java.io.File;
import java.io.IOException;
import net.schmizz.sshj.common.LoggerFactory;
/** A very rudimentary psuedo-terminal based on console I/O. */
class RudimentaryPTY {
@@ -33,18 +34,18 @@ class RudimentaryPTY {
final Shell shell = session.startShell();
new StreamCopier(shell.getInputStream(), System.out)
new StreamCopier(shell.getInputStream(), System.out, LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stdout");
new StreamCopier(shell.getErrorStream(), System.err)
new StreamCopier(shell.getErrorStream(), System.err, LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stderr");
// Now make System.in act as stdin. To exit, hit Ctrl+D (since that results in an EOF on System.in)
// This is kinda messy because java only allows console input after you hit return
// But this is just an example... a GUI app could implement a proper PTY
new StreamCopier(System.in, shell.getOutputStream())
new StreamCopier(System.in, shell.getOutputStream(), LoggerFactory.DEFAULT)
.bufSize(shell.getRemoteMaxPacketSize())
.copy();

View File

@@ -5,6 +5,7 @@ import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectListener;
import net.schmizz.sshj.common.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -42,8 +43,8 @@ public class X11 {
final Command cmd = sess.exec("/usr/X11/bin/xcalc");
new StreamCopier(cmd.getInputStream(), System.out).spawn("stdout");
new StreamCopier(cmd.getErrorStream(), System.err).spawn("stderr");
new StreamCopier(cmd.getInputStream(), System.out, LoggerFactory.DEFAULT).spawn("stdout");
new StreamCopier(cmd.getErrorStream(), System.err, LoggerFactory.DEFAULT).spawn("stderr");
// Wait for session & X11 channel to get closed
ssh.getConnection().join();

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip

View File

@@ -0,0 +1,23 @@
FROM sickp/alpine-sshd:7.5-r2
ADD authorized_keys /home/sshj/.ssh/authorized_keys
ADD test-container/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key
ADD test-container/ssh_host_ecdsa_key.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
RUN apk add --no-cache tini
RUN \
echo "root:smile" | chpasswd && \
adduser -D -s /bin/ash sshj && \
passwd -u sshj && \
chmod 600 /home/sshj/.ssh/authorized_keys && \
chmod 600 /etc/ssh/ssh_host_ecdsa_key && \
chmod 644 /etc/ssh/ssh_host_ecdsa_key.pub && \
chmod 600 /etc/ssh/ssh_host_ed25519_key && \
chmod 644 /etc/ssh/ssh_host_ed25519_key.pub && \
chown -R sshj:sshj /home/sshj
ENTRYPOINT ["/sbin/tini", "/entrypoint.sh"]

View File

@@ -0,0 +1,7 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOEQcvowiV3igdRO7rKPrZrao1hCQrnC4tgsxqSJdQCbABI+vHrdbJRfWZNuSk48aAtARJzJVmkn/r63EPJgkh8= root@itgcpkerberosstack-cbgateway-0-20151117031915
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHQiZm0wBbmI8gohA/N9ir1O+egikL6S9FjZS8GHbx4rTHI1V+vbXxx2O9bFWtep1PFb4iowtZkxf6gvRjGkL6M= ajvanerp@Heimdall.local
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDAdJiRkkBM8yC8seTEoAn2PfwbLKrkcahZ0xxPoWICJ root@sshj
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8ww4hJG/gHJYdkjTTBDF1GNz+228nuWprPV+NbQauA ajvanerp@Heimdall.local
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOaWrwt3drIOjeBq2LSHRavxAT7ja2f+5soOUJl/zKSI ajvanerp@Heimdall.xebialabs.com
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQmTIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmuhA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPCliztspKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmLGGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQ== no-passphrase
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKRyZAtOJJfAhPU6xE6ZXY564vwErAI3n3Yn4lTHL9bxev9Ily6eCqPLcV0WbSV04pztngFn9MjT7yb8mcXheHpIaWEH569sMpmpOtyfn4p68SceuXBGyyPGMIcfOTknkASd1JYSD4EPkd9rZmCzcx3vEnLu8ChnA/G221xSVQ5VC/jD/c/CgNUayhQ+xbn57qHKKtZwfTa21QmwIabGYJNwlVjlKTCdddeVnZfKqKrG7cxHQApsxd21rhM9IT/C/f4Y/Tx3WUUVeam0iZ265oiPHoPALqJIWSQIUheRYAxYAQqJwSQ0Or9MM8XXun2Iy3RUSGk6eIvrCsFbNURsHNs7Pu0UnpYv6FZ3vCkFep/1pAT6fQvY7pDOOWDHKXArD4watc9gIWaQBH73wDW/KgBcnMRSoGWgQjsYqIamP4oV1+HqUI3lRAsXZaX+eiBGt3+3A5KebP27UJ1YUwhwlzs7wzTKaCu0OaL+hOsP1F2AxAa995bgFksMd23645ux3YCJKXG4sGpJ1Z/Hs49K72gv+QjLZVxXqY623c8+3OUhlixqoEFd4iG7UMc5a552ch/VA+jaspmLZoFhPz99aBRVb1oCSPxSwLw+Q/wxv6pZmT+14rqTzY2farjU53hM+CsUPh7dnWXhGG7RuA5wCdeOXOYjuksfzAoHIZhPqTgQ== ajvanerp@Heimdall.local

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOpOBFjqe0hjK/hs4WZ3dZqnzanq1L3/JbvV1TCkbe4ToAoGCCqGSM49
AwEHoUQDQgAEVzkrS7Yj0nXML7A3mE08YDthfBR/ZbyYJDIq1vTzcqs6KTaCT529
swNXWLHO+mbHviZcRiI57ULXHZ1emom/Jw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFc5K0u2I9J1zC+wN5hNPGA7YXwUf2W8mCQyKtb083KrOik2gk+dvbMDV1ixzvpmx74mXEYiOe1C1x2dXpqJvyc= root@404b27be2bf4

View File

@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBFG9PKAq8FtH0me+LHUE6YaVANCMqy/Znkffzief1W/gAAAKCyyoBkssqA
ZAAAAAtzc2gtZWQyNTUxOQAAACBFG9PKAq8FtH0me+LHUE6YaVANCMqy/Znkffzief1W/g
AAAED+Yfza2xk5LqP9pN6TpvhWYP0L60zOQJpHhbEuiS3LLkUb08oCrwW0fSZ74sdQTphp
UA0IyrL9meR9/OJ5/Vb+AAAAF2FqdmFuZXJwQEhlaW1kYWxsLmxvY2FsAQIDBAUG
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEUb08oCrwW0fSZ74sdQTphpUA0IyrL9meR9/OJ5/Vb+ ajvanerp@Heimdall.local

View File

@@ -0,0 +1,132 @@
# $OpenBSD: sshd_config,v 1.101 2017/03/14 07:19:07 djm Exp $
# This is the sshd server system-wide configuration file. See
# sshd_config(5) for more information.
# This sshd was compiled with PATH=/bin:/usr/bin:/sbin:/usr/sbin
# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented. Uncommented options override the
# default value.
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key
# Ciphers and keying
#RekeyLimit default none
# Logging
#SyslogFacility AUTH
#LogLevel INFO
# Authentication:
#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
#PubkeyAuthentication yes
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys
#AuthorizedPrincipalsFile none
#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes
# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no
# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication. Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
#UsePAM no
#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
#X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
#PrintMotd yes
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none
# no default banner path
#Banner none
# override default of no subsystems
Subsystem sftp /usr/lib/ssh/sftp-server
# the following are HPN related configuration options
# tcp receive buffer polling. disable in non autotuning kernels
#TcpRcvBufPoll yes
# disable hpn performance boosts
#HPNDisabled no
# buffer size for hpn to non-hpn connections
#HPNBufferSize 2048
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server
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

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
import net.schmizz.sshj.Config
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
import spock.lang.Specification
class IntegrationBaseSpec extends Specification {
protected static final int DOCKER_PORT = 2222
protected static final String USERNAME = "sshj"
protected static final String KEYFILE = "src/itest/resources/keyfiles/id_rsa"
protected final static String SERVER_IP = System.getProperty("serverIP", "127.0.0.1")
protected static SSHClient getConnectedClient(Config config) {
SSHClient sshClient = new SSHClient(config)
sshClient.addHostKeyVerifier(new PromiscuousVerifier())
sshClient.connect(SERVER_IP, DOCKER_PORT)
return sshClient
}
protected static SSHClient getConnectedClient() throws IOException {
return getConnectedClient(new DefaultConfig())
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj
import com.hierynomus.sshj.signature.SignatureEdDSA
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
class IntegrationSpec extends IntegrationBaseSpec {
@Unroll
def "should accept correct key for #signatureName"() {
given:
def config = new DefaultConfig()
config.setSignatureFactories(signatureFactory)
SSHClient sshClient = new SSHClient(config)
sshClient.addHostKeyVerifier(fingerprint) // test-containers/ssh_host_ecdsa_key's fingerprint
when:
sshClient.connect(SERVER_IP, DOCKER_PORT)
then:
sshClient.isConnected()
where:
signatureFactory << [new SignatureECDSA.Factory256(), new SignatureEdDSA.Factory()]
fingerprint << ["d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3", "dc:68:38:ce:fc:6f:2c:d6:6d:6b:34:eb:5c:f0:41:6a"]
signatureName = signatureFactory.getName()
}
def "should decline wrong key"() throws IOException {
given:
SSHClient sshClient = new SSHClient(new DefaultConfig())
sshClient.addHostKeyVerifier("d4:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3")
when:
sshClient.connect(SERVER_IP, DOCKER_PORT)
then:
thrown(TransportException.class)
}
@Unroll
def "should authenticate with key #key"() {
given:
SSHClient client = getConnectedClient()
when:
def keyProvider = passphrase != null ? client.loadKeys("src/itest/resources/keyfiles/$key", passphrase) : client.loadKeys("src/itest/resources/keyfiles/$key")
client.authPublickey(USERNAME, keyProvider)
then:
client.isAuthenticated()
where:
key | passphrase
// "id_ecdsa_nistp256" | null // TODO: Need to improve PKCS8 key support.
"id_ecdsa_opensshv1" | null
"id_ed25519_opensshv1" | null
"id_ed25519_opensshv1_aes256cbc.pem" | "foobar"
"id_ed25519_opensshv1_protected" | "sshjtest"
"id_rsa" | null
"id_rsa_opensshv1" | null
}
def "should not authenticate with wrong key"() {
given:
SSHClient client = getConnectedClient()
when:
client.authPublickey("sshj", "src/itest/resources/keyfiles/id_unknown_key")
then:
thrown(UserAuthException.class)
!client.isAuthenticated()
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.sftp
import com.hierynomus.sshj.IntegrationBaseSpec
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.sftp.OpenMode
import net.schmizz.sshj.sftp.RemoteFile
import net.schmizz.sshj.sftp.SFTPClient
import java.nio.charset.StandardCharsets
import static org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable
class FileWriteSpec extends IntegrationBaseSpec {
def "should append to file (GH issue #390)"() {
given:
SSHClient client = getConnectedClient()
client.authPublickey("sshj", "src/test/resources/id_rsa")
SFTPClient sftp = client.newSFTPClient()
def file = "/home/sshj/test.txt"
def initialText = "This is the initial text.\n".getBytes(StandardCharsets.UTF_16)
def appendText = "And here's the appended text.\n".getBytes(StandardCharsets.UTF_16)
when:
withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT))) { RemoteFile initial ->
initial.write(0, initialText, 0, initialText.length)
}
then:
withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read ->
def bytes = new byte[initialText.length]
read.read(0, bytes, 0, bytes.length)
bytes == initialText
}
when:
withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.APPEND))) { RemoteFile append ->
append.write(0, appendText, 0, appendText.length)
}
then:
withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read ->
def bytes = new byte[initialText.length + appendText.length]
read.read(0, bytes, 0, bytes.length)
Arrays.copyOfRange(bytes, 0, initialText.length) == initialText
Arrays.copyOfRange(bytes, initialText.length, initialText.length + appendText.length) == appendText
}
cleanup:
sftp.close()
client.close()
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.kex
import com.hierynomus.sshj.IntegrationBaseSpec
import com.hierynomus.sshj.transport.mac.Macs
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.transport.kex.Curve25519DH
import net.schmizz.sshj.transport.kex.Curve25519SHA256
import net.schmizz.sshj.transport.kex.DH
import net.schmizz.sshj.transport.kex.DHGexSHA1
import net.schmizz.sshj.transport.kex.DHGexSHA256
import net.schmizz.sshj.transport.kex.ECDH
import net.schmizz.sshj.transport.kex.ECDHNistP
import spock.lang.Unroll
class KexSpec extends IntegrationBaseSpec {
@Unroll
def "should correctly connect with #kex Key Exchange"() {
given:
def cfg = new DefaultConfig()
cfg.setKeyExchangeFactories(kexFactory)
def client = getConnectedClient(cfg)
when:
client.authPublickey(USERNAME, KEYFILE)
then:
client.authenticated
where:
kexFactory << [DHGroups.Group1SHA1(),
DHGroups.Group14SHA1(),
DHGroups.Group14SHA256(),
DHGroups.Group16SHA512(),
DHGroups.Group18SHA512(),
new DHGexSHA1.Factory(),
new DHGexSHA256.Factory(),
new Curve25519SHA256.Factory(),
new Curve25519SHA256.FactoryLibSsh(),
new ECDHNistP.Factory256(),
new ECDHNistP.Factory384(),
new ECDHNistP.Factory521()]
kex = kexFactory.name
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.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 {
@Unroll
def "should correctly connect with #mac MAC"() {
given:
def cfg = new DefaultConfig()
cfg.setMACFactories(macFactory)
def client = getConnectedClient(cfg)
when:
client.authPublickey(USERNAME, KEYFILE)
then:
client.authenticated
cleanup:
client.disconnect()
where:
macFactory << [Macs.HMACRIPEMD160(), Macs.HMACRIPEMD160OpenSsh(), Macs.HMACSHA2256(), Macs.HMACSHA2512()]
mac = macFactory.name
}
@Unroll
def "should correctly connect with Encrypt-Then-Mac #mac MAC"() {
given:
def cfg = new DefaultConfig()
cfg.setMACFactories(macFactory)
def client = getConnectedClient(cfg)
when:
client.authPublickey(USERNAME, KEYFILE)
then:
client.authenticated
cleanup:
client.disconnect()
where:
macFactory << [Macs.HMACRIPEMD160Etm(), Macs.HMACSHA2256Etm(), Macs.HMACSHA2512Etm()]
mac = macFactory.name
}
}

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJUMlsSlXqCZmCjlN4kV7hzP+p9pu0fwJ8r4m1qle58SoAoGCCqGSM49
AwEHoUQDQgAE4RBy+jCJXeKB1E7uso+tmtqjWEJCucLi2CzGpIl1AJsAEj68et1s
lF9Zk25KTjxoC0BEnMlWaSf+vrcQ8mCSHw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,9 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQR0ImZtMAW5iPIKIQPzfYq9TvnoIpC+
kvRY2UvBh28eK0xyNVfr218cdjvWxVrXqdTxW+IqMLWZMX+oL0YxpC+jAAAAsD+6Oow/uj
qMAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHQiZm0wBbmI8goh
A/N9ir1O+egikL6S9FjZS8GHbx4rTHI1V+vbXxx2O9bFWtep1PFb4iowtZkxf6gvRjGkL6
MAAAAgXNC11pInVAOd3xNphiHMoISeitf6h1IKbDM+niLrL5kAAAAXYWp2YW5lcnBASGVp
bWRhbGwubG9jYWwB
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAwHSYkZJATPMgvLHkxKAJ9j38Gyyq5HGoWdMcT6FiAiQAAAJDimgR84poE
fAAAAAtzc2gtZWQyNTUxOQAAACAwHSYkZJATPMgvLHkxKAJ9j38Gyyq5HGoWdMcT6FiAiQ
AAAECmsckQycWnfGQK6XtQpaMGODbAkMQOdJNK6XJSipB7dDAdJiRkkBM8yC8seTEoAn2P
fwbLKrkcahZ0xxPoWICJAAAACXJvb3RAc3NoagECAwQ=
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABBLQVXV9f
Wpw8AL9RTpAr//AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIJ8ww4hJG/gHJYdk
jTTBDF1GNz+228nuWprPV+NbQauAAAAAoGHEO7x3fSRBohvrIR52U4XD3uqRnhrPYm01k1
f4HHNNv46m92Zw6JKIB9Trrvp0sdMI8MVb79bN45rbn6mvpABtWl6T5TOTyMnKzDfAOx9c
FTaasWFmgtgkXOsu5pLrYBAQgCHWbzjjz6KoV1DmD4SAn9Ojf9Oh+YdAEKZcsvklgpu+Kj
nzN/DR0jt7Nzep2kNCLAS24QEkvQeATVSDiL8=
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB/aWL0WG
iYPOTxGlFwvaCNAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIOaWrwt3drIOjeBq
2LSHRavxAT7ja2f+5soOUJl/zKSIAAAAsKplAiFbOhzcOJYFYBYm8sqYbvhPF8jKdQFkbo
LAOeq+vQ0YBV9XUWQQM2tmL+RPjykPJZ2thcHLpVp3PfUEgo4bImCt939b3Ji3cEwD3QuK
MIhjhx1KvSJNF/uhjwPJnttwHG+ld8F5Gv7LpTOUmOzXKGLIgYRuwonhs5ezdNv5ERs+Cq
M9p/SW5ehL5KPJhGa5a+ZQXRojwEH7J4Q5xztH1gviTdIEpFWWQBH8rX6y
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEoAIBAAKCAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQm
TIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6
RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmu
hA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPClizt
spKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmL
GGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQIBIwKCAQBXvO4uJlbrLJQDPYAt
1i1ybGcF+rrR/Q34a2dgCpZDEwFiDTlcv1hxx689OXTf5cMPXGDZXX5udd9p7Wxa
NqnIrvVUtQWLdqcZuEeO+gitHr8IyMJf5Lm8C/u5vl1PYOYhO0qxwmrTP1u6fZPh
zWX2X1p5626/sy+TCisCRDeLRyes+Dtfs3bDjUq+zF3D/DmeYY55LUx0XS27uXNS
QuUDMSnymFyj4o+jPK0q/j5w4bB+0rbsij+EP7S//jOFrSEcZgBhhIj0rHA5fo6w
NrgtgRKD3HKFBM3b4VM8TdMbHsmf+nT9DjiDqcs+IxXMGlb1XTjtQFIN2eyRtNLd
eQ0bAoGBAMwgv3rGytRjVbR4TT77eF81svzitOJWRdfXuKB5gqM3jsPR08f1MEtZ
44kaI5ByJ3mBDt/EwNgLRdmBddPrLu3so9VLdRmWKI+KNGxwkcxzJv1xXdicgw+w
S5WgigJryuUbtdylXQTlRArLUKsXULk/MndhGiD+a4fZ3dUtktF9AoGBAMqxh6tr
S0ao0rN4hc9I92gwPubD1+XQr9TJQEEqGv3g5O3BdfDrTvizfaeNArahrIqyO5SK
7iDg0xcHqdxmVmmCJ8CkIWBPXLU6erQ1QNlBJmnzYn5lR0Ksx2h/myjeXztvJKEM
q4xUjAEzWjmwxxU3Y6l3FokvgIU4kOVoE4JdAoGARfyZa+xizHnUPeAae/5yaclE
rnmdGma43En2KGQsyj7vHpEVaSDdW6nKWuRj9wKRMPkMafpQvxnOzjsD06EXZ4RV
bbN4mw7pVcj8B+wUuyAqoAmchMfya8dqXy+6SfkSXS4Sd4knNODkIPVAOqjoegcJ
/QtZamXbuYyGkjuCy3sCgYBLSUEFJ9ohjymwX/cvvAQfYmCBmTLu9b2mzmhSt94j
yI+Lgl8BtnxrANbmdjQ1NLxuB6+61IRVWtIP3kZnzj1aY4sbqq1PqHLkOkrVOFnq
S2YKGJJMND8KIur67ZFm84J1KUgej61uow9uKQRBUEnx8AByJOsdAwPZteys+sVq
7wKBgAc3BL6ttDVYlL8U8fgvTCGuIajItvOQQj1n8RKyRLblYXBKAtY+pbULzmF+
HscRgJMcwEIosEbTzzBNEVQm6dS6R/Q534C00Fpsw1z/PFTI8AOdUzTROGjuN8Fg
YZoqMQLhk/PB8V4l7yJmPrE971RmJBBDlLDt6hZwOYEI2yF4
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,49 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEAykcmQLTiSXwIT1OsROmV2OeuL8BKwCN592J+JUxy/W8Xr/SJcung
qjy3FdFm0ldOKc7Z4BZ/TI0+8m/JnF4Xh6SGlhB+evbDKZqTrcn5+KevEnHrlwRssjxjCH
Hzk5J5AEndSWEg+BD5Hfa2Zgs3Md7xJy7vAoZwPxtttcUlUOVQv4w/3PwoDVGsoUPsW5+e
6hyirWcH02ttUJsCGmxmCTcJVY5SkwnXXXlZ2Xyqiqxu3MR0AKbMXdta4TPSE/wv3+GP08
d1lFFXmptImduuaIjx6DwC6iSFkkCFIXkWAMWAEKicEkNDq/TDPF17p9iMt0VEhpOniL6w
rBWzVEbBzbOz7tFJ6WL+hWd7wpBXqf9aQE+n0L2O6QzjlgxylwKw+MGrXPYCFmkAR+98A1
vyoAXJzEUqBloEI7GKiGpj+KFdfh6lCN5UQLF2Wl/nogRrd/twOSnmz9u1CdWFMIcJc7O8
M0ymgrtDmi/oTrD9RdgMQGvfeW4BZLDHdt+uObsd2AiSlxuLBqSdWfx7OPSu9oL/kIy2Vc
V6mOtt3PPtzlIZYsaqBBXeIhu1DHOWuednIf1QPo2rKZi2aBYT8/fWgUVW9aAkj8UsC8Pk
P8Mb+qWZk/teK6k82Nn2q41Od4TPgrFD4e3Z1l4Rhu0bgOcAnXjlzmI7pLH8wKByGYT6k4
EAAAdQ3L5mFty+ZhYAAAAHc3NoLXJzYQAAAgEAykcmQLTiSXwIT1OsROmV2OeuL8BKwCN5
92J+JUxy/W8Xr/SJcungqjy3FdFm0ldOKc7Z4BZ/TI0+8m/JnF4Xh6SGlhB+evbDKZqTrc
n5+KevEnHrlwRssjxjCHHzk5J5AEndSWEg+BD5Hfa2Zgs3Md7xJy7vAoZwPxtttcUlUOVQ
v4w/3PwoDVGsoUPsW5+e6hyirWcH02ttUJsCGmxmCTcJVY5SkwnXXXlZ2Xyqiqxu3MR0AK
bMXdta4TPSE/wv3+GP08d1lFFXmptImduuaIjx6DwC6iSFkkCFIXkWAMWAEKicEkNDq/TD
PF17p9iMt0VEhpOniL6wrBWzVEbBzbOz7tFJ6WL+hWd7wpBXqf9aQE+n0L2O6Qzjlgxylw
Kw+MGrXPYCFmkAR+98A1vyoAXJzEUqBloEI7GKiGpj+KFdfh6lCN5UQLF2Wl/nogRrd/tw
OSnmz9u1CdWFMIcJc7O8M0ymgrtDmi/oTrD9RdgMQGvfeW4BZLDHdt+uObsd2AiSlxuLBq
SdWfx7OPSu9oL/kIy2VcV6mOtt3PPtzlIZYsaqBBXeIhu1DHOWuednIf1QPo2rKZi2aBYT
8/fWgUVW9aAkj8UsC8PkP8Mb+qWZk/teK6k82Nn2q41Od4TPgrFD4e3Z1l4Rhu0bgOcAnX
jlzmI7pLH8wKByGYT6k4EAAAADAQABAAACAEeOg+nAE40LY6UsZHS8bVYeH3ClBcySwELT
hOyM7uDYu/hy+Wy9b8zJTbtaKJWgbPY9RrYPP1lFXk9FXH0EjC5f9XyAuT2mrcO5+yQvn0
5ng3dy9XSnDAzBcAc8yH4cAtInTzD2O0OGPZpr/Hp83Tm3NHg4EjVCedLZUSZMZ7cGaFpa
svzp9wE/M2KZNLP087K+Do5pNEuGZVVugH/4eOApqBOsFWoOwTFADJjzkSEdftp6ZM8WMp
XBU5T3UAnh3M3GbartlJqza9o1tKk5Ham9SFZvZFiQMvBaAr6kpzP+qh86hnuvb/EU1Tw1
ldj6skzjJCq3cTzeuIEn7BiUL1qEECjpjx7OG6L9/lWyOy27nU/tiQ1MCUDMs/u/Kpnm8X
1kvEYzq1iEQVjqEaKHQBA35GB5krXzVLK2XNMYzZDM4+bGbffX04t4CpVbJHY7mFrbO584
JlqsCxKvhDDJpNuWhT4HUrAMPCJRvFC57n12+wjLrDsBKMOGRwo1NqnaR75QOR5gtboav+
6P/o35U/gATyiePDF3HD/213gJcOwRXpH9oFleRStqiU2OsfcULlrjD6gIXCAQOOrt4l15
uEh8fnUdFbgmFfuTapKHHm6nVGs6K0JWpqlqlsiwsJxSBwRhRFO3G/iAsmxKDvWaq1yBIJ
yhDRTeA7fyCw8k+wsBAAABAGeNiRwkVyn/iUA61drLC5Y/0Gd+a540VrLMwyV3LGlDZPH3
RQFHH+HldhLqmp2ikHZWFq36vjTDr/ubCuwQNlJo4TAo5/RQk1/ChBqXj2IdT+vBysH+bK
IuZQoWXsfISMfQ7o+F5vv7WdItS9w44HpXayH12Q8D1Qr4Qnt0CeMIhrrV7MPsGVTIOpOU
FxH4xu9ovBWDnyloC4rWkBmeAzLCFtO1V1iGN7Six/OXvnxnbif+BsfdQt+OxHIYBOue5G
+Dkss+1xR8l8xrZsOpN2uY1QFIaE6UyElFleAEhtYL2vvuXTrL3EJKqRtIcWenL/wxYlkt
X1CJQS02JW+PtNUAAAEBAPWFstL1hWK4Fp5ipJSGSkDNvGGuzamAYEgqr6n5Zzb1R1HPyE
x6uEMB7ELQjOG4FENIQYBBnBRnMOWWFJp0V5UjFKDft1FabLiozqBtLCRnHnIGllFIWJK+
u/h9OL4OWXGUJx2Em4XdJBPqp0g56VI237AsnTbTGS0tGLOErLWbQY7npZeBFct/501RTP
M5i7F0QEDLjEDZbDxvCz8a5tjfvyP1awK2SyluiE4VPeYr5Op1JNPTJMz5U3YFsIZxdZHJ
AK5mX8hNzTHpTApkS7o0DvExn5DVB8OHOQFdc+BjBIqQwa953f3FaAw9V3o6Dt1zXe9OJR
tBUiBeurvDFk0AAAEBANLpAv9NDf3c8k0PAYhwu+SWoo0OclOWQSZes/93UeB0ch57LD+v
KVPR3hw2AzAsgZn/PcMbIC3SPLNnAlNftfTa98avQOEfmYqrH499zVPuMb7fieS/eQZ4LF
rsZ0o+W4CDVmzImgOe1hENWcfSeUKajEwpgtj440mJlBls37G/zHMMe5DkA2TAxKldiHOR
fmHOtFKT3fnKFa6PWbwlLFnWIod4zheWrwV7E1coBhh+CA5SlgQANRFs7J8zxPznOtIK2O
cF2+/92cM0TijlBk8u5jnN44cUmk4V1nYboCVb0JD2B7yF9ZYP6IB03jt5GEZSfHHCrZP8
vCxMmXDxtAUAAAAXYWp2YW5lcnBASGVpbWRhbGwubG9jYWwBAgME
-----END OPENSSH PRIVATE KEY-----

View File

@@ -0,0 +1,15 @@
-----BEGIN DSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,9B6744BB12A8EA8F
pzZw5s3zDVHYdejZxdTpaRx00Yd1grbJe6mIJGvZRB0Jm0hKXoOX71PUI814mc5+
a5pzbyO98aciL/Eat5m3P692WQ0yOPMuphRnklsM3s4qrCjp2aRRbWvbyV/QV9bp
Xz2yYvNqU3WJC3UJIaFFMvRo/lC/Wsz9OvHSSl3LnsXXhiOCeaE32etoOYdlk9ro
N9NqDdaw28t9//iiHhuQK4afK6TZkU6DatFljJHILCC416Xh9+DDK9E+CDKzmlcw
jSwtzgFKEhgrT0XKoZR9LJZDolT1YpFy7M3cFRYIuYvJfuLcjxVEldJE900QlaJS
ybb6RxV6SRVwQYXTbIClcXes+oNJMv59DivAfajxECQC5sAynW/FnY1sz0igmz6D
scclJuJIbawqiuV/Lv6bvgzMa/ZXL4b9JeJPuQELa7tCpvj4fpNk1IiftYISlwoT
iG5pL8yLhPL4/fxGnKJzUPCA9mbwiloW2cAZZxTd+Utqmhemcl9gF0JGNR2XeYwS
3obEjbkqMF0WR3AcVZU9B5d9SKUaAzTp4vu5yZtNVEIaiVlnI3hMwWMs2Jgahswo
QF9MCPsRYsxLs7/u4a4qoQ==
-----END DSA PRIVATE KEY-----

View File

@@ -15,10 +15,11 @@
*/
package com.hierynomus.sshj.backport;
import net.schmizz.sshj.common.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.nio.charset.Charset;
public class Jdk7HttpProxySocket extends Socket {
@@ -48,7 +49,7 @@ public class Jdk7HttpProxySocket extends Socket {
}
InetSocketAddress isa = (InetSocketAddress) endpoint;
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
getOutputStream().write(httpConnect.getBytes(IOUtils.UTF8));
checkAndFlushProxyResponse();
}
@@ -61,7 +62,7 @@ public class Jdk7HttpProxySocket extends Socket {
throw new SocketException("Empty response from proxy");
}
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
String proxyResponse = new String(tmpBuffer, 0, len, IOUtils.UTF8);
// Expecting HTTP/1.x 200 OK
if (proxyResponse.contains("200")) {

View File

@@ -32,7 +32,7 @@ public class Ed25519PublicKey extends EdDSAPublicKey {
public Ed25519PublicKey(EdDSAPublicKeySpec spec) {
super(spec);
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
if (!spec.getParams().getCurve().equals(ed25519.getCurve())) {
throw new SSHRuntimeException("Cannot create Ed25519 Public Key from wrong spec");
}

View File

@@ -16,14 +16,16 @@
package com.hierynomus.sshj.signature;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.signature.AbstractSignature;
import net.schmizz.sshj.signature.Signature;
import java.security.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
public class SignatureEdDSA implements Signature {
public class SignatureEdDSA extends AbstractSignature {
public static class Factory implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
@@ -37,54 +39,18 @@ public class SignatureEdDSA implements Signature {
}
}
final EdDSAEngine engine;
SignatureEdDSA() {
super(getEngine());
}
protected SignatureEdDSA() {
private static EdDSAEngine getEngine() {
try {
engine = new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
return new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
} catch (NoSuchAlgorithmException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public void init(PublicKey pubkey, PrivateKey prvkey) {
try {
if (pubkey != null) {
engine.initVerify(pubkey);
}
if (prvkey != null) {
engine.initSign(prvkey);
}
} catch (InvalidKeyException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public void update(byte[] H) {
update(H, 0, H.length);
}
@Override
public void update(byte[] H, int off, int len) {
try {
engine.update(H, off, len);
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public byte[] sign() {
try {
return engine.sign();
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public byte[] encode(byte[] signature) {
return signature;
@@ -93,17 +59,9 @@ public class SignatureEdDSA implements Signature {
@Override
public boolean verify(byte[] sig) {
try {
Buffer.PlainBuffer plainBuffer = new Buffer.PlainBuffer(sig);
String algo = plainBuffer.readString();
if (!"ssh-ed25519".equals(algo)) {
throw new SSHRuntimeException("Expected 'ssh-ed25519' key algorithm, but was: " + algo);
}
byte[] bytes = plainBuffer.readBytes();
return engine.verify(bytes);
return signature.verify(extractSig(sig, "ssh-ed25519"));
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
} catch (Buffer.BufferException e) {
throw new SSHRuntimeException(e);
}
}
}

View File

@@ -62,8 +62,11 @@ public class IdentificationStringParser {
}
}
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) {
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException {
byte[] bytes = new byte[lineBuffer.available()];
lineBuffer.readRawBytes(bytes);
String header = new String(bytes, 0, bytes.length - 1);
log.debug("Received header: {}", header);
}
private String readIdentification(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException, TransportException {

View File

@@ -19,23 +19,46 @@ import net.schmizz.sshj.transport.cipher.BlockCipher;
import net.schmizz.sshj.transport.cipher.Cipher;
/**
* All BlockCiphers supported by SSH according to the following RFCs
* All BlockCiphers supported by SSH according to the following RFCs:
*
* - https://tools.ietf.org/html/rfc4344#section-3.1
* - https://tools.ietf.org/html/rfc4253#section-6.3
* <ul>
* <li>https://tools.ietf.org/html/rfc4344#section-3.1</li>
* <li>https://tools.ietf.org/html/rfc4253#section-6.3</li>
* <li>TODO: https://tools.ietf.org/html/rfc5647</li>
* </ul>
*
* TODO: https://tools.ietf.org/html/rfc5647
*
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here.
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are deprecated and scheduled to be removed.
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class BlockCiphers {
public static final String COUNTER_MODE = "CTR";
public static final String CIPHER_BLOCK_CHAINING_MODE = "CBC";
public static Factory AES128CTR() {
return new Factory(16, 128, "aes128-ctr", "AES", COUNTER_MODE);
}
public static Factory AES192CTR() {
return new Factory(16, 192, "aes192-ctr", "AES", COUNTER_MODE);
}
public static Factory AES256CTR() {
return new Factory(16, 256, "aes256-ctr", "AES", COUNTER_MODE);
}
public static Factory AES128CBC() {
return new Factory(16, 128, "aes128-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory AES192CBC() {
return new Factory(16, 192, "aes192-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory AES256CBC() {
return new Factory(16, 256, "aes256-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory BlowfishCTR() {
return new Factory(8, 256, "blowfish-ctr", "Blowfish", COUNTER_MODE);
}
public static Factory BlowfishCBC() {
return new Factory(8, 128, "blowfish-cbc", "Blowfish", CIPHER_BLOCK_CHAINING_MODE);
}
public static Factory Twofish128CTR() {
return new Factory(16, 128, "twofish128-ctr", "Twofish", COUNTER_MODE);
}
@@ -90,6 +113,9 @@ public class BlockCiphers {
public static Factory TripleDESCTR() {
return new Factory(8, 192, "3des-ctr", "DESede", COUNTER_MODE);
}
public static Factory TripleDESCBC() {
return new Factory(8, 192, "3des-cbc", "DESede", CIPHER_BLOCK_CHAINING_MODE);
}
/** Named factory for BlockCipher */
public static class Factory

View File

@@ -25,6 +25,7 @@ import static com.hierynomus.sshj.transport.cipher.BlockCiphers.COUNTER_MODE;
*
* - http://tools.ietf.org/id/draft-kanno-secsh-camellia-01.txt
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class ExtendedBlockCiphers {
public static BlockCiphers.Factory Camellia128CTR() {
return new BlockCiphers.Factory(16, 128, "camellia128-ctr", "Camellia", COUNTER_MODE);

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.transport.cipher.BaseCipher;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
public class StreamCipher extends BaseCipher {
@@ -29,6 +28,6 @@ public class StreamCipher extends BaseCipher {
@Override
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
cipher.init(getMode(mode), getKeySpec(key), new SecureRandom());
cipher.init(getMode(mode), getKeySpec(key));
}
}

View File

@@ -23,6 +23,7 @@ import net.schmizz.sshj.transport.cipher.Cipher;
* - https://tools.ietf.org/html/rfc4253#section-6.3
* - https://tools.ietf.org/html/rfc4345
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class StreamCiphers {
public static Factory Arcfour() {

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.kex.AbstractDHG;
import net.schmizz.sshj.transport.kex.DH;
import net.schmizz.sshj.transport.kex.DHBase;
import javax.crypto.spec.DHParameterSpec;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
/**
*
*/
public class DHG extends AbstractDHG {
private BigInteger group;
private BigInteger generator;
public DHG(BigInteger group, BigInteger generator, Digest digest) {
super(new DH(), digest);
this.group = group;
this.generator = generator;
}
@Override
protected void initDH(DHBase dh) throws GeneralSecurityException {
dh.init(new DHParameterSpec(group, generator), trans.getConfig().getRandomFactory());
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.kex;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.SHA1;
import net.schmizz.sshj.transport.digest.SHA256;
import net.schmizz.sshj.transport.digest.SHA512;
import net.schmizz.sshj.transport.kex.KeyExchange;
import java.math.BigInteger;
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
/**
* Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups
*
* - https://tools.ietf.org/html/rfc4253
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class DHGroups {
public static DHGroups.Factory Group1SHA1() {
return new DHGroups.Factory("diffie-hellman-group1-sha1", P1, G, new SHA1.Factory());
}
public static DHGroups.Factory Group14SHA1() {
return new DHGroups.Factory("diffie-hellman-group14-sha1", P14, G, new SHA1.Factory());
}
public static DHGroups.Factory Group14SHA256() {
return new DHGroups.Factory("diffie-hellman-group14-sha256", P14, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA512() {
return new DHGroups.Factory("diffie-hellman-group15-sha512", P15, G, new SHA512.Factory());
}
public static DHGroups.Factory Group16SHA512() {
return new DHGroups.Factory("diffie-hellman-group16-sha512", P16, G, new SHA512.Factory());
}
public static DHGroups.Factory Group17SHA512() {
return new DHGroups.Factory("diffie-hellman-group17-sha512", P17, G, new SHA512.Factory());
}
public static DHGroups.Factory Group18SHA512() {
return new DHGroups.Factory("diffie-hellman-group18-sha512", P18, G, new SHA512.Factory());
}
/**
* Named factory for DHG1 key exchange
*/
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
private String name;
private BigInteger group;
private BigInteger generator;
private Factory.Named<Digest> digestFactory;
public Factory(String name, BigInteger group, BigInteger generator, Named<Digest> digestFactory) {
this.name = name;
this.group = group;
this.generator = generator;
this.digestFactory = digestFactory;
}
@Override
public KeyExchange create() {
return new DHG(group, generator, digestFactory.create());
}
@Override
public String getName() {
return name;
}
}
}

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.transport.kex;
import net.schmizz.sshj.transport.digest.SHA256;
import net.schmizz.sshj.transport.digest.SHA384;
import net.schmizz.sshj.transport.digest.SHA512;
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
/**
* Set of KEX methods that are not in official RFCs but are supported by some SSH servers.
*/
@SuppressWarnings("PMD.MethodNamingConventions")
public class ExtendedDHGroups {
public static DHGroups.Factory Group14SHA256AtSSH() {
return new DHGroups.Factory("diffie-hellman-group14-sha256@ssh.com", P14, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA256() {
return new DHGroups.Factory("diffie-hellman-group15-sha256", P15, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA256AtSSH() {
return new DHGroups.Factory("diffie-hellman-group15-sha256@ssh.com", P15, G, new SHA256.Factory());
}
public static DHGroups.Factory Group15SHA384AtSSH() {
return new DHGroups.Factory("diffie-hellman-group15-sha384@ssh.com", P15, G, new SHA384.Factory());
}
public static DHGroups.Factory Group16SHA256() {
return new DHGroups.Factory("diffie-hellman-group16-sha256", P16, G, new SHA256.Factory());
}
public static DHGroups.Factory Group16SHA384AtSSH() {
return new DHGroups.Factory("diffie-hellman-group16-sha384@ssh.com", P16, G, new SHA384.Factory());
}
public static DHGroups.Factory Group16SHA512AtSSH() {
return new DHGroups.Factory("diffie-hellman-group16-sha512@ssh.com", P16, G, new SHA512.Factory());
}
public static DHGroups.Factory Group18SHA512AtSSH() {
return new DHGroups.Factory("diffie-hellman-group18-sha512@ssh.com", P18, G, new SHA512.Factory());
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.mac;
import net.schmizz.sshj.transport.mac.BaseMAC;
import net.schmizz.sshj.transport.mac.MAC;
@SuppressWarnings("PMD.MethodNamingConventions")
public class Macs {
public static Factory HMACMD5() {
return new Factory("hmac-md5", "HmacMD5", 16, 16, false);
}
public static Factory HMACMD596() {
return new Factory("hmac-md5-96", "HmacMD5", 12, 16, false);
}
public static Factory HMACMD5Etm() {
return new Factory("hmac-md5-etm@openssh.com", "HmacMD5", 16, 16, true);
}
public static Factory HMACMD596Etm() {
return new Factory("hmac-md5-96-etm@openssh.com", "HmacMD5", 12, 16, true);
}
public static Factory HMACRIPEMD160() {
return new Factory("hmac-ripemd160", "HMACRIPEMD160", 20, 20, false);
}
public static Factory HMACRIPEMD16096() {
return new Factory("hmac-ripemd160-96", "HMACRIPEMD160", 12, 20, false);
}
public static Factory HMACRIPEMD160Etm() {
return new Factory("hmac-ripemd160-etm@openssh.com", "HMACRIPEMD160", 20, 20, true);
}
public static Factory HMACRIPEMD160OpenSsh() {
return new Factory("hmac-ripemd160@openssh.com", "HMACRIPEMD160", 20, 20, false);
}
public static Factory HMACSHA1() {
return new Factory("hmac-sha1", "HmacSHA1", 20, 20, false);
}
public static Factory HMACSHA196() {
return new Factory("hmac-sha1-96", "HmacSHA1", 12, 20, false);
}
public static Factory HMACSHA1Etm() {
return new Factory("hmac-sha1-etm@openssh.com", "HmacSHA1", 20, 20, true);
}
public static Factory HMACSHA196Etm() {
return new Factory("hmac-sha1-96@openssh.com", "HmacSHA1", 12, 20, true);
}
public static Factory HMACSHA2256() {
return new Factory("hmac-sha2-256", "HmacSHA256", 32, 32, false);
}
public static Factory HMACSHA2256Etm() {
return new Factory("hmac-sha2-256-etm@openssh.com", "HmacSHA256", 32, 32, true);
}
public static Factory HMACSHA2512() {
return new Factory("hmac-sha2-512", "HmacSHA512", 64, 64, false);
}
public static Factory HMACSHA2512Etm() {
return new Factory("hmac-sha2-512-etm@openssh.com", "HmacSHA512", 64, 64, true);
}
private static class Factory implements net.schmizz.sshj.common.Factory.Named<MAC> {
private String name;
private String algorithm;
private int bSize;
private int defBSize;
private final boolean etm;
public Factory(String name, String algorithm, int bSize, int defBSize, boolean etm) {
this.name = name;
this.algorithm = algorithm;
this.bSize = bSize;
this.defBSize = defBSize;
this.etm = etm;
}
@Override
public String getName() {
return name;
}
@Override
public BaseMAC create() {
return new BaseMAC(algorithm, bSize, defBSize, etm);
}
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.transport.verification;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.transport.mac.HMACSHA1;
import net.schmizz.sshj.transport.mac.MAC;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class KnownHostMatchers {
public static HostMatcher createMatcher(String hostEntry) throws SSHException {
if (hostEntry.contains(",")) {
return new AnyHostMatcher(hostEntry);
}
if (hostEntry.startsWith("!")) {
return new NegateHostMatcher(hostEntry);
}
if (hostEntry.startsWith("|1|")) {
return new HashedHostMatcher(hostEntry);
}
if (hostEntry.contains("*") || hostEntry.contains("?")) {
return new WildcardHostMatcher(hostEntry);
}
return new EquiHostMatcher(hostEntry);
}
public interface HostMatcher {
boolean match(String hostname) throws IOException;
}
private static class EquiHostMatcher implements HostMatcher {
private String host;
public EquiHostMatcher(String host) {
this.host = host;
}
@Override
public boolean match(String hostname) {
return host.equals(hostname);
}
}
private static class HashedHostMatcher implements HostMatcher {
private final MAC sha1 = new HMACSHA1();
private final String hash;
private final String salt;
private byte[] saltyBytes;
HashedHostMatcher(String hash) throws SSHException {
this.hash = hash;
final String[] hostParts = hash.split("\\|");
if (hostParts.length != 4) {
throw new SSHException("Unrecognized format for hashed hostname");
}
salt = hostParts[2];
}
@Override
public boolean match(String hostname) throws IOException {
return hash.equals(hashHost(hostname));
}
private String hashHost(String host) throws IOException {
sha1.init(getSaltyBytes());
return "|1|" + salt + "|" + Base64.encodeBytes(sha1.doFinal(host.getBytes(IOUtils.UTF8)));
}
private byte[] getSaltyBytes() throws IOException {
if (saltyBytes == null) {
saltyBytes = Base64.decode(salt);
}
return saltyBytes;
}
}
private static class AnyHostMatcher implements HostMatcher {
private final List<HostMatcher> matchers;
AnyHostMatcher(String hostEntry) throws SSHException {
matchers = new ArrayList<HostMatcher>();
for (String subEntry : hostEntry.split(",")) {
matchers.add(KnownHostMatchers.createMatcher(subEntry));
}
}
@Override
public boolean match(String hostname) throws IOException {
for (HostMatcher matcher : matchers) {
if (matcher.match(hostname)) {
return true;
}
}
return false;
}
}
private static class NegateHostMatcher implements HostMatcher {
private final HostMatcher matcher;
NegateHostMatcher(String hostEntry) throws SSHException {
this.matcher = createMatcher(hostEntry.substring(1));
}
@Override
public boolean match(String hostname) throws IOException {
return !matcher.match(hostname);
}
}
private static class WildcardHostMatcher implements HostMatcher {
private final Pattern pattern;
public WildcardHostMatcher(String hostEntry) {
this.pattern = Pattern.compile("^" + hostEntry.replace("[", "\\[").replace("]", "\\]").replace(".", "\\.").replace("*", ".*").replace("?", ".") + "$");
}
@Override
public boolean match(String hostname) throws IOException {
return pattern.matcher(hostname).matches();
}
@Override
public String toString() {
return "WildcardHostMatcher[" + pattern + ']';
}
}
}

View File

@@ -0,0 +1,258 @@
package com.hierynomus.sshj.userauth.certificate;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.Date;
import java.util.List;
import java.util.Map;
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Certificate wrapper for public keys, created to help implement
* protocol described here:
*
* https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
*
* Consumed primarily by net.shmizz.sshj.common.KeyType
*
* @param <T> inner public key type
*/
public class Certificate<T extends PublicKey> implements PublicKey {
private static final long serialVersionUID = 1L;
private final T publicKey;
private final byte[] nonce;
private final BigInteger serial;
private final long type;
private final String id;
private final List<String> validPrincipals;
private final Date validAfter;
private final Date validBefore;
private final Map<String, String> critOptions;
private final Map<String, String> extensions;
private final byte[] signatureKey;
private final byte[] signature;
Certificate(Builder<T> builder) {
this.publicKey = builder.getPublicKey();
this.nonce = builder.getNonce();
this.serial = builder.getSerial();
this.type = builder.getType();
this.id = builder.getId();
this.validPrincipals = builder.getValidPrincipals();
this.validAfter = builder.getValidAfter();
this.validBefore = builder.getValidBefore();
this.critOptions = builder.getCritOptions();
this.extensions = builder.getExtensions();
this.signatureKey = builder.getSignatureKey();
this.signature = builder.getSignature();
}
public static <P extends PublicKey> Builder<P> getBuilder() {
return new Builder<P>();
}
public byte[] getNonce() {
return nonce;
}
public BigInteger getSerial() {
return serial;
}
public long getType() {
return type;
}
public String getId() {
return id;
}
public List<String> getValidPrincipals() {
return validPrincipals;
}
public Date getValidAfter() {
return validAfter;
}
public Date getValidBefore() {
return validBefore;
}
public Map<String, String> getCritOptions() {
return critOptions;
}
public Map<String, String> getExtensions() {
return extensions;
}
public byte[] getSignatureKey() {
return signatureKey;
}
public byte[] getSignature() {
return signature;
}
public T getKey() {
return publicKey;
}
@Override
public byte[] getEncoded() {
return publicKey.getEncoded();
}
@Override
public String getAlgorithm() {
return publicKey.getAlgorithm();
}
@Override
public String getFormat() {
return publicKey.getFormat();
}
public static class Builder<T extends PublicKey> {
private T publicKey;
private byte[] nonce;
private BigInteger serial;
private long type;
private String id;
private List<String> validPrincipals;
private Date validAfter;
private Date validBefore;
private Map<String, String> critOptions;
private Map<String, String> extensions;
private byte[] signatureKey;
private byte[] signature;
public Certificate<T> build() {
return new Certificate<T>(this);
}
public T getPublicKey() {
return publicKey;
}
public Builder<T> publicKey(T publicKey) {
this.publicKey = publicKey;
return this;
}
public byte[] getNonce() {
return nonce;
}
public Builder<T> nonce(byte[] nonce) {
this.nonce = nonce;
return this;
}
public BigInteger getSerial() {
return serial;
}
public Builder<T> serial(BigInteger serial) {
this.serial = serial;
return this;
}
public long getType() {
return type;
}
public Builder<T> type(long type) {
this.type = type;
return this;
}
public String getId() {
return id;
}
public Builder<T> id(String id) {
this.id = id;
return this;
}
public List<String> getValidPrincipals() {
return validPrincipals;
}
public Builder<T> validPrincipals(List<String> validPrincipals) {
this.validPrincipals = validPrincipals;
return this;
}
public Date getValidAfter() {
return validAfter;
}
public Builder<T> validAfter(Date validAfter) {
this.validAfter = validAfter;
return this;
}
public Date getValidBefore() {
return validBefore;
}
public Builder<T> validBefore(Date validBefore) {
this.validBefore = validBefore;
return this;
}
public Map<String, String> getCritOptions() {
return critOptions;
}
public Builder<T> critOptions(Map<String, String> critOptions) {
this.critOptions = critOptions;
return this;
}
public Map<String, String> getExtensions() {
return extensions;
}
public Builder<T> extensions(Map<String, String> extensions) {
this.extensions = extensions;
return this;
}
public byte[] getSignatureKey() {
return signatureKey;
}
public Builder<T> signatureKey(byte[] signatureKey) {
this.signatureKey = signatureKey;
return this;
}
public byte[] getSignature() {
return signature;
}
public Builder<T> signature(byte[] signature) {
this.signature = signature;
return this;
}
}
}

View File

@@ -0,0 +1,246 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.userauth.keyprovider;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.common.Buffer.PlainBuffer;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Arrays;
/**
* Reads a key file in the new OpenSSH format.
* The format is described in the following document: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
*/
public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
private static final Logger logger = LoggerFactory.getLogger(OpenSSHKeyV1KeyFile.class);
private static final String BEGIN = "-----BEGIN ";
private static final String END = "-----END ";
private static final byte[] AUTH_MAGIC = "openssh-key-v1\0".getBytes();
public static final String OPENSSH_PRIVATE_KEY = "OPENSSH PRIVATE KEY-----";
public static final String BCRYPT = "bcrypt";
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@Override
public FileKeyProvider create() {
return new OpenSSHKeyV1KeyFile();
}
@Override
public String getName() {
return KeyFormat.OpenSSHv1.name();
}
}
@Override
protected KeyPair readKeyPair() throws IOException {
BufferedReader reader = new BufferedReader(resource.getReader());
try {
if (!checkHeader(reader)) {
throw new IOException("This key is not in 'openssh-key-v1' format");
}
String keyFile = readKeyFile(reader);
byte[] decode = Base64.decode(keyFile);
PlainBuffer keyBuffer = new PlainBuffer(decode);
return readDecodedKeyPair(keyBuffer);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
} finally {
IOUtils.closeQuietly(reader);
}
}
private KeyPair readDecodedKeyPair(final PlainBuffer keyBuffer) throws IOException, GeneralSecurityException {
byte[] bytes = new byte[AUTH_MAGIC.length];
keyBuffer.readRawBytes(bytes); // byte[] AUTH_MAGIC
if (!ByteArrayUtils.equals(bytes, 0, AUTH_MAGIC, 0, AUTH_MAGIC.length)) {
throw new IOException("This key does not contain the 'openssh-key-v1' format magic header");
}
String cipherName = keyBuffer.readString(); // string ciphername
String kdfName = keyBuffer.readString(); // string kdfname
byte[] kdfOptions = keyBuffer.readBytes(); // string kdfoptions
int nrKeys = keyBuffer.readUInt32AsInt(); // int number of keys N; Should be 1
if (nrKeys != 1) {
throw new IOException("We don't support having more than 1 key in the file (yet).");
}
PublicKey publicKey = readPublicKey(new PlainBuffer(keyBuffer.readBytes())); // string publickey1
PlainBuffer privateKeyBuffer = new PlainBuffer(keyBuffer.readBytes()); // string (possibly) encrypted, padded list of private keys
if ("none".equals(cipherName)) {
logger.debug("Reading unencrypted keypair");
return readUnencrypted(privateKeyBuffer, publicKey);
} else {
logger.info("Keypair is encrypted with: " + cipherName + ", " + kdfName + ", " + Arrays.toString(kdfOptions));
PlainBuffer decrypted = decryptBuffer(privateKeyBuffer, cipherName, kdfName, kdfOptions);
return readUnencrypted(decrypted, publicKey);
// throw new IOException("Cannot read encrypted keypair with " + cipherName + " yet.");
}
}
private PlainBuffer decryptBuffer(PlainBuffer privateKeyBuffer, String cipherName, String kdfName, byte[] kdfOptions) throws IOException {
Cipher cipher = createCipher(cipherName);
initializeCipher(kdfName, kdfOptions, cipher);
byte[] array = privateKeyBuffer.array();
cipher.update(array, 0, privateKeyBuffer.available());
return new PlainBuffer(array);
}
private void initializeCipher(String kdfName, byte[] kdfOptions, Cipher cipher) throws Buffer.BufferException {
if (kdfName.equals(BCRYPT)) {
PlainBuffer opts = new PlainBuffer(kdfOptions);
byte[] passphrase = new byte[0];
if (pwdf != null) {
CharBuffer charBuffer = CharBuffer.wrap(pwdf.reqPassword(null));
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
passphrase = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
}
byte[] keyiv = new byte[48];
new BCrypt().pbkdf(passphrase, opts.readBytes(), opts.readUInt32AsInt(), keyiv);
byte[] key = Arrays.copyOfRange(keyiv, 0, 32);
byte[] iv = Arrays.copyOfRange(keyiv, 32, 48);
cipher.init(Cipher.Mode.Decrypt, key, iv);
} else {
throw new IllegalStateException("No support for KDF '" + kdfName + "'.");
}
}
private Cipher createCipher(String cipherName) {
if (cipherName.equals(BlockCiphers.AES256CTR().getName())) {
return BlockCiphers.AES256CTR().create();
} else if (cipherName.equals(BlockCiphers.AES256CBC().getName())) {
return BlockCiphers.AES256CBC().create();
}
throw new IllegalStateException("Cipher '" + cipherName + "' not currently implemented for openssh-key-v1 format");
}
private PublicKey readPublicKey(final PlainBuffer plainBuffer) throws Buffer.BufferException, GeneralSecurityException {
return KeyType.fromString(plainBuffer.readString()).readPubKeyFromBuffer(plainBuffer);
}
private String readKeyFile(final BufferedReader reader) throws IOException {
StringBuilder sb = new StringBuilder();
String line = reader.readLine();
while (!line.startsWith(END)) {
sb.append(line);
line = reader.readLine();
}
return sb.toString();
}
private boolean checkHeader(final BufferedReader reader) throws IOException {
String line = reader.readLine();
while (line != null && !line.startsWith(BEGIN)) {
line = reader.readLine();
}
line = line.substring(BEGIN.length());
return line.startsWith(OPENSSH_PRIVATE_KEY);
}
private KeyPair readUnencrypted(final PlainBuffer keyBuffer, final PublicKey publicKey) throws IOException, GeneralSecurityException {
int privKeyListSize = keyBuffer.available();
if (privKeyListSize % 8 != 0) {
throw new IOException("The private key section must be a multiple of the block size (8)");
}
int checkInt1 = keyBuffer.readUInt32AsInt(); // uint32 checkint1
int checkInt2 = keyBuffer.readUInt32AsInt(); // uint32 checkint2
if (checkInt1 != checkInt2) {
throw new IOException("The checkInts differed, the key was not correctly decoded.");
}
// The private key section contains both the public key and the private key
String keyType = keyBuffer.readString(); // string keytype
KeyType kt = KeyType.fromString(keyType);
logger.info("Read key type: {}", keyType, kt);
KeyPair kp;
switch (kt) {
case ED25519:
byte[] pubKey = keyBuffer.readBytes(); // string publickey (again...)
keyBuffer.readUInt32(); // length of privatekey+publickey
byte[] privKey = new byte[32];
keyBuffer.readRawBytes(privKey); // string privatekey
keyBuffer.readRawBytes(new byte[32]); // string publickey (again...)
kp = new KeyPair(publicKey, new EdDSAPrivateKey(new EdDSAPrivateKeySpec(privKey, EdDSANamedCurveTable.getByName("Ed25519"))));
break;
case RSA:
BigInteger n = keyBuffer.readMPInt(); // Modulus
BigInteger e = keyBuffer.readMPInt(); // Public Exponent
BigInteger d = keyBuffer.readMPInt(); // Private Exponent
keyBuffer.readMPInt(); // iqmp (q^-1 mod p)
keyBuffer.readMPInt(); // p (Prime 1)
keyBuffer.readMPInt(); // q (Prime 2)
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(n, d)));
break;
case ECDSA256:
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-256"));
break;
case ECDSA384:
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-384"));
break;
case ECDSA521:
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-521"));
break;
default:
throw new IOException("Cannot decode keytype " + keyType + " in openssh-key-v1 files (yet).");
}
String comment = keyBuffer.readString(); // string comment
byte[] padding = new byte[keyBuffer.available()];
keyBuffer.readRawBytes(padding); // char[] padding
for (int i = 0; i < padding.length; i++) {
if ((int) padding[i] != i + 1) {
throw new IOException("Padding of key format contained wrong byte at position: " + i);
}
}
return kp;
}
private PrivateKey createECDSAPrivateKey(KeyType kt, PlainBuffer buffer, String name) throws GeneralSecurityException, Buffer.BufferException {
PublicKey pk = kt.readPubKeyFromBuffer(buffer); // Public key
BigInteger s = new BigInteger(1, buffer.readBytes());
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec);
return SecurityUtils.getKeyFactory("ECDSA").generatePrivate(pks);
}
}

View File

@@ -15,11 +15,11 @@
*/
package net.schmizz.concurrent;
import net.schmizz.sshj.common.LoggerFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import net.schmizz.sshj.common.LoggerFactory;
/**
* An event can be set, cleared, or awaited, similar to Python's {@code threading.event}. The key difference is that a
* waiter may be delivered an exception of parameterized type {@code T}.

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.concurrent;
import net.schmizz.sshj.common.LoggerFactory;
import org.slf4j.Logger;
import java.util.concurrent.TimeUnit;
@@ -22,8 +23,6 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import net.schmizz.sshj.common.LoggerFactory;
/**
* Represents promised data of the parameterized type {@code V} and allows waiting on it. An exception may also be
* delivered to a waiter, and will be of the parameterized type {@code T}.

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class KeepAlive extends Thread {
protected final Logger log;
@@ -65,6 +64,8 @@ public abstract class KeepAlive extends Thread {
}
Thread.sleep(hi * 1000);
}
} catch (InterruptedException e) {
// Interrupt signal may be catched when sleeping.
} catch (Exception e) {
// If we weren't interrupted, kill the transport, then this exception was unexpected.
// Else we're in shutdown-mode already, so don't forcibly kill the transport.

View File

@@ -15,12 +15,17 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.signature.SignatureEdDSA;
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;
/**
* Registers SpongyCastle as JCE provider.
*/
public class AndroidConfig
extends DefaultConfig {
@@ -30,7 +35,9 @@ public class AndroidConfig
// don't add ECDSA
protected void initSignatureFactories() {
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory());
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
// but add EdDSA
new SignatureEdDSA.Factory());
}
@Override

View File

@@ -16,8 +16,8 @@
package net.schmizz.sshj;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;

View File

@@ -18,6 +18,10 @@ package net.schmizz.sshj;
import com.hierynomus.sshj.signature.SignatureEdDSA;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
import com.hierynomus.sshj.transport.kex.DHGroups;
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
import com.hierynomus.sshj.transport.mac.Macs;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
@@ -27,20 +31,22 @@ import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.cipher.*;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.kex.*;
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
import net.schmizz.sshj.transport.kex.DHGexSHA1;
import net.schmizz.sshj.transport.kex.DHGexSHA256;
import net.schmizz.sshj.transport.kex.ECDHNistP;
import net.schmizz.sshj.transport.mac.*;
import net.schmizz.sshj.transport.random.BouncyCastleRandom;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
import net.schmizz.sshj.userauth.keyprovider.PKCS5KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import org.slf4j.Logger;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.io.IOException;
import java.util.*;
/**
* A {@link net.schmizz.sshj.Config} that is initialized as follows. Items marked with an asterisk are added to the config only if
@@ -48,9 +54,7 @@ import java.util.List;
* <p/>
* <ul>
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyExchangeFactories Key exchange}: {@link net.schmizz.sshj.transport.kex.DHG14}*, {@link net.schmizz.sshj.transport.kex.DHG1}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setCipherFactories Ciphers} [1]: {@link net.schmizz.sshj.transport.cipher.AES128CTR}, {@link net.schmizz.sshj.transport.cipher.AES192CTR}, {@link net.schmizz.sshj.transport.cipher.AES256CTR},
* {@link
* net.schmizz.sshj.transport.cipher.AES128CBC}, {@link net.schmizz.sshj.transport.cipher.AES192CBC}, {@link net.schmizz.sshj.transport.cipher.AES256CBC}, {@link net.schmizz.sshj.transport.cipher.AES192CBC}, {@link net.schmizz.sshj.transport.cipher.TripleDESCBC}, {@link net.schmizz.sshj.transport.cipher.BlowfishCBC}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setCipherFactories Ciphers}: {@link BlockCiphers}, {@link StreamCiphers} [1]</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setMACFactories MAC}: {@link net.schmizz.sshj.transport.mac.HMACSHA1}, {@link net.schmizz.sshj.transport.mac.HMACSHA196}, {@link net.schmizz.sshj.transport.mac.HMACMD5}, {@link
* net.schmizz.sshj.transport.mac.HMACMD596}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setCompressionFactories Compression}: {@link net.schmizz.sshj.transport.compression.NoneCompression}</li>
@@ -67,13 +71,11 @@ import java.util.List;
public class DefaultConfig
extends ConfigImpl {
private static final String VERSION = "SSHJ_0_17_2";
private Logger log;
public DefaultConfig() {
setLoggerFactory(LoggerFactory.DEFAULT);
setVersion(VERSION);
setVersion(readVersionFromProperties());
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
initKeyExchangeFactories(bouncyCastleRegistered);
initRandomFactory(bouncyCastleRegistered);
@@ -85,6 +87,18 @@ public class DefaultConfig
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
}
private String readVersionFromProperties() {
try {
Properties properties = new Properties();
properties.load(DefaultConfig.class.getClassLoader().getResourceAsStream("sshj.properties"));
String property = properties.getProperty("sshj.version");
return "SSHJ_" + property.replace('-', '_'); // '-' is a disallowed character, see RFC-4253#section-4.2
} catch (Exception e) {
log.error("Could not read the sshj.properties file, returning an 'unknown' version as fallback.");
return "SSHJ_VERSION_UNKNOWN";
}
}
@Override
public void setLoggerFactory(LoggerFactory loggerFactory) {
super.setLoggerFactory(loggerFactory);
@@ -93,16 +107,31 @@ public class DefaultConfig
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered) {
setKeyExchangeFactories(new Curve25519SHA256.Factory(),
setKeyExchangeFactories(
new Curve25519SHA256.Factory(),
new Curve25519SHA256.FactoryLibSsh(),
new DHGexSHA256.Factory(),
new ECDHNistP.Factory521(),
new ECDHNistP.Factory384(),
new ECDHNistP.Factory256(),
new DHGexSHA1.Factory(),
new DHG14.Factory(),
new DHG1.Factory());
DHGroups.Group1SHA1(),
DHGroups.Group14SHA1(),
DHGroups.Group14SHA256(),
DHGroups.Group15SHA512(),
DHGroups.Group16SHA512(),
DHGroups.Group17SHA512(),
DHGroups.Group18SHA512(),
ExtendedDHGroups.Group14SHA256AtSSH(),
ExtendedDHGroups.Group15SHA256(),
ExtendedDHGroups.Group15SHA256AtSSH(),
ExtendedDHGroups.Group15SHA384AtSSH(),
ExtendedDHGroups.Group16SHA256(),
ExtendedDHGroups.Group16SHA384AtSSH(),
ExtendedDHGroups.Group16SHA512AtSSH(),
ExtendedDHGroups.Group18SHA512AtSSH());
} else {
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
}
}
@@ -113,21 +142,25 @@ public class DefaultConfig
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered) {
setFileKeyProviderFactories(new PKCS8KeyFile.Factory(), new OpenSSHKeyFile.Factory(), new PuTTYKeyFile.Factory());
setFileKeyProviderFactories(
new OpenSSHKeyV1KeyFile.Factory(),
new PKCS8KeyFile.Factory(),
new PKCS5KeyFile.Factory(),
new OpenSSHKeyFile.Factory(),
new PuTTYKeyFile.Factory());
}
}
protected void initCipherFactories() {
List<Factory.Named<Cipher>> avail = new LinkedList<Factory.Named<Cipher>>(Arrays.<Factory.Named<Cipher>>asList(
new AES128CTR.Factory(),
new AES192CTR.Factory(),
new AES256CTR.Factory(),
new AES128CBC.Factory(),
new AES192CBC.Factory(),
new AES256CBC.Factory(),
new TripleDESCBC.Factory(),
new BlowfishCBC.Factory(),
BlockCiphers.AES128CBC(),
BlockCiphers.AES128CTR(),
BlockCiphers.AES192CBC(),
BlockCiphers.AES192CTR(),
BlockCiphers.AES256CBC(),
BlockCiphers.AES256CTR(),
BlockCiphers.BlowfishCBC(),
BlockCiphers.BlowfishCTR(),
BlockCiphers.Cast128CBC(),
BlockCiphers.Cast128CTR(),
@@ -139,6 +172,7 @@ public class DefaultConfig
BlockCiphers.Serpent192CTR(),
BlockCiphers.Serpent256CBC(),
BlockCiphers.Serpent256CTR(),
BlockCiphers.TripleDESCBC(),
BlockCiphers.TripleDESCTR(),
BlockCiphers.Twofish128CBC(),
BlockCiphers.Twofish128CTR(),
@@ -177,21 +211,33 @@ public class DefaultConfig
protected void initSignatureFactories() {
setSignatureFactories(
new SignatureECDSA.Factory(),
new SignatureEdDSA.Factory(),
new SignatureECDSA.Factory256(),
new SignatureECDSA.Factory384(),
new SignatureECDSA.Factory521(),
new SignatureRSA.Factory(),
new SignatureDSA.Factory(),
new SignatureEdDSA.Factory()
new SignatureDSA.Factory()
);
}
protected void initMACFactories() {
setMACFactories(
new HMACSHA1.Factory(),
new HMACSHA196.Factory(),
new HMACMD5.Factory(),
new HMACMD596.Factory(),
new HMACSHA2256.Factory(),
new HMACSHA2512.Factory()
Macs.HMACSHA1(),
Macs.HMACSHA1Etm(),
Macs.HMACSHA196(),
Macs.HMACSHA196Etm(),
Macs.HMACMD5(),
Macs.HMACMD5Etm(),
Macs.HMACMD596(),
Macs.HMACMD596Etm(),
Macs.HMACSHA2256(),
Macs.HMACSHA2256Etm(),
Macs.HMACSHA2512(),
Macs.HMACSHA2512Etm(),
Macs.HMACRIPEMD160(),
Macs.HMACRIPEMD160Etm(),
Macs.HMACRIPEMD16096(),
Macs.HMACRIPEMD160OpenSsh()
);
}

View File

@@ -15,10 +15,7 @@
*/
package net.schmizz.sshj;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.ConnectionImpl;
@@ -41,6 +38,7 @@ import net.schmizz.sshj.transport.compression.DelayedZlibCompression;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.compression.ZlibCompression;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.FingerprintVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import net.schmizz.sshj.userauth.UserAuth;
@@ -61,8 +59,8 @@ import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.charset.Charset;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.*;
/**
@@ -128,6 +126,9 @@ public class SSHClient
private final List<LocalPortForwarder> forwarders = new ArrayList<LocalPortForwarder>();
/** character set of the remote machine */
protected Charset remoteCharset = IOUtils.UTF8;
/** Default constructor. Initializes this object using {@link DefaultConfig}. */
public SSHClient() {
this(new DefaultConfig());
@@ -168,19 +169,23 @@ public class SSHClient
/**
* Add a {@link HostKeyVerifier} that will verify any host that's able to claim a host key with the given {@code
* fingerprint}, e.g. {@code "4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"}
* fingerprint}.
*
* The fingerprint can be specified in either an MD5 colon-delimited format (16 hexadecimal octets, delimited by a colon),
* or in a Base64 encoded format for SHA-1 or SHA-256 fingerprints.
* Valid examples are:
*
* <ul><li>"SHA1:2Fo8c/96zv32xc8GZWbOGYOlRak="</li>
* <li>"SHA256:oQGbQTujGeNIgh0ONthcEpA/BHxtt3rcYY+NxXTxQjs="</li>
* <li>"MD5:d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32"</li>
* <li>"d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32"</li></ul>
*
* @param fingerprint expected fingerprint in colon-delimited format (16 octets in hex delimited by a colon)
*
* @see SecurityUtils#getFingerprint
*/
public void addHostKeyVerifier(final String fingerprint) {
addHostKeyVerifier(new HostKeyVerifier() {
@Override
public boolean verify(String h, int p, PublicKey k) {
return SecurityUtils.getFingerprint(k).equals(fingerprint);
}
});
addHostKeyVerifier(FingerprintVerifier.getInstance(fingerprint));
}
// FIXME: there are way too many auth... overrides. Better API needed.
@@ -316,7 +321,7 @@ public class SSHClient
public void authPublickey(String username)
throws UserAuthException, TransportException {
final String base = System.getProperty("user.home") + File.separator + ".ssh" + File.separator;
authPublickey(username, base + "id_rsa", base + "id_dsa");
authPublickey(username, base + "id_rsa", base + "id_dsa", base + "id_ed25519", base + "id_ecdsa");
}
/**
@@ -354,8 +359,7 @@ public class SSHClient
* @throws TransportException if there was a transport-layer error
*/
public void authPublickey(String username, KeyProvider... keyProviders)
throws UserAuthException,
TransportException {
throws UserAuthException, TransportException {
authPublickey(username, Arrays.<KeyProvider>asList(keyProviders));
}
@@ -440,6 +444,15 @@ public class SSHClient
return conn;
}
/**
* Returns the character set used to communicate with the remote machine for certain strings (like paths).
*
* @return remote character set
*/
public Charset getRemoteCharset() {
return remoteCharset;
}
/** @return a {@link RemotePortForwarder} that allows requesting remote forwarding over this connection. */
public RemotePortForwarder getRemotePortForwarder() {
synchronized (conn) {
@@ -524,8 +537,13 @@ public class SSHClient
}
/**
* Creates a {@link KeyProvider} instance from given location on the file system. Currently only PKCS8 format
* private key files are supported (OpenSSH uses this format).
* Creates a {@link KeyProvider} instance from given location on the file system. Currently the following private key files are supported:
* <ul>
* <li>PKCS8 (OpenSSH uses this format)</li>
* <li>PKCS5</li>
* <li>Putty keyfile</li>
* <li>openssh-key-v1 (New OpenSSH keyfile format)</li>
* </ul>
* <p/>
*
* @param location the location of the key file
@@ -703,12 +721,22 @@ public class SSHClient
doKex();
}
/**
* Sets the character set used to communicate with the remote machine for certain strings (like paths)
*
* @param remoteCharset
* remote character set or {@code null} for default
*/
public void setRemoteCharset(Charset remoteCharset) {
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
}
@Override
public Session startSession()
throws ConnectionException, TransportException {
checkConnected();
checkAuthenticated();
final SessionChannel sess = new SessionChannel(conn);
final SessionChannel sess = new SessionChannel(conn, remoteCharset);
sess.open();
return sess;
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,8 +15,8 @@
*/
package net.schmizz.sshj.common;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
@@ -57,6 +57,11 @@ public class Buffer<T extends Buffer<T>> {
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
public static final int MAX_SIZE = (1 << 30);
/** Maximum size of a uint64 */
private static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
.shiftLeft(64)
.subtract(BigInteger.ONE);
protected static int getNextPowerOf2(int i) {
int j = 1;
while (j < i) {
@@ -127,8 +132,9 @@ public class Buffer<T extends Buffer<T>> {
protected void ensureAvailable(int a)
throws BufferException {
if (available() < a)
if (available() < a) {
throw new BufferException("Underflow");
}
}
public void ensureCapacity(int capacity) {
@@ -142,7 +148,6 @@ public class Buffer<T extends Buffer<T>> {
/** Compact this {@link SSHPacket} */
public void compact() {
System.err.println("COMPACTING");
if (available() > 0)
System.arraycopy(data, rpos, data, 0, wpos - rpos);
wpos -= rpos;
@@ -241,7 +246,7 @@ public class Buffer<T extends Buffer<T>> {
* @return this
*/
public T putBytes(byte[] b, int off, int len) {
return putUInt32(len - off).putRawBytes(b, off, len);
return putUInt32(len).putRawBytes(b, off, len);
}
public void readRawBytes(byte[] buf)
@@ -311,7 +316,7 @@ public class Buffer<T extends Buffer<T>> {
public T putUInt32(long uint32) {
ensureCapacity(4);
if (uint32 < 0 || uint32 > 0xffffffffL)
throw new RuntimeException("Invalid value: " + uint32);
throw new IllegalArgumentException("Invalid value: " + uint32);
data[wpos++] = (byte) (uint32 >> 24);
data[wpos++] = (byte) (uint32 >> 16);
data[wpos++] = (byte) (uint32 >> 8);
@@ -343,10 +348,31 @@ public class Buffer<T extends Buffer<T>> {
return uint64;
}
@SuppressWarnings("unchecked")
public BigInteger readUInt64AsBigInteger()
throws BufferException {
byte[] magnitude = new byte[8];
readRawBytes(magnitude);
return new BigInteger(1, magnitude);
}
public T putUInt64(long uint64) {
if (uint64 < 0)
throw new RuntimeException("Invalid value: " + uint64);
if (uint64 < 0) {
throw new IllegalArgumentException("Invalid value: " + uint64);
}
return putUInt64Unchecked(uint64);
}
public T putUInt64(BigInteger uint64) {
if (uint64.compareTo(MAX_UINT64_VALUE) > 0 ||
uint64.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException("Invalid value: " + uint64);
}
return putUInt64Unchecked(uint64.longValue());
}
@SuppressWarnings("unchecked")
private T putUInt64Unchecked(long uint64) {
ensureCapacity(8);
data[wpos++] = (byte) (uint64 >> 56);
data[wpos++] = (byte) (uint64 >> 48);
data[wpos++] = (byte) (uint64 >> 40);
@@ -361,22 +387,30 @@ public class Buffer<T extends Buffer<T>> {
/**
* Reads an SSH string
*
* @param cs the charset to use for decoding
*
* @return the string as a Java {@code String}
*/
public String readString(Charset cs)
throws BufferException {
int len = readUInt32AsInt();
if (len < 0 || len > 32768) {
throw new BufferException("Bad item length: " + len);
}
ensureAvailable(len);
String s = new String(data, rpos, len, cs);
rpos += len;
return s;
}
/**
* Reads an SSH string using {@code UTF8}
*
* @return the string as a Java {@code String}
*/
public String readString()
throws BufferException {
int len = readUInt32AsInt();
if (len < 0 || len > 32768)
throw new BufferException("Bad item length: " + len);
ensureAvailable(len);
String s;
try {
s = new String(data, rpos, len, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new SSHRuntimeException(e);
}
rpos += len;
return s;
return readString(IOUtils.UTF8);
}
/**
@@ -397,8 +431,12 @@ public class Buffer<T extends Buffer<T>> {
return putBytes(str, offset, len);
}
public T putString(String string, Charset cs) {
return putString(string.getBytes(cs));
}
public T putString(String string) {
return putString(string.getBytes(IOUtils.UTF8));
return putString(string, IOUtils.UTF8);
}
/**
@@ -425,10 +463,13 @@ public class Buffer<T extends Buffer<T>> {
public PublicKey readPublicKey()
throws BufferException {
KeyType keyType = KeyType.fromString(readString());
try {
return KeyType.fromString(readString()).readPubKeyFromBuffer(this);
return keyType.readPubKeyFromBuffer(this);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
} catch (UnsupportedOperationException uoe) {
throw new BufferException("Could not decode keytype " + keyType);
}
}

View File

@@ -94,4 +94,34 @@ public class ByteArrayUtils {
return sb.toString();
}
public static byte[] parseHex(String hex) {
if (hex == null) {
throw new IllegalArgumentException("Hex string is null");
}
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException("Hex string '" + hex + "' should have even length.");
}
byte[] result = new byte[hex.length() / 2];
for (int i = 0; i < result.length; i++) {
int hi = parseHexDigit(hex.charAt(i * 2)) << 4;
int lo = parseHexDigit(hex.charAt(i * 2 + 1));
result[i] = (byte) (hi + lo);
}
return result;
}
private static int parseHexDigit(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
throw new IllegalArgumentException("Digit '" + c + "' out of bounds [0-9a-fA-F]");
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.common;
import com.hierynomus.sshj.secg.SecgUtils;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.ECKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
class ECDSAVariationsAdapter {
private final static String BASE_ALGORITHM_NAME = "ecdsa-sha2-nistp";
private final static Logger log = LoggerFactory.getLogger(ECDSAVariationsAdapter.class);
public final static Map<String, String> SUPPORTED_CURVES = new HashMap<String, String>();
public final static Map<String, String> NIST_CURVES_NAMES = new HashMap<String, String>();
static {
NIST_CURVES_NAMES.put("256", "p-256");
NIST_CURVES_NAMES.put("384", "p-384");
NIST_CURVES_NAMES.put("521", "p-521");
SUPPORTED_CURVES.put("256", "nistp256");
SUPPORTED_CURVES.put("384", "nistp384");
SUPPORTED_CURVES.put("521", "nistp521");
}
static PublicKey readPubKeyFromBuffer(Buffer<?> buf, String variation) throws GeneralSecurityException {
String algorithm = BASE_ALGORITHM_NAME + variation;
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new GeneralSecurityException("BouncyCastle is required to read a key of type " + algorithm);
}
try {
// final String algo = buf.readString(); it has been already read
final String curveName = buf.readString();
final int keyLen = buf.readUInt32AsInt();
final byte x04 = buf.readByte(); // it must be 0x04, but don't think
// we need that check
final byte[] x = new byte[(keyLen - 1) / 2];
final byte[] y = new byte[(keyLen - 1) / 2];
buf.readRawBytes(x);
buf.readRawBytes(y);
if (log.isDebugEnabled()) {
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
algorithm, curveName, keyLen, x04, Arrays.toString(x), Arrays.toString(y)));
}
if (!SUPPORTED_CURVES.values().contains(curveName)) {
throw new GeneralSecurityException(String.format("Unknown curve %s", curveName));
}
BigInteger bigX = new BigInteger(1, x);
BigInteger bigY = new BigInteger(1, y);
String name = NIST_CURVES_NAMES.get(variation);
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
ECPoint p = new ECPoint(bigX, bigY);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
return keyFactory.generatePublic(publicKeySpec);
} catch (Exception ex) {
throw new GeneralSecurityException(ex);
}
}
static void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
final ECPublicKey ecdsa = (ECPublicKey) pk;
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
buf.putString("nistp" + Integer.toString(fieldSizeFromKey(ecdsa)))
.putBytes(encoded);
}
static boolean isECKeyWithFieldSize(Key key, int fieldSize) {
return "ECDSA".equals(key.getAlgorithm())
&& fieldSizeFromKey((ECKey) key) == fieldSize;
}
private static int fieldSizeFromKey(ECKey ecPublicKey) {
return ecPublicKey.getParams().getCurve().getField().getFieldSize();
}
}

View File

@@ -15,8 +15,6 @@
*/
package net.schmizz.sshj.common;
import org.slf4j.Logger;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
@@ -42,7 +40,7 @@ public class IOUtils {
if (c != null)
c.close();
} catch (IOException logged) {
loggerFactory.getLogger(IOUtils.class).warn("Error closing {} - {}", c, logged);
loggerFactory.getLogger(IOUtils.class).warn("Error closing {} - {}", c, logged);
}
}
}

View File

@@ -15,17 +15,13 @@
*/
package net.schmizz.sshj.common;
import com.hierynomus.sshj.secg.SecgUtils;
import com.hierynomus.sshj.signature.Ed25519PublicKey;
import com.hierynomus.sshj.userauth.certificate.Certificate;
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.EdDSAPublicKeySpec;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import net.schmizz.sshj.common.Buffer.BufferException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,15 +30,17 @@ import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import java.util.*;
/** Type of key e.g. rsa, dsa */
public enum KeyType {
/** SSH identifier for RSA keys */
RSA("ssh-rsa") {
@Override
@@ -60,18 +58,16 @@ public enum KeyType {
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
final RSAPublicKey rsaKey = (RSAPublicKey) pk;
buf.putString(sType)
.putMPInt(rsaKey.getPublicExponent()) // e
.putMPInt(rsaKey.getModulus()); // n
buf.putMPInt(rsaKey.getPublicExponent()) // e
.putMPInt(rsaKey.getModulus()); // n
}
@Override
protected boolean isMyType(Key key) {
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
}
},
/** SSH identifier for DSA keys */
@@ -93,13 +89,12 @@ public enum KeyType {
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
final DSAPublicKey dsaKey = (DSAPublicKey) pk;
buf.putString(sType)
.putMPInt(dsaKey.getParams().getP()) // p
.putMPInt(dsaKey.getParams().getQ()) // q
.putMPInt(dsaKey.getParams().getG()) // g
.putMPInt(dsaKey.getY()); // y
buf.putMPInt(dsaKey.getParams().getP()) // p
.putMPInt(dsaKey.getParams().getQ()) // q
.putMPInt(dsaKey.getParams().getG()) // g
.putMPInt(dsaKey.getY()); // y
}
@Override
@@ -109,70 +104,66 @@ public enum KeyType {
},
/** SSH identifier for ECDSA keys */
ECDSA("ecdsa-sha2-nistp256") {
private final Logger log = LoggerFactory.getLogger(getClass());
/** SSH identifier for ECDSA-256 keys */
ECDSA256("ecdsa-sha2-nistp256") {
@Override
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new GeneralSecurityException("BouncyCastle is required to read a key of type " + sType);
}
try {
// final String algo = buf.readString(); it has been already read
final String curveName = buf.readString();
final int keyLen = buf.readUInt32AsInt();
final byte x04 = buf.readByte(); // it must be 0x04, but don't think we need that check
final byte[] x = new byte[(keyLen - 1) / 2];
final byte[] y = new byte[(keyLen - 1) / 2];
buf.readRawBytes(x);
buf.readRawBytes(y);
if(log.isDebugEnabled()) {
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
sType,
curveName,
keyLen,
x04,
Arrays.toString(x),
Arrays.toString(y))
);
}
if (!NISTP_CURVE.equals(curveName)) {
throw new GeneralSecurityException(String.format("Unknown curve %s", curveName));
}
BigInteger bigX = new BigInteger(1, x);
BigInteger bigY = new BigInteger(1, y);
X9ECParameters ecParams = NISTNamedCurves.getByName("p-256");
ECPoint pPublicPoint = ecParams.getCurve().createPoint(bigX, bigY);
ECParameterSpec spec = new ECParameterSpec(ecParams.getCurve(),
ecParams.getG(), ecParams.getN());
ECPublicKeySpec publicSpec = new ECPublicKeySpec(pPublicPoint, spec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
return keyFactory.generatePublic(publicSpec);
} catch (Exception ex) {
throw new GeneralSecurityException(ex);
}
return ECDSAVariationsAdapter.readPubKeyFromBuffer(buf, "256");
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
final ECPublicKey ecdsa = (ECPublicKey) pk;
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
buf.putString(sType)
.putString(NISTP_CURVE)
.putBytes(encoded);
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
ECDSAVariationsAdapter.writePubKeyContentsIntoBuffer(pk, buf);
}
@Override
protected boolean isMyType(Key key) {
return ("ECDSA".equals(key.getAlgorithm()));
return ECDSAVariationsAdapter.isECKeyWithFieldSize(key, 256);
}
},
/** SSH identifier for ECDSA-384 keys */
ECDSA384("ecdsa-sha2-nistp384") {
@Override
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
return ECDSAVariationsAdapter.readPubKeyFromBuffer(buf, "384");
}
@Override
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
ECDSAVariationsAdapter.writePubKeyContentsIntoBuffer(pk, buf);
}
@Override
protected boolean isMyType(Key key) {
return ECDSAVariationsAdapter.isECKeyWithFieldSize(key, 384);
}
},
/** SSH identifier for ECDSA-521 keys */
ECDSA521("ecdsa-sha2-nistp521") {
@Override
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
return ECDSAVariationsAdapter.readPubKeyFromBuffer(buf, "521");
}
@Override
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
ECDSAVariationsAdapter.writePubKeyContentsIntoBuffer(pk, buf);
}
@Override
protected boolean isMyType(Key key) {
return ECDSAVariationsAdapter.isECKeyWithFieldSize(key, 521);
}
},
@@ -192,7 +183,7 @@ public enum KeyType {
);
}
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(p, ed25519);
return new Ed25519PublicKey(publicSpec);
@@ -202,9 +193,9 @@ public enum KeyType {
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
EdDSAPublicKey key = (EdDSAPublicKey) pk;
buf.putString(sType).putBytes(key.getAbyte());
buf.putBytes(key.getAbyte());
}
@Override
@@ -213,6 +204,44 @@ public enum KeyType {
}
},
/** Signed rsa certificate */
RSA_CERT("ssh-rsa-cert-v01@openssh.com") {
@Override
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
return CertUtils.readPubKey(buf, RSA);
}
@Override
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
CertUtils.writePubKeyContentsIntoBuffer(pk, RSA, buf);
}
@Override
protected boolean isMyType(Key key) {
return CertUtils.isCertificateOfType(key, RSA);
}
},
/** Signed dsa certificate */
DSA_CERT("ssh-dss-cert-v01@openssh.com") {
@Override
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException {
return CertUtils.readPubKey(buf, DSA);
}
@Override
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
CertUtils.writePubKeyContentsIntoBuffer(pk, DSA, buf);
}
@Override
protected boolean isMyType(Key key) {
return CertUtils.isCertificateOfType(key, DSA);
}
},
/** Unrecognized */
UNKNOWN("unknown") {
@Override
@@ -226,15 +255,17 @@ public enum KeyType {
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
}
@Override
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
}
@Override
protected boolean isMyType(Key key) {
return false;
}
};
private static final String NISTP_CURVE = "nistp256";
protected final String sType;
private KeyType(String type) {
@@ -244,7 +275,11 @@ public enum KeyType {
public abstract PublicKey readPubKeyFromBuffer(Buffer<?> buf)
throws GeneralSecurityException;
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
protected abstract void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf);
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
writePubKeyContentsIntoBuffer(pk, buf.putString(sType));
}
protected abstract boolean isMyType(Key key);
@@ -266,4 +301,129 @@ public enum KeyType {
public String toString() {
return sType;
}
static class CertUtils {
@SuppressWarnings("unchecked")
static <T extends PublicKey> Certificate<T> readPubKey(Buffer<?> buf, KeyType innerKeyType) throws GeneralSecurityException {
Certificate.Builder<T> builder = Certificate.getBuilder();
try {
builder.nonce(buf.readBytes());
builder.publicKey((T) innerKeyType.readPubKeyFromBuffer(buf));
builder.serial(buf.readUInt64AsBigInteger());
builder.type(buf.readUInt32());
builder.id(buf.readString());
builder.validPrincipals(unpackList(buf.readBytes()));
builder.validAfter(dateFromEpoch(buf.readUInt64()));
builder.validBefore(dateFromEpoch(buf.readUInt64()));
builder.critOptions(unpackMap(buf.readBytes()));
builder.extensions(unpackMap(buf.readBytes()));
buf.readString(); // reserved
builder.signatureKey(buf.readBytes());
builder.signature(buf.readBytes());
} catch (Buffer.BufferException be) {
throw new GeneralSecurityException(be);
}
return builder.build();
}
static void writePubKeyContentsIntoBuffer(PublicKey publicKey, KeyType innerKeyType, Buffer<?> buf) {
Certificate<PublicKey> certificate = toCertificate(publicKey);
buf.putBytes(certificate.getNonce());
innerKeyType.writePubKeyContentsIntoBuffer(certificate.getKey(), buf);
buf.putUInt64(certificate.getSerial())
.putUInt32(certificate.getType())
.putString(certificate.getId())
.putBytes(packList(certificate.getValidPrincipals()))
.putUInt64(epochFromDate(certificate.getValidAfter()))
.putUInt64(epochFromDate(certificate.getValidBefore()))
.putBytes(packMap(certificate.getCritOptions()))
.putBytes(packMap(certificate.getExtensions()))
.putString("") // reserved
.putBytes(certificate.getSignatureKey())
.putBytes(certificate.getSignature());
}
static boolean isCertificateOfType(Key key, KeyType innerKeyType) {
if (!(key instanceof Certificate)) {
return false;
}
@SuppressWarnings("unchecked")
Key innerKey = ((Certificate<PublicKey>) key).getKey();
return innerKeyType.isMyType(innerKey);
}
@SuppressWarnings("unchecked")
static Certificate<PublicKey> toCertificate(PublicKey key) {
if (!(key instanceof Certificate)) {
throw new UnsupportedOperationException("Can't convert non-certificate key " +
key.getAlgorithm() + " to certificate");
}
return ((Certificate<PublicKey>) key);
}
private static Date dateFromEpoch(long seconds) {
return new Date(seconds * 1000);
}
private static long epochFromDate(Date date) {
return date.getTime() / 1000;
}
private static String unpackString(byte[] packedString) throws BufferException {
if (packedString.length == 0) {
return "";
}
return new Buffer.PlainBuffer(packedString).readString();
}
private static List<String> unpackList(byte[] packedString) throws BufferException {
List<String> list = new ArrayList<String>();
Buffer<?> buf = new Buffer.PlainBuffer(packedString);
while (buf.available() > 0) {
list.add(buf.readString());
}
return list;
}
private static Map<String, String> unpackMap(byte[] packedString) throws BufferException {
Map<String, String> map = new LinkedHashMap<String, String>();
Buffer<?> buf = new Buffer.PlainBuffer(packedString);
while (buf.available() > 0) {
String name = buf.readString();
String data = unpackString(buf.readStringAsBytes());
map.put(name, data);
}
return map;
}
private static byte[] packString(String data) {
if (data == null || data.isEmpty()) {
return "".getBytes();
}
return new Buffer.PlainBuffer().putString(data).getCompactData();
}
private static byte[] packList(Iterable<String> strings) {
Buffer<?> buf = new Buffer.PlainBuffer();
for (String string : strings) {
buf.putString(string);
}
return buf.getCompactData();
}
private static byte[] packMap(Map<String, String> map) {
Buffer<?> buf = new Buffer.PlainBuffer();
List<String> keys = new ArrayList<String>(map.keySet());
Collections.sort(keys);
for (String key : keys) {
buf.putString(key);
String value = map.get(key);
buf.putString(packString(value));
}
return buf.getCompactData();
}
}
}

View File

@@ -34,7 +34,7 @@ public class SSHRuntimeException
}
public SSHRuntimeException(Throwable cause) {
this(null, cause);
this(cause.getMessage(), cause);
}
}

View File

@@ -15,13 +15,24 @@
*/
package net.schmizz.sshj.common;
import java.security.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.lang.String.format;
@@ -36,12 +47,17 @@ public class SecurityUtils {
*/
public static final String BOUNCY_CASTLE = "BC";
/**
* Identifier for the BouncyCastle JCE provider
*/
public static final String SPONGY_CASTLE = "SC";
/*
* Security provider identifier. null = default JCE
*/
private static String securityProvider = null;
// relate to BC registration
// relate to BC registration (or SpongyCastle on Android)
private static Boolean registerBouncyCastle;
private static boolean registrationDone;
@@ -68,19 +84,21 @@ public class SecurityUtils {
}
if (securityProvider == null) {
MessageDigest.getInstance("MD5", provider.getName());
KeyAgreement.getInstance("DH", provider.getName());
MessageDigest.getInstance("MD5", provider);
KeyAgreement.getInstance("DH", provider);
setSecurityProvider(provider.getName());
return true;
}
} catch (NoSuchAlgorithmException e) {
LOG.info(format("Security Provider '%s' does not support necessary algorithm", providerClassName), e);
} catch (NoSuchProviderException e) {
LOG.info("Registration of Security Provider '{}' unexpectedly failed", providerClassName);
} catch (Exception e) {
LOG.info(format("Registration of Security Provider '%s' unexpectedly failed", providerClassName), e);
}
return false;
}
public static synchronized Cipher getCipher(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
register();
@@ -221,11 +239,11 @@ public class SecurityUtils {
* Attempts registering BouncyCastle as security provider if it has not been previously attempted and returns
* whether the registration succeeded.
*
* @return whether BC registered
* @return whether BC (or SC on Android) registered
*/
public static synchronized boolean isBouncyCastleRegistered() {
register();
return BOUNCY_CASTLE.equals(securityProvider);
return BOUNCY_CASTLE.equals(securityProvider) || SPONGY_CASTLE.equals(securityProvider);
}
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
@@ -254,7 +272,7 @@ public class SecurityUtils {
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut JCE provider");
}
}
registrationDone = true;
}
registrationDone = true;
}
}

View File

@@ -15,7 +15,6 @@
*/
package net.schmizz.sshj.common;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.ExceptionChainer;
import org.slf4j.Logger;
@@ -68,8 +67,11 @@ public class StreamCopier {
}
public StreamCopier listener(Listener listener) {
if (listener == null) listener = NULL_LISTENER;
this.listener = listener;
if (listener == null) {
this.listener = NULL_LISTENER;
} else {
this.listener = listener;
}
return this;
}
@@ -126,11 +128,13 @@ public class StreamCopier {
final long startTime = System.currentTimeMillis();
if (length == -1) {
while ((read = in.read(buf)) != -1)
count = write(buf, count, read);
while ((read = in.read(buf)) != -1) {
count += write(buf, count, read);
}
} else {
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1)
count = write(buf, count, read);
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1) {
count += write(buf, count, read);
}
}
if (!keepFlushing)
@@ -146,14 +150,13 @@ public class StreamCopier {
return count;
}
private long write(byte[] buf, long count, int read)
private long write(byte[] buf, long curPos, int len)
throws IOException {
out.write(buf, 0, read);
count += read;
out.write(buf, 0, len);
if (keepFlushing)
out.flush();
listener.reportProgress(count);
return count;
listener.reportProgress(curPos + len);
return len;
}
}

View File

@@ -126,11 +126,13 @@ public class ConnectionImpl
@Override
public void handle(Message msg, SSHPacket buf)
throws SSHException {
if (msg.in(91, 100))
if (msg.in(91, 100)) {
getChannel(buf).handle(msg, buf);
else if (msg.in(80, 90))
} else if (msg.in(80, 90)) {
switch (msg) {
case GLOBAL_REQUEST:
gotGlobalRequest(buf);
break;
case REQUEST_SUCCESS:
gotGlobalReqResponse(buf);
break;
@@ -142,10 +144,11 @@ public class ConnectionImpl
break;
default:
super.handle(msg, buf);
break;
}
else
} else {
super.handle(msg, buf);
}
}
@Override
@@ -174,11 +177,11 @@ public class ConnectionImpl
}
@Override
public void join()
throws InterruptedException {
public void join() throws InterruptedException {
synchronized (internalSynchronizer) {
while (!channels.isEmpty())
while (!channels.isEmpty()) {
internalSynchronizer.wait();
}
}
}
@@ -259,6 +262,20 @@ public class ConnectionImpl
channels.clear();
}
private void gotGlobalRequest(SSHPacket buf)
throws ConnectionException, TransportException {
try {
final String requestName = buf.readString();
boolean wantReply = buf.readBoolean();
log.debug("Received GLOBAL_REQUEST `{}`; want reply: {}", requestName, wantReply);
if (wantReply) {
trans.write(new SSHPacket(Message.REQUEST_FAILURE));
}
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
}
@Override
public void setTimeoutMs(int timeoutMs) {
this.timeoutMs = timeoutMs;

View File

@@ -26,6 +26,7 @@ import org.slf4j.Logger;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
@@ -51,6 +52,8 @@ public abstract class AbstractChannel
private final int id;
/** Remote recipient ID */
private int recipient;
/** Remote character set */
private final Charset remoteCharset;
private boolean eof = false;
@@ -78,12 +81,16 @@ public abstract class AbstractChannel
private volatile boolean autoExpand = false;
protected AbstractChannel(Connection conn, String type) {
this(conn, type, null);
}
protected AbstractChannel(Connection conn, String type, Charset remoteCharset) {
this.conn = conn;
this.loggerFactory = conn.getTransport().getConfig().getLoggerFactory();
this.type = type;
this.log = loggerFactory.getLogger(getClass());
this.trans = conn.getTransport();
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
id = conn.nextID();
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize(), loggerFactory);
@@ -95,7 +102,8 @@ public abstract class AbstractChannel
protected void init(int recipient, long remoteWinSize, long remoteMaxPacketSize) {
this.recipient = recipient;
rwin = new Window.Remote(remoteWinSize, (int) Math.min(remoteMaxPacketSize, REMOTE_MAX_PACKET_SIZE_CEILING), loggerFactory);
rwin = new Window.Remote(remoteWinSize, (int) Math.min(remoteMaxPacketSize, REMOTE_MAX_PACKET_SIZE_CEILING),
conn.getTimeoutMs(), loggerFactory);
out = new ChannelOutputStream(this, trans, rwin);
log.debug("Initialized - {}", this);
}
@@ -135,6 +143,11 @@ public abstract class AbstractChannel
return recipient;
}
@Override
public Charset getRemoteCharset() {
return remoteCharset;
}
@Override
public int getRemoteMaxPacketSize() {
return rwin.getMaxPacketSize();
@@ -189,7 +202,7 @@ public abstract class AbstractChannel
default:
gotUnknown(msg, buf);
break;
}
}
@@ -329,6 +342,8 @@ public abstract class AbstractChannel
protected void gotUnknown(Message msg, SSHPacket buf)
throws ConnectionException, TransportException {
log.warn("Got unknown packet with type {}", msg);
}
protected void handleRequest(String reqType, SSHPacket buf)
@@ -348,10 +363,12 @@ public abstract class AbstractChannel
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
if (len < 0 || len > getLocalMaxPacketSize() || len > buf.available())
if (len < 0 || len > getLocalMaxPacketSize() || len > buf.available()) {
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Bad item length: " + len);
if (log.isTraceEnabled())
}
if (log.isTraceEnabled()) {
log.trace("IN #{}: {}", id, ByteArrayUtils.printHex(buf.array(), buf.rpos(), len));
}
stream.receive(buf.array(), buf.rpos(), len);
}

View File

@@ -24,6 +24,7 @@ import net.schmizz.sshj.transport.TransportException;
import java.io.Closeable;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
/**
@@ -123,6 +124,9 @@ public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
*/
int getRecipient();
/** @return the character set used to communicate with the remote machine for certain strings (like paths). */
Charset getRemoteCharset();
/**
* @return the maximum packet size as specified by the remote end.
*/

View File

@@ -20,7 +20,6 @@ import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -93,36 +92,43 @@ public final class ChannelInputStream
throws IOException {
synchronized (buf) {
for (; ; ) {
if (buf.available() > 0)
if (buf.available() > 0) {
break;
if (eof)
if (error != null)
}
if (eof) {
if (error != null) {
throw error;
else
} else {
return -1;
}
}
try {
buf.wait();
} catch (InterruptedException e) {
throw (IOException) new InterruptedIOException().initCause(e);
}
}
if (len > buf.available())
if (len > buf.available()) {
len = buf.available();
}
buf.readRawBytes(b, off, len);
if (buf.rpos() > win.getMaxPacketSize() && buf.available() == 0)
if (buf.rpos() > win.getMaxPacketSize() && buf.available() == 0) {
buf.clear();
}
}
if (!chan.getAutoExpand())
if (!chan.getAutoExpand()) {
checkWindow();
}
return len;
}
public void receive(byte[] data, int offset, int len)
throws ConnectionException, TransportException {
if (eof)
if (eof) {
throw new ConnectionException("Getting data on EOF'ed stream");
}
synchronized (buf) {
buf.putRawBytes(data, offset, len);
buf.notifyAll();
@@ -133,8 +139,9 @@ public final class ChannelInputStream
synchronized (win) {
win.consume(len);
}
if (chan.getAutoExpand())
if (chan.getAutoExpand()) {
checkWindow();
}
}
private void checkWindow()
@@ -144,7 +151,7 @@ public final class ChannelInputStream
if (adjustment > 0) {
log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST to #{} for {} bytes", chan.getRecipient(), adjustment);
trans.write(new SSHPacket(Message.CHANNEL_WINDOW_ADJUST)
.putUInt32(chan.getRecipient()).putUInt32(adjustment));
.putUInt32(chan.getRecipient()).putUInt32(adjustment));
win.expand(adjustment);
}
}

View File

@@ -27,9 +27,7 @@ import java.io.OutputStream;
* {@link OutputStream} for channels. Buffers data upto the remote window's maximum packet size. Data can also be
* flushed via {@link #flush()} and is also flushed on {@link #close()}.
*/
public final class ChannelOutputStream
extends OutputStream
implements ErrorNotifiable {
public final class ChannelOutputStream extends OutputStream implements ErrorNotifiable {
private final Channel chan;
private final Transport trans;
@@ -56,8 +54,7 @@ public final class ChannelOutputStream
dataOffset = packet.wpos();
}
int write(byte[] data, int off, int len)
throws TransportException, ConnectionException {
int write(byte[] data, int off, int len) throws TransportException, ConnectionException {
final int bufferSize = packet.wpos() - dataOffset;
if (bufferSize >= win.getMaxPacketSize()) {
flush(bufferSize, true);
@@ -69,15 +66,13 @@ public final class ChannelOutputStream
}
}
boolean flush(boolean canAwaitExpansion)
throws TransportException, ConnectionException {
boolean flush(boolean canAwaitExpansion) throws TransportException, ConnectionException {
return flush(packet.wpos() - dataOffset, canAwaitExpansion);
}
boolean flush(int bufferSize, boolean canAwaitExpansion)
throws TransportException, ConnectionException {
while (bufferSize > 0) {
boolean flush(int bufferSize, boolean canAwaitExpansion) throws TransportException, ConnectionException {
int dataLeft = bufferSize;
while (dataLeft > 0) {
long remoteWindowSize = win.getSize();
if (remoteWindowSize == 0) {
if (canAwaitExpansion) {
@@ -91,7 +86,7 @@ public final class ChannelOutputStream
// a) how much data we have
// b) the max packet size
// c) what the current window size will allow
final int writeNow = Math.min(bufferSize, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
final int writeNow = Math.min(dataLeft, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
packet.wpos(headerOffset);
packet.putMessageID(Message.CHANNEL_DATA);
@@ -99,7 +94,7 @@ public final class ChannelOutputStream
packet.putUInt32(writeNow);
packet.wpos(dataOffset + writeNow);
final int leftOverBytes = bufferSize - writeNow;
final int leftOverBytes = dataLeft - writeNow;
if (leftOverBytes > 0) {
leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes);
}
@@ -115,7 +110,7 @@ public final class ChannelOutputStream
leftOvers.clear();
}
bufferSize = leftOverBytes;
dataLeft = leftOverBytes;
}
return true;
@@ -140,10 +135,12 @@ public final class ChannelOutputStream
public synchronized void write(final byte[] data, int off, int len)
throws IOException {
checkClose();
while (len > 0) {
final int n = buffer.write(data, off, len);
off += n;
len -= n;
int length = len;
int offset = off;
while (length > 0) {
final int n = buffer.write(data, offset, length);
offset += n;
length -= n;
}
}
@@ -152,8 +149,7 @@ public final class ChannelOutputStream
this.error = error;
}
private void checkClose()
throws SSHException {
private void checkClose() throws SSHException {
if (closed) {
if (error != null)
throw error;
@@ -163,8 +159,7 @@ public final class ChannelOutputStream
}
@Override
public synchronized void close()
throws IOException {
public synchronized void close() throws IOException {
if (!closed) {
try {
buffer.flush(false);
@@ -182,8 +177,7 @@ public final class ChannelOutputStream
* @throws IOException
*/
@Override
public synchronized void flush()
throws IOException {
public synchronized void flush() throws IOException {
checkClose();
buffer.flush(true);
}

View File

@@ -20,6 +20,8 @@ import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.connection.ConnectionException;
import org.slf4j.Logger;
import java.util.concurrent.TimeUnit;
public abstract class Window {
protected final Logger log;
@@ -59,8 +61,9 @@ public abstract class Window {
synchronized (lock) {
size -= dec;
log.debug("Consuming by {} down to {}", dec, size);
if (size < 0)
if (size < 0) {
throw new ConnectionException("Window consumed to below 0");
}
}
}
@@ -72,18 +75,23 @@ public abstract class Window {
/** Controls how much data we can send before an adjustment notification from remote end is required. */
public static final class Remote
extends Window {
private final long timeoutMs;
public Remote(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) {
public Remote(long initialWinSize, int maxPacketSize, long timeoutMs, LoggerFactory loggerFactory) {
super(initialWinSize, maxPacketSize, loggerFactory);
this.timeoutMs = timeoutMs;
}
public long awaitExpansion(long was)
throws ConnectionException {
public long awaitExpansion(long was) throws ConnectionException {
synchronized (lock) {
long end = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
while (size <= was) {
log.debug("Waiting, need size to grow from {} bytes", was);
try {
lock.wait();
lock.wait(timeoutMs);
if ((size <= was) && ((System.nanoTime() - end) > 0)) {
throw new ConnectionException("Timeout when trying to expand the window size");
}
} catch (InterruptedException ie) {
throw new ConnectionException(ie);
}

View File

@@ -25,6 +25,7 @@ import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.OpenFailException;
import net.schmizz.sshj.transport.TransportException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
/** Base class for direct channels whose open is initated by the client. */
@@ -41,6 +42,15 @@ public abstract class AbstractDirectChannel
conn.attach(this);
}
protected AbstractDirectChannel(Connection conn, String type, Charset remoteCharset) {
super(conn, type, remoteCharset);
/*
* We expect to receive channel open confirmation/rejection and want to be able to next this packet.
*/
conn.attach(this);
}
@Override
public void open()
throws ConnectionException, TransportException {

View File

@@ -107,6 +107,7 @@ public class LocalPortForwarder {
private final Connection conn;
private final Parameters parameters;
private final ServerSocket serverSocket;
private Thread runningThread;
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket, LoggerFactory loggerFactory) {
this.conn = conn;
@@ -132,10 +133,29 @@ public class LocalPortForwarder {
*
* @throws IOException
*/
public void listen()
throws IOException {
public void listen() throws IOException {
listen(Thread.currentThread());
}
/**
* Returns whether this listener is running (ie. whether a thread is attached to it).
*
* @return
*/
public boolean isRunning() {
return this.runningThread != null && !serverSocket.isClosed();
}
/**
* Start listening for incoming connections and forward to remote host as a channel and ensure that the thread is registered.
* This is useful if for instance {@link #close() is called from another thread}
*
* @throws IOException
*/
public void listen(Thread runningThread) throws IOException {
this.runningThread = runningThread;
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
while (!Thread.currentThread().isInterrupted()) {
while (!runningThread.isInterrupted()) {
try {
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
@@ -161,6 +181,7 @@ public class LocalPortForwarder {
public void close() throws IOException {
if (!serverSocket.isClosed()) {
log.info("Closing listener on {}", serverSocket.getLocalSocketAddress());
runningThread.interrupt();
serverSocket.close();
}
}

View File

@@ -22,6 +22,7 @@ import net.schmizz.sshj.connection.channel.ChannelInputStream;
import net.schmizz.sshj.transport.TransportException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -47,6 +48,10 @@ public class SessionChannel
super(conn, "session");
}
public SessionChannel(Connection conn, Charset remoteCharset) {
super(conn, "session", remoteCharset);
}
@Override
public void allocateDefaultPTY()
throws ConnectionException, TransportException {
@@ -93,7 +98,7 @@ public class SessionChannel
throws ConnectionException, TransportException {
checkReuse();
log.debug("Will request to exec `{}`", command);
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command, getRemoteCharset()))
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
usedUp = true;
return this;

View File

@@ -21,7 +21,6 @@ import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.OpenFailException;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;

View File

@@ -130,7 +130,7 @@ public class RemotePortForwarder
// Addresses match up
return true;
}
if ("localhost".equals(address) && (channelForward.address.equals("127.0.0.1") || channelForward.address.equals("::1"))) {
if ("localhost".equals(address) && ("127.0.0.1".equals(channelForward.address) || "::1".equals(channelForward.address))) {
// Localhost special case.
return true;
}

View File

@@ -18,17 +18,17 @@ package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.SSHException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PacketReader
extends Thread {
public class PacketReader extends Thread {
/** Logger */
/**
* Logger
*/
private final Logger log;
private final InputStream in;
@@ -64,7 +64,7 @@ public class PacketReader
| lenBuf[3] & 0x000000ffL);
if (len > SFTPPacket.MAX_SIZE) {
throw new SSHException(String.format("Indicated packet length %d too large", len));
throw new SSHException(String.format("Indicated packet length %d too large", len));
}
return (int) len;
@@ -100,7 +100,7 @@ public class PacketReader
log.debug("Received {} packet", resp.getType());
if (promise == null)
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
+ ", no such request was made");
+ ", no such request was made");
else
promise.deliver(resp);
}

View File

@@ -18,8 +18,14 @@ package net.schmizz.sshj.sftp;
public class PathComponents {
static String adjustForParent(String parent, String path, String pathSep) {
return (path.startsWith(pathSep)) ? path // Absolute path, nothing to adjust
: (parent + (parent.endsWith(pathSep) ? "" : pathSep) + path); // Relative path
if (path.startsWith(pathSep)) {
return path; // Absolute path, nothing to adjust
} else if (parent.endsWith(pathSep)) {
return parent + path; // Relative path, parent endsWith '/'
} else if (parent.isEmpty()) {
return path;
}
return parent + pathSep + path; // Relative path
}
static String trimTrailingSeparator(String somePath, String pathSep) {
@@ -33,7 +39,8 @@ public class PathComponents {
public PathComponents(String parent, String name, String pathSep) {
this.parent = parent;
this.name = name;
this.path = trimTrailingSeparator(adjustForParent(parent, name, pathSep), pathSep);
String adjusted = adjustForParent(parent, name, pathSep);
this.path = !pathSep.equals(adjusted) ? trimTrailingSeparator(adjusted, pathSep) : adjusted;
}
public String getParent() {

View File

@@ -70,22 +70,30 @@ public class PathHelper {
*/
public PathComponents getComponents(final String path)
throws IOException {
if (path.equals(pathSep))
return getComponents("", "");
if (path.equals(pathSep)) {
return getComponents("", "/");
}
if (path.isEmpty() || path.equals(".") || path.equals("." + pathSep))
if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path)) {
return getComponents(getDotDir());
}
final String withoutTrailSep = trimTrailingSeparator(path);
final int lastSep = withoutTrailSep.lastIndexOf(pathSep);
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep);
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length());
String parent;
String name;
if (lastSep == -1) {
parent = "";
name = withoutTrailSep;
} else {
parent = lastSep == 0 ? "/" : withoutTrailSep.substring(0, lastSep);
name = withoutTrailSep.substring(lastSep + pathSep.length());
}
if (name.equals(".") || name.equals("..")) {
if (".".equals(name) || "..".equals(name)) {
return getComponents(canonicalizer.canonicalize(path));
} else {
return getComponents(parent, name);
}
}
}

View File

@@ -32,6 +32,7 @@ public class RemoteDirectory
public List<RemoteResourceInfo> scan(RemoteResourceFilter filter)
throws IOException {
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
// TODO: Remove GOTO!
loop:
for (; ; ) {
final Response res = requester.request(newRequest(PacketType.READDIR))
@@ -41,13 +42,14 @@ public class RemoteDirectory
case NAME:
final int count = res.readUInt32AsInt();
for (int i = 0; i < count; i++) {
final String name = res.readString();
final String name = res.readString(requester.sub.getRemoteCharset());
res.readString(); // long name - IGNORED - shdve never been in the protocol
final FileAttributes attrs = res.readFileAttributes();
final PathComponents comps = requester.getPathHelper().getComponents(path, name);
final RemoteResourceInfo inf = new RemoteResourceInfo(comps, attrs);
if (!(name.equals(".") || name.equals("..")) && (filter == null || filter.accept(inf)))
if (!(".".equals(name) || "..".equals(name)) && (filter == null || filter.accept(inf))) {
rri.add(inf);
}
}
break;

View File

@@ -81,10 +81,8 @@ public class RemoteFile
protected Promise<Response, SFTPException> asyncWrite(long fileOffset, byte[] data, int off, int len)
throws IOException {
return requester.request(newRequest(PacketType.WRITE)
.putUInt64(fileOffset)
// TODO The SFTP spec claims this field is unneeded...? See #187
.putUInt32(len)
.putRawBytes(data, off, len)
.putUInt64(fileOffset)
.putString(data, off, len)
);
}
@@ -194,10 +192,10 @@ public class RemoteFile
@Override
public long skip(long n) throws IOException {
final long fileLength = length();
final Long previousFileOffset = fileOffset;
fileOffset = Math.min(fileOffset + n, fileLength);
return fileOffset - previousFileOffset;
final long fileLength = length();
final Long previousFileOffset = fileOffset;
fileOffset = Math.min(fileOffset + n, fileLength);
return fileOffset - previousFileOffset;
}
@Override
@@ -341,7 +339,7 @@ public class RemoteFile
public int available() throws IOException {
boolean lastRead = true;
while (!eof && (pending.available() <= 0) && lastRead) {
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
}
return pending.available();
}

View File

@@ -16,7 +16,6 @@
package net.schmizz.sshj.sftp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;

View File

@@ -20,7 +20,7 @@ import net.schmizz.sshj.common.Buffer;
public final class Response
extends SFTPPacket<Response> {
public static enum StatusCode {
public enum StatusCode {
UNKNOWN(-1),
OK(0),
EOF(1),
@@ -68,6 +68,9 @@ public final class Response
this.code = code;
}
public int getCode() {
return code;
}
}
private final int protocolVersion;
@@ -122,6 +125,7 @@ public final class Response
return ensurePacketTypeIs(PacketType.STATUS).ensureStatusIs(StatusCode.OK);
}
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public Response ensureStatusIs(StatusCode acceptable)
throws SFTPException {
final StatusCode sc = readStatusCode();

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.xfer.FilePermission;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalSourceFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.connection.channel.direct.Session;
@@ -25,6 +26,7 @@ import org.slf4j.Logger;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
@@ -137,7 +139,7 @@ public class SFTPEngine
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
throws IOException {
final byte[] handle = doRequest(
newRequest(PacketType.OPEN).putString(path).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
newRequest(PacketType.OPEN).putString(path, sub.getRemoteCharset()).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
return new RemoteFile(this, path, handle);
}
@@ -155,7 +157,7 @@ public class SFTPEngine
public RemoteDirectory openDir(String path)
throws IOException {
final byte[] handle = doRequest(
newRequest(PacketType.OPENDIR).putString(path)
newRequest(PacketType.OPENDIR).putString(path, sub.getRemoteCharset())
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
return new RemoteDirectory(this, path, handle);
}
@@ -163,7 +165,7 @@ public class SFTPEngine
public void setAttributes(String path, FileAttributes attrs)
throws IOException {
doRequest(
newRequest(PacketType.SETSTAT).putString(path).putFileAttributes(attrs)
newRequest(PacketType.SETSTAT).putString(path, sub.getRemoteCharset()).putFileAttributes(attrs)
).ensureStatusPacketIsOK();
}
@@ -173,13 +175,13 @@ public class SFTPEngine
throw new SFTPException("READLINK is not supported in SFTPv" + operativeVersion);
return readSingleName(
doRequest(
newRequest(PacketType.READLINK).putString(path)
));
newRequest(PacketType.READLINK).putString(path, sub.getRemoteCharset())
), sub.getRemoteCharset());
}
public void makeDir(String path, FileAttributes attrs)
throws IOException {
doRequest(newRequest(PacketType.MKDIR).putString(path).putFileAttributes(attrs)).ensureStatusPacketIsOK();
doRequest(newRequest(PacketType.MKDIR).putString(path, sub.getRemoteCharset()).putFileAttributes(attrs)).ensureStatusPacketIsOK();
}
public void makeDir(String path)
@@ -192,21 +194,21 @@ public class SFTPEngine
if (operativeVersion < 3)
throw new SFTPException("SYMLINK is not supported in SFTPv" + operativeVersion);
doRequest(
newRequest(PacketType.SYMLINK).putString(linkpath).putString(targetpath)
newRequest(PacketType.SYMLINK).putString(linkpath, sub.getRemoteCharset()).putString(targetpath, sub.getRemoteCharset())
).ensureStatusPacketIsOK();
}
public void remove(String filename)
throws IOException {
doRequest(
newRequest(PacketType.REMOVE).putString(filename)
newRequest(PacketType.REMOVE).putString(filename, sub.getRemoteCharset())
).ensureStatusPacketIsOK();
}
public void removeDir(String path)
throws IOException {
doRequest(
newRequest(PacketType.RMDIR).putString(path)
newRequest(PacketType.RMDIR).putString(path, sub.getRemoteCharset())
).ensureStatusIs(Response.StatusCode.OK);
}
@@ -225,7 +227,7 @@ public class SFTPEngine
if (operativeVersion < 1)
throw new SFTPException("RENAME is not supported in SFTPv" + operativeVersion);
doRequest(
newRequest(PacketType.RENAME).putString(oldPath).putString(newPath)
newRequest(PacketType.RENAME).putString(oldPath, sub.getRemoteCharset()).putString(newPath, sub.getRemoteCharset())
).ensureStatusPacketIsOK();
}
@@ -233,8 +235,8 @@ public class SFTPEngine
throws IOException {
return readSingleName(
doRequest(
newRequest(PacketType.REALPATH).putString(path)
));
newRequest(PacketType.REALPATH).putString(path, sub.getRemoteCharset())
), sub.getRemoteCharset());
}
public void setTimeoutMs(int timeoutMs) {
@@ -258,20 +260,32 @@ public class SFTPEngine
protected FileAttributes stat(PacketType pt, String path)
throws IOException {
return doRequest(newRequest(pt).putString(path))
return doRequest(newRequest(pt).putString(path, sub.getRemoteCharset()))
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
protected static String readSingleName(Response res)
private static byte[] readSingleNameAsBytes(Response res)
throws IOException {
res.ensurePacketTypeIs(PacketType.NAME);
if (res.readUInt32AsInt() == 1)
return res.readString();
return res.readStringAsBytes();
else
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
}
/** Using UTF-8 */
protected static String readSingleName(Response res)
throws IOException {
return readSingleName(res, IOUtils.UTF8);
}
/** Using any character set */
protected static String readSingleName(Response res, Charset charset)
throws IOException {
return new String(readSingleNameAsBytes(res), charset);
}
protected synchronized void transmit(SFTPPacket<Request> payload)
throws IOException {
final int len = payload.available();

View File

@@ -60,14 +60,12 @@ public class SFTPFileTransfer
}
@Override
public void upload(LocalSourceFile localFile, String remotePath)
throws IOException {
public void upload(LocalSourceFile localFile, String remotePath) throws IOException {
new Uploader(localFile, remotePath).upload(getTransferListener());
}
@Override
public void download(String source, LocalDestFile dest)
throws IOException {
public void download(String source, LocalDestFile dest) throws IOException {
final PathComponents pathComponents = engine.getPathHelper().getComponents(source);
final FileAttributes attributes = engine.stat(source);
new Downloader().download(getTransferListener(), new RemoteResourceInfo(pathComponents, attributes), dest);
@@ -91,10 +89,10 @@ public class SFTPFileTransfer
private class Downloader {
@SuppressWarnings("PMD.MissingBreakInSwitch")
private void download(final TransferListener listener,
final RemoteResourceInfo remote,
final LocalDestFile local)
throws IOException {
final LocalDestFile local) throws IOException {
final LocalDestFile adjustedFile;
switch (remote.getAttributes().getType()) {
case DIRECTORY:
@@ -104,8 +102,7 @@ public class SFTPFileTransfer
log.warn("Server did not supply information about the type of file at `{}` " +
"-- assuming it is a regular file!", remote.getPath());
case REGULAR:
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()),
remote, local);
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()), remote, local);
break;
default:
throw new IOException(remote + " is not a regular file or directory");

View File

@@ -15,34 +15,48 @@
*/
package net.schmizz.sshj.signature;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.common.SecurityUtils;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.*;
/** An abstract class for {@link Signature} that implements common functionality. */
/**
* An abstract class for {@link Signature} that implements common functionality.
*/
public abstract class AbstractSignature
implements Signature {
protected final String algorithm;
protected java.security.Signature signature;
@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
protected final java.security.Signature signature;
protected AbstractSignature(String algorithm) {
this.algorithm = algorithm;
try {
this.signature = SecurityUtils.getSignature(algorithm);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
}
}
protected AbstractSignature(@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
java.security.Signature signatureEngine) {
this.signature = signatureEngine;
}
@Override
public void init(PublicKey publicKey, PrivateKey privateKey) {
public void initVerify(PublicKey publicKey) {
try {
signature = SecurityUtils.getSignature(algorithm);
if (publicKey != null)
signature.initVerify(publicKey);
if (privateKey != null)
signature.initSign(privateKey);
} catch (GeneralSecurityException e) {
signature.initVerify(publicKey);
} catch (InvalidKeyException e) {
throw new SSHRuntimeException(e);
}
}
@Override
public void initSign(PrivateKey privateKey) {
try {
signature.initSign(privateKey);
} catch (InvalidKeyException e) {
throw new SSHRuntimeException(e);
}
}
@@ -70,23 +84,24 @@ public abstract class AbstractSignature
}
}
protected byte[] extractSig(byte[] sig) {
if (sig[0] == 0 && sig[1] == 0 && sig[2] == 0) {
int i = 0;
int j = sig[i++] << 24 & 0xff000000
| sig[i++] << 16 & 0x00ff0000
| sig[i++] << 8 & 0x0000ff00
| sig[i++] & 0x000000ff;
i += j;
j = sig[i++] << 24 & 0xff000000
| sig[i++] << 16 & 0x00ff0000
| sig[i++] << 8 & 0x0000ff00
| sig[i++] & 0x000000ff;
byte[] newSig = new byte[j];
System.arraycopy(sig, i, newSig, 0, j);
sig = newSig;
/**
* Check whether the signature is generated using the expected algorithm, and if so, return the signature blob
*
* @param sig The full signature
* @param expectedKeyAlgorithm The expected key algorithm
* @return The blob part of the signature
*/
protected byte[] extractSig(byte[] sig, String expectedKeyAlgorithm) {
Buffer.PlainBuffer buffer = new Buffer.PlainBuffer(sig);
try {
String algo = buffer.readString();
if (!expectedKeyAlgorithm.equals(algo)) {
throw new SSHRuntimeException("Expected '" + expectedKeyAlgorithm + "' key algorithm, but got: " + algo);
}
return buffer.readBytes();
} catch (Buffer.BufferException e) {
throw new SSHRuntimeException(e);
}
return sig;
}
}
}

View File

@@ -22,13 +22,24 @@ import java.security.PublicKey;
public interface Signature {
/**
* Initialize this signature with the given public key and private key. If the private key is null, only signature
* verification can be performed.
* Initialize this signature with the given public key for signature verification.
*
* @param pubkey (null-ok) specify in case verification is needed
* @param prvkey (null-ok) specify in case signing is needed
* Note that subsequent calls to either {@link #initVerify(PublicKey)} or {@link #initSign(PrivateKey)} will
* overwrite prior initialization.
*
* @param pubkey the public key to use for signature verification
*/
void init(PublicKey pubkey, PrivateKey prvkey);
void initVerify(PublicKey pubkey);
/**
* Initialize this signature with the given private key for signing.
*
* Note that subsequent calls to either {@link #initVerify(PublicKey)} or {@link #initSign(PrivateKey)} will
* overwrite prior initialization.
*
* @param prvkey the private key to use for signing
*/
void initSign(PrivateKey prvkey);
/**
* Convenience method, same as calling {@link #update(byte[], int, int)} with offset as {@code 0} and {@code

View File

@@ -17,14 +17,23 @@ package net.schmizz.sshj.signature;
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.Arrays;
/** DSA {@link Signature} */
/**
* DSA {@link Signature}
*/
public class SignatureDSA
extends AbstractSignature {
/** A named factory for DSA signature */
/**
* A named factory for DSA signature
*/
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Signature> {
@@ -74,33 +83,33 @@ public class SignatureDSA
@Override
public boolean verify(byte[] sig) {
sig = extractSig(sig);
// ASN.1
int frst = (sig[0] & 0x80) != 0 ? 1 : 0;
int scnd = (sig[20] & 0x80) != 0 ? 1 : 0;
int length = sig.length + 6 + frst + scnd;
byte[] tmp = new byte[length];
tmp[0] = (byte) 0x30;
tmp[1] = (byte) 0x2c;
tmp[1] += frst;
tmp[1] += scnd;
tmp[2] = (byte) 0x02;
tmp[3] = (byte) 0x14;
tmp[3] += frst;
System.arraycopy(sig, 0, tmp, 4 + frst, 20);
tmp[4 + tmp[3]] = (byte) 0x02;
tmp[5 + tmp[3]] = (byte) 0x14;
tmp[5 + tmp[3]] += scnd;
System.arraycopy(sig, 20, tmp, 6 + tmp[3] + scnd, 20);
sig = tmp;
try {
return signature.verify(sig);
byte[] sigBlob = extractSig(sig, "ssh-dss");
return signature.verify(asnEncode(sigBlob));
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
} catch (IOException e) {
throw new SSHRuntimeException(e);
}
}
/**
* Encodes the signature as a DER sequence (ASN.1 format).
*/
private byte[] asnEncode(byte[] sigBlob) throws IOException {
byte[] r = new BigInteger(1, Arrays.copyOfRange(sigBlob, 0, 20)).toByteArray();
byte[] s = new BigInteger(1, Arrays.copyOfRange(sigBlob, 20, 40)).toByteArray();
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(new ASN1Integer(r));
vector.add(new ASN1Integer(s));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
asnOS.writeObject(new DERSequence(vector));
asnOS.flush();
return baos.toByteArray();
}
}

View File

@@ -18,32 +18,69 @@ package net.schmizz.sshj.signature;
import net.schmizz.sshj.common.Buffer;
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.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SignatureException;
/** ECDSA {@link Signature} */
public class SignatureECDSA
extends AbstractSignature {
public class SignatureECDSA extends AbstractSignature {
/** A named factory for ECDSA signature */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Signature> {
/** A named factory for ECDSA-256 signature */
public static class Factory256 implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureECDSA();
return new SignatureECDSA("SHA256withECDSA", KeyType.ECDSA256.toString());
}
@Override
public String getName() {
return KeyType.ECDSA.toString();
return KeyType.ECDSA256.toString();
}
}
public SignatureECDSA() {
super("SHA256withECDSA");
/** A named factory for ECDSA-384 signature */
public static class Factory384 implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureECDSA("SHA384withECDSA", KeyType.ECDSA384.toString());
}
@Override
public String getName() {
return KeyType.ECDSA384.toString();
}
}
/** A named factory for ECDSA-521 signature */
public static class Factory521 implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureECDSA("SHA512withECDSA", KeyType.ECDSA521.toString());
}
@Override
public String getName() {
return KeyType.ECDSA521.toString();
}
}
private String keyTypeName;
public SignatureECDSA(String algorithm, String keyTypeName) {
super(algorithm);
this.keyTypeName = keyTypeName;
}
@Override
@@ -61,7 +98,7 @@ public class SignatureECDSA
System.arraycopy(sig, 4, r, 0, rLen);
System.arraycopy(sig, 6 + rLen, s, 0, sLen);
Buffer buf = new Buffer.PlainBuffer();
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
buf.putMPInt(new BigInteger(r));
buf.putMPInt(new BigInteger(s));
@@ -70,68 +107,34 @@ public class SignatureECDSA
@Override
public boolean verify(byte[] sig) {
byte[] r;
byte[] s;
try {
Buffer sigbuf = new Buffer.PlainBuffer(sig);
final String algo = new String(sigbuf.readBytes());
if (!"ecdsa-sha2-nistp256".equals(algo)) {
throw new SSHRuntimeException(String.format("Signature :: ecdsa-sha2-nistp256 expected, got %s", algo));
}
final int rsLen = sigbuf.readUInt32AsInt();
if (!(sigbuf.available() == rsLen)) {
throw new SSHRuntimeException("Invalid key length");
}
r = sigbuf.readBytes();
s = sigbuf.readBytes();
} catch (Exception e) {
throw new SSHRuntimeException(e);
}
int rLen = r.length;
int sLen = s.length;
/* We can't have the high bit set, so add an extra zero at the beginning if so. */
if ((r[0] & 0x80) != 0) {
rLen++;
}
if ((s[0] & 0x80) != 0) {
sLen++;
}
/* Calculate total output length */
int length = 6 + rLen + sLen;
byte[] asn1 = new byte[length];
/* ASN.1 SEQUENCE tag */
asn1[0] = (byte) 0x30;
/* Size of SEQUENCE */
asn1[1] = (byte) (4 + rLen + sLen);
/* ASN.1 INTEGER tag */
asn1[2] = (byte) 0x02;
/* "r" INTEGER length */
asn1[3] = (byte) rLen;
/* Copy in the "r" INTEGER */
System.arraycopy(r, 0, asn1, 4, rLen);
/* ASN.1 INTEGER tag */
asn1[rLen + 4] = (byte) 0x02;
/* "s" INTEGER length */
asn1[rLen + 5] = (byte) sLen;
/* Copy in the "s" INTEGER */
System.arraycopy(s, 0, asn1, (6 + rLen), sLen);
try {
return signature.verify(asn1);
byte[] sigBlob = extractSig(sig, keyTypeName);
return signature.verify(asnEncode(sigBlob));
} catch (SignatureException e) {
throw new SSHRuntimeException(e);
} catch (IOException e) {
throw new SSHRuntimeException(e);
}
}
/**
* Encodes the signature as a DER sequence (ASN.1 format).
*/
private byte[] asnEncode(byte[] sigBlob) throws IOException {
Buffer.PlainBuffer sigbuf = new Buffer.PlainBuffer(sigBlob);
byte[] r = sigbuf.readBytes();
byte[] s = sigbuf.readBytes();
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(new ASN1Integer(r));
vector.add(new ASN1Integer(s));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
asnOS.writeObject(new DERSequence(vector));
asnOS.flush();
return baos.toByteArray();
}
}

View File

@@ -51,7 +51,7 @@ public class SignatureRSA
@Override
public boolean verify(byte[] sig) {
sig = extractSig(sig);
sig = extractSig(sig, "ssh-rsa");
try {
return signature.verify(sig);
} catch (SignatureException e) {

View File

@@ -44,6 +44,7 @@ abstract class Converter {
protected int cipherSize = 8;
protected long seq = -1;
protected boolean authed;
protected boolean etm;
long getSequenceNumber() {
return seq;
@@ -56,6 +57,7 @@ abstract class Converter {
if (compression != null)
compression.init(getCompressionType());
this.cipherSize = cipher.getIVSize();
this.etm = mac.isEtm();
}
void setAuthenticated() {

View File

@@ -16,14 +16,14 @@
package net.schmizz.sshj.transport;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
import net.schmizz.sshj.transport.mac.MAC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Decodes packets from the SSH binary protocol per the current algorithms. */
/**
* Decodes packets from the SSH binary protocol per the current algorithms.
*/
final class Decoder
extends Converter {
@@ -31,16 +31,26 @@ final class Decoder
private final Logger log;
/** What we pass decoded packets to */
/**
* What we pass decoded packets to
*/
private final SSHPacketHandler packetHandler;
/** Buffer where as-yet undecoded data lives */
/**
* Buffer where as-yet undecoded data lives
*/
private final SSHPacket inputBuffer = new SSHPacket();
/** Used in case compression is active to store the uncompressed data */
/**
* Used in case compression is active to store the uncompressed data
*/
private final SSHPacket uncompressBuffer = new SSHPacket();
/** MAC result is stored here */
/**
* MAC result is stored here
*/
private byte[] macResult;
/** -1 if packet length not yet been decoded, else the packet length */
/**
* -1 if packet length not yet been decoded, else the packet length
*/
private int packetLength = -1;
/**
@@ -62,53 +72,97 @@ final class Decoder
*/
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 {
int need;
/* Decoding loop */
for (; ; )
if (packetLength == -1) // Waiting for beginning of packet
{
if (packetLength == -1) { // Waiting for beginning of packet
assert inputBuffer.rpos() == 0 : "buffer cleared";
need = cipherSize - inputBuffer.available();
if (need <= 0)
if (need <= 0) {
packetLength = decryptLength();
else
} else {
// Need more data
break;
}
} else {
assert inputBuffer.rpos() == 4 : "packet length read";
need = packetLength + (mac != null ? mac.getBlockSize() : 0) - inputBuffer.available();
if (need <= 0) {
decryptPayload(inputBuffer.array());
decryptBuffer(cipherSize, packetLength + 4 - cipherSize); // Decrypt the rest of the payload
seq = seq + 1 & 0xffffffffL;
if (mac != null)
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())
if (log.isTraceEnabled()) {
log.trace("Received packet #{}: {}", seq, plain.printHex());
}
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet
inputBuffer.clear();
packetLength = -1;
} else
} else {
// Need more data
break;
}
}
return need;
@@ -120,8 +174,9 @@ final class Decoder
mac.update(data, 0, packetLength + 4); // packetLength+4 = entire packet w/o mac
mac.doFinal(macResult, 0); // compute
// Check against the received MAC
if (!ByteArrayUtils.equals(macResult, 0, data, packetLength + 4, mac.getBlockSize()))
if (!ByteArrayUtils.equals(macResult, 0, data, packetLength + 4, mac.getBlockSize())) {
throw new TransportException(DisconnectReason.MAC_ERROR, "MAC Error");
}
}
private SSHPacket decompressed()
@@ -133,7 +188,7 @@ final class Decoder
private int decryptLength()
throws TransportException {
cipher.update(inputBuffer.array(), 0, cipherSize);
decryptBuffer(0, cipherSize);
final int len; // Read packet length
try {
@@ -142,22 +197,26 @@ final class Decoder
throw new TransportException(be);
}
if (isInvalidPacketLength(len)) { // Check packet length validity
log.error("Error decoding packet (invalid length) {}", inputBuffer.printHex());
throw new TransportException(DisconnectReason.PROTOCOL_ERROR, "invalid packet length: " + len);
}
checkPacketLength(len);
return len;
}
private static boolean isInvalidPacketLength(int len) {
return len < 5 || len > MAX_PACKET_LEN;
private void decryptBuffer(int offset, int length) {
cipher.update(inputBuffer.array(), offset, length);
}
private void decryptPayload(final byte[] data) {
cipher.update(data, cipherSize, packetLength + 4 - cipherSize);
private void checkPacketLength(int len) throws TransportException {
if (len < 5 || len > MAX_PACKET_LEN) { // Check packet length validity
log.error("Error decoding packet (invalid length) {}", inputBuffer.printHex());
throw new TransportException(DisconnectReason.PROTOCOL_ERROR, "invalid packet length: " + len);
}
}
// private void decryptPayload(final byte[] data, int offset, int length) {
// cipher.update(data, cipherSize, packetLength + 4 - cipherSize);
// }
/**
* Adds {@code len} bytes from {@code b} to the decoder buffer. When a packet has been successfully decoded, hooks
* in to {@link SSHPacketHandler#handle} of the {@link SSHPacketHandler} this decoder was initialized with.

View File

@@ -39,19 +39,6 @@ final class Encoder
log = loggerFactory.getLogger(getClass());
}
private SSHPacket checkHeaderSpace(SSHPacket buffer) {
if (buffer.rpos() < 5) {
log.warn("Performance cost: when sending a packet, ensure that "
+ "5 bytes are available in front of the buffer");
SSHPacket nb = new SSHPacket(buffer.available() + 5);
nb.rpos(5);
nb.wpos(5);
nb.putBuffer(buffer);
buffer = nb;
}
return buffer;
}
private void compress(SSHPacket buffer) {
compression.compress(buffer);
}
@@ -75,39 +62,63 @@ final class Encoder
long encode(SSHPacket buffer) {
encodeLock.lock();
try {
if (log.isTraceEnabled())
log.trace("Encoding packet #{}: {}", seq, buffer.printHex());
if (log.isTraceEnabled()) {
// Add +1 to seq as we log before actually incrementing the sequence.
log.trace("Encoding packet #{}: {}", seq + 1, buffer.printHex());
}
if (usingCompression())
if (usingCompression()) {
compress(buffer);
}
final int payloadSize = buffer.available();
int lengthWithoutPadding;
if (etm) {
// in Encrypt-Then-Mac mode, the length field is not encrypted, so we should keep it out of the
// padding length calculation
lengthWithoutPadding = 1 + payloadSize; // padLength (1 byte) + payload
} else {
lengthWithoutPadding = 4 + 1 + payloadSize; // packetLength (4 bytes) + padLength (1 byte) + payload
}
// Compute padding length
int padLen = -(payloadSize + 5) & cipherSize - 1;
if (padLen < cipherSize)
int padLen = cipherSize - (lengthWithoutPadding % cipherSize);
if (padLen < 4) {
padLen += cipherSize;
}
final int startOfPacket = buffer.rpos() - 5;
final int packetLen = payloadSize + 1 + padLen;
int packetLen = 1 + payloadSize + padLen; // packetLength = padLen (1 byte) + payload + padding
if (packetLen < 16) {
padLen += cipherSize;
packetLen = 1 + payloadSize + padLen;
}
final int endOfPadding = startOfPacket + 4 + packetLen;
// Put packet header
buffer.wpos(startOfPacket);
buffer.putUInt32(packetLen);
buffer.putByte((byte) padLen);
// Now wpos will mark end of padding
buffer.wpos(startOfPacket + 5 + payloadSize + padLen);
buffer.wpos(endOfPadding);
// Fill padding
prng.fill(buffer.array(), buffer.wpos() - padLen, padLen);
prng.fill(buffer.array(), endOfPadding - padLen, padLen);
seq = seq + 1 & 0xffffffffL;
if (mac != null)
putMAC(buffer, startOfPacket, buffer.wpos());
cipher.update(buffer.array(), startOfPacket, 4 + packetLen);
if (etm) {
cipher.update(buffer.array(), startOfPacket + 4, packetLen);
putMAC(buffer, startOfPacket, endOfPadding);
} else {
if (mac != null) {
putMAC(buffer, startOfPacket, endOfPadding);
}
cipher.update(buffer.array(), startOfPacket, 4 + packetLen);
}
buffer.rpos(startOfPacket); // Make ready-to-read
return seq;

View File

@@ -26,7 +26,6 @@ import net.schmizz.sshj.transport.mac.MAC;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
@@ -158,6 +157,7 @@ final class KeyExchanger
"Key exchange packet received when key exchange was not ongoing");
}
@SuppressWarnings("PMD.CompareObjectsWithEquals")
private static void ensureReceivedMatchesExpected(Message got, Message expected)
throws TransportException {
if (got != expected)
@@ -197,6 +197,12 @@ final class KeyExchanger
if (hkv.verify(transport.getRemoteHost(), transport.getRemotePort(), key))
return;
}
log.error("Disconnecting because none of the configured Host key verifiers ({}) could verify '{}' host key with fingerprint {} for {}:{}",
hostVerifiers,
KeyType.fromKey(key),
SecurityUtils.getFingerprint(key),
transport.getRemoteHost(),
transport.getRemotePort());
throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE,
"Could not verify `" + KeyType.fromKey(key)

View File

@@ -16,7 +16,6 @@
package net.schmizz.sshj.transport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.net.SocketTimeoutException;
@@ -61,10 +60,8 @@ public final class Reader
}
}
} catch (Exception e) {
//noinspection StatementWithEmptyBody
if (isInterrupted()) {
// We are meant to shut up and draw to a close if interrupted
} else {
// We are meant to shut up and draw to a close if interrupted
if (!isInterrupted()) {
trans.die(e);
}
}

View File

@@ -33,7 +33,9 @@ import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/** A thread-safe {@link Transport} implementation. */
/**
* A thread-safe {@link Transport} implementation.
*/
public final class TransportImpl
implements Transport, DisconnectListener {
@@ -49,11 +51,11 @@ public final class TransportImpl
static final class ConnInfo {
final String host;
final int port;
final InputStream in;
final OutputStream out;
public ConnInfo(String host, int port, InputStream in, OutputStream out) {
ConnInfo(String host, int port, InputStream in, OutputStream out) {
this.host = host;
this.port = port;
this.in = in;
@@ -88,24 +90,32 @@ public final class TransportImpl
private final Event<TransportException> close;
/** Client version identification string */
/**
* Client version identification string
*/
private final String clientID;
private volatile int timeoutMs = 30 * 1000; // Crazy long, but it was the original default
private volatile boolean authed = false;
/** Currently active service e.g. UserAuthService, ConnectionService */
/**
* Currently active service e.g. UserAuthService, ConnectionService
*/
private volatile Service service;
private DisconnectListener disconnectListener;
private ConnInfo connInfo;
/** Server version identification string */
/**
* Server version identification string
*/
private String serverID;
/** Message identifier of last packet received */
/**
* Message identifier of last packet received
*/
private Message msg;
private final ReentrantLock writeLock = new ReentrantLock();
@@ -115,9 +125,9 @@ public final class TransportImpl
this.loggerFactory = config.getLoggerFactory();
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
this.nullService = new NullService(this);
this.nullService = new NullService(this);
this.service = nullService;
this.log = loggerFactory.getLogger(getClass());
this.log = loggerFactory.getLogger(getClass());
this.disconnectListener = this;
this.reader = new Reader(this);
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock, loggerFactory);
@@ -138,7 +148,7 @@ public final class TransportImpl
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
this.log = loggerFactory.getLogger(getClass());
this.nullService = new NullService(this);
this.nullService = new NullService(this);
this.service = nullService;
this.disconnectListener = this;
this.reader = new Reader(this);
@@ -186,14 +196,18 @@ public final class TransportImpl
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
while ((serverID = readIdentification(buf)).isEmpty()) {
int b = connInfo.in.read();
if (b == -1)
if (b == -1) {
log.error("Received end of connection, but no identification received. ");
throw new TransportException("Server closed connection during identification exchange");
}
buf.putByte((byte) b);
}
}
/**
* Receive the server identification string.
*
* @throws IOException If there was an error writing to the outputstream.
*/
private void sendClientIdent() throws IOException {
@@ -212,9 +226,7 @@ public final class TransportImpl
* This is not efficient but is only done once.
*
* @param buffer The buffer to read from.
*
* @return empty string if full ident string has not yet been received
*
* @throws IOException
*/
private String readIdentification(Buffer.PlainBuffer buffer)
@@ -226,7 +238,7 @@ public final class TransportImpl
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
"Server does not support SSHv2, identified as: " + ident);
"Server does not support SSHv2, identified as: " + ident);
return ident;
}
@@ -337,7 +349,6 @@ public final class TransportImpl
* Sends a service request for the specified service
*
* @param serviceName name of the service being requested
*
* @throws TransportException if there is an error while sending the request
*/
private void sendServiceRequest(String serviceName)
@@ -456,9 +467,9 @@ public final class TransportImpl
log.debug("Sending SSH_MSG_DISCONNECT: reason=[{}], msg=[{}]", reason, message);
try {
write(new SSHPacket(Message.DISCONNECT)
.putUInt32(reason.toInt())
.putString(message)
.putString(""));
.putUInt32(reason.toInt())
.putString(message)
.putString(""));
} catch (IOException worthless) {
log.debug("Error writing packet: {}", worthless.toString());
}
@@ -476,7 +487,6 @@ public final class TransportImpl
*
* @param msg the message identifer
* @param buf buffer containg rest of the packet
*
* @throws SSHException if an error occurs during handling (unrecoverable)
*/
@Override
@@ -494,32 +504,27 @@ public final class TransportImpl
else
switch (msg) {
case DISCONNECT: {
case DISCONNECT:
gotDisconnect(buf);
break;
}
case IGNORE: {
case IGNORE:
log.debug("Received SSH_MSG_IGNORE");
break;
}
case UNIMPLEMENTED: {
case UNIMPLEMENTED:
gotUnimplemented(buf);
break;
}
case DEBUG: {
case DEBUG:
gotDebug(buf);
break;
}
case SERVICE_ACCEPT: {
case SERVICE_ACCEPT:
gotServiceAccept();
break;
}
case USERAUTH_BANNER: {
case USERAUTH_BANNER:
log.debug("Received USERAUTH_BANNER");
break;
}
default:
sendUnimplemented();
break;
}
}
@@ -552,7 +557,7 @@ public final class TransportImpl
try {
if (!serviceAccept.hasWaiters())
throw new TransportException(DisconnectReason.PROTOCOL_ERROR,
"Got a service accept notification when none was awaited");
"Got a service accept notification when none was awaited");
serviceAccept.set();
} finally {
serviceAccept.unlock();
@@ -563,7 +568,6 @@ public final class TransportImpl
* Got an SSH_MSG_UNIMPLEMENTED, so lets see where we're at and act accordingly.
*
* @param packet The 'unimplemented' packet received
*
* @throws TransportException
*/
private void gotUnimplemented(SSHPacket packet)
@@ -586,7 +590,7 @@ public final class TransportImpl
try {
if (!close.isSet()) {
log.error("Dying because - {}", ex);
log.error("Dying because - {}", ex.getMessage(), ex);
final SSHException causeOfDeath = SSHException.chainer.chain(ex);

View File

@@ -15,7 +15,14 @@
*/
package net.schmizz.sshj.transport.cipher;
/** {@code aes128-cbc} cipher */
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
/**
* {@code aes128-cbc} cipher
*
* @deprecated Use {@link BlockCiphers#AES128CBC()}
*/
@Deprecated
public class AES128CBC
extends BlockCipher {
@@ -32,6 +39,11 @@ public class AES128CBC
public String getName() {
return "aes128-cbc";
}
@Override
public String toString() {
return getName();
}
}
public AES128CBC() {

View File

@@ -15,11 +15,18 @@
*/
package net.schmizz.sshj.transport.cipher;
/** {@code aes128-ctr} cipher */
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
/**
* {@code aes128-ctr} cipher
*
* @deprecated Use {@link BlockCiphers#AES128CTR()}
*/
@Deprecated
public class AES128CTR
extends BlockCipher {
/** Named factory for AES128CBC Cipher */
/** Named factory for AES128CTR Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@@ -32,6 +39,11 @@ public class AES128CTR
public String getName() {
return "aes128-ctr";
}
@Override
public String toString() {
return getName();
}
}
public AES128CTR() {

View File

@@ -15,7 +15,14 @@
*/
package net.schmizz.sshj.transport.cipher;
/** {@code aes192-cbc} cipher */
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
/**
* {@code aes192-cbc} cipher
*
* @deprecated Use {@link BlockCiphers#AES192CBC()}
*/
@Deprecated
public class AES192CBC
extends BlockCipher {
@@ -32,6 +39,11 @@ public class AES192CBC
public String getName() {
return "aes192-cbc";
}
@Override
public String toString() {
return getName();
}
}
public AES192CBC() {

View File

@@ -15,7 +15,14 @@
*/
package net.schmizz.sshj.transport.cipher;
/** {@code aes192-ctr} cipher */
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
/**
* {@code aes192-ctr} cipher
*
* @deprecated Use {@link BlockCiphers#AES192CTR()}
*/
@Deprecated
public class AES192CTR
extends BlockCipher {
@@ -32,6 +39,11 @@ public class AES192CTR
public String getName() {
return "aes192-ctr";
}
@Override
public String toString() {
return getName();
}
}
public AES192CTR() {

View File

@@ -15,7 +15,14 @@
*/
package net.schmizz.sshj.transport.cipher;
/** {@code aes256-ctr} cipher */
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
/**
* {@code aes256-cbc} cipher
*
* @deprecated Use {@link BlockCiphers#AES256CBC()}
*/
@Deprecated
public class AES256CBC
extends BlockCipher {
@@ -32,6 +39,11 @@ public class AES256CBC
public String getName() {
return "aes256-cbc";
}
@Override
public String toString() {
return getName();
}
}
public AES256CBC() {

View File

@@ -15,11 +15,18 @@
*/
package net.schmizz.sshj.transport.cipher;
/** {@code aes256-ctr} cipher */
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
/**
* {@code aes256-ctr} cipher
*
* @deprecated Use {@link BlockCiphers#AES256CTR()}
*/
@Deprecated
public class AES256CTR
extends BlockCipher {
/** Named factory for AES256CBC Cipher */
/** Named factory for AES256CTR Cipher */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
@@ -32,6 +39,11 @@ public class AES256CTR
public String getName() {
return "aes256-ctr";
}
@Override
public String toString() {
return getName();
}
}
public AES256CTR() {

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