diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ca2841c3..7e7a78a1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,9 +9,11 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" + open-pull-requests-limit: 25 - package-ecosystem: "github-actions" # Workflow files stored in the # default location of `.github/workflows` directory: "/" schedule: - interval: "weekly" + interval: "weekly" + open-pull-requests-limit: 25 diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 00000000..729c50a2 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,23 @@ +name: Dependabot auto-merge patch versions +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Enable auto-merge for Dependabot PRs + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 73c8ad13..a70d2437 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -31,18 +31,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: 'zulu' - java-version: '17' - check-latest: true + java-version: '21' + check-latest: true cache: 'maven' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 # ℹī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -69,4 +69,4 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 0159aa13..420557da 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -7,15 +7,15 @@ jobs: coverity: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up build JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: 'zulu' - java-version: '17' - check-latest: true + java-version: '21' + check-latest: true cache: 'maven' - - uses: vapier/coverity-scan-action@v1 + - uses: vapier/coverity-scan-action@2068473c7bdf8c2fb984a6a40ae76ee7facd7a85 # v1.8.0 with: email: ${{ secrets.COVERITY_SCAN_EMAIL }} token: ${{ secrets.COVERITY_SCAN_TOKEN }} diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml index c96733e0..8d74e9de 100644 --- a/.github/workflows/javadoc.yml +++ b/.github/workflows/javadoc.yml @@ -11,17 +11,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎ī¸ - uses: actions/checkout@v3 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up build JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: 'zulu' - java-version: '17' + java-version: '21' check-latest: true - name: Build Javadoc - run: mvn -B -V javadoc:javadoc --file pom.xml + run: ./mvnw -B -V -e javadoc:javadoc - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4 + uses: JamesIves/github-pages-deploy-action@94f3c658273cf92fb48ef99e5fbc02bd2dc642b2 # v4.6.3 with: folder: target/site/apidocs target-folder: ${{github.ref_name}} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index ba2a157d..9cdd4cb7 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -15,42 +15,67 @@ on: jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - java: ['8', '11', '17'] steps: - - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: - path: ~/.m2/repository - key: ${{ runner.os }}-${{ matrix.java }}-maven-${{ hashFiles('**/pom.xml') }} + path: | + ~/.m2/repository + ~/.m2/wrapper + key: ${{ runner.os }}-build-maven-${{ hashFiles('**/pom.xml', '**/maven-wrapper.properties') }} restore-keys: | - ${{ runner.os }}-${{ matrix.java }}-maven- - - name: Set up build JDK - uses: actions/setup-java@v3 + ${{ runner.os }}-build-maven- + - name: Set up JDK + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: 'zulu' - java-version: '17' + java-version: '21' check-latest: true - name: Build with Maven - run: mvn -B -V -DskipTests=true package --file pom.xml - - uses: actions/upload-artifact@v3 + run: ./mvnw -B -V -e verify -DskipTests=true -DskipITs=true + - name: Archive target directory + run: tar -cf target.tar target + - name: Upload target directory archive + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + with: + name: build_target + path: target.tar + test: + needs: build + runs-on: ubuntu-latest + strategy: + matrix: + java: ['8', '11', '17', '21', '22'] + steps: + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Cache local Maven repository + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: - name: java-${{ matrix.java }}-jars path: | - **/target/*.jar - **/target/bom.* - if: always() - - name: Set up test JDK ${{ matrix.java }} - uses: actions/setup-java@v3 + ~/.m2/repository + ~/.m2/wrapper + key: ${{ runner.os }}-test-${{ matrix.java }}-maven-${{ hashFiles('**/pom.xml', '**/maven-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-test-${{ matrix.java }}-maven- + - name: Set up JDK + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: 'zulu' java-version: ${{ matrix.java }} check-latest: true + - name: Download target directory archive + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: build_target + - name: Extract target directory archive + run: tar -xf target.tar - name: Test with Maven - run: mvn -B -V -P coverage verify -Denforcer.skip=true -Dmaven.resources.skip=true -Dmaven.main.skip=true -Dassembly.skipAssembly=true -Dmaven.javadoc.skip=true -DskipITs=false --file pom.xml - - uses: actions/upload-artifact@v3 + run: ./mvnw -B -V -e -Pcoverage verify -Denforcer.skip=true -Dmaven.resources.skip=true -Dflatten.skip=true -Dmaven.main.skip=true -Dbnd.skip=true -Dassembly.skipAssembly=true -Dmaven.javadoc.skip=true -Dcyclonedx.skip=true -Dformatter.skip=true -Dforbiddenapis.skip=true -DskipTests=false -DskipITs=false + - name: Upload test results + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: java-${{ matrix.java }}-testresults path: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e9aa023e..7a354aea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 # Runs a set of commands using the runners shell - name: Release diff --git a/.gitignore b/.gitignore index 606ae2fb..389ad015 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.geany nb-configuration.xml .flattened-pom.xml +target.tar # Created by https://www.toptal.com/developers/gitignore/api/intellij+all,netbeans,eclipse,visualstudiocode,vim,emacs,macos,windows,linux,java,maven # Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,netbeans,eclipse,visualstudiocode,vim,emacs,macos,windows,linux,java,maven diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index d8b2495a..f95f1ee8 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,5 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 00000000..2b547b6f --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1,4 @@ +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=21.0.4-tem +maven=3.9.8 diff --git a/ChangeLog.md b/ChangeLog.md index daa2e1d6..10a242e0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,45 @@ +* [0.2.19](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.19) + * Enforce DHGEX prime modulus bit length meets configured constraints. + * #604 Fix possible rekeying timeouts. +* [0.2.18](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.18) + * Handle negated patterns according to ssh_config(5) by @bmiddaugh in https://github.com/mwiede/jsch/pull/565 +* [0.2.17](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.17) + * Add PBKDF2-HMAC-SHA512/256 & PBKDF2-HMAC-SHA512/224, which are both supported as of Java 21. +* [0.2.16](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.16) + * Add support for sntrup761x25519-sha512@openssh.com KEX algorithm. + * Switch to bnd-maven-plugin in order to support Multi-Release OSGi bundle JAR's via supplemental manifest files. + * Introduce JSchProxyException to replace generic JschException in Proxy implementations by @mvegter in https://github.com/mwiede/jsch/pull/467 +* [0.2.15](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.15) + * address [CVE-2023-48795](https://nvd.nist.gov/vuln/detail/CVE-2023-48795) by adding support for new strict key exchange extension + * Add support for `ext-info-in-auth@openssh.com` extension +* [0.2.14](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.14) + * [#450](https://github.com/mwiede/jsch/issues/450) use Socket.connect() with a timeout that has been supported since Java 1.4 instead of using old method of creating a separate thread and joining to that thread with timeout. +* [0.2.13](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.13) + * #411 Add flush operation from Fix added is/jsch#39, with new config option to allow disabling in case it causes regressions. + * #403 add a warning when Channel.getInputStream() or Channel.getExtInputStream() is called after Channel.connect(). +* [0.2.12](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.12) + * Further refine previous fixes for windows line endings in PEM keys from [#369](https://github.com/mwiede/jsch/issues/369) & [#362](https://github.com/mwiede/jsch/issues/362). + * replace call to BigInteger.intValueExact to remain comptaible with Android [#397](https://github.com/mwiede/jsch/pull/397) + * Introduce JSchSessionDisconnectException to allow the reasonCode to be retrieved without String parsing [#416](https://github.com/mwiede/jsch/pull/416) + * Introduce specific JSchException for HostKey related failures [#410](https://github.com/mwiede/jsch/pull/410) +* [0.2.11](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.11) + * [#369](https://github.com/mwiede/jsch/issues/369) fix multi-line PEM key parsing to work with windows line endings due to regression from previous fix for [#362](https://github.com/mwiede/jsch/issues/362). +* [0.2.10](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.10) + * Fix new Java 21 compiler warning: `possible 'this' escape before subclass is fully initialized`. + * Tweak OSGi bundle manifest to allow Log4j 3. + * [#362](https://github.com/mwiede/jsch/issues/362) fix PEM key parsing to work with windows line endings. + * [#361](https://github.com/mwiede/jsch/issues/361) guard against `UIKeyboardInteractive` implementations that include NULL elements in the `String[]` returned from `promptKeyboardInteractive()`. + * Add a default implmentation of the deprecated `decrypt()` method to the `Identity` interface that throws an `UnsupportedOperationException`. +* [0.2.9](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.9) + * [#293](https://github.com/mwiede/jsch/issues/293) allow UserAuthNone to be extended. + * Make JGSS module optional. + * Tweak OSGi bundle manifest: + * Avoid self-import. + * Mark JGSS as optional. + * Loosen import versions of dependencies. + * Correctly adhere to the Multi-release JAR spec by ensuring all public classes under versioned directories preside over classes present in the top-level directory. + * Eliminate stray `System.err.println()` calls. + * Change PageantConnector to use JNA's built-in support for `User32.SendMessage()`. * [0.2.8](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.8) * [#287](https://github.com/mwiede/jsch/issues/287) add algorithm type information to algorithm negotiation logs. * [#289](https://github.com/mwiede/jsch/issues/289) wrap NoClassDefFoundError's for invalid private keys. diff --git a/Readme.md b/Readme.md index 2af96bd4..94dfc8d0 100644 --- a/Readme.md +++ b/Readme.md @@ -33,7 +33,7 @@ with com.github.mwiede jsch - 0.2.7 + 0.2.19 ``` @@ -43,7 +43,7 @@ When you have an artifact `foo:bar`, which contains `com.jcraft:jsch` as a trans com.github.mwiede jsch - 0.2.7 + 0.2.19 foo @@ -60,8 +60,10 @@ When you have an artifact `foo:bar`, which contains `com.jcraft:jsch` as a trans *Addition*: You can further exclude any of `com.jcraft:jsch.agentproxy.jsch`, `com.jcraft:jsch.agentproxy.core` or `com.jcraft:jsch.agentproxy.pageant`, because these modules where integrated in this fork (see release notes of [0.1.66](https://github.com/mwiede/jsch/releases/tag/jsch-0.1.66)). ## FAQ +### Which is the minimum Java version required? + * Java 8. For more limitations, see next answer. ### Are ssh-ed25519, ssh-ed448, curve25519-sha256, curve448-sha512 & chacha20-poly1305@openssh.com supported? - * This library is a Multi-Release-jar, which means that you can only use certain features when a more recent Java version is used. + * This library is a [Multi-Release-jar](https://openjdk.org/jeps/238), which means that you can only use certain features when a more recent Java version is used. * In order to use ssh-ed25519 & ssh-ed448, you must use at least Java 15 or add [Bouncy Castle](https://www.bouncycastle.org/java.html) (bcprov-jdk18on) to the classpath. * In order to use curve25519-sha256 & curve448-sha512, you must use at least Java 11 or add [Bouncy Castle](https://www.bouncycastle.org/java.html) (bcprov-jdk18on) to the classpath. * In order to use chacha20-poly1305@openssh.com, you must add [Bouncy Castle](https://www.bouncycastle.org/java.html) (bcprov-jdk18on) to the classpath. diff --git a/examples/AES.java b/examples/AES.java index 69e43b87..7a918fc9 100644 --- a/examples/AES.java +++ b/examples/AES.java @@ -1,10 +1,18 @@ -/** - * This program will demonstrate how to use "aes128-cbc". - * - */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +/** This program will demonstrate how to use "aes128-cbc". */ +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class AES { public static void main(String[] arg) { @@ -142,5 +150,3 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - - diff --git a/examples/ChangePassphrase.java b/examples/ChangePassphrase.java index f720b3b5..c1dd39ac 100644 --- a/examples/ChangePassphrase.java +++ b/examples/ChangePassphrase.java @@ -3,10 +3,12 @@ * a new private key. A passphrase will be prompted if the given private-key has been encrypted. * After successfully loading the content of the private-key, the new passphrase will be prompted * and the given private-key will be re-encrypted with that new passphrase. - * */ -import com.jcraft.jsch.*; -import javax.swing.*; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.KeyPair; +import javax.swing.JOptionPane; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class ChangePassphrase { public static void main(String[] arg) { diff --git a/examples/Compression.java b/examples/Compression.java index d11a6853..81b1814b 100644 --- a/examples/Compression.java +++ b/examples/Compression.java @@ -2,11 +2,21 @@ * This program will demonstrate the packet compression. You will be asked username, hostname and * passwd. If everything works fine, you will get the shell prompt. In this program, all data from * sshd server to jsch will be compressed. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class Compression { public static void main(String[] arg) { @@ -141,5 +151,3 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - - diff --git a/examples/Daemon.java b/examples/Daemon.java index 49de1f6a..58f4c996 100644 --- a/examples/Daemon.java +++ b/examples/Daemon.java @@ -1,12 +1,26 @@ /** * This program will demonstrate how to provide a network service like inetd by using remote * port-forwarding functionality. - * */ -import com.jcraft.jsch.*; -import java.io.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.ChannelForwardedTCPIP; +import com.jcraft.jsch.ForwardedTCPIPDaemon; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class Daemon { public static void main(String[] arg) { diff --git a/examples/Exec.java b/examples/Exec.java index c2c0401b..dc8348de 100644 --- a/examples/Exec.java +++ b/examples/Exec.java @@ -2,12 +2,23 @@ * This program will demonstrate remote exec. You will be asked username, hostname, displayname, * passwd and command. If everything works fine, given command will be invoked on the remote side * and outputs will be printed out. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; -import java.io.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.InputStream; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class Exec { public static void main(String[] arg) { diff --git a/examples/JSchWithAgentProxy.java b/examples/JSchWithAgentProxy.java index deb4cc8a..34d5ebef 100644 --- a/examples/JSchWithAgentProxy.java +++ b/examples/JSchWithAgentProxy.java @@ -1,6 +1,13 @@ -import com.jcraft.jsch.*; -import java.io.*; -import javax.swing.*; +import com.jcraft.jsch.AgentIdentityRepository; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.IdentityRepository; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.SSHAgentConnector; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import javax.swing.JOptionPane; public class JSchWithAgentProxy { public static void main(String[] arg) { diff --git a/examples/JumpHosts.java b/examples/JumpHosts.java index bdfcb907..e2bd0aaf 100644 --- a/examples/JumpHosts.java +++ b/examples/JumpHosts.java @@ -3,11 +3,21 @@ * to host2 and host3. java JumpHosts usr1@host1 usr2@host2 usr3@host3. You will be asked passwords * for those destinations. If everything works fine, you will get file lists of your home-directory * at host3. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class JumpHosts { public static void main(String[] arg) { diff --git a/examples/KeyGen.java b/examples/KeyGen.java index bb91e4f4..3a2cde3d 100644 --- a/examples/KeyGen.java +++ b/examples/KeyGen.java @@ -2,10 +2,12 @@ * This progam will demonstrate the keypair generation. You will be asked a passphrase for * output_keyfile. If everything works fine, you will get the keypair, output_keyfile and * output_keyfile+".pub". The private key and public key are in the OpenSSH format. - * */ -import com.jcraft.jsch.*; -import javax.swing.*; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.KeyPair; +import javax.swing.JOptionPane; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class KeyGen { public static void main(String[] arg) { diff --git a/examples/KnownHosts.java b/examples/KnownHosts.java index f3eeea81..7de50ce8 100644 --- a/examples/KnownHosts.java +++ b/examples/KnownHosts.java @@ -3,11 +3,24 @@ * hostname, a path for 'known_hosts' and passwd. If everything works fine, you will get the shell * prompt. In current implementation, jsch only reads 'known_hosts' for checking and does not modify * it. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.HostKeyRepository; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class KnownHosts { public static void main(String[] arg) { @@ -159,5 +172,3 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - - diff --git a/examples/Logger.java b/examples/Logger.java index 4f76617b..5c391004 100644 --- a/examples/Logger.java +++ b/examples/Logger.java @@ -1,10 +1,18 @@ -/** - * This program will demonstrate how to enable logging mechanism and get logging messages. - * - */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +/** This program will demonstrate how to enable logging mechanism and get logging messages. */ +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class Logger { public static void main(String[] arg) { @@ -44,6 +52,7 @@ public static void main(String[] arg) { public static class MyLogger implements com.jcraft.jsch.Logger { static java.util.Map name = new java.util.HashMap<>(); + static { name.put(DEBUG, "DEBUG: "); name.put(INFO, "INFO: "); @@ -158,5 +167,3 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - - diff --git a/examples/OpenSSHConfig.java b/examples/OpenSSHConfig.java index f7dd22de..e5c7db45 100644 --- a/examples/OpenSSHConfig.java +++ b/examples/OpenSSHConfig.java @@ -2,11 +2,14 @@ * This program demonsrates how to use OpenSSHConfig class. You will be asked username, hostname and * passwd. If everything works fine, you will get the shell prompt. Output may be ugly because of * lacks of terminal-emulation, but you can issue commands. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ConfigRepository; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import javax.swing.JOptionPane; public class OpenSSHConfig { public static void main(String[] arg) { @@ -96,7 +99,7 @@ public boolean promptYesNo(String message) { } } - public static abstract class MyUserInfo implements UserInfo, UIKeyboardInteractive { + public abstract static class MyUserInfo implements UserInfo, UIKeyboardInteractive { @Override public String getPassword() { return null; diff --git a/examples/PortForwardingL.java b/examples/PortForwardingL.java index 1cd7a114..c217bd99 100644 --- a/examples/PortForwardingL.java +++ b/examples/PortForwardingL.java @@ -3,11 +3,20 @@ * on the local host will be forwarded to the given remote host and port on the remote side. You * will be asked username, hostname, port:host:hostport and passwd. If everything works fine, you * will get the shell prompt. Try the port on localhost. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class PortForwardingL { public static void main(String[] arg) { diff --git a/examples/PortForwardingR.java b/examples/PortForwardingR.java index cb74ba18..7eba1e6c 100644 --- a/examples/PortForwardingR.java +++ b/examples/PortForwardingR.java @@ -3,11 +3,20 @@ * on the remote host will be forwarded to the given host and port on the local side. You will be * asked username, hostname, port:host:hostport and passwd. If everything works fine, you will get * the shell prompt. Try the port on remote host. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class PortForwardingR { public static void main(String[] arg) { diff --git a/examples/ScpFrom.java b/examples/ScpFrom.java index 756b8e55..611ecc5a 100644 --- a/examples/ScpFrom.java +++ b/examples/ScpFrom.java @@ -1,12 +1,27 @@ /** * This program will demonstrate the file transfer from remote to local. If everything works fine, a * file 'file1' on 'remotehost' will copied to local 'file1'. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; -import java.io.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class ScpFrom { public static void main(String[] arg) { diff --git a/examples/ScpTo.java b/examples/ScpTo.java index 46140172..79d09a44 100644 --- a/examples/ScpTo.java +++ b/examples/ScpTo.java @@ -1,12 +1,27 @@ /** * This program will demonstrate the file transfer from local to remote. You will be asked passwd. * If everything works fine, a local file 'file1' will copied to 'file2' on 'remotehost'. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; -import java.io.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class ScpTo { public static void main(String[] arg) { diff --git a/examples/ScpToNoneCipher.java b/examples/ScpToNoneCipher.java index f1752ca5..4a102b0b 100644 --- a/examples/ScpToNoneCipher.java +++ b/examples/ScpToNoneCipher.java @@ -1,12 +1,27 @@ /** * This program will demonstrate how to enable none cipher. You will be asked passwd. If everything * works fine, a local file 'file1' will copied to 'file2' on 'remotehost'. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; -import java.io.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class ScpToNoneCipher { public static void main(String[] arg) { diff --git a/examples/Sftp.java b/examples/Sftp.java index 1b79b413..8084bcdd 100644 --- a/examples/Sftp.java +++ b/examples/Sftp.java @@ -2,11 +2,27 @@ * This program will demonstrate the sftp protocol support. If everything works fine, you will get a * prompt 'sftp>'. 'help' command will show available command. In current implementation, the * destination path for 'get' and 'put' commands must be a file, not a directory. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpATTRS; +import com.jcraft.jsch.SftpException; +import com.jcraft.jsch.SftpProgressMonitor; +import com.jcraft.jsch.SftpStatVFS; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.ProgressMonitor; public class Sftp { public static void main(String[] arg) { diff --git a/examples/Shell.java b/examples/Shell.java index 9db4fe95..6ba9f730 100644 --- a/examples/Shell.java +++ b/examples/Shell.java @@ -2,11 +2,13 @@ * This program enables you to connect to sshd server and get the shell prompt. You will be asked * username, hostname and passwd. If everything works fine, you will get the shell prompt. Output * may be ugly because of lacks of terminal-emulation, but you can issue commands. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import javax.swing.JOptionPane; public class Shell { public static void main(String[] arg) { @@ -91,7 +93,7 @@ public boolean promptYesNo(String message) { } } - public static abstract class MyUserInfo implements UserInfo, UIKeyboardInteractive { + public abstract static class MyUserInfo implements UserInfo, UIKeyboardInteractive { @Override public String getPassword() { return null; diff --git a/examples/SocketForwardingL.java b/examples/SocketForwardingL.java index 51b78d38..a1071a47 100644 --- a/examples/SocketForwardingL.java +++ b/examples/SocketForwardingL.java @@ -2,13 +2,14 @@ * This program enables you to connect to sshd server and forward the docker socket. You will be * asked username, hostname and passwd. If everything works fine, you will get the response code to * the _ping endpoint of the dockerd. - * */ -import com.jcraft.jsch.*; - -import javax.swing.*; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; import java.net.HttpURLConnection; import java.net.URL; +import javax.swing.JOptionPane; public class SocketForwardingL { public static void main(String[] arg) { @@ -75,13 +76,12 @@ public boolean promptYesNo(String message) { HttpURLConnection myURLConnection2 = (HttpURLConnection) myURL.openConnection(); System.out.println("Docker Ping http response code: " + myURLConnection2.getResponseCode()); - } catch (Exception e) { System.out.println(e); } } - public static abstract class MyUserInfo implements UserInfo, UIKeyboardInteractive { + public abstract static class MyUserInfo implements UserInfo, UIKeyboardInteractive { @Override public String getPassword() { return null; diff --git a/examples/StreamForwarding.java b/examples/StreamForwarding.java index 47a360bd..7fdd4842 100644 --- a/examples/StreamForwarding.java +++ b/examples/StreamForwarding.java @@ -4,11 +4,21 @@ * command, but you don't have to assign and open a local tcp port. You will be asked username, * hostname, host:hostport and passwd. If everything works fine, System.in and System.out streams * will be forwared to remote port and you can send messages from command line. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class StreamForwarding { public static void main(String[] arg) { @@ -144,5 +154,3 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - - diff --git a/examples/Subsystem.java b/examples/Subsystem.java index fa4065f7..2f3e85e9 100644 --- a/examples/Subsystem.java +++ b/examples/Subsystem.java @@ -1,10 +1,19 @@ -/** - * This program will demonstrate how to use the Subsystem channel. - * - */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +/** This program will demonstrate how to use the Subsystem channel. */ +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSubsystem; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class Subsystem { public static void main(String[] arg) { diff --git a/examples/Sudo.java b/examples/Sudo.java index e0b8ec1b..cb3da7c8 100644 --- a/examples/Sudo.java +++ b/examples/Sudo.java @@ -1,11 +1,21 @@ -/** - * This program will demonstrate how to exec 'sudo' on the remote. - * - */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; -import java.io.*; +/** This program will demonstrate how to exec 'sudo' on the remote. */ +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.InputStream; +import java.io.OutputStream; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class Sudo { public static void main(String[] arg) { @@ -52,7 +62,6 @@ public static void main(String[] arg) { // password prompt and use a custom one. ((ChannelExec) channel).setCommand("sudo -S -p '' " + command); - InputStream in = channel.getInputStream(); OutputStream out = channel.getOutputStream(); ((ChannelExec) channel).setErrStream(System.err); diff --git a/examples/UserAuthKI.java b/examples/UserAuthKI.java index 67cfdd06..81699dbf 100644 --- a/examples/UserAuthKI.java +++ b/examples/UserAuthKI.java @@ -1,11 +1,21 @@ /** * This program will demonstrate the keyboard-interactive authentication. If the remote sshd * supports keyboard-interactive authentication, you will be prompted. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class UserAuthKI { public static void main(String[] arg) { diff --git a/examples/UserAuthPubKey.java b/examples/UserAuthPubKey.java index 6ed96078..adc6a3c0 100644 --- a/examples/UserAuthPubKey.java +++ b/examples/UserAuthPubKey.java @@ -2,11 +2,22 @@ * This program will demonstrate the user authentification by public key. You will be asked * username, hostname, privatekey(id_dsa) and passphrase. If everything works fine, you will get the * shell prompt. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class UserAuthPubKey { public static void main(String[] arg) { @@ -53,7 +64,6 @@ public static void main(String[] arg) { } } - public static class MyUserInfo implements UserInfo, UIKeyboardInteractive { @Override public String getPassword() { diff --git a/examples/ViaHTTP.java b/examples/ViaHTTP.java index 5d3ea03d..ab4e8759 100644 --- a/examples/ViaHTTP.java +++ b/examples/ViaHTTP.java @@ -1,11 +1,22 @@ /** * This program will demonstrate the ssh session via HTTP proxy. You will be asked username, * hostname, proxy-server and passwd. If everything works fine, you will get the shell prompt. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.ProxyHTTP; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class ViaHTTP { public static void main(String[] arg) { @@ -144,7 +155,4 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - } - - diff --git a/examples/ViaSOCKS5.java b/examples/ViaSOCKS5.java index 8ee9fdec..d232c97b 100644 --- a/examples/ViaSOCKS5.java +++ b/examples/ViaSOCKS5.java @@ -1,11 +1,22 @@ /** * This program will demonstrate the ssh session via SOCKS proxy. You will be asked username, * hostname, proxy-server and passwd. If everything works fine, you will get the shell prompt. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.ProxySOCKS5; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class ViaSOCKS5 { public static void main(String[] arg) { @@ -145,5 +156,3 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - - diff --git a/examples/X11Forwarding.java b/examples/X11Forwarding.java index 95d9fa1c..5a819fe5 100644 --- a/examples/X11Forwarding.java +++ b/examples/X11Forwarding.java @@ -2,11 +2,21 @@ * This program will demonstrate X11 forwarding. You will be asked username, hostname, displayname * and passwd. If your X server does not run at 127.0.0.1, please enter correct displayname. If * everything works fine, you will get the shell prompt. Try X applications; for example, xlogo. - * */ -import com.jcraft.jsch.*; -import java.awt.*; -import javax.swing.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; public class X11Forwarding { public static void main(String[] arg) { @@ -149,5 +159,3 @@ public String[] promptKeyboardInteractive(String destination, String name, Strin } } } - - diff --git a/mvnw b/mvnw index 633bbb74..19529ddf 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.2.0 +# Apache Maven Wrapper startup batch script, version 3.3.2 # # Optional ENV vars # ----------------- @@ -35,15 +35,17 @@ set -euf # OS specific support. native_path() { printf %s\\n "$1"; } case "$(uname)" in -(CYGWIN*|MINGW*) [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" - native_path() { cygpath --path --windows "$1"; } ;; +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; esac # set JAVACMD and JAVACCMD set_java_home() { # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched - if [ -n "${JAVA_HOME-}" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" JAVACCMD="$JAVA_HOME/jre/sh/javac" @@ -51,17 +53,25 @@ set_java_home() { JAVACMD="$JAVA_HOME/bin/java" JAVACCMD="$JAVA_HOME/bin/javac" - if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ] ; then + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 return 1 fi fi else - JAVACMD="$('set' +e; 'unset' -f command 2>/dev/null; 'command' -v java)" || : - JAVACCMD="$('set' +e; 'unset' -f command 2>/dev/null; 'command' -v javac)" || : + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : - if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ] ; then + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 return 1 fi @@ -72,7 +82,8 @@ set_java_home() { hash_string() { str="${1:-}" h=0 while [ -n "$str" ]; do - h=$(( ( h * 31 + $(LC_CTYPE=C printf %d "'$str") ) % 4294967296 )) + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) str="${str#?}" done printf %x\\n $h @@ -86,32 +97,40 @@ die() { exit 1 } +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties while IFS="=" read -r key value; do case "${key-}" in - distributionUrl) distributionUrl="${value-}" ;; - distributionSha256Sum) distributionSha256Sum="${value-}" ;; + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; esac -done < "${0%/*}/.mvn/wrapper/maven-wrapper.properties" +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" - case "${distributionUrl##*/}" in -(maven-mvnd-*bin.*) +maven-mvnd-*bin.*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in - (*AMD64:CYGWIN*|*AMD64:MINGW*) distributionPlatform=windows-amd64 ;; - (:Darwin*x86_64) distributionPlatform=darwin-amd64 ;; - (:Darwin*arm64) distributionPlatform=darwin-aarch64 ;; - (:Linux*x86_64*) distributionPlatform=linux-amd64 ;; - (*) echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 - distributionPlatform=linux-amd64 - ;; + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; esac distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" ;; -(maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; -(*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; esac # apply MVNW_REPOURL and calculate MAVEN_HOME @@ -120,7 +139,8 @@ esac distributionUrlName="${distributionUrl##*/}" distributionUrlNameMain="${distributionUrlName%.*}" distributionUrlNameMain="${distributionUrlNameMain%-bin}" -MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" exec_maven() { unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : @@ -133,8 +153,8 @@ if [ -d "$MAVEN_HOME" ]; then fi case "${distributionUrl-}" in -(*?-bin.zip|*?maven-mvnd-?*-?*.zip) ;; -(*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; esac # prepare tmp dir @@ -168,17 +188,17 @@ case "${MVNW_PASSWORD:+has-password}" in has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; esac -if [ -z "${MVNW_USERNAME-}" ] && command -v wget > /dev/null; then +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then verbose "Found wget ... using wget" - wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" -elif [ -z "${MVNW_USERNAME-}" ] && command -v curl > /dev/null; then + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then verbose "Found curl ... using curl" - curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" elif set_java_home; then verbose "Falling back to use Java to download" javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" - cat > "$javaSource" <<-END + cat >"$javaSource" <<-END public class Downloader extends java.net.Authenticator { protected java.net.PasswordAuthentication getPasswordAuthentication() @@ -188,13 +208,13 @@ elif set_java_home; then public static void main( String[] args ) throws Exception { setDefault( new Downloader() ); - java.nio.file.Files.copy( new java.net.URL( args[0] ).openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); } } END # For Cygwin/MinGW, switch paths to Windows format before running javac and java verbose " - Compiling Downloader.java ..." - "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" verbose " - Running Downloader.java ..." "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" fi @@ -206,12 +226,12 @@ if [ -n "${distributionSha256Sum-}" ]; then echo "Checksum validation is not supported for maven-mvnd." >&2 echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 exit 1 - elif command -v sha256sum > /dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c > /dev/null 2>&1; then + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then distributionSha256Result=true fi - elif command -v shasum > /dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c > /dev/null 2>&1; then + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then distributionSha256Result=true fi else @@ -227,12 +247,12 @@ if [ -n "${distributionSha256Sum-}" ]; then fi # unzip and move -if command -v unzip > /dev/null; then - unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" else - tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" fi -printf %s\\n "$distributionUrl" > "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" clean || : diff --git a/mvnw.cmd b/mvnw.cmd index dd02e16f..b150b91e 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -19,7 +19,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM Apache Maven Wrapper startup batch script, version 3.3.2 @REM @REM Optional ENV vars @REM MVNW_REPOURL - repo url base for downloading maven distribution @@ -79,6 +79,9 @@ if ($env:MVNW_REPOURL) { $distributionUrlName = $distributionUrl -replace '^.*/','' $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" @@ -123,6 +126,7 @@ if ($distributionSha256Sum) { if ($USE_MVND) { Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." } diff --git a/pom.xml b/pom.xml index 6b36e786..7f075059 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.jcraft jsch jar - 0.2.8 + 0.2.20-SNAPSHOT JSch https://github.com/mwiede/jsch JSch is a pure Java implementation of SSH2 @@ -47,12 +47,14 @@ +9 + mwiede Matthias Wiedemann mwiede@gmx.de Community https://github.com/mwiede + norrisjeremy Jeremy Norris Community https://github.com/norrisjeremy @@ -73,20 +75,21 @@ - 2023-03-21T03:04:14Z + 2024-08-06T09:07:30Z UTF-8 UTF-8 true - 2.6.2 - 5.13.0 - 2.20.0 - 3.0.0 + 2.10.0 + 5.14.0 + 2.23.1 + 2.29.2 + 3.4.0 org.bouncycastle bcprov-jdk18on - 1.72 + 1.78.1 true @@ -94,6 +97,13 @@ junixsocket-common ${junixsocket.version} + + com.kohlschutter + compiler-annotations + 1.7.4 + provided + true + net.java.dev.jna jna-jpms @@ -121,19 +131,19 @@ org.slf4j slf4j-api - 2.0.7 + 2.0.16 true org.junit.jupiter junit-jupiter - 5.9.2 + 5.11.0 test org.testcontainers junit-jupiter - 1.17.6 + 1.20.1 test @@ -150,7 +160,13 @@ commons-codec commons-codec - 1.15 + 1.17.1 + test + + + commons-io + commons-io + 2.16.1 test @@ -158,12 +174,6 @@ junixsocket-native-common ${junixsocket.version} - - com.google.guava - guava - 31.1-jre - test - org.apache.logging.log4j log4j-core-test @@ -216,16 +226,25 @@ com.github.valfirst slf4j-test - 2.8.1 + 3.0.1 test + + + + com.google.errorprone + error_prone_annotations + ${errorprone.version} + + + org.apache.maven.plugins maven-enforcer-plugin - 3.2.1 + 3.5.0 enforce-java @@ -235,7 +254,7 @@ - 16 + 17 @@ -249,7 +268,7 @@ org.codehaus.mojo flatten-maven-plugin - 1.4.0 + 1.6.0 ossrh all @@ -274,7 +293,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.3.0 + 3.6.0 @@ -293,7 +312,7 @@ org.codehaus.mojo templating-maven-plugin - 1.0.0 + 3.0.0 @@ -305,14 +324,14 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.13.0 8 true true + true - -Xlint:all,-processing,-classfile - -Werror + -Xlint:all,-processing,-classfile,-options @@ -384,40 +403,33 @@ - org.apache.felix - maven-bundle-plugin - 5.1.8 + biz.aQute.bnd + bnd-maven-plugin + 7.0.0 - - com.jcraft.jsch;-noimport:=true - - <_nouses>true - - com.sun.jna, com.sun.jna.platform - + - - - bundle-manifest - process-classes - - manifest - - - org.apache.maven.plugins @@ -446,12 +458,12 @@ org.apache.maven.plugins maven-clean-plugin - 3.2.0 + 3.4.0 org.apache.maven.plugins maven-resources-plugin - 3.3.0 + 3.3.1 copy-licenses @@ -477,16 +489,13 @@ org.apache.maven.plugins maven-jar-plugin - 3.3.0 + 3.4.2 true true - - true - ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -494,7 +503,7 @@ org.apache.maven.plugins maven-assembly-plugin - 3.5.0 + 3.7.1 attach-sources @@ -513,13 +522,13 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 + 3.8.0 true none com.jcraft.jsch com.jcraft.jsch.* - ${project.build.sourceDirectory}:${project.build.directory}/generated-sources/java-templates:${project.basedir}/src/main/java9:${project.basedir}/src/main/java11:${project.basedir}/src/main/java15:${project.basedir}/src/main/java16 + ${project.build.sourceDirectory}:${project.build.directory}/generated-sources/java-templates:${project.basedir}/src/main/java9:${project.basedir}/src/main/java10:${project.basedir}/src/main/java11:${project.basedir}/src/main/java15:${project.basedir}/src/main/java16 @@ -533,7 +542,11 @@ org.cyclonedx cyclonedx-maven-plugin - 2.7.5 + 2.8.1 + + false + false + @@ -545,7 +558,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0 + 3.1.1 false release @@ -554,33 +567,32 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.2.5 org.apache.maven.plugins maven-deploy-plugin - 3.1.0 + 3.1.3 org.apache.maven.plugins maven-install-plugin - 3.1.0 + 3.1.3 org.apache.maven.plugins maven-site-plugin - 3.12.1 + 3.20.0 org.apache.maven.plugins maven-dependency-plugin - 3.5.0 + 3.7.1 - + --> org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.12 com/jcraft/jsch/JavaVersion.class + com/jcraft/jsch/JplLogger.class com/jcraft/jsch/UnixDomainSocketFactory.class + com/jcraft/jsch/jce/KeyPairGenEdDSA.class + com/jcraft/jsch/jce/SignatureEd25519.class + com/jcraft/jsch/jce/SignatureEd448.class + com/jcraft/jsch/jce/XDH.class META-INF/versions/9/com/jcraft/jsch/JavaVersion.class META-INF/versions/10/com/jcraft/jsch/JavaVersion.class + + de.thetaphi + forbiddenapis + 3.7 + + + jdk-unsafe + jdk-deprecated + jdk-non-portable + jdk-reflection + + + com.jcraft.jsch.annotations.SuppressForbiddenApi + + + org.apache.maven.plugins maven-shade-plugin @@ -749,7 +781,7 @@ com.google.errorprone error_prone_core - 2.18.0 + ${errorprone.version} @@ -757,8 +789,7 @@ - + + forbiddenapis + + [16,) + + + + + de.thetaphi + forbiddenapis + + 16 + + + + check + + check + + + + jdk-system-out + + + + + testCheck + + testCheck + + + + commons-io-unsafe-2.14.0 + + + + + + + + + + bnd + + [17,) + + + + + biz.aQute.bnd + bnd-maven-plugin + + + bnd-process + process-classes + + bnd-process + + + + + + ---> diff --git a/src/main/java/com/jcraft/jsch/AgentIdentity.java b/src/main/java/com/jcraft/jsch/AgentIdentity.java index 2fe5a2bc..3f83e4fb 100644 --- a/src/main/java/com/jcraft/jsch/AgentIdentity.java +++ b/src/main/java/com/jcraft/jsch/AgentIdentity.java @@ -60,12 +60,6 @@ public byte[] getSignature(byte[] data, String alg) { return agent.sign(blob, data, alg); } - @Override - @Deprecated - public boolean decrypt() { - throw new RuntimeException("not implemented"); - } - @Override public String getAlgName() { return algname; diff --git a/src/main/java/com/jcraft/jsch/Buffer.java b/src/main/java/com/jcraft/jsch/Buffer.java index e19b8000..e57fef60 100644 --- a/src/main/java/com/jcraft/jsch/Buffer.java +++ b/src/main/java/com/jcraft/jsch/Buffer.java @@ -290,7 +290,6 @@ static Buffer fromBytes(byte[][] args) { return buf; } - /* * static String[] chars={ "0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f" }; * static void dump_buffer(){ int foo; for(int i=0; i * https://raw.githubusercontent.com/openssh/openssh-portable/master/PROTOCOL */ public class ChannelDirectStreamLocal extends ChannelDirectTCPIP { - static private final int LOCAL_WINDOW_SIZE_MAX = 0x20000; - static private final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; - static private final byte[] _type = Util.str2byte("direct-streamlocal@openssh.com"); + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + private static final byte[] _type = Util.str2byte("direct-streamlocal@openssh.com"); private String socketPath; ChannelDirectStreamLocal() { super(); type = _type; - setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); - setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); - setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; } @Override diff --git a/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java b/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java index 9465c7ae..c1522efb 100644 --- a/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java +++ b/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java @@ -26,13 +26,14 @@ package com.jcraft.jsch; -import java.io.*; +import java.io.InputStream; +import java.io.OutputStream; public class ChannelDirectTCPIP extends Channel { - static private final int LOCAL_WINDOW_SIZE_MAX = 0x20000; - static private final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; - static private final byte[] _type = Util.str2byte("direct-tcpip"); + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + private static final byte[] _type = Util.str2byte("direct-tcpip"); String host; int port; @@ -42,9 +43,9 @@ public class ChannelDirectTCPIP extends Channel { ChannelDirectTCPIP() { super(); type = _type; - setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); - setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); - setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; } @Override diff --git a/src/main/java/com/jcraft/jsch/ChannelExec.java b/src/main/java/com/jcraft/jsch/ChannelExec.java index 4f31d480..56143bbb 100644 --- a/src/main/java/com/jcraft/jsch/ChannelExec.java +++ b/src/main/java/com/jcraft/jsch/ChannelExec.java @@ -26,8 +26,9 @@ package com.jcraft.jsch; -import java.io.*; -import java.util.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; public class ChannelExec extends ChannelSession { diff --git a/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java b/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java index 5ec0805d..15de3829 100644 --- a/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java +++ b/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java @@ -26,19 +26,19 @@ package com.jcraft.jsch; -import java.net.*; -import java.io.*; +import java.io.PipedOutputStream; +import java.net.Socket; import java.util.Vector; public class ChannelForwardedTCPIP extends Channel { private static Vector pool = new Vector<>(); - static private final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; // static private final int LOCAL_WINDOW_SIZE_MAX=0x100000; - static private final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; - static private final int TIMEOUT = 10 * 1000; + private static final int TIMEOUT = 10 * 1000; private Socket socket = null; private ForwardedTCPIPDaemon daemon = null; @@ -46,9 +46,9 @@ public class ChannelForwardedTCPIP extends Channel { ChannelForwardedTCPIP() { super(); - setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); - setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); - setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; io = new IO(); connected = true; } @@ -278,7 +278,7 @@ static void delPort(Session session, String address_to_bind, int rport) { // string address_to_bind (e.g. "127.0.0.1") // uint32 port number to bind packet.reset(); - buf.putByte((byte) 80/* SSH_MSG_GLOBAL_REQUEST */); + buf.putByte((byte) 80 /* SSH_MSG_GLOBAL_REQUEST */); buf.putString(Util.str2byte("cancel-tcpip-forward")); buf.putByte((byte) 0); buf.putString(Util.str2byte(address_to_bind)); @@ -315,7 +315,7 @@ private void setSocketFactory(SocketFactory factory) { ((ConfigLHost) config).factory = factory; } - static abstract class Config { + abstract static class Config { Session session; int rport; int allocated_rport; diff --git a/src/main/java/com/jcraft/jsch/ChannelSession.java b/src/main/java/com/jcraft/jsch/ChannelSession.java index 0827f04c..dbb8be5d 100644 --- a/src/main/java/com/jcraft/jsch/ChannelSession.java +++ b/src/main/java/com/jcraft/jsch/ChannelSession.java @@ -26,7 +26,8 @@ package com.jcraft.jsch; -import java.util.*; +import java.util.Enumeration; +import java.util.Hashtable; class ChannelSession extends Channel { private static byte[] _session = Util.str2byte("session"); diff --git a/src/main/java/com/jcraft/jsch/ChannelSftp.java b/src/main/java/com/jcraft/jsch/ChannelSftp.java index 4ede6f4c..6c7b6e3b 100644 --- a/src/main/java/com/jcraft/jsch/ChannelSftp.java +++ b/src/main/java/com/jcraft/jsch/ChannelSftp.java @@ -26,7 +26,14 @@ package com.jcraft.jsch; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Hashtable; @@ -34,8 +41,8 @@ public class ChannelSftp extends ChannelSession { - static private final int LOCAL_MAXIMUM_PACKET_SIZE = 32 * 1024; - static private final int LOCAL_WINDOW_SIZE_MAX = (64 * LOCAL_MAXIMUM_PACKET_SIZE); + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 32 * 1024; + private static final int LOCAL_WINDOW_SIZE_MAX = (64 * LOCAL_MAXIMUM_PACKET_SIZE); private static final byte SSH_FXP_INIT = 1; private static final byte SSH_FXP_VERSION = 2; @@ -157,6 +164,8 @@ public class ChannelSftp extends ChannelSession { private Charset fEncoding = StandardCharsets.UTF_8; private boolean fEncoding_is_utf8 = true; + private boolean useWriteFlushWorkaround = true; + private RequestQueue rq = new RequestQueue(16); /** @@ -181,11 +190,19 @@ public int getBulkRequests() { return rq.size(); } + public void setUseWriteFlushWorkaround(boolean useWriteFlushWorkaround) { + this.useWriteFlushWorkaround = useWriteFlushWorkaround; + } + + public boolean getUseWriteFlushWorkaround() { + return useWriteFlushWorkaround; + } + public ChannelSftp() { super(); - setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); - setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); - setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; } @Override @@ -432,14 +449,8 @@ public void put(String src, String dst, SftpProgressMonitor monitor, int mode) monitor.count(size_of_dst); } } - FileInputStream fis = null; - try { - fis = new FileInputStream(_src); + try (InputStream fis = new FileInputStream(_src)) { _put(fis, _dst, monitor, mode); - } finally { - if (fis != null) { - fis.close(); - } } } } catch (Exception e) { @@ -604,8 +615,10 @@ public void _put(InputStream src, String dst, SftpProgressMonitor monitor, int m int _ackid = ackid[0]; if (startid > _ackid || _ackid > seq - 1) { if (_ackid == seq) { - System.err.println( - "ack error: startid=" + startid + " seq=" + seq + " _ackid=" + _ackid); + if (getSession().getLogger().isEnabled(Logger.ERROR)) { + getSession().getLogger().log(Logger.ERROR, + "ack error: startid=" + startid + " seq=" + seq + " _ackid=" + _ackid); + } } else { throw new SftpException(SSH_FX_FAILURE, "ack error: startid=" + startid + " seq=" + seq + " _ackid=" + _ackid); @@ -758,6 +771,9 @@ public void write(byte[] d, int s, int len) throws IOException { try { int _len = len; while (_len > 0) { + if (useWriteFlushWorkaround && rwsize < 21 + handle.length + _len + 4) { + flush(); + } int sent = sendWRITE(handle, _offset[0], d, s, _len); writecount++; _offset[0] += sent; @@ -929,20 +945,11 @@ public void get(String src, String dst, SftpProgressMonitor monitor, int mode) } } - FileOutputStream fos = null; _dstExist = _dstFile.exists(); - try { - if (mode == OVERWRITE) { - fos = new FileOutputStream(_dst); - } else { - fos = new FileOutputStream(_dst, true); // append - } + try (OutputStream fos = mode == OVERWRITE ? new FileOutputStream(_dst) + : new FileOutputStream(_dst, true) /* append */) { // System.err.println("_get: "+_src+", "+_dst); _get(_src, fos, monitor, mode, new File(_dst).length()); - } finally { - if (fos != null) { - fos.close(); - } } } } catch (Exception e) { @@ -1069,12 +1076,10 @@ private void _get(String src, OutputStream dst, SftpProgressMonitor monitor, int length -= 4; int length_of_data = buf.getInt(); // length of data - /** - * Since sftp protocol version 6, "end-of-file" has been defined, - * - * byte SSH_FXP_DATA uint32 request-id string data bool end-of-file [optional] - * - * but some sftpd server will send such a field in the sftp protocol 3 ;-( + /* + * Since sftp protocol version 6, "end-of-file" has been defined, byte SSH_FXP_DATA uint32 + * request-id string data bool end-of-file [optional] but some sftpd server will send such a + * field in the sftp protocol 3 ;-( */ int optional_data = length - length_of_data; @@ -1103,7 +1108,6 @@ private void _get(String src, OutputStream dst, SftpProgressMonitor monitor, int break loop; } } - } // System.err.println("length: "+length); // length should be 0 @@ -1136,7 +1140,6 @@ private void _get(String src, OutputStream dst, SftpProgressMonitor monitor, int } } - private class RequestQueue { class OutOfOrderException extends Exception { private static final long serialVersionUID = -1L; @@ -1146,6 +1149,7 @@ class OutOfOrderException extends Exception { this.offset = offset; } } + class Request { int id; long offset; @@ -1428,12 +1432,10 @@ public int read(byte[] d, int s, int len) throws IOException { int length_of_data = buf.getInt(); rest_length -= 4; - /** - * Since sftp protocol version 6, "end-of-file" has been defined, - * - * byte SSH_FXP_DATA uint32 request-id string data bool end-of-file [optional] - * - * but some sftpd server will send such a field in the sftp protocol 3 ;-( + /* + * Since sftp protocol version 6, "end-of-file" has been defined, byte SSH_FXP_DATA uint32 + * request-id string data bool end-of-file [optional] but some sftpd server will send such + * a field in the sftp protocol 3 ;-( */ int optional_data = rest_length - length_of_data; @@ -1508,8 +1510,10 @@ public void close() throws IOException { rq.cancel(header, buf); try { _sendCLOSE(handle, header); + } catch (IOException e) { + throw e; } catch (Exception e) { - throw new IOException("error"); + throw new IOException(e.toString(), e); } } }; @@ -1584,7 +1588,6 @@ public void ls(String path, LsEntrySelector selector) throws SftpException { _pattern = Util.unquote(_pattern); pattern = Util.str2byte(_pattern, fEncoding); } - } } @@ -2513,7 +2516,7 @@ private void sendOPENW(byte[] path) throws Exception { } private void sendOPENA(byte[] path) throws Exception { - sendOPEN(path, SSH_FXF_WRITE | /* SSH_FXF_APPEND| */SSH_FXF_CREAT); + sendOPEN(path, SSH_FXF_WRITE | /* SSH_FXF_APPEND | */ SSH_FXF_CREAT); } private void sendOPEN(byte[] path, int mode) throws Exception { @@ -3025,7 +3028,6 @@ public interface LsEntrySelector { public final int BREAK = 1; /** - *

* The select method will be invoked in ls method for each file entry. * If this method returns BREAK, ls will be canceled. * diff --git a/src/main/java/com/jcraft/jsch/ChannelShell.java b/src/main/java/com/jcraft/jsch/ChannelShell.java index 2dc582f0..7fc3d3d3 100644 --- a/src/main/java/com/jcraft/jsch/ChannelShell.java +++ b/src/main/java/com/jcraft/jsch/ChannelShell.java @@ -26,8 +26,6 @@ package com.jcraft.jsch; -import java.util.*; - public class ChannelShell extends ChannelSession { ChannelShell() { diff --git a/src/main/java/com/jcraft/jsch/ChannelSubsystem.java b/src/main/java/com/jcraft/jsch/ChannelSubsystem.java index 8c8ea5af..537ae391 100644 --- a/src/main/java/com/jcraft/jsch/ChannelSubsystem.java +++ b/src/main/java/com/jcraft/jsch/ChannelSubsystem.java @@ -26,7 +26,9 @@ package com.jcraft.jsch; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; public class ChannelSubsystem extends ChannelSession { boolean want_reply = true; diff --git a/src/main/java/com/jcraft/jsch/ChannelX11.java b/src/main/java/com/jcraft/jsch/ChannelX11.java index cbc442e9..92e29d26 100644 --- a/src/main/java/com/jcraft/jsch/ChannelX11.java +++ b/src/main/java/com/jcraft/jsch/ChannelX11.java @@ -27,15 +27,15 @@ package com.jcraft.jsch; import java.io.IOException; -import java.net.*; +import java.net.Socket; import java.util.Hashtable; class ChannelX11 extends Channel { - static private final int LOCAL_WINDOW_SIZE_MAX = 0x20000; - static private final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; - static private final int TIMEOUT = 10 * 1000; + private static final int TIMEOUT = 10 * 1000; private static String host = "127.0.0.1"; private static int port = 6000; @@ -114,9 +114,9 @@ static void removeFakedCookie(Session session) { ChannelX11() { super(); - setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); - setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); - setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; type = Util.str2byte("x11"); diff --git a/src/main/java/com/jcraft/jsch/Compression.java b/src/main/java/com/jcraft/jsch/Compression.java index a961efd6..0c084409 100644 --- a/src/main/java/com/jcraft/jsch/Compression.java +++ b/src/main/java/com/jcraft/jsch/Compression.java @@ -27,8 +27,8 @@ package com.jcraft.jsch; public interface Compression { - static public final int INFLATER = 0; - static public final int DEFLATER = 1; + public static final int INFLATER = 0; + public static final int DEFLATER = 1; default void init(int type, int level, Session session) { init(type, level); diff --git a/src/main/java/com/jcraft/jsch/DH25519SNTRUP761.java b/src/main/java/com/jcraft/jsch/DH25519SNTRUP761.java new file mode 100644 index 00000000..1c6732ce --- /dev/null +++ b/src/main/java/com/jcraft/jsch/DH25519SNTRUP761.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DH25519SNTRUP761 extends DHXECKEM { + public DH25519SNTRUP761() { + kem_name = "sntrup761"; + sha_name = "sha-512"; + curve_name = "X25519"; + kem_pubkey_len = 1158; + kem_encap_len = 1039; + xec_key_len = 32; + } +} diff --git a/src/main/java/com/jcraft/jsch/DHECN.java b/src/main/java/com/jcraft/jsch/DHECN.java index e0455247..a20abf06 100644 --- a/src/main/java/com/jcraft/jsch/DHECN.java +++ b/src/main/java/com/jcraft/jsch/DHECN.java @@ -62,7 +62,7 @@ public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C sha = c.getDeclaredConstructor().newInstance(); sha.init(); } catch (Exception e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } buf = new Buffer(); @@ -111,7 +111,9 @@ public boolean next(Buffer _buf) throws Exception { j = _buf.getByte(); j = _buf.getByte(); if (j != SSH_MSG_KEX_ECDH_REPLY) { - System.err.println("type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + } return false; } @@ -131,8 +133,7 @@ public boolean next(Buffer _buf) throws Exception { return false; } - K = ecdh.getSecret(r_s[0], r_s[1]); - K = normalize(K); + K = encodeAsMPInt(normalize(ecdh.getSecret(r_s[0], r_s[1]))); byte[] sig_of_H = _buf.getString(); @@ -157,11 +158,11 @@ public boolean next(Buffer _buf) throws Exception { buf.putString(K_S); buf.putString(Q_C); buf.putString(Q_S); - buf.putMPInt(K); byte[] foo = new byte[buf.getLength()]; buf.getByte(foo); sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); H = sha.digest(); i = 0; diff --git a/src/main/java/com/jcraft/jsch/DHGEX.java b/src/main/java/com/jcraft/jsch/DHGEX.java index 1c13879a..393b0ba2 100644 --- a/src/main/java/com/jcraft/jsch/DHGEX.java +++ b/src/main/java/com/jcraft/jsch/DHGEX.java @@ -26,6 +26,8 @@ package com.jcraft.jsch; +import java.math.BigInteger; + abstract class DHGEX extends KeyExchange { private static final int SSH_MSG_KEX_DH_GEX_GROUP = 31; @@ -68,7 +70,7 @@ public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C sha = c.getDeclaredConstructor().newInstance(); sha.init(); } catch (Exception e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } buf = new Buffer(); @@ -79,15 +81,14 @@ public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C min = Integer.parseInt(session.getConfig("dhgex_min")); max = Integer.parseInt(session.getConfig("dhgex_max")); preferred = Integer.parseInt(session.getConfig("dhgex_preferred")); - if (checkInvalidSize(min) || checkInvalidSize(max) || checkInvalidSize(preferred) - || preferred < min || max < preferred) { + if (min <= 0 || max <= 0 || preferred <= 0 || preferred < min || preferred > max) { throw new JSchException( "Invalid DHGEX sizes: min=" + min + " max=" + max + " preferred=" + preferred); } dh = c.getDeclaredConstructor().newInstance(); dh.init(); } catch (Exception e) { - throw e; + throw new JSchException(e.toString(), e); } packet.reset(); @@ -118,13 +119,20 @@ public boolean next(Buffer _buf) throws Exception { _buf.getByte(); j = _buf.getByte(); if (j != SSH_MSG_KEX_DH_GEX_GROUP) { - System.err.println("type: must be SSH_MSG_KEX_DH_GEX_GROUP " + j); + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_DH_GEX_GROUP " + j); + } return false; } p = _buf.getMPInt(); g = _buf.getMPInt(); + int bits = new BigInteger(1, p).bitLength(); + if (bits < min || bits > max) { + return false; + } + dh.setP(p); dh.setG(g); // The client responds with: @@ -158,7 +166,9 @@ public boolean next(Buffer _buf) throws Exception { j = _buf.getByte(); j = _buf.getByte(); if (j != SSH_MSG_KEX_DH_GEX_REPLY) { - System.err.println("type: must be SSH_MSG_KEX_DH_GEX_REPLY " + j); + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_DH_GEX_REPLY " + j); + } return false; } @@ -171,7 +181,7 @@ public boolean next(Buffer _buf) throws Exception { dh.checkRange(); - K = normalize(dh.getK()); + K = encodeAsMPInt(normalize(dh.getK())); // The hash H is computed as the HASH hash of the concatenation of the // following: @@ -204,11 +214,11 @@ public boolean next(Buffer _buf) throws Exception { buf.putMPInt(g); buf.putMPInt(e); buf.putMPInt(f); - buf.putMPInt(K); byte[] foo = new byte[buf.getLength()]; buf.getByte(foo); sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); H = sha.digest(); @@ -233,8 +243,4 @@ public boolean next(Buffer _buf) throws Exception { public int getState() { return state; } - - static boolean checkInvalidSize(int size) { - return (size < 1024 || size > 8192 || size % 1024 != 0); - } } diff --git a/src/main/java/com/jcraft/jsch/DHGN.java b/src/main/java/com/jcraft/jsch/DHGN.java index ea68ee72..ddb41dfb 100644 --- a/src/main/java/com/jcraft/jsch/DHGN.java +++ b/src/main/java/com/jcraft/jsch/DHGN.java @@ -64,7 +64,7 @@ public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C sha = c.getDeclaredConstructor().newInstance(); sha.init(); } catch (Exception e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } buf = new Buffer(); @@ -75,8 +75,7 @@ public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C dh = c.getDeclaredConstructor().newInstance(); dh.init(); } catch (Exception e) { - // System.err.println(e); - throw e; + throw new JSchException(e.toString(), e); } dh.setP(P()); @@ -119,8 +118,10 @@ public boolean next(Buffer _buf) throws Exception { j = _buf.getInt(); j = _buf.getByte(); j = _buf.getByte(); - if (j != 31) { - System.err.println("type: must be 31 " + j); + if (j != SSH_MSG_KEXDH_REPLY) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEXDH_REPLY " + j); + } return false; } @@ -133,7 +134,7 @@ public boolean next(Buffer _buf) throws Exception { dh.checkRange(); - K = normalize(dh.getK()); + K = encodeAsMPInt(normalize(dh.getK())); // The hash H is computed as the HASH hash of the concatenation of the // following: @@ -155,10 +156,11 @@ public boolean next(Buffer _buf) throws Exception { buf.putString(K_S); buf.putMPInt(e); buf.putMPInt(f); - buf.putMPInt(K); byte[] foo = new byte[buf.getLength()]; buf.getByte(foo); + sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); H = sha.digest(); // System.err.print("H -> "); //dump(H, 0, H.length); diff --git a/src/main/java/com/jcraft/jsch/DHXEC.java b/src/main/java/com/jcraft/jsch/DHXEC.java index b4942b80..b8cf55cc 100644 --- a/src/main/java/com/jcraft/jsch/DHXEC.java +++ b/src/main/java/com/jcraft/jsch/DHXEC.java @@ -63,7 +63,7 @@ public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C sha = c.getDeclaredConstructor().newInstance(); sha.init(); } catch (Exception e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } buf = new Buffer(); @@ -111,7 +111,9 @@ public boolean next(Buffer _buf) throws Exception { j = _buf.getByte(); j = _buf.getByte(); if (j != SSH_MSG_KEX_ECDH_REPLY) { - System.err.println("type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + } return false; } @@ -129,8 +131,7 @@ public boolean next(Buffer _buf) throws Exception { return false; } - K = xdh.getSecret(Q_S); - K = normalize(K); + K = encodeAsMPInt(normalize(xdh.getSecret(Q_S))); byte[] sig_of_H = _buf.getString(); @@ -169,11 +170,11 @@ public boolean next(Buffer _buf) throws Exception { buf.putString(K_S); buf.putString(Q_C); buf.putString(Q_S); - buf.putMPInt(K); byte[] foo = new byte[buf.getLength()]; buf.getByte(foo); sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); H = sha.digest(); i = 0; diff --git a/src/main/java/com/jcraft/jsch/DHXECKEM.java b/src/main/java/com/jcraft/jsch/DHXECKEM.java new file mode 100644 index 00000000..1bee11fb --- /dev/null +++ b/src/main/java/com/jcraft/jsch/DHXECKEM.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class DHXECKEM extends KeyExchange { + + private static final int SSH_MSG_KEX_ECDH_INIT = 30; + private static final int SSH_MSG_KEX_ECDH_REPLY = 31; + private int state; + + byte[] Q_C; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + byte[] e; + + private Buffer buf; + private Packet packet; + + private KEM kem; + private XDH xdh; + + protected String kem_name; + protected String sha_name; + protected String curve_name; + protected int kem_pubkey_len; + protected int kem_encap_len; + protected int xec_key_len; + + @Override + public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception { + this.V_S = V_S; + this.V_C = V_C; + this.I_S = I_S; + this.I_C = I_C; + + try { + Class c = Class.forName(session.getConfig(sha_name)).asSubclass(HASH.class); + sha = c.getDeclaredConstructor().newInstance(); + sha.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + buf = new Buffer(); + packet = new Packet(buf); + + packet.reset(); + // command + string len + Q_C len + buf.checkFreeSize(1 + 4 + kem_pubkey_len + xec_key_len); + buf.putByte((byte) SSH_MSG_KEX_ECDH_INIT); + + try { + Class k = Class.forName(session.getConfig(kem_name)).asSubclass(KEM.class); + kem = k.getDeclaredConstructor().newInstance(); + kem.init(); + + Class c = Class.forName(session.getConfig("xdh")).asSubclass(XDH.class); + xdh = c.getDeclaredConstructor().newInstance(); + xdh.init(curve_name, xec_key_len); + + byte[] kem_public_key_C = kem.getPublicKey(); + byte[] xec_public_key_C = xdh.getQ(); + Q_C = new byte[kem_pubkey_len + xec_key_len]; + System.arraycopy(kem_public_key_C, 0, Q_C, 0, kem_pubkey_len); + System.arraycopy(xec_public_key_C, 0, Q_C, kem_pubkey_len, xec_key_len); + buf.putString(Q_C); + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException(e.toString(), e); + } + + if (V_S == null) { // This is a really ugly hack for Session.checkKexes ;-( + return; + } + + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_KEX_ECDH_INIT sent"); + session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEX_ECDH_REPLY"); + } + + state = SSH_MSG_KEX_ECDH_REPLY; + } + + @Override + public boolean next(Buffer _buf) throws Exception { + int i, j; + switch (state) { + case SSH_MSG_KEX_ECDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_ECDH_REPLY + // string K_S, server's public host key + // string Q_S, server's ephemeral public key octet string + // string the signature on the exchange hash + j = _buf.getInt(); + j = _buf.getByte(); + j = _buf.getByte(); + if (j != SSH_MSG_KEX_ECDH_REPLY) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + } + return false; + } + + K_S = _buf.getString(); + + byte[] Q_S = _buf.getString(); + if (Q_S.length != kem_encap_len + xec_key_len) { + return false; + } + + byte[] encapsulation = new byte[kem_encap_len]; + byte[] xec_public_key_S = new byte[xec_key_len]; + System.arraycopy(Q_S, 0, encapsulation, 0, kem_encap_len); + System.arraycopy(Q_S, kem_encap_len, xec_public_key_S, 0, xec_key_len); + + // RFC 5656, + // 4. ECDH Key Exchange + // All elliptic curve public keys MUST be validated after they are + // received. An example of a validation algorithm can be found in + // Section 3.2.2 of [SEC1]. If a key fails validation, + // the key exchange MUST fail. + if (!xdh.validate(xec_public_key_S)) { + return false; + } + + byte[] tmp = null; + try { + tmp = kem.decapsulate(encapsulation); + sha.update(tmp, 0, tmp.length); + } finally { + Util.bzero(tmp); + } + try { + tmp = normalize(xdh.getSecret(xec_public_key_S)); + sha.update(tmp, 0, tmp.length); + } finally { + Util.bzero(tmp); + } + K = encodeAsString(sha.digest()); + + byte[] sig_of_H = _buf.getString(); + + // The hash H is computed as the HASH hash of the concatenation of the + // following: + // string V_C, client's identification string (CR and LF excluded) + // string V_S, server's identification string (CR and LF excluded) + // string I_C, payload of the client's SSH_MSG_KEXINIT + // string I_S, payload of the server's SSH_MSG_KEXINIT + // string K_S, server's public host key + // string Q_C, client's ephemeral public key octet string + // string Q_S, server's ephemeral public key octet string + // string K, shared secret + + // draft-josefsson-ntruprime-ssh-02, + // 3. Key Exchange Method: sntrup761x25519-sha512 + // ... + // The SSH_MSG_KEX_ECDH_REPLY's signature value is computed as described + // in [RFC5656] with the following changes. Instead of encoding the + // shared secret K as 'mpint', it MUST be encoded as 'string'. The + // shared secret K value MUST be the 64-byte output octet string of the + // SHA-512 hash computed with the input as the 32-byte octet string key + // output from the key encapsulation mechanism of sntrup761 concatenated + // with the 32-byte octet string of X25519(a, X25519(b, 9)) = X25519(b, + // X25519(a, 9)). + buf.reset(); + buf.putString(V_C); + buf.putString(V_S); + buf.putString(I_C); + buf.putString(I_S); + buf.putString(K_S); + buf.putString(Q_C); + buf.putString(Q_S); + byte[] foo = new byte[buf.getLength()]; + buf.getByte(foo); + + sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); + H = sha.digest(); + + i = 0; + j = 0; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + String alg = Util.byte2str(K_S, i, j); + i += j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state = STATE_END; + return result; + } + return false; + } + + @Override + public int getState() { + return state; + } +} diff --git a/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java b/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java index d1233943..ef0fedc5 100644 --- a/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java +++ b/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java @@ -26,7 +26,8 @@ package com.jcraft.jsch; -import java.io.*; +import java.io.InputStream; +import java.io.OutputStream; public interface ForwardedTCPIPDaemon extends Runnable { void setChannel(ChannelForwardedTCPIP channel, InputStream in, OutputStream out); diff --git a/src/main/java/com/jcraft/jsch/HostKey.java b/src/main/java/com/jcraft/jsch/HostKey.java index dd6178ac..0f9922b6 100644 --- a/src/main/java/com/jcraft/jsch/HostKey.java +++ b/src/main/java/com/jcraft/jsch/HostKey.java @@ -26,6 +26,8 @@ package com.jcraft.jsch; +import java.util.Locale; + public class HostKey { private static final byte[][] names = @@ -118,11 +120,13 @@ public String getKey() { public String getFingerPrint(JSch jsch) { HASH hash = null; try { - String _c = JSch.getConfig("FingerprintHash").toLowerCase(); + String _c = JSch.getConfig("FingerprintHash").toLowerCase(Locale.ROOT); Class c = Class.forName(JSch.getConfig(_c)).asSubclass(HASH.class); hash = c.getDeclaredConstructor().newInstance(); } catch (Exception e) { - System.err.println("getFingerPrint: " + e); + if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { + jsch.getInstanceLogger().log(Logger.ERROR, "getFingerPrint: " + e.getMessage(), e); + } } return Util.getFingerPrint(hash, key, false, true); } diff --git a/src/main/java/com/jcraft/jsch/IO.java b/src/main/java/com/jcraft/jsch/IO.java index 9c84488b..d00241af 100644 --- a/src/main/java/com/jcraft/jsch/IO.java +++ b/src/main/java/com/jcraft/jsch/IO.java @@ -26,7 +26,9 @@ package com.jcraft.jsch; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.SocketException; class IO { diff --git a/src/main/java/com/jcraft/jsch/Identity.java b/src/main/java/com/jcraft/jsch/Identity.java index 5aee38c8..2520cd2c 100644 --- a/src/main/java/com/jcraft/jsch/Identity.java +++ b/src/main/java/com/jcraft/jsch/Identity.java @@ -45,12 +45,12 @@ public interface Identity { /** * Signs on data with this identity, and returns the result. + * *

* IMPORTANT NOTE:
* The {@link #getSignature(byte[], String)} method should be overridden to ensure {@code ssh-rsa} * type public keys function with the {@code rsa-sha2-256} or {@code rsa-sha2-512} signature * algorithms. - *

* * @param data data to be signed * @return the signature @@ -60,17 +60,17 @@ public interface Identity { /** * Signs on data with this identity, and returns the result. + * *

* IMPORTANT NOTE:
* The default implementation of this method simply calls {@link #getSignature(byte[])}, which * will fail with {@code ssh-rsa} type public keys when utilized with the {@code rsa-sha2-256} or * {@code rsa-sha2-512} signature algorithms:
* it exists only to maintain backwards compatibility of this interface. - *

+ * *

* This default method should be overridden by implementations to ensure the {@code rsa-sha2-256} * and {@code rsa-sha2-512} signature algorithms function correctly. - *

* * @param data data to be signed * @param alg signature algorithm to use @@ -83,22 +83,30 @@ public default byte[] getSignature(byte[] data, String alg) { } /** + * This method is deprecated and the default implmentation of this method will throw an + * {@link UnsupportedOperationException}. + * * @deprecated The decryption should be done automatically in {@link #setPassphrase(byte[])} + * @return true if the decryption is succeeded or this identity is not cyphered. * @see #setPassphrase(byte[]) */ @Deprecated - public boolean decrypt(); + public default boolean decrypt() { + throw new UnsupportedOperationException("not implemented"); + } /** * Returns the name of the key algorithm. * - * @return "ssh-rsa" or "ssh-dss" + * @return the name of the key algorithm */ public String getAlgName(); /** * Returns the name of this identity. It will be useful to identify this object in the * {@link IdentityRepository}. + * + * @return the name of this identity */ public String getName(); @@ -109,8 +117,6 @@ public default byte[] getSignature(byte[] data, String alg) { */ public boolean isEncrypted(); - /** - * Disposes internally allocated data, like byte array for the private key. - */ + /** Disposes internally allocated data, like byte array for the private key. */ public void clear(); } diff --git a/src/main/java/com/jcraft/jsch/IdentityFile.java b/src/main/java/com/jcraft/jsch/IdentityFile.java index 6c804bfc..caf40135 100644 --- a/src/main/java/com/jcraft/jsch/IdentityFile.java +++ b/src/main/java/com/jcraft/jsch/IdentityFile.java @@ -26,27 +26,24 @@ package com.jcraft.jsch; -import java.io.*; - class IdentityFile implements Identity { - private JSch jsch; private KeyPair kpair; private String identity; - static IdentityFile newInstance(String prvfile, String pubfile, JSch jsch) throws JSchException { - KeyPair kpair = KeyPair.load(jsch, prvfile, pubfile); - return new IdentityFile(jsch, prvfile, kpair); + static IdentityFile newInstance(String prvfile, String pubfile, JSch.InstanceLogger instLogger) + throws JSchException { + KeyPair kpair = KeyPair.load(instLogger, prvfile, pubfile); + return new IdentityFile(prvfile, kpair); } - static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, JSch jsch) - throws JSchException { + static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, + JSch.InstanceLogger instLogger) throws JSchException { - KeyPair kpair = KeyPair.load(jsch, prvkey, pubkey); - return new IdentityFile(jsch, name, kpair); + KeyPair kpair = KeyPair.load(instLogger, prvkey, pubkey); + return new IdentityFile(name, kpair); } - private IdentityFile(JSch jsch, String name, KeyPair kpair) throws JSchException { - this.jsch = jsch; + private IdentityFile(String name, KeyPair kpair) { this.identity = name; this.kpair = kpair; } @@ -95,30 +92,21 @@ public byte[] getSignature(byte[] data, String alg) { return kpair.getSignature(data, alg); } - /** - * @deprecated This method should not be invoked. - * @see #setPassphrase(byte[] passphrase) - */ - @Override - @Deprecated - public boolean decrypt() { - throw new RuntimeException("not implemented"); - } - /** * Returns the name of the key algorithm. * - * @return "ssh-rsa" or "ssh-dss" + * @return the name of the key algorithm */ @Override public String getAlgName() { - byte[] name = kpair.getKeyTypeName(); - return Util.byte2str(name); + return kpair.getKeyTypeString(); } /** * Returns the name of this identity. It will be useful to identify this object in the * {@link IdentityRepository}. + * + * @return the name of this identity */ @Override public String getName() { @@ -135,9 +123,7 @@ public boolean isEncrypted() { return kpair.isEncrypted(); } - /** - * Disposes internally allocated data, like byte array for the private key. - */ + /** Disposes internally allocated data, like byte array for the private key. */ @Override public void clear() { kpair.dispose(); diff --git a/src/main/java/com/jcraft/jsch/JSch.java b/src/main/java/com/jcraft/jsch/JSch.java index 963c1f03..f942eaff 100644 --- a/src/main/java/com/jcraft/jsch/JSch.java +++ b/src/main/java/com/jcraft/jsch/JSch.java @@ -32,12 +32,11 @@ import java.util.Vector; public class JSch { - /** - * The version number. - */ + /** The version number. */ public static final String VERSION = Version.getVersion(); static Hashtable config = new Hashtable<>(); + static { config.put("kex", Util.getSystemProperty("jsch.kex", "curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256")); @@ -45,8 +44,12 @@ public class JSch { "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-rsa")); config.put("prefer_known_host_key_types", Util.getSystemProperty("jsch.prefer_known_host_key_types", "yes")); + config.put("enable_strict_kex", Util.getSystemProperty("jsch.enable_strict_kex", "yes")); + config.put("require_strict_kex", Util.getSystemProperty("jsch.require_strict_kex", "no")); config.put("enable_server_sig_algs", Util.getSystemProperty("jsch.enable_server_sig_algs", "yes")); + config.put("enable_ext_info_in_auth", + Util.getSystemProperty("jsch.enable_ext_info_in_auth", "yes")); config.put("cipher.s2c", Util.getSystemProperty("jsch.cipher", "aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com")); config.put("cipher.c2s", Util.getSystemProperty("jsch.cipher", @@ -99,6 +102,9 @@ public class JSch { config.put("curve25519-sha256", "com.jcraft.jsch.DH25519"); config.put("curve25519-sha256@libssh.org", "com.jcraft.jsch.DH25519"); config.put("curve448-sha512", "com.jcraft.jsch.DH448"); + config.put("sntrup761x25519-sha512@openssh.com", "com.jcraft.jsch.DH25519SNTRUP761"); + + config.put("sntrup761", "com.jcraft.jsch.bc.SNTRUP761"); config.put("dh", "com.jcraft.jsch.jce.DH"); config.put("3des-cbc", "com.jcraft.jsch.jce.TripleDESCBC"); @@ -195,6 +201,8 @@ public class JSch { config.put("pbkdf2-hmac-sha256", "com.jcraft.jsch.jce.PBKDF2HMACSHA256"); config.put("pbkdf2-hmac-sha384", "com.jcraft.jsch.jce.PBKDF2HMACSHA384"); config.put("pbkdf2-hmac-sha512", "com.jcraft.jsch.jce.PBKDF2HMACSHA512"); + config.put("pbkdf2-hmac-sha512-224", "com.jcraft.jsch.jce.PBKDF2HMACSHA512224"); + config.put("pbkdf2-hmac-sha512-256", "com.jcraft.jsch.jce.PBKDF2HMACSHA512256"); config.put("bcrypt", "com.jcraft.jsch.jbcrypt.JBCrypt"); config.put("argon2", "com.jcraft.jsch.bc.Argon2"); config.put("scrypt", "com.jcraft.jsch.bc.SCrypt"); @@ -228,12 +236,14 @@ public class JSch { config.put("try_additional_pubkey_algorithms", Util.getSystemProperty("jsch.try_additional_pubkey_algorithms", "yes")); config.put("enable_auth_none", Util.getSystemProperty("jsch.enable_auth_none", "yes")); + config.put("use_sftp_write_flush_workaround", + Util.getSystemProperty("jsch.use_sftp_write_flush_workaround", "yes")); config.put("CheckCiphers", Util.getSystemProperty("jsch.check_ciphers", "chacha20-poly1305@openssh.com")); config.put("CheckMacs", Util.getSystemProperty("jsch.check_macs", "")); config.put("CheckKexes", Util.getSystemProperty("jsch.check_kexes", - "curve25519-sha256,curve25519-sha256@libssh.org,curve448-sha512")); + "sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,curve448-sha512")); config.put("CheckSignatures", Util.getSystemProperty("jsch.check_signatures", "ssh-ed25519,ssh-ed448")); config.put("FingerprintHash", Util.getSystemProperty("jsch.fingerprint_hash", "sha256")); @@ -242,9 +252,11 @@ public class JSch { config.put("ClearAllForwardings", "no"); } + final InstanceLogger instLogger = new InstanceLogger(); + private Vector sessionPool = new Vector<>(); - private IdentityRepository defaultIdentityRepository = new LocalIdentityRepository(this); + private IdentityRepository defaultIdentityRepository = new LocalIdentityRepository(instLogger); private IdentityRepository identityRepository = defaultIdentityRepository; @@ -256,7 +268,6 @@ public class JSch { * * @param identityRepository if null is given, the default repository, which usually * refers to ~/.ssh/, will be used. - * * @see #getIdentityRepository() */ public synchronized void setIdentityRepository(IdentityRepository identityRepository) { @@ -291,7 +302,6 @@ public boolean isEnabled(int level) { public void log(int level, String message) {} }; static Logger logger = DEVNULL; - private Logger instLogger; public JSch() {} @@ -301,11 +311,8 @@ public JSch() {} * "user.name" will be referred. * * @param host hostname - * * @throws JSchException if username or host are invalid. - * * @return the instance of Session class. - * * @see #getSession(String username, String host, int port) * @see com.jcraft.jsch.Session * @see com.jcraft.jsch.ConfigRepository @@ -321,11 +328,8 @@ public Session getSession(String host) throws JSchException { * * @param username user name * @param host hostname - * * @throws JSchException if username or host are invalid. - * * @return the instance of Session class. - * * @see #getSession(String username, String host, int port) * @see com.jcraft.jsch.Session */ @@ -341,11 +345,8 @@ public Session getSession(String username, String host) throws JSchException { * @param username user name * @param host hostname * @param port port number - * * @throws JSchException if username or host are invalid. - * * @return the instance of Session class. - * * @see #getSession(String username, String host, int port) * @see com.jcraft.jsch.Session */ @@ -373,7 +374,6 @@ protected boolean removeSession(Session session) { * Sets the hostkey repository. * * @param hkrepo - * * @see com.jcraft.jsch.HostKeyRepository * @see com.jcraft.jsch.KnownHosts */ @@ -385,9 +385,7 @@ public void setHostKeyRepository(HostKeyRepository hkrepo) { * Sets the instance of KnownHosts, which refers to filename. * * @param filename filename of known_hosts file. - * * @throws JSchException if the given filename is invalid. - * * @see com.jcraft.jsch.KnownHosts */ public void setKnownHosts(String filename) throws JSchException { @@ -404,9 +402,7 @@ public void setKnownHosts(String filename) throws JSchException { * Sets the instance of KnownHosts generated with stream. * * @param stream the instance of InputStream from known_hosts file. - * * @throws JSchException if an I/O error occurs. - * * @see com.jcraft.jsch.KnownHosts */ public void setKnownHosts(InputStream stream) throws JSchException { @@ -424,7 +420,6 @@ public void setKnownHosts(InputStream stream) throws JSchException { * KnownHosts. * * @return current hostkey repository. - * * @see com.jcraft.jsch.HostKeyRepository * @see com.jcraft.jsch.KnownHosts */ @@ -438,9 +433,7 @@ public HostKeyRepository getHostKeyRepository() { * Sets the private key, which will be referred in the public key authentication. * * @param prvkey filename of the private key. - * * @throws JSchException if prvkey is invalid. - * * @see #addIdentity(String prvkey, String passphrase) */ public void addIdentity(String prvkey) throws JSchException { @@ -453,9 +446,7 @@ public void addIdentity(String prvkey) throws JSchException { * * @param prvkey filename of the private key. * @param passphrase passphrase for prvkey. - * * @throws JSchException if passphrase is not right. - * * @see #addIdentity(String prvkey, byte[] passphrase) */ public void addIdentity(String prvkey, String passphrase) throws JSchException { @@ -474,13 +465,11 @@ public void addIdentity(String prvkey, String passphrase) throws JSchException { * * @param prvkey filename of the private key. * @param passphrase passphrase for prvkey. - * * @throws JSchException if passphrase is not right. - * * @see #addIdentity(String prvkey, String pubkey, byte[] passphrase) */ public void addIdentity(String prvkey, byte[] passphrase) throws JSchException { - Identity identity = IdentityFile.newInstance(prvkey, null, this); + Identity identity = IdentityFile.newInstance(prvkey, null, instLogger); addIdentity(identity, passphrase); } @@ -491,11 +480,10 @@ public void addIdentity(String prvkey, byte[] passphrase) throws JSchException { * @param prvkey filename of the private key. * @param pubkey filename of the public key. * @param passphrase passphrase for prvkey. - * * @throws JSchException if passphrase is not right. */ public void addIdentity(String prvkey, String pubkey, byte[] passphrase) throws JSchException { - Identity identity = IdentityFile.newInstance(prvkey, pubkey, this); + Identity identity = IdentityFile.newInstance(prvkey, pubkey, instLogger); addIdentity(identity, passphrase); } @@ -507,11 +495,10 @@ public void addIdentity(String prvkey, String pubkey, byte[] passphrase) throws * @param prvkey private key in byte array. * @param pubkey public key in byte array. * @param passphrase passphrase for prvkey. - * */ public void addIdentity(String name, byte[] prvkey, byte[] pubkey, byte[] passphrase) throws JSchException { - Identity identity = IdentityFile.newInstance(name, prvkey, pubkey, this); + Identity identity = IdentityFile.newInstance(name, prvkey, pubkey, instLogger); addIdentity(identity, passphrase); } @@ -521,7 +508,6 @@ public void addIdentity(String name, byte[] prvkey, byte[] pubkey, byte[] passph * * @param identity private key. * @param passphrase passphrase for identity. - * * @throws JSchException if passphrase is not right. */ public void addIdentity(Identity identity, byte[] passphrase) throws JSchException { @@ -572,7 +558,6 @@ public void removeIdentity(String name) throws JSchException { * Removes the identity from identityRepository. * * @param identity the indentity to be removed. - * * @throws JSchException if identity is invalid. */ public void removeIdentity(Identity identity) throws JSchException { @@ -583,7 +568,6 @@ public void removeIdentity(Identity identity) throws JSchException { * Lists names of identities included in the identityRepository. * * @return names of identities - * * @throws JSchException if identityReposory has problems. */ public Vector getIdentityNames() throws JSchException { @@ -655,7 +639,6 @@ public static void setConfig(String key, String value) { * Sets the logger * * @param logger logger or null if no logging should take place - * * @see com.jcraft.jsch.Logger */ public static void setLogger(Logger logger) { @@ -671,10 +654,7 @@ public static void setLogger(Logger logger) { * statically set logger is returned. */ public Logger getInstanceLogger() { - if (this.instLogger == null) { - return logger; - } - return instLogger; + return instLogger.getLogger(); } /** @@ -684,7 +664,7 @@ public Logger getInstanceLogger() { * used */ public void setInstanceLogger(Logger logger) { - this.instLogger = logger; + instLogger.setLogger(logger); } /** @@ -696,4 +676,21 @@ public void setInstanceLogger(Logger logger) { public static Logger getLogger() { return logger; } + + static class InstanceLogger { + private Logger logger; + + private InstanceLogger() {} + + Logger getLogger() { + if (logger == null) { + return JSch.logger; + } + return logger; + } + + void setLogger(Logger logger) { + this.logger = logger; + } + } } diff --git a/src/main/java/com/jcraft/jsch/JSchAlgoNegoFailException.java b/src/main/java/com/jcraft/jsch/JSchAlgoNegoFailException.java index fbdbf446..6e668250 100644 --- a/src/main/java/com/jcraft/jsch/JSchAlgoNegoFailException.java +++ b/src/main/java/com/jcraft/jsch/JSchAlgoNegoFailException.java @@ -1,5 +1,7 @@ package com.jcraft.jsch; +import java.util.Locale; + /** * Extension of {@link JSchException} to indicate when a connection fails during algorithm * negotiation. @@ -35,7 +37,7 @@ public String getServerProposal() { } private static String failString(int algorithmIndex, String jschProposal, String serverProposal) { - return String.format( + return String.format(Locale.ROOT, "Algorithm negotiation fail: algorithmName=\"%s\" jschProposal=\"%s\" serverProposal=\"%s\"", algorithmNameFromIndex(algorithmIndex), jschProposal, serverProposal); } diff --git a/src/main/java/com/jcraft/jsch/JSchChangedHostKeyException.java b/src/main/java/com/jcraft/jsch/JSchChangedHostKeyException.java new file mode 100644 index 00000000..4a1ee613 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSchChangedHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchChangedHostKeyException extends JSchHostKeyException { + private static final long serialVersionUID = -1L; + + JSchChangedHostKeyException() { + super(); + } + + JSchChangedHostKeyException(String s) { + super(s); + } +} diff --git a/src/main/java/com/jcraft/jsch/JSchHostKeyException.java b/src/main/java/com/jcraft/jsch/JSchHostKeyException.java new file mode 100644 index 00000000..91a3f896 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSchHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public abstract class JSchHostKeyException extends JSchException { + private static final long serialVersionUID = -1L; + + JSchHostKeyException() { + super(); + } + + JSchHostKeyException(String s) { + super(s); + } +} diff --git a/src/main/java/com/jcraft/jsch/JSchProxyException.java b/src/main/java/com/jcraft/jsch/JSchProxyException.java new file mode 100644 index 00000000..88f8d99f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSchProxyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchProxyException extends JSchException { + private static final long serialVersionUID = -1L; + + public JSchProxyException(String s) { + super(s); + } + + public JSchProxyException(String s, Throwable e) { + super(s, e); + } +} diff --git a/src/main/java/com/jcraft/jsch/JSchRevokedHostKeyException.java b/src/main/java/com/jcraft/jsch/JSchRevokedHostKeyException.java new file mode 100644 index 00000000..f25e2f67 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSchRevokedHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchRevokedHostKeyException extends JSchHostKeyException { + private static final long serialVersionUID = -1L; + + JSchRevokedHostKeyException() { + super(); + } + + JSchRevokedHostKeyException(String s) { + super(s); + } +} diff --git a/src/main/java/com/jcraft/jsch/JSchSessionDisconnectException.java b/src/main/java/com/jcraft/jsch/JSchSessionDisconnectException.java new file mode 100644 index 00000000..cb994f98 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSchSessionDisconnectException.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchSessionDisconnectException extends JSchException { + private static final long serialVersionUID = -1L; + + // RFC 4253 11.1. + private final int reasonCode; // RFC 4250 4.2.2. + private final String description; + private final String languageTag; + + JSchSessionDisconnectException(String s, int reasonCode, String description, String languageTag) { + super(s); + this.reasonCode = reasonCode; + this.description = description; + this.languageTag = languageTag; + } + + public int getReasonCode() { + return reasonCode; + } + + public String getDescription() { + return description; + } + + public String getLanguageTag() { + return languageTag; + } +} diff --git a/src/main/java/com/jcraft/jsch/JSchStrictKexException.java b/src/main/java/com/jcraft/jsch/JSchStrictKexException.java new file mode 100644 index 00000000..3454c1d2 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSchStrictKexException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchStrictKexException extends JSchException { + private static final long serialVersionUID = -1L; + + JSchStrictKexException() { + super(); + } + + JSchStrictKexException(String s) { + super(s); + } +} diff --git a/src/main/java/com/jcraft/jsch/JSchUnknownHostKeyException.java b/src/main/java/com/jcraft/jsch/JSchUnknownHostKeyException.java new file mode 100644 index 00000000..1957bda7 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSchUnknownHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchUnknownHostKeyException extends JSchHostKeyException { + private static final long serialVersionUID = -1L; + + JSchUnknownHostKeyException() { + super(); + } + + JSchUnknownHostKeyException(String s) { + super(s); + } +} diff --git a/src/main/java/com/jcraft/jsch/JUnixSocketFactory.java b/src/main/java/com/jcraft/jsch/JUnixSocketFactory.java index d3038cdd..053e4947 100644 --- a/src/main/java/com/jcraft/jsch/JUnixSocketFactory.java +++ b/src/main/java/com/jcraft/jsch/JUnixSocketFactory.java @@ -26,17 +26,13 @@ package com.jcraft.jsch; -import com.jcraft.jsch.AgentProxyException; -import com.jcraft.jsch.USocketFactory; - -import org.newsclub.net.unix.AFUNIXServerSocketChannel; -import org.newsclub.net.unix.AFUNIXSocketChannel; -import org.newsclub.net.unix.AFUNIXSocketAddress; - import java.io.IOException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.file.Path; +import org.newsclub.net.unix.AFUNIXServerSocketChannel; +import org.newsclub.net.unix.AFUNIXSocketAddress; +import org.newsclub.net.unix.AFUNIXSocketChannel; public class JUnixSocketFactory implements USocketFactory { diff --git a/src/main/java/com/jcraft/jsch/JplLogger.java b/src/main/java/com/jcraft/jsch/JplLogger.java new file mode 100644 index 00000000..baf437ed --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JplLogger.java @@ -0,0 +1,23 @@ +package com.jcraft.jsch; + +public class JplLogger implements com.jcraft.jsch.Logger { + + public JplLogger() { + throw new UnsupportedOperationException("JplLogger requires Java9+."); + } + + @Override + public boolean isEnabled(int level) { + throw new UnsupportedOperationException("JplLogger requires Java9+."); + } + + @Override + public void log(int level, String message) { + throw new UnsupportedOperationException("JplLogger requires Java9+."); + } + + @Override + public void log(int level, String message, Throwable cause) { + throw new UnsupportedOperationException("JplLogger requires Java9+."); + } +} diff --git a/src/main/java/com/jcraft/jsch/KeyPairGenXEC.java b/src/main/java/com/jcraft/jsch/KEM.java similarity index 90% rename from src/main/java/com/jcraft/jsch/KeyPairGenXEC.java rename to src/main/java/com/jcraft/jsch/KEM.java index fc4d5f0f..90dcaacf 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairGenXEC.java +++ b/src/main/java/com/jcraft/jsch/KEM.java @@ -26,6 +26,10 @@ package com.jcraft.jsch; -public interface KeyPairGenXEC { - void init(String name) throws Exception; +public interface KEM { + void init() throws Exception; + + byte[] getPublicKey() throws Exception; + + byte[] decapsulate(byte[] encapsulation) throws Exception; } diff --git a/src/main/java/com/jcraft/jsch/KeyExchange.java b/src/main/java/com/jcraft/jsch/KeyExchange.java index 673dcfbd..3090391e 100644 --- a/src/main/java/com/jcraft/jsch/KeyExchange.java +++ b/src/main/java/com/jcraft/jsch/KeyExchange.java @@ -26,6 +26,8 @@ package com.jcraft.jsch; +import java.util.Locale; + public abstract class KeyExchange { static final int PROPOSAL_KEX_ALGS = 0; @@ -198,11 +200,13 @@ protected static String[] guess(Session session, byte[] I_S, byte[] I_C) throws public String getFingerPrint() { HASH hash = null; try { - String _c = session.getConfig("FingerprintHash").toLowerCase(); + String _c = session.getConfig("FingerprintHash").toLowerCase(Locale.ROOT); Class c = Class.forName(session.getConfig(_c)).asSubclass(HASH.class); hash = c.getDeclaredConstructor().newInstance(); } catch (Exception e) { - System.err.println("getFingerPrint: " + e); + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "getFingerPrint: " + e.getMessage(), e); + } } return Util.getFingerPrint(hash, getHostKey(), true, false); } @@ -211,6 +215,11 @@ byte[] getK() { return K; } + void clearK() { + Util.bzero(K); + K = null; + } + byte[] getH() { return H; } @@ -226,16 +235,59 @@ byte[] getHostKey() { /* * It seems JCE included in Oracle's Java7u6(and later) has suddenly changed its behavior. The * secrete generated by KeyAgreement#generateSecret() may start with 0, even if it is a positive - * value. + * value. See https://bugs.openjdk.org/browse/JDK-7146728. */ protected byte[] normalize(byte[] secret) { - if (secret.length > 1 && secret[0] == 0 && (secret[1] & 0x80) == 0) { - byte[] tmp = new byte[secret.length - 1]; - System.arraycopy(secret, 1, tmp, 0, tmp.length); - return normalize(tmp); - } else { + // This should be a timing safe version of the following: + // if (secret.length > 1 && secret[0] == 0 && (secret[1] & 0x80) == 0) { + // byte[] tmp = new byte[secret.length - 1]; + // System.arraycopy(secret, 1, tmp, 0, tmp.length); + // Util.bzero(secret); + // return normalize(tmp); + // } else { + // return secret; + // } + + int len = secret.length; + if (len < 2) { return secret; } + + // secret[0] == 0 + int a = 0; + int s0 = secret[0] & 0xff; + for (int i = 0; i < 8; i++) { + int j = s0 >>> i; + j &= 0x1; + a |= j; + } + a ^= 0x1; + + // (secret[1..n] & 0x80) == 0 && secret[1..n] != 0 + int offset = 0; + for (int i = 1; i < len; i++) { + int j = secret[i] & 0x80; + j >>>= 7; + j ^= 0x1; + a &= j; + offset += a; + j = secret[i] & 0x7f; + for (int k = 0; k < 7; k++) { + int l = j >>> k; + l &= 0x1; + l ^= 0x1; + a &= l; + } + } + + len -= offset; + // Try to remain timing safe by performing an allocation + copy for leading bytes removed + byte[] foo = new byte[len]; + byte[] bar = new byte[offset]; + System.arraycopy(secret, 0, bar, 0, offset); + System.arraycopy(secret, offset, foo, 0, len); + Util.bzero(secret); + return foo; } protected boolean verify(String alg, byte[] K_S, int index, byte[] sig_of_H) throws Exception { @@ -274,7 +326,7 @@ protected boolean verify(String alg, byte[] K_S, int index, byte[] sig_of_H) thr sig = c.getDeclaredConstructor().newInstance(); sig.init(); } catch (Exception e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } sig.setPubKey(ee, n); sig.update(H); @@ -325,7 +377,7 @@ protected boolean verify(String alg, byte[] K_S, int index, byte[] sig_of_H) thr sig = c.getDeclaredConstructor().newInstance(); sig.init(); } catch (Exception e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } sig.setPubKey(f, p, q, g); sig.update(H); @@ -368,7 +420,7 @@ protected boolean verify(String alg, byte[] K_S, int index, byte[] sig_of_H) thr sig = c.getDeclaredConstructor().newInstance(); sig.init(); } catch (Exception e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } sig.setPubKey(r, s); @@ -400,7 +452,7 @@ protected boolean verify(String alg, byte[] K_S, int index, byte[] sig_of_H) thr sig = c.getDeclaredConstructor().newInstance(); sig.init(); } catch (Exception | NoClassDefFoundError e) { - System.err.println(e); + throw new JSchException(e.toString(), e); } sig.setPubKey(tmp); @@ -413,10 +465,38 @@ protected boolean verify(String alg, byte[] K_S, int index, byte[] sig_of_H) thr session.getLogger().log(Logger.INFO, "ssh_eddsa_verify: " + alg + " signature " + result); } } else { - System.err.println("unknown alg"); + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "unknown alg: " + alg); + } } return result; } + protected byte[] encodeAsMPInt(byte[] raw) { + int i = (raw[0] & 0x80) >>> 7; + int len = raw.length + i; + byte[] foo = new byte[len + 4]; + // Try to remain timing safe by performing an extra allocation when i == 0 + byte[] bar = new byte[i ^ 0x1]; + foo[0] = (byte) (len >>> 24); + foo[1] = (byte) (len >>> 16); + foo[2] = (byte) (len >>> 8); + foo[3] = (byte) (len); + System.arraycopy(raw, 0, foo, 4 + i, len - i); + Util.bzero(raw); + return foo; + } + + protected byte[] encodeAsString(byte[] raw) { + int len = raw.length; + byte[] foo = new byte[len + 4]; + foo[0] = (byte) (len >>> 24); + foo[1] = (byte) (len >>> 16); + foo[2] = (byte) (len >>> 8); + foo[3] = (byte) (len); + System.arraycopy(raw, 0, foo, 4, len); + Util.bzero(raw); + return foo; + } } diff --git a/src/main/java/com/jcraft/jsch/KeyPair.java b/src/main/java/com/jcraft/jsch/KeyPair.java index f6826bec..5492b469 100644 --- a/src/main/java/com/jcraft/jsch/KeyPair.java +++ b/src/main/java/com/jcraft/jsch/KeyPair.java @@ -26,7 +26,11 @@ package com.jcraft.jsch; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -37,6 +41,7 @@ public abstract class KeyPair { /** DEFERRED should not be be used. */ public static final int DEFERRED = -1; + public static final int ERROR = 0; public static final int DSA = 1; public static final int RSA = 2; @@ -64,15 +69,15 @@ public static KeyPair genKeyPair(JSch jsch, int type) throws JSchException { public static KeyPair genKeyPair(JSch jsch, int type, int key_size) throws JSchException { KeyPair kpair = null; if (type == DSA) { - kpair = new KeyPairDSA(jsch); + kpair = new KeyPairDSA(jsch.instLogger); } else if (type == RSA) { - kpair = new KeyPairRSA(jsch); + kpair = new KeyPairRSA(jsch.instLogger); } else if (type == ECDSA) { - kpair = new KeyPairECDSA(jsch); + kpair = new KeyPairECDSA(jsch.instLogger); } else if (type == ED25519) { - kpair = new KeyPairEd25519(jsch); + kpair = new KeyPairEd25519(jsch.instLogger); } else if (type == ED448) { - kpair = new KeyPairEd448(jsch); + kpair = new KeyPairEd448(jsch.instLogger); } if (kpair != null) { kpair.generate(key_size); @@ -108,7 +113,7 @@ public void setPublicKeyComment(String publicKeyComment) { protected String publicKeyComment = "no comment"; - JSch jsch = null; + JSch.InstanceLogger instLogger; protected Cipher cipher; private KDF kdf; private HASH sha1; @@ -117,8 +122,8 @@ public void setPublicKeyComment(String publicKeyComment) { private byte[] passphrase; - public KeyPair(JSch jsch) { - this.jsch = jsch; + KeyPair(JSch.InstanceLogger instLogger) { + this.instLogger = instLogger; } static byte[][] header = @@ -184,8 +189,8 @@ public void writePrivateKey(OutputStream out, byte[] passphrase) { out.write(cr); // out.close(); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to write private key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to write private key", e); } } } @@ -234,8 +239,8 @@ public void writePublicKey(OutputStream out, String comment) { out.write(Util.str2byte(comment)); out.write(cr); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to write public key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to write public key", e); } } } @@ -249,9 +254,9 @@ public void writePublicKey(OutputStream out, String comment) { */ public void writePublicKey(String name, String comment) throws FileNotFoundException, IOException { - FileOutputStream fos = new FileOutputStream(name); - writePublicKey(fos, comment); - fos.close(); + try (OutputStream fos = new FileOutputStream(name)) { + writePublicKey(fos, comment); + } } /** @@ -281,8 +286,8 @@ public void writeSECSHPublicKey(OutputStream out, String comment) { out.write(Util.str2byte("---- END SSH2 PUBLIC KEY ----")); out.write(cr); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to write public key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to write public key", e); } } } @@ -297,9 +302,9 @@ public void writeSECSHPublicKey(OutputStream out, String comment) { */ public void writeSECSHPublicKey(String name, String comment) throws FileNotFoundException, IOException { - FileOutputStream fos = new FileOutputStream(name); - writeSECSHPublicKey(fos, comment); - fos.close(); + try (OutputStream fos = new FileOutputStream(name)) { + writeSECSHPublicKey(fos, comment); + } } /** @@ -321,9 +326,9 @@ public void writePrivateKey(String name) throws FileNotFoundException, IOExcepti */ public void writePrivateKey(String name, byte[] passphrase) throws FileNotFoundException, IOException { - FileOutputStream fos = new FileOutputStream(name); - writePrivateKey(fos, passphrase); - fos.close(); + try (OutputStream fos = new FileOutputStream(name)) { + writePrivateKey(fos, passphrase); + } } /** @@ -372,8 +377,8 @@ private byte[] encrypt(byte[] plain, byte[][] _iv, byte[] passphrase) { cipher.init(Cipher.ENCRYPT_MODE, key, iv); cipher.update(encoded, 0, encoded.length, encoded, 0); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to encrypt key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to encrypt key", e); } } Util.bzero(key); @@ -392,8 +397,8 @@ private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv) { cipher.update(data, 0, data.length, plain, 0); return plain; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to decrypt key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to decrypt key", e); } } return null; @@ -463,8 +468,8 @@ private Random genRandom() { Class.forName(JSch.getConfig("random")).asSubclass(Random.class); random = c.getDeclaredConstructor().newInstance(); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to create random", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create random", e); } } } @@ -477,8 +482,8 @@ private HASH genHash() { hash = c.getDeclaredConstructor().newInstance(); hash.init(); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to create hash", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create hash", e); } } return hash; @@ -490,8 +495,8 @@ private Cipher genCipher() { Class.forName(JSch.getConfig("3des-cbc")).asSubclass(Cipher.class); cipher = c.getDeclaredConstructor().newInstance(); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to create cipher", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create cipher", e); } } return cipher; @@ -562,8 +567,8 @@ synchronized byte[] genKey(byte[] passphrase, byte[] iv) { Util.bzero(tmp); } } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate key from passphrase", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate key from passphrase", e); } } return key; @@ -637,10 +642,15 @@ public static KeyPair load(JSch jsch, String prvkey) throws JSchException { if (!new File(pubkey).exists()) { pubkey = null; } - return load(jsch, prvkey, pubkey); + return load(jsch.instLogger, prvkey, pubkey); } public static KeyPair load(JSch jsch, String prvfile, String pubfile) throws JSchException { + return load(jsch.instLogger, prvfile, pubfile); + } + + static KeyPair load(JSch.InstanceLogger instLogger, String prvfile, String pubfile) + throws JSchException { byte[] prvkey = null; byte[] pubkey = null; @@ -665,13 +675,18 @@ public static KeyPair load(JSch jsch, String prvfile, String pubfile) throws JSc } try { - return load(jsch, prvkey, pubkey); + return load(instLogger, prvkey, pubkey); } finally { Util.bzero(prvkey); } } public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchException { + return load(jsch.instLogger, prvkey, pubkey); + } + + static KeyPair load(JSch.InstanceLogger instLogger, byte[] prvkey, byte[] pubkey) + throws JSchException { byte[] iv = new byte[8]; // 8 boolean encrypted = true; @@ -697,16 +712,16 @@ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchE KeyPair kpair = null; if (_type.equals("ssh-rsa")) { - kpair = KeyPairRSA.fromSSHAgent(jsch, buf); + kpair = KeyPairRSA.fromSSHAgent(instLogger, buf); } else if (_type.equals("ssh-dss")) { - kpair = KeyPairDSA.fromSSHAgent(jsch, buf); + kpair = KeyPairDSA.fromSSHAgent(instLogger, buf); } else if (_type.equals("ecdsa-sha2-nistp256") || _type.equals("ecdsa-sha2-nistp384") || _type.equals("ecdsa-sha2-nistp521")) { - kpair = KeyPairECDSA.fromSSHAgent(jsch, buf); + kpair = KeyPairECDSA.fromSSHAgent(instLogger, buf); } else if (_type.equals("ssh-ed25519")) { - kpair = KeyPairEd25519.fromSSHAgent(jsch, buf); + kpair = KeyPairEd25519.fromSSHAgent(instLogger, buf); } else if (_type.equals("ssh-ed448")) { - kpair = KeyPairEd448.fromSSHAgent(jsch, buf); + kpair = KeyPairEd448.fromSSHAgent(instLogger, buf); } else { throw new JSchException("privatekey: invalid key " + _type); } @@ -717,7 +732,7 @@ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchE byte[] buf = prvkey; if (buf != null) { - KeyPair ppk = loadPPK(jsch, buf); + KeyPair ppk = loadPPK(instLogger, buf); if (ppk != null) return ppk; } @@ -885,11 +900,13 @@ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchE int _len = _buf.length; while (i < _len) { if (_buf[i] == '\n') { - boolean xd = (_buf[i - 1] == '\r'); + boolean xd = (i > 0 && _buf[i - 1] == '\r'); // ignore \n (or \r\n) System.arraycopy(_buf, i + 1, _buf, i - (xd ? 1 : 0), _len - (i + 1)); - if (xd) + if (xd) { _len--; + i--; + } _len--; continue; } @@ -906,7 +923,7 @@ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchE } if (vendor == VENDOR_OPENSSH_V1) { - return loadOpenSSHKeyv1(jsch, data); + return loadOpenSSHKeyv1(instLogger, data); } else if (data != null && data.length > 4 && // FSecure data[0] == (byte) 0x3f && data[1] == (byte) 0x6f && data[2] == (byte) 0xf9 && data[3] == (byte) 0xeb) { @@ -1077,25 +1094,25 @@ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchE } } } catch (Exception ee) { - if (jsch.getInstanceLogger().isEnabled(Logger.WARN)) { - jsch.getInstanceLogger().log(Logger.WARN, "failed to parse public key", ee); + if (instLogger.getLogger().isEnabled(Logger.WARN)) { + instLogger.getLogger().log(Logger.WARN, "failed to parse public key", ee); } } } KeyPair kpair = null; if (type == DSA) { - kpair = new KeyPairDSA(jsch); + kpair = new KeyPairDSA(instLogger); } else if (type == RSA) { - kpair = new KeyPairRSA(jsch); + kpair = new KeyPairRSA(instLogger); } else if (type == ECDSA) { - kpair = new KeyPairECDSA(jsch, pubkey); + kpair = new KeyPairECDSA(instLogger, pubkey); } else if (type == ED25519) { - kpair = new KeyPairEd25519(jsch, pubkey, null); + kpair = new KeyPairEd25519(instLogger, pubkey, null); } else if (type == ED448) { - kpair = new KeyPairEd448(jsch, pubkey, null); + kpair = new KeyPairEd448(instLogger, pubkey, null); } else if (vendor == VENDOR_PKCS8) { - kpair = new KeyPairPKCS8(jsch); + kpair = new KeyPairPKCS8(instLogger); } if (kpair != null) { @@ -1127,7 +1144,8 @@ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchE } } - static KeyPair loadOpenSSHKeyv1(JSch jsch, byte[] data) throws JSchException { + static KeyPair loadOpenSSHKeyv1(JSch.InstanceLogger instLogger, byte[] data) + throws JSchException { if (data == null) { throw new JSchException("invalid privatekey"); } @@ -1149,7 +1167,7 @@ static KeyPair loadOpenSSHKeyv1(JSch jsch, byte[] data) throws JSchException { } byte[] publickeyblob = buffer.getString(); - KeyPair kpair = parsePubkeyBlob(jsch, publickeyblob, null); + KeyPair kpair = parsePubkeyBlob(instLogger, publickeyblob, null); kpair.encrypted = !"none".equals(cipherName); kpair.publickeyblob = publickeyblob; kpair.vendor = VENDOR_OPENSSH_V1; @@ -1204,13 +1222,13 @@ private static boolean isOpenSSHPrivateKey(byte[] buf, int i, int len) { && ident.equals(Util.byte2str(Arrays.copyOfRange(buf, i, i + ident.length()))); } - static private byte a2b(byte c) { + private static byte a2b(byte c) { if ('0' <= c && c <= '9') return (byte) (c - '0'); return (byte) (c - 'a' + 10); } - static private byte b2a(byte c) { + private static byte b2a(byte c) { if (0 <= c && c <= 9) return (byte) (c + '0'); return (byte) (c - 10 + 'A'); @@ -1226,7 +1244,7 @@ public void finalize() { dispose(); } - static KeyPair loadPPK(JSch jsch, byte[] buf) throws JSchException { + static KeyPair loadPPK(JSch.InstanceLogger instLogger, byte[] buf) throws JSchException { byte[] pubkey = null; byte[] prvkey = null; byte[] _prvkey = null; @@ -1273,7 +1291,7 @@ static KeyPair loadPPK(JSch jsch, byte[] buf) throws JSchException { prvkey = Util.fromBase64(_prvkey, 0, _prvkey.length); pubkey = Util.fromBase64(pubkey, 0, pubkey.length); - KeyPair kpair = parsePubkeyBlob(jsch, pubkey, typ); + KeyPair kpair = parsePubkeyBlob(instLogger, pubkey, typ); kpair.encrypted = !v.get("Encryption").equals("none"); kpair.publickeyblob = pubkey; kpair.vendor = ppkVersion; @@ -1367,8 +1385,8 @@ static KeyPair loadPPK(JSch jsch, byte[] buf) throws JSchException { } } - private static KeyPair parsePubkeyBlob(JSch jsch, byte[] pubkeyblob, String typ) - throws JSchException { + private static KeyPair parsePubkeyBlob(JSch.InstanceLogger instLogger, byte[] pubkeyblob, + String typ) throws JSchException { Buffer _buf = new Buffer(pubkeyblob); _buf.skip(pubkeyblob.length); @@ -1386,7 +1404,7 @@ private static KeyPair parsePubkeyBlob(JSch jsch, byte[] pubkeyblob, String typ) byte[] n_array = new byte[_buf.getInt()]; _buf.getByte(n_array); - return new KeyPairRSA(jsch, n_array, pub_array, null); + return new KeyPairRSA(instLogger, n_array, pub_array, null); } else if (typ.equals("ssh-dss")) { byte[] p_array = new byte[_buf.getInt()]; _buf.getByte(p_array); @@ -1397,7 +1415,7 @@ private static KeyPair parsePubkeyBlob(JSch jsch, byte[] pubkeyblob, String typ) byte[] y_array = new byte[_buf.getInt()]; _buf.getByte(y_array); - return new KeyPairDSA(jsch, p_array, q_array, g_array, y_array, null); + return new KeyPairDSA(instLogger, p_array, q_array, g_array, y_array, null); } else if (typ.equals("ecdsa-sha2-nistp256") || typ.equals("ecdsa-sha2-nistp384") || typ.equals("ecdsa-sha2-nistp521")) { byte[] name = _buf.getString(); // nistpXXX @@ -1410,15 +1428,15 @@ private static KeyPair parsePubkeyBlob(JSch jsch, byte[] pubkeyblob, String typ) _buf.getByte(r_array); _buf.getByte(s_array); - return new KeyPairECDSA(jsch, name, r_array, s_array, null); + return new KeyPairECDSA(instLogger, name, r_array, s_array, null); } else if (typ.equals("ssh-ed25519") || typ.equals("ssh-ed448")) { byte[] pub_array = new byte[_buf.getInt()]; _buf.getByte(pub_array); if (typ.equals("ssh-ed25519")) { - return new KeyPairEd25519(jsch, pub_array, null); + return new KeyPairEd25519(instLogger, pub_array, null); } else { - return new KeyPairEd448(jsch, pub_array, null); + return new KeyPairEd448(instLogger, pub_array, null); } } else { throw new JSchException("key type " + typ + " is not supported"); diff --git a/src/main/java/com/jcraft/jsch/KeyPairDSA.java b/src/main/java/com/jcraft/jsch/KeyPairDSA.java index c4d15fa2..d030525c 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairDSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairDSA.java @@ -38,13 +38,13 @@ class KeyPairDSA extends KeyPair { // private int key_size=0; private int key_size = 1024; - KeyPairDSA(JSch jsch) { - this(jsch, null, null, null, null, null); + KeyPairDSA(JSch.InstanceLogger instLogger) { + this(instLogger, null, null, null, null, null); } - KeyPairDSA(JSch jsch, byte[] P_array, byte[] Q_array, byte[] G_array, byte[] pub_array, - byte[] prv_array) { - super(jsch); + KeyPairDSA(JSch.InstanceLogger instLogger, byte[] P_array, byte[] Q_array, byte[] G_array, + byte[] pub_array, byte[] prv_array) { + super(instLogger); this.P_array = P_array; this.Q_array = Q_array; this.G_array = G_array; @@ -136,8 +136,8 @@ boolean parse(byte[] plain) { byte[][] tmp = buf.getBytes(1, ""); prv_array = tmp[0]; } catch (JSchException e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -165,7 +165,6 @@ else if (vendor == VENDOR_OPENSSH_V1) { publicKeyComment = Util.byte2str(prvKEyBuffer.getString()); // if(P_array!=null) key_size = (new BigInteger(P_array)).bitLength(); return true; - } int index = 0; @@ -264,8 +263,8 @@ else if (vendor == VENDOR_OPENSSH_V1) { if (P_array != null) key_size = (new BigInteger(P_array)).bitLength(); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -322,8 +321,8 @@ public byte[] getSignature(byte[] data) { tmp[1] = sig; return Buffer.fromBytes(tmp).buffer; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate signature", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); } } return null; @@ -354,8 +353,8 @@ public Signature getVerifier() { dsa.setPubKey(pub_array, P_array, Q_array, G_array); return dsa; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to create verifier", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); } } return null; @@ -366,7 +365,7 @@ public Signature getVerifier(String alg) { return getVerifier(); } - static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { byte[][] tmp = buf.getBytes(7, "invalid key format"); @@ -375,7 +374,7 @@ static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { byte[] G_array = tmp[3]; byte[] pub_array = tmp[4]; byte[] prv_array = tmp[5]; - KeyPairDSA kpair = new KeyPairDSA(jsch, P_array, Q_array, G_array, pub_array, prv_array); + KeyPairDSA kpair = new KeyPairDSA(instLogger, P_array, Q_array, G_array, pub_array, prv_array); kpair.publicKeyComment = Util.byte2str(tmp[6]); kpair.vendor = VENDOR_OPENSSH; return kpair; diff --git a/src/main/java/com/jcraft/jsch/KeyPairECDSA.java b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java index 489b16da..c3221353 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairECDSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java @@ -46,12 +46,12 @@ class KeyPairECDSA extends KeyPair { private int key_size = 256; - KeyPairECDSA(JSch jsch) { - this(jsch, null, null, null, null); + KeyPairECDSA(JSch.InstanceLogger instLogger) { + this(instLogger, null, null, null, null); } - KeyPairECDSA(JSch jsch, byte[] pubkey) { - this(jsch, null, null, null, null); + KeyPairECDSA(JSch.InstanceLogger instLogger, byte[] pubkey) { + this(instLogger, null, null, null, null); if (pubkey != null) { byte[] name = new byte[8]; @@ -67,8 +67,9 @@ class KeyPairECDSA extends KeyPair { } } - KeyPairECDSA(JSch jsch, byte[] name, byte[] r_array, byte[] s_array, byte[] prv_array) { - super(jsch); + KeyPairECDSA(JSch.InstanceLogger instLogger, byte[] name, byte[] r_array, byte[] s_array, + byte[] prv_array) { + super(instLogger); if (name != null) this.name = name; this.r_array = r_array; @@ -166,8 +167,8 @@ boolean parse(byte[] plain) { prv_array = tmp[0]; key_size = prv_array.length >= 64 ? 521 : (prv_array.length >= 48 ? 384 : 256); } catch (JSchException e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -200,7 +201,6 @@ boolean parse(byte[] plain) { prvKeyBuffer.getByte(x); prvKeyBuffer.getByte(y); - prv_array = prvKeyBuffer.getString(); publicKeyComment = Util.byte2str(prvKeyBuffer.getString()); r_array = x; @@ -208,7 +208,6 @@ boolean parse(byte[] plain) { key_size = x.length >= 64 ? 521 : (x.length >= 48 ? 384 : 256); return true; - } int index = 0; @@ -300,8 +299,8 @@ boolean parse(byte[] plain) { if (prv_array != null) key_size = prv_array.length >= 64 ? 521 : (prv_array.length >= 48 ? 384 : 256); } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -362,8 +361,8 @@ public byte[] getSignature(byte[] data) { tmp[1] = sig; return Buffer.fromBytes(tmp).buffer; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate signature", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); } } return null; @@ -394,8 +393,8 @@ public Signature getVerifier() { ecdsa.setPubKey(r_array, s_array); return ecdsa; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to create verifier", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); } } return null; @@ -406,7 +405,7 @@ public Signature getVerifier(String alg) { return getVerifier(); } - static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { byte[][] tmp = buf.getBytes(5, "invalid key format"); @@ -416,7 +415,7 @@ static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { byte[] s_array = foo[1]; byte[] prv_array = tmp[3]; - KeyPairECDSA kpair = new KeyPairECDSA(jsch, name, r_array, s_array, prv_array); + KeyPairECDSA kpair = new KeyPairECDSA(instLogger, name, r_array, s_array, prv_array); kpair.publicKeyComment = Util.byte2str(tmp[4]); kpair.vendor = VENDOR_OPENSSH; return kpair; diff --git a/src/main/java/com/jcraft/jsch/KeyPairEd25519.java b/src/main/java/com/jcraft/jsch/KeyPairEd25519.java index 5552aeda..eb7990a5 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairEd25519.java +++ b/src/main/java/com/jcraft/jsch/KeyPairEd25519.java @@ -32,12 +32,12 @@ class KeyPairEd25519 extends KeyPairEdDSA { private static int keySize = 32; - KeyPairEd25519(JSch jsch) { - this(jsch, null, null); + KeyPairEd25519(JSch.InstanceLogger instLogger) { + this(instLogger, null, null); } - KeyPairEd25519(JSch jsch, byte[] pub_array, byte[] prv_array) { - super(jsch, pub_array, prv_array); + KeyPairEd25519(JSch.InstanceLogger instLogger, byte[] pub_array, byte[] prv_array) { + super(instLogger, pub_array, prv_array); } @Override @@ -60,13 +60,13 @@ String getJceName() { return "Ed25519"; } - static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { byte[][] tmp = buf.getBytes(4, "invalid key format"); byte[] pub_array = tmp[1]; byte[] prv_array = Arrays.copyOf(tmp[2], keySize); - KeyPairEd25519 kpair = new KeyPairEd25519(jsch, pub_array, prv_array); + KeyPairEd25519 kpair = new KeyPairEd25519(instLogger, pub_array, prv_array); kpair.publicKeyComment = Util.byte2str(tmp[3]); kpair.vendor = VENDOR_OPENSSH; return kpair; diff --git a/src/main/java/com/jcraft/jsch/KeyPairEd448.java b/src/main/java/com/jcraft/jsch/KeyPairEd448.java index 67c8da8c..e22093b6 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairEd448.java +++ b/src/main/java/com/jcraft/jsch/KeyPairEd448.java @@ -32,12 +32,12 @@ class KeyPairEd448 extends KeyPairEdDSA { private static int keySize = 57; - KeyPairEd448(JSch jsch) { - this(jsch, null, null); + KeyPairEd448(JSch.InstanceLogger instLogger) { + this(instLogger, null, null); } - KeyPairEd448(JSch jsch, byte[] pub_array, byte[] prv_array) { - super(jsch, pub_array, prv_array); + KeyPairEd448(JSch.InstanceLogger instLogger, byte[] pub_array, byte[] prv_array) { + super(instLogger, pub_array, prv_array); } @Override @@ -60,13 +60,13 @@ String getJceName() { return "Ed448"; } - static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { byte[][] tmp = buf.getBytes(4, "invalid key format"); byte[] pub_array = tmp[1]; byte[] prv_array = Arrays.copyOf(tmp[2], keySize); - KeyPairEd448 kpair = new KeyPairEd448(jsch, pub_array, prv_array); + KeyPairEd448 kpair = new KeyPairEd448(instLogger, pub_array, prv_array); kpair.publicKeyComment = Util.byte2str(tmp[3]); kpair.vendor = VENDOR_OPENSSH; return kpair; diff --git a/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java b/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java index a50f5b98..08ce3c23 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java @@ -32,8 +32,8 @@ abstract class KeyPairEdDSA extends KeyPair { private byte[] pub_array; private byte[] prv_array; - KeyPairEdDSA(JSch jsch, byte[] pub_array, byte[] prv_array) { - super(jsch); + KeyPairEdDSA(JSch.InstanceLogger instLogger, byte[] pub_array, byte[] prv_array) { + super(instLogger); this.pub_array = pub_array; this.prv_array = prv_array; } @@ -85,8 +85,8 @@ boolean parse(byte[] plain) { byte[][] tmp = buf.getBytes(1, ""); prv_array = tmp[0]; } catch (JSchException e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -110,8 +110,8 @@ boolean parse(byte[] plain) { publicKeyComment = Util.byte2str(buf.getString()); return true; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -126,14 +126,14 @@ boolean parse(byte[] plain) { prv_array = keypairgen.getPrv(); return true; } catch (Exception | NoClassDefFoundError e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } } else { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key"); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key"); } return false; } @@ -179,8 +179,8 @@ public byte[] getSignature(byte[] data, String alg) { tmp[1] = sig; return Buffer.fromBytes(tmp).buffer; } catch (Exception | NoClassDefFoundError e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate signature", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); } } return null; @@ -208,8 +208,8 @@ public Signature getVerifier(String alg) { eddsa.setPubKey(pub_array); return eddsa; } catch (Exception | NoClassDefFoundError e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to create verifier", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); } } return null; diff --git a/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java index 36d5a418..131dd6db 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java +++ b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java @@ -122,11 +122,10 @@ class KeyPairPKCS8 extends KeyPair { private static final byte[] pbeWithSHA1AndRC2CBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0b}; - private KeyPair kpair = null; - KeyPairPKCS8(JSch jsch) { - super(jsch); + KeyPairPKCS8(JSch.InstanceLogger instLogger) { + super(instLogger); } @Override @@ -218,7 +217,7 @@ boolean parse(byte[] plain) { throw new ASN1Exception(); } - _kpair = new KeyPairRSA(jsch); + _kpair = new KeyPairRSA(instLogger); _kpair.copy(this); if (_kpair.parse(_data)) { kpair = _kpair; @@ -306,10 +305,10 @@ boolean parse(byte[] plain) { byte[] pub_array = (new BigInteger(G_array)) .modPow(new BigInteger(prv_array), new BigInteger(P_array)).toByteArray(); - _key = new KeyPairDSA(jsch, P_array, Q_array, G_array, pub_array, prv_array); + _key = new KeyPairDSA(instLogger, P_array, Q_array, G_array, pub_array, prv_array); _plain = _key.getPrivateKey(); - _kpair = new KeyPairDSA(jsch); + _kpair = new KeyPairDSA(instLogger); _kpair.copy(this); if (_kpair.parse(_plain)) { kpair = _kpair; @@ -408,10 +407,10 @@ boolean parse(byte[] plain) { byte[] r_array = tmp[0]; byte[] s_array = tmp[1]; - _key = new KeyPairECDSA(jsch, name, r_array, s_array, prv_array); + _key = new KeyPairECDSA(instLogger, name, r_array, s_array, prv_array); _plain = _key.getPrivateKey(); - _kpair = new KeyPairECDSA(jsch); + _kpair = new KeyPairECDSA(instLogger); _kpair.copy(this); if (_kpair.parse(_plain)) { kpair = _kpair; @@ -431,9 +430,9 @@ boolean parse(byte[] plain) { prv_array = curvePrivateKey.getContent(); if (Util.array_equals(privateKeyAlgorithmID, ed25519)) { - _kpair = new KeyPairEd25519(jsch); + _kpair = new KeyPairEd25519(instLogger); } else { - _kpair = new KeyPairEd448(jsch); + _kpair = new KeyPairEd448(instLogger); } _kpair.copy(this); if (_kpair.parse(prv_array)) { @@ -447,14 +446,14 @@ boolean parse(byte[] plain) { "unsupported privateKeyAlgorithm oid: " + Util.toHex(privateKeyAlgorithmID)); } } catch (ASN1Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "PKCS8: failed to parse key: ASN1 parsing error", + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to parse key: ASN1 parsing error", e); } return false; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "PKCS8: failed to parse key: " + e.getMessage(), + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to parse key: " + e.getMessage(), e); } return false; @@ -766,15 +765,15 @@ public boolean decrypt(byte[] _passphrase) { throw new JSchException("failed to parse decrypted key"); } } catch (ASN1Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, - "PKCS8: failed to decrypt key: ASN1 parsing error", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: ASN1 parsing error", + e); } return false; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, - "PKCS8: failed to decrypt key: " + e.getMessage(), e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: " + e.getMessage(), + e); } return false; } finally { @@ -797,9 +796,9 @@ static String getPBKDF2Name(byte[] id) throws JSchException { } else if (Util.array_equals(id, hmacWithSha512)) { name = "pbkdf2-hmac-sha512"; } else if (Util.array_equals(id, hmacWithSha512224)) { - throw new JSchException("unsupported pbkdf2 function: pbkdf2-hmac-sha512-224"); + name = "pbkdf2-hmac-sha512-224"; } else if (Util.array_equals(id, hmacWithSha512256)) { - throw new JSchException("unsupported pbkdf2 function: pbkdf2-hmac-sha512-256"); + name = "pbkdf2-hmac-sha512-256"; } if (name == null) { @@ -862,6 +861,13 @@ static Cipher getCipher(byte[] id, ASN1 encryptparams, byte[][] ivp) throws Exce } static int parseASN1IntegerAsInt(byte[] content) { - return new BigInteger(content).intValueExact(); + BigInteger b = new BigInteger(content); + // https://github.com/mwiede/jsch/issues/392 not using intValueExact() because of Android + // incompatibility. + if (b.bitLength() <= 31) { + return b.intValue(); + } else { + throw new ArithmeticException("BigInteger out of int range"); + } } } diff --git a/src/main/java/com/jcraft/jsch/KeyPairRSA.java b/src/main/java/com/jcraft/jsch/KeyPairRSA.java index 56b37491..af2c57f9 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairRSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairRSA.java @@ -41,12 +41,12 @@ class KeyPairRSA extends KeyPair { private int key_size = 1024; - KeyPairRSA(JSch jsch) { - this(jsch, null, null, null); + KeyPairRSA(JSch.InstanceLogger instLogger) { + this(instLogger, null, null, null); } - KeyPairRSA(JSch jsch, byte[] n_array, byte[] pub_array, byte[] prv_array) { - super(jsch); + KeyPairRSA(JSch.InstanceLogger instLogger, byte[] n_array, byte[] pub_array, byte[] prv_array) { + super(instLogger); this.n_array = n_array; this.pub_array = pub_array; this.prv_array = prv_array; @@ -139,8 +139,8 @@ boolean parse(byte[] plain) { q_array = tmp[2]; c_array = tmp[3]; } catch (JSchException e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -170,8 +170,8 @@ boolean parse(byte[] plain) { return true; } - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key"); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key"); } return false; } @@ -342,8 +342,8 @@ boolean parse(byte[] plain) { } } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); } return false; } @@ -403,8 +403,8 @@ public byte[] getSignature(byte[] data, String alg) { tmp[1] = sig; return Buffer.fromBytes(tmp).buffer; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate signature", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); } } return null; @@ -433,21 +433,21 @@ public Signature getVerifier(String alg) { rsa.setPubKey(pub_array, n_array); return rsa; } catch (Exception e) { - if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { - jsch.getInstanceLogger().log(Logger.ERROR, "failed to create verifier", e); + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); } } return null; } - static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { byte[][] tmp = buf.getBytes(8, "invalid key format"); byte[] n_array = tmp[1]; byte[] pub_array = tmp[2]; byte[] prv_array = tmp[3]; - KeyPairRSA kpair = new KeyPairRSA(jsch, n_array, pub_array, prv_array); + KeyPairRSA kpair = new KeyPairRSA(instLogger, n_array, pub_array, prv_array); kpair.c_array = tmp[4]; // iqmp kpair.p_array = tmp[5]; kpair.q_array = tmp[6]; diff --git a/src/main/java/com/jcraft/jsch/KnownHosts.java b/src/main/java/com/jcraft/jsch/KnownHosts.java index 86e60f26..950c19ee 100644 --- a/src/main/java/com/jcraft/jsch/KnownHosts.java +++ b/src/main/java/com/jcraft/jsch/KnownHosts.java @@ -54,7 +54,7 @@ class KnownHosts implements HostKeyRepository { void setKnownHosts(String filename) throws JSchException { try { known_hosts = filename; - FileInputStream fis = new FileInputStream(Util.checkTilde(filename)); + InputStream fis = new FileInputStream(Util.checkTilde(filename)); setKnownHosts(fis); } catch (FileNotFoundException e) { // The non-existing file should be allowed. @@ -67,8 +67,7 @@ void setKnownHosts(InputStream input) throws JSchException { byte i; int j; boolean error = false; - try { - InputStream fis = input; + try (InputStream fis = input) { String host; String key = null; int type; @@ -265,12 +264,6 @@ void setKnownHosts(InputStream input) throws JSchException { if (e instanceof JSchException) throw (JSchException) e; throw new JSchException(e.toString(), e); - } finally { - try { - input.close(); - } catch (IOException e) { - throw new JSchException(e.toString(), e); - } } } diff --git a/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java b/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java index 8daf3860..5b692d07 100644 --- a/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java +++ b/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java @@ -32,10 +32,10 @@ class LocalIdentityRepository implements IdentityRepository { private static final String name = "Local Identity Repository"; private Vector identities = new Vector<>(); - private JSch jsch; + private JSch.InstanceLogger instLogger; - LocalIdentityRepository(JSch jsch) { - this.jsch = jsch; + LocalIdentityRepository(JSch.InstanceLogger instLogger) { + this.instLogger = instLogger; } @Override @@ -82,7 +82,7 @@ public synchronized void add(Identity identity) { @Override public synchronized boolean add(byte[] identity) { try { - Identity _identity = IdentityFile.newInstance("from remote:", identity, null, jsch); + Identity _identity = IdentityFile.newInstance("from remote:", identity, null, instLogger); add(_identity); return true; } catch (JSchException e) { diff --git a/src/main/java/com/jcraft/jsch/OpenSSHConfig.java b/src/main/java/com/jcraft/jsch/OpenSSHConfig.java index 4dd6ff05..76328a2e 100644 --- a/src/main/java/com/jcraft/jsch/OpenSSHConfig.java +++ b/src/main/java/com/jcraft/jsch/OpenSSHConfig.java @@ -36,6 +36,7 @@ import java.util.Arrays; import java.util.Hashtable; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.Vector; import java.util.stream.Collectors; @@ -44,6 +45,7 @@ /** * This class implements ConfigRepository interface, and parses OpenSSH's configuration file. The * following keywords will be recognized, + * *
    *
  • Host
  • *
  • User
  • @@ -75,9 +77,10 @@ */ public class OpenSSHConfig implements ConfigRepository { - private static final Set keysWithListAdoption = - Stream.of("KexAlgorithms", "Ciphers", "HostKeyAlgorithms", "MACs", "PubkeyAcceptedAlgorithms", - "PubkeyAcceptedKeyTypes").map(String::toUpperCase).collect(Collectors.toSet()); + private static final Set keysWithListAdoption = Stream + .of("KexAlgorithms", "Ciphers", "HostKeyAlgorithms", "MACs", "PubkeyAcceptedAlgorithms", + "PubkeyAcceptedKeyTypes") + .map(string -> string.toUpperCase(Locale.ROOT)).collect(Collectors.toSet()); /** * Parses the given string, and returns an instance of ConfigRepository. @@ -158,6 +161,7 @@ static Hashtable getKeymap() { } private static final Hashtable keymap = new Hashtable<>(); + static { keymap.put("kex", "KexAlgorithms"); keymap.put("server_host_key", "HostKeyAlgorithms"); @@ -184,6 +188,8 @@ class MyConfig implements Config { byte[] _host = Util.str2byte(host); if (hosts.size() > 1) { for (int i = 1; i < hosts.size(); i++) { + boolean anyPositivePatternMatches = false; + boolean anyNegativePatternMatches = false; String patterns[] = hosts.elementAt(i).split("[ \t]"); for (int j = 0; j < patterns.length; j++) { boolean negate = false; @@ -193,13 +199,17 @@ class MyConfig implements Config { foo = foo.substring(1).trim(); } if (Util.glob(Util.str2byte(foo), _host)) { - if (!negate) { - _configs.addElement(config.get(hosts.elementAt(i))); + if (negate) { + anyNegativePatternMatches = true; + } else { + anyPositivePatternMatches = true; } - } else if (negate) { - _configs.addElement(config.get(hosts.elementAt(i))); } } + + if (anyPositivePatternMatches && !anyNegativePatternMatches) { + _configs.addElement(config.get(hosts.elementAt(i))); + } } } } @@ -209,13 +219,13 @@ private String find(String key) { if (keymap.get(key) != null) { key = keymap.get(key); } - key = key.toUpperCase(); + key = key.toUpperCase(Locale.ROOT); String value = null; for (int i = 0; i < _configs.size(); i++) { Vector v = _configs.elementAt(i); for (int j = 0; j < v.size(); j++) { String[] kv = v.elementAt(j); - if (kv[0].toUpperCase().equals(key)) { + if (kv[0].toUpperCase(Locale.ROOT).equals(key)) { value = kv[1]; break; } @@ -255,13 +265,13 @@ private String find(String key) { } private String[] multiFind(String key) { - key = key.toUpperCase(); + key = key.toUpperCase(Locale.ROOT); Vector value = new Vector<>(); for (int i = 0; i < _configs.size(); i++) { Vector v = _configs.elementAt(i); for (int j = 0; j < v.size(); j++) { String[] kv = v.elementAt(j); - if (kv[0].toUpperCase().equals(key)) { + if (kv[0].toUpperCase(Locale.ROOT).equals(key)) { String foo = kv[1]; if (foo != null) { value.remove(foo); diff --git a/src/main/java/com/jcraft/jsch/PBKDF.java b/src/main/java/com/jcraft/jsch/PBKDF.java index abd09ce6..67e6c9cf 100644 --- a/src/main/java/com/jcraft/jsch/PBKDF.java +++ b/src/main/java/com/jcraft/jsch/PBKDF.java @@ -26,9 +26,7 @@ package com.jcraft.jsch; -/** - * Use PBKDF2 instead. - */ +/** Use PBKDF2 instead. */ @Deprecated public interface PBKDF { byte[] getKey(byte[] pass, byte[] salt, int iteration, int size); diff --git a/src/main/java/com/jcraft/jsch/PageantConnector.java b/src/main/java/com/jcraft/jsch/PageantConnector.java index 37949c30..898a8aab 100644 --- a/src/main/java/com/jcraft/jsch/PageantConnector.java +++ b/src/main/java/com/jcraft/jsch/PageantConnector.java @@ -26,33 +26,29 @@ package com.jcraft.jsch; -import com.sun.jna.Platform; -import com.sun.jna.Native; import com.sun.jna.Memory; import com.sun.jna.Pointer; -import com.sun.jna.Structure; - -import com.sun.jna.win32.W32APIOptions; - -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinBase; import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES; -import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinDef.LPARAM; +import com.sun.jna.platform.win32.WinDef.LRESULT; import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinUser; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; +import com.sun.jna.platform.win32.WinUser.COPYDATASTRUCT; +import java.util.Locale; public class PageantConnector implements AgentConnector { private static final int AGENT_MAX_MSGLEN = 262144; - private static final int AGENT_COPYDATA_ID = 0x804e50ba; + private static final long AGENT_COPYDATA_ID = 0x804e50baL; - private User32 libU = null; - private Kernel32 libK = null; + private final User32 user32; + private final Kernel32 kernel32; public PageantConnector() throws AgentProxyException { if (!Util.getSystemProperty("os.name", "").startsWith("Windows")) { @@ -60,8 +56,8 @@ public PageantConnector() throws AgentProxyException { } try { - libU = User32.INSTANCE; - libK = Kernel32.INSTANCE; + user32 = User32.INSTANCE; + kernel32 = Kernel32.INSTANCE; } catch (UnsatisfiedLinkError | NoClassDefFoundError e) { throw new AgentProxyException(e.toString(), e); } @@ -74,35 +70,7 @@ public String getName() { @Override public boolean isAvailable() { - return libU.FindWindow("Pageant", "Pageant") != null; - } - - private interface User32 extends com.sun.jna.platform.win32.User32 { - User32 INSTANCE = Native.load("user32", User32.class, W32APIOptions.DEFAULT_OPTIONS); - - long SendMessage(HWND hWnd, int msg, WPARAM num1, byte[] num2); - } - - public static class COPYDATASTRUCT32 extends Structure { - public int dwData; - public int cbData; - public Pointer lpData; - - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[] {"dwData", "cbData", "lpData"}); - } - } - - public static class COPYDATASTRUCT64 extends Structure { - public int dwData; - public long cbData; - public Pointer lpData; - - @Override - protected List getFieldOrder() { - return Arrays.asList(new String[] {"dwData", "cbData", "lpData"}); - } + return user32.FindWindow("Pageant", "Pageant") != null; } @Override @@ -111,40 +79,38 @@ public void query(Buffer buffer) throws AgentProxyException { throw new AgentProxyException("Query too large."); } - HWND hwnd = libU.FindWindow("Pageant", "Pageant"); + HWND hwnd = user32.FindWindow("Pageant", "Pageant"); if (hwnd == null) { throw new AgentProxyException("Pageant is not runnning."); } - String mapname = String.format("PageantRequest%08x", libK.GetCurrentThreadId()); + String mapname = + String.format(Locale.ROOT, "PageantRequest%08x", kernel32.GetCurrentThreadId()); - // TODO - SECURITY_ATTRIBUTES psa = null; + HANDLE sharedFile = null; + Pointer sharedMemory = null; + try { + // TODO + SECURITY_ATTRIBUTES psa = null; - HANDLE sharedFile = libK.CreateFileMapping(WinBase.INVALID_HANDLE_VALUE, psa, - WinNT.PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); - if (sharedFile == null || sharedFile == WinBase.INVALID_HANDLE_VALUE) { - throw new AgentProxyException("Unable to create shared file mapping."); - } + sharedFile = kernel32.CreateFileMapping(WinBase.INVALID_HANDLE_VALUE, psa, + WinNT.PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); + if (sharedFile == null || sharedFile == WinBase.INVALID_HANDLE_VALUE) { + throw new AgentProxyException("Unable to create shared file mapping."); + } - Pointer sharedMemory = - Kernel32.INSTANCE.MapViewOfFile(sharedFile, WinNT.SECTION_MAP_WRITE, 0, 0, 0); + sharedMemory = kernel32.MapViewOfFile(sharedFile, WinNT.SECTION_MAP_WRITE, 0, 0, 0); + if (sharedMemory == null) { + throw new AgentProxyException("Unable to create shared file mapping."); + } - byte[] data = null; - long rcode = 0; - try { sharedMemory.write(0, buffer.buffer, 0, buffer.getLength()); - if (Platform.is64Bit()) { - COPYDATASTRUCT64 cds64 = new COPYDATASTRUCT64(); - data = install64(mapname, cds64); - rcode = sendMessage(hwnd, data); - } else { - COPYDATASTRUCT32 cds32 = new COPYDATASTRUCT32(); - data = install32(mapname, cds32); - rcode = sendMessage(hwnd, data); - } + COPYDATASTRUCT cds = createCDS(mapname); + long rcode = sendMessage(hwnd, cds); + // Dummy read to make sure COPYDATASTRUCT isn't GC'd early + long foo = cds.dwData.longValue(); buffer.rewind(); if (rcode != 0) { @@ -157,50 +123,31 @@ public void query(Buffer buffer) throws AgentProxyException { buffer.checkFreeSize(i); sharedMemory.read(4, buffer.buffer, 0, i); } else { - throw new AgentProxyException("User32.SendMessage() returned 0"); + throw new AgentProxyException( + "User32.SendMessage() returned 0 with cds.dwData: " + Long.toHexString(foo)); } } finally { if (sharedMemory != null) - libK.UnmapViewOfFile(sharedMemory); + kernel32.UnmapViewOfFile(sharedMemory); if (sharedFile != null) - libK.CloseHandle(sharedFile); - } - } - - private static byte[] install32(String mapname, COPYDATASTRUCT32 cds) { - cds.dwData = AGENT_COPYDATA_ID; - cds.cbData = mapname.length() + 1; - cds.lpData = new Memory(mapname.length() + 1); - { - byte[] foo = Util.str2byte(mapname, StandardCharsets.US_ASCII); - cds.lpData.write(0, foo, 0, foo.length); - cds.lpData.setByte(foo.length, (byte) 0); - cds.write(); + kernel32.CloseHandle(sharedFile); } - byte[] data = new byte[12]; - Pointer cdsp = cds.getPointer(); - cdsp.read(0, data, 0, 12); - return data; } - private static byte[] install64(String mapname, COPYDATASTRUCT64 cds) { - cds.dwData = AGENT_COPYDATA_ID; - cds.cbData = mapname.length() + 1; - cds.lpData = new Memory(mapname.length() + 1); - { - byte[] foo = Util.str2byte(mapname, StandardCharsets.US_ASCII); - cds.lpData.write(0, foo, 0, foo.length); - cds.lpData.setByte(foo.length, (byte) 0); - cds.write(); - } - byte[] data = new byte[24]; - Pointer cdsp = cds.getPointer(); - cdsp.read(0, data, 0, 24); - return data; + static COPYDATASTRUCT createCDS(String mapname) { + Memory foo = new Memory(mapname.length() + 1); + foo.setString(0, mapname, "US-ASCII"); + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + cds.dwData = new ULONG_PTR(AGENT_COPYDATA_ID); + cds.cbData = (int) foo.size(); + cds.lpData = foo; + cds.write(); + return cds; } - long sendMessage(HWND hwnd, byte[] data) { - - return libU.SendMessage(hwnd, WinUser.WM_COPYDATA, null, data); + long sendMessage(HWND hwnd, COPYDATASTRUCT cds) { + LPARAM data = new LPARAM(Pointer.nativeValue(cds.getPointer())); + LRESULT result = user32.SendMessage(hwnd, WinUser.WM_COPYDATA, null, data); + return result.longValue(); } } diff --git a/src/main/java/com/jcraft/jsch/PortWatcher.java b/src/main/java/com/jcraft/jsch/PortWatcher.java index 4616fc52..0508f5d5 100644 --- a/src/main/java/com/jcraft/jsch/PortWatcher.java +++ b/src/main/java/com/jcraft/jsch/PortWatcher.java @@ -37,6 +37,7 @@ class PortWatcher { private static Vector pool = new Vector<>(); private static InetAddress anyLocalAddress = null; + static { // 0.0.0.0 /* @@ -49,7 +50,6 @@ class PortWatcher { } } - Session session; int lport; int rport; diff --git a/src/main/java/com/jcraft/jsch/Proxy.java b/src/main/java/com/jcraft/jsch/Proxy.java index abc2d280..e214ed8e 100644 --- a/src/main/java/com/jcraft/jsch/Proxy.java +++ b/src/main/java/com/jcraft/jsch/Proxy.java @@ -26,7 +26,8 @@ package com.jcraft.jsch; -import java.io.*; +import java.io.InputStream; +import java.io.OutputStream; import java.net.Socket; public interface Proxy { diff --git a/src/main/java/com/jcraft/jsch/ProxyHTTP.java b/src/main/java/com/jcraft/jsch/ProxyHTTP.java index 028cf830..728c25bb 100644 --- a/src/main/java/com/jcraft/jsch/ProxyHTTP.java +++ b/src/main/java/com/jcraft/jsch/ProxyHTTP.java @@ -26,8 +26,10 @@ package com.jcraft.jsch; -import java.io.*; -import java.net.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; public class ProxyHTTP implements Proxy { private static int DEFAULTPORT = 80; @@ -163,7 +165,7 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim } catch (Exception eee) { } String message = "ProxyHTTP: " + e.toString(); - throw new JSchException(message, e); + throw new JSchProxyException(message, e); } } diff --git a/src/main/java/com/jcraft/jsch/ProxySOCKS4.java b/src/main/java/com/jcraft/jsch/ProxySOCKS4.java index aad0c017..540bfceb 100644 --- a/src/main/java/com/jcraft/jsch/ProxySOCKS4.java +++ b/src/main/java/com/jcraft/jsch/ProxySOCKS4.java @@ -31,8 +31,11 @@ package com.jcraft.jsch; -import java.io.*; -import java.net.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; public class ProxySOCKS4 implements Proxy { private static int DEFAULTPORT = 1080; @@ -120,7 +123,7 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim buf[index++] = byteAddress[i]; } } catch (UnknownHostException uhe) { - throw new JSchException("ProxySOCKS4: " + uhe.toString(), uhe); + throw new JSchProxyException("ProxySOCKS4: " + uhe.toString(), uhe); } if (user != null) { @@ -156,12 +159,12 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim while (s < len) { int i = in.read(buf, s, len - s); if (i <= 0) { - throw new JSchException("ProxySOCKS4: stream is closed"); + throw new JSchProxyException("ProxySOCKS4: stream is closed"); } s += i; } if (buf[0] != 0) { - throw new JSchException("ProxySOCKS4: server returns VN " + buf[0]); + throw new JSchProxyException("ProxySOCKS4: server returns VN " + buf[0]); } if (buf[1] != 90) { try { @@ -169,7 +172,7 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim } catch (Exception eee) { } String message = "ProxySOCKS4: server returns CD " + buf[1]; - throw new JSchException(message); + throw new JSchProxyException(message); } } catch (RuntimeException e) { throw e; @@ -179,7 +182,7 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim socket.close(); } catch (Exception eee) { } - throw new JSchException("ProxySOCKS4: " + e.toString(), e); + throw new JSchProxyException("ProxySOCKS4: " + e.toString(), e); } } diff --git a/src/main/java/com/jcraft/jsch/ProxySOCKS5.java b/src/main/java/com/jcraft/jsch/ProxySOCKS5.java index b1b556dc..ea6df018 100644 --- a/src/main/java/com/jcraft/jsch/ProxySOCKS5.java +++ b/src/main/java/com/jcraft/jsch/ProxySOCKS5.java @@ -31,8 +31,10 @@ package com.jcraft.jsch; -import java.io.*; -import java.net.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; public class ProxySOCKS5 implements Proxy { private static int DEFAULTPORT = 1080; @@ -177,7 +179,7 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim socket.close(); } catch (Exception eee) { } - throw new JSchException("fail in SOCKS5 proxy"); + throw new JSchProxyException("fail in SOCKS5 proxy"); } /* @@ -239,7 +241,7 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim socket.close(); } catch (Exception eee) { } - throw new JSchException("ProxySOCKS5: server returns " + buf[1]); + throw new JSchProxyException("ProxySOCKS5: server returns " + buf[1]); } switch (buf[3] & 0xff) { @@ -268,7 +270,7 @@ public void connect(SocketFactory socket_factory, String host, int port, int tim } catch (Exception eee) { } String message = "ProxySOCKS5: " + e.toString(); - throw new JSchException(message, e); + throw new JSchProxyException(message, e); } } @@ -312,7 +314,7 @@ private void fill(InputStream in, byte[] buf, int len) throws JSchException, IOE while (s < len) { int i = in.read(buf, s, len - s); if (i <= 0) { - throw new JSchException("ProxySOCKS5: stream is closed"); + throw new JSchProxyException("ProxySOCKS5: stream is closed"); } s += i; } diff --git a/src/main/java/com/jcraft/jsch/ServerSocketFactory.java b/src/main/java/com/jcraft/jsch/ServerSocketFactory.java index c43d140b..c60f64f4 100644 --- a/src/main/java/com/jcraft/jsch/ServerSocketFactory.java +++ b/src/main/java/com/jcraft/jsch/ServerSocketFactory.java @@ -26,8 +26,9 @@ package com.jcraft.jsch; -import java.net.*; -import java.io.*; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; public interface ServerSocketFactory { public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) diff --git a/src/main/java/com/jcraft/jsch/Session.java b/src/main/java/com/jcraft/jsch/Session.java index 857e580b..22a0c103 100644 --- a/src/main/java/com/jcraft/jsch/Session.java +++ b/src/main/java/com/jcraft/jsch/Session.java @@ -37,9 +37,9 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Properties; import java.util.Vector; - import javax.crypto.AEADBadTagException; public class Session { @@ -93,6 +93,8 @@ public class Session { private byte[] MACc2s; private byte[] MACs2c; + // RFC 4253 6.4. each direction must run independently hence we have an incoming and outgoing + // sequence number private int seqi = 0; private int seqo = 0; @@ -114,6 +116,15 @@ public class Session { private volatile boolean isConnected = false; + private volatile boolean doExtInfo = false; + private boolean enable_server_sig_algs = true; + private boolean enable_ext_info_in_auth = true; + + private volatile boolean initialKex = true; + private volatile boolean doStrictKex = false; + private boolean enable_strict_kex = true; + private boolean require_strict_kex = false; + private volatile boolean isAuthed = false; private Thread connectThread = null; @@ -148,7 +159,7 @@ public class Session { protected boolean daemon_thread = false; - private long kex_start_time = 0L; + private volatile long kex_start_time = 0L; int max_auth_tries = 6; int auth_failures = 0; @@ -191,6 +202,7 @@ public void connect(int connectTimeout) throws JSchException { if (isConnected) { throw new JSchException("session is already connected"); } + initialKex = true; io = new IO(); if (random == null) { @@ -305,6 +317,10 @@ public void connect(int connectTimeout) throws JSchException { getLogger().log(Logger.INFO, "Local version string: " + Util.byte2str(V_C)); } + enable_server_sig_algs = getConfig("enable_server_sig_algs").equals("yes"); + enable_ext_info_in_auth = getConfig("enable_ext_info_in_auth").equals("yes"); + enable_strict_kex = getConfig("enable_strict_kex").equals("yes"); + require_strict_kex = getConfig("require_strict_kex").equals("yes"); send_kexinit(); buf = read(buf); @@ -362,9 +378,14 @@ public void connect(int connectTimeout) throws JSchException { } receive_newkeys(buf, kex); + initialKex = false; } else { in_kex = false; - throw new JSchException("invalid protocol(newkyes): " + buf.getCommand()); + throw new JSchException("invalid protocol(newkeys): " + buf.getCommand()); + } + + if (enable_server_sig_algs && enable_ext_info_in_auth && doExtInfo) { + send_extinfo(); } try { @@ -379,16 +400,16 @@ public void connect(int connectTimeout) throws JSchException { boolean auth = false; boolean auth_cancel = false; - UserAuth ua = null; + UserAuthNone uan = null; try { - Class c = - Class.forName(getConfig("userauth.none")).asSubclass(UserAuth.class); - ua = c.getDeclaredConstructor().newInstance(); + Class c = + Class.forName(getConfig("userauth.none")).asSubclass(UserAuthNone.class); + uan = c.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new JSchException(e.toString(), e); } - auth = ua.start(this); + auth = uan.start(this); String cmethods = getConfig("PreferredAuthentications"); @@ -396,12 +417,12 @@ public void connect(int connectTimeout) throws JSchException { String smethods = null; if (!auth) { - smethods = ((UserAuthNone) ua).getMethods(); + smethods = uan.getMethods(); if (smethods != null) { - smethods = smethods.toLowerCase(); + smethods = smethods.toLowerCase(Locale.ROOT); } else { // methods: publickey,password,keyboard-interactive - // smethods="publickey,password,keyboard-interactive"; + // smethods = "publickey,password,keyboard-interactive"; smethods = cmethods; } } @@ -426,7 +447,7 @@ public void connect(int connectTimeout) throws JSchException { continue; } - // System.err.println(" method: "+method); + // System.err.println(" method: " + method); if (getLogger().isEnabled(Logger.INFO)) { String str = "Authentications that can continue: "; @@ -439,7 +460,7 @@ public void connect(int connectTimeout) throws JSchException { getLogger().log(Logger.INFO, "Next authentication method: " + method); } - ua = null; + UserAuth ua = null; try { Class c = null; if (getConfig("userauth." + method) != null) { @@ -468,7 +489,7 @@ public void connect(int connectTimeout) throws JSchException { if (!tmp.equals(smethods)) { methodi = 0; } - // System.err.println("PartialAuth: "+methods); + // System.err.println("PartialAuth: " + methods); auth_cancel = false; continue loop; } catch (RuntimeException ee) { @@ -476,8 +497,8 @@ public void connect(int connectTimeout) throws JSchException { } catch (JSchException ee) { throw ee; } catch (Exception ee) { - // System.err.println("ee: "+ee); // SSH_MSG_DISCONNECT: 2 Too many authentication - // failures + // SSH_MSG_DISCONNECT: Too many authentication failures + // System.err.println("ee: " + ee); if (getLogger().isEnabled(Logger.WARN)) { getLogger().log(Logger.WARN, "an exception during authentication\n" + ee.toString()); @@ -562,6 +583,30 @@ private KeyExchange receive_kexinit(Buffer buf) throws Exception { } System.arraycopy(buf.buffer, buf.s, I_S, 0, I_S.length); + if (initialKex) { + if (enable_strict_kex || require_strict_kex) { + doStrictKex = checkServerStrictKex(); + if (doStrictKex) { + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Doing strict KEX"); + } + + if (seqi != 1) { + throw new JSchStrictKexException("KEXINIT not first packet from server"); + } + } else if (require_strict_kex) { + throw new JSchStrictKexException("Strict KEX not supported by server"); + } + } + + if (enable_server_sig_algs) { + doExtInfo = checkServerExtInfo(); + if (doExtInfo && getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "ext-info messaging supported by server"); + } + } + } + if (!in_kex) { // We are in rekeying activated by the remote! send_kexinit(); } @@ -569,7 +614,9 @@ private KeyExchange receive_kexinit(Buffer buf) throws Exception { guess = KeyExchange.guess(this, I_S, I_C); if (guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("ext-info-c") - || guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("ext-info-s")) { + || guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("ext-info-s") + || guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("kex-strict-c-v00@openssh.com") + || guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("kex-strict-s-v00@openssh.com")) { throw new JSchException("Invalid Kex negotiated: " + guess[KeyExchange.PROPOSAL_KEX_ALGS]); } @@ -592,6 +639,50 @@ private KeyExchange receive_kexinit(Buffer buf) throws Exception { return kex; } + private boolean checkServerStrictKex() { + Buffer sb = new Buffer(I_S); + sb.setOffSet(17); + byte[] sp = sb.getString(); // server proposal + + int l = 0; + int m = 0; + while (l < sp.length) { + while (l < sp.length && sp[l] != ',') + l++; + if (m == l) + continue; + if ("kex-strict-s-v00@openssh.com".equals(Util.byte2str(sp, m, l - m))) { + return true; + } + l++; + m = l; + } + + return false; + } + + private boolean checkServerExtInfo() { + Buffer sb = new Buffer(I_S); + sb.setOffSet(17); + byte[] sp = sb.getString(); // server proposal + + int l = 0; + int m = 0; + while (l < sp.length) { + while (l < sp.length && sp[l] != ',') + l++; + if (m == l) + continue; + if ("ext-info-s".equals(Util.byte2str(sp, m, l - m))) { + return true; + } + l++; + m = l; + } + + return false; + } + private volatile boolean in_kex = false; private volatile boolean in_prompt = false; private volatile String[] not_available_shks = null; @@ -675,11 +766,14 @@ private void send_kexinit() throws Exception { } } - String enable_server_sig_algs = getConfig("enable_server_sig_algs"); - if (enable_server_sig_algs.equals("yes") && !isAuthed) { + if (enable_server_sig_algs && !isAuthed) { kex += ",ext-info-c"; } + if ((enable_strict_kex || require_strict_kex) && initialKex) { + kex += ",kex-strict-c-v00@openssh.com"; + } + String server_host_key = getConfig("server_host_key"); String[] not_available_shks = checkSignatures(getConfig("CheckSignatures")); // Cache for UserAuthPublicKey @@ -749,8 +843,8 @@ private void send_kexinit() throws Exception { } } - in_kex = true; kex_start_time = System.currentTimeMillis(); + in_kex = true; // byte SSH_MSG_KEXINIT(20) // byte[16] cookie (random bytes) @@ -807,6 +901,20 @@ private void send_newkeys() throws Exception { } } + private void send_extinfo() throws Exception { + // send SSH_MSG_EXT_INFO(7) + packet.reset(); + buf.putByte((byte) SSH_MSG_EXT_INFO); + buf.putInt(1); + buf.putString(Util.str2byte("ext-info-in-auth@openssh.com")); + buf.putString(Util.str2byte("0")); + write(packet); + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "SSH_MSG_EXT_INFO sent"); + } + } + private void checkHost(String chost, int port, KeyExchange kex) throws JSchException { String shkc = getConfig("StrictHostKeyChecking"); @@ -892,10 +1000,10 @@ private void checkHost(String chost, int port, KeyExchange kex) throws JSchExcep insert = true; } else { if (i == HostKeyRepository.NOT_INCLUDED) - throw new JSchException( + throw new JSchUnknownHostKeyException( "UnknownHostKey: " + chost + ". " + key_type + " key fingerprint is " + key_fprint); else - throw new JSchException("HostKey has been changed: " + chost); + throw new JSchChangedHostKeyException("HostKey has been changed: " + chost); } } @@ -916,7 +1024,7 @@ private void checkHost(String chost, int port, KeyExchange kex) throws JSchExcep if (getLogger().isEnabled(Logger.INFO)) { getLogger().log(Logger.INFO, "Host '" + chost + "' has provided revoked key."); } - throw new JSchException("revoked HostKey: " + chost); + throw new JSchRevokedHostKeyException("revoked HostKey: " + chost); } } } @@ -1174,7 +1282,9 @@ Buffer read(Buffer buf) throws Exception { } } - seqi++; + if (++seqi == 0 && (enable_strict_kex || require_strict_kex) && initialKex) { + throw new JSchStrictKexException("incoming sequence number wrapped during initial KEX"); + } if (inflater != null) { // inflater.uncompress(buf); @@ -1185,7 +1295,9 @@ Buffer read(Buffer buf) throws Exception { buf.buffer = foo; buf.index = 5 + uncompress_len[0]; } else { - System.err.println("fail in inflater"); + if (getLogger().isEnabled(Logger.ERROR)) { + getLogger().log(Logger.ERROR, "fail in inflater"); + } break; } } @@ -1197,11 +1309,16 @@ Buffer read(Buffer buf) throws Exception { buf.getInt(); buf.getShort(); int reason_code = buf.getInt(); - byte[] description = buf.getString(); - byte[] language_tag = buf.getString(); - throw new JSchException("SSH_MSG_DISCONNECT: " + reason_code + " " - + Util.byte2str(description) + " " + Util.byte2str(language_tag)); + byte[] description_array = buf.getString(); + byte[] language_tag_array = buf.getString(); + String description = Util.byte2str(description_array); + String language_tag = Util.byte2str(language_tag_array); + throw new JSchSessionDisconnectException( + "SSH_MSG_DISCONNECT: " + reason_code + " " + description + " " + language_tag, + reason_code, description, language_tag); // break; + } else if (initialKex && doStrictKex) { + break; } else if (type == SSH_MSG_IGNORE) { } else if (type == SSH_MSG_UNIMPLEMENTED) { buf.rewind(); @@ -1234,8 +1351,7 @@ Buffer read(Buffer buf) throws Exception { buf.getInt(); buf.getShort(); boolean ignore = false; - String enable_server_sig_algs = getConfig("enable_server_sig_algs"); - if (!enable_server_sig_algs.equals("yes")) { + if (!enable_server_sig_algs) { ignore = true; if (getLogger().isEnabled(Logger.INFO)) { getLogger().log(Logger.INFO, @@ -1324,7 +1440,7 @@ private void start_discard(Buffer buf, Cipher cipher, MAC mac, int mac_already, } catch (IOException e) { ioe = e; if (getLogger().isEnabled(Logger.ERROR)) { - getLogger().log(Logger.ERROR, "start_discard finished early due to " + e.getMessage()); + getLogger().log(Logger.ERROR, "start_discard finished early due to " + e.getMessage(), e); } } @@ -1344,8 +1460,19 @@ byte[] getSessionId() { } private void receive_newkeys(Buffer buf, KeyExchange kex) throws Exception { - updateKeys(kex); + try { + updateKeys(kex); + } finally { + kex.clearK(); + } in_kex = false; + if (doStrictKex) { + seqi = 0; + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"); + } + } } private void updateKeys(KeyExchange kex) throws Exception { @@ -1367,7 +1494,7 @@ private void updateKeys(KeyExchange kex) throws Exception { */ buf.reset(); - buf.putMPInt(K); + buf.putByte(K); buf.putByte(H); buf.putByte((byte) 0x41); buf.putByte(session_id); @@ -1406,7 +1533,7 @@ private void updateKeys(KeyExchange kex) throws Exception { s2ccipher = cc.getDeclaredConstructor().newInstance(); while (s2ccipher.getBlockSize() > Es2c.length) { buf.reset(); - buf.putMPInt(K); + buf.putByte(K); buf.putByte(H); buf.putByte(Es2c); hash.update(buf.buffer, 0, buf.index); @@ -1435,7 +1562,7 @@ private void updateKeys(KeyExchange kex) throws Exception { c2scipher = cc.getDeclaredConstructor().newInstance(); while (c2scipher.getBlockSize() > Ec2s.length) { buf.reset(); - buf.putMPInt(K); + buf.putByte(K); buf.putByte(H); buf.putByte(Ec2s); hash.update(buf.buffer, 0, buf.index); @@ -1469,7 +1596,6 @@ private void updateKeys(KeyExchange kex) throws Exception { } } - /* * RFC 4253 7.2. Output from Key Exchange If the key length needed is longer than the output of * the HASH, the key is extended by computing HASH of the concatenation of K and H and the entire @@ -1484,7 +1610,7 @@ private byte[] expandKey(Buffer buf, byte[] K, byte[] H, byte[] key, HASH hash, int size = hash.getBlockSize(); while (result.length < required_length) { buf.reset(); - buf.putMPInt(K); + buf.putByte(K); buf.putByte(H); buf.putByte(result); hash.update(buf.buffer, 0, buf.index); @@ -1511,7 +1637,6 @@ private byte[] expandKey(Buffer buf, byte[] K, byte[] H, byte[] key, HASH hash, continue; } synchronized (c) { - if (c.rwsize < length) { try { c.notifyme++; @@ -1530,7 +1655,6 @@ private byte[] expandKey(Buffer buf, byte[] K, byte[] H, byte[] key, HASH hash, c.rwsize -= length; break; } - } if (c.close || !c.isConnected()) { throw new IOException("channel is broken"); @@ -1552,7 +1676,7 @@ private byte[] expandKey(Buffer buf, byte[] K, byte[] H, byte[] key, HASH hash, } command = packet.buffer.getCommand(); recipient = c.getRecipient(); - length -= len; + length -= (int) len; c.rwsize -= len; sendit = true; } @@ -1613,13 +1737,29 @@ void write(Packet packet) throws Exception { } private void _write(Packet packet) throws Exception { + boolean initialKex = this.initialKex; + boolean doStrictKex = this.doStrictKex; + boolean enable_strict_kex = this.enable_strict_kex; + boolean require_strict_kex = this.require_strict_kex; + boolean resetSeqo = packet.buffer.getCommand() == SSH_MSG_NEWKEYS && doStrictKex; + synchronized (lock) { encode(packet); if (io != null) { io.put(packet); - seqo++; + if (++seqo == 0 && (enable_strict_kex || require_strict_kex) && initialKex) { + throw new JSchStrictKexException("outgoing sequence number wrapped during initial KEX"); + } + if (resetSeqo) { + seqo = 0; + } } } + + if (resetSeqo && io != null && getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"); + } } Runnable thread; @@ -1642,7 +1782,7 @@ void run() { try { buf = read(buf); stimeout = 0; - } catch (InterruptedIOException/* SocketTimeoutException */ ee) { + } catch (InterruptedIOException /* SocketTimeoutException */ ee) { if (!in_kex && stimeout < serverAliveCountMax) { sendKeepAliveMsg(); stimeout++; @@ -1996,6 +2136,17 @@ public void disconnect() { } io = null; socket = null; + + // RFC 4253 6.4. the 'sequence_number' is never reset, even if keys/algorithms are renegotiated + // later. Hence we only reset these on session disconnect as the sequence has to start at zero + // for the first packet during (re)connect. + seqi = 0; + seqo = 0; + initialKex = true; + doStrictKex = false; + doExtInfo = false; + serverSigAlgs = null; + // synchronized(jsch.pool){ // jsch.pool.removeElement(this); // } @@ -2617,9 +2768,6 @@ public void setConfig(Hashtable newconf) { String key = (newkey.equals("PubkeyAcceptedKeyTypes") ? "PubkeyAcceptedAlgorithms" : newkey); String value = newconf.get(newkey); - if (key.equals("enable_server_sig_algs") && !value.equals("yes")) { - serverSigAlgs = null; - } config.put(key, value); } } @@ -2633,9 +2781,6 @@ public void setConfig(String key, String value) { if (key.equals("PubkeyAcceptedKeyTypes")) { config.put("PubkeyAcceptedAlgorithms", value); } else { - if (key.equals("enable_server_sig_algs") && !value.equals("yes")) { - serverSigAlgs = null; - } config.put(key, value); } } @@ -3052,9 +3197,14 @@ private void applyConfig() throws JSchException { checkConfig(config, "kex"); checkConfig(config, "server_host_key"); checkConfig(config, "prefer_known_host_key_types"); + checkConfig(config, "enable_server_sig_algs"); + checkConfig(config, "enable_ext_info_in_auth"); + checkConfig(config, "enable_strict_kex"); + checkConfig(config, "require_strict_kex"); checkConfig(config, "enable_pubkey_auth_query"); checkConfig(config, "try_additional_pubkey_algorithms"); checkConfig(config, "enable_auth_none"); + checkConfig(config, "use_sftp_write_flush_workaround"); checkConfig(config, "cipher.c2s"); checkConfig(config, "cipher.s2c"); @@ -3106,7 +3256,7 @@ private void applyConfig() throws JSchException { } if (ifile == null) continue; - Identity identity = IdentityFile.newInstance(ifile, null, jsch); + Identity identity = IdentityFile.newInstance(ifile, null, jsch.instLogger); ir.add(identity); } this.setIdentityRepository(ir); @@ -3138,7 +3288,6 @@ private void applyConfig() throws JSchException { if (value != null) { setConfig("ClearAllForwardings", value); } - } private void applyConfigChannel(ChannelSession channel) throws JSchException { diff --git a/src/main/java/com/jcraft/jsch/SftpATTRS.java b/src/main/java/com/jcraft/jsch/SftpATTRS.java index e7180bde..cff4c660 100644 --- a/src/main/java/com/jcraft/jsch/SftpATTRS.java +++ b/src/main/java/com/jcraft/jsch/SftpATTRS.java @@ -26,8 +26,11 @@ package com.jcraft.jsch; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; /* * uint32 flags uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE uint32 uid present only if @@ -123,13 +126,20 @@ else if ((permissions & S_IXGRP) != 0) } public String getAtimeString() { - Date date = new Date(((long) atime) * 1000L); - return (date.toString()); + return toDateString(Integer.toUnsignedLong(atime)); } public String getMtimeString() { - Date date = new Date(((long) mtime) * 1000L); - return (date.toString()); + return toDateString(Integer.toUnsignedLong(mtime)); + } + + private static DateTimeFormatter DTF = + DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT); + + static String toDateString(long epochSeconds) { + Instant instant = Instant.ofEpochSecond(epochSeconds); + ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); + return DTF.format(zdt); } public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001; diff --git a/src/main/java/com/jcraft/jsch/SftpStatVFS.java b/src/main/java/com/jcraft/jsch/SftpStatVFS.java index 257b005e..3054d6d3 100644 --- a/src/main/java/com/jcraft/jsch/SftpStatVFS.java +++ b/src/main/java/com/jcraft/jsch/SftpStatVFS.java @@ -26,9 +26,6 @@ package com.jcraft.jsch; -import java.text.SimpleDateFormat; -import java.util.Date; - public class SftpStatVFS { /* @@ -74,8 +71,8 @@ static SftpStatVFS getStatVFS(Buffer buf) { int flag = (int) buf.getLong(); statvfs.namemax = buf.getLong(); - statvfs.flag = (flag & 1/* SSH2_FXE_STATVFS_ST_RDONLY */) != 0 ? 1/* ST_RDONLY */ : 0; - statvfs.flag |= (flag & 2/* SSH2_FXE_STATVFS_ST_NOSUID */) != 0 ? 2/* ST_NOSUID */ : 0; + statvfs.flag = (flag & 1 /* SSH2_FXE_STATVFS_ST_RDONLY */) != 0 ? 1 /* ST_RDONLY */ : 0; + statvfs.flag |= (flag & 2 /* SSH2_FXE_STATVFS_ST_NOSUID */) != 0 ? 2 /* ST_NOSUID */ : 0; return statvfs; } diff --git a/src/main/java/com/jcraft/jsch/Slf4jLogger.java b/src/main/java/com/jcraft/jsch/Slf4jLogger.java index c24c1563..0fccd2c9 100644 --- a/src/main/java/com/jcraft/jsch/Slf4jLogger.java +++ b/src/main/java/com/jcraft/jsch/Slf4jLogger.java @@ -3,9 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - * JSch logger to log entries using the SLF4J framework - */ +/** JSch logger to log entries using the SLF4J framework */ public class Slf4jLogger implements com.jcraft.jsch.Logger { private static final Logger logger = LoggerFactory.getLogger(JSch.class); diff --git a/src/main/java/com/jcraft/jsch/SocketFactory.java b/src/main/java/com/jcraft/jsch/SocketFactory.java index f0a26c3f..e31f340f 100644 --- a/src/main/java/com/jcraft/jsch/SocketFactory.java +++ b/src/main/java/com/jcraft/jsch/SocketFactory.java @@ -26,8 +26,11 @@ package com.jcraft.jsch; -import java.net.*; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.UnknownHostException; public interface SocketFactory { public Socket createSocket(String host, int port) throws IOException, UnknownHostException; diff --git a/src/main/java/com/jcraft/jsch/UnixDomainSocketFactory.java b/src/main/java/com/jcraft/jsch/UnixDomainSocketFactory.java index 280cdbcd..f5dcf344 100644 --- a/src/main/java/com/jcraft/jsch/UnixDomainSocketFactory.java +++ b/src/main/java/com/jcraft/jsch/UnixDomainSocketFactory.java @@ -26,9 +26,6 @@ package com.jcraft.jsch; -import com.jcraft.jsch.AgentProxyException; -import com.jcraft.jsch.USocketFactory; - import java.io.IOException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; diff --git a/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java b/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java index 7a10780e..92e9cd76 100644 --- a/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java +++ b/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java @@ -221,5 +221,3 @@ public boolean start(Session session) throws Exception { return false; } } - - diff --git a/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java b/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java index c7aff920..2fbabdbe 100644 --- a/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java +++ b/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java @@ -26,6 +26,8 @@ package com.jcraft.jsch; +import java.util.Locale; + class UserAuthKeyboardInteractive extends UserAuth { @Override public boolean start(Session session) throws Exception { @@ -129,7 +131,7 @@ public boolean start(Session session) throws Exception { byte[][] response = null; if (password != null && prompt.length == 1 && !echo[0] - && prompt[0].toLowerCase().indexOf("password:") >= 0) { + && prompt[0].toLowerCase(Locale.ROOT).indexOf("password:") >= 0) { response = new byte[1][]; response[0] = password; password = null; @@ -141,7 +143,7 @@ public boolean start(Session session) throws Exception { if (_response != null) { response = new byte[_response.length][]; for (int i = 0; i < _response.length; i++) { - response[i] = Util.str2byte(_response[i]); + response[i] = _response[i] != null ? Util.str2byte(_response[i]) : Util.empty; } } } diff --git a/src/main/java/com/jcraft/jsch/UserAuthNone.java b/src/main/java/com/jcraft/jsch/UserAuthNone.java index 24444063..a306a5cc 100644 --- a/src/main/java/com/jcraft/jsch/UserAuthNone.java +++ b/src/main/java/com/jcraft/jsch/UserAuthNone.java @@ -26,20 +26,21 @@ package com.jcraft.jsch; -class UserAuthNone extends UserAuth { - private static final int SSH_MSG_SERVICE_ACCEPT = 6; - private String methods = null; +public class UserAuthNone extends UserAuth { + protected static final int SSH_MSG_SERVICE_REQUEST = 5; + protected static final int SSH_MSG_SERVICE_ACCEPT = 6; + + private String methods; @Override public boolean start(Session session) throws Exception { super.start(session); - // send // byte SSH_MSG_SERVICE_REQUEST(5) // string service name "ssh-userauth" packet.reset(); - buf.putByte((byte) Session.SSH_MSG_SERVICE_REQUEST); + buf.putByte((byte) SSH_MSG_SERVICE_REQUEST); buf.putString(Util.str2byte("ssh-userauth")); session.write(packet); @@ -73,7 +74,7 @@ public boolean start(Session session) throws Exception { // string service name ("ssh-connection") // string "none" packet.reset(); - buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + buf.putByte((byte) UserAuth.SSH_MSG_USERAUTH_REQUEST); buf.putString(_username); buf.putString(Util.str2byte("ssh-connection")); buf.putString(Util.str2byte("none")); @@ -83,10 +84,10 @@ public boolean start(Session session) throws Exception { buf = session.read(buf); command = buf.getCommand() & 0xff; - if (command == SSH_MSG_USERAUTH_SUCCESS) { + if (command == UserAuth.SSH_MSG_USERAUTH_SUCCESS) { return true; } - if (command == SSH_MSG_USERAUTH_BANNER) { + if (command == UserAuth.SSH_MSG_USERAUTH_BANNER) { buf.getInt(); buf.getByte(); buf.getByte(); @@ -101,22 +102,22 @@ public boolean start(Session session) throws Exception { } continue loop; } - if (command == SSH_MSG_USERAUTH_FAILURE) { + if (command == UserAuth.SSH_MSG_USERAUTH_FAILURE) { buf.getInt(); buf.getByte(); buf.getByte(); byte[] foo = buf.getString(); int partial_success = buf.getByte(); - methods = Util.byte2str(foo); - // System.err.println("UserAuthNONE: "+methods+ - // " partial_success:"+(partial_success!=0)); - // if(partial_success!=0){ + setMethods(Util.byte2str(foo)); + // System.err.println("UserAuthNone: " + methods + " partial_success:" + (partial_success != + // 0)); + // if (partial_success != 0) { // throw new JSchPartialAuthException(new String(foo)); // } break; } else { - // System.err.println("USERAUTH fail ("+command+")"); + // System.err.println("USERAUTH fail (" + command + ")"); throw new JSchException("USERAUTH fail (" + command + ")"); } } @@ -124,7 +125,11 @@ public boolean start(Session session) throws Exception { return false; } - String getMethods() { + protected String getMethods() { return methods; } + + protected void setMethods(String methods) { + this.methods = methods; + } } diff --git a/src/main/java/com/jcraft/jsch/UserAuthPassword.java b/src/main/java/com/jcraft/jsch/UserAuthPassword.java index 6d75da94..375f11e4 100644 --- a/src/main/java/com/jcraft/jsch/UserAuthPassword.java +++ b/src/main/java/com/jcraft/jsch/UserAuthPassword.java @@ -127,7 +127,7 @@ public boolean start(Session session) throws Exception { throw new JSchAuthCancelException("password"); } - byte[] newpassword = Util.str2byte(response[0]); + byte[] newpassword = response[0] != null ? Util.str2byte(response[0]) : Util.empty; // send // byte SSH_MSG_USERAUTH_REQUEST(50) @@ -174,7 +174,6 @@ public boolean start(Session session) throws Exception { Util.bzero(password); password = null; } - } } finally { diff --git a/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java index 8ab97c1c..5efa84e4 100644 --- a/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java +++ b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java @@ -26,7 +26,12 @@ package com.jcraft.jsch; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; class UserAuthPublicKey extends UserAuth { diff --git a/src/main/java/com/jcraft/jsch/Util.java b/src/main/java/com/jcraft/jsch/Util.java index 03aefbb2..b7ef747d 100644 --- a/src/main/java/com/jcraft/jsch/Util.java +++ b/src/main/java/com/jcraft/jsch/Util.java @@ -26,10 +26,13 @@ package com.jcraft.jsch; -import java.net.Socket; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Vector; @@ -149,7 +152,7 @@ static boolean glob(byte[] pattern, byte[] name) { return glob0(pattern, 0, name, 0); } - static private boolean glob0(byte[] pattern, int pattern_index, byte[] name, int name_index) { + private static boolean glob0(byte[] pattern, int pattern_index, byte[] name, int name_index) { if (name.length > 0 && name[0] == '.') { if (pattern.length > 0 && pattern[0] == '.') { if (pattern.length == 2 && pattern[1] == '*') @@ -161,7 +164,7 @@ static private boolean glob0(byte[] pattern, int pattern_index, byte[] name, int return glob(pattern, pattern_index, name, name_index); } - static private boolean glob(byte[] pattern, int pattern_index, byte[] name, int name_index) { + private static boolean glob(byte[] pattern, int pattern_index, byte[] name, int name_index) { // System.err.println("glob: "+new String(pattern)+", "+pattern_index+" "+new String(name)+", // "+name_index); @@ -368,55 +371,20 @@ static boolean array_equals(byte[] foo, byte bar[]) { } static Socket createSocket(String host, int port, int timeout) throws JSchException { - Socket socket = null; - if (timeout == 0) { - try { - socket = new Socket(host, port); - return socket; - } catch (Exception e) { - String message = e.toString(); - throw new JSchException(message, e); - } - } - final String _host = host; - final int _port = port; - final Socket[] sockp = new Socket[1]; - final Exception[] ee = new Exception[1]; - String message = ""; - Thread tmp = new Thread(() -> { - sockp[0] = null; - try { - sockp[0] = new Socket(_host, _port); - } catch (Exception e) { - ee[0] = e; - if (sockp[0] != null && sockp[0].isConnected()) { - try { - sockp[0].close(); - } catch (Exception eee) { - } - } - sockp[0] = null; - } - }); - tmp.setName("Opening Socket " + host); - tmp.start(); + Socket socket = new Socket(); try { - tmp.join(timeout); - message = "timeout: "; - } catch (InterruptedException eee) { - } - if (sockp[0] != null && sockp[0].isConnected()) { - socket = sockp[0]; - } else { - message += "socket is not established"; - if (ee[0] != null) { - message = ee[0].toString(); + socket.connect(new InetSocketAddress(host, port), timeout); + return socket; + } catch (Exception e) { + try { + socket.close(); + } catch (Exception ignore) { } - tmp.interrupt(); - tmp = null; - throw new JSchException(message, ee[0]); + + String message = + e instanceof SocketTimeoutException ? "timeout: socket is not established" : e.toString(); + throw new JSchException(message, e); } - return socket; } static byte[] str2byte(String str, Charset encoding) { @@ -512,8 +480,7 @@ private static int skipUTF8Char(byte b) { static byte[] fromFile(String _file) throws IOException { _file = checkTilde(_file); File file = new File(_file); - FileInputStream fis = new FileInputStream(_file); - try { + try (InputStream fis = new FileInputStream(_file)) { byte[] result = new byte[(int) (file.length())]; int len = 0; while (true) { @@ -522,11 +489,7 @@ static byte[] fromFile(String _file) throws IOException { break; len += i; } - fis.close(); return result; - } finally { - if (fis != null) - fis.close(); } } diff --git a/src/main/java/com/jcraft/jsch/annotations/SuppressForbiddenApi.java b/src/main/java/com/jcraft/jsch/annotations/SuppressForbiddenApi.java new file mode 100644 index 00000000..fb9a3569 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/annotations/SuppressForbiddenApi.java @@ -0,0 +1,13 @@ +package com.jcraft.jsch.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, + ElementType.PARAMETER, ElementType.TYPE}) +public @interface SuppressForbiddenApi { + String[] value(); +} diff --git a/src/main/java/com/jcraft/jsch/bc/CAST128CBC.java b/src/main/java/com/jcraft/jsch/bc/CAST128CBC.java index d3b6c933..62071aaa 100644 --- a/src/main/java/com/jcraft/jsch/bc/CAST128CBC.java +++ b/src/main/java/com/jcraft/jsch/bc/CAST128CBC.java @@ -28,9 +28,11 @@ import com.jcraft.jsch.Cipher; import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.engines.CAST5Engine; import org.bouncycastle.crypto.modes.CBCBlockCipher; -import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; public class CAST128CBC implements Cipher { private static final int ivsize = 8; @@ -64,7 +66,7 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { try { ParametersWithIV keyspec = new ParametersWithIV(new KeyParameter(key, 0, key.length), iv, 0, iv.length); - cipher = new BufferedBlockCipher(new CBCBlockCipher(new CAST5Engine())); + cipher = new DefaultBufferedBlockCipher(CBCBlockCipher.newInstance(new CAST5Engine())); cipher.init(mode == ENCRYPT_MODE, keyspec); } catch (Exception e) { cipher = null; diff --git a/src/main/java/com/jcraft/jsch/bc/CAST128CTR.java b/src/main/java/com/jcraft/jsch/bc/CAST128CTR.java index 986858dd..f1821012 100644 --- a/src/main/java/com/jcraft/jsch/bc/CAST128CTR.java +++ b/src/main/java/com/jcraft/jsch/bc/CAST128CTR.java @@ -28,13 +28,15 @@ import com.jcraft.jsch.Cipher; import org.bouncycastle.crypto.engines.CAST5Engine; +import org.bouncycastle.crypto.modes.CTRModeCipher; import org.bouncycastle.crypto.modes.SICBlockCipher; -import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; public class CAST128CTR implements Cipher { private static final int ivsize = 8; private static final int bsize = 16; - private SICBlockCipher cipher; + private CTRModeCipher cipher; @Override public int getIVSize() { @@ -63,7 +65,7 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { try { ParametersWithIV keyspec = new ParametersWithIV(new KeyParameter(key, 0, key.length), iv, 0, iv.length); - cipher = new SICBlockCipher(new CAST5Engine()); + cipher = SICBlockCipher.newInstance(new CAST5Engine()); cipher.init(mode == ENCRYPT_MODE, keyspec); } catch (Exception e) { cipher = null; diff --git a/src/main/java/com/jcraft/jsch/bc/ChaCha20Poly1305.java b/src/main/java/com/jcraft/jsch/bc/ChaCha20Poly1305.java index a088fa84..a0452a72 100644 --- a/src/main/java/com/jcraft/jsch/bc/ChaCha20Poly1305.java +++ b/src/main/java/com/jcraft/jsch/bc/ChaCha20Poly1305.java @@ -31,7 +31,8 @@ import javax.crypto.AEADBadTagException; import org.bouncycastle.crypto.engines.ChaChaEngine; import org.bouncycastle.crypto.macs.Poly1305; -import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; public class ChaCha20Poly1305 implements Cipher { // Actually the block size, not IV size diff --git a/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java b/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java index bf57cc27..8be3d381 100644 --- a/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java +++ b/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java @@ -26,8 +26,10 @@ package com.jcraft.jsch.bc; -import java.security.*; -import org.bouncycastle.crypto.params.*; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters; public class KeyPairGenEdDSA implements com.jcraft.jsch.KeyPairGenEdDSA { byte[] prv; // private diff --git a/src/main/java/com/jcraft/jsch/bc/SEEDCBC.java b/src/main/java/com/jcraft/jsch/bc/SEEDCBC.java index dd032fde..3c9d2d4c 100644 --- a/src/main/java/com/jcraft/jsch/bc/SEEDCBC.java +++ b/src/main/java/com/jcraft/jsch/bc/SEEDCBC.java @@ -28,9 +28,11 @@ import com.jcraft.jsch.Cipher; import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.engines.SEEDEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; -import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; public class SEEDCBC implements Cipher { private static final int ivsize = 16; @@ -64,7 +66,7 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { try { ParametersWithIV keyspec = new ParametersWithIV(new KeyParameter(key, 0, key.length), iv, 0, iv.length); - cipher = new BufferedBlockCipher(new CBCBlockCipher(new SEEDEngine())); + cipher = new DefaultBufferedBlockCipher(CBCBlockCipher.newInstance(new SEEDEngine())); cipher.init(mode == ENCRYPT_MODE, keyspec); } catch (Exception e) { cipher = null; diff --git a/src/main/java/com/jcraft/jsch/bc/SNTRUP761.java b/src/main/java/com/jcraft/jsch/bc/SNTRUP761.java new file mode 100644 index 00000000..b1f2677b --- /dev/null +++ b/src/main/java/com/jcraft/jsch/bc/SNTRUP761.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.bc; + +import com.jcraft.jsch.KEM; +import com.jcraft.jsch.annotations.SuppressForbiddenApi; +import java.lang.reflect.Constructor; +import java.security.SecureRandom; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimeKEMExtractor; +import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimeKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimeKeyPairGenerator; +import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimeParameters; +import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimePrivateKeyParameters; +import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimePublicKeyParameters; + +public class SNTRUP761 implements KEM { + SNTRUPrimeKEMExtractor extractor; + SNTRUPrimePublicKeyParameters publicKey; + + @Override + public void init() throws Exception { + SNTRUPrimeKeyPairGenerator kpg = new SNTRUPrimeKeyPairGenerator(); + kpg.init(new SNTRUPrimeKeyGenerationParameters(new SecureRandom(), sntrup761())); + AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + extractor = new SNTRUPrimeKEMExtractor((SNTRUPrimePrivateKeyParameters) kp.getPrivate()); + publicKey = (SNTRUPrimePublicKeyParameters) kp.getPublic(); + } + + @Override + public byte[] getPublicKey() throws Exception { + return publicKey.getEncoded(); + } + + @Override + public byte[] decapsulate(byte[] encapsulation) throws Exception { + return extractor.extractSecret(encapsulation); + } + + // Bouncy Castle before 1.78 defines sharedKeyBytes differently than OpenSSH (16 instead of 32) + // https://github.com/bcgit/bc-java/issues/1554 + // https://github.com/bcgit/bc-java/commit/db3ae60 + @SuppressForbiddenApi("jdk-reflection") + static SNTRUPrimeParameters sntrup761() throws Exception { + if (SNTRUPrimeParameters.sntrup761.getSessionKeySize() == 32 * 8) { + return SNTRUPrimeParameters.sntrup761; + } + Constructor c = + SNTRUPrimeParameters.class.getDeclaredConstructor(String.class, int.class, int.class, + int.class, int.class, int.class, int.class, int.class, int.class); + c.setAccessible(true); + return c.newInstance("sntrup761", 761, 4591, 286, 1158, 1007, 1158, 1763, 32); + } +} diff --git a/src/main/java/com/jcraft/jsch/bc/SignatureEdDSA.java b/src/main/java/com/jcraft/jsch/bc/SignatureEdDSA.java index cdf33e5e..df58207e 100644 --- a/src/main/java/com/jcraft/jsch/bc/SignatureEdDSA.java +++ b/src/main/java/com/jcraft/jsch/bc/SignatureEdDSA.java @@ -26,13 +26,18 @@ package com.jcraft.jsch.bc; +import com.jcraft.jsch.Buffer; import java.nio.charset.StandardCharsets; -import java.security.*; -import java.util.Arrays; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; import org.bouncycastle.crypto.Signer; -import org.bouncycastle.crypto.params.*; -import org.bouncycastle.crypto.signers.*; -import com.jcraft.jsch.Buffer; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; +import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed448PublicKeyParameters; +import org.bouncycastle.crypto.signers.Ed25519Signer; +import org.bouncycastle.crypto.signers.Ed448Signer; abstract class SignatureEdDSA implements com.jcraft.jsch.SignatureEdDSA { diff --git a/src/main/java/com/jcraft/jsch/bc/TwofishCBC.java b/src/main/java/com/jcraft/jsch/bc/TwofishCBC.java index 716de0a5..2a875497 100644 --- a/src/main/java/com/jcraft/jsch/bc/TwofishCBC.java +++ b/src/main/java/com/jcraft/jsch/bc/TwofishCBC.java @@ -28,9 +28,11 @@ import com.jcraft.jsch.Cipher; import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.engines.TwofishEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; -import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; abstract class TwofishCBC implements Cipher { private static final int ivsize = 16; @@ -59,7 +61,7 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { try { ParametersWithIV keyspec = new ParametersWithIV(new KeyParameter(key, 0, key.length), iv, 0, iv.length); - cipher = new BufferedBlockCipher(new CBCBlockCipher(new TwofishEngine())); + cipher = new DefaultBufferedBlockCipher(CBCBlockCipher.newInstance(new TwofishEngine())); cipher.init(mode == ENCRYPT_MODE, keyspec); } catch (Exception e) { cipher = null; diff --git a/src/main/java/com/jcraft/jsch/bc/TwofishCTR.java b/src/main/java/com/jcraft/jsch/bc/TwofishCTR.java index e86c7285..ab6ad230 100644 --- a/src/main/java/com/jcraft/jsch/bc/TwofishCTR.java +++ b/src/main/java/com/jcraft/jsch/bc/TwofishCTR.java @@ -28,12 +28,14 @@ import com.jcraft.jsch.Cipher; import org.bouncycastle.crypto.engines.TwofishEngine; +import org.bouncycastle.crypto.modes.CTRModeCipher; import org.bouncycastle.crypto.modes.SICBlockCipher; -import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; abstract class TwofishCTR implements Cipher { private static final int ivsize = 16; - private SICBlockCipher cipher; + private CTRModeCipher cipher; @Override public int getIVSize() { @@ -58,7 +60,7 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { try { ParametersWithIV keyspec = new ParametersWithIV(new KeyParameter(key, 0, key.length), iv, 0, iv.length); - cipher = new SICBlockCipher(new TwofishEngine()); + cipher = SICBlockCipher.newInstance(new TwofishEngine()); cipher.init(mode == ENCRYPT_MODE, keyspec); } catch (Exception e) { cipher = null; diff --git a/src/main/java/com/jcraft/jsch/bc/XDH.java b/src/main/java/com/jcraft/jsch/bc/XDH.java index 7f70b9a2..89db552c 100644 --- a/src/main/java/com/jcraft/jsch/bc/XDH.java +++ b/src/main/java/com/jcraft/jsch/bc/XDH.java @@ -26,9 +26,13 @@ package com.jcraft.jsch.bc; -import java.security.*; -import java.util.Arrays; -import org.bouncycastle.crypto.params.*; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.X25519PublicKeyParameters; +import org.bouncycastle.crypto.params.X448PrivateKeyParameters; +import org.bouncycastle.crypto.params.X448PublicKeyParameters; public class XDH implements com.jcraft.jsch.XDH { byte[] Q_array; diff --git a/src/main/java/com/jcraft/jsch/jbcrypt/BCrypt.java b/src/main/java/com/jcraft/jsch/jbcrypt/BCrypt.java index c3741aec..332f879b 100644 --- a/src/main/java/com/jcraft/jsch/jbcrypt/BCrypt.java +++ b/src/main/java/com/jcraft/jsch/jbcrypt/BCrypt.java @@ -23,20 +23,25 @@ /** * BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in "A * Future-Adaptable Password Scheme" by Niels Provos and David Mazieres. + * *

    * This password hashing system tries to thwart off-line password cracking using a * computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher. The work * factor of the algorithm is parameterised, so it can be increased as computers get faster. + * *

    * Usage is really simple. To hash a password for the first time, call the hashpw method with a * random salt, like this: + * *

    * * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
    *
    + * *

    * To check whether a plaintext password matches one that has been hashed previously, use the * checkpw method: + * *

    * * if (BCrypt.checkpw(candidate_password, stored_hash))
    @@ -44,14 +49,17 @@ * else
    *     System.out.println("It does not match");
    *
    + * *

    * The gensalt() method takes an optional parameter (log_rounds) that determines the computational * complexity of the hashing: + * *

    * * String strong_salt = BCrypt.gensalt(10)
    * String stronger_salt = BCrypt.gensalt(12)
    *
    + * *

    * The amount of work increases exponentially (2**log_rounds), so each increment is twice as much * work. The default log_rounds is 10, and the valid range is 4 to 30. @@ -226,17 +234,17 @@ public class BCrypt { // bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls // this "ciphertext", but it is really plaintext or an IV. We keep // the name to make code comparison easier. - static private final int bf_crypt_ciphertext[] = + private static final int bf_crypt_ciphertext[] = {0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274}; // Table for Base64 encoding - static private final char base64_code[] = {'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + private static final char base64_code[] = {'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; // Table for Base64 decoding - static private final byte index_64[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + private static final byte index_64[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, @@ -325,7 +333,7 @@ private static byte[] decode_base64(String s, int maxolen) throws IllegalArgumen if (c1 == -1 || c2 == -1) break; o = (byte) (c1 << 2); - o |= (c2 & 0x30) >> 4; + o |= (byte) ((c2 & 0x30) >> 4); rs.append((char) o); if (++olen >= maxolen || off >= slen) break; @@ -333,7 +341,7 @@ private static byte[] decode_base64(String s, int maxolen) throws IllegalArgumen if (c3 == -1) break; o = (byte) ((c2 & 0x0f) << 4); - o |= (c3 & 0x3c) >> 2; + o |= (byte) ((c3 & 0x3c) >> 2); rs.append((char) o); if (++olen >= maxolen || off >= slen) break; @@ -400,9 +408,7 @@ private static int streamtoword(byte data[], int offp[]) { return word; } - /** - * Initialise the Blowfish key schedule - */ + /** Initialise the Blowfish key schedule */ private void init_key() { P = P_orig.clone(); S = S_orig.clone(); @@ -468,9 +474,7 @@ private void ekskey(byte data[], byte key[]) { } } - /** - * Compatibility with new OpenBSD function. - */ + /** Compatibility with new OpenBSD function. */ public void hash(byte[] hpass, byte[] hsalt, byte[] output) { init_key(); ekskey(hsalt, hpass); @@ -496,9 +500,7 @@ public void hash(byte[] hpass, byte[] hsalt, byte[] output) { } } - /** - * Compatibility with new OpenBSD function. - */ + /** Compatibility with new OpenBSD function. */ public void pbkdf(byte[] password, byte[] salt, int rounds, byte[] output) { try { MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); @@ -710,7 +712,7 @@ public static boolean checkpw(String plaintext, String hashed) { return false; byte ret = 0; for (int i = 0; i < try_bytes.length; i++) - ret |= hashed_bytes[i] ^ try_bytes[i]; + ret |= (byte) (hashed_bytes[i] ^ try_bytes[i]); return ret == 0; } } diff --git a/src/main/java/com/jcraft/jsch/jce/AES128CBC.java b/src/main/java/com/jcraft/jsch/jce/AES128CBC.java index 62bb4ba4..860f4571 100644 --- a/src/main/java/com/jcraft/jsch/jce/AES128CBC.java +++ b/src/main/java/com/jcraft/jsch/jce/AES128CBC.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class AES128CBC implements Cipher { +public class AES128CBC implements com.jcraft.jsch.Cipher { private static final int ivsize = 16; private static final int bsize = 16; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,7 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -61,9 +61,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { try { SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); - cipher = javax.crypto.Cipher.getInstance("AES/CBC/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), keyspec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/AES128CTR.java b/src/main/java/com/jcraft/jsch/jce/AES128CTR.java index 38c0c79a..1cd5dd80 100644 --- a/src/main/java/com/jcraft/jsch/jce/AES128CTR.java +++ b/src/main/java/com/jcraft/jsch/jce/AES128CTR.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class AES128CTR implements Cipher { +public class AES128CTR implements com.jcraft.jsch.Cipher { private static final int ivsize = 16; private static final int bsize = 16; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,7 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -61,9 +61,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { try { SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); - cipher = javax.crypto.Cipher.getInstance("AES/CTR/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), keyspec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/AES192CBC.java b/src/main/java/com/jcraft/jsch/jce/AES192CBC.java index 1dcb8639..1d1e16bb 100644 --- a/src/main/java/com/jcraft/jsch/jce/AES192CBC.java +++ b/src/main/java/com/jcraft/jsch/jce/AES192CBC.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class AES192CBC implements Cipher { +public class AES192CBC implements com.jcraft.jsch.Cipher { private static final int ivsize = 16; private static final int bsize = 24; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,7 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -60,9 +60,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); - cipher = javax.crypto.Cipher.getInstance("AES/CBC/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), keyspec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/AES192CTR.java b/src/main/java/com/jcraft/jsch/jce/AES192CTR.java index 12b21066..8d266391 100644 --- a/src/main/java/com/jcraft/jsch/jce/AES192CTR.java +++ b/src/main/java/com/jcraft/jsch/jce/AES192CTR.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class AES192CTR implements Cipher { +public class AES192CTR implements com.jcraft.jsch.Cipher { private static final int ivsize = 16; private static final int bsize = 24; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,7 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -60,9 +60,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); - cipher = javax.crypto.Cipher.getInstance("AES/CTR/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), keyspec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/AES256CBC.java b/src/main/java/com/jcraft/jsch/jce/AES256CBC.java index 8fe2028b..9a736c41 100644 --- a/src/main/java/com/jcraft/jsch/jce/AES256CBC.java +++ b/src/main/java/com/jcraft/jsch/jce/AES256CBC.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class AES256CBC implements Cipher { +public class AES256CBC implements com.jcraft.jsch.Cipher { private static final int ivsize = 16; private static final int bsize = 32; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,7 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -60,9 +60,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); - cipher = javax.crypto.Cipher.getInstance("AES/CBC/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), keyspec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/AES256CTR.java b/src/main/java/com/jcraft/jsch/jce/AES256CTR.java index d78b2daa..c1f72dcc 100644 --- a/src/main/java/com/jcraft/jsch/jce/AES256CTR.java +++ b/src/main/java/com/jcraft/jsch/jce/AES256CTR.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class AES256CTR implements Cipher { +public class AES256CTR implements com.jcraft.jsch.Cipher { private static final int ivsize = 16; private static final int bsize = 32; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,7 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -60,9 +60,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); - cipher = javax.crypto.Cipher.getInstance("AES/CTR/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), keyspec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/AESGCM.java b/src/main/java/com/jcraft/jsch/jce/AESGCM.java index 7a521ceb..ae94e7c3 100644 --- a/src/main/java/com/jcraft/jsch/jce/AESGCM.java +++ b/src/main/java/com/jcraft/jsch/jce/AESGCM.java @@ -26,15 +26,16 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; import java.nio.ByteBuffer; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; -abstract class AESGCM implements Cipher { +abstract class AESGCM implements com.jcraft.jsch.Cipher { // Actually the block size, not IV size private static final int ivsize = 16; private static final int tagsize = 16; - private javax.crypto.Cipher cipher; + private Cipher cipher; private SecretKeySpec keyspec; private int mode; private ByteBuffer iv; @@ -52,7 +53,6 @@ public int getTagSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (iv.length > 12) { tmp = new byte[12]; @@ -65,13 +65,13 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { System.arraycopy(key, 0, tmp, 0, tmp.length); key = tmp; } - this.mode = ((mode == ENCRYPT_MODE) ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE); + this.mode = + ((mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE) ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE); this.iv = ByteBuffer.wrap(iv); this.initcounter = this.iv.getLong(4); try { keyspec = new SecretKeySpec(key, "AES"); - cipher = javax.crypto.Cipher.getInstance("AES/GCM/" + pad); + cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(this.mode, keyspec, new GCMParameterSpec(tagsize * 8, iv)); } catch (Exception e) { cipher = null; diff --git a/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java b/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java index d2a71fd2..5b6ea72f 100644 --- a/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java +++ b/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java @@ -26,14 +26,13 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; -public class ARCFOUR implements Cipher { +public class ARCFOUR implements com.jcraft.jsch.Cipher { private static final int ivsize = 8; private static final int bsize = 16; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -47,7 +46,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; byte[] tmp; if (key.length > bsize) { tmp = new byte[bsize]; @@ -56,10 +54,11 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { - cipher = javax.crypto.Cipher.getInstance("RC4"); + cipher = Cipher.getInstance("RC4"); SecretKeySpec _key = new SecretKeySpec(key, "RC4"); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), _key); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java b/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java index b0962fea..7d10fafe 100644 --- a/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java +++ b/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java @@ -26,15 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; -public class ARCFOUR128 implements Cipher { +public class ARCFOUR128 implements com.jcraft.jsch.Cipher { private static final int ivsize = 8; private static final int bsize = 16; private static final int skip = 1536; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -55,10 +54,11 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { key = tmp; } try { - cipher = javax.crypto.Cipher.getInstance("RC4"); + cipher = Cipher.getInstance("RC4"); SecretKeySpec _key = new SecretKeySpec(key, "RC4"); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), _key); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key); byte[] foo = new byte[1]; for (int i = 0; i < skip; i++) { cipher.update(foo, 0, 1, foo, 0); diff --git a/src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java b/src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java index 6e160382..5f822a1a 100644 --- a/src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java +++ b/src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java @@ -26,15 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; -public class ARCFOUR256 implements Cipher { +public class ARCFOUR256 implements com.jcraft.jsch.Cipher { private static final int ivsize = 8; private static final int bsize = 32; private static final int skip = 1536; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -55,10 +54,11 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { key = tmp; } try { - cipher = javax.crypto.Cipher.getInstance("RC4"); + cipher = Cipher.getInstance("RC4"); SecretKeySpec _key = new SecretKeySpec(key, "RC4"); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), _key); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key); byte[] foo = new byte[1]; for (int i = 0; i < skip; i++) { cipher.update(foo, 0, 1, foo, 0); diff --git a/src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java b/src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java index d3c048ba..443993e3 100644 --- a/src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java +++ b/src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class BlowfishCBC implements Cipher { +public class BlowfishCBC implements com.jcraft.jsch.Cipher { private static final int ivsize = 8; private static final int bsize = 16; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,8 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; - // if(padding) pad="PKCS5Padding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -61,9 +60,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish"); - cipher = javax.crypto.Cipher.getInstance("Blowfish/CBC/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), skeySpec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("Blowfish/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); } catch (Exception e) { throw e; } diff --git a/src/main/java/com/jcraft/jsch/jce/BlowfishCTR.java b/src/main/java/com/jcraft/jsch/jce/BlowfishCTR.java index 04978e8d..40b52d1e 100644 --- a/src/main/java/com/jcraft/jsch/jce/BlowfishCTR.java +++ b/src/main/java/com/jcraft/jsch/jce/BlowfishCTR.java @@ -26,13 +26,14 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; -public class BlowfishCTR implements Cipher { +public class BlowfishCTR implements com.jcraft.jsch.Cipher { private static final int ivsize = 8; private static final int bsize = 32; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -46,8 +47,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; - // if(padding) pad="PKCS5Padding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -61,9 +60,10 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish"); - cipher = javax.crypto.Cipher.getInstance("Blowfish/CTR/" + pad); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), skeySpec, new IvParameterSpec(iv)); + cipher = Cipher.getInstance("Blowfish/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); } catch (Exception e) { throw e; } diff --git a/src/main/java/com/jcraft/jsch/jce/DH.java b/src/main/java/com/jcraft/jsch/jce/DH.java index 114102ea..c9673e63 100644 --- a/src/main/java/com/jcraft/jsch/jce/DH.java +++ b/src/main/java/com/jcraft/jsch/jce/DH.java @@ -26,12 +26,16 @@ package com.jcraft.jsch.jce; -import java.math.BigInteger; -import java.security.*; -import javax.crypto.*; -import javax.crypto.interfaces.*; -import javax.crypto.spec.*; import com.jcraft.jsch.JSchException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import javax.crypto.KeyAgreement; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPublicKeySpec; public class DH implements com.jcraft.jsch.DH { BigInteger p; @@ -39,8 +43,6 @@ public class DH implements com.jcraft.jsch.DH { BigInteger e; // my public key byte[] e_array; BigInteger f; // your public key - BigInteger K; // shared secret key - byte[] K_array; private KeyPairGenerator myKpairGen; private KeyAgreement myKeyAgree; @@ -66,17 +68,11 @@ public byte[] getE() throws Exception { @Override public byte[] getK() throws Exception { - if (K == null) { - KeyFactory myKeyFac = KeyFactory.getInstance("DH"); - DHPublicKeySpec keySpec = new DHPublicKeySpec(f, p, g); - PublicKey yourPubKey = myKeyFac.generatePublic(keySpec); - myKeyAgree.doPhase(yourPubKey, true); - byte[] mySharedSecret = myKeyAgree.generateSecret(); - K = new BigInteger(1, mySharedSecret); - K_array = K.toByteArray(); - K_array = mySharedSecret; - } - return K_array; + KeyFactory myKeyFac = KeyFactory.getInstance("DH"); + DHPublicKeySpec keySpec = new DHPublicKeySpec(f, p, g); + PublicKey yourPubKey = myKeyFac.generatePublic(keySpec); + myKeyAgree.doPhase(yourPubKey, true); + return myKeyAgree.generateSecret(); } @Override diff --git a/src/main/java/com/jcraft/jsch/jce/ECDH256.java b/src/main/java/com/jcraft/jsch/jce/ECDH256.java index 7a40ab87..872a8b95 100644 --- a/src/main/java/com/jcraft/jsch/jce/ECDH256.java +++ b/src/main/java/com/jcraft/jsch/jce/ECDH256.java @@ -26,7 +26,7 @@ package com.jcraft.jsch.jce; -public class ECDH256 extends ECDHN implements com.jcraft.jsch.ECDH { +public class ECDH256 extends ECDHN { public void init() throws Exception { super.init(256); } diff --git a/src/main/java/com/jcraft/jsch/jce/ECDH384.java b/src/main/java/com/jcraft/jsch/jce/ECDH384.java index 85b57a0f..8477f040 100644 --- a/src/main/java/com/jcraft/jsch/jce/ECDH384.java +++ b/src/main/java/com/jcraft/jsch/jce/ECDH384.java @@ -26,7 +26,7 @@ package com.jcraft.jsch.jce; -public class ECDH384 extends ECDHN implements com.jcraft.jsch.ECDH { +public class ECDH384 extends ECDHN { public void init() throws Exception { super.init(384); } diff --git a/src/main/java/com/jcraft/jsch/jce/ECDH521.java b/src/main/java/com/jcraft/jsch/jce/ECDH521.java index 42f3a393..77f6237b 100644 --- a/src/main/java/com/jcraft/jsch/jce/ECDH521.java +++ b/src/main/java/com/jcraft/jsch/jce/ECDH521.java @@ -26,7 +26,7 @@ package com.jcraft.jsch.jce; -public class ECDH521 extends ECDHN implements com.jcraft.jsch.ECDH { +public class ECDH521 extends ECDHN { public void init() throws Exception { super.init(521); } diff --git a/src/main/java/com/jcraft/jsch/jce/ECDHN.java b/src/main/java/com/jcraft/jsch/jce/ECDHN.java index 9cca4b35..84f9e164 100644 --- a/src/main/java/com/jcraft/jsch/jce/ECDHN.java +++ b/src/main/java/com/jcraft/jsch/jce/ECDHN.java @@ -26,13 +26,19 @@ package com.jcraft.jsch.jce; +import com.jcraft.jsch.ECDH; import java.math.BigInteger; -import java.security.*; -import javax.crypto.*; -import java.security.spec.*; -import java.security.interfaces.*; - -public class ECDHN implements com.jcraft.jsch.ECDH { +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; +import javax.crypto.KeyAgreement; + +public class ECDHN implements ECDH { byte[] Q_array; ECPublicKey publicKey; diff --git a/src/main/java/com/jcraft/jsch/jce/HMAC.java b/src/main/java/com/jcraft/jsch/jce/HMAC.java index 96705062..4b23f18e 100644 --- a/src/main/java/com/jcraft/jsch/jce/HMAC.java +++ b/src/main/java/com/jcraft/jsch/jce/HMAC.java @@ -26,9 +26,12 @@ package com.jcraft.jsch.jce; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Logger; import com.jcraft.jsch.MAC; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Mac; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; abstract class HMAC implements MAC { protected String name; @@ -75,7 +78,9 @@ public void doFinal(byte[] buf, int offset) { try { mac.doFinal(buf, offset); } catch (ShortBufferException e) { - System.err.println(e); + if (JSch.getLogger().isEnabled(Logger.ERROR)) { + JSch.getLogger().log(Logger.ERROR, e.getMessage(), e); + } } } diff --git a/src/main/java/com/jcraft/jsch/jce/HMACMD5.java b/src/main/java/com/jcraft/jsch/jce/HMACMD5.java index 00581652..681d3baa 100644 --- a/src/main/java/com/jcraft/jsch/jce/HMACMD5.java +++ b/src/main/java/com/jcraft/jsch/jce/HMACMD5.java @@ -26,10 +26,6 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.MAC; -import javax.crypto.*; -import javax.crypto.spec.*; - public class HMACMD5 extends HMAC { public HMACMD5() { name = "hmac-md5"; diff --git a/src/main/java/com/jcraft/jsch/jce/HMACMD5ETM.java b/src/main/java/com/jcraft/jsch/jce/HMACMD5ETM.java index 97b835b9..e490fad4 100644 --- a/src/main/java/com/jcraft/jsch/jce/HMACMD5ETM.java +++ b/src/main/java/com/jcraft/jsch/jce/HMACMD5ETM.java @@ -26,10 +26,6 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.MAC; -import javax.crypto.*; -import javax.crypto.spec.*; - public class HMACMD5ETM extends HMACMD5 { public HMACMD5ETM() { name = "hmac-md5-etm@openssh.com"; diff --git a/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java b/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java index 56682dea..29c06c96 100644 --- a/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java +++ b/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java @@ -26,8 +26,15 @@ package com.jcraft.jsch.jce; -import java.security.*; -import java.security.interfaces.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.DSAKey; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; public class KeyPairGenDSA implements com.jcraft.jsch.KeyPairGenDSA { byte[] x; // private diff --git a/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java b/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java index d0587e1e..ac678491 100644 --- a/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java +++ b/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java @@ -26,10 +26,14 @@ package com.jcraft.jsch.jce; -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.*; import com.jcraft.jsch.JSchException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; public class KeyPairGenECDSA implements com.jcraft.jsch.KeyPairGenECDSA { byte[] d; diff --git a/src/main/java/com/jcraft/jsch/jce/KeyPairGenEdDSA.java b/src/main/java/com/jcraft/jsch/jce/KeyPairGenEdDSA.java new file mode 100644 index 00000000..3f684ebc --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/KeyPairGenEdDSA.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class KeyPairGenEdDSA implements com.jcraft.jsch.KeyPairGenEdDSA { + + public KeyPairGenEdDSA() { + throw new UnsupportedOperationException("KeyPairGenEdDSA requires Java15+."); + } + + @Override + public void init(String name, int keylen) throws Exception { + throw new UnsupportedOperationException("KeyPairGenEdDSA requires Java15+."); + } + + @Override + public byte[] getPrv() { + throw new UnsupportedOperationException("KeyPairGenEdDSA requires Java15+."); + } + + @Override + public byte[] getPub() { + throw new UnsupportedOperationException("KeyPairGenEdDSA requires Java15+."); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java b/src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java index 32c82d5f..5a04940c 100644 --- a/src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java +++ b/src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java @@ -26,8 +26,14 @@ package com.jcraft.jsch.jce; -import java.security.*; -import java.security.interfaces.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; public class KeyPairGenRSA implements com.jcraft.jsch.KeyPairGenRSA { byte[] d; // private diff --git a/src/main/java/com/jcraft/jsch/jce/MD5.java b/src/main/java/com/jcraft/jsch/jce/MD5.java index 19267302..21afabb7 100644 --- a/src/main/java/com/jcraft/jsch/jce/MD5.java +++ b/src/main/java/com/jcraft/jsch/jce/MD5.java @@ -27,8 +27,7 @@ package com.jcraft.jsch.jce; import com.jcraft.jsch.HASH; - -import java.security.*; +import java.security.MessageDigest; public class MD5 implements HASH { MessageDigest md; @@ -40,11 +39,7 @@ public int getBlockSize() { @Override public void init() throws Exception { - try { - md = MessageDigest.getInstance("MD5"); - } catch (Exception e) { - System.err.println(e); - } + md = MessageDigest.getInstance("MD5"); } @Override diff --git a/src/main/java/com/jcraft/jsch/jce/PBKDF.java b/src/main/java/com/jcraft/jsch/jce/PBKDF.java index 8cc6a315..2cfb574b 100644 --- a/src/main/java/com/jcraft/jsch/jce/PBKDF.java +++ b/src/main/java/com/jcraft/jsch/jce/PBKDF.java @@ -26,16 +26,12 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.HASH; - -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.SecretKeyFactory; -import java.security.spec.InvalidKeySpecException; import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; -/** - * Use PBKDF2 instead. - */ +/** Use PBKDF2 instead. */ @Deprecated public class PBKDF implements com.jcraft.jsch.PBKDF { @Override diff --git a/src/main/java/com/jcraft/jsch/jce/PBKDF2.java b/src/main/java/com/jcraft/jsch/jce/PBKDF2.java index ef466e15..8279d69a 100644 --- a/src/main/java/com/jcraft/jsch/jce/PBKDF2.java +++ b/src/main/java/com/jcraft/jsch/jce/PBKDF2.java @@ -26,10 +26,9 @@ package com.jcraft.jsch.jce; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.SecretKeyFactory; import java.security.spec.InvalidKeySpecException; -import java.security.NoSuchAlgorithmException; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; abstract class PBKDF2 implements com.jcraft.jsch.PBKDF2 { private SecretKeyFactory skf; diff --git a/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512224.java b/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512224.java new file mode 100644 index 00000000..de9a04ba --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512224.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA512224 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA512/224"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512256.java b/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512256.java new file mode 100644 index 00000000..cf1db901 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512256.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA512256 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA512/256"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SHA1.java b/src/main/java/com/jcraft/jsch/jce/SHA1.java index 5684536e..47967818 100644 --- a/src/main/java/com/jcraft/jsch/jce/SHA1.java +++ b/src/main/java/com/jcraft/jsch/jce/SHA1.java @@ -27,8 +27,7 @@ package com.jcraft.jsch.jce; import com.jcraft.jsch.HASH; - -import java.security.*; +import java.security.MessageDigest; public class SHA1 implements HASH { MessageDigest md; @@ -40,11 +39,7 @@ public int getBlockSize() { @Override public void init() throws Exception { - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (Exception e) { - System.err.println(e); - } + md = MessageDigest.getInstance("SHA-1"); } @Override diff --git a/src/main/java/com/jcraft/jsch/jce/SHA224.java b/src/main/java/com/jcraft/jsch/jce/SHA224.java index 44105378..7eef4fa4 100644 --- a/src/main/java/com/jcraft/jsch/jce/SHA224.java +++ b/src/main/java/com/jcraft/jsch/jce/SHA224.java @@ -27,8 +27,7 @@ package com.jcraft.jsch.jce; import com.jcraft.jsch.HASH; - -import java.security.*; +import java.security.MessageDigest; public class SHA224 implements HASH { MessageDigest md; @@ -40,11 +39,7 @@ public int getBlockSize() { @Override public void init() throws Exception { - try { - md = MessageDigest.getInstance("SHA-224"); - } catch (Exception e) { - System.err.println(e); - } + md = MessageDigest.getInstance("SHA-224"); } @Override diff --git a/src/main/java/com/jcraft/jsch/jce/SHA256.java b/src/main/java/com/jcraft/jsch/jce/SHA256.java index fbd1a0bc..67288a1a 100644 --- a/src/main/java/com/jcraft/jsch/jce/SHA256.java +++ b/src/main/java/com/jcraft/jsch/jce/SHA256.java @@ -27,8 +27,7 @@ package com.jcraft.jsch.jce; import com.jcraft.jsch.HASH; - -import java.security.*; +import java.security.MessageDigest; public class SHA256 implements HASH { MessageDigest md; @@ -40,11 +39,7 @@ public int getBlockSize() { @Override public void init() throws Exception { - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (Exception e) { - System.err.println(e); - } + md = MessageDigest.getInstance("SHA-256"); } @Override diff --git a/src/main/java/com/jcraft/jsch/jce/SHA384.java b/src/main/java/com/jcraft/jsch/jce/SHA384.java index bb176a75..662f3e0b 100644 --- a/src/main/java/com/jcraft/jsch/jce/SHA384.java +++ b/src/main/java/com/jcraft/jsch/jce/SHA384.java @@ -26,9 +26,10 @@ package com.jcraft.jsch.jce; -import java.security.*; +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; -public class SHA384 implements com.jcraft.jsch.HASH { +public class SHA384 implements HASH { MessageDigest md; @Override @@ -38,11 +39,7 @@ public int getBlockSize() { @Override public void init() throws Exception { - try { - md = MessageDigest.getInstance("SHA-384"); - } catch (Exception e) { - System.err.println(e); - } + md = MessageDigest.getInstance("SHA-384"); } @Override diff --git a/src/main/java/com/jcraft/jsch/jce/SHA512.java b/src/main/java/com/jcraft/jsch/jce/SHA512.java index b7c5c291..5c2df9c3 100644 --- a/src/main/java/com/jcraft/jsch/jce/SHA512.java +++ b/src/main/java/com/jcraft/jsch/jce/SHA512.java @@ -26,9 +26,10 @@ package com.jcraft.jsch.jce; -import java.security.*; +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; -public class SHA512 implements com.jcraft.jsch.HASH { +public class SHA512 implements HASH { MessageDigest md; @Override @@ -38,11 +39,7 @@ public int getBlockSize() { @Override public void init() throws Exception { - try { - md = MessageDigest.getInstance("SHA-512"); - } catch (Exception e) { - System.err.println(e); - } + md = MessageDigest.getInstance("SHA-512"); } @Override diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java b/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java index fbbe11b0..5f6a687e 100644 --- a/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java +++ b/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java @@ -26,20 +26,24 @@ package com.jcraft.jsch.jce; +import com.jcraft.jsch.Buffer; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.security.*; -import java.security.spec.*; -import com.jcraft.jsch.Buffer; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; public class SignatureDSA implements com.jcraft.jsch.SignatureDSA { - java.security.Signature signature; + Signature signature; KeyFactory keyFactory; @Override public void init() throws Exception { - signature = java.security.Signature.getInstance("SHA1withDSA"); + signature = Signature.getInstance("SHA1withDSA"); keyFactory = KeyFactory.getInstance("DSA"); } @@ -131,15 +135,15 @@ public boolean verify(byte[] sig) throws Exception { tmp = new byte[length]; tmp[0] = (byte) 0x30; tmp[1] = (byte) (_frst.length + _scnd.length + 4); - tmp[1] += frst; - tmp[1] += scnd; + tmp[1] += (byte) frst; + tmp[1] += (byte) scnd; tmp[2] = (byte) 0x02; tmp[3] = (byte) _frst.length; - tmp[3] += frst; + tmp[3] += (byte) frst; System.arraycopy(_frst, 0, tmp, 4 + frst, _frst.length); tmp[4 + tmp[3]] = (byte) 0x02; tmp[5 + tmp[3]] = (byte) _scnd.length; - tmp[5 + tmp[3]] += scnd; + tmp[5 + tmp[3]] += (byte) scnd; System.arraycopy(_scnd, 0, tmp, 6 + tmp[3] + scnd, _scnd.length); sig = tmp; diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java b/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java index b11755bb..307a043f 100644 --- a/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java +++ b/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java @@ -26,12 +26,21 @@ package com.jcraft.jsch.jce; -import java.math.BigInteger; -import java.security.*; -import java.security.spec.*; import com.jcraft.jsch.Buffer; - -abstract class SignatureECDSAN implements com.jcraft.jsch.SignatureECDSA { +import com.jcraft.jsch.SignatureECDSA; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; + +abstract class SignatureECDSAN implements SignatureECDSA { Signature signature; KeyFactory keyFactory; @@ -46,7 +55,7 @@ public void init() throws Exception { foo = "SHA384withECDSA"; else if (name.equals("ecdsa-sha2-nistp521")) foo = "SHA512withECDSA"; - signature = java.security.Signature.getInstance(foo); + signature = Signature.getInstance(foo); keyFactory = KeyFactory.getInstance("EC"); } @@ -99,7 +108,7 @@ public byte[] sign() throws Exception { // so we have to convert it. if (sig[0] == 0x30 && // in ASN.1 ((sig[1] + 2 == sig.length) - || ((sig[1] & 0x80) != 0 && (sig[2] & 0xff) + 3 == sig.length))) {// 2bytes for len + || ((sig[1] & 0x80) != 0 && (sig[2] & 0xff) + 3 == sig.length))) { // 2bytes for len int index = 3; if ((sig[1] & 0x80) != 0 && (sig[2] & 0xff) + 3 == sig.length) diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureEd25519.java b/src/main/java/com/jcraft/jsch/jce/SignatureEd25519.java new file mode 100644 index 00000000..0c1b604d --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SignatureEd25519.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.SignatureEdDSA; + +public class SignatureEd25519 implements SignatureEdDSA { + + public SignatureEd25519() { + throw new UnsupportedOperationException("SignatureEd25519 requires Java15+."); + } + + @Override + public void init() throws Exception { + throw new UnsupportedOperationException("SignatureEd25519 requires Java15+."); + } + + @Override + public void setPubKey(byte[] y_arr) throws Exception { + throw new UnsupportedOperationException("SignatureEd25519 requires Java15+."); + } + + @Override + public void setPrvKey(byte[] bytes) throws Exception { + throw new UnsupportedOperationException("SignatureEd25519 requires Java15+."); + } + + @Override + public byte[] sign() throws Exception { + throw new UnsupportedOperationException("SignatureEd25519 requires Java15+."); + } + + @Override + public void update(byte[] foo) throws Exception { + throw new UnsupportedOperationException("SignatureEd25519 requires Java15+."); + } + + @Override + public boolean verify(byte[] sig) throws Exception { + throw new UnsupportedOperationException("SignatureEd25519 requires Java15+."); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureEd448.java b/src/main/java/com/jcraft/jsch/jce/SignatureEd448.java new file mode 100644 index 00000000..2addd66f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SignatureEd448.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.SignatureEdDSA; + +public class SignatureEd448 implements SignatureEdDSA { + + public SignatureEd448() { + throw new UnsupportedOperationException("SignatureEd448 requires Java15+."); + } + + @Override + public void init() throws Exception { + throw new UnsupportedOperationException("SignatureEd448 requires Java15+."); + } + + @Override + public void setPubKey(byte[] y_arr) throws Exception { + throw new UnsupportedOperationException("SignatureEd448 requires Java15+."); + } + + @Override + public void setPrvKey(byte[] bytes) throws Exception { + throw new UnsupportedOperationException("SignatureEd448 requires Java15+."); + } + + @Override + public byte[] sign() throws Exception { + throw new UnsupportedOperationException("SignatureEd448 requires Java15+."); + } + + @Override + public void update(byte[] foo) throws Exception { + throw new UnsupportedOperationException("SignatureEd448 requires Java15+."); + } + + @Override + public boolean verify(byte[] sig) throws Exception { + throw new UnsupportedOperationException("SignatureEd448 requires Java15+."); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureRSAN.java b/src/main/java/com/jcraft/jsch/jce/SignatureRSAN.java index 1a98267f..e1f5f392 100644 --- a/src/main/java/com/jcraft/jsch/jce/SignatureRSAN.java +++ b/src/main/java/com/jcraft/jsch/jce/SignatureRSAN.java @@ -26,15 +26,20 @@ package com.jcraft.jsch.jce; +import com.jcraft.jsch.Buffer; +import com.jcraft.jsch.SignatureRSA; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.security.*; -import java.security.spec.*; -import com.jcraft.jsch.Buffer; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; -abstract class SignatureRSAN implements com.jcraft.jsch.SignatureRSA { +abstract class SignatureRSAN implements SignatureRSA { - java.security.Signature signature; + Signature signature; KeyFactory keyFactory; abstract String getName(); @@ -51,7 +56,7 @@ else if (name.equals("ssh-rsa-sha384@ssh.com")) foo = "SHA384withRSA"; else if (name.equals("ssh-rsa-sha224@ssh.com")) foo = "SHA224withRSA"; - signature = java.security.Signature.getInstance(foo); + signature = Signature.getInstance(foo); keyFactory = KeyFactory.getInstance("RSA"); } diff --git a/src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java b/src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java index 93442843..35150857 100644 --- a/src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java +++ b/src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java @@ -26,14 +26,16 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; -public class TripleDESCBC implements Cipher { +public class TripleDESCBC implements com.jcraft.jsch.Cipher { private static final int ivsize = 8; private static final int bsize = 24; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -47,8 +49,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; - // if(padding) pad="PKCS5Padding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -62,18 +62,18 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { - cipher = javax.crypto.Cipher.getInstance("DESede/CBC/" + pad); + cipher = Cipher.getInstance("DESede/CBC/NoPadding"); /* * // The following code does not work on IBM's JDK 1.4.1 SecretKeySpec skeySpec = new - * SecretKeySpec(key, "DESede"); cipher.init((mode==ENCRYPT_MODE? - * javax.crypto.Cipher.ENCRYPT_MODE: javax.crypto.Cipher.DECRYPT_MODE), skeySpec, new - * IvParameterSpec(iv)); + * SecretKeySpec(key, "DESede"); cipher.init((mode==com.jcraft.jsch.Cipher.ENCRYPT_MODE? + * Cipher.ENCRYPT_MODE: Cipher.DECRYPT_MODE), skeySpec, new IvParameterSpec(iv)); */ DESedeKeySpec keyspec = new DESedeKeySpec(key); SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede"); SecretKey _key = keyfactory.generateSecret(keyspec); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), _key, new IvParameterSpec(iv)); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java b/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java index c4fd3338..de10ca35 100644 --- a/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java +++ b/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java @@ -26,14 +26,16 @@ package com.jcraft.jsch.jce; -import com.jcraft.jsch.Cipher; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; -public class TripleDESCTR implements Cipher { +public class TripleDESCTR implements com.jcraft.jsch.Cipher { private static final int ivsize = 8; private static final int bsize = 24; - private javax.crypto.Cipher cipher; + private Cipher cipher; @Override public int getIVSize() { @@ -47,8 +49,6 @@ public int getBlockSize() { @Override public void init(int mode, byte[] key, byte[] iv) throws Exception { - String pad = "NoPadding"; - // if(padding) pad="PKCS5Padding"; byte[] tmp; if (iv.length > ivsize) { tmp = new byte[ivsize]; @@ -62,18 +62,18 @@ public void init(int mode, byte[] key, byte[] iv) throws Exception { } try { - cipher = javax.crypto.Cipher.getInstance("DESede/CTR/" + pad); + cipher = Cipher.getInstance("DESede/CTR/NoPadding"); /* * // The following code does not work on IBM's JDK 1.4.1 SecretKeySpec skeySpec = new - * SecretKeySpec(key, "DESede"); cipher.init((mode==ENCRYPT_MODE? - * javax.crypto.Cipher.ENCRYPT_MODE: javax.crypto.Cipher.DECRYPT_MODE), skeySpec, new - * IvParameterSpec(iv)); + * SecretKeySpec(key, "DESede"); cipher.init((mode==com.jcraft.jsch.Cipher.ENCRYPT_MODE? + * Cipher.ENCRYPT_MODE: Cipher.DECRYPT_MODE), skeySpec, new IvParameterSpec(iv)); */ DESedeKeySpec keyspec = new DESedeKeySpec(key); SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede"); SecretKey _key = keyfactory.generateSecret(keyspec); - cipher.init((mode == ENCRYPT_MODE ? javax.crypto.Cipher.ENCRYPT_MODE - : javax.crypto.Cipher.DECRYPT_MODE), _key, new IvParameterSpec(iv)); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); } catch (Exception e) { cipher = null; throw e; diff --git a/src/main/java11/com/jcraft/jsch/jce/KeyPairGenXEC.java b/src/main/java/com/jcraft/jsch/jce/XDH.java similarity index 69% rename from src/main/java11/com/jcraft/jsch/jce/KeyPairGenXEC.java rename to src/main/java/com/jcraft/jsch/jce/XDH.java index 1935bfd3..83379f1e 100644 --- a/src/main/java11/com/jcraft/jsch/jce/KeyPairGenXEC.java +++ b/src/main/java/com/jcraft/jsch/jce/XDH.java @@ -26,29 +26,29 @@ package com.jcraft.jsch.jce; -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.*; +public class XDH implements com.jcraft.jsch.XDH { -public class KeyPairGenXEC implements com.jcraft.jsch.KeyPairGenXEC { - XECPublicKey pubKey; - XECPrivateKey prvKey; + public XDH() { + throw new UnsupportedOperationException("XDH requires Java11+."); + } + + @Override + public void init(String name, int keylen) throws Exception { + throw new UnsupportedOperationException("XDH requires Java11+."); + } @Override - public void init(String name) throws Exception { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH"); - NamedParameterSpec paramSpec = new NamedParameterSpec(name); - kpg.initialize(paramSpec); - KeyPair kp = kpg.genKeyPair(); - prvKey = (XECPrivateKey) kp.getPrivate(); - pubKey = (XECPublicKey) kp.getPublic(); + public byte[] getQ() throws Exception { + throw new UnsupportedOperationException("XDH requires Java11+."); } - XECPublicKey getPublicKey() { - return pubKey; + @Override + public byte[] getSecret(byte[] Q) throws Exception { + throw new UnsupportedOperationException("XDH requires Java11+."); } - XECPrivateKey getPrivateKey() { - return prvKey; + @Override + public boolean validate(byte[] u) throws Exception { + throw new UnsupportedOperationException("XDH requires Java11+."); } } diff --git a/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java b/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java index 5a2a1698..689da9c4 100644 --- a/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java +++ b/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java @@ -27,7 +27,6 @@ package com.jcraft.jsch.jgss; import com.jcraft.jsch.JSchException; - import java.net.InetAddress; import java.net.UnknownHostException; import org.ietf.jgss.GSSContext; diff --git a/src/main/java/com/jcraft/jsch/juz/Compression.java b/src/main/java/com/jcraft/jsch/juz/Compression.java index 0fbd5667..990c40e3 100644 --- a/src/main/java/com/jcraft/jsch/juz/Compression.java +++ b/src/main/java/com/jcraft/jsch/juz/Compression.java @@ -1,13 +1,17 @@ package com.jcraft.jsch.juz; -import com.jcraft.jsch.*; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Logger; +import com.jcraft.jsch.Session; import java.util.function.Supplier; +import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; /** * This example demonstrates the packet compression without using jzlib[1]. * + *

    * The ssh protocol adopts zlib[2] for the packet compression. Fortunately, JDK has provided wrapper * classes for zlib(j.u.z.{Deflater, Inflater}), but it does not expose enough functionality of * zlib, unfortunately; it must not allow to compress data with SYNC_FLUSH. So, JSch has been using @@ -15,12 +19,12 @@ * j.u.z.Deflater, and SYNC_FLUSH has been supported at last. This example shows how to enable the * packet compression by using JDK's java.util.zip package. * - * + *

    * [1] http://www.jcraft.com/jzlib/ [2] http://www.zlib.net/ [3] * https://bugs.openjdk.java.net/browse/JDK-4206909 */ public class Compression implements com.jcraft.jsch.Compression { - static private final int BUF_SIZE = 4096; + private static final int BUF_SIZE = 4096; private final int buffer_margin = 32 + 20; // AES256 + HMACSHA1 private Deflater deflater; private Inflater inflater; @@ -115,7 +119,7 @@ public byte[] uncompress(byte[] buf, int start, int[] len) { System.arraycopy(tmpbuf, 0, inflated_buf, inflated_end, result); inflated_end += result; } while (inflater.getRemaining() > 0); - } catch (java.util.zip.DataFormatException e) { + } catch (DataFormatException e) { logMessage(Logger.WARN, () -> "an exception during uncompress\n" + e.toString()); } diff --git a/src/main/java/com/jcraft/jsch/jzlib/Adler32.java b/src/main/java/com/jcraft/jsch/jzlib/Adler32.java index 6fbfad83..a20b8907 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/Adler32.java +++ b/src/main/java/com/jcraft/jsch/jzlib/Adler32.java @@ -33,9 +33,9 @@ final class Adler32 implements Checksum { // largest prime smaller than 65536 - static final private int BASE = 65521; + private static final int BASE = 65521; // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - static final private int NMAX = 5552; + private static final int NMAX = 5552; private long s1 = 1L; private long s2 = 0L; diff --git a/src/main/java/com/jcraft/jsch/jzlib/CRC32.java b/src/main/java/com/jcraft/jsch/jzlib/CRC32.java index 7c311a8d..6e9591ec 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/CRC32.java +++ b/src/main/java/com/jcraft/jsch/jzlib/CRC32.java @@ -37,6 +37,7 @@ final class CRC32 implements Checksum { */ private int v = 0; private static int[] crc_table = null; + static { crc_table = new int[256]; for (int n = 0; n < 256; n++) { diff --git a/src/main/java/com/jcraft/jsch/jzlib/Compression.java b/src/main/java/com/jcraft/jsch/jzlib/Compression.java index 809373d8..279ee189 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/Compression.java +++ b/src/main/java/com/jcraft/jsch/jzlib/Compression.java @@ -26,14 +26,14 @@ package com.jcraft.jsch.jzlib; -import java.util.function.Supplier; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Logger; import com.jcraft.jsch.Session; import java.io.UncheckedIOException; +import java.util.function.Supplier; public class Compression implements com.jcraft.jsch.Compression { - static private final int BUF_SIZE = 4096; + private static final int BUF_SIZE = 4096; private final int buffer_margin = 32 + 20; // AES256 + HMACSHA1 private Deflater deflater; private Inflater inflater; diff --git a/src/main/java/com/jcraft/jsch/jzlib/Deflate.java b/src/main/java/com/jcraft/jsch/jzlib/Deflate.java index e2dd7068..4e15b04c 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/Deflate.java +++ b/src/main/java/com/jcraft/jsch/jzlib/Deflate.java @@ -32,12 +32,12 @@ final class Deflate implements Cloneable { - static final private int MAX_MEM_LEVEL = 9; + private static final int MAX_MEM_LEVEL = 9; - static final private int Z_DEFAULT_COMPRESSION = -1; + private static final int Z_DEFAULT_COMPRESSION = -1; - static final private int MAX_WBITS = 15; // 32K LZ77 window - static final private int DEF_MEM_LEVEL = 8; + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_MEM_LEVEL = 8; static class Config { int good_length; // reduce lazy search above this match length @@ -55,10 +55,11 @@ static class Config { } } - static final private int STORED = 0; - static final private int FAST = 1; - static final private int SLOW = 2; - static final private Config[] config_table; + private static final int STORED = 0; + private static final int FAST = 1; + private static final int SLOW = 2; + private static final Config[] config_table; + static { config_table = new Config[10]; // good lazy nice chain @@ -75,7 +76,7 @@ static class Config { config_table[9] = new Config(32, 258, 258, 4096, SLOW); } - static final private String[] z_errmsg = {"need dictionary", // Z_NEED_DICT 2 + private static final String[] z_errmsg = {"need dictionary", // Z_NEED_DICT 2 "stream end", // Z_STREAM_END 1 "", // Z_OK 0 "file error", // Z_ERRNO (-1) @@ -87,80 +88,80 @@ static class Config { ""}; // block not completed, need more input or more output - static final private int NeedMore = 0; + private static final int NeedMore = 0; // block flush performed - static final private int BlockDone = 1; + private static final int BlockDone = 1; // finish started, need only more output at next deflate - static final private int FinishStarted = 2; + private static final int FinishStarted = 2; // finish done, accept no more input or output - static final private int FinishDone = 3; + private static final int FinishDone = 3; // preset dictionary flag in zlib header - static final private int PRESET_DICT = 0x20; - - static final private int Z_FILTERED = 1; - static final private int Z_HUFFMAN_ONLY = 2; - static final private int Z_DEFAULT_STRATEGY = 0; - - static final private int Z_NO_FLUSH = 0; - static final private int Z_PARTIAL_FLUSH = 1; - static final private int Z_SYNC_FLUSH = 2; - static final private int Z_FULL_FLUSH = 3; - static final private int Z_FINISH = 4; - - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; - - static final private int INIT_STATE = 42; - static final private int BUSY_STATE = 113; - static final private int FINISH_STATE = 666; + private static final int PRESET_DICT = 0x20; + + private static final int Z_FILTERED = 1; + private static final int Z_HUFFMAN_ONLY = 2; + private static final int Z_DEFAULT_STRATEGY = 0; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private static final int INIT_STATE = 42; + private static final int BUSY_STATE = 113; + private static final int FINISH_STATE = 666; // The deflate compression method - static final private int Z_DEFLATED = 8; + private static final int Z_DEFLATED = 8; - static final private int STORED_BLOCK = 0; - static final private int STATIC_TREES = 1; - static final private int DYN_TREES = 2; + private static final int STORED_BLOCK = 0; + private static final int STATIC_TREES = 1; + private static final int DYN_TREES = 2; // The three kinds of block type - static final private int Z_BINARY = 0; - static final private int Z_ASCII = 1; - static final private int Z_UNKNOWN = 2; + private static final int Z_BINARY = 0; + private static final int Z_ASCII = 1; + private static final int Z_UNKNOWN = 2; - static final private int Buf_size = 8 * 2; + private static final int Buf_size = 8 * 2; // repeat previous bit length 3-6 times (2 bits of repeat count) - static final private int REP_3_6 = 16; + private static final int REP_3_6 = 16; // repeat a zero length 3-10 times (3 bits of repeat count) - static final private int REPZ_3_10 = 17; + private static final int REPZ_3_10 = 17; // repeat a zero length 11-138 times (7 bits of repeat count) - static final private int REPZ_11_138 = 18; + private static final int REPZ_11_138 = 18; - static final private int MIN_MATCH = 3; - static final private int MAX_MATCH = 258; - static final private int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + private static final int MIN_MATCH = 3; + private static final int MAX_MATCH = 258; + private static final int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - static final private int MAX_BITS = 15; - static final private int D_CODES = 30; - static final private int BL_CODES = 19; - static final private int LENGTH_CODES = 29; - static final private int LITERALS = 256; - static final private int L_CODES = (LITERALS + 1 + LENGTH_CODES); - static final private int HEAP_SIZE = (2 * L_CODES + 1); + private static final int MAX_BITS = 15; + private static final int D_CODES = 30; + private static final int BL_CODES = 19; + private static final int LENGTH_CODES = 29; + private static final int LITERALS = 256; + private static final int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private static final int HEAP_SIZE = (2 * L_CODES + 1); - static final private int END_BLOCK = 256; + private static final int END_BLOCK = 256; ZStream strm; // pointer back to this zlib stream int status; // as the name implies @@ -435,7 +436,7 @@ void scan_tree(short[] tree, // the tree to be scanned if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { - bl_tree[curlen * 2] += count; + bl_tree[curlen * 2] += (short) count; } else if (curlen != 0) { if (curlen != prevlen) bl_tree[curlen * 2]++; @@ -487,7 +488,6 @@ int build_bl_tree() { return max_blindex; } - // Send the header for a block using dynamic Huffman trees: the counts, the // lengths of the bit length codes, the literal tree and the distance tree. // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. @@ -572,13 +572,13 @@ final void put_byte(byte c) { } final void put_short(int w) { - put_byte((byte) (w/* &0xff */)); + put_byte((byte) (w /* &0xff */)); put_byte((byte) (w >>> 8)); } final void putShortMSB(int b) { put_byte((byte) (b >> 8)); - put_byte((byte) (b/* &0xff */)); + put_byte((byte) (b /* &0xff */)); } final void send_code(int c, short[] tree) { @@ -591,13 +591,13 @@ void send_bits(int value, int length) { if (bi_valid > Buf_size - len) { int val = value; // bi_buf |= (val << bi_valid); - bi_buf |= ((val << bi_valid) & 0xffff); + bi_buf |= (short) ((val << bi_valid) & 0xffff); put_short(bi_buf); bi_buf = (short) (val >>> (Buf_size - bi_valid)); bi_valid += len - Buf_size; } else { // bi_buf |= (value) << bi_valid; - bi_buf |= (((value) << bi_valid) & 0xffff); + bi_buf |= (short) (((value) << bi_valid) & 0xffff); bi_valid += len; } } @@ -629,7 +629,6 @@ void _tr_align() { last_eob_len = 7; } - // Save the match info and tally the frequency counts. Return true if // the current block must be flushed. boolean _tr_tally(int dist, // distance of matched string @@ -659,7 +658,7 @@ boolean _tr_tally(int dist, // distance of matched string int in_length = strstart - block_start; int dcode; for (dcode = 0; dcode < D_CODES; dcode++) { - out_length += (int) dyn_dtree[dcode * 2] * (5L + Tree.extra_dbits[dcode]); + out_length += (int) dyn_dtree[dcode * 2] * (5 + Tree.extra_dbits[dcode]); } out_length >>>= 3; if ((matches < (last_lit / 2)) && out_length < in_length / 2) @@ -835,7 +834,6 @@ int deflate_stored(int flush) { flush_block_only(false); if (strm.avail_out == 0) return NeedMore; - } // Flush if we may have to slide, otherwise block_start may become @@ -869,7 +867,7 @@ void _tr_flush_block(int buf, // input block, or NULL if too old int stored_len, // length of input block boolean eof // true if this is the last block for a file ) { - int opt_lenb, static_lenb;// opt_len and static_len in bytes + int opt_lenb, static_lenb; // opt_len and static_len in bytes int max_blindex = 0; // index of last bit length code of non zero freq // Build the Huffman trees unless a stored block is forced @@ -1541,7 +1539,6 @@ int deflate(int flush) { status = BUSY_STATE; putShortMSB(header); - // Save the adler32 of the preset dictionary: if (strstart != 0) { long adler = strm.adler.getValue(); @@ -1620,7 +1617,7 @@ int deflate(int flush) { // as a special marker by inflate_sync(). if (flush == Z_FULL_FLUSH) { // state.head[s.hash_size-1]=0; - for (int i = 0; i < hash_size/*-1*/; i++) // forget history + for (int i = 0; i < hash_size /*-1*/; i++) // forget history head[i] = 0; } } diff --git a/src/main/java/com/jcraft/jsch/jzlib/Deflater.java b/src/main/java/com/jcraft/jsch/jzlib/Deflater.java index af2a74da..0941a214 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/Deflater.java +++ b/src/main/java/com/jcraft/jsch/jzlib/Deflater.java @@ -32,26 +32,26 @@ final class Deflater extends ZStream { - static final private int MAX_WBITS = 15; // 32K LZ77 window - static final private int DEF_WBITS = MAX_WBITS; - - static final private int Z_NO_FLUSH = 0; - static final private int Z_PARTIAL_FLUSH = 1; - static final private int Z_SYNC_FLUSH = 2; - static final private int Z_FULL_FLUSH = 3; - static final private int Z_FINISH = 4; - - static final private int MAX_MEM_LEVEL = 9; - - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_WBITS = MAX_WBITS; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int MAX_MEM_LEVEL = 9; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; private boolean finished = false; diff --git a/src/main/java/com/jcraft/jsch/jzlib/DeflaterOutputStream.java b/src/main/java/com/jcraft/jsch/jzlib/DeflaterOutputStream.java index b0b252e4..ce4be27f 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/DeflaterOutputStream.java +++ b/src/main/java/com/jcraft/jsch/jzlib/DeflaterOutputStream.java @@ -26,7 +26,9 @@ package com.jcraft.jsch.jzlib; -import java.io.*; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; final class DeflaterOutputStream extends FilterOutputStream { diff --git a/src/main/java/com/jcraft/jsch/jzlib/GZIPHeader.java b/src/main/java/com/jcraft/jsch/jzlib/GZIPHeader.java index bfa2f245..8c69a1cd 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/GZIPHeader.java +++ b/src/main/java/com/jcraft/jsch/jzlib/GZIPHeader.java @@ -31,7 +31,6 @@ package com.jcraft.jsch.jzlib; import java.nio.charset.StandardCharsets; -import java.util.Arrays; /** * @see http://www.ietf.org/rfc/rfc1952.txt diff --git a/src/main/java/com/jcraft/jsch/jzlib/InfBlocks.java b/src/main/java/com/jcraft/jsch/jzlib/InfBlocks.java index 02a8aba6..458ff5d2 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/InfBlocks.java +++ b/src/main/java/com/jcraft/jsch/jzlib/InfBlocks.java @@ -31,10 +31,10 @@ package com.jcraft.jsch.jzlib; final class InfBlocks { - static final private int MANY = 1440; + private static final int MANY = 1440; // And'ing with mask[n] masks the lower n bits - static final private int[] inflate_mask = {0x00000000, 0x00000001, 0x00000003, 0x00000007, + private static final int[] inflate_mask = {0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff}; @@ -42,26 +42,26 @@ final class InfBlocks { static final int[] border = { // Order of the bit length code lengths 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; - - static final private int TYPE = 0; // get type bits (3, including end bit) - static final private int LENS = 1; // get lengths for stored - static final private int STORED = 2;// processing stored block - static final private int TABLE = 3; // get table lengths - static final private int BTREE = 4; // get bit lengths tree for a dynamic block - static final private int DTREE = 5; // get length, distance trees for a dynamic block - static final private int CODES = 6; // processing fixed or dynamic block - static final private int DRY = 7; // output remaining window bytes - static final private int DONE = 8; // finished last block, done - static final private int BAD = 9; // ot a data error--stuck here + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private static final int TYPE = 0; // get type bits (3, including end bit) + private static final int LENS = 1; // get lengths for stored + private static final int STORED = 2; // processing stored block + private static final int TABLE = 3; // get table lengths + private static final int BTREE = 4; // get bit lengths tree for a dynamic block + private static final int DTREE = 5; // get length, distance trees for a dynamic block + private static final int CODES = 6; // processing fixed or dynamic block + private static final int DRY = 7; // output remaining window bytes + private static final int DONE = 8; // finished last block, done + private static final int BAD = 9; // ot a data error--stuck here int mode; // current inflate_block mode @@ -151,7 +151,6 @@ int proc(int r) { while (true) { switch (mode) { case TYPE: - while (k < (3)) { if (n != 0) { r = Z_OK; @@ -197,7 +196,6 @@ int proc(int r) { mode = CODES; break; case 2: // dynamic - { b >>>= (3); k -= (3); @@ -206,7 +204,6 @@ int proc(int r) { mode = TABLE; break; case 3: // illegal - { b >>>= (3); k -= (3); @@ -225,7 +222,6 @@ int proc(int r) { } break; case LENS: - while (k < (32)) { if (n != 0) { r = Z_OK; @@ -313,7 +309,6 @@ int proc(int r) { mode = last != 0 ? DRY : TYPE; break; case TABLE: - while (k < (14)) { if (n != 0) { r = Z_OK; diff --git a/src/main/java/com/jcraft/jsch/jzlib/InfCodes.java b/src/main/java/com/jcraft/jsch/jzlib/InfCodes.java index 028b222e..edfd0891 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/InfCodes.java +++ b/src/main/java/com/jcraft/jsch/jzlib/InfCodes.java @@ -32,33 +32,33 @@ final class InfCodes { - static final private int[] inflate_mask = {0x00000000, 0x00000001, 0x00000003, 0x00000007, + private static final int[] inflate_mask = {0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff}; - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; // waiting for "i:"=input, // "o:"=output, // "x:"=nothing - static final private int START = 0; // x: set up for LEN - static final private int LEN = 1; // i: get length/literal/eob next - static final private int LENEXT = 2; // i: getting length extra (have base) - static final private int DIST = 3; // i: get distance next - static final private int DISTEXT = 4;// i: getting distance extra - static final private int COPY = 5; // o: copying bytes in window, waiting for space - static final private int LIT = 6; // o: got literal, waiting for output space - static final private int WASH = 7; // o: got eob, possibly still output waiting - static final private int END = 8; // x: got eob and all data flushed - static final private int BADCODE = 9;// x: got error + private static final int START = 0; // x: set up for LEN + private static final int LEN = 1; // i: get length/literal/eob next + private static final int LENEXT = 2; // i: getting length extra (have base) + private static final int DIST = 3; // i: get distance next + private static final int DISTEXT = 4; // i: getting distance extra + private static final int COPY = 5; // o: copying bytes in window, waiting for space + private static final int LIT = 6; // o: got literal, waiting for output space + private static final int WASH = 7; // o: got eob, possibly still output waiting + private static final int END = 8; // x: got eob and all data flushed + private static final int BADCODE = 9; // x: got error int mode; // current inflate_codes mode @@ -433,7 +433,6 @@ int proc(int r) { return s.inflate_flush(r); case BADCODE: // x: got error - r = Z_DATA_ERROR; s.bitb = b; @@ -599,7 +598,6 @@ int inflate_fast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, } r = 0; // copy rest from start of window } - } // copy all or what's left diff --git a/src/main/java/com/jcraft/jsch/jzlib/InfTree.java b/src/main/java/com/jcraft/jsch/jzlib/InfTree.java index 0c8f0ba0..26b6fa6d 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/InfTree.java +++ b/src/main/java/com/jcraft/jsch/jzlib/InfTree.java @@ -32,17 +32,17 @@ final class InfTree { - static final private int MANY = 1440; - - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; + private static final int MANY = 1440; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; static final int fixed_bl = 9; static final int fixed_bd = 5; @@ -77,7 +77,6 @@ final class InfTree { 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, - 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, diff --git a/src/main/java/com/jcraft/jsch/jzlib/Inflate.java b/src/main/java/com/jcraft/jsch/jzlib/Inflate.java index b6f487fc..c937d6a9 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/Inflate.java +++ b/src/main/java/com/jcraft/jsch/jzlib/Inflate.java @@ -34,10 +34,10 @@ final class Inflate { - static final private int MAX_WBITS = 15; // 32K LZ77 window + private static final int MAX_WBITS = 15; // 32K LZ77 window // preset dictionary flag in zlib header - static final private int PRESET_DICT = 0x20; + private static final int PRESET_DICT = 0x20; static final int Z_NO_FLUSH = 0; static final int Z_PARTIAL_FLUSH = 1; @@ -45,43 +45,43 @@ final class Inflate { static final int Z_FULL_FLUSH = 3; static final int Z_FINISH = 4; - static final private int Z_DEFLATED = 8; - - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; - - static final private int METHOD = 0; // waiting for method byte - static final private int FLAG = 1; // waiting for flag byte - static final private int DICT4 = 2; // four dictionary check bytes to go - static final private int DICT3 = 3; // three dictionary check bytes to go - static final private int DICT2 = 4; // two dictionary check bytes to go - static final private int DICT1 = 5; // one dictionary check byte to go - static final private int DICT0 = 6; // waiting for inflateSetDictionary - static final private int BLOCKS = 7; // decompressing blocks - static final private int CHECK4 = 8; // four check bytes to go - static final private int CHECK3 = 9; // three check bytes to go - static final private int CHECK2 = 10; // two check bytes to go - static final private int CHECK1 = 11; // one check byte to go - static final private int DONE = 12; // finished check, done - static final private int BAD = 13; // got an error--stay here - - static final private int HEAD = 14; - static final private int LENGTH = 15; - static final private int TIME = 16; - static final private int OS = 17; - static final private int EXLEN = 18; - static final private int EXTRA = 19; - static final private int NAME = 20; - static final private int COMMENT = 21; - static final private int HCRC = 22; - static final private int FLAGS = 23; + private static final int Z_DEFLATED = 8; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private static final int METHOD = 0; // waiting for method byte + private static final int FLAG = 1; // waiting for flag byte + private static final int DICT4 = 2; // four dictionary check bytes to go + private static final int DICT3 = 3; // three dictionary check bytes to go + private static final int DICT2 = 4; // two dictionary check bytes to go + private static final int DICT1 = 5; // one dictionary check byte to go + private static final int DICT0 = 6; // waiting for inflateSetDictionary + private static final int BLOCKS = 7; // decompressing blocks + private static final int CHECK4 = 8; // four check bytes to go + private static final int CHECK3 = 9; // three check bytes to go + private static final int CHECK2 = 10; // two check bytes to go + private static final int CHECK1 = 11; // one check byte to go + private static final int DONE = 12; // finished check, done + private static final int BAD = 13; // got an error--stay here + + private static final int HEAD = 14; + private static final int LENGTH = 15; + private static final int TIME = 16; + private static final int OS = 17; + private static final int EXLEN = 18; + private static final int EXTRA = 19; + private static final int NAME = 20; + private static final int COMMENT = 21; + private static final int HCRC = 22; + private static final int FLAGS = 23; static final int INFLATE_ANY = 0x40000000; @@ -288,7 +288,6 @@ int inflate(int f) { } this.mode = DICT4; case DICT4: - if (z.avail_in == 0) return r; r = f; @@ -298,7 +297,6 @@ int inflate(int f) { this.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L; this.mode = DICT3; case DICT3: - if (z.avail_in == 0) return r; r = f; @@ -308,7 +306,6 @@ int inflate(int f) { this.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L; this.mode = DICT2; case DICT2: - if (z.avail_in == 0) return r; r = f; @@ -318,7 +315,6 @@ int inflate(int f) { this.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L; this.mode = DICT1; case DICT1: - if (z.avail_in == 0) return r; r = f; @@ -356,7 +352,6 @@ int inflate(int f) { } this.mode = CHECK4; case CHECK4: - if (z.avail_in == 0) return r; r = f; @@ -366,7 +361,6 @@ int inflate(int f) { this.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L; this.mode = CHECK3; case CHECK3: - if (z.avail_in == 0) return r; r = f; @@ -376,7 +370,6 @@ int inflate(int f) { this.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L; this.mode = CHECK2; case CHECK2: - if (z.avail_in == 0) return r; r = f; @@ -386,7 +379,6 @@ int inflate(int f) { this.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L; this.mode = CHECK1; case CHECK1: - if (z.avail_in == 0) return r; r = f; @@ -447,7 +439,6 @@ int inflate(int f) { return Z_DATA_ERROR; case FLAGS: - try { r = readBytes(2, r, f); } catch (Return e) { @@ -625,7 +616,7 @@ int inflateSetDictionary(byte[] dictionary, int dictLength) { return Z_OK; } - static private byte[] mark = {(byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff}; + private static byte[] mark = {(byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff}; int inflateSync() { int n; // number of bytes to look at diff --git a/src/main/java/com/jcraft/jsch/jzlib/Inflater.java b/src/main/java/com/jcraft/jsch/jzlib/Inflater.java index 6e8d97d7..d4aede85 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/Inflater.java +++ b/src/main/java/com/jcraft/jsch/jzlib/Inflater.java @@ -32,26 +32,26 @@ final class Inflater extends ZStream { - static final private int MAX_WBITS = 15; // 32K LZ77 window - static final private int DEF_WBITS = MAX_WBITS; - - static final private int Z_NO_FLUSH = 0; - static final private int Z_PARTIAL_FLUSH = 1; - static final private int Z_SYNC_FLUSH = 2; - static final private int Z_FULL_FLUSH = 3; - static final private int Z_FINISH = 4; - - static final private int MAX_MEM_LEVEL = 9; - - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_WBITS = MAX_WBITS; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int MAX_MEM_LEVEL = 9; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; private int param_w = -1; private JZlib.WrapperType param_wrapperType = null; diff --git a/src/main/java/com/jcraft/jsch/jzlib/InflaterInputStream.java b/src/main/java/com/jcraft/jsch/jzlib/InflaterInputStream.java index 12287c8d..28e92360 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/InflaterInputStream.java +++ b/src/main/java/com/jcraft/jsch/jzlib/InflaterInputStream.java @@ -26,7 +26,11 @@ package com.jcraft.jsch.jzlib; -import java.io.*; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; final class InflaterInputStream extends FilterInputStream { protected final Inflater inflater; @@ -223,7 +227,7 @@ byte[] getAvailIn() { void readHeader() throws IOException { - byte[] empty = "".getBytes(); + byte[] empty = "".getBytes(StandardCharsets.UTF_8); inflater.setInput(empty, 0, 0, false); inflater.setOutput(empty, 0, 0); @@ -239,7 +243,7 @@ void readHeader() throws IOException { throw new IOException("no input"); inflater.setInput(b1); err = inflater.inflate(JZlib.Z_NO_FLUSH); - if (err != 0/* Z_OK */) + if (err != 0 /* Z_OK */) throw new IOException(inflater.msg); } while (inflater.istate.inParsingHeader()); } diff --git a/src/main/java/com/jcraft/jsch/jzlib/StaticTree.java b/src/main/java/com/jcraft/jsch/jzlib/StaticTree.java index 41f8fcc7..12a1f863 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/StaticTree.java +++ b/src/main/java/com/jcraft/jsch/jzlib/StaticTree.java @@ -31,13 +31,13 @@ package com.jcraft.jsch.jzlib; final class StaticTree { - static final private int MAX_BITS = 15; + private static final int MAX_BITS = 15; - static final private int BL_CODES = 19; - static final private int D_CODES = 30; - static final private int LITERALS = 256; - static final private int LENGTH_CODES = 29; - static final private int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private static final int BL_CODES = 19; + private static final int D_CODES = 30; + private static final int LITERALS = 256; + private static final int LENGTH_CODES = 29; + private static final int L_CODES = (LITERALS + 1 + LENGTH_CODES); // Bit length codes must not exceed MAX_BL_BITS bits static final int MAX_BL_BITS = 7; diff --git a/src/main/java/com/jcraft/jsch/jzlib/Tree.java b/src/main/java/com/jcraft/jsch/jzlib/Tree.java index 261311eb..9668bdf2 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/Tree.java +++ b/src/main/java/com/jcraft/jsch/jzlib/Tree.java @@ -31,13 +31,13 @@ package com.jcraft.jsch.jzlib; final class Tree { - static final private int MAX_BITS = 15; - static final private int BL_CODES = 19; - static final private int D_CODES = 30; - static final private int LITERALS = 256; - static final private int LENGTH_CODES = 29; - static final private int L_CODES = (LITERALS + 1 + LENGTH_CODES); - static final private int HEAP_SIZE = (2 * L_CODES + 1); + private static final int MAX_BITS = 15; + private static final int BL_CODES = 19; + private static final int D_CODES = 30; + private static final int LITERALS = 256; + private static final int LENGTH_CODES = 29; + private static final int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private static final int HEAP_SIZE = (2 * L_CODES + 1); // Bit length codes must not exceed MAX_BL_BITS bits static final int MAX_BL_BITS = 7; @@ -67,7 +67,6 @@ final class Tree { static final byte[] bl_order = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - // The lengths of the bit length codes are sent in order of decreasing // probability, to avoid transmitting the lengths for unused bit // length codes. @@ -205,7 +204,7 @@ void gen_bitlen(Deflate s) { if (m > max_code) continue; if (tree[m * 2 + 1] != bits) { - s.opt_len += ((long) bits - (long) tree[m * 2 + 1]) * (long) tree[m * 2]; + s.opt_len += (int) (((long) bits - (long) tree[m * 2 + 1]) * (long) tree[m * 2]); tree[m * 2 + 1] = (short) bits; } n--; @@ -304,7 +303,7 @@ void build_tree(Deflate s) { // the given tree and the field len is set for all tree elements. // OUT assertion: the field code is set for all tree elements of non // zero code length. - private final static void gen_codes(short[] tree, // the tree to decorate + private static final void gen_codes(short[] tree, // the tree to decorate int max_code, // largest code with non zero frequency short[] bl_count, // number of codes at each bit length short[] next_code) { @@ -337,7 +336,7 @@ private final static void gen_codes(short[] tree, // the tree to decorate // Reverse the first len bits of a code, using straightforward code (a faster // method would use a table) // IN assertion: 1 <= len <= 15 - private final static int bi_reverse(int code, // the value to invert + private static final int bi_reverse(int code, // the value to invert int len // its bit length ) { int res = 0; @@ -349,4 +348,3 @@ private final static int bi_reverse(int code, // the value to invert return res >>> 1; } } - diff --git a/src/main/java/com/jcraft/jsch/jzlib/ZStream.java b/src/main/java/com/jcraft/jsch/jzlib/ZStream.java index 5064ca3e..bffc0938 100644 --- a/src/main/java/com/jcraft/jsch/jzlib/ZStream.java +++ b/src/main/java/com/jcraft/jsch/jzlib/ZStream.java @@ -32,26 +32,26 @@ class ZStream { - static final private int MAX_WBITS = 15; // 32K LZ77 window - static final private int DEF_WBITS = MAX_WBITS; - - static final private int Z_NO_FLUSH = 0; - static final private int Z_PARTIAL_FLUSH = 1; - static final private int Z_SYNC_FLUSH = 2; - static final private int Z_FULL_FLUSH = 3; - static final private int Z_FINISH = 4; - - static final private int MAX_MEM_LEVEL = 9; - - static final private int Z_OK = 0; - static final private int Z_STREAM_END = 1; - static final private int Z_NEED_DICT = 2; - static final private int Z_ERRNO = -1; - static final private int Z_STREAM_ERROR = -2; - static final private int Z_DATA_ERROR = -3; - static final private int Z_MEM_ERROR = -4; - static final private int Z_BUF_ERROR = -5; - static final private int Z_VERSION_ERROR = -6; + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_WBITS = MAX_WBITS; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int MAX_MEM_LEVEL = 9; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; byte[] next_in; // next input byte int next_in_index; @@ -358,7 +358,6 @@ void setNextOutIndex(int next_out_index) { int getAvailOut() { return avail_out; - } void setAvailOut(int avail_out) { diff --git a/src/main/java11/com/jcraft/jsch/jce/XDH.java b/src/main/java11/com/jcraft/jsch/jce/XDH.java index 3e1dadb5..4c07a54c 100644 --- a/src/main/java11/com/jcraft/jsch/jce/XDH.java +++ b/src/main/java11/com/jcraft/jsch/jce/XDH.java @@ -27,11 +27,15 @@ package com.jcraft.jsch.jce; import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.interfaces.XECPublicKey; +import java.security.spec.NamedParameterSpec; +import java.security.spec.XECPublicKeySpec; import java.util.Arrays; -import java.security.*; -import javax.crypto.*; -import java.security.spec.*; -import java.security.interfaces.*; +import javax.crypto.KeyAgreement; public class XDH implements com.jcraft.jsch.XDH { byte[] Q_array; @@ -44,11 +48,13 @@ public class XDH implements com.jcraft.jsch.XDH { public void init(String name, int keylen) throws Exception { this.keylen = keylen; myKeyAgree = KeyAgreement.getInstance("XDH"); - KeyPairGenXEC kpair = new KeyPairGenXEC(); - kpair.init(name); - publicKey = kpair.getPublicKey(); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH"); + NamedParameterSpec paramSpec = new NamedParameterSpec(name); + kpg.initialize(paramSpec); + KeyPair kp = kpg.genKeyPair(); + publicKey = (XECPublicKey) kp.getPublic(); Q_array = rotate(publicKey.getU().toByteArray()); - myKeyAgree.init(kpair.getPrivateKey()); + myKeyAgree.init(kp.getPrivate()); } @Override diff --git a/src/main/java15/com/jcraft/jsch/jce/KeyPairGenEdDSA.java b/src/main/java15/com/jcraft/jsch/jce/KeyPairGenEdDSA.java index cf648614..fa02abb4 100644 --- a/src/main/java15/com/jcraft/jsch/jce/KeyPairGenEdDSA.java +++ b/src/main/java15/com/jcraft/jsch/jce/KeyPairGenEdDSA.java @@ -26,9 +26,11 @@ package com.jcraft.jsch.jce; -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.EdECPrivateKey; +import java.security.interfaces.EdECPublicKey; +import java.security.spec.EdECPoint; import java.util.Arrays; public class KeyPairGenEdDSA implements com.jcraft.jsch.KeyPairGenEdDSA { @@ -50,7 +52,7 @@ public void init(String name, int keylen) throws Exception { prv = prvKey.getBytes().get(); pub = rotate(point.getY().toByteArray()); if (point.isXOdd()) { - pub[pub.length - 1] |= 0x80; + pub[pub.length - 1] |= (byte) 0x80; } } diff --git a/src/main/java15/com/jcraft/jsch/jce/SignatureEdDSA.java b/src/main/java15/com/jcraft/jsch/jce/SignatureEdDSA.java index ee0d8828..2386e20f 100644 --- a/src/main/java15/com/jcraft/jsch/jce/SignatureEdDSA.java +++ b/src/main/java15/com/jcraft/jsch/jce/SignatureEdDSA.java @@ -26,12 +26,18 @@ package com.jcraft.jsch.jce; +import com.jcraft.jsch.Buffer; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.security.*; -import java.security.spec.*; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.EdECPoint; +import java.security.spec.EdECPrivateKeySpec; +import java.security.spec.EdECPublicKeySpec; +import java.security.spec.NamedParameterSpec; import java.util.Arrays; -import com.jcraft.jsch.Buffer; abstract class SignatureEdDSA implements com.jcraft.jsch.SignatureEdDSA { @@ -46,7 +52,7 @@ abstract class SignatureEdDSA implements com.jcraft.jsch.SignatureEdDSA { @Override public void init() throws Exception { - signature = java.security.Signature.getInstance("EdDSA"); + signature = Signature.getInstance("EdDSA"); keyFactory = KeyFactory.getInstance("EdDSA"); } diff --git a/src/main/java16/com/jcraft/jsch/UnixDomainSocketFactory.java b/src/main/java16/com/jcraft/jsch/UnixDomainSocketFactory.java index 48995918..cb0f42f5 100644 --- a/src/main/java16/com/jcraft/jsch/UnixDomainSocketFactory.java +++ b/src/main/java16/com/jcraft/jsch/UnixDomainSocketFactory.java @@ -26,9 +26,6 @@ package com.jcraft.jsch; -import com.jcraft.jsch.AgentProxyException; -import com.jcraft.jsch.USocketFactory; - import java.io.IOException; import java.net.StandardProtocolFamily; import java.net.UnixDomainSocketAddress; diff --git a/src/main/java9/com/jcraft/jsch/JavaVersion.java b/src/main/java9/com/jcraft/jsch/JavaVersion.java index c3f8afb3..6d9fd0d9 100644 --- a/src/main/java9/com/jcraft/jsch/JavaVersion.java +++ b/src/main/java9/com/jcraft/jsch/JavaVersion.java @@ -1,7 +1,10 @@ package com.jcraft.jsch; +import com.jcraft.jsch.annotations.SuppressForbiddenApi; + final class JavaVersion { + @SuppressForbiddenApi("jdk-deprecated") static int getVersion() { return Runtime.version().major(); } diff --git a/src/main/java9/module-info.java b/src/main/java9/module-info.java index 28e87027..10139cc9 100644 --- a/src/main/java9/module-info.java +++ b/src/main/java9/module-info.java @@ -1,12 +1,12 @@ module com.jcraft.jsch { - exports com.jcraft.jsch; + exports com.jcraft.jsch; - requires java.security.jgss; - requires static java.logging; - requires static org.apache.logging.log4j; - requires static org.slf4j; - requires static org.bouncycastle.provider; - requires static org.newsclub.net.unix; - requires static com.sun.jna; - requires static com.sun.jna.platform; + requires static com.sun.jna; + requires static com.sun.jna.platform; + requires static java.logging; + requires static java.security.jgss; + requires static org.apache.logging.log4j; + requires static org.bouncycastle.provider; + requires static org.newsclub.net.unix; + requires static org.slf4j; } diff --git a/src/test/java/com/jcraft/jsch/AbstractBufferMargin.java b/src/test/java/com/jcraft/jsch/AbstractBufferMargin.java index 98cadfc2..3d85fa53 100644 --- a/src/test/java/com/jcraft/jsch/AbstractBufferMargin.java +++ b/src/test/java/com/jcraft/jsch/AbstractBufferMargin.java @@ -6,7 +6,6 @@ import com.github.valfirst.slf4jtest.LoggingEvent; import com.github.valfirst.slf4jtest.TestLogger; import com.github.valfirst.slf4jtest.TestLoggerFactory; -import com.google.common.io.ByteStreams; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -15,8 +14,10 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.input.BoundedInputStream; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -45,26 +46,30 @@ public abstract class AbstractBufferMargin { private Slf4jLogConsumer sshdLogConsumer; @Container - public GenericContainer sshd = new GenericContainer<>( - new ImageFromDockerfile().withFileFromClasspath("asyncsshd.py", "docker/asyncsshd.py") - .withFileFromClasspath("ssh_host_ed448_key", "docker/ssh_host_ed448_key") - .withFileFromClasspath("ssh_host_ed448_key.pub", "docker/ssh_host_ed448_key.pub") - .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") - .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") - .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") - .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") - .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") - .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") - .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") - .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") - .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") - .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") - .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") - .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") - .withFileFromClasspath("authorized_keys", "docker/authorized_keys") - .withFileFromClasspath("Dockerfile", "docker/Dockerfile.asyncssh") - .withBuildArg("MAX_PKTSIZE", Integer.toString(maxPktSize()))) - .withExposedPorts(22); + public GenericContainer sshd; + + protected AbstractBufferMargin(int maxPktSize) { + sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("asyncsshd.py", "docker/asyncsshd.py") + .withFileFromClasspath("ssh_host_ed448_key", "docker/ssh_host_ed448_key") + .withFileFromClasspath("ssh_host_ed448_key.pub", "docker/ssh_host_ed448_key.pub") + .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.asyncssh") + .withBuildArg("MAX_PKTSIZE", Integer.toString(maxPktSize))) + .withExposedPorts(22); + } @BeforeAll public static void beforeAll() { @@ -101,8 +106,6 @@ public static void afterAll() { sshdLogger.clearAll(); } - protected abstract int maxPktSize(); - protected void doTestSftp(String cipher, String mac, String compression) throws Exception { JSch ssh = createRSAIdentity(); Session session = createSession(ssh); @@ -138,7 +141,8 @@ private JSch createRSAIdentity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } @@ -207,7 +211,9 @@ private void doScp(Session session, boolean debugException) throws Exception { byte[] buf = new byte[17]; is.read(buf, 0, 17); sendAck(os); - Files.copy(ByteStreams.limit(is, 100L * 1024L), out); + Files.copy( + BoundedInputStream.builder().setMaxCount(100L * 1024L).setInputStream(is).get(), + out); checkAck(is); sendAck(os); } diff --git a/src/test/java/com/jcraft/jsch/Algorithms2IT.java b/src/test/java/com/jcraft/jsch/Algorithms2IT.java index a070f5d4..6c9e37d0 100644 --- a/src/test/java/com/jcraft/jsch/Algorithms2IT.java +++ b/src/test/java/com/jcraft/jsch/Algorithms2IT.java @@ -16,6 +16,7 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; @@ -146,7 +147,7 @@ public void testKEXs(String kex) throws Exception { session.setConfig("kex", kex); doSftp(session, true); - String expected = String.format("kex: algorithm: %s.*", kex); + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); checkLogs(expected); } @@ -177,9 +178,9 @@ public void testDHGEXSizes(String kex, String size) throws Exception { session.setConfig("dhgex_preferred", size); doSftp(session, true); - String expectedKex = String.format("kex: algorithm: %s.*", kex); - String expectedSizes = - String.format("SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); + String expectedKex = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + String expectedSizes = String.format(Locale.ROOT, + "SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); checkLogs(expectedKex); checkLogs(expectedSizes); } @@ -235,7 +236,7 @@ public void testRSA(String keyType) throws Exception { session.setConfig("server_host_key", keyType); doSftp(session, true); - String expected = String.format("kex: host key algorithm: %s.*", keyType); + String expected = String.format(Locale.ROOT, "kex: host key algorithm: %s.*", keyType); checkLogs(expected); } @@ -250,8 +251,8 @@ public void testCiphers(String cipher, String compression) throws Exception { session.setConfig("compression.c2s", compression); doSftp(session, true); - String expectedS2C = String.format("kex: server->client cipher: %s.*", cipher); - String expectedC2S = String.format("kex: client->server cipher: %s.*", cipher); + String expectedS2C = String.format(Locale.ROOT, "kex: server->client cipher: %s.*", cipher); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server cipher: %s.*", cipher); checkLogs(expectedS2C); checkLogs(expectedC2S); } @@ -274,8 +275,8 @@ public void testMACs(String mac, String compression) throws Exception { session.setConfig("cipher.c2s", "aes128-ctr"); doSftp(session, true); - String expectedS2C = String.format("kex: server->client .* MAC: %s.*", mac); - String expectedC2S = String.format("kex: client->server .* MAC: %s.*", mac); + String expectedS2C = String.format(Locale.ROOT, "kex: server->client .* MAC: %s.*", mac); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server .* MAC: %s.*", mac); checkLogs(expectedS2C); checkLogs(expectedC2S); } @@ -304,7 +305,7 @@ public void testCompressionImpls(String impl) throws Exception { session.setConfig("zlib", impl); doSftp(session, true); - String expectedImpl = String.format("zlib using %s", impl); + String expectedImpl = String.format(Locale.ROOT, "zlib using %s", impl); String expectedS2C = "kex: server->client .* compression: zlib.*"; String expectedC2S = "kex: client->server .* compression: zlib.*"; checkLogs(expectedImpl); @@ -332,7 +333,8 @@ private JSch createEd448Identity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/Algorithms3IT.java b/src/test/java/com/jcraft/jsch/Algorithms3IT.java index 08705968..3da1f74b 100644 --- a/src/test/java/com/jcraft/jsch/Algorithms3IT.java +++ b/src/test/java/com/jcraft/jsch/Algorithms3IT.java @@ -14,6 +14,7 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; @@ -96,8 +97,8 @@ public void testCiphers(String cipher, String compression) throws Exception { session.setConfig("compression.c2s", compression); doSftp(session, true); - String expectedS2C = String.format("kex: server->client cipher: %s.*", cipher); - String expectedC2S = String.format("kex: client->server cipher: %s.*", cipher); + String expectedS2C = String.format(Locale.ROOT, "kex: server->client cipher: %s.*", cipher); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server cipher: %s.*", cipher); checkLogs(expectedS2C); checkLogs(expectedC2S); } @@ -113,7 +114,8 @@ private JSch createRSAIdentity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/Algorithms4IT.java b/src/test/java/com/jcraft/jsch/Algorithms4IT.java new file mode 100644 index 00000000..4d01b5b7 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/Algorithms4IT.java @@ -0,0 +1,190 @@ +package com.jcraft.jsch; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class Algorithms4IT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(Algorithms4IT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config.openssh96") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.openssh96")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @ParameterizedTest + @ValueSource(strings = {"sntrup761x25519-sha512@openssh.com"}) + public void testBCKEXs(String kex) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("kex", kex); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + checkLogs(expected); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + // Skip OpenSSH log checks, as log output from Docker falls behind and these assertions + // frequently run before they are output + // Optional actualSshd = + // sshdLogger.getAllLoggingEvents().stream() + // .map(LoggingEvent::getFormattedMessage) + // .filter(msg -> msg.matches("STDERR: debug1: " + expected)) + // .findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + // assertTrue(actualSshd.isPresent(), () -> "sshd: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/src/test/java/com/jcraft/jsch/AlgorithmsIT.java b/src/test/java/com/jcraft/jsch/AlgorithmsIT.java index bc83053f..676970e2 100644 --- a/src/test/java/com/jcraft/jsch/AlgorithmsIT.java +++ b/src/test/java/com/jcraft/jsch/AlgorithmsIT.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; @@ -115,7 +116,7 @@ public void testJava11KEXs(String kex) throws Exception { session.setConfig("kex", kex); doSftp(session, true); - String expected = String.format("kex: algorithm: %s.*", kex); + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); checkLogs(expected); } @@ -128,7 +129,7 @@ public void testBCKEXs(String kex) throws Exception { session.setConfig("kex", kex); doSftp(session, true); - String expected = String.format("kex: algorithm: %s.*", kex); + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); checkLogs(expected); } @@ -144,7 +145,7 @@ public void testKEXs(String kex) throws Exception { session.setConfig("kex", kex); doSftp(session, true); - String expected = String.format("kex: algorithm: %s.*", kex); + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); checkLogs(expected); } @@ -164,9 +165,9 @@ public void testDHGEXSizes(String kex, String size) throws Exception { session.setConfig("dhgex_preferred", size); doSftp(session, true); - String expectedKex = String.format("kex: algorithm: %s.*", kex); - String expectedSizes = - String.format("SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); + String expectedKex = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + String expectedSizes = String.format(Locale.ROOT, + "SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); checkLogs(expectedKex); checkLogs(expectedSizes); } @@ -257,7 +258,7 @@ public void testRSA(String keyType) throws Exception { session.setConfig("server_host_key", keyType); doSftp(session, true); - String expected = String.format("kex: host key algorithm: %s.*", keyType); + String expected = String.format(Locale.ROOT, "kex: host key algorithm: %s.*", keyType); checkLogs(expected); } @@ -296,8 +297,8 @@ public void testCiphers(String cipher, String compression) throws Exception { session.setConfig("compression.c2s", compression); doSftp(session, true); - String expectedS2C = String.format("kex: server->client cipher: %s.*", cipher); - String expectedC2S = String.format("kex: client->server cipher: %s.*", cipher); + String expectedS2C = String.format(Locale.ROOT, "kex: server->client cipher: %s.*", cipher); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server cipher: %s.*", cipher); checkLogs(expectedS2C); checkLogs(expectedC2S); } @@ -329,8 +330,8 @@ public void testMACs(String mac, String compression) throws Exception { session.setConfig("cipher.c2s", "aes128-ctr"); doSftp(session, true); - String expectedS2C = String.format("kex: server->client .* MAC: %s.*", mac); - String expectedC2S = String.format("kex: client->server .* MAC: %s.*", mac); + String expectedS2C = String.format(Locale.ROOT, "kex: server->client .* MAC: %s.*", mac); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server .* MAC: %s.*", mac); checkLogs(expectedS2C); checkLogs(expectedC2S); } @@ -344,8 +345,10 @@ public void testCompressions(String compression) throws Exception { session.setConfig("compression.c2s", compression); doSftp(session, true); - String expectedS2C = String.format("kex: server->client .* compression: %s.*", compression); - String expectedC2S = String.format("kex: client->server .* compression: %s.*", compression); + String expectedS2C = + String.format(Locale.ROOT, "kex: server->client .* compression: %s.*", compression); + String expectedC2S = + String.format(Locale.ROOT, "kex: client->server .* compression: %s.*", compression); checkLogs(expectedS2C); checkLogs(expectedC2S); } @@ -360,7 +363,7 @@ public void testCompressionImpls(String impl) throws Exception { session.setConfig("zlib@openssh.com", impl); doSftp(session, true); - String expectedImpl = String.format("zlib using %s", impl); + String expectedImpl = String.format(Locale.ROOT, "zlib using %s", impl); String expectedS2C = "kex: server->client .* compression: zlib@openssh\\.com.*"; String expectedC2S = "kex: client->server .* compression: zlib@openssh\\.com.*"; checkLogs(expectedImpl); @@ -392,7 +395,7 @@ public void testFingerprintHashes(String fingerprint) throws Exception { } catch (JSchException expected) { } - String expected = String.format("RSA key fingerprint is %s.", fingerprint); + String expected = String.format(Locale.ROOT, "RSA key fingerprint is %s.", fingerprint); List msgs = userInfo.getMessages().stream().map(msg -> msg.split("\n")) .flatMap(Arrays::stream).collect(toList()); Optional actual = msgs.stream().filter(msg -> msg.equals(expected)).findFirst(); @@ -460,7 +463,8 @@ private JSch createEd25519Identity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/ExtInfoInAuthIT.java b/src/test/java/com/jcraft/jsch/ExtInfoInAuthIT.java new file mode 100644 index 00000000..fb58e2c0 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/ExtInfoInAuthIT.java @@ -0,0 +1,234 @@ +package com.jcraft.jsch; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class ExtInfoInAuthIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config.ExtInfoInAuthIT") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.ExtInfoInAuthIT")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testExtInfoInAuthYes() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh, "rsa"); + session.setConfig("enable_ext_info_in_auth", "yes"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-rsa"); + doSftp(session, "rsa", true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,ext-info-s,.*"; + String expectedClientKex = "client proposal: KEX algorithms: .*,ext-info-c,.*"; + String expected1 = "ext-info messaging supported by server"; + String expected2 = "SSH_MSG_EXT_INFO sent"; + String expectedServerSigAlgs1 = "server-sig-algs="; + String expectedServerSigAlgs2 = "server-sig-algs=<.*ssh-rsa.*>"; + String expectedServerSigAlgs3 = "server-sig-algs=<.*ecdsa.*>"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expectedServerSigAlgs1); + checkLogs(expectedServerSigAlgs2); + checkNoLogs(expectedServerSigAlgs3); + } + + @Test + public void testExtInfoInAuthNo() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh, "ecdsa"); + session.setConfig("enable_ext_info_in_auth", "no"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-rsa"); + session.setTimeout(timeout); + + assertThrows(JSchException.class, session::connect, "Auth fail for methods 'publickey'"); + + String expectedServerKex = "server proposal: KEX algorithms: .*,ext-info-s,.*"; + String expectedClientKex = "client proposal: KEX algorithms: .*,ext-info-c,.*"; + String expected1 = "ext-info messaging supported by server"; + String expected2 = "SSH_MSG_EXT_INFO sent"; + String expectedServerSigAlgs1 = "server-sig-algs="; + String expectedServerSigAlgs2 = "server-sig-algs=<.*ssh-rsa.*>"; + String expectedServerSigAlgs3 = "server-sig-algs=<.*ecdsa.*>"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkNoLogs(expected2); + checkLogs(expectedServerSigAlgs1); + checkNoLogs(expectedServerSigAlgs2); + checkNoLogs(expectedServerSigAlgs3); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh, String username) throws Exception { + Session session = ssh.getSession(username, sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, String username, boolean debugException) throws Exception { + String testFile = String.format(Locale.ROOT, "/%s/test", username); + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), testFile); + sftp.get(testFile, out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private void checkNoLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertFalse(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java b/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java index 4352136c..5bb4ec66 100644 --- a/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java +++ b/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java @@ -8,6 +8,7 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.testcontainers.containers.GenericContainer; @@ -60,9 +61,9 @@ public void testJSchAlgoNegoFailException(String algorithmName, String serverPro JSchAlgoNegoFailException e = assertThrows(JSchAlgoNegoFailException.class, session::connect); if (algorithmName.equals("kex")) { - jschProposal += ",ext-info-c"; + jschProposal += ",ext-info-c,kex-strict-c-v00@openssh.com"; } - String message = String.format( + String message = String.format(Locale.ROOT, "Algorithm negotiation fail: algorithmName=\"%s\" jschProposal=\"%s\" serverProposal=\"%s\"", algorithmName, jschProposal, serverProposal); @@ -83,7 +84,8 @@ private JSch createRSAIdentity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/JSchStrictKexExceptionIT.java b/src/test/java/com/jcraft/jsch/JSchStrictKexExceptionIT.java new file mode 100644 index 00000000..e8a7fef6 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/JSchStrictKexExceptionIT.java @@ -0,0 +1,91 @@ +package com.jcraft.jsch; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class JSchStrictKexExceptionIT { + + private static final int timeout = 2000; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @Test + public void testEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "yes"); + session.setConfig("require_strict_kex", "yes"); + session.setTimeout(timeout); + + assertThrows(JSchStrictKexException.class, session::connect, + "Strict KEX not supported by server"); + } + + @Test + public void testNoEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "no"); + session.setConfig("require_strict_kex", "yes"); + session.setTimeout(timeout); + + assertThrows(JSchStrictKexException.class, session::connect, + "Strict KEX not supported by server"); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/src/test/java/com/jcraft/jsch/JSchTest.java b/src/test/java/com/jcraft/jsch/JSchTest.java index f1e00a36..e2735a7a 100644 --- a/src/test/java/com/jcraft/jsch/JSchTest.java +++ b/src/test/java/com/jcraft/jsch/JSchTest.java @@ -1,11 +1,11 @@ package com.jcraft.jsch; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import java.util.Hashtable; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; class JSchTest { @@ -64,7 +64,7 @@ void checkLoggerBehavior() throws Exception { assertSame(JSch.DEVNULL, jsch.getInstanceLogger(), "instance logger should be DEVNULL"); } - final static class TestLogger implements Logger { + static final class TestLogger implements Logger { @Override public boolean isEnabled(int level) { return true; diff --git a/src/test/java/com/jcraft/jsch/KeyExchangeTest.java b/src/test/java/com/jcraft/jsch/KeyExchangeTest.java new file mode 100644 index 00000000..861013fe --- /dev/null +++ b/src/test/java/com/jcraft/jsch/KeyExchangeTest.java @@ -0,0 +1,236 @@ +package com.jcraft.jsch; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.Arrays; +import java.util.Random; +import org.junit.jupiter.api.Test; + +public class KeyExchangeTest { + + private final Random random = new Random(); + private final KeyExchange kex = new TestKex(); + + @Test + public void testNormalize0() { + byte[] secret = new byte[0]; + doNormalize(secret); + } + + @Test + public void testNormalize1() { + byte[] secret = new byte[1]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + doNormalize(secret); + } + } + + @Test + public void testNormalize2() { + byte[] secret = new byte[2]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + doNormalize(secret); + } + } + } + + @Test + public void testNormalize3() { + byte[] secret = new byte[3]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + for (int k = 0; k <= 0xff; k++) { + secret[2] = (byte) k; + doNormalize(secret); + } + } + } + } + + @Test + public void testNormalizeRandom() { + for (int i = 0; i < 1000000; i++) { + byte[] secret = new byte[64]; + random.nextBytes(secret); + doNormalize(secret); + } + } + + @Test + public void testEncodeAsMPInt1() { + byte[] secret = new byte[1]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + doEncodeAsMPInt(secret); + } + } + + @Test + public void testEncodeAsMPInt2() { + byte[] secret = new byte[2]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + doEncodeAsMPInt(secret); + } + } + } + + @Test + public void testEncodeAsMPInt3() { + byte[] secret = new byte[3]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + for (int k = 0; k <= 0xff; k++) { + secret[2] = (byte) k; + doEncodeAsMPInt(secret); + } + } + } + } + + @Test + public void testEncodeAsMPIntRandom() { + for (int i = 0; i < 1000000; i++) { + byte[] secret = new byte[64]; + random.nextBytes(secret); + doEncodeAsMPInt(secret); + } + } + + @Test + public void testEncodeAsString0() { + byte[] secret = new byte[0]; + doEncodeAsString(secret); + } + + @Test + public void testEncodeAsString1() { + byte[] secret = new byte[1]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + doEncodeAsString(secret); + } + } + + @Test + public void testEncodeAsString2() { + byte[] secret = new byte[2]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + doEncodeAsString(secret); + } + } + } + + @Test + public void testEncodeAsString3() { + byte[] secret = new byte[3]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + for (int k = 0; k <= 0xff; k++) { + secret[2] = (byte) k; + doEncodeAsString(secret); + } + } + } + } + + @Test + public void testEncodeAsStringRandom() { + for (int i = 0; i < 1000000; i++) { + byte[] secret = new byte[64]; + random.nextBytes(secret); + doEncodeAsString(secret); + } + } + + private void doNormalize(byte[] secret) { + byte[] expected = normalize(Arrays.copyOf(secret, secret.length)); + byte[] actual = kex.normalize(Arrays.copyOf(secret, secret.length)); + try { + assertArrayEquals(expected, actual); + } catch (Exception e) { + System.out.println(" secret = " + Arrays.toString(secret)); + System.out.println("expected = " + Arrays.toString(expected)); + System.out.println(" actual = " + Arrays.toString(actual)); + throw e; + } + } + + // Copy of old implementation + private static byte[] normalize(byte[] secret) { + if (secret.length > 1 && secret[0] == 0 && (secret[1] & 0x80) == 0) { + byte[] tmp = new byte[secret.length - 1]; + System.arraycopy(secret, 1, tmp, 0, tmp.length); + Util.bzero(secret); + return normalize(tmp); + } else { + return secret; + } + } + + private void doEncodeAsMPInt(byte[] secret) { + Buffer b = new Buffer(); + b.putMPInt(secret); + byte[] expected = new byte[b.getLength()]; + b.getByte(expected); + byte[] actual = kex.encodeAsMPInt(Arrays.copyOf(secret, secret.length)); + try { + assertArrayEquals(expected, actual); + } catch (Throwable t) { + System.out.println(" secret = " + Arrays.toString(secret)); + System.out.println("expected = " + Arrays.toString(expected)); + System.out.println(" actual = " + Arrays.toString(actual)); + throw t; + } + } + + private void doEncodeAsString(byte[] secret) { + Buffer b = new Buffer(); + b.putString(secret); + byte[] expected = new byte[b.getLength()]; + b.getByte(expected); + byte[] actual = kex.encodeAsString(Arrays.copyOf(secret, secret.length)); + try { + assertArrayEquals(expected, actual); + } catch (Throwable t) { + System.out.println(" secret = " + Arrays.toString(secret)); + System.out.println("expected = " + Arrays.toString(expected)); + System.out.println(" actual = " + Arrays.toString(actual)); + throw t; + } + } + + static class TestKex extends KeyExchange { + + @Override + public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean next(Buffer buf) throws Exception { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public int getState() { + throw new UnsupportedOperationException("Not supported"); + } + } +} diff --git a/src/test/java/com/jcraft/jsch/KeyPair2IT.java b/src/test/java/com/jcraft/jsch/KeyPair2IT.java index fc6d7f24..f2b66b9a 100644 --- a/src/test/java/com/jcraft/jsch/KeyPair2IT.java +++ b/src/test/java/com/jcraft/jsch/KeyPair2IT.java @@ -1,5 +1,9 @@ package com.jcraft.jsch; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URISyntaxException; +import java.nio.file.Paths; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.testcontainers.containers.GenericContainer; @@ -7,11 +11,6 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import java.net.URISyntaxException; -import java.nio.file.Paths; - -import static org.junit.jupiter.api.Assertions.assertTrue; - @Testcontainers public class KeyPair2IT { @@ -56,7 +55,6 @@ void connectWithPublicKey(String path, String password, String keyType) throws E } finally { session.disconnect(); } - } @ParameterizedTest @@ -97,9 +95,7 @@ public boolean promptYesNo(String message) { } @Override - public void showMessage(String message) { - - } + public void showMessage(String message) {} }); if (keyType != null) { @@ -111,7 +107,6 @@ public void showMessage(String message) { } finally { session.disconnect(); } - } private JSch createIdentity(String path, String password) diff --git a/src/test/java/com/jcraft/jsch/KeyPair2Test.java b/src/test/java/com/jcraft/jsch/KeyPair2Test.java index 99b95e58..bbfa5189 100644 --- a/src/test/java/com/jcraft/jsch/KeyPair2Test.java +++ b/src/test/java/com/jcraft/jsch/KeyPair2Test.java @@ -1,17 +1,18 @@ package com.jcraft.jsch; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class KeyPair2Test { diff --git a/src/test/java/com/jcraft/jsch/KeyPairIT.java b/src/test/java/com/jcraft/jsch/KeyPairIT.java index aef8dec1..a356dd49 100644 --- a/src/test/java/com/jcraft/jsch/KeyPairIT.java +++ b/src/test/java/com/jcraft/jsch/KeyPairIT.java @@ -1,5 +1,9 @@ package com.jcraft.jsch; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URISyntaxException; +import java.nio.file.Paths; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.testcontainers.containers.GenericContainer; @@ -7,11 +11,6 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import java.net.URISyntaxException; -import java.nio.file.Paths; - -import static org.junit.jupiter.api.Assertions.assertTrue; - @Testcontainers public class KeyPairIT { @@ -39,7 +38,6 @@ void connectWithPublicKey(String path, String password, String keyType) throws E } finally { session.disconnect(); } - } @ParameterizedTest @@ -80,9 +78,7 @@ public boolean promptYesNo(String message) { } @Override - public void showMessage(String message) { - - } + public void showMessage(String message) {} }); if (keyType != null) { @@ -94,7 +90,6 @@ public void showMessage(String message) { } finally { session.disconnect(); } - } private JSch createIdentity(String path, String password) diff --git a/src/test/java/com/jcraft/jsch/KeyPairTest.java b/src/test/java/com/jcraft/jsch/KeyPairTest.java index 084a5eae..faada575 100644 --- a/src/test/java/com/jcraft/jsch/KeyPairTest.java +++ b/src/test/java/com/jcraft/jsch/KeyPairTest.java @@ -1,20 +1,23 @@ package com.jcraft.jsch; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Stream; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; class KeyPairTest { @@ -35,6 +38,12 @@ static Stream keyArgs() { Arguments.of("docker/ssh_host_dsa_key", null, "ssh-dss"), // encrypted dsa Arguments.of("encrypted_openssh_private_key_dsa", "secret123", "ssh-dss"), + // unencrypted RSA with windows (\r\n) line endings + Arguments.of("issue362_rsa", null, "ssh-rsa"), + Arguments.of("issue_369_rsa_opensshv1", null, "ssh-rsa"), + Arguments.of("issue_369_rsa_pem", null, "ssh-rsa"), + Arguments.of("encrypted_issue_369_rsa_opensshv1", "secret123", "ssh-rsa"), + Arguments.of("encrypted_issue_369_rsa_pem", "secret123", "ssh-rsa"), // ecdsa EC private key format Arguments.of("docker/id_ecdsa256", null, "ecdsa-sha2-nistp256"), // Arguments.of("docker/id_ecdsa384", null, "ecdsa-sha2-nistp384"), // @@ -156,7 +165,7 @@ void decryptEncryptedOpensshKey(String keyFile) throws URISyntaxException, JSchE final String prvkey = Paths.get(ClassLoader.getSystemResource(keyFile).toURI()).toFile().getAbsolutePath(); assertTrue(new File(prvkey).exists()); - IdentityFile identity = IdentityFile.newInstance(prvkey, null, jSch); + IdentityFile identity = IdentityFile.newInstance(prvkey, null, jSch.instLogger); // Decrypt the key file assertTrue(identity.getKeyPair().decrypt("secret123")); @@ -169,5 +178,4 @@ void decryptEncryptedOpensshKey(String keyFile) throws URISyntaxException, JSchE assertTrue(identity.getKeyPair().decrypt((byte[]) null)); assertTrue(identity.getKeyPair().decrypt((String) null)); } - } diff --git a/src/test/java/com/jcraft/jsch/KnownHostsTest.java b/src/test/java/com/jcraft/jsch/KnownHostsTest.java index 527aad56..c74df310 100644 --- a/src/test/java/com/jcraft/jsch/KnownHostsTest.java +++ b/src/test/java/com/jcraft/jsch/KnownHostsTest.java @@ -10,6 +10,10 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; + +import com.jcraft.jsch.KnownHosts.HashedHostKey; +import com.jcraft.jsch.jce.HMACSHA256; +import com.jcraft.jsch.jce.HMACSHA512; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -35,23 +39,19 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import com.jcraft.jsch.KnownHosts.HashedHostKey; -import com.jcraft.jsch.jce.HMACSHA1; -import com.jcraft.jsch.jce.HMACSHA256; -import com.jcraft.jsch.jce.HMACSHA512; class KnownHostsTest { - private final static String rsaKey = + private static final String rsaKey = "AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkc" + "cKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81e" + "FzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpI" + "oaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G" + "3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=="; - private final static String hashValue = + private static final String hashValue = "|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="; - private final static String hostLine = "ssh.example.com,192.168.1.61"; - private final static byte[] dsaKeyBytes = Util.str2byte(" ssh-dsa"); - private final static byte[] rsaKeyBytes = Util.str2byte(" ssh-rsa"); + private static final String hostLine = "ssh.example.com,192.168.1.61"; + private static final byte[] dsaKeyBytes = Util.str2byte(" ssh-dsa"); + private static final byte[] rsaKeyBytes = Util.str2byte(" ssh-rsa"); private LinkedList messages; private JSch jsch; private Hashtable orgConfig; @@ -201,7 +201,6 @@ MAC createHMAC(String hmacClassname) { assertEquals(HMACSHA512.class.getName(), kh.hmacsha1.getClass().getName(), "hmac class mismatch"); assertEquals("", messages.stream().collect(Collectors.joining("\r\n"))); - } @Test @@ -977,7 +976,6 @@ void sync() throws IOException { assertEquals(1, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); assertEquals("[192.277.325.5]:123: key type ssh-rsa", getHostKeysString(hosts), "unexpected hosts"); - } private String getMessagesAsString() { @@ -1142,5 +1140,4 @@ public boolean isEnabled(int level) { return true; } } - } diff --git a/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java b/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java index 104504a1..5b485ab4 100644 --- a/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java +++ b/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java @@ -14,6 +14,7 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; @@ -143,7 +144,8 @@ private JSch createRSAIdentity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/OpenSSHConfigTest.java b/src/test/java/com/jcraft/jsch/OpenSSHConfigTest.java index 67bc0b60..f40df771 100644 --- a/src/test/java/com/jcraft/jsch/OpenSSHConfigTest.java +++ b/src/test/java/com/jcraft/jsch/OpenSSHConfigTest.java @@ -1,22 +1,23 @@ package com.jcraft.jsch; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Paths; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class OpenSSHConfigTest { - Map keyMap = OpenSSHConfig.getKeymap().entrySet().stream().collect( - Collectors.toMap(entry -> entry.getValue().toUpperCase(), Map.Entry::getKey, (s, s2) -> s2)); + Map keyMap = OpenSSHConfig.getKeymap().entrySet().stream().collect(Collectors + .toMap(entry -> entry.getValue().toUpperCase(Locale.ROOT), Map.Entry::getKey, (s, s2) -> s2)); @Test void parseFile() throws IOException, URISyntaxException { @@ -52,7 +53,7 @@ void appendKexAlgorithms() throws IOException { void appendAlgorithms(String key) throws IOException { OpenSSHConfig parse = OpenSSHConfig.parse(key + " +someValue,someValue1"); ConfigRepository.Config config = parse.getConfig(""); - String mappedKey = Optional.ofNullable(keyMap.get(key.toUpperCase())).orElse(key); + String mappedKey = Optional.ofNullable(keyMap.get(key.toUpperCase(Locale.ROOT))).orElse(key); assertEquals(JSch.getConfig(mappedKey) + "," + "someValue,someValue1", config.getValue(mappedKey)); } @@ -63,7 +64,7 @@ void appendAlgorithms(String key) throws IOException { void prependAlgorithms(String key) throws IOException { OpenSSHConfig parse = OpenSSHConfig.parse(key + " ^someValue,someValue1"); ConfigRepository.Config config = parse.getConfig(""); - String mappedKey = Optional.ofNullable(keyMap.get(key.toUpperCase())).orElse(key); + String mappedKey = Optional.ofNullable(keyMap.get(key.toUpperCase(Locale.ROOT))).orElse(key); assertEquals("someValue,someValue1," + JSch.getConfig(mappedKey), config.getValue(mappedKey)); } @@ -88,4 +89,24 @@ void replaceKexAlgorithms() throws IOException { assertEquals("diffie-hellman-group1-sha1", kex.getValue("kex")); } + @Test + void parseFileWithNegations() throws IOException, URISyntaxException { + final String configFile = + Paths.get(ClassLoader.getSystemResource("config_with_negations").toURI()).toFile() + .getAbsolutePath(); + final OpenSSHConfig openSSHConfig = OpenSSHConfig.parseFile(configFile); + + assertUserEquals(openSSHConfig, "my.example.com", "u1"); + assertUserEquals(openSSHConfig, "my-jump.example.com", "jump-u1"); + assertUserEquals(openSSHConfig, "my-proxy.example.com", "proxy-u1"); + assertUserEquals(openSSHConfig, "my.example.org", "u2"); + } + + private void assertUserEquals(OpenSSHConfig openSSHConfig, String host, String expected) { + final ConfigRepository.Config config = openSSHConfig.getConfig(host); + assertNotNull(config); + String actual = config.getUser(); + assertEquals(expected, actual, String.format(Locale.ROOT, + "Expected user for host %s to be %s, but was %s", host, expected, actual)); + } } diff --git a/src/test/java/com/jcraft/jsch/PktSize128IT.java b/src/test/java/com/jcraft/jsch/PktSize128IT.java index fb095e73..a739d17b 100644 --- a/src/test/java/com/jcraft/jsch/PktSize128IT.java +++ b/src/test/java/com/jcraft/jsch/PktSize128IT.java @@ -5,6 +5,10 @@ public class PktSize128IT extends AbstractBufferMargin { + public PktSize128IT() { + super(128); + } + @ParameterizedTest @CsvSource(value = { // 16 byte tag (MAC doesn't matter) @@ -34,9 +38,4 @@ public void testSftp(String cipher, String mac, String compression) throws Excep public void testScp(String cipher, String mac, String compression) throws Exception { doTestScp(cipher, mac, compression); } - - @Override - protected int maxPktSize() { - return 128; - } } diff --git a/src/test/java/com/jcraft/jsch/PktSize160IT.java b/src/test/java/com/jcraft/jsch/PktSize160IT.java index b107c217..40c34872 100644 --- a/src/test/java/com/jcraft/jsch/PktSize160IT.java +++ b/src/test/java/com/jcraft/jsch/PktSize160IT.java @@ -5,6 +5,10 @@ public class PktSize160IT extends AbstractBufferMargin { + public PktSize160IT() { + super(160); + } + @ParameterizedTest @CsvSource(value = { // 32 byte MAC @@ -20,9 +24,4 @@ public void testSftp(String cipher, String mac, String compression) throws Excep public void testScp(String cipher, String mac, String compression) throws Exception { doTestScp(cipher, mac, compression); } - - @Override - protected int maxPktSize() { - return 160; - } } diff --git a/src/test/java/com/jcraft/jsch/PktSize192IT.java b/src/test/java/com/jcraft/jsch/PktSize192IT.java index 81af9b14..aa78cfaf 100644 --- a/src/test/java/com/jcraft/jsch/PktSize192IT.java +++ b/src/test/java/com/jcraft/jsch/PktSize192IT.java @@ -5,6 +5,10 @@ public class PktSize192IT extends AbstractBufferMargin { + public PktSize192IT() { + super(192); + } + @ParameterizedTest @CsvSource(value = { // 64 byte MAC @@ -12,9 +16,4 @@ public class PktSize192IT extends AbstractBufferMargin { public void testSftp(String cipher, String mac, String compression) throws Exception { doTestSftp(cipher, mac, compression); } - - @Override - protected int maxPktSize() { - return 192; - } } diff --git a/src/test/java/com/jcraft/jsch/PktSize32768IT.java b/src/test/java/com/jcraft/jsch/PktSize32768IT.java index e8f65dbc..e2e2e125 100644 --- a/src/test/java/com/jcraft/jsch/PktSize32768IT.java +++ b/src/test/java/com/jcraft/jsch/PktSize32768IT.java @@ -5,6 +5,10 @@ public class PktSize32768IT extends AbstractBufferMargin { + public PktSize32768IT() { + super(32768); + } + @ParameterizedTest @CsvSource(value = { // 16 byte tag (MAC doesn't matter) @@ -40,9 +44,4 @@ public void testSftp(String cipher, String mac, String compression) throws Excep public void testScp(String cipher, String mac, String compression) throws Exception { doTestScp(cipher, mac, compression); } - - @Override - protected int maxPktSize() { - return 32768; - } } diff --git a/src/test/java/com/jcraft/jsch/SSHAgentIT.java b/src/test/java/com/jcraft/jsch/SSHAgentIT.java index 0ab7c170..57afd010 100644 --- a/src/test/java/com/jcraft/jsch/SSHAgentIT.java +++ b/src/test/java/com/jcraft/jsch/SSHAgentIT.java @@ -18,6 +18,7 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; import org.junit.jupiter.api.AfterAll; @@ -353,7 +354,8 @@ private JSch createEd25519Identity(USocketFactory factory) throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java b/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java index 17478043..9f7a5df8 100644 --- a/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java +++ b/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java @@ -14,6 +14,7 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; @@ -139,8 +140,8 @@ public void testNoServerSigAlgs() throws Exception { doSftp(session, true); String expectedKex = "kex: host key algorithm: rsa-sha2-512"; - String expectedPubkeysNoServerSigs = - String.format("No server-sig-algs found, using PubkeyAcceptedAlgorithms = %s", algos); + // String expectedPubkeysNoServerSigs = String.format(Locale.ROOT, + // "No server-sig-algs found, using PubkeyAcceptedAlgorithms = %s", algos); String expectedPreauthFail1 = "ssh-rsa-sha512@ssh.com preauth failure"; String expectedPreauthFail2 = "ssh-rsa-sha384@ssh.com preauth failure"; String expectedPreauthFail3 = "ssh-rsa-sha256@ssh.com preauth failure"; @@ -167,7 +168,8 @@ private JSch createRSAIdentity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/SessionReconnectIT.java b/src/test/java/com/jcraft/jsch/SessionReconnectIT.java new file mode 100644 index 00000000..80be660a --- /dev/null +++ b/src/test/java/com/jcraft/jsch/SessionReconnectIT.java @@ -0,0 +1,162 @@ +package com.jcraft.jsch; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class SessionReconnectIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(SessionReconnectIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>(new ImageFromDockerfile() + .withFileFromClasspath("dropbear_rsa_host_key", "docker/dropbear_rsa_host_key") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.dropbear")).withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testReconnectWithExtraAlgorithms() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + try { + doSftp(session, false); + fail("exception expected"); + } catch (JSchAlgoNegoFailException e) { + // Dropbear does not support rsa-sha2-512/rsa-sha2-256, so add ssh-rsa + String serverHostKey = session.getConfig("server_host_key") + ",ssh-rsa"; + String pubkeyAcceptedAlgorithms = session.getConfig("PubkeyAcceptedAlgorithms") + ",ssh-rsa"; + session.setConfig("server_host_key", serverHostKey); + session.setConfig("PubkeyAcceptedAlgorithms", pubkeyAcceptedAlgorithms); + doSftp(session, true); + } + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/src/test/java/com/jcraft/jsch/SessionTest.java b/src/test/java/com/jcraft/jsch/SessionTest.java index 2969ac8a..ebc12d98 100644 --- a/src/test/java/com/jcraft/jsch/SessionTest.java +++ b/src/test/java/com/jcraft/jsch/SessionTest.java @@ -1,16 +1,15 @@ package com.jcraft.jsch; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +import com.jcraft.jsch.JSchTest.TestLogger; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import com.jcraft.jsch.JSchTest.TestLogger; - -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.*; - class SessionTest { static JSch jsch = new JSch(); @@ -37,7 +36,7 @@ private static Stream sshConfigs() { Arguments.of(":42 host:99", "0.0.0.0", "host", 99, null), // bind is empty Arguments.of("*:42 host:99", "0.0.0.0", "host", 99, null), // bind is asterisk Arguments.of("bind_adress:42 socket", "bind_adress", null, -1, "socket"), // socket - Arguments.of("42 socket", "127.0.0.1", null, -1, "socket")// socket wo bind + Arguments.of("42 socket", "127.0.0.1", null, -1, "socket") // socket wo bind ); } diff --git a/src/test/java/com/jcraft/jsch/SftpATTRSTest.java b/src/test/java/com/jcraft/jsch/SftpATTRSTest.java new file mode 100644 index 00000000..d8359204 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/SftpATTRSTest.java @@ -0,0 +1,38 @@ +package com.jcraft.jsch; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Date; +import java.util.Random; +import org.junit.jupiter.api.Test; + +public class SftpATTRSTest { + + private final Random random = new Random(); + + @Test + public void testToDateString0() { + String expected = new Date(0L).toString(); + String actual = SftpATTRS.toDateString(0L); + assertEquals(expected, actual); + } + + @Test + public void testToDateStringNow() { + long now = System.currentTimeMillis() / 1000L; + String expected = new Date(now * 1000L).toString(); + String actual = SftpATTRS.toDateString(now); + assertEquals(expected, actual); + } + + @Test + public void testToDateStringRandom() { + for (int i = 0; i < 1000000; i++) { + int j = random.ints(Integer.MIN_VALUE, Integer.MAX_VALUE).findFirst().getAsInt(); + long l = Integer.toUnsignedLong(j); + String expected = new Date(l * 1000L).toString(); + String actual = SftpATTRS.toDateString(l); + assertEquals(expected, actual); + } + } +} diff --git a/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java b/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java index 17d661ec..5ea2a6d1 100644 --- a/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java +++ b/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java @@ -9,18 +9,29 @@ import com.github.valfirst.slf4jtest.TestLoggerFactory; import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import uk.org.lidalia.slf4jext.ConventionalLevelHierarchy; +import org.slf4j.event.Level; public class Slf4jLoggerTest { private static final TestLogger logger = TestLoggerFactory.getTestLogger(JSch.class); + private final Set OFF_LEVELS = EnumSet.noneOf(Level.class); + private final Set TRACE_LEVELS = + EnumSet.of(Level.TRACE, Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR); + private final Set DEBUG_LEVELS = + EnumSet.of(Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR); + private final Set INFO_LEVELS = EnumSet.of(Level.INFO, Level.WARN, Level.ERROR); + private final Set WARN_LEVELS = EnumSet.of(Level.WARN, Level.ERROR); + private final Set ERROR_LEVELS = EnumSet.of(Level.ERROR); + private final Exception testException = new Exception("dummy exception"); @BeforeEach @@ -37,7 +48,7 @@ public static void afterAll() { public void testIsEnabled() { Slf4jLogger sl = new Slf4jLogger(); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.DEBUG_LEVELS); + logger.setEnabledLevelsForAllThreads(DEBUG_LEVELS); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); @@ -45,7 +56,7 @@ public void testIsEnabled() { assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.ERROR_LEVELS); + logger.setEnabledLevelsForAllThreads(ERROR_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); @@ -53,7 +64,7 @@ public void testIsEnabled() { assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.INFO_LEVELS); + logger.setEnabledLevelsForAllThreads(INFO_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); @@ -61,7 +72,7 @@ public void testIsEnabled() { assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.OFF_LEVELS); + logger.setEnabledLevelsForAllThreads(OFF_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should not be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should not be enabled"); @@ -69,7 +80,7 @@ public void testIsEnabled() { assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.TRACE_LEVELS); + logger.setEnabledLevelsForAllThreads(TRACE_LEVELS); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); @@ -77,7 +88,7 @@ public void testIsEnabled() { assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertTrue(sl.isEnabled(-1), "trace should be enabled"); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.WARN_LEVELS); + logger.setEnabledLevelsForAllThreads(WARN_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); @@ -95,25 +106,25 @@ public void testLogging() { List> expectedExceptions = Arrays.asList(Optional.empty(), Optional.ofNullable(null), Optional.of(testException)); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.TRACE_LEVELS); + logger.setEnabledLevelsForAllThreads(TRACE_LEVELS); sl.log(-1, "debug message"); sl.log(-1, "debug message with null cause", null); sl.log(-1, "debug message with cause", testException); checkMessages(expectedMessages, expectedExceptions); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.TRACE_LEVELS); + logger.setEnabledLevelsForAllThreads(TRACE_LEVELS); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); checkMessages(expectedMessages, expectedExceptions); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.ERROR_LEVELS); + logger.setEnabledLevelsForAllThreads(ERROR_LEVELS); sl.log(-1, "debug message"); sl.log(-1, "debug message with null cause", null); sl.log(-1, "debug message with cause", testException); checkMessages(Collections.emptyList(), Collections.emptyList()); - logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.ERROR_LEVELS); + logger.setEnabledLevelsForAllThreads(ERROR_LEVELS); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); diff --git a/src/test/java/com/jcraft/jsch/StrictKexIT.java b/src/test/java/com/jcraft/jsch/StrictKexIT.java new file mode 100644 index 00000000..a16f4974 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/StrictKexIT.java @@ -0,0 +1,270 @@ +package com.jcraft.jsch; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class StrictKexIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config.openssh96") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.openssh96")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testEnableStrictKexNoRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "yes"); + session.setConfig("require_strict_kex", "no"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expected3); + } + + @Test + public void testEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "yes"); + session.setConfig("require_strict_kex", "yes"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expected3); + } + + @Test + public void testNoEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "no"); + session.setConfig("require_strict_kex", "yes"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expected3); + } + + @Test + public void testNoEnableStrictKexNoRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "no"); + session.setConfig("require_strict_kex", "no"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkNoLogs(expectedClientKex); + checkNoLogs(expected1); + checkNoLogs(expected2); + checkNoLogs(expected3); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private void checkNoLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertFalse(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/src/test/java/com/jcraft/jsch/UserAuthIT.java b/src/test/java/com/jcraft/jsch/UserAuthIT.java index a0e5fc11..774cae5f 100644 --- a/src/test/java/com/jcraft/jsch/UserAuthIT.java +++ b/src/test/java/com/jcraft/jsch/UserAuthIT.java @@ -14,6 +14,7 @@ import java.nio.file.Paths; import java.util.Base64; import java.util.List; +import java.util.Locale; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; import org.junit.jupiter.api.AfterAll; @@ -144,7 +145,8 @@ private JSch createRSAIdentity() throws Exception { private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); - String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } diff --git a/src/test/java/com/jcraft/jsch/jbcrypt/BCryptTest.java b/src/test/java/com/jcraft/jsch/jbcrypt/BCryptTest.java index c4edcbdd..2c34f3ff 100644 --- a/src/test/java/com/jcraft/jsch/jbcrypt/BCryptTest.java +++ b/src/test/java/com/jcraft/jsch/jbcrypt/BCryptTest.java @@ -14,11 +14,13 @@ package com.jcraft.jsch.jbcrypt; -import org.junit.jupiter.api.Test; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * JUnit unit tests for BCrypt routines @@ -69,9 +71,7 @@ public class BCryptTest { {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO", "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"},}; - /** - * Test method for 'BCrypt.hashpw(String, String)' - */ + /** Test method for 'BCrypt.hashpw(String, String)' */ @Test public void testHashpw() { // System.out.print("BCrypt.hashpw(): "); @@ -86,9 +86,7 @@ public void testHashpw() { // System.out.println(""); } - /** - * Test method for 'BCrypt.gensalt(int)' - */ + /** Test method for 'BCrypt.gensalt(int)' */ @Test public void testGensaltInt() { // System.out.print("BCrypt.gensalt(log_rounds):"); @@ -106,9 +104,7 @@ public void testGensaltInt() { // System.out.println(""); } - /** - * Test method for 'BCrypt.gensalt()' - */ + /** Test method for 'BCrypt.gensalt()' */ @Test public void testGensalt() { // System.out.print("BCrypt.gensalt(): "); @@ -123,9 +119,7 @@ public void testGensalt() { // System.out.println(""); } - /** - * Test method for 'BCrypt.checkpw(String, String)' expecting success - */ + /** Test method for 'BCrypt.checkpw(String, String)' expecting success */ @Test public void testCheckpw_success() { // System.out.print("BCrypt.checkpw w/ good passwords: "); @@ -138,9 +132,7 @@ public void testCheckpw_success() { // System.out.println(""); } - /** - * Test method for 'BCrypt.checkpw(String, String)' expecting failure - */ + /** Test method for 'BCrypt.checkpw(String, String)' expecting failure */ @Test public void testCheckpw_failure() { // System.out.print("BCrypt.checkpw w/ bad passwords: "); @@ -154,9 +146,7 @@ public void testCheckpw_failure() { // System.out.println(""); } - /** - * Test for correct hashing of non-US-ASCII passwords - */ + /** Test for correct hashing of non-US-ASCII passwords */ @Test public void testInternationalChars() { // System.out.print("BCrypt.hashpw w/ international chars: "); @@ -246,14 +236,14 @@ public BCryptPbkdfTV(byte[] pass, byte[] salt, int rounds, byte[] out) { } BCryptPbkdfTV[] bcrypt_pbkdf_test_vectors = new BCryptPbkdfTV[] { - new BCryptPbkdfTV("password".getBytes(), "salt".getBytes(), 4, + new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 4, new byte[] {(byte) 0x5b, (byte) 0xbf, (byte) 0x0c, (byte) 0xc2, (byte) 0x93, (byte) 0x58, (byte) 0x7f, (byte) 0x1c, (byte) 0x36, (byte) 0x35, (byte) 0x55, (byte) 0x5c, (byte) 0x27, (byte) 0x79, (byte) 0x65, (byte) 0x98, (byte) 0xd4, (byte) 0x7e, (byte) 0x57, (byte) 0x90, (byte) 0x71, (byte) 0xbf, (byte) 0x42, (byte) 0x7e, (byte) 0x9d, (byte) 0x8f, (byte) 0xbe, (byte) 0x84, (byte) 0x2a, (byte) 0xba, (byte) 0x34, (byte) 0xd9,}), - new BCryptPbkdfTV("password".getBytes(), "salt".getBytes(), 8, + new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 8, new byte[] {(byte) 0xe1, (byte) 0x36, (byte) 0x7e, (byte) 0xc5, (byte) 0x15, (byte) 0x1a, (byte) 0x33, (byte) 0xfa, (byte) 0xac, (byte) 0x4c, (byte) 0xc1, (byte) 0xc1, (byte) 0x44, (byte) 0xcd, (byte) 0x23, (byte) 0xfa, (byte) 0x15, (byte) 0xd5, @@ -265,7 +255,7 @@ public BCryptPbkdfTV(byte[] pass, byte[] salt, int rounds, byte[] out) { (byte) 0xe7, (byte) 0x4b, (byte) 0xba, (byte) 0x51, (byte) 0x72, (byte) 0x3f, (byte) 0xef, (byte) 0xa9, (byte) 0xf9, (byte) 0x47, (byte) 0x4d, (byte) 0x65, (byte) 0x08, (byte) 0x84, (byte) 0x5e, (byte) 0x8d}), - new BCryptPbkdfTV("password".getBytes(), "salt".getBytes(), 42, + new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 42, new byte[] {(byte) 0x83, (byte) 0x3c, (byte) 0xf0, (byte) 0xdc, (byte) 0xf5, (byte) 0x6d, (byte) 0xb6, (byte) 0x56, (byte) 0x08, (byte) 0xe8, (byte) 0xf0, (byte) 0xdc, (byte) 0x0c, (byte) 0xe8, (byte) 0x82, (byte) 0xbd}),}; diff --git a/src/test/java/com/jcraft/jsch/jzlib/Adler32Test.java b/src/test/java/com/jcraft/jsch/jzlib/Adler32Test.java index b3e09d95..68362eff 100644 --- a/src/test/java/com/jcraft/jsch/jzlib/Adler32Test.java +++ b/src/test/java/com/jcraft/jsch/jzlib/Adler32Test.java @@ -1,6 +1,6 @@ package com.jcraft.jsch.jzlib; -import static com.jcraft.jsch.jzlib.Package.*; +import static com.jcraft.jsch.jzlib.Package.randombuf; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; diff --git a/src/test/java/com/jcraft/jsch/jzlib/CRC32Test.java b/src/test/java/com/jcraft/jsch/jzlib/CRC32Test.java index b30acd83..76cc445b 100644 --- a/src/test/java/com/jcraft/jsch/jzlib/CRC32Test.java +++ b/src/test/java/com/jcraft/jsch/jzlib/CRC32Test.java @@ -1,6 +1,6 @@ package com.jcraft.jsch.jzlib; -import static com.jcraft.jsch.jzlib.Package.*; +import static com.jcraft.jsch.jzlib.Package.randombuf; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; diff --git a/src/test/java/com/jcraft/jsch/jzlib/DeflateInflateTest.java b/src/test/java/com/jcraft/jsch/jzlib/DeflateInflateTest.java index 7cfdf795..2a9b8988 100644 --- a/src/test/java/com/jcraft/jsch/jzlib/DeflateInflateTest.java +++ b/src/test/java/com/jcraft/jsch/jzlib/DeflateInflateTest.java @@ -1,6 +1,19 @@ package com.jcraft.jsch.jzlib; -import static com.jcraft.jsch.jzlib.JZlib.*; +import static com.jcraft.jsch.jzlib.JZlib.Z_BEST_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_BEST_SPEED; +import static com.jcraft.jsch.jzlib.JZlib.Z_DATA_ERROR; +import static com.jcraft.jsch.jzlib.JZlib.Z_DEFAULT_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_DEFAULT_STRATEGY; +import static com.jcraft.jsch.jzlib.JZlib.Z_FILTERED; +import static com.jcraft.jsch.jzlib.JZlib.Z_FINISH; +import static com.jcraft.jsch.jzlib.JZlib.Z_FULL_FLUSH; +import static com.jcraft.jsch.jzlib.JZlib.Z_NEED_DICT; +import static com.jcraft.jsch.jzlib.JZlib.Z_NO_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_NO_FLUSH; +import static com.jcraft.jsch.jzlib.JZlib.Z_OK; +import static com.jcraft.jsch.jzlib.JZlib.Z_STREAM_END; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -90,7 +103,7 @@ public void testDeflaterAndInflaterCanDeflateAndInflateDataInLargeBuffer() { @Test public void testDeflaterAndInflaterCanDeflateAndInflateDataInSmallBuffer() { - byte[] data = "hello, hello!".getBytes(); + byte[] data = "hello, hello!".getBytes(UTF_8); err = deflater.init(Z_DEFAULT_COMPRESSION); assertEquals(Z_OK, err); @@ -142,8 +155,8 @@ public void testDeflaterAndInflaterCanDeflateAndInflateDataInSmallBuffer() { @Test public void testDeflaterAndInflaterSupportDictionary() { - byte[] hello = "hello".getBytes(); - byte[] dictionary = "hello, hello!".getBytes(); + byte[] hello = "hello".getBytes(UTF_8); + byte[] dictionary = "hello, hello!".getBytes(UTF_8); err = deflater.init(Z_DEFAULT_COMPRESSION); assertEquals(Z_OK, err); @@ -198,7 +211,7 @@ public void testDeflaterAndInflaterSupportDictionary() { @Test public void testDeflaterAndInflaterSupportSync() { - byte[] hello = "hello".getBytes(); + byte[] hello = "hello".getBytes(UTF_8); err = deflater.init(Z_DEFAULT_COMPRESSION); assertEquals(Z_OK, err); @@ -244,12 +257,12 @@ public void testDeflaterAndInflaterSupportSync() { byte[] actual = new byte[total_out]; System.arraycopy(uncompr, 0, actual, 0, total_out); - assertEquals(new String(hello), "hel" + new String(actual)); + assertEquals(new String(hello, UTF_8), "hel" + new String(actual, UTF_8)); } @Test public void testInflaterCanInflateGzipData() { - byte[] hello = "foo".getBytes(); + byte[] hello = "foo".getBytes(UTF_8); byte[] data = {(byte) 0x1f, (byte) 0x8b, (byte) 0x08, (byte) 0x18, (byte) 0x08, (byte) 0xeb, (byte) 0x7a, (byte) 0x0b, (byte) 0x00, (byte) 0x0b, (byte) 0x58, (byte) 0x00, (byte) 0x59, (byte) 0x00, (byte) 0x4b, (byte) 0xcb, (byte) 0xcf, (byte) 0x07, (byte) 0x00, (byte) 0x21, @@ -284,7 +297,7 @@ public void testInflaterCanInflateGzipData() { @Test public void testInflaterAndDeflaterCanSupportGzipData() { - byte[] data = "hello, hello!".getBytes(); + byte[] data = "hello, hello!".getBytes(UTF_8); err = deflater.init(Z_DEFAULT_COMPRESSION, 15 + 16); assertEquals(Z_OK, err); diff --git a/src/test/java/com/jcraft/jsch/jzlib/DeflaterInflaterStreamTest.java b/src/test/java/com/jcraft/jsch/jzlib/DeflaterInflaterStreamTest.java index 82c2f671..84464f86 100644 --- a/src/test/java/com/jcraft/jsch/jzlib/DeflaterInflaterStreamTest.java +++ b/src/test/java/com/jcraft/jsch/jzlib/DeflaterInflaterStreamTest.java @@ -1,6 +1,10 @@ package com.jcraft.jsch.jzlib; -import static com.jcraft.jsch.jzlib.Package.*; +import static com.jcraft.jsch.jzlib.Package.randombuf; +import static com.jcraft.jsch.jzlib.Package.readArray; +import static com.jcraft.jsch.jzlib.Package.readIS; +import static com.jcraft.jsch.jzlib.Package.uncheckedConsumer; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -94,7 +98,7 @@ public void testDeflaterAndInflaterCanDeflateAndInflateNowrapDataWithMaxWbits() Arrays.asList(randombuf(10240), "{\"color\":2,\"id\":\"EvLd4UG.CXjnk35o1e8LrYYQfHu0h.d*SqVJPoqmzXM::Ly::Snaps::Store::Commit\"}" - .getBytes()) + .getBytes(UTF_8)) .forEach(uncheckedConsumer(data1 -> { Deflater deflater = new Deflater(JZlib.Z_DEFAULT_COMPRESSION, JZlib.MAX_WBITS, true); diff --git a/src/test/java/com/jcraft/jsch/jzlib/WrapperTypeTest.java b/src/test/java/com/jcraft/jsch/jzlib/WrapperTypeTest.java index 91b24949..f5140b5b 100644 --- a/src/test/java/com/jcraft/jsch/jzlib/WrapperTypeTest.java +++ b/src/test/java/com/jcraft/jsch/jzlib/WrapperTypeTest.java @@ -1,11 +1,26 @@ package com.jcraft.jsch.jzlib; -import static com.jcraft.jsch.jzlib.JZlib.*; -import static com.jcraft.jsch.jzlib.Package.*; +import static com.jcraft.jsch.jzlib.JZlib.DEF_WBITS; +import static com.jcraft.jsch.jzlib.JZlib.W_ANY; +import static com.jcraft.jsch.jzlib.JZlib.W_GZIP; +import static com.jcraft.jsch.jzlib.JZlib.W_NONE; +import static com.jcraft.jsch.jzlib.JZlib.W_ZLIB; +import static com.jcraft.jsch.jzlib.JZlib.Z_BEST_SPEED; +import static com.jcraft.jsch.jzlib.JZlib.Z_DATA_ERROR; +import static com.jcraft.jsch.jzlib.JZlib.Z_DEFAULT_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_NO_FLUSH; +import static com.jcraft.jsch.jzlib.JZlib.Z_OK; +import static com.jcraft.jsch.jzlib.JZlib.Z_STREAM_END; +import static com.jcraft.jsch.jzlib.Package.readArray; +import static com.jcraft.jsch.jzlib.Package.readIS; +import static com.jcraft.jsch.jzlib.Package.uncheckedConsumer; +import static com.jcraft.jsch.jzlib.Package.uncheckedFunction; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import com.jcraft.jsch.jzlib.JZlib.WrapperType; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -17,7 +32,7 @@ import org.junit.jupiter.api.Test; public class WrapperTypeTest { - private final byte[] data = "hello, hello!".getBytes(); + private final byte[] data = "hello, hello!".getBytes(UTF_8); private final int comprLen = 40000; private final int uncomprLen = comprLen; @@ -92,7 +107,7 @@ public void testZStreamCanDetectDataTypeOfInput() { c.good.forEach(w -> { ZStream inflater = inflate(compr, uncompr, w); int total_out = (int) inflater.total_out; - assertEquals(new String(data), new String(uncompr, 0, total_out)); + assertEquals(new String(data, UTF_8), new String(uncompr, 0, total_out, UTF_8)); }); c.bad.forEach(w -> { @@ -129,7 +144,7 @@ public void testDeflaterCanSupportWbitsPlus32() { assertEquals(Z_OK, err); int total_out = (int) inflater.total_out; - assertEquals(new String(data), new String(uncompr, 0, total_out)); + assertEquals(new String(data, UTF_8), new String(uncompr, 0, total_out, UTF_8)); deflater = new Deflater(); err = deflater.init(Z_BEST_SPEED, DEF_WBITS + 16, 9); @@ -156,7 +171,7 @@ public void testDeflaterCanSupportWbitsPlus32() { assertEquals(Z_OK, err); total_out = (int) inflater.total_out; - assertEquals(new String(data), new String(uncompr, 0, total_out)); + assertEquals(new String(data, UTF_8), new String(uncompr, 0, total_out, UTF_8)); } private void deflate(ZStream deflater, byte[] data, byte[] compr) { diff --git a/src/test/resources/config_with_negations b/src/test/resources/config_with_negations new file mode 100644 index 00000000..ff8ed382 --- /dev/null +++ b/src/test/resources/config_with_negations @@ -0,0 +1,11 @@ +Host *.example.com !*-jump.example.com !*-proxy.example.com + User u1 + +Host *-jump.example.com + User jump-u1 + +Host *-proxy.example.com + User proxy-u1 + +Host *.example.org + User u2 diff --git a/src/test/resources/docker/Dockerfile.ExtInfoInAuthIT b/src/test/resources/docker/Dockerfile.ExtInfoInAuthIT new file mode 100644 index 00000000..06a3f9bb --- /dev/null +++ b/src/test/resources/docker/Dockerfile.ExtInfoInAuthIT @@ -0,0 +1,36 @@ +FROM alpine:3.19 +RUN apk update && \ + apk upgrade && \ + apk add openssh && \ + rm /var/cache/apk/* && \ + addgroup -g 1000 rsa && \ + adduser -Du 1000 -G rsa -Hh /rsa -s /bin/sh -g rsa rsa && \ + mkdir -p /rsa/.ssh && \ + chown -R rsa:rsa /rsa && \ + chmod 700 /rsa /rsa/.ssh && \ + passwd -u rsa && \ + addgroup -g 1001 ecdsa && \ + adduser -Du 1001 -G ecdsa -Hh /ecdsa -s /bin/sh -g ecdsa ecdsa && \ + mkdir -p /ecdsa/.ssh && \ + chown -R ecdsa:ecdsa /ecdsa && \ + chmod 700 /ecdsa /ecdsa/.ssh && \ + passwd -u ecdsa +COPY ssh_host_rsa_key /etc/ssh/ +COPY ssh_host_rsa_key.pub /etc/ssh/ +COPY ssh_host_ecdsa256_key /etc/ssh/ +COPY ssh_host_ecdsa256_key.pub /etc/ssh/ +COPY ssh_host_ecdsa384_key /etc/ssh/ +COPY ssh_host_ecdsa384_key.pub /etc/ssh/ +COPY ssh_host_ecdsa521_key /etc/ssh/ +COPY ssh_host_ecdsa521_key.pub /etc/ssh/ +COPY ssh_host_ed25519_key /etc/ssh/ +COPY ssh_host_ed25519_key.pub /etc/ssh/ +COPY ssh_host_dsa_key /etc/ssh/ +COPY ssh_host_dsa_key.pub /etc/ssh/ +COPY sshd_config /etc/ssh/ +COPY authorized_keys /rsa/.ssh/ +COPY authorized_keys /ecdsa/.ssh/ +RUN chown rsa:rsa /rsa/.ssh/authorized_keys && \ + chown ecdsa:ecdsa /ecdsa/.ssh/authorized_keys && \ + chmod 600 /etc/ssh/ssh_*_key /rsa/.ssh/authorized_keys /ecdsa/.ssh/authorized_keys +ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"] diff --git a/src/test/resources/docker/Dockerfile.openssh96 b/src/test/resources/docker/Dockerfile.openssh96 new file mode 100644 index 00000000..474c9282 --- /dev/null +++ b/src/test/resources/docker/Dockerfile.openssh96 @@ -0,0 +1,23 @@ +FROM alpine:3.19 +RUN apk update && \ + apk upgrade && \ + apk add openssh && \ + rm /var/cache/apk/* && \ + mkdir /root/.ssh && \ + chmod 700 /root/.ssh +COPY ssh_host_rsa_key /etc/ssh/ +COPY ssh_host_rsa_key.pub /etc/ssh/ +COPY ssh_host_ecdsa256_key /etc/ssh/ +COPY ssh_host_ecdsa256_key.pub /etc/ssh/ +COPY ssh_host_ecdsa384_key /etc/ssh/ +COPY ssh_host_ecdsa384_key.pub /etc/ssh/ +COPY ssh_host_ecdsa521_key /etc/ssh/ +COPY ssh_host_ecdsa521_key.pub /etc/ssh/ +COPY ssh_host_ed25519_key /etc/ssh/ +COPY ssh_host_ed25519_key.pub /etc/ssh/ +COPY ssh_host_dsa_key /etc/ssh/ +COPY ssh_host_dsa_key.pub /etc/ssh/ +COPY sshd_config /etc/ssh/ +COPY authorized_keys /root/.ssh/ +RUN chmod 600 /etc/ssh/ssh_*_key /root/.ssh/authorized_keys +ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"] diff --git a/src/test/resources/docker/authorized_keys.KeyPairIT b/src/test/resources/docker/authorized_keys.KeyPairIT index fc007f26..555a29d4 100644 --- a/src/test/resources/docker/authorized_keys.KeyPairIT +++ b/src/test/resources/docker/authorized_keys.KeyPairIT @@ -5,6 +5,11 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDNVEfQu64p2GczvWj0AlncuNG+PzPd0jZ1/hfx/JDz ssh-dss AAAAB3NzaC1kc3MAAACBAKCQzxCEIM7yb5fbSmuJCz/+v40WEbULx4JKCXBb7zmRtDtweBqUJXDM1bMjQ4VwGB1EkfMZJQR7rPrOAxfdMdCpkqgPR0+xlljZLVarSJHwt1KR9EudUjUdD2ZrPlvPlG1sAyvMVTK5KCx2QaS2Bn/gukxrvT7DkaqCFHg2P7rLAAAAFQCF+m0B12XxhF1P+3hF3ktGmfYpywAAAIBX+azef1TX90FwS7vyngCrdJsOYqWtj1zQ8MlrwrWL0jdbjcIG/0vcq1x84LTgs+hAPylDBCGGJa/td88kJ6jIUeWiebRUG0zEH8LM+nehQA5O6TuEV3U0Dg73I0Mb04ZzN2GqYqW+KwHdiaU5M8XQv3V/PCmY8d7R5WcMKKDCcQAAAIEAmy0FgQoaWyHdD84b7zD0YjziMsVELAbGWwdxNuNe6wtACcctCdIQigQYaC2HwGjlDKVMjQfd4RWArshg7p0PlEw7//HSgoVhwcBbDpmWGCS6TxSSQpDmdDgiVegERMYjo2BZoZQTuJIx/BhQggVISkELwZfsi1BnPC5Aecq7ILI= test ssh-dss AAAAB3NzaC1kc3MAAACBAMKmaZwslPWE83wv+Ofl9oaMeOR03Yk0cyDc5fSAgjepXZQR5sps0KFORRufI6dLQHY4XLoyPc/Hr0ra+vY/l+p20cJVjQ07oosPU/d2eLdZ66rdjXCyrJPjFjnNJtumnBnJZqWGObKspnWk73vPOflReyGpUFe51PF1usur3uDLAAAAFQC3dSlxZZVNbvqjJpg8/oSMuG27/QAAAIEApkd3miYVc/Cl1QwdqwInIjby1yGNCfFsZALGBYc67lkd5lGBdNlL+fgi+BwC6UXu5OAqWB/b9OtvJR8NRcM77V252IER4t95t2ZdG66M1T5q1aOVL/ehPZFHf0oPXHJLcsybzqKFAtQj5hEbnrwJYW0fWi87C9LApplzPTVYQikAAACAUEu5tFmImzyQsUBMN/j1GWCCSVznjfRHkeGwx5koC0D2iK3mMphnF9avsoX2PrboEbGqy69JPCekKQiPPWxcQRTRqFS/ySkECJ5lJ2zJrW1whjLQEJbOq7WmusSML9UXCpNSwCqhJxPYx5a8Pq7lrrBP21jT/Z/A4m4c4wMkLXo= test ssh-dss AAAAB3NzaC1kc3MAAACBAPmIBCP1AA8EiTL0VAdzau+1KuRmPkg+H2du4zt4ifhzEx4MscZ7cpBsUAM/EBC/ECWTHzDiWyFFuenvxDlfTi9XxySdOKM6XGLkY7oLsY1y5bShyLrycxDD7MRQ8HpUc9TPzE2PDH4Od6o98e+vdXP0+IDUZGbSuwEnaABX/1+ZAAAAFQDIB9KUd1/gnzM4s4FXCQb7zCnjVwAAAIBptrE9g/ooRnh4bOWiA3/StxbjMRmA9f9bJbM55CinL4yf/GdGmeBp6YjDBxDyk1oXmVJ2hLVXgW6kETbt+D01SouCTgLPlFwLFhqZQ+A4JQOKuLIBaM92hI7sAfTewkxbmAk/e1vyOeyfcQDhbncESzVBZrheXC1fTKv7O2PgbgAAAIBKhGEiNwKNA/pfYvkEbX90/jVoIEKZfqTk+PB2gbVMT98LWPaexrDwEGuCCK3KOtMncDai+0r1ujf1hzNJ4z43Z1n15tPvAIwQsObTL+eUQkVjHA8LXOq8n4yYyPidkjGzWcfBAm4Lm6vKkzFM3VRouQIHpWpjRl3wGTJZkOoH0Q== test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDY+326Z0bUOn8kYUc399P2mr7Eh0E7Nc9zPrv80B05ykfmsCM053SiGYQgwP5aBzZk83PdWdYIK12U9eO5WzTwT2po9PrZT0AvsBGnU7MkzSSlXX79e9ELbGYNWV4DafT/VHxVwCuoctG7XKUorGA3ZHxGbhNWyo/+6LNwyCMcxfpjpCelJWn4MTwFqs0DVme4R2Sy+LaVg3Nv5Vbbi27cIbA+6Orit2ajDUUCHJvcQlpGrM6x9w0zbt4pUI6yHPCHIIqZEPzj1285GCF6ZQzi0TMEf4wHtA8fN1Xgn1vBw+IEkrAAnQUeTWOR1nc6CDFEGTlyYlIuz634I4BwfhBl test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD1ub4yJ8TCXgPaTtASEA034K+85zo2MDlRCB7tHgpmIpK7btPxizYEPEPywVnBRGMJnhIyKKHc7AHzCos3tkGVYNBlDGmnoaZTeZqhmsRSqqxHyfIRQ1T+aGIN4scliZ+iW8RI/djYyfPlcAR3Kku+Q3xsR5K2yzzxZiYB/rD9XjvRRtc7cYZlHaMe2NBtQhh3hamQVSEJMjfSCpq4okULk0UytXhdbPtP/tLDvqeK7op+q97hFS0wRY9xTaNM+qUhuhaKNbhuexgGQBk7We45LcR6O3XwjAB+X7owa4JtKx9oKY93+3qtM+5+6nRnFnkB/GL5fdKPmIswQ1vOSgGeEhg/M0yzX0PEMx4qfczpUp2rFtMTNg9O6Xz9qJCnZy/I1EmK6FEXerpEY0x41mY2NBBor9vataM9IxzJ1VIra8JYjnPqG48V9LFhSEpFfi1CquqJu1fmtxtH4YNWWNQECKp0s+WmxKAu2ZAAr1wl+tNkkiZOaDSluT14qMkSEjU= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCze/5G1FPKvgcnLXqHV4jbUfO+gi75BUKUWKBowdg63kr9e3GdGVmwtBBWTJMyxEWVtJMrH8CARWoyViILQy9oFfbbcwuAnfhVjdPVNpK7RL1ZwCCrsg6UDpE+Vp4rTWzUcDXUyI2MZrePJCwU0+UWdvoEJXwE2Nitb+sS802IsdlwiRj9knzKJ5rVcpGpQ0Qkc0jbeNDfznRAE8ARawkDLcXEio9V6t4psAWf3BbHGYec9PZAYcdzCHcwmLmTT/DSOF+ut2fxvjBydWJzGHmKyozlFNjThgXt0i0fcedLG0oAHNNBL8UeEYQ7H0glbT9rsnMqOAtgBYD/nJGfMkMMjP7NRS0Y0jtkmY8PQ3l7RkHTFeKVkKpKtyD3+LsfuOZkh5A6rhOZgMP9+22AZhIXGho9kJZSSF7B9CWa+TCr5iZo9lLXbeMFRrk5Ltg7BNHkO5Rx8SGroGYGkLXt89+eL+Ht1I4jbq1fWF55YQYnTOovvnNVVCp4i0c8KlNAqSM= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC96PvllVEOgtQdHnaoga3gi8md+/lUwBG0SOv4co9QaJ2kvJZDTiSLmJc4euEuciiplvzKpKSWSVqaZLboXiVVt8MnPmIwIlp8bhIqJ+0431FNWWq6RA/4EszHiARxMIwUqw+svN2GMm5fGr+PK3FR6mXGjiVlv4/5Vb0BCUjgB0vIqq123DmKpxrT6S3Ik5vYCCn0zKtuWxfxp3hpCqC2TdqD7/rFTX/AstmlhTO4FRDEK/7JNBSWY8+9/rQS/c/HSSW7qMGEoGFkELKSYFSdwLcyAMS1HySzNR1SuyBLDsOSaN5iWm5cBqKIK175Uc3Xe3fIn6+C5ld0m/nN75bw6QJiDH7I2jMYDTBpDqztBiqdjcSwvNTogGQg/oJLC7aAz+E5gfkTsg/Q6bPf6quyPIQ+KKH5v2YVFQIw2Te0X7/OZACsqUnCbGiDXCixPTTGoRypcR0MQDJxR497y7zvO4kiqQpsdBoELuhWSW8+9BcovL4/jSyqUEGvGYoh/QM= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqGObM6DyGcra+/eBdm7mIvnLM9oUDUwpT3hRbF7S7V6bb64EuFlduGXJKK+i4/a/ls1Sm3GVwnWvwuRxrVXgv1497MYDzDcflfm+bQLUN38qNscVhn3jdtFuCTIc7VKmtEd9zn4HVzQhUYWq/oiqwOsDeP1QidWbNwgBthCxzhfdi9XUpdpT3Tm/mtLwEz8KZ2kP9VdfXBql6NI64COcI+B2nzE2YwxZsLGFj1orstRZ3WOLTJdLtQ1NhBQqTEwFD06Jhf50wo13xV84jWeZom+ylJQcj9njnfH8Kf7T+1eyBAqCPLvS6kM5Q9J6bGPj67Hkp96zPT/y3Gh1le80o3JrYjo20NqWzO+/OIXilrDoYdUKJbgoM7GXtMlxFNEnj7BIf6TLNZJY9bA6dw1yFs9wOl9LRhC1zUC6kESx8DIiqSpxrZTF0ScgvC/gKqX6K81ovJBklrAuTQpTqGKOW2eY5jqiRAVa7Gx8sPPtHkGqodD/sFOKurggVDf2Y1vs= test ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBcLHSACx0yr1S902vlRRnXA12YpaXIOkZUXoNFb01+CxQBgGpVTEVMuakLpIW/XcD9ltu+c6Czk5WnBKEoHNuI= test ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPKyvhX/3Q32EEhZKF6s+fEzfQwabsSpXgFJ2LwpGxKFxzE2jw4Nvwe9W3YbPyrZU6K2MMA7p7ZdJwdDP/tQ9dhttOGGSkea9Cq4dcdgC9cdBuQ4aGb8crmY3Z5wc1dPxQ== test ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAG3XTlAFoQd4iIBt5KoTHJk46v1AdZSd+wfCSpc2/GCzHNGVqidrKCrypszpkQOW6vYG8riilyfF36rJa9ipjR+5gDApLIe/ix1Fknr8MU6nWoYv2+NBTsEcaS/VTeiUuMKftMtlO2wOaWFcbZBQUAlb3LDnpMyYKCqIXhhl4vqCxBukg== test diff --git a/src/test/resources/docker/sshd_config.ExtInfoInAuthIT b/src/test/resources/docker/sshd_config.ExtInfoInAuthIT new file mode 100644 index 00000000..73dd194a --- /dev/null +++ b/src/test/resources/docker/sshd_config.ExtInfoInAuthIT @@ -0,0 +1,25 @@ +ChallengeResponseAuthentication no +HostbasedAuthentication no +PasswordAuthentication no +PubkeyAuthentication yes +AuthenticationMethods publickey +PubkeyAcceptedAlgorithms ssh-ed25519 +UseDNS no +PrintMotd no +PermitRootLogin yes +Subsystem sftp internal-sftp +HostKey /etc/ssh/ssh_host_ecdsa256_key +HostKey /etc/ssh/ssh_host_ecdsa384_key +HostKey /etc/ssh/ssh_host_ecdsa521_key +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 +HostKeyAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-96-etm@openssh.com,hmac-sha1-96,hmac-md5-etm@openssh.com,hmac-md5,hmac-md5-96-etm@openssh.com,hmac-md5-96 +LogLevel DEBUG3 +Match User rsa + PubkeyAcceptedAlgorithms rsa-sha2-512,rsa-sha2-256,ssh-rsa +Match User ecdsa + PubkeyAcceptedAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 diff --git a/src/test/resources/docker/sshd_config.openssh96 b/src/test/resources/docker/sshd_config.openssh96 new file mode 100644 index 00000000..12c4064f --- /dev/null +++ b/src/test/resources/docker/sshd_config.openssh96 @@ -0,0 +1,21 @@ +ChallengeResponseAuthentication no +HostbasedAuthentication no +PasswordAuthentication no +PubkeyAuthentication yes +AuthenticationMethods publickey +PubkeyAcceptedAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +UseDNS no +PrintMotd no +PermitRootLogin yes +Subsystem sftp internal-sftp +HostKey /etc/ssh/ssh_host_ecdsa256_key +HostKey /etc/ssh/ssh_host_ecdsa384_key +HostKey /etc/ssh/ssh_host_ecdsa521_key +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 +HostKeyAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-96-etm@openssh.com,hmac-sha1-96,hmac-md5-etm@openssh.com,hmac-md5,hmac-md5-96-etm@openssh.com,hmac-md5-96 +LogLevel DEBUG3 diff --git a/src/test/resources/encrypted_issue_369_rsa_opensshv1 b/src/test/resources/encrypted_issue_369_rsa_opensshv1 new file mode 100644 index 00000000..bb9a2709 --- /dev/null +++ b/src/test/resources/encrypted_issue_369_rsa_opensshv1 @@ -0,0 +1,39 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCd+5LZfU +vM8no5y5XUnD99AAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQC96PvllVEO +gtQdHnaoga3gi8md+/lUwBG0SOv4co9QaJ2kvJZDTiSLmJc4euEuciiplvzKpKSWSVqaZL +boXiVVt8MnPmIwIlp8bhIqJ+0431FNWWq6RA/4EszHiARxMIwUqw+svN2GMm5fGr+PK3FR +6mXGjiVlv4/5Vb0BCUjgB0vIqq123DmKpxrT6S3Ik5vYCCn0zKtuWxfxp3hpCqC2TdqD7/ +rFTX/AstmlhTO4FRDEK/7JNBSWY8+9/rQS/c/HSSW7qMGEoGFkELKSYFSdwLcyAMS1HySz +NR1SuyBLDsOSaN5iWm5cBqKIK175Uc3Xe3fIn6+C5ld0m/nN75bw6QJiDH7I2jMYDTBpDq +ztBiqdjcSwvNTogGQg/oJLC7aAz+E5gfkTsg/Q6bPf6quyPIQ+KKH5v2YVFQIw2Te0X7/O +ZACsqUnCbGiDXCixPTTGoRypcR0MQDJxR497y7zvO4kiqQpsdBoELuhWSW8+9BcovL4/jS +yqUEGvGYoh/QMAAAWQCCm+hXX3/lj0BTGT+TRll0G/c21MVlKHmh4pSbWuWcVsNYf0Xk9T +AzQL2Aw2iIlCR0nZAb6fbeWkO7FcZwdUWgEH+qhUE5jnzsrGs8dVbuMQcCo//PRK5wPqGu +z1Na6IUSvuvVUk+t+spmVLOjbgKVmXD8NMz3yRrlb92bP/u10OWvHS2lQf8L4qyG0SCbsR ++mfKuuhquOvL+0hqeMwaMjcIeIDqF7XM3lYStliF8atc0KiyX3jNh3xzHZof791WmXgz6f +kHa0vpY/Ds84hkT0CKa7q7v08V2RXisdwfqs36sMMJDTSJbF8f72mSrX3ZiTDCRVzxxa7K +JOt6eKJMBlobsJvDq9cE1paypIiThIwdrcVKTtdk9QjWiVzalF5xrvYAO5rM9jt1RMvEHi +8uAuAz4rOqGlnNU3ruxTtyynAIk+Ao+TFxRwUfX053oJGCvvUgpQvIhK1PPTAJQxKL299C +Hsid01jDXujRCKURcFcBkjczHMV/Y35tBhRm31E5oQofONaejPLyna5RoKlfmw7feOqh1U +GhJPl0kX0RcOG4WafyXjDP8a4mlkukka2HxoVnI4DTo5rvNNifa1VN27jDcVJG9EoC4KdQ +Jn9jwrQtGgpYUC2Mw1yz8IImIjhzaOwuofuEa8XpSCDWN9EIF1+QNRFb8e8s8PkYG2qB3T +abxXFrZlXjG5xOpv2Nx5FfjLpzRXWNzyjy3boxJ2DMamcKoHsWV0FZe0dyoSWz0pVQc55Z +LcVH3pFM/mfyv5S3bEsoQsDHIoJJ1yAOHoAAKzKj8J6xqvXa6j71JIce68cIYx3Qrs3W92 +xrOAjVlE6M476C4lPJdNBxTdkClvgdnulzv56BYNAoBWtbuymZiwB1TZetSGDsDSdtD2rX +wNGHHdUceau+OEh+rsZpmFzBfN0ezyogzy+5FFVrlKFrcUnIlPm+1cgEncLES9AZttwkjx +HzUxdBgm7rq1sCB22Kld6dN5akRXGFC2DpU3t91XPw1lavTqudGC3ovMFfoFrfJPy95eVt +yAA73aHS93t0xZTxCkXff6sP0E2C/ztq38LP2SpvT7SZa250DLD+v3pdRgz5wYUNlnTlH/ +wvRdqHkEnQWwcOyoO33E/iZS/u88EjfMDMu3JbS/LR+0S7iZS5MSh8O3KUtNR51/qhP/Em +Q6v4R/2+odWi7yr0PrCsuyURaq44o12unHE2pzHmas5mN48+agF6thTUmiMR0csoMs/yEW +TeAbdPTEjxNc3cxvrMmnWhVCLFDNYRScq+O/PEWsPN7ofoYagUUM+TbgGGs7kWOOcyo/oN +gxMowxWX3sraUevHEpl8Fgbapl18W/ppNd1eIT965SiI2cHPzUW6aTPYW6y6BKOBHhX3np +EPf7ApjnUhuHF8Brvikgtu2eHoAA9gKFs/X7sOD8KWIhsV5tIr6psCqalOA4QGdMsmZuaW +GCIfeaCsMM/HcTJZzj8OBBpLAOC0/lSs8Y9qRqDK8Aid12Tp9FrtJKlvt5DsnsR8an4zsJ +e4k9sN7Z+bxVxKyhQulc16hSSUBJTQdCJ3fbdlyklTgx45maL8Q/bQhYlQ2y/EBIASwOwq +Gq5gJw1RNm2CaLBP/tjD+qCjIZ4DsPuHsdv1a51BGiTjt9YqV/eqXdZ+Npz89jFQw7WGc8 +AStzRvxNE7fIAZgK3LtlNNDpgEX0+g6VCPfiPrl8cvz003x1SMtSMYftPjbvuKosJaMu4K +g3t7vdQjTObNpuJMOcuTKiY3pJDBbNAcF1OKbzGpTF25zspvzHou60BKt1mQD4ktuSkh9k +IfwiPacrs5zA3S680z93Pt17M11tfMXgYpN7z34DCzdj5COdwo7Ei9Et3dxdv/Nzj83kxh +KmpEq3lxqRzHG2mb2h1zHQW3RR0= +-----END OPENSSH PRIVATE KEY----- diff --git a/src/test/resources/encrypted_issue_369_rsa_opensshv1.pub b/src/test/resources/encrypted_issue_369_rsa_opensshv1.pub new file mode 100644 index 00000000..2ba237d7 --- /dev/null +++ b/src/test/resources/encrypted_issue_369_rsa_opensshv1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC96PvllVEOgtQdHnaoga3gi8md+/lUwBG0SOv4co9QaJ2kvJZDTiSLmJc4euEuciiplvzKpKSWSVqaZLboXiVVt8MnPmIwIlp8bhIqJ+0431FNWWq6RA/4EszHiARxMIwUqw+svN2GMm5fGr+PK3FR6mXGjiVlv4/5Vb0BCUjgB0vIqq123DmKpxrT6S3Ik5vYCCn0zKtuWxfxp3hpCqC2TdqD7/rFTX/AstmlhTO4FRDEK/7JNBSWY8+9/rQS/c/HSSW7qMGEoGFkELKSYFSdwLcyAMS1HySzNR1SuyBLDsOSaN5iWm5cBqKIK175Uc3Xe3fIn6+C5ld0m/nN75bw6QJiDH7I2jMYDTBpDqztBiqdjcSwvNTogGQg/oJLC7aAz+E5gfkTsg/Q6bPf6quyPIQ+KKH5v2YVFQIw2Te0X7/OZACsqUnCbGiDXCixPTTGoRypcR0MQDJxR497y7zvO4kiqQpsdBoELuhWSW8+9BcovL4/jSyqUEGvGYoh/QM= test diff --git a/src/test/resources/encrypted_issue_369_rsa_pem b/src/test/resources/encrypted_issue_369_rsa_pem new file mode 100644 index 00000000..b6469663 --- /dev/null +++ b/src/test/resources/encrypted_issue_369_rsa_pem @@ -0,0 +1,42 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,D5AFEF41BADEB0A6ADDCC294C4C34402 + +BgIiIqe5ABrapndv+KDz2EoUj/QWtEJ81cCVLBJT05GTA93ZPYmwFOOHzwDtZN2n +36qqSkiytcPcF4onogR50qziz/k3cN38Eiiqyrm1kOrIkxaCUogy0I2vQvDSYr8v +CuW7btHpHpv8TlbWWNO2OGawH3ZYplNZoU8lPkHrGd8s0SdwHWjGm5pvKtbNRMjP +G8UYgBO4259oCRKpXbJJK2J7cH44itedZeiHPhh2gukqyFIaiu5m8ZG8X+sCnxw3 +DYYdm9C24uaC55Pfh7DTaGdvKXlprR816FUnqqF30Len+JegHbT3EG68PYsakIwr +O2h/jJo7Io61/w5lzoFoGrh2MJ73sHpsLk+ytQIghfUEHc1pBZnI9YcHhiHqEhUb +GZH/knfhR/V+OjFL8eXD+Mv6RHilxe22Dma0kk45B9rtAHa7wCtkRpQPBerUQ/Rz +uD39LPc5hS9tdvMVVZHJRCx10B+yvRo+CutuTpyZo27Hr5EtamGCgKU8BIr5EYaH +FkRAMy7Q+27kiXgD0H+DViyZZQ1gM09gxItQuw/E4tjjaAqsy/xnZtGdmKARqqZe +3oPXIv/bmLWrO9c2Hq8xFB5iJYRrzgmmt03RPMyrQLPw8gfbGVrehn1F9F5TLfdc +J2Ao2K+Bv7bTTh7OunwGXF/UmAphvREcH2yhB4VOr04R54z8Vo81x8kRJtTlbP1g +fsYHRK/BSG3R8pTge7MJwWLYW4lyGyNQ1G3NWbhsafbC2sBxm43uP1sO32SR7moe +t4Um+DzxNeVx1zaSuw0XSc/W87F5GuXKpYRmeaKo/EkC3LLKI2UFK/5iv9m3//Jn +6XPMA1jfiavN/hZwKTHImCdHP7uqEn0VwfmoNnSovZUcVikuPjToRPoxOYblPed4 +RDmsT1DrEO3zRiZ4wY/al1/Qs5ivojoAzwpuJDbG9C9H4B82gDvXcqAr8LeJS2gZ +9w3hD+8XJJMf3wA112RaoDJn7xzJC/fTmyq5tiS0pMkGP0bH9RVAVTVPoKLjs70m +oW81L/3Z6FK7t+6vDonQITZqal79jU7HNwb0Jelv95kT4jUtkzeLjLGgsBGRGBqg +ACAwFqMKqAZAmqCI2aai8ky054aNSi0CCEN+kQEs29BRzs7/+RdlPUZCQKeY9siT +5htrC1g8xn+F/HudIzcue6Vg0KmONCn/PjJbSjK5lyQc3PN8W6Fa6POJRZgHpkR/ +vfN+IJlh9vmU7CqZcT08SZDp/2x/eIq58yoBCBDwu46vr477+Vc25rSpl1Wt+9sj +PZ5WKLqHmPy8vBmG5nOv1p3OKHv/AL7LvhvBk/B33oK7mDI0HhGaHDmMyPrikI1n +DcStuKhNgtZxV6V5YLVfbGI92Q6bggyN9eslb2AMcPg3Jusw1wQHKtGBYTveQC3I +zakD00bl6eyR8L8GM7NQFlBWO6QAQRAjwbrop9uixrXFqJ+kcsJ2EXikhHOKpW8B +3uhmGkIokzbCy2SLy8ErzBPNea58vc4kI+CJEFANsrhOhrkIw+9+MVz6XnOUafb7 +mk4k9Vu+EdAT8zwFpdo4oDH3gO6Y7E6fzFvCWblXmy34xf2C2G7uxl2jHJFbqPxY +3sCcKfFMOZACsdiq4SqtgilzLvnyxf6QY704hIS4Tl6QpLVxshIDHVNW6TUJ7qwg +I1fDyFWFponmtV0pS28yYAEM7Eb4aoUvXWEeQh6PE+CPB/s/6UQ++IgM0INAt4kn +LIljwtHAcWtKvoWup4CHelnBpdEqzWjWbiopW1/166m6/BFNJKarUNZtuuj3vorU +rAi/pwNxQ9a32pNW9ox0ir/C04Aqb2Jj58BD+X/dw2PbfilHPNraOJqdiMn8802S +2Vlj8R3VpbeETBc8J4zRibV0ZL2TsNRlBPXAaCVJhBgqUdYGAeaLqioK3hdQqHA0 +y/c+DsjFwODP+jtfVYHTgKIeAS4BnUt/3TwfzdSC7CgdZztk9aPc9bhQrFM1rdZN +olV2k2nTHLtflLPvQWckL5W0vBYqELjwBCJ4fUG3aNRwoml00uYWWtliOA7qABW7 +dkX4Xx0iUwql00tEiugxHHOgC2S5cDmSeC2+emGFb8ca55nrxIkCJWG0wOm+/gtH +UDBHmgxD01dk4QFG5wF77AAIBXWuFcYVLp9wzgAIhnEqGbYy5+hzoCxYTjjIpUv5 +MA5A1z2Yc9n0Qvu08swzO9YvrRpQPy/3Ys2nVhLF8MSI6qn20o/sDYy8DAum4GIr +RaVuIIo7cnqkDGXjnVTtganrRfdS0YzUh7jkoxN0us9YBMKyJnNNIDMEJW5SOU2i +t2qluYBIdfEdvB/LWOgJt0YDQUtk0ed7RCQqauBKDPWLvZ+Q8j4DOdZgW8makCS1 +-----END RSA PRIVATE KEY----- diff --git a/src/test/resources/encrypted_issue_369_rsa_pem.pub b/src/test/resources/encrypted_issue_369_rsa_pem.pub new file mode 100644 index 00000000..92c85d94 --- /dev/null +++ b/src/test/resources/encrypted_issue_369_rsa_pem.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqGObM6DyGcra+/eBdm7mIvnLM9oUDUwpT3hRbF7S7V6bb64EuFlduGXJKK+i4/a/ls1Sm3GVwnWvwuRxrVXgv1497MYDzDcflfm+bQLUN38qNscVhn3jdtFuCTIc7VKmtEd9zn4HVzQhUYWq/oiqwOsDeP1QidWbNwgBthCxzhfdi9XUpdpT3Tm/mtLwEz8KZ2kP9VdfXBql6NI64COcI+B2nzE2YwxZsLGFj1orstRZ3WOLTJdLtQ1NhBQqTEwFD06Jhf50wo13xV84jWeZom+ylJQcj9njnfH8Kf7T+1eyBAqCPLvS6kM5Q9J6bGPj67Hkp96zPT/y3Gh1le80o3JrYjo20NqWzO+/OIXilrDoYdUKJbgoM7GXtMlxFNEnj7BIf6TLNZJY9bA6dw1yFs9wOl9LRhC1zUC6kESx8DIiqSpxrZTF0ScgvC/gKqX6K81ovJBklrAuTQpTqGKOW2eY5jqiRAVa7Gx8sPPtHkGqodD/sFOKurggVDf2Y1vs= test diff --git a/src/test/resources/issue362_rsa b/src/test/resources/issue362_rsa new file mode 100644 index 00000000..0c0e41d8 --- /dev/null +++ b/src/test/resources/issue362_rsa @@ -0,0 +1,3 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2Pt9umdG1Dp/JGFHN/fT9pq+xIdBOzXPcz67/NAdOcpH5rAjNOd0ohmEIMD+Wgc2ZPNz3VnWCCtdlPXjuVs08E9qaPT62U9AL7ARp1OzJM0kpV1+/XvRC2xmDVleA2n0/1R8VcArqHLRu1ylKKxgN2R8Rm4TVsqP/uizcMgjHMX6Y6QnpSVp+DE8BarNA1ZnuEdksvi2lYNzb+VW24tu3CGwPujq4rdmow1FAhyb3EJaRqzOsfcNM27eKVCOshzwhyCKmRD849dvORghemUM4tEzBH+MB7QPHzdV4J9bwcPiBJKwAJ0FHk1jkdZ3OggxRBk5cmJSLs+t+COAcH4QZQIDAQABAoIBAGcA5AuEEWyYJFkZ0Nwxyq6LgTn8VywLfGJiCo2WIfmYHA/X1666nXSCFmYSF+yW9exwYbVXezI/m9ol7CfGs1fM61/Nw/M7GuZId+jt4+H5fIb/3lPo3jDFEaEOpoGKYCKBcdCnPFJnx0ZhUYoAYmCJVDF++bE+0aKZxu0oJPr35Lda1hBeCfrb8fDReAAU71jIoOGyuSyOXlCVyT+OmBhVme0u2xDpdY8cd5t9YueTK7BFNjNt3sJOI4L+2wRskNwxqHCrECtuOXmzEyza7vqo8cjRGSx1HZfqhoKgiRpRkRmSi8p+X1+DZOVZuCxc3BTZ7WrPgVZhu7u9hsISKyECgYEA+C9ajUNsx7Hx9gOg6Zvr+zV461PjKI/0n9g0DTZ5RHdcAfeNs3ugmsiDK6b9SayUW08EVyveJ1OJjT/NPvdP0/KhCAjUt+kN0j9f9CPdHKEWoboDca8u67dayOAhC10mNhoAp3tFQryfgpb678GIzqnXkVjGgyWC/8Z81AN7uAkCgYEA39CcJAUVMc6FigTwoMOkdOgNK6qCxAxkNBTdfB/cBBtG3h8g49n0u/fAMjkfL6awdl53j3jVCP4pUZgSSqArLb0aGo84G2vP92iN3d3j/kPiox22w8KG2XR7tHLWcc+CFfk8mdiKdPFxaWVQaYJ6htGq9LGqY3sw5lVzmk0wlH0CgYEAuWim/WGhoo4NdPzA+cTCRqlr7GJ/EY558fBS8ov/jGafFdkawztYgEnLtJDMKH4FVzFwzK65CCggWqWPb7rSqERaiOYQBFTXPnqZ9InWZczyW1/bstJs+yu/ZtIJ3bN5GHHUi0pMM882Wxjv3q12xu2bXbo0k0Uy2GIwXzM6+gECgYBDByur0eXeC7aMdhxGWTEoXdKL8D3HTtq3ikQmhzgR9sVLglEMS9rybCkgIWFImQgh+vqdehd64Pso130q4jrsMMTfjWLFO42Fz8ck2e4M2PHH3f89M0XFXBAsI3Q7k2SnBgRzIpmcmi5X3SKu5oehVqt3KroXnu4vHQpI/LL+1QKBgD3zxf795zFXw53iZXtKkzYNQhEMVRzbx6dQE+tguB0YCb2vPvYaxfejg/02hCQ4twIMuMK18v0Y9W7E34C0m++lP8UbnHdR0Dsg9ryH7OlUMp4662060HWDnDy79swB/Nnk9dxf+BoPIXGny84bEpk6cLoiHuL6TRpQhuJ292DW +-----END RSA PRIVATE KEY----- diff --git a/src/test/resources/issue362_rsa.pub b/src/test/resources/issue362_rsa.pub new file mode 100644 index 00000000..d378cbe0 --- /dev/null +++ b/src/test/resources/issue362_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDY+326Z0bUOn8kYUc399P2mr7Eh0E7Nc9zPrv80B05ykfmsCM053SiGYQgwP5aBzZk83PdWdYIK12U9eO5WzTwT2po9PrZT0AvsBGnU7MkzSSlXX79e9ELbGYNWV4DafT/VHxVwCuoctG7XKUorGA3ZHxGbhNWyo/+6LNwyCMcxfpjpCelJWn4MTwFqs0DVme4R2Sy+LaVg3Nv5Vbbi27cIbA+6Orit2ajDUUCHJvcQlpGrM6x9w0zbt4pUI6yHPCHIIqZEPzj1285GCF6ZQzi0TMEf4wHtA8fN1Xgn1vBw+IEkrAAnQUeTWOR1nc6CDFEGTlyYlIuz634I4BwfhBl test diff --git a/src/test/resources/issue_369_rsa_opensshv1 b/src/test/resources/issue_369_rsa_opensshv1 new file mode 100644 index 00000000..2422781e --- /dev/null +++ b/src/test/resources/issue_369_rsa_opensshv1 @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4gIBAAKCAYEA9bm+MifEwl4D2k7QEhANN+CvvOc6NjA5UQge7R4KZiKSu27T +8Ys2BDxD8sFZwURjCZ4SMiih3OwB8wqLN7ZBlWDQZQxpp6GmU3maoZrEUqqsR8ny +EUNU/mhiDeLHJYmfolvESP3Y2Mnz5XAEdypLvkN8bEeStss88WYmAf6w/V470UbX +O3GGZR2jHtjQbUIYd4WpkFUhCTI30gqauKJFC5NFMrV4XWz7T/7Sw76niu6Kfqve +4RUtMEWPcU2jTPqlIboWijW4bnsYBkAZO1nuOS3Eejt18IwAfl+6MGuCbSsfaCmP +d/t6rTPufup0ZxZ5Afxi+X3Sj5iLMENbzkoBnhIYPzNMs19DxDMeKn3M6VKdqxbT +EzYPTul8/aiQp2cvyNRJiuhRF3q6RGNMeNZmNjQQaK/b2rWjPSMcydVSK2vCWI5z +6huPFfSxYUhKRX4tQqrqibtX5rcbR+GDVljUBAiqdLPlpsSgLtmQAK9cJfrTZJIm +Tmg0pbk9eKjJEhI1AgMBAAECggGAEOhyf2YAInWwoy1oIM4M8srZnNB2T3M7Bmne +Iue9xHBdk1sZZ1XyZhE1hbcrM2K+w9MmImBsXtS4f546nR9D3QD35fQYMwoq9TR5 +YORS3PNUfm4VY320E/tfv9/aXylcnCHfXDxnVudyileOXxrAcnuXTKYSINTUQTIL +rHh3ej+pMwnCVptFqaCD8GPv14zEPTkrxTwuVUEo2SGUqt6zjIvaJ5aYDSmqE0OQ +AhsU0Tj/u56c6/T2kos+xweWFH5siuTKHnC4GOxeFMlHOKohTVPUgkzzN9MbzuSA +YjZAknZeHTHcaWFtFFeSFBbmzVJZKfoDCC5ESpLYZLRgV9AqU0dsoDJUP5ET8lHx +uh4SNcCXR8acN3qkGSJRijAff2jdVQiLe0hqkMurhuJzArGHVpjphXWvDwxe8mLe +nrjheykKn6CuWkrJWoFUIrDUaCcY26p9dNFdxPrEZfgUpnzCiDCGDIgj1lJvt1U0 +ifx7ttUI45UC6URU/yR9EP2TvmRBAoHBAPwYRRjVacqINmAJFnU9+4eD22uOI1ed +bfMUqqwt4ivWWmKs0ZI+GJmXiYSWettuN+Rtv1KOK8SAZSz3OdSWcwLuz8dlh4O7 +cjjInaXTz9Tf4sqMaiC/IdZlmt6ngZ7ZG+r8zR9HhxUwy7Cs9GpYF5m3BJmZn1LZ +bP9TH3dqvZH5G+Wn8ltNl1pVKM6rC0ARKGBMa7Bo0rsv1rUvmEetaZu5iiah5r6h +XTmb5jPLEaMxklxkqfkOIHgairYegEip1QKBwQD5iDbu0QxiA3Uu7DeZx+6GHUY8 +ffKkYXcnKdXFNIbeBGFcHkWlmZeuA6IqcOBbFTHpSpKcRmVuzvmd3TtP4FcIQxuH +RqJ6TRMeXm+Y+GzBLeVKiOCrABsKapUy3TLTDtguAbFlTduuM1HOYmq5vCe3o2MK +tgpOKJEHaOWHzrznMRgehjoWTeq3WuuMZmbYfjDsDo7IleZslWkIokWD0U7SERBo +D05/x9x5fSNqyyjM8NZ/ik2ujPUR+WdCHtXqluECgcA/k59Zc/kKKvALqD8RsmAM +/SQJK/+dyQZBl6SzZ57yj0ycNhlkWGS714vG9GxnipRt93+YwmInXHonrPHYu1im +FLQyBVj3z/4uc+nOOGzhstTvYBojyBAwkc9M99GozfhMexUAHnnizjuQgw3hA/Zv +vchbNHMJ4eurOLtm0nScq8ZtVL26aQcSsQdpl5luvuT/5EYEZ2s96gKsDyTIbuOD +cnd05r/as7dfIAIebcg07/uJcZmsRfPKVmdFJswTh0ECgcBr8FU61ujRWQeOpZWj +is2N7Anezuhv3M1K/pi+9mrEjQaEb3/XE2p+VooGa89Q9wkhDiX/PaBQ320wsWsf +sT5Uj5rP2GkeGEsF3vnNJOD+a1j89dqhfak0x0gEuZRrocc3l3niBVzarM5dRUs/ +TrmrgRytnHM2veuGVgS7y10BcMYrJgrobQn0CHtNv1oLmgKVifKPp/AF1leZ6X/C +dn7u9XywVraxJYYkc1IntvvOMvvGLdBOiiDUhpr5Cheko2ECgcAEFU2CTCaTzV/s +xTySE3AK6flhNaxdwL0wjK0I8DUZhfpqrBJVnlfV35Y5dXWl09qR/f1r6ZQOg2Ew +HiUnsFq8XxAwvQ7SrrLvGY7OAxp4mSeUufP/uP9hNIZQoXof6gxosLxeTfDq4ddA +dWQCygbX8PQRrrg7BMqC1pRIGljwt7ijKy2H4RS8WteGlM+nTrps9iEImAqxoOAz +ky2V56s1enP1E+5C/1Hr6yyVSxBF46482Bpl2WvJG6BBQRV146E= +-----END RSA PRIVATE KEY----- diff --git a/src/test/resources/issue_369_rsa_opensshv1.pub b/src/test/resources/issue_369_rsa_opensshv1.pub new file mode 100644 index 00000000..f85c3b06 --- /dev/null +++ b/src/test/resources/issue_369_rsa_opensshv1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD1ub4yJ8TCXgPaTtASEA034K+85zo2MDlRCB7tHgpmIpK7btPxizYEPEPywVnBRGMJnhIyKKHc7AHzCos3tkGVYNBlDGmnoaZTeZqhmsRSqqxHyfIRQ1T+aGIN4scliZ+iW8RI/djYyfPlcAR3Kku+Q3xsR5K2yzzxZiYB/rD9XjvRRtc7cYZlHaMe2NBtQhh3hamQVSEJMjfSCpq4okULk0UytXhdbPtP/tLDvqeK7op+q97hFS0wRY9xTaNM+qUhuhaKNbhuexgGQBk7We45LcR6O3XwjAB+X7owa4JtKx9oKY93+3qtM+5+6nRnFnkB/GL5fdKPmIswQ1vOSgGeEhg/M0yzX0PEMx4qfczpUp2rFtMTNg9O6Xz9qJCnZy/I1EmK6FEXerpEY0x41mY2NBBor9vataM9IxzJ1VIra8JYjnPqG48V9LFhSEpFfi1CquqJu1fmtxtH4YNWWNQECKp0s+WmxKAu2ZAAr1wl+tNkkiZOaDSluT14qMkSEjU= test diff --git a/src/test/resources/issue_369_rsa_pem b/src/test/resources/issue_369_rsa_pem new file mode 100644 index 00000000..bdd875ba --- /dev/null +++ b/src/test/resources/issue_369_rsa_pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG5AIBAAKCAYEAs3v+RtRTyr4HJy16h1eI21HzvoIu+QVClFigaMHYOt5K/Xtx +nRlZsLQQVkyTMsRFlbSTKx/AgEVqMlYiC0MvaBX223MLgJ34VY3T1TaSu0S9WcAg +q7IOlA6RPlaeK01s1HA11MiNjGa3jyQsFNPlFnb6BCV8BNjYrW/rEvNNiLHZcIkY +/ZJ8yiea1XKRqUNEJHNI23jQ3850QBPAEWsJAy3FxIqPVereKbAFn9wWxxmHnPT2 +QGHHcwh3MJi5k0/w0jhfrrdn8b4wcnVicxh5isqM5RTY04YF7dItH3HnSxtKABzT +QS/FHhGEOx9IJW0/a7JzKjgLYAWA/5yRnzJDDIz+zUUtGNI7ZJmPD0N5e0ZB0xXi +lZCqSrcg9/i7H7jmZIeQOq4TmYDD/fttgGYSFxoaPZCWUkhewfQlmvkwq+YmaPZS +123jBUa5OS7YOwTR5DuUcfEhq6BmBpC17fPfni/h7dSOI26tX1heeWEGJ0zqL75z +VVQqeItHPCpTQKkjAgMBAAECggGAHHtwvoyzP1koiW8OIqwha6x1oaXHDn1nM2Nd +EUKxraXZAKC2RtffA8uPTCKauVOsNzWQpSdExRY+4/4HKQJgY4QYyHpZOO/YmLsJ +AqzGXDFsWvHCPXzkE1q8ccgNEZIX0x57bGjnDYC/YFe9JxD5Kbd3tXC9XYeL2voH +s9ooU6jleJZJAPReGTZvu6+SremexqoExc9GVj3M9N4tfJYfuAFrgOT8GgZLexIT +0mp6st26R44YBd9+ZyLQx0V1LYthx+Z3uMA4BYyTc0P3TcgRiazIBjhFC7gcbFwR +kma/MFv9P4jvUeNYJuQIueF5Bb24/FvRYp5o6HGTiE2pGUv1DAGK1PvI711D1aDd +TAcLhwkzPog1C9KPyz3mAn6p6x8S8+Lk35AjGGBE7i3s6ZGJF17SJW7BwzKJ1vSe +XX9U3XLq3DErPx9AnQhCAiPihohx3Cm8Hy68NGSHjRdu5CeWJiczX0TKHUNSx6TM +Jo5Ew5Lplr01x5UsjDbwgEOPToEBAoHBAN5UGGcXgkQ2jn/pH/BOQ7FnHNtS0/lo +feBnUHLEGRnFylka3sRak0fF6bMj48NrGyiI9QFNv/NeHqwhjGYGVeK75HGJENPC +6Wlewifo6Nw/GxmBo0sewEZ8XEyqClIkpj1oOhIbkGyeT/dXZjbyvx+kdz0FTAyU +/fUB20Ai3rgS600r6GSQKloOs2iAnELugpkTYeB7Fiwe2613i4l7myVLowhwYhu3 +BBW5kg+ApasSTwloJlC454TO1RylJ/GwjwKBwQDOqslErBZfb2Lvm8DkequQ9kzp +dIw8pZoPLXkRsU+X2kifxCY9W6SWlgzMQptX/wWwI6QB7fMIwLB1Umtzuu7vwlzB ++NBgeflLdWlWT76/mc2GfbW9QsKvnAR2ar3z9GV+zmHp09NPddFn2bZdLlXl736p +4C8MfLTZlmSwwJm2MYyoGQB/NfMMrJ/yaAPJV+AdlE0hV3A+5a1cRim2n8rUVEHj +OJ4x7AqxA+xt88picKIAXEtTGyjsj6DQQdUBYC0CgcEAv00u/i3NSfKDpO4sLDK7 +rn8h5lobyQQvI5LiNw4i5vk4xnkHa37gMabLEvhzt6eGY9eMsYV7/+VhkQ0A6JzU +89Zml4av8vZIrwD5ISwYicLHB6hzoGSiX0QMi27YmJuuazIunXwYRk3mUtZiPi+b +Ype6fcf8Cut8pX/mbwZSC6ND0lBQk4800e7KUsYvLqxZtWtnEaf3iRk4PseZSkAQ +XAP8EXvZ/yz4F9VoJ2yzoEKNvXNfXJ/tnmn2F8LIXv9jAoHAccENwa/bLLKZyXt4 +xApFbygzE3kkS6l3UA1ei3+GaPYsbUxBJBrSUFTNPI0ZBmmHzvj/KFS6JkIxnpI8 +NNpa9DuOZPI4eDILJx68WVbRjpLwzqtZIpChqpl811VPsvz99LtSp6sBr8YQ+lGa +kFWV0Fdv579PBleKEA445BVPRjqlykzguiSO4JYQABSCqQumf4GGpuiDDwvKzXSN +N0ljElZCYfhjEuVyyRZ4x9iduGt4sCwdBeR4NSWlhZwGy5gNAoHBAMmDur+qZ5sL +bb+AVsIPOhS1Nk64QyXru6Bz0M6doY3pgZJRGmXPwqfpHFsVGrjFoYU0Xxz4zWJb +fY0UBJZfb2vgLcHGVYCUL6C6d+f0m6DFDB7iqY06hU7EirYzA6iAcKiIHgAbELB2 +7ux+jieXxWKaMsJWYgR7XQn/UbC5xuOPSazZ5ws3Hz8toUK1tsYjXe+RoZioij2f +Xm+9NXEFFGhLtOvepf9qw1fHlIIetCOJ/yrCJKVzFmzhUReGPAnmoA== +-----END RSA PRIVATE KEY----- diff --git a/src/test/resources/issue_369_rsa_pem.pub b/src/test/resources/issue_369_rsa_pem.pub new file mode 100644 index 00000000..2178b42d --- /dev/null +++ b/src/test/resources/issue_369_rsa_pem.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCze/5G1FPKvgcnLXqHV4jbUfO+gi75BUKUWKBowdg63kr9e3GdGVmwtBBWTJMyxEWVtJMrH8CARWoyViILQy9oFfbbcwuAnfhVjdPVNpK7RL1ZwCCrsg6UDpE+Vp4rTWzUcDXUyI2MZrePJCwU0+UWdvoEJXwE2Nitb+sS802IsdlwiRj9knzKJ5rVcpGpQ0Qkc0jbeNDfznRAE8ARawkDLcXEio9V6t4psAWf3BbHGYec9PZAYcdzCHcwmLmTT/DSOF+ut2fxvjBydWJzGHmKyozlFNjThgXt0i0fcedLG0oAHNNBL8UeEYQ7H0glbT9rsnMqOAtgBYD/nJGfMkMMjP7NRS0Y0jtkmY8PQ3l7RkHTFeKVkKpKtyD3+LsfuOZkh5A6rhOZgMP9+22AZhIXGho9kJZSSF7B9CWa+TCr5iZo9lLXbeMFRrk5Ltg7BNHkO5Rx8SGroGYGkLXt89+eL+Ht1I4jbq1fWF55YQYnTOovvnNVVCp4i0c8KlNAqSM= test