├── .editorconfig
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── ci.yml
│ └── gradle-wrapper-validation.yaml
├── .gitignore
├── LICENSE.txt
├── README.md
├── build.gradle.kts
├── config
├── checkstyle
│ ├── checkstyle.xml
│ └── suppressions.xml
└── quality.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
├── spotless.xml.prefs
└── src
├── main
└── java
│ └── com
│ └── trilead
│ └── ssh2
│ ├── AuthAgentCallback.java
│ ├── ChannelCondition.java
│ ├── Connection.java
│ ├── ConnectionInfo.java
│ ├── ConnectionMonitor.java
│ ├── DHGexParameters.java
│ ├── DebugLogger.java
│ ├── DynamicPortForwarder.java
│ ├── ExtendedServerHostKeyVerifier.java
│ ├── ExtensionInfo.java
│ ├── HTTPProxyData.java
│ ├── HTTPProxyException.java
│ ├── InteractiveCallback.java
│ ├── KnownHosts.java
│ ├── LocalPortForwarder.java
│ ├── LocalStreamForwarder.java
│ ├── ProxyData.java
│ ├── SCPClient.java
│ ├── SFTPException.java
│ ├── SFTPv3Client.java
│ ├── SFTPv3DirectoryEntry.java
│ ├── SFTPv3FileAttributes.java
│ ├── SFTPv3FileHandle.java
│ ├── ServerHostKeyVerifier.java
│ ├── Session.java
│ ├── StreamGobbler.java
│ ├── auth
│ ├── AuthenticationManager.java
│ └── SignatureProxy.java
│ ├── channel
│ ├── AuthAgentForwardThread.java
│ ├── Channel.java
│ ├── ChannelInputStream.java
│ ├── ChannelManager.java
│ ├── ChannelOutputStream.java
│ ├── DynamicAcceptThread.java
│ ├── IChannelWorkerThread.java
│ ├── LocalAcceptThread.java
│ ├── RemoteAcceptThread.java
│ ├── RemoteForwardingData.java
│ ├── RemoteX11AcceptThread.java
│ ├── StreamForwarder.java
│ └── X11ServerData.java
│ ├── compression
│ ├── CompressionFactory.java
│ ├── ICompressor.java
│ ├── Zlib.java
│ └── ZlibOpenSSH.java
│ ├── crypto
│ ├── Base64.java
│ ├── CryptoWishList.java
│ ├── KeyMaterial.java
│ ├── PEMDecoder.java
│ ├── PEMStructure.java
│ ├── SimpleDERReader.java
│ ├── cipher
│ │ ├── AES.java
│ │ ├── BlockCipher.java
│ │ ├── BlockCipherFactory.java
│ │ ├── BlowFish.java
│ │ ├── CBCMode.java
│ │ ├── CTRMode.java
│ │ ├── CipherInputStream.java
│ │ ├── CipherOutputStream.java
│ │ ├── DES.java
│ │ ├── DESede.java
│ │ ├── EtmCipher.java
│ │ └── NullCipher.java
│ ├── dh
│ │ ├── Curve25519Exchange.java
│ │ ├── DhExchange.java
│ │ ├── DhGroupExchange.java
│ │ ├── EcDhExchange.java
│ │ └── GenericDhExchange.java
│ ├── digest
│ │ ├── HMAC.java
│ │ ├── HashForSSH2Types.java
│ │ ├── MAC.java
│ │ └── MACs.java
│ └── keys
│ │ ├── Ed25519KeyFactory.java
│ │ ├── Ed25519KeyPairGenerator.java
│ │ ├── Ed25519PrivateKey.java
│ │ ├── Ed25519Provider.java
│ │ └── Ed25519PublicKey.java
│ ├── log
│ └── Logger.java
│ ├── packets
│ ├── PacketChannelAuthAgentReq.java
│ ├── PacketChannelOpenConfirmation.java
│ ├── PacketChannelOpenFailure.java
│ ├── PacketChannelTrileadPing.java
│ ├── PacketChannelWindowAdjust.java
│ ├── PacketDisconnect.java
│ ├── PacketExtInfo.java
│ ├── PacketGlobalCancelForwardRequest.java
│ ├── PacketGlobalForwardRequest.java
│ ├── PacketGlobalTrileadPing.java
│ ├── PacketIgnore.java
│ ├── PacketKexDHInit.java
│ ├── PacketKexDHReply.java
│ ├── PacketKexDhGexGroup.java
│ ├── PacketKexDhGexInit.java
│ ├── PacketKexDhGexReply.java
│ ├── PacketKexDhGexRequest.java
│ ├── PacketKexDhGexRequestOld.java
│ ├── PacketKexInit.java
│ ├── PacketNewKeys.java
│ ├── PacketOpenDirectTCPIPChannel.java
│ ├── PacketOpenSessionChannel.java
│ ├── PacketServiceAccept.java
│ ├── PacketServiceRequest.java
│ ├── PacketSessionExecCommand.java
│ ├── PacketSessionPtyRequest.java
│ ├── PacketSessionPtyResize.java
│ ├── PacketSessionStartShell.java
│ ├── PacketSessionSubsystemRequest.java
│ ├── PacketSessionX11Request.java
│ ├── PacketUserauthBanner.java
│ ├── PacketUserauthFailure.java
│ ├── PacketUserauthInfoRequest.java
│ ├── PacketUserauthInfoResponse.java
│ ├── PacketUserauthRequestInteractive.java
│ ├── PacketUserauthRequestNone.java
│ ├── PacketUserauthRequestPassword.java
│ ├── PacketUserauthRequestPublicKey.java
│ ├── Packets.java
│ ├── TypesReader.java
│ └── TypesWriter.java
│ ├── sftp
│ ├── AttrTextHints.java
│ ├── AttribBits.java
│ ├── AttribFlags.java
│ ├── AttribPermissions.java
│ ├── AttribTypes.java
│ ├── ErrorCodes.java
│ ├── OpenFlags.java
│ └── Packet.java
│ ├── signature
│ ├── DSASHA1Verify.java
│ ├── ECDSASHA2Verify.java
│ ├── Ed25519Verify.java
│ ├── RSASHA1Verify.java
│ ├── RSASHA256Verify.java
│ ├── RSASHA512Verify.java
│ └── SSHSignature.java
│ ├── transport
│ ├── ClientServerHello.java
│ ├── KexManager.java
│ ├── KexParameters.java
│ ├── KexState.java
│ ├── MessageHandler.java
│ ├── NegotiateException.java
│ ├── NegotiatedParameters.java
│ ├── TransportConnection.java
│ └── TransportManager.java
│ └── util
│ ├── TimeoutService.java
│ └── Tokenizer.java
└── test
├── java
└── com
│ └── trilead
│ └── ssh2
│ ├── AsyncSSHCompatibilityTest.java
│ ├── DropbearCompatibilityTest.java
│ ├── ExtensionInfoTest.java
│ ├── KnownHostsTest.java
│ ├── OpenSSHCompatibilityTest.java
│ ├── PubkeyConstants.java
│ ├── SshLogger.java
│ ├── TestExtendedHostKeyVerifier.java
│ ├── compression
│ └── CompressionFactoryTest.java
│ ├── crypto
│ ├── PEMDecoderTest.java
│ ├── SimpleDERReaderTest.java
│ ├── cipher
│ │ ├── AESCBCTest.java
│ │ ├── AESCTRTest.java
│ │ └── AESTest.java
│ ├── dh
│ │ └── Curve25519ExchangeTest.java
│ └── keys
│ │ ├── Ed25519KeyFactoryTest.java
│ │ ├── Ed25519KeyPairGeneratorTest.java
│ │ └── IsValidEdDSAKeyPair.java
│ ├── packets
│ ├── PacketExtInfoTest.java
│ ├── PacketsTest.java
│ └── TypesReaderTest.java
│ ├── signature
│ ├── ECDSASHA2VerifyTest.java
│ └── Ed25519VerifyTest.java
│ └── transport
│ └── KexManagerTest.java
└── resources
├── asyncssh-server
├── Dockerfile
├── requirements.txt
└── server.py
├── com
└── trilead
│ └── ssh2
│ ├── crypto
│ ├── dsa-openssh2-private-key.txt
│ ├── dsa-openssh2-private-key.txt.pub
│ ├── dsa-private-key.txt
│ ├── ecdsa-nistp256-openssh2-private-key.txt
│ ├── ecdsa-nistp256-openssh2-private-key.txt.pub
│ ├── ecdsa-nistp256-private-key.txt
│ ├── ecdsa-nistp384-openssh2-private-key.txt
│ ├── ecdsa-nistp384-openssh2-private-key.txt.pub
│ ├── ecdsa-nistp384-private-key.txt
│ ├── ecdsa-nistp521-openssh2-private-key.txt
│ ├── ecdsa-nistp521-openssh2-private-key.txt.pub
│ ├── ecdsa-nistp521-private-key.txt
│ ├── ed25519-openssh2-private-key.txt
│ ├── ed25519-openssh2-private-key.txt.pub
│ ├── rsa-openssh2-private-key.txt
│ ├── rsa-openssh2-private-key.txt.pub
│ └── rsa-private-key.txt
│ └── known_hosts
├── dropbear-server
├── Dockerfile
└── run.sh
├── logback-test.xml
└── openssh-server
├── Dockerfile
└── run.sh
/.editorconfig:
--------------------------------------------------------------------------------
1 | root=true
2 |
3 | [*]
4 | charset=utf-8
5 | end_of_line=lf
6 | insert_final_newline=true
7 | indent_style=space
8 | indent_size=4
9 |
10 | [*.java]
11 | indent_style=tab
12 | tab_width=4
13 |
14 | [{*.cql,*.ddl,*.sql}]
15 | indent_style=space
16 | indent_size=2
17 |
18 | [{*.yml,*.yaml}]
19 | indent_style=space
20 | indent_size=2
21 |
22 | [*.md]
23 | indent_style=space
24 | indent_size=2
25 |
26 | [gradlew]
27 | indent_style = unset
28 | indent_size = unset
29 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gradle
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | assignees:
9 | - kruton
10 | - package-ecosystem: docker
11 | directory: "/src/test/resources/asyncssh-server"
12 | schedule:
13 | interval: daily
14 | open-pull-requests-limit: 10
15 | assignees:
16 | - kruton
17 | - package-ecosystem: docker
18 | directory: "/src/test/resources/dropbear-server"
19 | schedule:
20 | interval: daily
21 | open-pull-requests-limit: 10
22 | assignees:
23 | - kruton
24 | - package-ecosystem: docker
25 | directory: "/src/test/resources/openssh-server"
26 | schedule:
27 | interval: daily
28 | open-pull-requests-limit: 10
29 | assignees:
30 | - kruton
31 | - package-ecosystem: "github-actions"
32 | directory: "/" # Location of package manifests
33 | schedule:
34 | interval: "weekly"
35 | open-pull-requests-limit: 10
36 | assignees:
37 | - kruton
38 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Gradle
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
3 |
4 | name: Continuous Integration
5 |
6 | on:
7 | push:
8 | branches: '*'
9 | pull_request:
10 | branches: [ main ]
11 | schedule:
12 | - cron: "0 7 * * *"
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | strategy:
18 | matrix:
19 | java: [ 11, 17 ]
20 |
21 | steps:
22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
23 | - name: Set up JDK ${{ matrix.java }}
24 | uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
25 | with:
26 | distribution: 'zulu'
27 | java-version: ${{ matrix.java }}
28 | - name: Cache Gradle Home files
29 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
30 | continue-on-error: true
31 | with:
32 | path: ~/.gradle/caches
33 | key: ${{ runner.os }}-gradle-home-examples-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }}
34 | - name: Grant execute permission for gradlew
35 | run: chmod +x gradlew
36 | - name: Build with Gradle
37 | run: ./gradlew build jacocoTestReport --info
38 | - name: Upload to Sonatype
39 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.java == '11'
40 | run: |
41 | echo "${{ secrets.MAVEN_GPG_PRIVATE_KEY }}" > ~/.gradle/secring.gpg.b64
42 | base64 -d ~/.gradle/secring.gpg.b64 > ~/.gradle/secring.gpg
43 | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -PsonatypeUsername=${SONATYPE_USERNAME} -PsonatypePassword=${SONATYPE_PASSWORD} -Psigning.keyId=${GPG_KEYID} -Psigning.secretKeyRingFile=$(echo ~/.gradle/secring.gpg) -Psigning.password=${GPG_PASSWORD}
44 | env:
45 | GPG_KEYID: ${{ secrets.MAVEN_GPG_KEYID }}
46 | GPG_PASSWORD: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
47 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
48 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
49 | - name: Upload coverate to CodeClimate
50 | uses: paambaati/codeclimate-action@f429536ee076d758a24705203199548125a28ca7 # v9.0.0
51 | env:
52 | CC_TEST_REPORTER_ID: 9c22853d2ecf28aec51c1b578072031a3a655790cffb9fecfed6101920e5446c
53 | JACOCO_SOURCE_PATH: "${{github.workspace}}/src/main/java"
54 | with:
55 | coverageLocations: ${{github.workspace}}/build/reports/jacoco/test/jacocoTestReport.xml:jacoco
56 |
--------------------------------------------------------------------------------
/.github/workflows/gradle-wrapper-validation.yaml:
--------------------------------------------------------------------------------
1 | name: "Validate Gradle Wrapper"
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | validation:
13 | name: "Validation"
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
17 | - uses: gradle/actions/wrapper-validation@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle files
2 | .gradle
3 | /local.properties
4 |
5 | # IDEA files
6 | /.idea/
7 | *.iml
8 | *.ipr
9 | *.iws
10 |
11 | # MacOS X stuff
12 | .DS_Store
13 |
14 | # Build output artifacts
15 | /build
16 |
17 | # Testing files
18 | /captures
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ConnectBot's SSH library
2 | [](https://github.com/connectbot/sshlib/actions/workflows/ci.yml)
3 | [](https://search.maven.org/artifact/org.connectbot/sshlib)
4 |
5 | This is ConnectBot's SSH library. It started as a continuation of the Trilead SSH2 library,
6 | but has had several features added to it since then.
7 |
8 | This library retains its original [3-Clause BSD license](
9 | https://opensource.org/licenses/BSD-3-Clause).
10 |
11 | ##### Encryption:
12 | * aes256-ctr ([RFC 4344](https://tools.ietf.org/html/rfc4344#section-4))
13 | * aes128-ctr ([RFC 4344](https://tools.ietf.org/html/rfc4344#section-4))
14 | * aes256-cbc ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.3))
15 | * aes128-cbc ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.3))
16 | * blowfish-ctr ([RFC 4344](https://tools.ietf.org/html/rfc4344#section-4))
17 | * blowfish-cbc ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.3))
18 | * 3des-ctr ([RFC 4344](https://tools.ietf.org/html/rfc4344#section-4))
19 | * 3des-cbc ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.3))
20 |
21 | ##### MACs:
22 | * hmac-sha2-512-etm@openssh.com ([OpenSSH PROTOCOL](
23 | https://github.com/openssh/openssh-portable/blob/e1b26ce504662a5d5b991091228984ccfd25f280/PROTOCOL#L54))
24 | * hmac-sha2-256-etm@openssh.com ([OpenSSH PROTOCOL](
25 | https://github.com/openssh/openssh-portable/blob/e1b26ce504662a5d5b991091228984ccfd25f280/PROTOCOL#L54))
26 | * hmac-sha1-etm@openssh.com ([OpenSSH PROTOCOL](
27 | https://github.com/openssh/openssh-portable/blob/e1b26ce504662a5d5b991091228984ccfd25f280/PROTOCOL#L54))
28 | * hmac-sha2-512 ([RFC 4868](https://tools.ietf.org/html/rfc4868))
29 | * hmac-sha2-256 ([RFC 4868](https://tools.ietf.org/html/rfc4868))
30 | * hmac-sha1 ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.4))
31 |
32 | ##### Key support:
33 | * Ed25519 ([RFC 8709](https://tools.ietf.org/html/rfc8709))
34 | * ECDSA ([RFC 5656](https://tools.ietf.org/html/rfc5656#section-3))
35 | * RSA ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-6.6))
36 |
37 | ##### Key exchange:
38 | * ecdh-sha2-nistp521 ([RFC 5656](https://tools.ietf.org/html/rfc5656#section-4))
39 | * ecdh-sha2-nistp384 ([RFC 5656](https://tools.ietf.org/html/rfc5656#section-4))
40 | * ecdh-sha2-nistp256 ([RFC 5656](https://tools.ietf.org/html/rfc5656#section-4))
41 | * curve25519-sha256 ([RFC 8731](https://tools.ietf.org/html/rfc8731))
42 | * diffie-hellman-group18-sha512 ([RFC 8268](https://tools.ietf.org/html/rfc8268))
43 | * diffie-hellman-group16-sha512 ([RFC 8268](https://tools.ietf.org/html/rfc8268))
44 | * diffie-hellman-group14-sha256 ([RFC 8268](https://tools.ietf.org/html/rfc8268))
45 | * diffie-hellman-group-exchange-sha256 ([RFC 4419](https://tools.ietf.org/html/rfc4419))
46 | * diffie-hellman-group-exchange-sha1 ([RFC 4419](https://tools.ietf.org/html/rfc4419))
47 | * diffie-hellman-group14-sha1 ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-8.1))
48 | * diffie-hellman-group1-sha1 ([RFC 4253](https://tools.ietf.org/html/rfc4253#section-8.1))
49 |
--------------------------------------------------------------------------------
/config/checkstyle/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/config/checkstyle/suppressions.xml:
--------------------------------------------------------------------------------
1 |
2 |
30 |
33 |
34 |
35 |
38 |
41 |
44 |
46 |
47 |
--------------------------------------------------------------------------------
/config/quality.gradle.kts:
--------------------------------------------------------------------------------
1 | apply(plugin = "checkstyle")
2 |
3 | configure {
4 | toolVersion = "8.14"
5 | }
6 |
7 | tasks.named("check") {
8 | dependsOn("checkstyle")
9 | }
10 |
11 | tasks.register("checkstyle") {
12 | source = fileTree("src/main/java")
13 | exclude("**/gen/**")
14 |
15 | classpath = files()
16 | }
17 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #Tue, 06 Nov 2018 18:14:48 +0900
2 | # Project-wide Gradle settings.
3 |
4 | # IDE (e.g. Android Studio) users:
5 | # Gradle settings configured through the IDE *will override*
6 | # any settings specified in this file.
7 |
8 | # For more details on how to configure your build environment visit
9 | # http://www.gradle.org/docs/current/userguide/build_environment.html
10 |
11 | # Specifies the JVM arguments used for the daemon process.
12 | # The setting is particularly useful for tweaking memory settings.
13 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
14 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
15 |
16 | # When configured, Gradle will run in incubating parallel mode.
17 | # This option should only be used with decoupled projects. More details, visit
18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
19 | # org.gradle.parallel=true
20 |
21 | version=2.2.26-SNAPSHOT
22 | group=org.connectbot
23 | description=SSH library used in the ConnectBot app
24 |
25 | bintrayUser=dummyUser
26 | bintrayApiKey=dummyApiKey
27 |
28 | officialJdk=openjdk9
29 | gitHubUrl=https\://github.com/connectbot/sshlib
30 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/connectbot/sshlib/9d7dd4ec0c7e94099c280932d7d4be2be5f39566/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "sshlib"
2 |
--------------------------------------------------------------------------------
/spotless.xml.prefs:
--------------------------------------------------------------------------------
1 | formatCommentText=false
2 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/AuthAgentCallback.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2;
2 |
3 | import java.security.KeyPair;
4 | import java.util.Map;
5 |
6 | /**
7 | * AuthAgentCallback.
8 | *
9 | * @author Kenny Root
10 | * @version $Id$
11 | */
12 | public interface AuthAgentCallback {
13 |
14 | /**
15 | * @return array of blobs containing the OpenSSH-format encoded public keys
16 | */
17 | Map retrieveIdentities();
18 |
19 | /**
20 | * @param pair A RSAPrivateKey, ECPrivateKey, or
21 | * DSAPrivateKey containing a DSA, EC, or RSA private
22 | * and corresponding PublicKey.
23 | * @param comment comment associated with this key
24 | * @param confirmUse whether to prompt before using this key
25 | * @param lifetime lifetime in seconds for key to be remembered
26 | * @return success or failure
27 | */
28 | boolean addIdentity(KeyPair pair, String comment, boolean confirmUse, int lifetime);
29 |
30 | /**
31 | * @param publicKey byte blob containing the OpenSSH-format encoded public key
32 | * @return success or failure
33 | */
34 | boolean removeIdentity(byte[] publicKey);
35 |
36 | /**
37 | * @return success or failure
38 | */
39 | boolean removeAllIdentities();
40 |
41 | /**
42 | * @param publicKey byte blob containing the OpenSSH-format encoded public key
43 | * @return A RSAPrivateKey or DSAPrivateKey
44 | * containing a DSA or RSA private key of
45 | * the user in Trilead object format.
46 | */
47 | KeyPair getKeyPair(byte[] publicKey);
48 |
49 | /**
50 | * @return
51 | */
52 | boolean isAgentLocked();
53 |
54 | /**
55 | * @param lockPassphrase
56 | */
57 | boolean setAgentLock(String lockPassphrase);
58 |
59 | /**
60 | * @param unlockPassphrase
61 | * @return
62 | */
63 | boolean requestAgentUnlock(String unlockPassphrase);
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/ChannelCondition.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | /**
5 | * Contains constants that can be used to specify what conditions to wait for on
6 | * a SSH-2 channel (e.g., represented by a {@link Session}).
7 | *
8 | * @see Session#waitForCondition(int, long)
9 | *
10 | * @author Christian Plattner, plattner@trilead.com
11 | * @version $Id: ChannelCondition.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
12 | */
13 |
14 | public interface ChannelCondition
15 | {
16 | /**
17 | * A timeout has occurred, none of your requested conditions is fulfilled.
18 | * However, other conditions may be true - therefore, NEVER use the "=="
19 | * operator to test for this (or any other) condition. Always use
20 | * something like ((cond & ChannelCondition.CLOSED) != 0).
21 | */
22 | int TIMEOUT = 1;
23 |
24 | /**
25 | * The underlying SSH-2 channel, however not necessarily the whole connection,
26 | * has been closed. This implies EOF. Note that there may still
27 | * be unread stdout or stderr data in the local window, i.e, STDOUT_DATA
28 | * or/and STDERR_DATA may be set at the same time.
29 | */
30 | int CLOSED = 2;
31 |
32 | /**
33 | * There is stdout data available that is ready to be consumed.
34 | */
35 | int STDOUT_DATA = 4;
36 |
37 | /**
38 | * There is stderr data available that is ready to be consumed.
39 | */
40 | int STDERR_DATA = 8;
41 |
42 | /**
43 | * EOF on has been reached, no more _new_ stdout or stderr data will arrive
44 | * from the remote server. However, there may be unread stdout or stderr
45 | * data, i.e, STDOUT_DATA or/and STDERR_DATA
46 | * may be set at the same time.
47 | */
48 | int EOF = 16;
49 |
50 | /**
51 | * The exit status of the remote process is available.
52 | * Some servers never send the exist status, or occasionally "forget" to do so.
53 | */
54 | int EXIT_STATUS = 32;
55 |
56 | /**
57 | * The exit signal of the remote process is available.
58 | */
59 | int EXIT_SIGNAL = 64;
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/ConnectionInfo.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | /**
5 | * In most cases you probably do not need the information contained in here.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: ConnectionInfo.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
9 | */
10 | public class ConnectionInfo
11 | {
12 | /**
13 | * The used key exchange (KEX) algorithm in the latest key exchange.
14 | */
15 | public String keyExchangeAlgorithm;
16 |
17 | /**
18 | * The currently used crypto algorithm for packets from to the client to the
19 | * server.
20 | */
21 | public String clientToServerCryptoAlgorithm;
22 | /**
23 | * The currently used crypto algorithm for packets from to the server to the
24 | * client.
25 | */
26 | public String serverToClientCryptoAlgorithm;
27 |
28 | /**
29 | * The currently used MAC algorithm for packets from to the client to the
30 | * server.
31 | */
32 | public String clientToServerMACAlgorithm;
33 | /**
34 | * The currently used MAC algorithm for packets from to the server to the
35 | * client.
36 | */
37 | public String serverToClientMACAlgorithm;
38 |
39 | /**
40 | * The type of the server host key (currently either "ssh-dss" or
41 | * "ssh-rsa").
42 | */
43 | public String serverHostKeyAlgorithm;
44 | /**
45 | * The server host key that was sent during the latest key exchange.
46 | */
47 | public byte[] serverHostKey;
48 |
49 | /**
50 | * Number of kex exchanges performed on this connection so far.
51 | */
52 | public int keyExchangeCounter = 0;
53 |
54 | /**
55 | * The currently used compression algorithm for packets from the client to
56 | * the server.
57 | */
58 | public String clientToServerCompressionAlgorithm;
59 |
60 | /**
61 | * The currently used compression algorithm for packets from the server to
62 | * the client.
63 | */
64 | public String serverToClientCompressionAlgorithm;
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/ConnectionMonitor.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | /**
5 | * A ConnectionMonitor is used to get notified when the
6 | * underlying socket of a connection is closed.
7 | *
8 | * @author Christian Plattner, plattner@trilead.com
9 | * @version $Id: ConnectionMonitor.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
10 | */
11 |
12 | public interface ConnectionMonitor
13 | {
14 | /**
15 | * This method is called after the connection's underlying
16 | * socket has been closed. E.g., due to the {@link Connection#close()} request of the
17 | * user, if the peer closed the connection, due to a fatal error during connect()
18 | * (also if the socket cannot be established) or if a fatal error occured on
19 | * an established connection.
20 | *
21 | * This is an experimental feature.
22 | *
23 | * You MUST NOT make any assumption about the thread that invokes this method.
24 | *
25 | * Please note: if the connection is not connected (e.g., there was no successful
26 | * connect() call), then the invocation of {@link Connection#close()} will NOT trigger
27 | * this method.
28 | *
29 | * @see Connection#addConnectionMonitor(ConnectionMonitor)
30 | *
31 | * @param reason Includes an indication why the socket was closed.
32 | */
33 | void connectionLost(Throwable reason);
34 | }
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/DebugLogger.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2;
2 |
3 | /**
4 | * An interface which needs to be implemented if you
5 | * want to capture debugging messages.
6 | *
7 | * @see Connection#enableDebugging(boolean, DebugLogger)
8 | *
9 | * @author Christian Plattner, plattner@trilead.com
10 | * @version $Id: DebugLogger.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
11 | */
12 | public interface DebugLogger
13 | {
14 |
15 | /**
16 | * Log a debug message.
17 | *
18 | * @param level 0-99, 99 is a the most verbose level
19 | * @param className the class that generated the message
20 | * @param message the debug message
21 | */
22 | void log(int level, String className, String message);
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/DynamicPortForwarder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007 Kenny Root, Jeffrey Sharkey
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions
6 | * are met:
7 | *
8 | * a.) Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * b.) Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | * c.) Neither the name of Trilead nor the names of its contributors may
14 | * be used to endorse or promote products derived from this software
15 | * without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 | * POSSIBILITY OF SUCH DAMAGE.
28 | */
29 |
30 | package com.trilead.ssh2;
31 |
32 | import java.io.IOException;
33 | import java.net.InetSocketAddress;
34 |
35 | import com.trilead.ssh2.channel.ChannelManager;
36 | import com.trilead.ssh2.channel.DynamicAcceptThread;
37 |
38 | /**
39 | * A DynamicPortForwarder forwards TCP/IP connections to a local
40 | * port via the secure tunnel to another host which is selected via the
41 | * SOCKS protocol. Checkout {@link Connection#createDynamicPortForwarder(int)}
42 | * on how to create one.
43 | *
44 | * @author Kenny Root
45 | * @version $Id: $
46 | */
47 | public class DynamicPortForwarder {
48 | ChannelManager cm;
49 |
50 | DynamicAcceptThread dat;
51 |
52 | DynamicPortForwarder(ChannelManager cm, int local_port)
53 | throws IOException
54 | {
55 | this.cm = cm;
56 |
57 | dat = new DynamicAcceptThread(cm, local_port);
58 | dat.setDaemon(true);
59 | dat.start();
60 | }
61 |
62 | DynamicPortForwarder(ChannelManager cm, InetSocketAddress addr) throws IOException {
63 | this.cm = cm;
64 |
65 | dat = new DynamicAcceptThread(cm, addr);
66 | dat.setDaemon(true);
67 | dat.start();
68 | }
69 |
70 | /**
71 | * Stop TCP/IP forwarding of newly arriving connections.
72 | *
73 | */
74 | public void close() {
75 | dat.stopWorking();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/ExtendedServerHostKeyVerifier.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * This extends the {@link ServerHostKeyVerifier} interface by allowing the remote server to indicate it has multiple
7 | * server key algorithms available. After authentication, the {@link #getKnownKeyAlgorithmsForHost(String, int)} method
8 | * may be called and compared against the list of server-controller keys. If a key algorithm has been added then
9 | * {@link #addServerHostKey(String, int, String, byte[])} will be called. If a key algorithm has been removed, then
10 | * {@link #removeServerHostKey(String, int, String, byte[])} will be called.
11 | *
12 | * @author Kenny Root
13 | */
14 | public abstract class ExtendedServerHostKeyVerifier implements ServerHostKeyVerifier {
15 | /**
16 | * Called during connection to determine which keys are known for this host.
17 | *
18 | * @param hostname the hostname used to create the {@link Connection} object
19 | * @param port the server's remote TCP port
20 | * @return list of hostkey algorithms for the given hostname and port combination
21 | * or {@code null} if none are known.
22 | */
23 | public abstract List getKnownKeyAlgorithmsForHost(String hostname, int port);
24 |
25 | /**
26 | * After authentication, if the server indicates it no longer uses this key, this method will be called
27 | * for the app to remove its record of it.
28 | *
29 | * @param hostname the hostname used to create the {@link Connection} object
30 | * @param port the server's remote TCP port
31 | * @param serverHostKeyAlgorithm key algorithm of removed key
32 | * @param serverHostKey key data of removed key
33 | */
34 | public abstract void removeServerHostKey(String hostname, int port, String serverHostKeyAlgorithm,
35 | byte[] serverHostKey);
36 |
37 | /**
38 | * After authentication, if the server indicates it has another keyAlgorithm, this method will be
39 | * called for the app to add it to its record of known keys for this hostname.
40 | *
41 | * @param hostname the hostname used to create the {@link Connection} object
42 | * @param port the server's remote TCP port
43 | * @param keyAlgorithm SSH standard name for the key to be added
44 | * @param serverHostKey SSH encoding of the key data for the key to be added
45 | */
46 | public abstract void addServerHostKey(String hostname, int port, String keyAlgorithm, byte[] serverHostKey);
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/ExtensionInfo.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2;
2 |
3 | import com.trilead.ssh2.packets.PacketExtInfo;
4 | import java.util.Collections;
5 | import java.util.HashSet;
6 | import java.util.Set;
7 |
8 | /**
9 | * SSH extensions reported by the server
10 | *
11 | * https://tools.ietf.org/html/draft-ietf-curdle-ssh-ext-info-15
12 | */
13 | public class ExtensionInfo
14 | {
15 | private final Set signatureAlgorithmsAccepted;
16 |
17 | /**
18 | * @return Signature algorithms that server will accept. If empty, this extension was absent.
19 | */
20 | public Set getSignatureAlgorithmsAccepted()
21 | {
22 | return signatureAlgorithmsAccepted;
23 | }
24 |
25 | public static ExtensionInfo fromPacketExtInfo(PacketExtInfo packetExtInfo)
26 | {
27 | String rawAlgs = packetExtInfo.getExtNameToValue().get("server-sig-algs");
28 | if (rawAlgs == null)
29 | {
30 | return new ExtensionInfo(Collections.emptySet());
31 | }
32 |
33 | Set algsSet = new HashSet<>();
34 | Collections.addAll(algsSet, rawAlgs.split(","));
35 | return new ExtensionInfo(algsSet);
36 | }
37 |
38 | public static ExtensionInfo noExtInfoSeen()
39 | {
40 | return new ExtensionInfo(Collections.emptySet());
41 | }
42 |
43 | private ExtensionInfo(Set signatureAlgorithmsAccepted)
44 | {
45 | this.signatureAlgorithmsAccepted = Collections.unmodifiableSet(signatureAlgorithmsAccepted);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/HTTPProxyException.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | import java.io.IOException;
5 |
6 | /**
7 | * May be thrown upon connect() if a HTTP proxy is being used.
8 | *
9 | * @see Connection#connect()
10 | * @see Connection#setProxyData(ProxyData)
11 | *
12 | * @author Christian Plattner, plattner@trilead.com
13 | * @version $Id: HTTPProxyException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
14 | */
15 |
16 | public class HTTPProxyException extends IOException
17 | {
18 | private static final long serialVersionUID = 2241537397104426186L;
19 |
20 | public final String httpResponse;
21 | public final int httpErrorCode;
22 |
23 | public HTTPProxyException(String httpResponse, int httpErrorCode)
24 | {
25 | super("HTTP Proxy Error (" + httpErrorCode + " " + httpResponse + ")");
26 | this.httpResponse = httpResponse;
27 | this.httpErrorCode = httpErrorCode;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/InteractiveCallback.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | /**
5 | * An InteractiveCallback is used to respond to challenges sent
6 | * by the server if authentication mode "keyboard-interactive" is selected.
7 | *
8 | * @see Connection#authenticateWithKeyboardInteractive(String,
9 | * String[], InteractiveCallback)
10 | *
11 | * @author Christian Plattner, plattner@trilead.com
12 | * @version $Id: InteractiveCallback.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
13 | */
14 |
15 | public interface InteractiveCallback
16 | {
17 | /**
18 | * This callback interface is used during a "keyboard-interactive"
19 | * authentication. Every time the server sends a set of challenges (however,
20 | * most often just one challenge at a time), this callback function will be
21 | * called to give your application a chance to talk to the user and to
22 | * determine the response(s).
23 | *
24 | * Some copy-paste information from the standard: a command line interface
25 | * (CLI) client SHOULD print the name and instruction (if non-empty), adding
26 | * newlines. Then for each prompt in turn, the client SHOULD display the
27 | * prompt and read the user input. The name and instruction fields MAY be
28 | * empty strings, the client MUST be prepared to handle this correctly. The
29 | * prompt field(s) MUST NOT be empty strings.
30 | *
31 | * Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details.
32 | *
33 | * Note: clients SHOULD use control character filtering as discussed in
34 | * RFC4251 to avoid attacks by including
35 | * terminal control characters in the fields to be displayed.
36 | *
37 | * @param name
38 | * the name String sent by the server.
39 | * @param instruction
40 | * the instruction String sent by the server.
41 | * @param numPrompts
42 | * number of prompts - may be zero (in this case, you should just
43 | * return a String array of length zero).
44 | * @param prompt
45 | * an array (length numPrompts) of Strings
46 | * @param echo
47 | * an array (length numPrompts) of booleans. For
48 | * each prompt, the corresponding echo field indicates whether or
49 | * not the user input should be echoed as characters are typed.
50 | * @return an array of reponses - the array size must match the parameter
51 | * numPrompts.
52 | */
53 | String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo)
54 | throws Exception;
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/LocalPortForwarder.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | import java.io.IOException;
5 | import java.net.InetSocketAddress;
6 |
7 | import com.trilead.ssh2.channel.ChannelManager;
8 | import com.trilead.ssh2.channel.LocalAcceptThread;
9 |
10 |
11 | /**
12 | * A LocalPortForwarder forwards TCP/IP connections to a local
13 | * port via the secure tunnel to another host (which may or may not be identical
14 | * to the remote SSH-2 server). Checkout {@link Connection#createLocalPortForwarder(int, String, int)}
15 | * on how to create one.
16 | *
17 | * @author Christian Plattner, plattner@trilead.com
18 | * @version $Id: LocalPortForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
19 | */
20 | public class LocalPortForwarder
21 | {
22 | ChannelManager cm;
23 |
24 | String host_to_connect;
25 |
26 | int port_to_connect;
27 |
28 | LocalAcceptThread lat;
29 |
30 | LocalPortForwarder(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
31 | throws IOException
32 | {
33 | this.cm = cm;
34 | this.host_to_connect = host_to_connect;
35 | this.port_to_connect = port_to_connect;
36 |
37 | lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect);
38 | lat.setDaemon(true);
39 | lat.start();
40 | }
41 |
42 | LocalPortForwarder(ChannelManager cm, InetSocketAddress addr, String host_to_connect, int port_to_connect)
43 | throws IOException
44 | {
45 | this.cm = cm;
46 | this.host_to_connect = host_to_connect;
47 | this.port_to_connect = port_to_connect;
48 |
49 | lat = new LocalAcceptThread(cm, addr, host_to_connect, port_to_connect);
50 | lat.setDaemon(true);
51 | lat.start();
52 | }
53 |
54 | /**
55 | * Stop TCP/IP forwarding of newly arriving connections.
56 | *
57 | */
58 | public void close() {
59 | lat.stopWorking();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/LocalStreamForwarder.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.OutputStream;
7 |
8 | import com.trilead.ssh2.channel.Channel;
9 | import com.trilead.ssh2.channel.ChannelManager;
10 | import com.trilead.ssh2.channel.LocalAcceptThread;
11 |
12 |
13 | /**
14 | * A LocalStreamForwarder forwards an Input- and Outputstream
15 | * pair via the secure tunnel to another host (which may or may not be identical
16 | * to the remote SSH-2 server).
17 | *
18 | * @author Christian Plattner, plattner@trilead.com
19 | * @version $Id: LocalStreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
20 | */
21 | public class LocalStreamForwarder
22 | {
23 | ChannelManager cm;
24 |
25 | String host_to_connect;
26 | int port_to_connect;
27 | LocalAcceptThread lat;
28 |
29 | Channel cn;
30 |
31 | LocalStreamForwarder(ChannelManager cm, String host_to_connect, int port_to_connect) throws IOException
32 | {
33 | this.cm = cm;
34 | this.host_to_connect = host_to_connect;
35 | this.port_to_connect = port_to_connect;
36 |
37 | cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, "127.0.0.1", 0);
38 | }
39 |
40 | /**
41 | * @return An InputStream object.
42 | */
43 | public InputStream getInputStream() {
44 | return cn.getStdoutStream();
45 | }
46 |
47 | /**
48 | * Get the OutputStream. Please be aware that the implementation MAY use an
49 | * internal buffer. To make sure that the buffered data is sent over the
50 | * tunnel, you have to call the flush method of the
51 | * OutputStream. To signal EOF, please use the
52 | * close method of the OutputStream.
53 | *
54 | * @return An OutputStream object.
55 | */
56 | public OutputStream getOutputStream() {
57 | return cn.getStdinStream();
58 | }
59 |
60 | /**
61 | * Close the underlying SSH forwarding channel and free up resources.
62 | * You can also use this method to force the shutdown of the underlying
63 | * forwarding channel. Pending output (OutputStream not flushed) will NOT
64 | * be sent. Pending input (InputStream) can still be read. If the shutdown
65 | * operation is already in progress (initiated from either side), then this
66 | * call is a no-op.
67 | *
68 | * @throws IOException
69 | */
70 | public void close() throws IOException
71 | {
72 | cm.closeChannel(cn, "Closed due to user request.", true);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/ProxyData.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | import java.io.IOException;
5 | import java.net.Socket;
6 |
7 | /**
8 | * An abstract interface implemented by all proxy data implementations.
9 | *
10 | * @see HTTPProxyData
11 | *
12 | * @author Christian Plattner, plattner@trilead.com
13 | * @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
14 | */
15 |
16 | public interface ProxyData
17 | {
18 | /**
19 | * Connects the socket to the given destination using the proxy method that this instance
20 | * represents.
21 | * @param hostname hostname of end host (not proxy)
22 | * @param port port of end host (not proxy)
23 | * @param connectTimeout number of seconds before giving up on connecting to end host
24 | * @throws IOException if the connection could not be completed
25 | * @return connected socket instance
26 | */
27 | Socket openConnection(String hostname, int port, int connectTimeout) throws IOException;
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/SFTPException.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | import java.io.IOException;
5 |
6 | import com.trilead.ssh2.sftp.ErrorCodes;
7 |
8 |
9 | /**
10 | * Used in combination with the SFTPv3Client. This exception wraps
11 | * error messages sent by the SFTP server.
12 | *
13 | * @author Christian Plattner, plattner@trilead.com
14 | * @version $Id: SFTPException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
15 | */
16 |
17 | public class SFTPException extends IOException
18 | {
19 | private static final long serialVersionUID = 578654644222421811L;
20 |
21 | private final String sftpErrorMessage;
22 | private final int sftpErrorCode;
23 |
24 | private static String constructMessage(String s, int errorCode)
25 | {
26 | String[] detail = ErrorCodes.getDescription(errorCode);
27 |
28 | if (detail == null)
29 | return s + " (UNKNOW SFTP ERROR CODE)";
30 |
31 | return s + " (" + detail[0] + ": " + detail[1] + ")";
32 | }
33 |
34 | SFTPException(String msg, int errorCode)
35 | {
36 | super(constructMessage(msg, errorCode));
37 | sftpErrorMessage = msg;
38 | sftpErrorCode = errorCode;
39 | }
40 |
41 | /**
42 | * Get the error message sent by the server. Often, this
43 | * message does not help a lot (e.g., "failure").
44 | *
45 | * @return the plain string as sent by the server.
46 | */
47 | public String getServerErrorMessage()
48 | {
49 | return sftpErrorMessage;
50 | }
51 |
52 | /**
53 | * Get the error code sent by the server.
54 | *
55 | * @return an error code as defined in the SFTP specs.
56 | */
57 | public int getServerErrorCode()
58 | {
59 | return sftpErrorCode;
60 | }
61 |
62 | /**
63 | * Get the symbolic name of the error code as given in the SFTP specs.
64 | *
65 | * @return e.g., "SSH_FX_INVALID_FILENAME".
66 | */
67 | public String getServerErrorCodeSymbol()
68 | {
69 | String[] detail = ErrorCodes.getDescription(sftpErrorCode);
70 |
71 | if (detail == null)
72 | return "UNKNOW SFTP ERROR CODE " + sftpErrorCode;
73 |
74 | return detail[0];
75 | }
76 |
77 | /**
78 | * Get the description of the error code as given in the SFTP specs.
79 | *
80 | * @return e.g., "The filename is not valid."
81 | */
82 | public String getServerErrorCodeVerbose()
83 | {
84 | String[] detail = ErrorCodes.getDescription(sftpErrorCode);
85 |
86 | if (detail == null)
87 | return "The error code " + sftpErrorCode + " is unknown.";
88 |
89 | return detail[1];
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/SFTPv3DirectoryEntry.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | /**
5 | * A SFTPv3DirectoryEntry as returned by {@link SFTPv3Client#ls(String)}.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: SFTPv3DirectoryEntry.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
9 | */
10 |
11 | public class SFTPv3DirectoryEntry
12 | {
13 | /**
14 | * A relative name within the directory, without any path components.
15 | */
16 | public String filename;
17 |
18 | /**
19 | * An expanded format for the file name, similar to what is returned by
20 | * "ls -l" on Un*x systems.
21 | *
22 | * The format of this field is unspecified by the SFTP v3 protocol.
23 | * It MUST be suitable for use in the output of a directory listing
24 | * command (in fact, the recommended operation for a directory listing
25 | * command is to simply display this data). However, clients SHOULD NOT
26 | * attempt to parse the longname field for file attributes; they SHOULD
27 | * use the attrs field instead.
28 | *
29 | * The recommended format for the longname field is as follows:
30 | * -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
31 | */
32 | public String longEntry;
33 |
34 | /**
35 | * The attributes of this entry.
36 | */
37 | public SFTPv3FileAttributes attributes;
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/SFTPv3FileHandle.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | /**
5 | * A SFTPv3FileHandle.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: SFTPv3FileHandle.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
9 | */
10 |
11 | public class SFTPv3FileHandle
12 | {
13 | final SFTPv3Client client;
14 | final byte[] fileHandle;
15 | boolean isClosed = false;
16 |
17 | /* The constructor is NOT public */
18 |
19 | SFTPv3FileHandle(SFTPv3Client client, byte[] h)
20 | {
21 | this.client = client;
22 | this.fileHandle = h;
23 | }
24 |
25 | /**
26 | * Get the SFTPv3Client instance which created this handle.
27 | *
28 | * @return A SFTPv3Client instance.
29 | */
30 | public SFTPv3Client getClient()
31 | {
32 | return client;
33 | }
34 |
35 | /**
36 | * Check if this handle was closed with the {@link SFTPv3Client#closeFile(SFTPv3FileHandle)} method
37 | * of the SFTPv3Client instance which created the handle.
38 | *
39 | * @return if the handle is closed.
40 | */
41 | public boolean isClosed()
42 | {
43 | return isClosed;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/ServerHostKeyVerifier.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2;
3 |
4 | /**
5 | * A callback interface used to implement a client specific method of checking
6 | * server host keys.
7 | *
8 | * @author Christian Plattner, plattner@trilead.com
9 | * @version $Id: ServerHostKeyVerifier.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
10 | */
11 |
12 | public interface ServerHostKeyVerifier
13 | {
14 | /**
15 | * The actual verifier method, it will be called by the key exchange code
16 | * on EVERY key exchange - this can happen several times during the lifetime
17 | * of a connection.
18 | *
19 | * Note: SSH-2 servers are allowed to change their hostkey at ANY time.
20 | *
21 | * @param hostname the hostname used to create the {@link Connection} object
22 | * @param port the remote TCP port
23 | * @param serverHostKeyAlgorithm the public key algorithm (ssh-rsa or ssh-dss)
24 | * @param serverHostKey the server's public key blob
25 | * @return if the client wants to accept the server's host key - if not, the
26 | * connection will be closed.
27 | * @throws Exception Will be wrapped with an IOException, extended version of returning false =)
28 | */
29 | boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey)
30 | throws Exception;
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/auth/SignatureProxy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Jonas Dippel, Michael Perk, Marc Totzke
3 | */
4 |
5 | package com.trilead.ssh2.auth;
6 |
7 | import java.io.IOException;
8 | import java.security.PublicKey;
9 |
10 | public abstract class SignatureProxy
11 | {
12 | public static final String SHA1 = "SHA-1";
13 | public static final String SHA256 = "SHA-256";
14 | public static final String SHA384 = "SHA-384";
15 | public static final String SHA512 = "SHA-512";
16 |
17 | /**
18 | * Holds the public key which belongs to the private key which is used in the signing process.
19 | */
20 | private PublicKey mPublicKey;
21 |
22 | /**
23 | * Instantiates a new SignatureProxy which needs a public key for the
24 | * later authentication process.
25 | *
26 | * @param publicKey The public key.
27 | * @throws IllegalArgumentException Might be thrown id the public key is invalid.
28 | */
29 | public SignatureProxy(PublicKey publicKey)
30 | {
31 | if (publicKey == null)
32 | {
33 | throw new IllegalArgumentException("Public key must not be null");
34 | }
35 | mPublicKey = publicKey;
36 | }
37 |
38 | /**
39 | * This method should sign a given byte array message using the private key.
40 | *
41 | * @param message The message which should be signed.
42 | * @param hashAlgorithm The hashing algorithm which should be used.
43 | * @return The signed message.
44 | * @throws IOException This exception might be thrown during the signing process.
45 | */
46 | public abstract byte[] sign(byte[] message, String hashAlgorithm) throws IOException;
47 |
48 | public PublicKey getPublicKey()
49 | {
50 | return mPublicKey;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/ChannelInputStream.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.channel;
3 |
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 |
7 | /**
8 | * ChannelInputStream.
9 | *
10 | * @author Christian Plattner, plattner@trilead.com
11 | * @version $Id: ChannelInputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
12 | */
13 | public final class ChannelInputStream extends InputStream
14 | {
15 | Channel c;
16 |
17 | boolean isClosed = false;
18 | boolean isEOF = false;
19 | boolean extendedFlag = false;
20 |
21 | ChannelInputStream(Channel c, boolean isExtended)
22 | {
23 | this.c = c;
24 | this.extendedFlag = isExtended;
25 | }
26 |
27 | public int available() throws IOException
28 | {
29 | if (isEOF)
30 | return 0;
31 |
32 | int avail = c.cm.getAvailable(c, extendedFlag);
33 |
34 | /* We must not return -1 on EOF */
35 |
36 | return (avail > 0) ? avail : 0;
37 | }
38 |
39 | public void close() {
40 | isClosed = true;
41 | }
42 |
43 | public int read(byte[] b, int off, int len) throws IOException
44 | {
45 | if (b == null)
46 | throw new NullPointerException();
47 |
48 | if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
49 | throw new IndexOutOfBoundsException();
50 |
51 | if (len == 0)
52 | return 0;
53 |
54 | if (isEOF)
55 | return -1;
56 |
57 | int ret = c.cm.getChannelData(c, extendedFlag, b, off, len);
58 |
59 | if (ret == -1)
60 | {
61 | isEOF = true;
62 | }
63 |
64 | return ret;
65 | }
66 |
67 | public int read(byte[] b) throws IOException
68 | {
69 | return read(b, 0, b.length);
70 | }
71 |
72 | public int read() throws IOException
73 | {
74 | /* Yes, this stream is pure and unbuffered, a single byte read() is slow */
75 |
76 | final byte b[] = new byte[1];
77 |
78 | int ret = read(b, 0, 1);
79 |
80 | if (ret != 1)
81 | return -1;
82 |
83 | return b[0] & 0xff;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/ChannelOutputStream.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.channel;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 |
6 | /**
7 | * ChannelOutputStream.
8 | *
9 | * @author Christian Plattner, plattner@trilead.com
10 | * @version $Id: ChannelOutputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
11 | */
12 | public final class ChannelOutputStream extends OutputStream
13 | {
14 | Channel c;
15 |
16 | private byte[] writeBuffer;
17 |
18 | boolean isClosed = false;
19 |
20 | ChannelOutputStream(Channel c)
21 | {
22 | this.c = c;
23 | writeBuffer = new byte[1];
24 | }
25 |
26 | public void write(int b) throws IOException
27 | {
28 | writeBuffer[0] = (byte) b;
29 |
30 | write(writeBuffer, 0, 1);
31 | }
32 |
33 | public void close() throws IOException
34 | {
35 | if (!isClosed)
36 | {
37 | isClosed = true;
38 | c.cm.sendEOF(c);
39 | }
40 | }
41 |
42 | public void flush() throws IOException
43 | {
44 | if (isClosed)
45 | throw new IOException("This OutputStream is closed.");
46 |
47 | /* This is a no-op, since this stream is unbuffered */
48 | }
49 |
50 | public void write(byte[] b, int off, int len) throws IOException
51 | {
52 | if (isClosed)
53 | throw new IOException("This OutputStream is closed.");
54 |
55 | if (b == null)
56 | throw new NullPointerException();
57 |
58 | if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
59 | throw new IndexOutOfBoundsException();
60 |
61 | if (len == 0)
62 | return;
63 |
64 | c.cm.sendData(c, b, off, len);
65 | }
66 |
67 | public void write(byte[] b) throws IOException
68 | {
69 | write(b, 0, b.length);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/IChannelWorkerThread.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.channel;
3 |
4 | /**
5 | * IChannelWorkerThread.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
9 | */
10 | interface IChannelWorkerThread
11 | {
12 | void stopWorking();
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.channel;
3 |
4 | import java.io.IOException;
5 | import java.net.InetSocketAddress;
6 | import java.net.ServerSocket;
7 | import java.net.Socket;
8 |
9 | /**
10 | * LocalAcceptThread.
11 | *
12 | * @author Christian Plattner, plattner@trilead.com
13 | * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
14 | */
15 | public class LocalAcceptThread extends Thread implements IChannelWorkerThread
16 | {
17 | ChannelManager cm;
18 | String host_to_connect;
19 | int port_to_connect;
20 |
21 | final ServerSocket ss;
22 |
23 | public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
24 | throws IOException
25 | {
26 | this.cm = cm;
27 | this.host_to_connect = host_to_connect;
28 | this.port_to_connect = port_to_connect;
29 |
30 | ss = new ServerSocket(local_port);
31 | }
32 |
33 | public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
34 | int port_to_connect) throws IOException
35 | {
36 | this.cm = cm;
37 | this.host_to_connect = host_to_connect;
38 | this.port_to_connect = port_to_connect;
39 |
40 | ss = new ServerSocket();
41 | ss.bind(localAddress);
42 | }
43 |
44 | public void run()
45 | {
46 | try
47 | {
48 | cm.registerThread(this);
49 | }
50 | catch (IOException e)
51 | {
52 | stopWorking();
53 | return;
54 | }
55 |
56 | while (true)
57 | {
58 | Socket s = null;
59 |
60 | try
61 | {
62 | s = ss.accept();
63 | }
64 | catch (IOException e)
65 | {
66 | stopWorking();
67 | return;
68 | }
69 |
70 | Channel cn = null;
71 | StreamForwarder r2l = null;
72 | StreamForwarder l2r = null;
73 |
74 | try
75 | {
76 | /* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
77 |
78 | cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
79 | .getPort());
80 |
81 | }
82 | catch (IOException e)
83 | {
84 | /* Simply close the local socket and wait for the next incoming connection */
85 |
86 | try
87 | {
88 | s.close();
89 | }
90 | catch (IOException ignore)
91 | {
92 | }
93 |
94 | continue;
95 | }
96 |
97 | try
98 | {
99 | r2l = new StreamForwarder(cn, null, s, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
100 | l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
101 | }
102 | catch (IOException e)
103 | {
104 | try
105 | {
106 | /* This message is only visible during debugging, since we discard the channel immediatelly */
107 | cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
108 | true);
109 | }
110 | catch (IOException ignore)
111 | {
112 | }
113 |
114 | continue;
115 | }
116 |
117 | r2l.setDaemon(true);
118 | l2r.setDaemon(true);
119 | r2l.start();
120 | l2r.start();
121 | }
122 | }
123 |
124 | public void stopWorking()
125 | {
126 | try
127 | {
128 | /* This will lead to an IOException in the ss.accept() call */
129 | ss.close();
130 | }
131 | catch (IOException e)
132 | {
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.channel;
3 |
4 | import java.io.IOException;
5 | import java.net.Socket;
6 |
7 | import com.trilead.ssh2.log.Logger;
8 |
9 |
10 | /**
11 | * RemoteAcceptThread.
12 | *
13 | * @author Christian Plattner, plattner@trilead.com
14 | * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
15 | */
16 | public class RemoteAcceptThread extends Thread
17 | {
18 | private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
19 |
20 | Channel c;
21 |
22 | String remoteConnectedAddress;
23 | int remoteConnectedPort;
24 | String remoteOriginatorAddress;
25 | int remoteOriginatorPort;
26 | String targetAddress;
27 | int targetPort;
28 |
29 | Socket s;
30 |
31 | public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
32 | String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
33 | {
34 | this.c = c;
35 | this.remoteConnectedAddress = remoteConnectedAddress;
36 | this.remoteConnectedPort = remoteConnectedPort;
37 | this.remoteOriginatorAddress = remoteOriginatorAddress;
38 | this.remoteOriginatorPort = remoteOriginatorPort;
39 | this.targetAddress = targetAddress;
40 | this.targetPort = targetPort;
41 |
42 | if (log.isEnabled())
43 | log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
44 | + remoteOriginatorAddress + "/" + remoteOriginatorPort);
45 | }
46 |
47 | public void run()
48 | {
49 | try
50 | {
51 | c.cm.sendOpenConfirmation(c);
52 |
53 | s = new Socket(targetAddress, targetPort);
54 |
55 | StreamForwarder r2l = new StreamForwarder(c, null, s, c.getStdoutStream(), s.getOutputStream(),
56 | "RemoteToLocal");
57 | StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
58 | "LocalToRemote");
59 |
60 | /* No need to start two threads, one can be executed in the current thread */
61 |
62 | r2l.setDaemon(true);
63 | r2l.start();
64 | l2r.run();
65 |
66 | while (r2l.isAlive())
67 | {
68 | try
69 | {
70 | r2l.join();
71 | }
72 | catch (InterruptedException e)
73 | {
74 | }
75 | }
76 |
77 | /* If the channel is already closed, then this is a no-op */
78 |
79 | c.cm.closeChannel(c, "EOF on both streams reached.", true);
80 | s.close();
81 | }
82 | catch (IOException e)
83 | {
84 | log.log(50, "IOException in proxy code: " + e.getMessage());
85 |
86 | try
87 | {
88 | c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
89 | }
90 | catch (IOException e1)
91 | {
92 | }
93 | try
94 | {
95 | if (s != null)
96 | s.close();
97 | }
98 | catch (IOException e1)
99 | {
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/RemoteForwardingData.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.channel;
3 |
4 | /**
5 | * RemoteForwardingData. Data about a requested remote forwarding.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: RemoteForwardingData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
9 | */
10 | public class RemoteForwardingData
11 | {
12 | public String bindAddress;
13 | public int bindPort;
14 |
15 | String targetAddress;
16 | int targetPort;
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/StreamForwarder.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.channel;
3 |
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.OutputStream;
7 | import java.net.Socket;
8 |
9 | /**
10 | * A StreamForwarder forwards data between two given streams.
11 | * If two StreamForwarder threads are used (one for each direction)
12 | * then one can be configured to shutdown the underlying channel/socket
13 | * if both threads have finished forwarding (EOF).
14 | *
15 | * @author Christian Plattner, plattner@trilead.com
16 | * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
17 | */
18 | public class StreamForwarder extends Thread
19 | {
20 | final OutputStream os;
21 | final InputStream is;
22 | final byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
23 | final Channel c;
24 | final StreamForwarder sibling;
25 | final Socket s;
26 | final String mode;
27 |
28 | StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode) {
29 | this.is = is;
30 | this.os = os;
31 | this.mode = mode;
32 | this.c = c;
33 | this.sibling = sibling;
34 | this.s = s;
35 | }
36 |
37 | public void run()
38 | {
39 | try
40 | {
41 | while (true)
42 | {
43 | int len = is.read(buffer);
44 | if (len <= 0)
45 | break;
46 | os.write(buffer, 0, len);
47 | os.flush();
48 | }
49 | }
50 | catch (IOException ignore)
51 | {
52 | try
53 | {
54 | c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
55 | + ignore.getMessage(), true);
56 | }
57 | catch (IOException e)
58 | {
59 | }
60 | }
61 | finally
62 | {
63 | try
64 | {
65 | os.close();
66 | }
67 | catch (IOException e1)
68 | {
69 | }
70 | try
71 | {
72 | is.close();
73 | }
74 | catch (IOException e2)
75 | {
76 | }
77 |
78 | if (sibling != null)
79 | {
80 | while (sibling.isAlive())
81 | {
82 | try
83 | {
84 | sibling.join();
85 | }
86 | catch (InterruptedException e)
87 | {
88 | }
89 | }
90 |
91 | try
92 | {
93 | c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
94 | }
95 | catch (IOException e3)
96 | {
97 | }
98 | }
99 |
100 | if (s != null) {
101 | try
102 | {
103 | s.close();
104 | }
105 | catch (IOException e1)
106 | {
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/channel/X11ServerData.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.channel;
3 |
4 | /**
5 | * X11ServerData. Data regarding an x11 forwarding target.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: X11ServerData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
9 | *
10 | */
11 | public class X11ServerData
12 | {
13 | public String hostname;
14 | public int port;
15 | public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/compression/ICompressor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007 Kenny Root, Jeffrey Sharkey
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions
6 | * are met:
7 | *
8 | * a.) Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * b.) Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | * c.) Neither the name of Trilead nor the names of its contributors may
14 | * be used to endorse or promote products derived from this software
15 | * without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 | * POSSIBILITY OF SUCH DAMAGE.
28 | */
29 |
30 | package com.trilead.ssh2.compression;
31 |
32 | /**
33 | * @author Kenny Root
34 | *
35 | */
36 | public interface ICompressor {
37 | int getBufferSize();
38 |
39 | int compress(byte[] buf, int start, int len, byte[] output);
40 |
41 | byte[] uncompress(byte[] buf, int start, int[] len);
42 |
43 | boolean canCompressPreauth();
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/compression/ZlibOpenSSH.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007 Kenny Root, Jeffrey Sharkey
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions
6 | * are met:
7 | *
8 | * a.) Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * b.) Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | * c.) Neither the name of Trilead nor the names of its contributors may
14 | * be used to endorse or promote products derived from this software
15 | * without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 | * POSSIBILITY OF SUCH DAMAGE.
28 | */
29 |
30 | package com.trilead.ssh2.compression;
31 |
32 | /**
33 | * Defines how zlib@openssh.org compression works.
34 | * See
35 | * http://www.openssh.org/txt/draft-miller-secsh-compression-delayed-00.txt
36 | * compression is disabled until userauth has occurred.
37 | *
38 | * @author Matt Johnston
39 | *
40 | */
41 | public class ZlibOpenSSH extends Zlib {
42 |
43 | public boolean canCompressPreauth() {
44 | return false;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto;
3 |
4 | import com.trilead.ssh2.compression.CompressionFactory;
5 | import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
6 | import com.trilead.ssh2.crypto.digest.MACs;
7 | import com.trilead.ssh2.transport.KexManager;
8 |
9 |
10 | /**
11 | * CryptoWishList.
12 | *
13 | * @author Christian Plattner, plattner@trilead.com
14 | * @version $Id: CryptoWishList.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
15 | */
16 | public class CryptoWishList implements Cloneable
17 | {
18 | public CryptoWishList() {
19 | kexAlgorithms = KexManager.getDefaultKexAlgorithmList();
20 | serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList();
21 | c2s_enc_algos = BlockCipherFactory.getDefaultCipherList();
22 | s2c_enc_algos = BlockCipherFactory.getDefaultCipherList();
23 | c2s_mac_algos = MACs.getMacList();
24 | s2c_mac_algos = MACs.getMacList();
25 | c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
26 | s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
27 | }
28 |
29 | public CryptoWishList(CryptoWishList other) {
30 | kexAlgorithms = other.kexAlgorithms.clone();
31 | serverHostKeyAlgorithms = other.serverHostKeyAlgorithms.clone();
32 | c2s_enc_algos = other.c2s_enc_algos.clone();
33 | s2c_enc_algos = other.s2c_enc_algos.clone();
34 | c2s_mac_algos = other.c2s_mac_algos.clone();
35 | s2c_mac_algos = other.s2c_mac_algos.clone();
36 | c2s_comp_algos = other.c2s_comp_algos.clone();
37 | s2c_comp_algos = other.s2c_comp_algos.clone();
38 | }
39 |
40 | public String[] kexAlgorithms;
41 | public String[] serverHostKeyAlgorithms;
42 | public String[] c2s_enc_algos;
43 | public String[] s2c_enc_algos;
44 | public String[] c2s_mac_algos;
45 | public String[] s2c_mac_algos;
46 | public String[] c2s_comp_algos;
47 | public String[] s2c_comp_algos;
48 |
49 | @Override
50 | public CryptoWishList clone() {
51 | return new CryptoWishList(this);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto;
3 |
4 |
5 | import java.math.BigInteger;
6 |
7 | import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
8 |
9 | /**
10 | * Establishes key material for iv/key/mac (both directions).
11 | *
12 | * @author Christian Plattner, plattner@trilead.com
13 | * @version $Id: KeyMaterial.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
14 | */
15 | public class KeyMaterial
16 | {
17 | public byte[] initial_iv_client_to_server;
18 | public byte[] initial_iv_server_to_client;
19 | public byte[] enc_key_client_to_server;
20 | public byte[] enc_key_server_to_client;
21 | public byte[] integrity_key_client_to_server;
22 | public byte[] integrity_key_server_to_client;
23 |
24 | private static byte[] calculateKey(HashForSSH2Types sh, BigInteger K, byte[] H, byte type, byte[] SessionID,
25 | int keyLength)
26 | {
27 | byte[] res = new byte[keyLength];
28 |
29 | int dglen = sh.getDigestLength();
30 | int numRounds = (keyLength + dglen - 1) / dglen;
31 |
32 | byte[][] tmp = new byte[numRounds][];
33 |
34 | sh.reset();
35 | sh.updateBigInt(K);
36 | sh.updateBytes(H);
37 | sh.updateByte(type);
38 | sh.updateBytes(SessionID);
39 |
40 | tmp[0] = sh.getDigest();
41 |
42 | int off = 0;
43 | int produced = Math.min(dglen, keyLength);
44 |
45 | System.arraycopy(tmp[0], 0, res, off, produced);
46 |
47 | keyLength -= produced;
48 | off += produced;
49 |
50 | for (int i = 1; i < numRounds; i++)
51 | {
52 | sh.updateBigInt(K);
53 | sh.updateBytes(H);
54 |
55 | for (int j = 0; j < i; j++)
56 | sh.updateBytes(tmp[j]);
57 |
58 | tmp[i] = sh.getDigest();
59 |
60 | produced = Math.min(dglen, keyLength);
61 | System.arraycopy(tmp[i], 0, res, off, produced);
62 | keyLength -= produced;
63 | off += produced;
64 | }
65 |
66 | return res;
67 | }
68 |
69 | public static KeyMaterial create(String hashAlgo, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
70 | int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC)
71 | throws IllegalArgumentException
72 | {
73 | KeyMaterial km = new KeyMaterial();
74 |
75 | HashForSSH2Types sh = new HashForSSH2Types(hashAlgo);
76 |
77 | km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
78 |
79 | km.initial_iv_server_to_client = calculateKey(sh, K, H, (byte) 'B', SessionID, blockSizeSC);
80 |
81 | km.enc_key_client_to_server = calculateKey(sh, K, H, (byte) 'C', SessionID, keyLengthCS);
82 |
83 | km.enc_key_server_to_client = calculateKey(sh, K, H, (byte) 'D', SessionID, keyLengthSC);
84 |
85 | km.integrity_key_client_to_server = calculateKey(sh, K, H, (byte) 'E', SessionID, macLengthCS);
86 |
87 | km.integrity_key_server_to_client = calculateKey(sh, K, H, (byte) 'F', SessionID, macLengthSC);
88 |
89 | return km;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto;
3 |
4 | /**
5 | * Parsed PEM structure.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
9 | */
10 |
11 | public class PEMStructure
12 | {
13 | public int pemType;
14 | String dekInfo[];
15 | String procType[];
16 | public byte[] data;
17 | }
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/AES.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.cipher;
3 |
4 | import javax.crypto.Cipher;
5 | import javax.crypto.NoSuchPaddingException;
6 | import javax.crypto.ShortBufferException;
7 | import javax.crypto.spec.IvParameterSpec;
8 | import javax.crypto.spec.SecretKeySpec;
9 | import java.security.InvalidAlgorithmParameterException;
10 | import java.security.InvalidKeyException;
11 | import java.security.NoSuchAlgorithmException;
12 |
13 | /**
14 | * AES modes for SSH using the JCE.
15 | */
16 | public abstract class AES implements BlockCipher
17 | {
18 | private final int AES_BLOCK_SIZE = 16;
19 |
20 | protected Cipher cipher;
21 |
22 | @Override
23 | public void init(boolean forEncryption, byte[] key, byte[] iv) {
24 | try {
25 | cipher.init(forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE,
26 | new SecretKeySpec(key, "AES"),
27 | new IvParameterSpec(iv));
28 | } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
29 | throw new IllegalArgumentException("Cannot initialize " + cipher.getAlgorithm(), e);
30 | }
31 | }
32 |
33 | @Override
34 | public int getBlockSize() {
35 | return AES_BLOCK_SIZE;
36 | }
37 |
38 | @Override
39 | public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
40 | try {
41 | cipher.update(src, srcoff, AES_BLOCK_SIZE, dst, dstoff);
42 | } catch (ShortBufferException e) {
43 | throw new AssertionError(e);
44 | }
45 | }
46 |
47 | public static class CBC extends AES {
48 | public CBC() throws IllegalArgumentException {
49 | try {
50 | cipher = Cipher.getInstance("AES/CBC/NoPadding");
51 | } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
52 | throw new IllegalArgumentException("Cannot initialize AES/CBC/NoPadding", e);
53 | }
54 | }
55 | }
56 |
57 | public static class CTR extends AES {
58 | public CTR() throws IllegalArgumentException {
59 | try {
60 | cipher = Cipher.getInstance("AES/CTR/NoPadding");
61 | } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
62 | throw new IllegalArgumentException("Cannot initialize AES/CBC/NoPadding", e);
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipher.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.cipher;
2 |
3 | /**
4 | * BlockCipher.
5 | *
6 | * @author Christian Plattner, plattner@trilead.com
7 | * @version $Id: BlockCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
8 | */
9 | public interface BlockCipher
10 | {
11 | void init(boolean forEncryption, byte[] key, byte[] iv) throws IllegalArgumentException;
12 |
13 | int getBlockSize();
14 |
15 | void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff);
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.cipher;
3 |
4 | import java.lang.reflect.Constructor;
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * BlockCipherFactory.
9 | *
10 | * @author Christian Plattner, plattner@trilead.com
11 | * @version $Id: BlockCipherFactory.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
12 | */
13 | public class BlockCipherFactory
14 | {
15 | private static class CipherEntry
16 | {
17 | final String type;
18 | final int blocksize;
19 | final int keysize;
20 | final String cipherClass;
21 |
22 | CipherEntry(String type, int blockSize, int keySize, String cipherClass)
23 | {
24 | this.type = type;
25 | this.blocksize = blockSize;
26 | this.keysize = keySize;
27 | this.cipherClass = cipherClass;
28 | }
29 | }
30 |
31 | private static final ArrayList ciphers = new ArrayList<>();
32 |
33 | static
34 | {
35 | /* Higher Priority First */
36 |
37 | ciphers.add(new CipherEntry("aes256-ctr", 16, 32, "com.trilead.ssh2.crypto.cipher.AES$CTR"));
38 | ciphers.add(new CipherEntry("aes128-ctr", 16, 16, "com.trilead.ssh2.crypto.cipher.AES$CTR"));
39 | ciphers.add(new CipherEntry("blowfish-ctr", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish$CTR"));
40 |
41 | ciphers.add(new CipherEntry("aes256-cbc", 16, 32, "com.trilead.ssh2.crypto.cipher.AES$CBC"));
42 | ciphers.add(new CipherEntry("aes128-cbc", 16, 16, "com.trilead.ssh2.crypto.cipher.AES$CBC"));
43 | ciphers.add(new CipherEntry("blowfish-cbc", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish$CBC"));
44 |
45 | ciphers.add(new CipherEntry("3des-ctr", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede$CTR"));
46 | ciphers.add(new CipherEntry("3des-cbc", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede$CBC"));
47 | }
48 |
49 | public static String[] getDefaultCipherList()
50 | {
51 | String list[] = new String[ciphers.size()];
52 | for (int i = 0; i < ciphers.size(); i++)
53 | {
54 | CipherEntry ce = ciphers.get(i);
55 | list[i] = ce.type;
56 | }
57 | return list;
58 | }
59 |
60 | public static void checkCipherList(String[] cipherCandidates)
61 | {
62 | for (String cipherCandidate : cipherCandidates)
63 | getEntry(cipherCandidate);
64 | }
65 |
66 | public static BlockCipher createCipher(String type, boolean encrypt, byte[] key, byte[] iv)
67 | {
68 | try
69 | {
70 | CipherEntry ce = getEntry(type);
71 | Class cc = Class.forName(ce.cipherClass);
72 | Constructor constructor = cc.getConstructor();
73 | BlockCipher bc = constructor.newInstance();
74 | bc.init(encrypt, key, iv);
75 | return bc;
76 | }
77 | catch (Exception e)
78 | {
79 | throw new IllegalArgumentException("Cannot instantiate " + type, e);
80 | }
81 | }
82 |
83 | private static CipherEntry getEntry(String type)
84 | {
85 | for (CipherEntry ce : ciphers) {
86 | if (ce.type.equals(type))
87 | return ce;
88 | }
89 | throw new IllegalArgumentException("Unknown algorithm " + type);
90 | }
91 |
92 | public static int getBlockSize(String type)
93 | {
94 | CipherEntry ce = getEntry(type);
95 | return ce.blocksize;
96 | }
97 |
98 | public static int getKeySize(String type)
99 | {
100 | CipherEntry ce = getEntry(type);
101 | return ce.keysize;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/CBCMode.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.cipher;
2 |
3 | /**
4 | * CBCMode.
5 | *
6 | * @author Christian Plattner, plattner@trilead.com
7 | * @version $Id: CBCMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
8 | */
9 | public class CBCMode implements BlockCipher
10 | {
11 | BlockCipher tc;
12 | int blockSize;
13 | boolean doEncrypt;
14 |
15 | byte[] cbc_vector;
16 | byte[] tmp_vector;
17 |
18 | public void init(boolean forEncryption, byte[] key, byte[] iv)
19 | {
20 | }
21 |
22 | public CBCMode(BlockCipher tc, byte[] iv, boolean doEncrypt)
23 | throws IllegalArgumentException
24 | {
25 | this.tc = tc;
26 | this.blockSize = tc.getBlockSize();
27 | this.doEncrypt = doEncrypt;
28 |
29 | if (this.blockSize != iv.length)
30 | throw new IllegalArgumentException("IV must be " + blockSize
31 | + " bytes long! (currently " + iv.length + ")");
32 |
33 | this.cbc_vector = new byte[blockSize];
34 | this.tmp_vector = new byte[blockSize];
35 | System.arraycopy(iv, 0, cbc_vector, 0, blockSize);
36 | }
37 |
38 | public int getBlockSize()
39 | {
40 | return blockSize;
41 | }
42 |
43 | private void encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
44 | {
45 | for (int i = 0; i < blockSize; i++)
46 | cbc_vector[i] ^= src[srcoff + i];
47 |
48 | tc.transformBlock(cbc_vector, 0, dst, dstoff);
49 |
50 | System.arraycopy(dst, dstoff, cbc_vector, 0, blockSize);
51 | }
52 |
53 | private void decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
54 | {
55 | /* Assume the worst, src and dst are overlapping... */
56 |
57 | System.arraycopy(src, srcoff, tmp_vector, 0, blockSize);
58 |
59 | tc.transformBlock(src, srcoff, dst, dstoff);
60 |
61 | for (int i = 0; i < blockSize; i++)
62 | dst[dstoff + i] ^= cbc_vector[i];
63 |
64 | /* ...that is why we need a tmp buffer. */
65 |
66 | byte[] swap = cbc_vector;
67 | cbc_vector = tmp_vector;
68 | tmp_vector = swap;
69 | }
70 |
71 | public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
72 | {
73 | if (doEncrypt)
74 | encryptBlock(src, srcoff, dst, dstoff);
75 | else
76 | decryptBlock(src, srcoff, dst, dstoff);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/CTRMode.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.cipher;
3 |
4 | /**
5 | * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: CTRMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
9 | */
10 | public class CTRMode implements BlockCipher
11 | {
12 | byte[] X;
13 | byte[] Xenc;
14 |
15 | BlockCipher bc;
16 | int blockSize;
17 | boolean doEncrypt;
18 |
19 | int count = 0;
20 |
21 | public void init(boolean forEncryption, byte[] key, byte[] iv)
22 | {
23 | }
24 |
25 | public CTRMode(BlockCipher tc, byte[] iv, boolean doEnc) throws IllegalArgumentException
26 | {
27 | bc = tc;
28 | blockSize = bc.getBlockSize();
29 | doEncrypt = doEnc;
30 |
31 | if (blockSize != iv.length)
32 | throw new IllegalArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.length + ")");
33 |
34 | X = new byte[blockSize];
35 | Xenc = new byte[blockSize];
36 |
37 | System.arraycopy(iv, 0, X, 0, blockSize);
38 | }
39 |
40 | public final int getBlockSize()
41 | {
42 | return blockSize;
43 | }
44 |
45 | public final void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
46 | {
47 | bc.transformBlock(X, 0, Xenc, 0);
48 |
49 | for (int i = 0; i < blockSize; i++)
50 | {
51 | dst[dstoff + i] = (byte) (src[srcoff + i] ^ Xenc[i]);
52 | }
53 |
54 | for (int i = (blockSize - 1); i >= 0; i--)
55 | {
56 | X[i]++;
57 | if (X[i] != 0)
58 | break;
59 |
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.cipher;
3 |
4 | import java.io.BufferedOutputStream;
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.IOException;
7 | import java.io.OutputStream;
8 |
9 | /**
10 | * CipherOutputStream.
11 | *
12 | * @author Christian Plattner, plattner@trilead.com
13 | * @version $Id: CipherOutputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
14 | */
15 | public class CipherOutputStream
16 | {
17 | private BlockCipher currentCipher;
18 | private final BufferedOutputStream bo;
19 | private byte[] buffer;
20 | private byte[] enc;
21 | private int blockSize;
22 | private int pos;
23 | private boolean recordingOutput;
24 | private final ByteArrayOutputStream recordingOutputStream = new ByteArrayOutputStream();
25 |
26 | public CipherOutputStream(BlockCipher tc, OutputStream bo)
27 | {
28 | if (bo instanceof BufferedOutputStream) {
29 | this.bo = (BufferedOutputStream) bo;
30 | } else {
31 | this.bo = new BufferedOutputStream(bo);
32 | }
33 | changeCipher(tc);
34 | }
35 |
36 | public void flush() throws IOException
37 | {
38 | if (pos != 0)
39 | throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");
40 |
41 | bo.flush();
42 | }
43 |
44 | public void changeCipher(BlockCipher bc)
45 | {
46 | this.currentCipher = bc;
47 | blockSize = bc.getBlockSize();
48 | buffer = new byte[blockSize];
49 | enc = new byte[blockSize];
50 | pos = 0;
51 | }
52 |
53 | public void startRecording() {
54 | recordingOutput = true;
55 | }
56 |
57 | public byte[] getRecordedOutput() {
58 | recordingOutput = false;
59 | byte[] recordedOutput = recordingOutputStream.toByteArray();
60 | recordingOutputStream.reset();
61 | return recordedOutput;
62 | }
63 |
64 | private void writeBlock() throws IOException
65 | {
66 | try
67 | {
68 | currentCipher.transformBlock(buffer, 0, enc, 0);
69 | }
70 | catch (Exception e)
71 | {
72 | throw new IOException("Error while decrypting block.", e);
73 | }
74 |
75 | bo.write(enc, 0, blockSize);
76 | pos = 0;
77 |
78 | if (recordingOutput) {
79 | recordingOutputStream.write(enc, 0, blockSize);
80 | }
81 | }
82 |
83 | public void write(byte[] src, int off, int len) throws IOException
84 | {
85 | while (len > 0)
86 | {
87 | int avail = blockSize - pos;
88 | int copy = Math.min(avail, len);
89 |
90 | System.arraycopy(src, off, buffer, pos, copy);
91 | pos += copy;
92 | off += copy;
93 | len -= copy;
94 |
95 | if (pos >= blockSize)
96 | writeBlock();
97 | }
98 | }
99 |
100 | public void write(int b) throws IOException
101 | {
102 | buffer[pos++] = (byte) b;
103 | if (pos >= blockSize)
104 | writeBlock();
105 | }
106 |
107 | public void writePlain(int b) throws IOException
108 | {
109 | if (pos != 0)
110 | throw new IOException("Cannot write plain since crypto buffer is not aligned.");
111 | bo.write(b);
112 | }
113 |
114 | public void writePlain(byte[] b, int off, int len) throws IOException
115 | {
116 | if (pos != 0)
117 | throw new IOException("Cannot write plain since crypto buffer is not aligned.");
118 | bo.write(b, off, len);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/EtmCipher.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.cipher;
2 |
3 | public interface EtmCipher {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/cipher/NullCipher.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.cipher;
2 |
3 | /**
4 | * NullCipher.
5 | *
6 | * @author Christian Plattner, plattner@trilead.com
7 | * @version $Id: NullCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
8 | */
9 | public class NullCipher implements BlockCipher
10 | {
11 | private int blockSize = 8;
12 |
13 | public NullCipher()
14 | {
15 | }
16 |
17 | public NullCipher(int blockSize)
18 | {
19 | this.blockSize = blockSize;
20 | }
21 |
22 | public void init(boolean forEncryption, byte[] key, byte[] iv)
23 | {
24 | }
25 |
26 | public int getBlockSize()
27 | {
28 | return blockSize;
29 | }
30 |
31 | public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
32 | {
33 | System.arraycopy(src, srcoff, dst, dstoff, blockSize);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/dh/Curve25519Exchange.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.dh;
2 |
3 | import com.google.crypto.tink.subtle.X25519;
4 |
5 | import java.io.IOException;
6 | import java.math.BigInteger;
7 | import java.security.InvalidKeyException;
8 |
9 | /**
10 | * Created by Kenny Root on 1/23/16.
11 | */
12 | public class Curve25519Exchange extends GenericDhExchange {
13 | public static final String NAME = "curve25519-sha256";
14 | public static final String ALT_NAME = "curve25519-sha256@libssh.org";
15 | public static final int KEY_SIZE = 32;
16 |
17 | private byte[] clientPublic;
18 | private byte[] clientPrivate;
19 | private byte[] serverPublic;
20 |
21 | public Curve25519Exchange() {
22 | super();
23 | }
24 |
25 | /*
26 | * Used to test known vectors.
27 | */
28 | public Curve25519Exchange(byte[] secret) throws InvalidKeyException {
29 | if (secret.length != KEY_SIZE) {
30 | throw new AssertionError("secret must be key size");
31 | }
32 | clientPrivate = secret.clone();
33 | }
34 |
35 | @Override
36 | public void init(String name) throws IOException {
37 | if (!NAME.equals(name) && !ALT_NAME.equals(name)) {
38 | throw new IOException("Invalid name " + name);
39 | }
40 |
41 | clientPrivate = X25519.generatePrivateKey();
42 | try {
43 | clientPublic = X25519.publicFromPrivate(clientPrivate);
44 | } catch (InvalidKeyException e) {
45 | throw new IOException(e);
46 | }
47 | }
48 |
49 | @Override
50 | public byte[] getE() {
51 | return clientPublic.clone();
52 | }
53 |
54 | @Override
55 | protected byte[] getServerE() {
56 | return serverPublic.clone();
57 | }
58 |
59 | @Override
60 | public void setF(byte[] f) throws IOException {
61 | if (f.length != KEY_SIZE) {
62 | throw new IOException("Server sent invalid key length " + f.length + " (expected " +
63 | KEY_SIZE + ")");
64 | }
65 | serverPublic = f.clone();
66 | try {
67 | byte[] sharedSecretBytes = X25519.computeSharedSecret(clientPrivate, serverPublic);
68 | int allBytes = 0;
69 | for (int i = 0; i < sharedSecretBytes.length; i++) {
70 | allBytes |= sharedSecretBytes[i];
71 | }
72 | if (allBytes == 0) {
73 | throw new IOException("Invalid key computed; all zeroes");
74 | }
75 | sharedSecret = new BigInteger(1, sharedSecretBytes);
76 | } catch (InvalidKeyException e) {
77 | throw new IOException(e);
78 | }
79 | }
80 |
81 | @Override
82 | public String getHashAlgo() {
83 | return "SHA-256";
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.dh;
3 |
4 | import java.math.BigInteger;
5 | import java.security.SecureRandom;
6 |
7 | import com.trilead.ssh2.DHGexParameters;
8 | import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
9 |
10 |
11 | /**
12 | * DhGroupExchange.
13 | *
14 | * @author Christian Plattner, plattner@trilead.com
15 | * @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
16 | */
17 | public class DhGroupExchange
18 | {
19 | /* Given by the standard */
20 |
21 | private BigInteger p;
22 | private BigInteger g;
23 |
24 | /* Client public and private */
25 |
26 | private BigInteger e;
27 | private BigInteger x;
28 |
29 | /* Server public */
30 |
31 | private BigInteger f;
32 |
33 | /* Shared secret */
34 |
35 | private BigInteger k;
36 |
37 | public DhGroupExchange(BigInteger p, BigInteger g)
38 | {
39 | this.p = p;
40 | this.g = g;
41 | }
42 |
43 | public void init(SecureRandom rnd)
44 | {
45 | k = null;
46 |
47 | x = new BigInteger(p.bitLength() - 1, rnd);
48 | e = g.modPow(x, p);
49 | }
50 |
51 | /**
52 | * @return Returns the e.
53 | */
54 | public BigInteger getE()
55 | {
56 | if (e == null)
57 | throw new IllegalStateException("Not initialized!");
58 |
59 | return e;
60 | }
61 |
62 | /**
63 | * @return Returns the shared secret k.
64 | */
65 | public BigInteger getK()
66 | {
67 | if (k == null)
68 | throw new IllegalStateException("Shared secret not yet known, need f first!");
69 |
70 | return k;
71 | }
72 |
73 | /**
74 | * Sets f and calculates the shared secret.
75 | */
76 | public void setF(BigInteger f)
77 | {
78 | if (e == null)
79 | throw new IllegalStateException("Not initialized!");
80 |
81 | BigInteger zero = BigInteger.valueOf(0);
82 |
83 | if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0)
84 | throw new IllegalArgumentException("Invalid f specified!");
85 |
86 | this.f = f;
87 | this.k = f.modPow(x, p);
88 | }
89 |
90 | public byte[] calculateH(String hashAlgo, byte[] clientversion, byte[] serverversion,
91 | byte[] clientKexPayload, byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
92 | {
93 | HashForSSH2Types hash = new HashForSSH2Types(hashAlgo);
94 |
95 | hash.updateByteString(clientversion);
96 | hash.updateByteString(serverversion);
97 | hash.updateByteString(clientKexPayload);
98 | hash.updateByteString(serverKexPayload);
99 | hash.updateByteString(hostKey);
100 | if (para.getMin_group_len() > 0)
101 | hash.updateUINT32(para.getMin_group_len());
102 | hash.updateUINT32(para.getPref_group_len());
103 | if (para.getMax_group_len() > 0)
104 | hash.updateUINT32(para.getMax_group_len());
105 | hash.updateBigInt(p);
106 | hash.updateBigInt(g);
107 | hash.updateBigInt(e);
108 | hash.updateBigInt(f);
109 | hash.updateBigInt(k);
110 |
111 | return hash.getDigest();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.dh;
3 |
4 | import java.io.IOException;
5 | import java.io.UnsupportedEncodingException;
6 | import java.math.BigInteger;
7 |
8 | import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
9 | import com.trilead.ssh2.log.Logger;
10 |
11 |
12 | /**
13 | * DhExchange.
14 | *
15 | * @author Christian Plattner, plattner@trilead.com
16 | * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
17 | */
18 | public abstract class GenericDhExchange
19 | {
20 | private static final Logger log = Logger.getLogger(GenericDhExchange.class);
21 |
22 | /* Shared secret */
23 |
24 | BigInteger sharedSecret;
25 |
26 | protected GenericDhExchange()
27 | {
28 | }
29 |
30 | public static GenericDhExchange getInstance(String algo) {
31 | if (Curve25519Exchange.NAME.equals(algo) || Curve25519Exchange.ALT_NAME.equals(algo)) {
32 | return new Curve25519Exchange();
33 | }
34 | if (algo.startsWith("ecdh-sha2-")) {
35 | return new EcDhExchange();
36 | } else {
37 | return new DhExchange();
38 | }
39 | }
40 |
41 | public abstract void init(String name) throws IOException;
42 |
43 | /**
44 | * @return Returns the e (public value)
45 | * @throws IllegalStateException
46 | */
47 | public abstract byte[] getE();
48 |
49 | /**
50 | * @return Returns the server's e (public value)
51 | * @throws IllegalStateException
52 | */
53 | protected abstract byte[] getServerE();
54 |
55 | /**
56 | * @return Returns the shared secret k.
57 | * @throws IllegalStateException
58 | */
59 | public BigInteger getK()
60 | {
61 | if (sharedSecret == null)
62 | throw new IllegalStateException("Shared secret not yet known, need f first!");
63 |
64 | return sharedSecret;
65 | }
66 |
67 | /**
68 | * @param f
69 | */
70 | public abstract void setF(byte[] f) throws IOException;
71 |
72 | public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
73 | byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
74 | {
75 | HashForSSH2Types hash = new HashForSSH2Types(getHashAlgo());
76 |
77 | if (log.isEnabled())
78 | {
79 | log.log(90, "Client: '" + new String(clientversion) + "'");
80 | log.log(90, "Server: '" + new String(serverversion) + "'");
81 | }
82 |
83 | hash.updateByteString(clientversion);
84 | hash.updateByteString(serverversion);
85 | hash.updateByteString(clientKexPayload);
86 | hash.updateByteString(serverKexPayload);
87 | hash.updateByteString(hostKey);
88 | hash.updateByteString(getE());
89 | hash.updateByteString(getServerE());
90 | hash.updateBigInt(sharedSecret);
91 |
92 | return hash.getDigest();
93 | }
94 |
95 | public abstract String getHashAlgo();
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.digest;
3 |
4 | import java.math.BigInteger;
5 | import java.security.DigestException;
6 | import java.security.MessageDigest;
7 | import java.security.NoSuchAlgorithmException;
8 |
9 | /**
10 | * HashForSSH2Types.
11 | *
12 | * @author Christian Plattner, plattner@trilead.com
13 | * @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
14 | */
15 | public class HashForSSH2Types
16 | {
17 | MessageDigest md;
18 |
19 | public HashForSSH2Types(String type)
20 | {
21 | try {
22 | md = MessageDigest.getInstance(type);
23 | } catch (NoSuchAlgorithmException e) {
24 | throw new RuntimeException("Unsupported algorithm " + type);
25 | }
26 | }
27 |
28 | public void updateByte(byte b)
29 | {
30 | /* HACK - to test it with J2ME */
31 | byte[] tmp = new byte[1];
32 | tmp[0] = b;
33 | md.update(tmp);
34 | }
35 |
36 | public void updateBytes(byte[] b)
37 | {
38 | md.update(b);
39 | }
40 |
41 | public void updateUINT32(int v)
42 | {
43 | md.update((byte) (v >> 24));
44 | md.update((byte) (v >> 16));
45 | md.update((byte) (v >> 8));
46 | md.update((byte) (v));
47 | }
48 |
49 | public void updateByteString(byte[] b)
50 | {
51 | updateUINT32(b.length);
52 | updateBytes(b);
53 | }
54 |
55 | public void updateBigInt(BigInteger b)
56 | {
57 | updateByteString(b.toByteArray());
58 | }
59 |
60 | public void reset()
61 | {
62 | md.reset();
63 | }
64 |
65 | public int getDigestLength()
66 | {
67 | return md.getDigestLength();
68 | }
69 |
70 | public byte[] getDigest()
71 | {
72 | byte[] tmp = new byte[md.getDigestLength()];
73 | getDigest(tmp);
74 | return tmp;
75 | }
76 |
77 | public void getDigest(byte[] out)
78 | {
79 | getDigest(out, 0);
80 | }
81 |
82 | public void getDigest(byte[] out, int off)
83 | {
84 | try {
85 | md.digest(out, off, out.length - off);
86 | } catch (DigestException e) {
87 | // TODO is this right?!
88 | throw new RuntimeException("Unable to digest", e);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/digest/MAC.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.digest;
2 |
3 | /**
4 | * Created by kenny on 2/12/17.
5 | */
6 | public interface MAC {
7 | void initMac(int seq);
8 | void update(byte[] packetdata, int off, int len);
9 | void getMac(byte[] out, int off);
10 | int size();
11 | boolean isEncryptThenMac();
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/digest/MACs.java:
--------------------------------------------------------------------------------
1 |
2 | package com.trilead.ssh2.crypto.digest;
3 |
4 | /**
5 | * MAC.
6 | *
7 | * @author Christian Plattner, plattner@trilead.com
8 | * @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
9 | */
10 | public final class MACs
11 | {
12 | /* Higher Priority First */
13 | private static final String[] MAC_LIST = {
14 | HMAC.HMAC_SHA2_256_ETM,
15 | HMAC.HMAC_SHA2_512_ETM,
16 | HMAC.HMAC_SHA1_ETM,
17 | HMAC.HMAC_SHA2_256,
18 | HMAC.HMAC_SHA2_512,
19 | HMAC.HMAC_SHA1,
20 | };
21 |
22 | public final static String[] getMacList()
23 | {
24 | return MAC_LIST;
25 | }
26 |
27 | public final static void checkMacList(String[] macs)
28 | {
29 | for (int i = 0; i < macs.length; i++) {
30 | getKeyLen(macs[i]);
31 | }
32 | }
33 |
34 | public final static int getKeyLen(String type)
35 | {
36 | if (type == null)
37 | throw new IllegalArgumentException("type == null");
38 |
39 | if (type.startsWith(HMAC.HMAC_SHA1))
40 | return 20;
41 | if (type.startsWith(HMAC.HMAC_MD5))
42 | return 16;
43 | if (type.startsWith(HMAC.HMAC_SHA2_256))
44 | return 32;
45 | if (type.startsWith(HMAC.HMAC_SHA2_512))
46 | return 64;
47 |
48 | throw new IllegalArgumentException("Unknown algorithm " + type);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/keys/Ed25519KeyFactory.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.keys;
2 |
3 | import java.security.InvalidKeyException;
4 | import java.security.Key;
5 | import java.security.KeyFactorySpi;
6 | import java.security.PrivateKey;
7 | import java.security.PublicKey;
8 | import java.security.spec.InvalidKeySpecException;
9 | import java.security.spec.KeySpec;
10 | import java.security.spec.PKCS8EncodedKeySpec;
11 | import java.security.spec.X509EncodedKeySpec;
12 |
13 | public class Ed25519KeyFactory extends KeyFactorySpi {
14 | @Override
15 | protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
16 | if (keySpec instanceof X509EncodedKeySpec) {
17 | return new Ed25519PublicKey((X509EncodedKeySpec) keySpec);
18 | }
19 | throw new InvalidKeySpecException("Unrecognized key spec: " + keySpec.getClass());
20 | }
21 |
22 | @Override
23 | protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
24 | if (keySpec instanceof PKCS8EncodedKeySpec) {
25 | return new Ed25519PrivateKey((PKCS8EncodedKeySpec) keySpec);
26 | }
27 | throw new InvalidKeySpecException("Unrecognized key spec: " + keySpec.getClass());
28 | }
29 |
30 | @Override
31 | protected T engineGetKeySpec(Key key, Class keySpec) throws InvalidKeySpecException {
32 | throw new InvalidKeySpecException("not implemented yet " + key + " " + keySpec);
33 | }
34 |
35 | @Override
36 | public Key engineTranslateKey(Key key) throws InvalidKeyException {
37 | if (key instanceof Ed25519PublicKey || key instanceof Ed25519PrivateKey) {
38 | return key;
39 | }
40 |
41 | if (key instanceof PublicKey && key.getFormat().equals("X.509")) {
42 | byte[] encoded = key.getEncoded();
43 | try {
44 | return new Ed25519PublicKey(new X509EncodedKeySpec(encoded));
45 | } catch (InvalidKeySpecException e) {
46 | throw new InvalidKeyException(e);
47 | }
48 | }
49 |
50 | if (key instanceof PrivateKey && key.getFormat().equals("PKCS#8")) {
51 | byte[] encoded = key.getEncoded();
52 | try {
53 | return new Ed25519PrivateKey(new PKCS8EncodedKeySpec(encoded));
54 | } catch (InvalidKeySpecException e) {
55 | throw new InvalidKeyException(e);
56 | }
57 | }
58 |
59 | throw new InvalidKeyException("Could not convert EdDSA key from key class " + key.getClass());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/keys/Ed25519KeyPairGenerator.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.keys;
2 |
3 | import com.google.crypto.tink.subtle.Ed25519Sign;
4 |
5 | import java.security.GeneralSecurityException;
6 | import java.security.KeyPair;
7 | import java.security.KeyPairGeneratorSpi;
8 | import java.security.SecureRandom;
9 |
10 | public class Ed25519KeyPairGenerator extends KeyPairGeneratorSpi {
11 | @Override
12 | public void initialize(int keySize, SecureRandom secureRandom) {
13 | // ignored.
14 | }
15 |
16 | @Override
17 | public KeyPair generateKeyPair() {
18 | try {
19 | Ed25519Sign.KeyPair kp = Ed25519Sign.KeyPair.newKeyPair();
20 | return new KeyPair(new Ed25519PublicKey(kp.getPublicKey()), new Ed25519PrivateKey(kp.getPrivateKey()));
21 | } catch (GeneralSecurityException e) {
22 | throw new IllegalStateException(e);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/trilead/ssh2/crypto/keys/Ed25519Provider.java:
--------------------------------------------------------------------------------
1 | package com.trilead.ssh2.crypto.keys;
2 |
3 | import java.security.AccessController;
4 | import java.security.PrivilegedAction;
5 | import java.security.Provider;
6 | import java.security.Security;
7 |
8 | public class Ed25519Provider extends Provider {
9 | public static final String NAME = "ConnectBot Ed25519 Provider";
10 | public static final String KEY_ALGORITHM = "Ed25519";
11 | private static final Object sInitLock = new Object();
12 | private static boolean sInitialized = false;
13 |
14 | public Ed25519Provider() {
15 | super(NAME, 1.0, "Not for use elsewhere");
16 | AccessController.doPrivileged((PrivilegedAction