mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 07:10:53 +03:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6213401db | ||
|
|
19ec6d9d8d | ||
|
|
0ba491c01a | ||
|
|
73d7560e6e | ||
|
|
0e0d730bbf | ||
|
|
6becee176a | ||
|
|
4b1619d54f | ||
|
|
64f448d438 | ||
|
|
a5efdf1f0d | ||
|
|
588853554a | ||
|
|
7bde5c15c1 | ||
|
|
3c85b86915 | ||
|
|
2ca0fa4732 | ||
|
|
769c896e53 | ||
|
|
eb19325bc6 | ||
|
|
2d8af5a687 | ||
|
|
c4fef33d8f | ||
|
|
ff85e832af | ||
|
|
0dcb4b9a7a | ||
|
|
2baf51bf64 | ||
|
|
3194fd9bd0 | ||
|
|
ab3f0143bd | ||
|
|
9671352bda | ||
|
|
91105e6a07 | ||
|
|
4e802cec86 | ||
|
|
dfdc464e08 | ||
|
|
fa7c40cc66 | ||
|
|
b1be9258b4 | ||
|
|
11543b2c00 | ||
|
|
3526694558 | ||
|
|
d618156ede | ||
|
|
98063680bc | ||
|
|
17754a65fe | ||
|
|
2bb52fcf7d | ||
|
|
1a70023e2d | ||
|
|
5e25c017bf | ||
|
|
27a5039831 | ||
|
|
c2d25a9d62 | ||
|
|
2a22809de2 | ||
|
|
9d1f6d9d83 | ||
|
|
4542d94440 | ||
|
|
46a0cbac9e | ||
|
|
f470ddf219 | ||
|
|
d09276fe01 | ||
|
|
241c355e20 | ||
|
|
56ef6c1223 |
55
.github/workflows/gradle.yml
vendored
Normal file
55
.github/workflows/gradle.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# This workflow will build a Java project with Gradle
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Build SSHJ
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
java12:
|
||||
name: Build with Java 12
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 12
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 12
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew check
|
||||
# java10:
|
||||
# name: Build with Java 10
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Set up JDK 10
|
||||
# uses: actions/setup-java@v1
|
||||
# with:
|
||||
# java-version: 10
|
||||
# - name: Grant execute permission for gradlew
|
||||
# run: chmod +x gradlew
|
||||
# - name: Build with Gradle
|
||||
# run: ./gradlew check -xanimalsnifferMain -xanimalsnifferTest
|
||||
|
||||
integration:
|
||||
name: Integration test
|
||||
needs: [java12]
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 12
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew integrationTest
|
||||
|
||||
1
.java-version
Normal file
1
.java-version
Normal file
@@ -0,0 +1 @@
|
||||
11.0
|
||||
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
||||
language: java
|
||||
dist: trusty
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- openjdk8
|
||||
- oraclejdk9
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
before_install:
|
||||
- pip install --user codecov
|
||||
|
||||
script:
|
||||
- ./gradlew check
|
||||
- ./gradlew integrationTest
|
||||
|
||||
after_success:
|
||||
- codecov
|
||||
2
NOTICE
2
NOTICE
@@ -15,7 +15,7 @@ The Apache Software Foundation (http://www.apache.org/):
|
||||
== in this case for the SSHD distribution. ==
|
||||
=========================================================================
|
||||
|
||||
This product contains software developped by JCraft,Inc. and subject to
|
||||
This product contains software developed by JCraft,Inc. and subject to
|
||||
the following license:
|
||||
|
||||
Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc.
|
||||
|
||||
15
README.adoc
15
README.adoc
@@ -1,7 +1,7 @@
|
||||
= sshj - SSHv2 library for Java
|
||||
Jeroen van Erp
|
||||
:sshj_groupid: com.hierynomus
|
||||
:sshj_version: 0.27.0
|
||||
:sshj_version: 0.30.0
|
||||
:source-highlighter: pygments
|
||||
|
||||
image:https://api.bintray.com/packages/hierynomus/maven/sshj/images/download.svg[link="https://bintray.com/hierynomus/maven/sshj/_latestVersion"]
|
||||
@@ -73,12 +73,12 @@ key exchange::
|
||||
`diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
|
||||
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
|
||||
|
||||
SSHJ also supports the following extended (non official) key exchange algoriths:
|
||||
SSHJ also supports the following extended (non official) key exchange algorithms:
|
||||
`diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`,
|
||||
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
|
||||
|
||||
signatures::
|
||||
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519`
|
||||
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519`, `ssh-rsa2-256`, `ssh-rsa2-512`
|
||||
|
||||
mac::
|
||||
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`, `hmac-ripemd160`, `hmac-ripemd160@openssh.com`
|
||||
@@ -105,6 +105,13 @@ Issue tracker: https://github.com/hierynomus/sshj/issues
|
||||
Fork away!
|
||||
|
||||
== Release history
|
||||
SSHJ 0.30.0 (2020-??-??)::
|
||||
* **BREAKING CHANGE**: Removed `setSignatureFactories` and `getSignatureFactories` from the Config and switched them for `getKeyAlgorithms` and `setKeyAlgorithms`
|
||||
* Fixed https://github.com/hierynomus/sshj/pulls/588[#588]: Add support for `ssh-rsa2-256` and `ssh-rsa2-512` signatures
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/579[#579]: Fix NPE in OpenSSHKnownHosts
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/587[#587]: Add passwordfinder retry for OpenSSHKeyV1KeyFile
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/586[#586]: Make KeyType compatible with Android Store
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/593[#593]: Change `UserAuth.getAllowedMethods()` to Collection return type
|
||||
SSHJ 0.27.0 (2019-01-24)::
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/415[#415]: Fixed wrongly prefixed '/' to path in SFTPClient.mkdirs
|
||||
* Added support for ETM (Encrypt-then-Mac) MAC algorithms.
|
||||
@@ -152,7 +159,7 @@ SSHJ 0.19.1 (2016-12-30)::
|
||||
* Enabled PKCS5 Key files in DefaultConfig
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/291[#291]: Fixed sshj.properties loading and chained exception messages
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/284[#284]: Correctly catch interrupt in keepalive thread
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/292[#292]: Pass the configured RandomFactory to Diffie Hellmann KEX
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/292[#292]: Pass the configured RandomFactory to Diffie Hellman KEX
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/256[#256]: SSHJ now builds if no git repository present
|
||||
* LocalPortForwarder now correctly interrupts its own thread on close()
|
||||
SSHJ 0.19.0 (2016-11-25)::
|
||||
|
||||
@@ -17,7 +17,7 @@ configurations {
|
||||
pom
|
||||
}
|
||||
|
||||
def bouncycastleVersion = "1.50"
|
||||
def bouncycastleVersion = "1.66"
|
||||
|
||||
dependencies {
|
||||
compile "org.slf4j:slf4j-api:1.7.7"
|
||||
|
||||
71
build.gradle
71
build.gradle
@@ -6,16 +6,15 @@ plugins {
|
||||
id "java"
|
||||
id "groovy"
|
||||
id "jacoco"
|
||||
id "osgi"
|
||||
id "com.github.blindpirate.osgi" version '0.0.3'
|
||||
id "maven-publish"
|
||||
id 'pl.allegro.tech.build.axion-release' version '1.9.2'
|
||||
id "com.bmuschko.docker-remote-api" version "3.2.1"
|
||||
id 'pl.allegro.tech.build.axion-release' version '1.11.0'
|
||||
id "com.bmuschko.docker-remote-api" version "6.4.0"
|
||||
id "com.github.hierynomus.license" version "0.12.1"
|
||||
id "com.jfrog.bintray" version "1.7"
|
||||
id "com.jfrog.bintray" version "1.8.5"
|
||||
id 'ru.vyarus.java-lib' version '1.0.5'
|
||||
// id 'ru.vyarus.pom' version '1.0.3'
|
||||
id 'ru.vyarus.github-info' version '1.1.0'
|
||||
id 'ru.vyarus.animalsniffer' version '1.4.2'
|
||||
}
|
||||
|
||||
group = "com.hierynomus"
|
||||
@@ -39,33 +38,29 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.6
|
||||
targetCompatibility = 1.6
|
||||
|
||||
configurations.compile.transitive = false
|
||||
|
||||
def bouncycastleVersion = "1.60"
|
||||
def bouncycastleVersion = "1.66"
|
||||
def sshdVersion = "2.1.0"
|
||||
|
||||
dependencies {
|
||||
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
||||
implementation "org.slf4j:slf4j-api:1.7.7"
|
||||
implementation "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
||||
implementation "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
||||
implementation "com.jcraft:jzlib:1.1.3"
|
||||
implementation "com.hierynomus:asn-one:0.4.0"
|
||||
|
||||
compile "org.slf4j:slf4j-api:1.7.7"
|
||||
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
|
||||
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
|
||||
compile "com.jcraft:jzlib:1.1.3"
|
||||
implementation "net.i2p.crypto:eddsa:0.3.0"
|
||||
|
||||
compile "net.i2p.crypto:eddsa:0.3.0"
|
||||
|
||||
testCompile "junit:junit:4.12"
|
||||
testCompile 'org.spockframework:spock-core:1.3-groovy-2.4'
|
||||
testCompile "org.mockito:mockito-core:2.28.2"
|
||||
testCompile "org.apache.sshd:sshd-core:$sshdVersion"
|
||||
testCompile "org.apache.sshd:sshd-sftp:$sshdVersion"
|
||||
testCompile "org.apache.sshd:sshd-scp:$sshdVersion"
|
||||
testRuntime "ch.qos.logback:logback-classic:1.2.3"
|
||||
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.4.4'
|
||||
testCompile 'org.apache.httpcomponents:httpclient:4.5.9'
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation 'org.spockframework:spock-core:1.3-groovy-2.4'
|
||||
testImplementation "org.mockito:mockito-core:2.28.2"
|
||||
testImplementation "org.apache.sshd:sshd-core:$sshdVersion"
|
||||
testImplementation "org.apache.sshd:sshd-sftp:$sshdVersion"
|
||||
testImplementation "org.apache.sshd:sshd-scp:$sshdVersion"
|
||||
testRuntimeOnly "ch.qos.logback:logback-classic:1.2.3"
|
||||
testImplementation 'org.glassfish.grizzly:grizzly-http-server:2.4.4'
|
||||
testImplementation 'org.apache.httpcomponents:httpclient:4.5.9'
|
||||
|
||||
}
|
||||
|
||||
@@ -78,6 +73,10 @@ license {
|
||||
excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java', '**/org/mindrot/jbcrypt/*.java'])
|
||||
}
|
||||
|
||||
if (!JavaVersion.current().isJava9Compatible()) {
|
||||
throw new GradleScriptException("Minimum compilation version is Java 9")
|
||||
}
|
||||
|
||||
// This disables the pedantic doclint feature of JDK8
|
||||
if (JavaVersion.current().isJava8Compatible()) {
|
||||
tasks.withType(Javadoc) {
|
||||
@@ -85,6 +84,11 @@ if (JavaVersion.current().isJava8Compatible()) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
compileJava {
|
||||
options.compilerArgs.addAll(['--release', '7'])
|
||||
}
|
||||
|
||||
task writeSshjVersionProperties {
|
||||
doLast {
|
||||
project.file("${project.buildDir}/resources/main").mkdirs()
|
||||
@@ -129,8 +133,8 @@ sourcesJar {
|
||||
}
|
||||
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestRuntime.extendsFrom testRuntime
|
||||
integrationTestImplementation.extendsFrom testImplementation
|
||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -223,7 +227,7 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey
|
||||
publications = ["maven"]
|
||||
pkg {
|
||||
repo = "maven"
|
||||
name = project.name
|
||||
name = "${project.name}"
|
||||
licenses = ["Apache-2.0"]
|
||||
vcsUrl = "https://github.com/hierynomus/sshj.git"
|
||||
labels = ["ssh", "sftp", "secure-shell", "network", "file-transfer"]
|
||||
@@ -257,22 +261,23 @@ jacocoTestReport {
|
||||
|
||||
task buildItestImage(type: DockerBuildImage) {
|
||||
inputDir = file('src/itest/docker-image')
|
||||
tag = 'sshj/sshd-itest'
|
||||
images.add('sshj/sshd-itest:latest')
|
||||
}
|
||||
|
||||
task createItestContainer(type: DockerCreateContainer) {
|
||||
dependsOn buildItestImage
|
||||
targetImageId { buildItestImage.getImageId() }
|
||||
portBindings = ['2222:22']
|
||||
targetImageId buildItestImage.getImageId()
|
||||
hostConfig.portBindings = ['2222:22']
|
||||
hostConfig.autoRemove = true
|
||||
}
|
||||
|
||||
task startItestContainer(type: DockerStartContainer) {
|
||||
dependsOn createItestContainer
|
||||
targetContainerId { createItestContainer.getContainerId() }
|
||||
targetContainerId createItestContainer.getContainerId()
|
||||
}
|
||||
|
||||
task stopItestContainer(type: DockerStopContainer) {
|
||||
targetContainerId { createItestContainer.getContainerId() }
|
||||
targetContainerId createItestContainer.getContainerId()
|
||||
}
|
||||
|
||||
task forkedUploadRelease(type: GradleBuild) {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package net.schmizz.sshj.examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** This examples demonstrates how to configure {@link net.schmizz.sshj.SSHClient} client with an in-memory known_hosts file */
|
||||
public class InMemoryKnownHosts {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
InputStream entry = new ByteArrayInputStream("localhost ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPmhSBtMctNa4hsZt8QGlsYSE5/gMkjeand69Vj4ir13".getBytes(Charset.defaultCharset()));
|
||||
SSHClient ssh = new SSHClient();
|
||||
ssh.addHostKeyVerifier(new InMemoryHostKeyVerifier(entry, Charset.defaultCharset()));
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSCPFileTransfer().download("test_file", new FileSystemFile("/tmp/"));
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static class InMemoryHostKeyVerifier implements HostKeyVerifier {
|
||||
|
||||
private final List<OpenSSHKnownHosts.KnownHostEntry> entries = new ArrayList<OpenSSHKnownHosts.KnownHostEntry>();
|
||||
|
||||
public InMemoryHostKeyVerifier(InputStream inputStream, Charset charset) throws IOException {
|
||||
final OpenSSHKnownHosts.EntryFactory entryFactory = new OpenSSHKnownHosts.EntryFactory();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset));
|
||||
while(reader.ready()) {
|
||||
String line = reader.readLine();
|
||||
try {
|
||||
OpenSSHKnownHosts.KnownHostEntry entry = entryFactory.parseEntry(line);
|
||||
if (entry != null) {
|
||||
entries.add(entry);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//log error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String hostname, int port, PublicKey key) {
|
||||
final KeyType type = KeyType.fromKey(key);
|
||||
if (type == KeyType.UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (OpenSSHKnownHosts.KnownHostEntry e : entries) {
|
||||
try {
|
||||
if (e.appliesTo(type, hostname) && e.verify(key)) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
//log error
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=8626cbf206b4e201ade7b87779090690447054bc93f052954c78480fa6ed186e
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
51
gradlew
vendored
51
gradlew
vendored
@@ -1,5 +1,21 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
@@ -109,8 +125,8 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
@@ -138,19 +154,19 @@ if $cygwin ; then
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -159,14 +175,9 @@ save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
21
gradlew.bat
vendored
21
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@@ -13,8 +29,11 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -7,6 +7,7 @@ ADD test-container/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ecdsa_key.pub
|
||||
ADD test-container/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key
|
||||
ADD test-container/ssh_host_ed25519_key.pub /etc/ssh/ssh_host_ed25519_key.pub
|
||||
ADD test-container/sshd_config /etc/ssh/sshd_config
|
||||
ADD test-container/users_rsa_ca.pub /etc/ssh/users_rsa_ca.pub
|
||||
|
||||
RUN apk add --no-cache tini
|
||||
RUN \
|
||||
@@ -20,4 +21,4 @@ RUN \
|
||||
chmod 644 /etc/ssh/ssh_host_ed25519_key.pub && \
|
||||
chown -R sshj:sshj /home/sshj
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "/entrypoint.sh"]
|
||||
ENTRYPOINT ["/sbin/tini", "/entrypoint.sh", "-o", "LogLevel=DEBUG2"]
|
||||
@@ -5,3 +5,5 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8ww4hJG/gHJYdkjTTBDF1GNz+228nuWprPV+NbQauA
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOaWrwt3drIOjeBq2LSHRavxAT7ja2f+5soOUJl/zKSI ajvanerp@Heimdall.xebialabs.com
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQmTIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmuhA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPCliztspKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmLGGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQ== no-passphrase
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKRyZAtOJJfAhPU6xE6ZXY564vwErAI3n3Yn4lTHL9bxev9Ily6eCqPLcV0WbSV04pztngFn9MjT7yb8mcXheHpIaWEH569sMpmpOtyfn4p68SceuXBGyyPGMIcfOTknkASd1JYSD4EPkd9rZmCzcx3vEnLu8ChnA/G221xSVQ5VC/jD/c/CgNUayhQ+xbn57qHKKtZwfTa21QmwIabGYJNwlVjlKTCdddeVnZfKqKrG7cxHQApsxd21rhM9IT/C/f4Y/Tx3WUUVeam0iZ265oiPHoPALqJIWSQIUheRYAxYAQqJwSQ0Or9MM8XXun2Iy3RUSGk6eIvrCsFbNURsHNs7Pu0UnpYv6FZ3vCkFep/1pAT6fQvY7pDOOWDHKXArD4watc9gIWaQBH73wDW/KgBcnMRSoGWgQjsYqIamP4oV1+HqUI3lRAsXZaX+eiBGt3+3A5KebP27UJ1YUwhwlzs7wzTKaCu0OaL+hOsP1F2AxAa995bgFksMd23645ux3YCJKXG4sGpJ1Z/Hs49K72gv+QjLZVxXqY623c8+3OUhlixqoEFd4iG7UMc5a552ch/VA+jaspmLZoFhPz99aBRVb1oCSPxSwLw+Q/wxv6pZmT+14rqTzY2farjU53hM+CsUPh7dnWXhGG7RuA5wCdeOXOYjuksfzAoHIZhPqTgQ== ajvanerp@Heimdall.local
|
||||
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMvfRYSe44VQGwxexOMibcM3+fWeUP1jrBofOxFDRRrzRF8dK/vll2svqTPXMRnITnT1UoemEcB5OHtvH4hzfh/HFeDxJ5S7UncYxoClTSa8MeMFG2Zj9CoUZs1SHbwSGg== root@sshj
|
||||
ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHquUYgkU9wJrcxDWVtdqtfqf6SBDdPDRxwDl7OCohV2UNu2KdjJwSj8j0fsPeMdHjSiv9OCnHYrVilQ+W5WW5q5wGXwk10oIcV0JJscohLA0nS7mKinBrxUwVHnNZbPExFciicnEArcYRb1BuT7HF8hfjuSSpWS0rob6kloSSi/jV7ZA== root@sshj
|
||||
|
||||
@@ -130,3 +130,5 @@ Subsystem sftp /usr/lib/ssh/sftp-server
|
||||
|
||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1
|
||||
macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-ripemd160-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com
|
||||
|
||||
TrustedUserCAKeys /etc/ssh/users_rsa_ca.pub
|
||||
1
src/itest/docker-image/test-container/users_rsa_ca.pub
Normal file
1
src/itest/docker-image/test-container/users_rsa_ca.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDN70b/cYHZQMD1YW0mlncXqC2l++sEWrVYlIUCzNxNhRYjI4UmEVEq3ru1h6K3ZVAJi1DcZuf5ne1ZXtwJ1Uw1JA4wGdKw+9TwAb5Gubn+VEowgt62kLAPeChiPucTXD0FDDhIUOBv3KxytdrJIYAtzZT27STsBiDF1+7Ld3wk/1Dg9NAaI6q40PmuicTEACQRHn5snI1t9+LgZTd3/PPE5pjJM0ow9+r6mlUUM5oHCk5sZ8DBuRR1Ram4sxp/LFQM+9feMmW3ZM2C5AN0JG4A7NXnlwiTKmNVrGI0iFucBBKhjxN1qdgBF11/42cCrerC9UW1auTTi9mqwEIqBGL30VOPy+dCPQQViP+C09CBgyr3wpZciPKP1mvmcOkC5FDzKg9e3v1JBq0fqZgwt+PPG8cGnxRCGEQ+ZMLDuAixkQUEwDWeMskHLkbjUEiVZydViCPSzFczGtKatQiQVZA5Zx0Gn2sUaQjykhWzqKNL8oIbolEdkH9ubOZWNi0brzU= root@sshj
|
||||
6
src/itest/generate.sh
Normal file
6
src/itest/generate.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# Don't call it frequently. It's rather a documentation how everything is generated.
|
||||
ssh-keygen -f resources/users_rsa_ca -t rsa -N ''
|
||||
mv resources/users_rsa_ca.pub docker-image/test-container
|
||||
ssh-keygen -f resources/keyfiles/id_rsa2 -t rsa -m pem -N ''
|
||||
ssh-keygen -s resources/users_rsa_ca -I my_key_id -n sshj resources/keyfiles/id_rsa2.pub
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
package com.hierynomus.sshj
|
||||
|
||||
import com.hierynomus.sshj.signature.SignatureEdDSA
|
||||
import com.hierynomus.sshj.key.KeyAlgorithms
|
||||
import net.schmizz.sshj.DefaultConfig
|
||||
import net.schmizz.sshj.SSHClient
|
||||
import net.schmizz.sshj.signature.SignatureECDSA
|
||||
import net.schmizz.sshj.transport.TransportException
|
||||
import net.schmizz.sshj.userauth.UserAuthException
|
||||
import spock.lang.Unroll
|
||||
@@ -29,7 +28,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
|
||||
def "should accept correct key for #signatureName"() {
|
||||
given:
|
||||
def config = new DefaultConfig()
|
||||
config.setSignatureFactories(signatureFactory)
|
||||
config.setKeyAlgorithms(Collections.singletonList(signatureFactory))
|
||||
SSHClient sshClient = new SSHClient(config)
|
||||
sshClient.addHostKeyVerifier(fingerprint) // test-containers/ssh_host_ecdsa_key's fingerprint
|
||||
|
||||
@@ -40,7 +39,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
|
||||
sshClient.isConnected()
|
||||
|
||||
where:
|
||||
signatureFactory << [new SignatureECDSA.Factory256(), new SignatureEdDSA.Factory()]
|
||||
signatureFactory << [KeyAlgorithms.ECDSASHANistp256(), KeyAlgorithms.EdDSA25519()]
|
||||
fingerprint << ["d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3", "dc:68:38:ce:fc:6f:2c:d6:6d:6b:34:eb:5c:f0:41:6a"]
|
||||
signatureName = signatureFactory.getName()
|
||||
}
|
||||
@@ -78,6 +77,8 @@ class IntegrationSpec extends IntegrationBaseSpec {
|
||||
"id_ed25519_opensshv1_protected" | "sshjtest"
|
||||
"id_rsa" | null
|
||||
"id_rsa_opensshv1" | null
|
||||
"id_ecdsa_nistp384_opensshv1" | null
|
||||
"id_ecdsa_nistp521_opensshv1" | null
|
||||
}
|
||||
|
||||
def "should not authenticate with wrong key"() {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.signature
|
||||
|
||||
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||
import net.schmizz.sshj.DefaultConfig
|
||||
import spock.lang.Unroll
|
||||
|
||||
class RsaSignatureClientKeySpec extends IntegrationBaseSpec {
|
||||
@Unroll
|
||||
def "should correctly connect using publickey auth with RSA key with signature"() {
|
||||
given:
|
||||
def client = getConnectedClient(new DefaultConfig())
|
||||
|
||||
when:
|
||||
client.authPublickey(USERNAME, "src/itest/resources/keyfiles/id_rsa2")
|
||||
|
||||
then:
|
||||
client.authenticated
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.signature
|
||||
|
||||
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||
import com.hierynomus.sshj.key.KeyAlgorithms
|
||||
import net.schmizz.sshj.DefaultConfig
|
||||
import spock.lang.Unroll
|
||||
|
||||
class SignatureSpec extends IntegrationBaseSpec {
|
||||
|
||||
@Unroll
|
||||
def "should correctly connect with #sig Signature"() {
|
||||
given:
|
||||
def cfg = new DefaultConfig()
|
||||
cfg.setKeyAlgorithms(Collections.singletonList(sigFactory))
|
||||
def client = getConnectedClient(cfg)
|
||||
|
||||
when:
|
||||
client.authPublickey(USERNAME, KEYFILE)
|
||||
|
||||
then:
|
||||
client.authenticated
|
||||
|
||||
where:
|
||||
sigFactory << [KeyAlgorithms.SSHRSA(), KeyAlgorithms.RSASHA256(), KeyAlgorithms.RSASHA512()]
|
||||
sig = sigFactory.name
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,6 @@ package com.hierynomus.sshj.transport.mac
|
||||
|
||||
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||
import net.schmizz.sshj.DefaultConfig
|
||||
import net.schmizz.sshj.transport.mac.HMACRIPEMD160
|
||||
import net.schmizz.sshj.transport.mac.HMACSHA2256
|
||||
import spock.lang.AutoCleanup
|
||||
import spock.lang.Unroll
|
||||
|
||||
class MacSpec extends IntegrationBaseSpec {
|
||||
|
||||
10
src/itest/resources/keyfiles/id_ecdsa_nistp384_opensshv1
Normal file
10
src/itest/resources/keyfiles/id_ecdsa_nistp384_opensshv1
Normal file
@@ -0,0 +1,10 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
|
||||
1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTL30WEnuOFUBsMXsTjIm3DN/n1nlD9
|
||||
Y6waHzsRQ0Ua80RfHSv75ZdrL6kz1zEZyE509VKHphHAeTh7bx+Ic34fxxXg8SeUu1J3GM
|
||||
aApU0mvDHjBRtmY/QqFGbNUh28EhoAAADYHWlHLx1pRy8AAAATZWNkc2Etc2hhMi1uaXN0
|
||||
cDM4NAAAAAhuaXN0cDM4NAAAAGEEy99FhJ7jhVAbDF7E4yJtwzf59Z5Q/WOsGh87EUNFGv
|
||||
NEXx0r++WXay+pM9cxGchOdPVSh6YRwHk4e28fiHN+H8cV4PEnlLtSdxjGgKVNJrwx4wUb
|
||||
ZmP0KhRmzVIdvBIaAAAAMQD3sx28SrtkuhN+Yu06BAoFLMMgneIqguM3jowaz0LWfP1Nhx
|
||||
Rnh9tNKM6YYvygCggAAAAPZmhlbm5la2VATGFwdG9w
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
12
src/itest/resources/keyfiles/id_ecdsa_nistp521_opensshv1
Normal file
12
src/itest/resources/keyfiles/id_ecdsa_nistp521_opensshv1
Normal file
@@ -0,0 +1,12 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
|
||||
1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQB6rlGIJFPcCa3MQ1lbXarX6n+kgQ3
|
||||
Tw0ccA5ezgqIVdlDbtinYycEo/I9H7D3jHR40or/Tgpx2K1YpUPluVluaucBl8JNdKCHFd
|
||||
CSbHKISwNJ0u5iopwa8VMFR5zWWzxMRXIonJxAK3GEW9Qbk+xxfIX47kkqVktK6G+pJaEk
|
||||
ov41e2QAAAEQwljQZcJY0GUAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
|
||||
AAAIUEAeq5RiCRT3AmtzENZW12q1+p/pIEN08NHHAOXs4KiFXZQ27Yp2MnBKPyPR+w94x0
|
||||
eNKK/04KcditWKVD5blZbmrnAZfCTXSghxXQkmxyiEsDSdLuYqKcGvFTBUec1ls8TEVyKJ
|
||||
ycQCtxhFvUG5PscXyF+O5JKlZLSuhvqSWhJKL+NXtkAAAAQVXt20tSeLzMU1U2nMv8CEEY
|
||||
Oyl1WIkGAcRatDBAfsE0+NcJ/eSbPXywWAqCzOElQ5ftNFz9t1kNXwW5qiLaaIBpAAAAD2
|
||||
ZoZW5uZWtlQExhcHRvcAECAwQ=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
39
src/itest/resources/keyfiles/id_rsa2
Normal file
39
src/itest/resources/keyfiles/id_rsa2
Normal file
@@ -0,0 +1,39 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
1
src/itest/resources/keyfiles/id_rsa2-cert.pub
Normal file
1
src/itest/resources/keyfiles/id_rsa2-cert.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgUsee85QZv49JUmGGA6cN852mR2cSsEst9KDH7gk3Lq4AAAADAQABAAABgQDQxnbF27Lzr9VvNlac6fNKpRV5TtqPPBzcsX29H1lu59l0IHylhHOlDgH2/e6YNnedf2o8zh/aIxUN+LPbjVLCa9XmztaI4c+HLeKPmZSlFc6fdrxvJUiN2tppVrAvrjan3xwsoH1YoSMpoPDrfdoPvHNIiiapBuhVmswc2O8hqfJ3dTqP2aejJ1cPVwi5x5nHhL8C/whCu70CJlgnusB9nDPeKn1hZyBPTIiiENsFJft+xfbsoXq3Eh9FckImDRA7SQ5igxCYtgm2RUCnCLF1Xb1XHaDk8EpcNDXYXAZdOdkbfq9hhnueDXY9X7EzILXE8t1efOs1H+U7gEa1PXgZglvBUzeCMEOtx5q9prJVbhKMWupHlcMuOV0oB8q0IcRq8u/XoN/V4DCc+UYDCY4OVGpMe8vmjEFCJu82MPNWm9+oSwVGo/93gBtiMBGwoKR67c/NQj6urkC3BnnZcqAZ2DXfBRGgCQiw1eicqj3Zq8ueiaqyR1Nil8EQ5QxJk70AAAAAAAAAAAAAAAEAAAAJbXlfa2V5X2lkAAAACAAAAARzc2hqAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDN70b/cYHZQMD1YW0mlncXqC2l++sEWrVYlIUCzNxNhRYjI4UmEVEq3ru1h6K3ZVAJi1DcZuf5ne1ZXtwJ1Uw1JA4wGdKw+9TwAb5Gubn+VEowgt62kLAPeChiPucTXD0FDDhIUOBv3KxytdrJIYAtzZT27STsBiDF1+7Ld3wk/1Dg9NAaI6q40PmuicTEACQRHn5snI1t9+LgZTd3/PPE5pjJM0ow9+r6mlUUM5oHCk5sZ8DBuRR1Ram4sxp/LFQM+9feMmW3ZM2C5AN0JG4A7NXnlwiTKmNVrGI0iFucBBKhjxN1qdgBF11/42cCrerC9UW1auTTi9mqwEIqBGL30VOPy+dCPQQViP+C09CBgyr3wpZciPKP1mvmcOkC5FDzKg9e3v1JBq0fqZgwt+PPG8cGnxRCGEQ+ZMLDuAixkQUEwDWeMskHLkbjUEiVZydViCPSzFczGtKatQiQVZA5Zx0Gn2sUaQjykhWzqKNL8oIbolEdkH9ubOZWNi0brzUAAAGUAAAADHJzYS1zaGEyLTUxMgAAAYBxPe4yc3IwnjwULBv1tk3uTr35MKqIiDrIuWX9+7w/EJ9hQPjSZjtiOABP/C63F2c43zvN1XUqJ1Yvrh1dqpI5nIxexMLFRgJrawALHNsc5h5uLBL6q9rgyJsUNWXMgl9nOUGDDh+Jg6yM6SUVd44WuDTbgxtIwc5Uq6fE+HFowM35Xt0u9UEOBqOzpyBNYjNEsCMfPxkbzYGcLW9Y2C28ygrizRn+rhXFG3sQxraiCPVAL+d9NMGV3TaFXScEoCuXKRyHab1ch9g613FBL16k2rxZWoHfwI07bAUrpcv5upR5RMeeYrNmE7mrQ5WHemACd6QJtknPxOfxgAKOck486b29dgAt5C6P81T0mL0+52bu0LkbRYxVEExdeDurvhue2ZzGG11PbEJdLpVbgcPJr7dUkPPG2nPW4JLqqnzlulqu7Me07mgJuVK1OnXUKKziQ8BcDqx6lsdBg/CuKvL4PedFkSh32W+7VIQq7e9sVEV+Q5CngWfr2sYFh3cu3kw= lagunov@unit-1381
|
||||
1
src/itest/resources/keyfiles/id_rsa2.pub
Normal file
1
src/itest/resources/keyfiles/id_rsa2.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQxnbF27Lzr9VvNlac6fNKpRV5TtqPPBzcsX29H1lu59l0IHylhHOlDgH2/e6YNnedf2o8zh/aIxUN+LPbjVLCa9XmztaI4c+HLeKPmZSlFc6fdrxvJUiN2tppVrAvrjan3xwsoH1YoSMpoPDrfdoPvHNIiiapBuhVmswc2O8hqfJ3dTqP2aejJ1cPVwi5x5nHhL8C/whCu70CJlgnusB9nDPeKn1hZyBPTIiiENsFJft+xfbsoXq3Eh9FckImDRA7SQ5igxCYtgm2RUCnCLF1Xb1XHaDk8EpcNDXYXAZdOdkbfq9hhnueDXY9X7EzILXE8t1efOs1H+U7gEa1PXgZglvBUzeCMEOtx5q9prJVbhKMWupHlcMuOV0oB8q0IcRq8u/XoN/V4DCc+UYDCY4OVGpMe8vmjEFCJu82MPNWm9+oSwVGo/93gBtiMBGwoKR67c/NQj6urkC3BnnZcqAZ2DXfBRGgCQiw1eicqj3Zq8ueiaqyR1Nil8EQ5QxJk70= lagunov@unit-1381
|
||||
38
src/itest/resources/users_rsa_ca
Normal file
38
src/itest/resources/users_rsa_ca
Normal file
@@ -0,0 +1,38 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
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
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
31
src/main/java/com/hierynomus/sshj/common/KeyAlgorithm.java
Normal file
31
src/main/java/com/hierynomus/sshj/common/KeyAlgorithm.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.common;
|
||||
|
||||
public class KeyAlgorithm {
|
||||
|
||||
public static final String RSA = "RSA";
|
||||
public static final String DSA = "DSA";
|
||||
|
||||
/** Elliptic curve signature key algorithm for use with BouncyCastle **/
|
||||
public static final String ECDSA = "ECDSA";
|
||||
|
||||
/** General elliptic curve algorithm identifier for use with BouncyCastle **/
|
||||
public static final String EC_BC = "EC";
|
||||
|
||||
/** General elliptic curve algorithm identifier for use with the Android Keystore **/
|
||||
public static final String EC_KEYSTORE = "EC";
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.common;
|
||||
|
||||
import org.bouncycastle.openssl.EncryptionException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when a key file could not be decrypted correctly, e.g. if its checkInts differed in the case of an OpenSSH
|
||||
* key file.
|
||||
*/
|
||||
public class KeyDecryptionFailedException extends IOException {
|
||||
|
||||
public static final String MESSAGE = "Decryption of the key failed. A supplied passphrase may be incorrect.";
|
||||
|
||||
public KeyDecryptionFailedException() {
|
||||
super(MESSAGE);
|
||||
}
|
||||
|
||||
public KeyDecryptionFailedException(EncryptionException cause) {
|
||||
super(MESSAGE, cause);
|
||||
}
|
||||
|
||||
}
|
||||
60
src/main/java/com/hierynomus/sshj/key/BaseKeyAlgorithm.java
Normal file
60
src/main/java/com/hierynomus/sshj/key/BaseKeyAlgorithm.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.key;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.signature.Signature;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class BaseKeyAlgorithm implements KeyAlgorithm {
|
||||
private final String keyAlgorithm;
|
||||
private final Factory.Named<Signature> signature;
|
||||
private final KeyType keyFormat;
|
||||
|
||||
public BaseKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
|
||||
this.keyAlgorithm = keyAlgorithm;
|
||||
this.signature = signature;
|
||||
this.keyFormat = keyFormat;
|
||||
}
|
||||
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
keyFormat.putPubKeyIntoBuffer(pk, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException {
|
||||
return keyFormat.readPubKeyFromBuffer(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyAlgorithm() {
|
||||
return keyAlgorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyType getKeyFormat() {
|
||||
return keyFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature newSignature() {
|
||||
return this.signature.create();
|
||||
}
|
||||
}
|
||||
45
src/main/java/com/hierynomus/sshj/key/KeyAlgorithm.java
Normal file
45
src/main/java/com/hierynomus/sshj/key/KeyAlgorithm.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.key;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.signature.Signature;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* In [RFC4252], the concept "public key algorithm" is used to establish
|
||||
* a relationship between one algorithm name, and:
|
||||
* <p>
|
||||
* A. procedures used to generate and validate a private/public
|
||||
* keypair;
|
||||
* B. a format used to encode a public key; and
|
||||
* C. procedures used to calculate, encode, and verify a signature.
|
||||
*/
|
||||
public interface KeyAlgorithm {
|
||||
|
||||
PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException;
|
||||
|
||||
void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||
|
||||
String getKeyAlgorithm();
|
||||
|
||||
KeyType getKeyFormat();
|
||||
|
||||
Signature newSignature();
|
||||
}
|
||||
65
src/main/java/com/hierynomus/sshj/key/KeyAlgorithms.java
Normal file
65
src/main/java/com/hierynomus/sshj/key/KeyAlgorithms.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.key;
|
||||
|
||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.signature.Signature;
|
||||
import net.schmizz.sshj.signature.SignatureDSA;
|
||||
import net.schmizz.sshj.signature.SignatureECDSA;
|
||||
import net.schmizz.sshj.signature.SignatureRSA;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class KeyAlgorithms {
|
||||
|
||||
public static List<String> SSH_RSA_SHA2_ALGORITHMS = Arrays.asList("rsa-sha2-512", "rsa-sha2-256");
|
||||
|
||||
public static Factory SSHRSA() { return new Factory("ssh-rsa", new SignatureRSA.FactorySSHRSA(), KeyType.RSA); }
|
||||
public static Factory SSHRSACertV01() { return new Factory("ssh-rsa-cert-v01@openssh.com", new SignatureRSA.FactoryCERT(), KeyType.RSA_CERT); }
|
||||
public static Factory RSASHA256() { return new Factory("rsa-sha2-256", new SignatureRSA.FactoryRSASHA256(), KeyType.RSA); }
|
||||
public static Factory RSASHA512() { return new Factory("rsa-sha2-512", new SignatureRSA.FactoryRSASHA512(), KeyType.RSA); }
|
||||
public static Factory SSHDSA() { return new Factory(KeyType.DSA.toString(), new SignatureDSA.Factory(), KeyType.DSA); }
|
||||
public static Factory SSHDSSCertV01() { return new Factory(KeyType.DSA_CERT.toString(), new SignatureDSA.Factory(), KeyType.DSA_CERT); }
|
||||
public static Factory ECDSASHANistp256() { return new Factory(KeyType.ECDSA256.toString(), new SignatureECDSA.Factory256(), KeyType.ECDSA256); }
|
||||
public static Factory ECDSASHANistp384() { return new Factory(KeyType.ECDSA384.toString(), new SignatureECDSA.Factory384(), KeyType.ECDSA384); }
|
||||
public static Factory ECDSASHANistp521() { return new Factory(KeyType.ECDSA521.toString(), new SignatureECDSA.Factory521(), KeyType.ECDSA521); }
|
||||
public static Factory EdDSA25519() { return new Factory(KeyType.ED25519.toString(), new SignatureEdDSA.Factory(), KeyType.ED25519); }
|
||||
|
||||
public static class Factory implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
|
||||
|
||||
private final String algorithmName;
|
||||
private final Named<Signature> signatureFactory;
|
||||
private final KeyType keyType;
|
||||
|
||||
public Factory(String algorithmName, Named<Signature> signatureFactory, KeyType keyType) {
|
||||
this.algorithmName = algorithmName;
|
||||
this.signatureFactory = signatureFactory;
|
||||
this.keyType = keyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return algorithmName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyAlgorithm create() {
|
||||
return new BaseKeyAlgorithm(algorithmName, signatureFactory, keyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public class SignatureEdDSA extends AbstractSignature {
|
||||
}
|
||||
|
||||
SignatureEdDSA() {
|
||||
super(getEngine());
|
||||
super(getEngine(), KeyType.ED25519.toString());
|
||||
}
|
||||
|
||||
private static EdDSAEngine getEngine() {
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.math.BigInteger;
|
||||
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
||||
|
||||
/**
|
||||
* Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups
|
||||
* Factory methods for Diffie Hellman KEX algorithms based on MODP groups / Oakley Groups
|
||||
*
|
||||
* - https://tools.ietf.org/html/rfc4253
|
||||
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.transport.kex;
|
||||
|
||||
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||
|
||||
/**
|
||||
* Stub kex algorithm factory that indicates support for SSH2_MSG_EXT_INFO.
|
||||
* Some servers will not send `rsa-sha2-*` signatures if the client doesn't indicate support.
|
||||
*
|
||||
* Note: Since the server sends `ext-info-s` to indicate support, this fake kex algorithm is never negotiated.
|
||||
*/
|
||||
public class ExtInfoClientFactory implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ext-info-c";
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyExchange create() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.hierynomus.sshj.userauth.keyprovider;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
|
||||
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
@@ -111,8 +113,16 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
||||
return readUnencrypted(privateKeyBuffer, publicKey);
|
||||
} else {
|
||||
logger.info("Keypair is encrypted with: " + cipherName + ", " + kdfName + ", " + Arrays.toString(kdfOptions));
|
||||
PlainBuffer decrypted = decryptBuffer(privateKeyBuffer, cipherName, kdfName, kdfOptions);
|
||||
return readUnencrypted(decrypted, publicKey);
|
||||
while (true) {
|
||||
PlainBuffer decryptionBuffer = new PlainBuffer(privateKeyBuffer);
|
||||
PlainBuffer decrypted = decryptBuffer(decryptionBuffer, cipherName, kdfName, kdfOptions);
|
||||
try {
|
||||
return readUnencrypted(decrypted, publicKey);
|
||||
} catch (KeyDecryptionFailedException e) {
|
||||
if (pwdf == null || !pwdf.shouldRetry(resource))
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
// throw new IOException("Cannot read encrypted keypair with " + cipherName + " yet.");
|
||||
}
|
||||
}
|
||||
@@ -133,9 +143,12 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
||||
CharBuffer charBuffer = CharBuffer.wrap(pwdf.reqPassword(null));
|
||||
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
|
||||
passphrase = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
|
||||
Arrays.fill(charBuffer.array(), '\u0000');
|
||||
Arrays.fill(byteBuffer.array(), (byte) 0);
|
||||
}
|
||||
byte[] keyiv = new byte[48];
|
||||
new BCrypt().pbkdf(passphrase, opts.readBytes(), opts.readUInt32AsInt(), keyiv);
|
||||
Arrays.fill(passphrase, (byte) 0);
|
||||
byte[] key = Arrays.copyOfRange(keyiv, 0, 32);
|
||||
byte[] iv = Arrays.copyOfRange(keyiv, 32, 48);
|
||||
cipher.init(Cipher.Mode.Decrypt, key, iv);
|
||||
@@ -184,7 +197,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
||||
int checkInt1 = keyBuffer.readUInt32AsInt(); // uint32 checkint1
|
||||
int checkInt2 = keyBuffer.readUInt32AsInt(); // uint32 checkint2
|
||||
if (checkInt1 != checkInt2) {
|
||||
throw new IOException("The checkInts differed, the key was not correctly decoded.");
|
||||
throw new KeyDecryptionFailedException();
|
||||
}
|
||||
// The private key section contains both the public key and the private key
|
||||
String keyType = keyBuffer.readString(); // string keytype
|
||||
@@ -207,7 +220,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
||||
keyBuffer.readMPInt(); // iqmp (q^-1 mod p)
|
||||
keyBuffer.readMPInt(); // p (Prime 1)
|
||||
keyBuffer.readMPInt(); // q (Prime 2)
|
||||
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(n, d)));
|
||||
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(new RSAPrivateKeySpec(n, d)));
|
||||
break;
|
||||
case ECDSA256:
|
||||
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-256"));
|
||||
@@ -239,7 +252,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
|
||||
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
|
||||
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
|
||||
ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec);
|
||||
return SecurityUtils.getKeyFactory("ECDSA").generatePrivate(pks);
|
||||
return SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ package net.schmizz.concurrent;
|
||||
* if (t instanceof SomeException)
|
||||
* return (SomeException) t;
|
||||
* else
|
||||
* return new SomeExcepion(t);
|
||||
* return new SomeException(t);
|
||||
* }
|
||||
* };
|
||||
* </pre>
|
||||
|
||||
@@ -30,6 +30,7 @@ public abstract class KeepAlive extends Thread {
|
||||
this.conn = conn;
|
||||
log = conn.getTransport().getConfig().getLoggerFactory().getLogger(getClass());
|
||||
setName(name);
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
public synchronized int getKeepAliveInterval() {
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
*/
|
||||
package net.schmizz.sshj;
|
||||
|
||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.key.KeyAlgorithms;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
import net.schmizz.sshj.signature.SignatureDSA;
|
||||
import net.schmizz.sshj.signature.SignatureRSA;
|
||||
import net.schmizz.sshj.transport.random.JCERandom;
|
||||
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Registers SpongyCastle as JCE provider.
|
||||
*/
|
||||
@@ -33,11 +34,14 @@ public class AndroidConfig
|
||||
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
|
||||
}
|
||||
|
||||
// don't add ECDSA
|
||||
protected void initSignatureFactories() {
|
||||
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
|
||||
// but add EdDSA
|
||||
new SignatureEdDSA.Factory());
|
||||
|
||||
@Override
|
||||
protected void initKeyAlgorithms() {
|
||||
setKeyAlgorithms(Arrays.<Factory.Named<KeyAlgorithm>>asList(
|
||||
KeyAlgorithms.EdDSA25519(),
|
||||
KeyAlgorithms.SSHRSA(),
|
||||
KeyAlgorithms.SSHDSA()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import net.schmizz.keepalive.KeepAliveProvider;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.LoggerFactory;
|
||||
@@ -77,11 +78,11 @@ public interface Config {
|
||||
Factory<Random> getRandomFactory();
|
||||
|
||||
/**
|
||||
* Retrieve the list of named factories for {@link Signature}
|
||||
* Retrieve the list of named factories for {@link com.hierynomus.sshj.key.KeyAlgorithm}
|
||||
*
|
||||
* @return a list of named {@link Signature} factories
|
||||
* @return a list of named {@link com.hierynomus.sshj.key.KeyAlgorithm} factories
|
||||
*/
|
||||
List<Factory.Named<Signature>> getSignatureFactories();
|
||||
List<Factory.Named<KeyAlgorithm>> getKeyAlgorithms();
|
||||
|
||||
/**
|
||||
* Returns the software version information for identification during SSH connection initialization. For example,
|
||||
@@ -132,11 +133,11 @@ public interface Config {
|
||||
void setRandomFactory(Factory<Random> randomFactory);
|
||||
|
||||
/**
|
||||
* Set the named factories for {@link Signature}.
|
||||
* Set the named factories for {@link KeyAlgorithm}.
|
||||
*
|
||||
* @param signatureFactories a list of named factories
|
||||
* @param keyAlgorithms a list of named factories
|
||||
*/
|
||||
void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories);
|
||||
void setKeyAlgorithms(List<Factory.Named<KeyAlgorithm>> keyAlgorithms);
|
||||
|
||||
/**
|
||||
* Set the software version information for identification during SSH connection initialization. For example, {@code
|
||||
@@ -160,7 +161,7 @@ public interface Config {
|
||||
/**
|
||||
* Gets whether the client should first wait for a received server ident, before sending the client ident.
|
||||
* <p/>
|
||||
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||
* <strong>NB:</strong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||
*
|
||||
* The default value is set to false.
|
||||
*
|
||||
@@ -171,7 +172,7 @@ public interface Config {
|
||||
/**
|
||||
* Sets whether the SSH client should wait for a received server ident, before sending the client ident.
|
||||
* <p/>
|
||||
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||
* <strong>NB:</strong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||
|
||||
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
|
||||
*/
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
package net.schmizz.sshj;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import net.schmizz.keepalive.KeepAliveProvider;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.LoggerFactory;
|
||||
import net.schmizz.sshj.signature.Signature;
|
||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
import net.schmizz.sshj.transport.compression.Compression;
|
||||
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||
@@ -42,7 +42,7 @@ public class ConfigImpl
|
||||
private List<Factory.Named<Cipher>> cipherFactories;
|
||||
private List<Factory.Named<Compression>> compressionFactories;
|
||||
private List<Factory.Named<MAC>> macFactories;
|
||||
private List<Factory.Named<Signature>> signatureFactories;
|
||||
private List<Factory.Named<KeyAlgorithm>> keyAlgorithms;
|
||||
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
|
||||
|
||||
private boolean waitForServerIdentBeforeSendingClientIdent = false;
|
||||
@@ -78,11 +78,6 @@ public class ConfigImpl
|
||||
return randomFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<Signature>> getSignatureFactories() {
|
||||
return signatureFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return version;
|
||||
@@ -138,15 +133,6 @@ public class ConfigImpl
|
||||
this.randomFactory = randomFactory;
|
||||
}
|
||||
|
||||
public void setSignatureFactories(Factory.Named<Signature>... signatureFactories) {
|
||||
setSignatureFactories(Arrays.asList(signatureFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories) {
|
||||
this.signatureFactories = signatureFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
@@ -172,6 +158,16 @@ public class ConfigImpl
|
||||
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<KeyAlgorithm>> getKeyAlgorithms() {
|
||||
return keyAlgorithms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyAlgorithms(List<Factory.Named<KeyAlgorithm>> keyAlgorithms) {
|
||||
this.keyAlgorithms = keyAlgorithms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggerFactory getLoggerFactory() {
|
||||
return loggerFactory;
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package net.schmizz.sshj;
|
||||
|
||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.key.KeyAlgorithms;
|
||||
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
|
||||
import com.hierynomus.sshj.transport.kex.DHGroups;
|
||||
import com.hierynomus.sshj.transport.kex.ExtInfoClientFactory;
|
||||
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
|
||||
import com.hierynomus.sshj.transport.mac.Macs;
|
||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||
@@ -26,10 +28,7 @@ import net.schmizz.keepalive.KeepAliveProvider;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.LoggerFactory;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
import net.schmizz.sshj.signature.SignatureDSA;
|
||||
import net.schmizz.sshj.signature.SignatureECDSA;
|
||||
import net.schmizz.sshj.signature.SignatureRSA;
|
||||
import net.schmizz.sshj.transport.cipher.*;
|
||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
import net.schmizz.sshj.transport.compression.NoneCompression;
|
||||
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
|
||||
import net.schmizz.sshj.transport.kex.DHGexSHA1;
|
||||
@@ -56,7 +55,7 @@ import java.util.*;
|
||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setMACFactories MAC}: {@link net.schmizz.sshj.transport.mac.HMACSHA1}, {@link net.schmizz.sshj.transport.mac.HMACSHA196}, {@link net.schmizz.sshj.transport.mac.HMACMD5}, {@link
|
||||
* net.schmizz.sshj.transport.mac.HMACMD596}</li>
|
||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setCompressionFactories Compression}: {@link net.schmizz.sshj.transport.compression.NoneCompression}</li>
|
||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setSignatureFactories Signature}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
|
||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyAlgorithms KeyAlgorithm}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
|
||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory PRNG}: {@link net.schmizz.sshj.transport.random.BouncyCastleRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
|
||||
* <li>{@link net.schmizz.sshj.ConfigImpl#setFileKeyProviderFactories Key file support}: {@link net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile}*, {@link
|
||||
* net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile}*</li>
|
||||
@@ -76,12 +75,12 @@ public class DefaultConfig
|
||||
setVersion(readVersionFromProperties());
|
||||
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
|
||||
initKeyExchangeFactories(bouncyCastleRegistered);
|
||||
initKeyAlgorithms();
|
||||
initRandomFactory(bouncyCastleRegistered);
|
||||
initFileKeyProviderFactories(bouncyCastleRegistered);
|
||||
initCipherFactories();
|
||||
initCompressionFactories();
|
||||
initMACFactories();
|
||||
initSignatureFactories();
|
||||
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
|
||||
}
|
||||
|
||||
@@ -99,8 +98,8 @@ public class DefaultConfig
|
||||
|
||||
@Override
|
||||
public void setLoggerFactory(LoggerFactory loggerFactory) {
|
||||
super.setLoggerFactory(loggerFactory);
|
||||
log = loggerFactory.getLogger(getClass());
|
||||
super.setLoggerFactory(loggerFactory);
|
||||
log = loggerFactory.getLogger(getClass());
|
||||
}
|
||||
|
||||
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
|
||||
@@ -127,15 +126,29 @@ public class DefaultConfig
|
||||
ExtendedDHGroups.Group16SHA256(),
|
||||
ExtendedDHGroups.Group16SHA384AtSSH(),
|
||||
ExtendedDHGroups.Group16SHA512AtSSH(),
|
||||
ExtendedDHGroups.Group18SHA512AtSSH());
|
||||
ExtendedDHGroups.Group18SHA512AtSSH(),
|
||||
new ExtInfoClientFactory());
|
||||
} else {
|
||||
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
|
||||
}
|
||||
}
|
||||
|
||||
protected void initKeyAlgorithms() {
|
||||
setKeyAlgorithms(Arrays.<Factory.Named<KeyAlgorithm>>asList(
|
||||
KeyAlgorithms.EdDSA25519(),
|
||||
KeyAlgorithms.ECDSASHANistp521(),
|
||||
KeyAlgorithms.ECDSASHANistp384(),
|
||||
KeyAlgorithms.ECDSASHANistp256(),
|
||||
KeyAlgorithms.RSASHA512(),
|
||||
KeyAlgorithms.RSASHA256(),
|
||||
KeyAlgorithms.SSHRSACertV01(),
|
||||
KeyAlgorithms.SSHDSSCertV01(),
|
||||
KeyAlgorithms.SSHRSA(),
|
||||
KeyAlgorithms.SSHDSA()));
|
||||
}
|
||||
|
||||
protected void initRandomFactory(boolean bouncyCastleRegistered) {
|
||||
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered
|
||||
? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
|
||||
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
|
||||
}
|
||||
|
||||
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
|
||||
@@ -207,18 +220,6 @@ public class DefaultConfig
|
||||
log.debug("Available cipher factories: {}", avail);
|
||||
}
|
||||
|
||||
protected void initSignatureFactories() {
|
||||
setSignatureFactories(
|
||||
new SignatureEdDSA.Factory(),
|
||||
new SignatureECDSA.Factory256(),
|
||||
new SignatureECDSA.Factory384(),
|
||||
new SignatureECDSA.Factory521(),
|
||||
new SignatureRSA.Factory(),
|
||||
new SignatureRSA.FactoryCERT(),
|
||||
new SignatureDSA.Factory()
|
||||
);
|
||||
}
|
||||
|
||||
protected void initMACFactories() {
|
||||
setMACFactories(
|
||||
Macs.HMACSHA1(),
|
||||
|
||||
@@ -515,7 +515,7 @@ public class SSHClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for createing a {@link KeyProvider} instance from given location on the file system. Creates a
|
||||
* Utility function for creating a {@link KeyProvider} instance from given location on the file system. Creates a
|
||||
* one-off {@link PasswordFinder} using {@link PasswordUtils#createOneOff(char[])}, and calls {@link
|
||||
* #loadKeys(String, PasswordFinder)}.
|
||||
*
|
||||
@@ -722,6 +722,19 @@ public class SSHClient
|
||||
return new SFTPClient(new SFTPEngine(this).init());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stateful FTP client is required in order to connect to Serv-U FTP servers.
|
||||
* @return Instantiated {@link SFTPClient} implementation.
|
||||
*
|
||||
* @throws IOException if there is an error starting the {@code sftp} subsystem
|
||||
*/
|
||||
public SFTPClient newStatefulSFTPClient()
|
||||
throws IOException {
|
||||
checkConnected();
|
||||
checkAuthenticated();
|
||||
return new StatefulSFTPClient(new SFTPEngine(this).init());
|
||||
}
|
||||
|
||||
/**
|
||||
* Does key re-exchange.
|
||||
*
|
||||
@@ -754,7 +767,7 @@ public class SSHClient
|
||||
|
||||
/**
|
||||
* Adds {@code zlib} compression to preferred compression algorithms. There is no guarantee that it will be
|
||||
* successfully negotiatied.
|
||||
* successfully negotiated.
|
||||
* <p/>
|
||||
* If the client is already connected renegotiation is done; otherwise this method simply returns (and compression
|
||||
* will be negotiated during connection establishment).
|
||||
|
||||
@@ -53,6 +53,10 @@ public abstract class SocketClient {
|
||||
this.defaultPort = defaultPort;
|
||||
}
|
||||
|
||||
protected InetSocketAddress makeInetSocketAddress(String hostname, int port) {
|
||||
return new InetSocketAddress(hostname, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a host via a proxy.
|
||||
* @param hostname The host name to connect to.
|
||||
@@ -83,7 +87,7 @@ public abstract class SocketClient {
|
||||
} else {
|
||||
socket = new Socket(proxy);
|
||||
}
|
||||
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
|
||||
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
|
||||
onConnect();
|
||||
}
|
||||
|
||||
@@ -131,7 +135,7 @@ public abstract class SocketClient {
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
socket = socketFactory.createSocket();
|
||||
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
|
||||
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
|
||||
onConnect();
|
||||
}
|
||||
}
|
||||
@@ -144,7 +148,7 @@ public abstract class SocketClient {
|
||||
this.port = port;
|
||||
socket = socketFactory.createSocket();
|
||||
socket.bind(new InetSocketAddress(localAddr, localPort));
|
||||
socket.connect(new InetSocketAddress(hostname, port), connectTimeout);
|
||||
socket.connect(makeInetSocketAddress(hostname, port), connectTimeout);
|
||||
onConnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ package net.schmizz.sshj.common;
|
||||
* <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li>
|
||||
* <li><em>Throws exceptions instead of returning null values.</em> Because some operations
|
||||
* (especially those that may permit the GZIP option) use IO streams, there
|
||||
* is a possiblity of an java.io.IOException being thrown. After some discussion and
|
||||
* is a possibility of an java.io.IOException being thrown. After some discussion and
|
||||
* thought, I've changed the behavior of the methods to throw java.io.IOExceptions
|
||||
* rather than return null if ever there's an error. I think this is more
|
||||
* appropriate, though it will require some changes to your code. Sorry,
|
||||
@@ -476,7 +476,7 @@ public class Base64
|
||||
* anywhere along their length by specifying
|
||||
* <var>srcOffset</var> and <var>destOffset</var>.
|
||||
* This method does not check to make sure your arrays
|
||||
* are large enough to accomodate <var>srcOffset</var> + 3 for
|
||||
* are large enough to accommodate <var>srcOffset</var> + 3 for
|
||||
* the <var>source</var> array or <var>destOffset</var> + 4 for
|
||||
* the <var>destination</var> array.
|
||||
* The actual number of significant bytes in your array is
|
||||
@@ -1007,7 +1007,7 @@ public class Base64
|
||||
* anywhere along their length by specifying
|
||||
* <var>srcOffset</var> and <var>destOffset</var>.
|
||||
* This method does not check to make sure your arrays
|
||||
* are large enough to accomodate <var>srcOffset</var> + 4 for
|
||||
* are large enough to accommodate <var>srcOffset</var> + 4 for
|
||||
* the <var>source</var> array or <var>destOffset</var> + 3 for
|
||||
* the <var>destination</var> array.
|
||||
* This method returns the actual number of bytes that
|
||||
@@ -1928,7 +1928,7 @@ public class Base64
|
||||
if( suspendEncoding ) {
|
||||
this.out.write( theByte );
|
||||
return;
|
||||
} // end if: supsended
|
||||
} // end if: suspended
|
||||
|
||||
// Encode?
|
||||
if( encode ) {
|
||||
@@ -1983,7 +1983,7 @@ public class Base64
|
||||
if( suspendEncoding ) {
|
||||
this.out.write( theBytes, off, len );
|
||||
return;
|
||||
} // end if: supsended
|
||||
} // end if: suspended
|
||||
|
||||
for( int i = 0; i < len; i++ ) {
|
||||
write( theBytes[ off + i ] );
|
||||
|
||||
@@ -58,7 +58,7 @@ public class Buffer<T extends Buffer<T>> {
|
||||
public static final int MAX_SIZE = (1 << 30);
|
||||
|
||||
/** Maximum size of a uint64 */
|
||||
private static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
|
||||
public static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
|
||||
.shiftLeft(64)
|
||||
.subtract(BigInteger.ONE);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.secg.SecgUtils;
|
||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
@@ -87,7 +88,7 @@ class ECDSAVariationsAdapter {
|
||||
ECPoint p = new ECPoint(bigX, bigY);
|
||||
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
|
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KeyAlgorithm.ECDSA);
|
||||
return keyFactory.generatePublic(publicKeySpec);
|
||||
} catch (Exception ex) {
|
||||
throw new GeneralSecurityException(ex);
|
||||
@@ -103,7 +104,7 @@ class ECDSAVariationsAdapter {
|
||||
}
|
||||
|
||||
static boolean isECKeyWithFieldSize(Key key, int fieldSize) {
|
||||
return "ECDSA".equals(key.getAlgorithm())
|
||||
return (KeyAlgorithm.ECDSA.equals(key.getAlgorithm()) || KeyAlgorithm.EC_KEYSTORE.equals(key.getAlgorithm()))
|
||||
&& fieldSizeFromKey((ECKey) key) == fieldSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.List;
|
||||
public interface Factory<T> {
|
||||
|
||||
/**
|
||||
* Inteface for a named factory. Named factories are simply factories that are identified by a name. Such names are
|
||||
* Interface for a named factory. Named factories are simply factories that are identified by a name. Such names are
|
||||
* used mainly in SSH algorithm negotiation.
|
||||
*
|
||||
* @param <T> type of object created by this factory
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
||||
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
@@ -30,9 +31,7 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.DSAPrivateKey;
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
@@ -53,7 +52,7 @@ public enum KeyType {
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new GeneralSecurityException(be);
|
||||
}
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
|
||||
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||
}
|
||||
|
||||
@@ -66,7 +65,7 @@ public enum KeyType {
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
|
||||
return KeyAlgorithm.RSA.equals(key.getAlgorithm());
|
||||
}
|
||||
},
|
||||
|
||||
@@ -84,7 +83,7 @@ public enum KeyType {
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new GeneralSecurityException(be);
|
||||
}
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.DSA);
|
||||
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
|
||||
}
|
||||
|
||||
@@ -99,7 +98,7 @@ public enum KeyType {
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return (key instanceof DSAPublicKey || key instanceof DSAPrivateKey);
|
||||
return KeyAlgorithm.DSA.equals(key.getAlgorithm());
|
||||
}
|
||||
|
||||
},
|
||||
@@ -221,6 +220,11 @@ public enum KeyType {
|
||||
protected boolean isMyType(Key key) {
|
||||
return CertUtils.isCertificateOfType(key, RSA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyType getParent() {
|
||||
return RSA;
|
||||
}
|
||||
},
|
||||
|
||||
/** Signed dsa certificate */
|
||||
@@ -240,6 +244,11 @@ public enum KeyType {
|
||||
protected boolean isMyType(Key key) {
|
||||
return CertUtils.isCertificateOfType(key, DSA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyType getParent() {
|
||||
return KeyType.DSA;
|
||||
}
|
||||
},
|
||||
|
||||
/** Unrecognized */
|
||||
@@ -284,10 +293,24 @@ public enum KeyType {
|
||||
protected abstract boolean isMyType(Key key);
|
||||
|
||||
public static KeyType fromKey(Key key) {
|
||||
KeyType result = UNKNOWN;
|
||||
for (KeyType kt : values())
|
||||
if (kt.isMyType((key)))
|
||||
return kt;
|
||||
return UNKNOWN;
|
||||
if (kt.isMyType((key)) && (result == UNKNOWN || kt.isSubType(result)))
|
||||
result = kt;
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isSubType(KeyType keyType) {
|
||||
for (KeyType node = this; node != null; node = node.getParent()) {
|
||||
if (keyType == node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public KeyType getParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static KeyType fromString(String sType) {
|
||||
@@ -373,8 +396,18 @@ public enum KeyType {
|
||||
}
|
||||
}
|
||||
|
||||
private static long epochFromDate(Date date) {
|
||||
return date.getTime() / 1000;
|
||||
private static BigInteger epochFromDate(Date date) {
|
||||
long time = date.getTime() / 1000;
|
||||
if (time >= Long.MAX_VALUE / 1000) {
|
||||
// Dealing with the signed longs in Java. Since the protocol requires a unix timestamp in milliseconds,
|
||||
// and since Java can store numbers not bigger than 2^63-1 as `long`, we can't distinguish dates
|
||||
// after `new Date(Long.MAX_VALUE / 1000)`. It's unlikely that someone uses certificate valid until
|
||||
// the 10 January of 294247 year. Supposing that such dates are unlimited.
|
||||
// OpenSSH expects to see 0xFF_FF_FF_FF_FF_FF_FF_FF in such cases.
|
||||
return Buffer.MAX_UINT64_VALUE;
|
||||
} else {
|
||||
return BigInteger.valueOf(time);
|
||||
}
|
||||
}
|
||||
|
||||
private static String unpackString(byte[] packedString) throws BufferException {
|
||||
|
||||
@@ -25,6 +25,7 @@ public enum Message {
|
||||
DEBUG(4),
|
||||
SERVICE_REQUEST(5),
|
||||
SERVICE_ACCEPT(6),
|
||||
EXT_INFO(7),
|
||||
KEXINIT(20),
|
||||
NEWKEYS(21),
|
||||
|
||||
|
||||
@@ -150,7 +150,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
|
||||
}
|
||||
|
||||
private void checkClose() throws SSHException {
|
||||
if (closed) {
|
||||
// Check whether either the Stream is closed, or the underlying channel is closed
|
||||
if (closed || !chan.isOpen()) {
|
||||
if (error != null)
|
||||
throw error;
|
||||
else
|
||||
@@ -160,7 +161,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!closed) {
|
||||
// Not closed yet, and underlying channel is open to flush the data to.
|
||||
if (!closed && chan.isOpen()) {
|
||||
try {
|
||||
buffer.flush(false);
|
||||
// trans.write(new SSHPacket(Message.CHANNEL_EOF).putUInt32(chan.getRecipient()));
|
||||
|
||||
@@ -28,7 +28,7 @@ import net.schmizz.sshj.transport.TransportException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Base class for direct channels whose open is initated by the client. */
|
||||
/** Base class for direct channels whose open is initiated by the client. */
|
||||
public abstract class AbstractDirectChannel
|
||||
extends AbstractChannel
|
||||
implements Channel.Direct {
|
||||
|
||||
@@ -20,7 +20,7 @@ import net.schmizz.sshj.connection.Connection;
|
||||
/** A channel for creating a direct TCP/IP connection from the server to a remote address. */
|
||||
public class DirectConnection extends DirectTCPIPChannel {
|
||||
public static final String LOCALHOST = "localhost";
|
||||
public static final int LOCALPORT = 65536;
|
||||
public static final int LOCALPORT = 65535;
|
||||
|
||||
public DirectConnection(Connection conn, String remoteHost, int remotePort) {
|
||||
super(conn, new Parameters(LOCALHOST, LOCALPORT, remoteHost, remotePort));
|
||||
|
||||
@@ -191,7 +191,7 @@ public interface Session
|
||||
TransportException;
|
||||
|
||||
/**
|
||||
* Set an enviornment variable.
|
||||
* Set an environment variable.
|
||||
*
|
||||
* @param name name of the variable
|
||||
* @param value value to set
|
||||
|
||||
@@ -28,7 +28,7 @@ public interface ForwardedChannelOpener {
|
||||
/**
|
||||
* Delegates a {@code SSH_MSG_CHANNEL_OPEN} request for the channel type claimed by this opener.
|
||||
*
|
||||
* @param buf {@link SSHPacket} containg the request except for the message identifier and channel type field
|
||||
* @param buf {@link SSHPacket} containing the request except for the message identifier and channel type field
|
||||
*/
|
||||
void handleOpen(SSHPacket buf)
|
||||
throws ConnectionException, TransportException;
|
||||
|
||||
@@ -42,6 +42,7 @@ public class PacketReader extends Thread {
|
||||
log = engine.getLoggerFactory().getLogger(getClass());
|
||||
this.in = engine.getSubsystem().getInputStream();
|
||||
setName("sftp reader");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
private void readIntoBuffer(byte[] buf, int off, int len)
|
||||
|
||||
@@ -108,6 +108,14 @@ public class SFTPEngine
|
||||
return operativeVersion;
|
||||
}
|
||||
|
||||
public boolean supportsServerExtension(final String extension, final String domain) {
|
||||
return serverExtensions.containsKey(extension + "@" + domain);
|
||||
}
|
||||
|
||||
public String getServerExtensionData(final String extension, final String domain) {
|
||||
return serverExtensions.get(extension + "@" + domain);
|
||||
}
|
||||
|
||||
public Request newExtendedRequest(String reqName) {
|
||||
return newRequest(PacketType.EXTENDED).putString(reqName);
|
||||
}
|
||||
|
||||
@@ -29,18 +29,26 @@ public abstract class AbstractSignature
|
||||
|
||||
@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
||||
protected final java.security.Signature signature;
|
||||
private final String signatureName;
|
||||
|
||||
protected AbstractSignature(String algorithm) {
|
||||
protected AbstractSignature(String algorithm, String signatureName) {
|
||||
try {
|
||||
this.signature = SecurityUtils.getSignature(algorithm);
|
||||
this.signatureName = signatureName;
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected AbstractSignature(@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
|
||||
java.security.Signature signatureEngine) {
|
||||
java.security.Signature signatureEngine, String signatureName) {
|
||||
this.signature = signatureEngine;
|
||||
this.signatureName = signatureName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSignatureName() {
|
||||
return signatureName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,8 @@ import java.security.PublicKey;
|
||||
/** Signature interface for SSH used to sign or verify data. Usually wraps a {@code javax.crypto.Signature} object. */
|
||||
public interface Signature {
|
||||
|
||||
String getSignatureName();
|
||||
|
||||
/**
|
||||
* Initialize this signature with the given public key for signature verification.
|
||||
*
|
||||
|
||||
@@ -15,15 +15,20 @@
|
||||
*/
|
||||
package net.schmizz.sshj.signature;
|
||||
|
||||
import com.hierynomus.asn1.encodingrules.der.DEREncoder;
|
||||
import com.hierynomus.asn1.types.ASN1Object;
|
||||
import com.hierynomus.asn1.types.constructed.ASN1Sequence;
|
||||
import com.hierynomus.asn1.types.primitive.ASN1Integer;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import org.bouncycastle.asn1.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DSA {@link Signature}
|
||||
@@ -50,7 +55,7 @@ public class SignatureDSA
|
||||
}
|
||||
|
||||
public SignatureDSA() {
|
||||
super("SHA1withDSA");
|
||||
super("SHA1withDSA", KeyType.DSA.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,8 +77,8 @@ public class SignatureDSA
|
||||
|
||||
// result must be 40 bytes, but length of r and s may not be 20 bytes
|
||||
|
||||
int r_copylen = (r.length < 20) ? r.length : 20;
|
||||
int s_copylen = (s.length < 20) ? s.length : 20;
|
||||
int r_copylen = Math.min(r.length, 20);
|
||||
int s_copylen = Math.min(s.length, 20);
|
||||
|
||||
System.arraycopy(r, r.length - r_copylen, result, 20 - r_copylen, r_copylen);
|
||||
System.arraycopy(s, s.length - s_copylen, result, 40 - s_copylen, s_copylen);
|
||||
@@ -97,18 +102,19 @@ public class SignatureDSA
|
||||
* Encodes the signature as a DER sequence (ASN.1 format).
|
||||
*/
|
||||
private byte[] asnEncode(byte[] sigBlob) throws IOException {
|
||||
byte[] r = new BigInteger(1, Arrays.copyOfRange(sigBlob, 0, 20)).toByteArray();
|
||||
byte[] s = new BigInteger(1, Arrays.copyOfRange(sigBlob, 20, 40)).toByteArray();
|
||||
BigInteger r = new BigInteger(1, Arrays.copyOfRange(sigBlob, 0, 20));
|
||||
BigInteger s = new BigInteger(1, Arrays.copyOfRange(sigBlob, 20, 40));
|
||||
|
||||
ASN1EncodableVector vector = new ASN1EncodableVector();
|
||||
vector.add(new ASN1Integer(r));
|
||||
List<ASN1Object> vector = new ArrayList<ASN1Object>();
|
||||
vector.add(new com.hierynomus.asn1.types.primitive.ASN1Integer(r));
|
||||
vector.add(new ASN1Integer(s));
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
|
||||
com.hierynomus.asn1.ASN1OutputStream asn1OutputStream = new com.hierynomus.asn1.ASN1OutputStream(new DEREncoder(), baos);
|
||||
|
||||
asn1OutputStream.writeObject(new ASN1Sequence(vector));
|
||||
asn1OutputStream.flush();
|
||||
|
||||
asnOS.writeObject(new DERSequence(vector));
|
||||
asnOS.flush();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
@@ -15,18 +15,22 @@
|
||||
*/
|
||||
package net.schmizz.sshj.signature;
|
||||
|
||||
import com.hierynomus.asn1.encodingrules.der.DERDecoder;
|
||||
import com.hierynomus.asn1.encodingrules.der.DEREncoder;
|
||||
import com.hierynomus.asn1.types.ASN1Object;
|
||||
import com.hierynomus.asn1.types.constructed.ASN1Sequence;
|
||||
import com.hierynomus.asn1.types.primitive.ASN1Integer;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import org.bouncycastle.asn1.ASN1EncodableVector;
|
||||
import org.bouncycastle.asn1.ASN1Integer;
|
||||
import org.bouncycastle.asn1.ASN1OutputStream;
|
||||
import org.bouncycastle.asn1.DERSequence;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** ECDSA {@link Signature} */
|
||||
public class SignatureECDSA extends AbstractSignature {
|
||||
@@ -79,28 +83,22 @@ public class SignatureECDSA extends AbstractSignature {
|
||||
private String keyTypeName;
|
||||
|
||||
public SignatureECDSA(String algorithm, String keyTypeName) {
|
||||
super(algorithm);
|
||||
super(algorithm, keyTypeName);
|
||||
this.keyTypeName = keyTypeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encode(byte[] sig) {
|
||||
int rIndex = 3;
|
||||
int rLen = sig[rIndex++] & 0xff;
|
||||
byte[] r = new byte[rLen];
|
||||
System.arraycopy(sig, rIndex, r, 0, r.length);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(sig);
|
||||
com.hierynomus.asn1.ASN1InputStream asn1InputStream = new com.hierynomus.asn1.ASN1InputStream(new DERDecoder(), bais);
|
||||
|
||||
int sIndex = rIndex + rLen + 1;
|
||||
int sLen = sig[sIndex++] & 0xff;
|
||||
byte[] s = new byte[sLen];
|
||||
System.arraycopy(sig, sIndex, s, 0, s.length);
|
||||
|
||||
System.arraycopy(sig, 4, r, 0, rLen);
|
||||
System.arraycopy(sig, 6 + rLen, s, 0, sLen);
|
||||
ASN1Sequence sequence = asn1InputStream.readObject();
|
||||
ASN1Integer r = (ASN1Integer) sequence.get(0);
|
||||
ASN1Integer s = (ASN1Integer) sequence.get(1);
|
||||
|
||||
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||
buf.putMPInt(new BigInteger(r));
|
||||
buf.putMPInt(new BigInteger(s));
|
||||
buf.putMPInt(r.getValue());
|
||||
buf.putMPInt(s.getValue());
|
||||
|
||||
return buf.getCompactData();
|
||||
}
|
||||
@@ -122,18 +120,18 @@ public class SignatureECDSA extends AbstractSignature {
|
||||
*/
|
||||
private byte[] asnEncode(byte[] sigBlob) throws IOException {
|
||||
Buffer.PlainBuffer sigbuf = new Buffer.PlainBuffer(sigBlob);
|
||||
byte[] r = sigbuf.readBytes();
|
||||
byte[] s = sigbuf.readBytes();
|
||||
BigInteger r = sigbuf.readMPInt();
|
||||
BigInteger s = sigbuf.readMPInt();
|
||||
|
||||
ASN1EncodableVector vector = new ASN1EncodableVector();
|
||||
List<ASN1Object> vector = new ArrayList<ASN1Object>();
|
||||
vector.add(new ASN1Integer(r));
|
||||
vector.add(new ASN1Integer(s));
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ASN1OutputStream asnOS = new ASN1OutputStream(baos);
|
||||
com.hierynomus.asn1.ASN1OutputStream asn1OutputStream = new com.hierynomus.asn1.ASN1OutputStream(new DEREncoder(), baos);
|
||||
|
||||
asnOS.writeObject(new DERSequence(vector));
|
||||
asnOS.flush();
|
||||
asn1OutputStream.writeObject(new ASN1Sequence(vector));
|
||||
asn1OutputStream.flush();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
@@ -22,26 +22,53 @@ import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Date;
|
||||
|
||||
/** RSA {@link Signature} */
|
||||
public class SignatureRSA
|
||||
extends AbstractSignature {
|
||||
|
||||
/** A named factory for RSA {@link Signature} */
|
||||
public static class Factory
|
||||
public static class FactorySSHRSA
|
||||
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureRSA(KeyType.RSA.toString());
|
||||
return new SignatureRSA("SHA1withRSA", KeyType.RSA, KeyType.RSA.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return KeyType.RSA.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** A named factory for RSA {@link Signature} */
|
||||
public static class FactoryRSASHA256
|
||||
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureRSA("SHA256withRSA", KeyType.RSA, "rsa-sha2-256");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "rsa-sha2-256";
|
||||
}
|
||||
}
|
||||
/** A named factory for RSA {@link Signature} */
|
||||
public static class FactoryRSASHA512
|
||||
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureRSA("SHA512withRSA", KeyType.RSA, "rsa-sha2-512");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "rsa-sha2-512";
|
||||
}
|
||||
}
|
||||
|
||||
/** A named factory for RSA {@link Signature} */
|
||||
@@ -50,7 +77,7 @@ public class SignatureRSA
|
||||
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureRSA(KeyType.RSA_CERT.toString());
|
||||
return new SignatureRSA("SHA1withRSA", KeyType.RSA_CERT, KeyType.RSA.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,19 +87,19 @@ public class SignatureRSA
|
||||
|
||||
}
|
||||
|
||||
private String keyTypeName;
|
||||
private KeyType keyType;
|
||||
|
||||
|
||||
public SignatureRSA(String keyTypeName) {
|
||||
super("SHA1withRSA");
|
||||
this.keyTypeName = keyTypeName;
|
||||
public SignatureRSA(String algorithm, KeyType keyType, String name) {
|
||||
super(algorithm, name);
|
||||
this.keyType = keyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void initVerify(PublicKey publicKey) {
|
||||
try {
|
||||
if (this.keyTypeName.equals(KeyType.RSA_CERT.toString()) && publicKey instanceof Certificate) {
|
||||
if (this.keyType.equals(KeyType.RSA_CERT) && publicKey instanceof Certificate) {
|
||||
signature.initVerify(((Certificate<PublicKey>) publicKey).getKey());
|
||||
} else {
|
||||
signature.initVerify(publicKey);
|
||||
@@ -89,7 +116,7 @@ public class SignatureRSA
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] sig) {
|
||||
sig = extractSig(sig, KeyType.RSA.toString());
|
||||
sig = extractSig(sig, getSignatureName());
|
||||
try {
|
||||
return signature.verify(sig);
|
||||
} catch (SignatureException e) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.sshj.common.*;
|
||||
@@ -30,9 +31,7 @@ import org.slf4j.Logger;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -94,7 +93,7 @@ final class KeyExchanger
|
||||
* Add a callback for host key verification.
|
||||
* <p/>
|
||||
* Any of the {@link HostKeyVerifier} implementations added this way can deem a host key to be acceptable, allowing
|
||||
* key exchange to successfuly complete. Otherwise, a {@link TransportException} will result during key exchange.
|
||||
* key exchange to successfully complete. Otherwise, a {@link TransportException} will result during key exchange.
|
||||
*
|
||||
* @param hkv object whose {@link HostKeyVerifier#verify} method will be invoked
|
||||
*/
|
||||
@@ -232,6 +231,10 @@ final class KeyExchanger
|
||||
}
|
||||
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
|
||||
negotiatedAlgs.getKeyExchangeAlgorithm());
|
||||
transport.setHostKeyAlgorithm(Factory.Named.Util.create(transport.getConfig().getKeyAlgorithms(),
|
||||
negotiatedAlgs.getSignatureAlgorithm()));
|
||||
transport.setRSASHA2Support(negotiatedAlgs.getRSASHA2Support());
|
||||
|
||||
try {
|
||||
kex.init(transport,
|
||||
transport.getServerID(), transport.getClientID(),
|
||||
|
||||
@@ -26,8 +26,10 @@ public final class NegotiatedAlgorithms {
|
||||
private final String c2sComp;
|
||||
private final String s2cComp;
|
||||
|
||||
private final boolean rsaSHA2Support;
|
||||
|
||||
NegotiatedAlgorithms(String kex, String sig, String c2sCipher, String s2cCipher, String c2sMAC, String s2cMAC,
|
||||
String c2sComp, String s2cComp) {
|
||||
String c2sComp, String s2cComp, boolean rsaSHA2Support) {
|
||||
this.kex = kex;
|
||||
this.sig = sig;
|
||||
this.c2sCipher = c2sCipher;
|
||||
@@ -36,6 +38,7 @@ public final class NegotiatedAlgorithms {
|
||||
this.s2cMAC = s2cMAC;
|
||||
this.c2sComp = c2sComp;
|
||||
this.s2cComp = s2cComp;
|
||||
this.rsaSHA2Support = rsaSHA2Support;
|
||||
}
|
||||
|
||||
public String getKeyExchangeAlgorithm() {
|
||||
@@ -70,6 +73,10 @@ public final class NegotiatedAlgorithms {
|
||||
return s2cComp;
|
||||
}
|
||||
|
||||
public boolean getRSASHA2Support() {
|
||||
return rsaSHA2Support;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ("[ " +
|
||||
@@ -80,7 +87,8 @@ public final class NegotiatedAlgorithms {
|
||||
"c2sMAC=" + c2sMAC + "; " +
|
||||
"s2cMAC=" + s2cMAC + "; " +
|
||||
"c2sComp=" + c2sComp + "; " +
|
||||
"s2cComp=" + s2cComp +
|
||||
"s2cComp=" + s2cComp + "; " +
|
||||
"rsaSHA2Support=" + rsaSHA2Support +
|
||||
" ]");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithms;
|
||||
import net.schmizz.sshj.Config;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.Message;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -38,7 +40,7 @@ class Proposal {
|
||||
|
||||
public Proposal(Config config) {
|
||||
kex = Factory.Named.Util.getNames(config.getKeyExchangeFactories());
|
||||
sig = Factory.Named.Util.getNames(config.getSignatureFactories());
|
||||
sig = Factory.Named.Util.getNames(config.getKeyAlgorithms());
|
||||
c2sCipher = s2cCipher = Factory.Named.Util.getNames(config.getCipherFactories());
|
||||
c2sMAC = s2cMAC = Factory.Named.Util.getNames(config.getMACFactories());
|
||||
c2sComp = s2cComp = Factory.Named.Util.getNames(config.getCompressionFactories());
|
||||
@@ -90,7 +92,7 @@ class Proposal {
|
||||
return kex;
|
||||
}
|
||||
|
||||
public List<String> getSignatureAlgorithms() {
|
||||
public List<String> getHostKeyAlgorithms() {
|
||||
return sig;
|
||||
}
|
||||
|
||||
@@ -126,31 +128,49 @@ class Proposal {
|
||||
throws TransportException {
|
||||
return new NegotiatedAlgorithms(
|
||||
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
|
||||
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
|
||||
firstMatch(this.getHostKeyAlgorithms(), other.getHostKeyAlgorithms()),
|
||||
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
|
||||
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
|
||||
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
|
||||
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()),
|
||||
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()),
|
||||
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms())
|
||||
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms()),
|
||||
other.getHostKeyAlgorithms().containsAll(KeyAlgorithms.SSH_RSA_SHA2_ALGORITHMS)
|
||||
);
|
||||
}
|
||||
|
||||
private static String firstMatch(List<String> a, List<String> b)
|
||||
throws TransportException {
|
||||
for (String aa : a)
|
||||
for (String bb : b)
|
||||
if (aa.equals(bb))
|
||||
return aa;
|
||||
for (String aa : a) {
|
||||
if (b.contains(aa)) {
|
||||
return aa;
|
||||
}
|
||||
}
|
||||
throw new TransportException("Unable to reach a settlement: " + a + " and " + b);
|
||||
}
|
||||
|
||||
private static List<String> allMatch(List<String> a, List<String> b) throws TransportException {
|
||||
List<String> res = new ArrayList<String>();
|
||||
for (String aa : a) {
|
||||
if (b.contains(aa)) {
|
||||
res.add(aa);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.isEmpty()) {
|
||||
throw new TransportException("Unable to reach a settlement: " + a + " and " + b);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static String toCommaString(List<String> sl) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i = 0;
|
||||
for (String s : sl) {
|
||||
if (i++ != 0)
|
||||
if (i++ != 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
@@ -30,6 +30,7 @@ public final class Reader
|
||||
this.trans = trans;
|
||||
log = trans.getConfig().getLoggerFactory().getLogger(getClass());
|
||||
setName("reader");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import net.schmizz.sshj.Config;
|
||||
import net.schmizz.sshj.Service;
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.SSHPacketHandler;
|
||||
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
||||
@@ -92,7 +94,7 @@ public interface Transport
|
||||
int getHeartbeatInterval();
|
||||
|
||||
/**
|
||||
* @param interval the interval in seconds, {@code 0} means no hearbeat
|
||||
* @param interval the interval in seconds, {@code 0} means no heartbeat
|
||||
* @deprecated Moved to {@link net.schmizz.keepalive.KeepAlive#getKeepAliveInterval()}. This is accessible through the {@link net.schmizz.sshj.connection.Connection}.
|
||||
* Scheduled to be removed in 0.12.0
|
||||
*/
|
||||
@@ -156,7 +158,7 @@ public interface Transport
|
||||
*
|
||||
* @return the sequence number of the packet sent
|
||||
*
|
||||
* @throws TransportException if an error occured sending the packet
|
||||
* @throws TransportException if an error occurred sending the packet
|
||||
*/
|
||||
long sendUnimplemented()
|
||||
throws TransportException;
|
||||
@@ -235,4 +237,7 @@ public interface Transport
|
||||
* @param e The exception that occurred.
|
||||
*/
|
||||
void die(Exception e);
|
||||
|
||||
KeyAlgorithm getHostKeyAlgorithm();
|
||||
KeyAlgorithm getClientKeyAlgorithm(KeyType keyType) throws TransportException;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.key.KeyAlgorithms;
|
||||
import com.hierynomus.sshj.transport.IdentificationStringParser;
|
||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||
import net.schmizz.concurrent.Event;
|
||||
@@ -30,6 +32,7 @@ import org.slf4j.Logger;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@@ -39,6 +42,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
public final class TransportImpl
|
||||
implements Transport, DisconnectListener {
|
||||
|
||||
|
||||
private static final class NullService
|
||||
extends AbstractService {
|
||||
|
||||
@@ -86,6 +90,10 @@ public final class TransportImpl
|
||||
|
||||
private final Decoder decoder;
|
||||
|
||||
private KeyAlgorithm hostKeyAlgorithm;
|
||||
|
||||
private boolean rsaSHA2Support;
|
||||
|
||||
private final Event<TransportException> serviceAccept;
|
||||
|
||||
private final Event<TransportException> close;
|
||||
@@ -104,6 +112,11 @@ public final class TransportImpl
|
||||
*/
|
||||
private volatile Service service;
|
||||
|
||||
/**
|
||||
* The next service that will be activated, only set when sending an SSH_MSG_SERVICE_REQUEST
|
||||
*/
|
||||
private volatile Service nextService;
|
||||
|
||||
private DisconnectListener disconnectListener;
|
||||
|
||||
private ConnInfo connInfo;
|
||||
@@ -324,8 +337,9 @@ public final class TransportImpl
|
||||
|
||||
@Override
|
||||
public synchronized void setService(Service service) {
|
||||
if (service == null)
|
||||
if (service == null) {
|
||||
service = nullService;
|
||||
}
|
||||
|
||||
log.debug("Setting active service to {}", service.getName());
|
||||
this.service = service;
|
||||
@@ -337,11 +351,12 @@ public final class TransportImpl
|
||||
serviceAccept.lock();
|
||||
try {
|
||||
serviceAccept.clear();
|
||||
this.nextService = service;
|
||||
sendServiceRequest(service.getName());
|
||||
serviceAccept.await(timeoutMs, TimeUnit.MILLISECONDS);
|
||||
setService(service);
|
||||
} finally {
|
||||
serviceAccept.unlock();
|
||||
this.nextService = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,8 +500,8 @@ public final class TransportImpl
|
||||
* This method is called in the context of the {@link #reader} thread via {@link Decoder#received} when a full
|
||||
* packet has been decoded.
|
||||
*
|
||||
* @param msg the message identifer
|
||||
* @param buf buffer containg rest of the packet
|
||||
* @param msg the message identifier
|
||||
* @param buf buffer containing rest of the packet
|
||||
* @throws SSHException if an error occurs during handling (unrecoverable)
|
||||
*/
|
||||
@Override
|
||||
@@ -496,13 +511,11 @@ public final class TransportImpl
|
||||
|
||||
log.trace("Received packet {}", msg);
|
||||
|
||||
if (msg.geq(50)) // not a transport layer packet
|
||||
if (msg.geq(50)) { // not a transport layer packet
|
||||
service.handle(msg, buf);
|
||||
|
||||
else if (msg.in(20, 21) || msg.in(30, 49)) // kex packet
|
||||
} else if (msg.in(20, 21) || msg.in(30, 49)) { // kex packet
|
||||
kexer.handle(msg, buf);
|
||||
|
||||
else
|
||||
} else {
|
||||
switch (msg) {
|
||||
case DISCONNECT:
|
||||
gotDisconnect(buf);
|
||||
@@ -519,6 +532,9 @@ public final class TransportImpl
|
||||
case SERVICE_ACCEPT:
|
||||
gotServiceAccept();
|
||||
break;
|
||||
case EXT_INFO:
|
||||
log.debug("Received SSH_MSG_EXT_INFO");
|
||||
break;
|
||||
case USERAUTH_BANNER:
|
||||
log.debug("Received USERAUTH_BANNER");
|
||||
break;
|
||||
@@ -526,6 +542,7 @@ public final class TransportImpl
|
||||
sendUnimplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void gotDebug(SSHPacket buf)
|
||||
@@ -558,6 +575,8 @@ public final class TransportImpl
|
||||
if (!serviceAccept.hasWaiters())
|
||||
throw new TransportException(DisconnectReason.PROTOCOL_ERROR,
|
||||
"Got a service accept notification when none was awaited");
|
||||
// Immediately switch to next service to prevent race condition mentioned in #559
|
||||
setService(nextService);
|
||||
serviceAccept.set();
|
||||
} finally {
|
||||
serviceAccept.unlock();
|
||||
@@ -641,4 +660,30 @@ public final class TransportImpl
|
||||
return connInfo;
|
||||
}
|
||||
|
||||
public void setHostKeyAlgorithm(KeyAlgorithm keyAlgorithm) {
|
||||
this.hostKeyAlgorithm = keyAlgorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyAlgorithm getHostKeyAlgorithm() {
|
||||
return this.hostKeyAlgorithm;
|
||||
}
|
||||
|
||||
public void setRSASHA2Support(boolean rsaSHA2Support) {
|
||||
this.rsaSHA2Support = rsaSHA2Support;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyAlgorithm getClientKeyAlgorithm(KeyType keyType) throws TransportException {
|
||||
if (keyType != KeyType.RSA || !rsaSHA2Support) {
|
||||
return Factory.Named.Util.create(getConfig().getKeyAlgorithms(), keyType.toString());
|
||||
}
|
||||
|
||||
List<Factory.Named<KeyAlgorithm>> factories = getConfig().getKeyAlgorithms();
|
||||
if (factories != null)
|
||||
for (Factory.Named<KeyAlgorithm> f : factories)
|
||||
if (f.getName().equals("ssh-rsa") || KeyAlgorithms.SSH_RSA_SHA2_ALGORITHMS.contains(f.getName()))
|
||||
return f.create();
|
||||
throw new TransportException("Cannot find an available KeyAlgorithm for type " + keyType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,8 +78,8 @@ public abstract class AbstractDHG extends AbstractDH
|
||||
digest.update(buf.array(), buf.rpos(), buf.available());
|
||||
H = digest.digest();
|
||||
|
||||
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
||||
KeyType.fromKey(hostKey).toString());
|
||||
|
||||
Signature signature = trans.getHostKeyAlgorithm().newSignature();
|
||||
signature.initVerify(hostKey);
|
||||
signature.update(H, 0, H.length);
|
||||
if (!signature.verify(sig))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport.kex;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import net.schmizz.sshj.common.*;
|
||||
import net.schmizz.sshj.signature.Signature;
|
||||
import net.schmizz.sshj.transport.Transport;
|
||||
@@ -30,9 +31,9 @@ import java.security.GeneralSecurityException;
|
||||
public abstract class AbstractDHGex extends AbstractDH {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private int minBits = 1024;
|
||||
private int maxBits = 8192;
|
||||
private int preferredBits = 2048;
|
||||
private final int minBits = 1024;
|
||||
private final int maxBits = 8192;
|
||||
private final int preferredBits = 2048;
|
||||
|
||||
public AbstractDHGex(Digest digest) {
|
||||
super(new DH(), digest);
|
||||
@@ -84,8 +85,8 @@ public abstract class AbstractDHGex extends AbstractDH {
|
||||
.putMPInt(k);
|
||||
digest.update(buf.array(), buf.rpos(), buf.available());
|
||||
H = digest.digest();
|
||||
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
|
||||
KeyType.fromKey(hostKey).toString());
|
||||
KeyAlgorithm keyAlgorithm = trans.getHostKeyAlgorithm();
|
||||
Signature signature = keyAlgorithm.newSignature();
|
||||
signature.initVerify(hostKey);
|
||||
signature.update(H, 0, H.length);
|
||||
if (!signature.verify(sig))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport.kex;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.transport.random.Random;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
@@ -31,7 +32,7 @@ public class Curve25519DH extends DHBase {
|
||||
private byte[] secretKey;
|
||||
|
||||
public Curve25519DH() {
|
||||
super("ECDSA", "ECDH");
|
||||
super(KeyAlgorithm.ECDSA, "ECDH");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport.kex;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
import net.schmizz.sshj.transport.random.Random;
|
||||
@@ -54,7 +55,7 @@ public class ECDH extends DHBase {
|
||||
|
||||
@Override
|
||||
public void computeK(byte[] f) throws GeneralSecurityException {
|
||||
KeyFactory keyFactory = SecurityUtils.getKeyFactory("EC");
|
||||
KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.EC_BC);
|
||||
ECPublicKeySpec keySpec = new ECPublicKeySpec(getDecoded(f, ecParameterSpec.getCurve()), ecParameterSpec);
|
||||
PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
|
||||
agreement.doPhase(yourPubKey, true);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport.verification;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.transport.verification.KnownHostMatchers;
|
||||
import net.schmizz.sshj.common.*;
|
||||
import org.slf4j.Logger;
|
||||
@@ -40,6 +41,11 @@ public class OpenSSHKnownHosts
|
||||
protected final File khFile;
|
||||
protected final List<KnownHostEntry> entries = new ArrayList<KnownHostEntry>();
|
||||
|
||||
public OpenSSHKnownHosts(Reader reader) throws IOException {
|
||||
this(reader, LoggerFactory.DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
public OpenSSHKnownHosts(File khFile)
|
||||
throws IOException {
|
||||
this(khFile, LoggerFactory.DEFAULT);
|
||||
@@ -50,29 +56,40 @@ public class OpenSSHKnownHosts
|
||||
this.khFile = khFile;
|
||||
log = loggerFactory.getLogger(getClass());
|
||||
if (khFile.exists()) {
|
||||
final EntryFactory entryFactory = new EntryFactory();
|
||||
final BufferedReader br = new BufferedReader(new FileReader(khFile));
|
||||
try {
|
||||
// Read in the file, storing each line as an entry
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
try {
|
||||
KnownHostEntry entry = entryFactory.parseEntry(line);
|
||||
if (entry != null) {
|
||||
entries.add(entry);
|
||||
}
|
||||
} catch (SSHException ignore) {
|
||||
log.debug("Bad line ({}): {} ", ignore.toString(), line);
|
||||
} catch (SSHRuntimeException ignore) {
|
||||
log.debug("Failed to process line ({}): {} ", ignore.toString(), line);
|
||||
}
|
||||
}
|
||||
readEntries(br);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(br);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OpenSSHKnownHosts(Reader reader, LoggerFactory loggerFactory) throws IOException {
|
||||
this.khFile = null;
|
||||
log = loggerFactory.getLogger(getClass());
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
readEntries(br);
|
||||
}
|
||||
|
||||
private void readEntries(BufferedReader br) throws IOException {
|
||||
final EntryFactory entryFactory = new EntryFactory();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
try {
|
||||
KnownHostEntry entry = entryFactory.parseEntry(line);
|
||||
if (entry != null) {
|
||||
entries.add(entry);
|
||||
}
|
||||
} catch (SSHException ignore) {
|
||||
log.debug("Bad line ({}): {} ", ignore.toString(), line);
|
||||
} catch (SSHRuntimeException ignore) {
|
||||
log.debug("Failed to process line ({}): {} ", ignore.toString(), line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public File getFile() {
|
||||
return khFile;
|
||||
}
|
||||
@@ -190,7 +207,7 @@ public class OpenSSHKnownHosts
|
||||
* Lines starting with `#' and empty lines are ignored as comments.
|
||||
*/
|
||||
public class EntryFactory {
|
||||
EntryFactory() {
|
||||
public EntryFactory() {
|
||||
}
|
||||
|
||||
public KnownHostEntry parseEntry(String line)
|
||||
@@ -239,7 +256,7 @@ public class OpenSSHKnownHosts
|
||||
final BigInteger e = new BigInteger(split[i++]);
|
||||
final BigInteger n = new BigInteger(split[i++]);
|
||||
try {
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
|
||||
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||
} catch (Exception ex) {
|
||||
log.error("Error reading entry `{}`, could not create key", line, ex);
|
||||
@@ -387,7 +404,7 @@ public class OpenSSHKnownHosts
|
||||
line.append(" ").append(type.toString());
|
||||
line.append(" ").append(getKeyString(key));
|
||||
|
||||
if (!comment.isEmpty()) line.append(" ").append(comment);
|
||||
if (comment != null && !comment.isEmpty()) line.append(" ").append(comment);
|
||||
|
||||
return line.toString();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import net.schmizz.sshj.Service;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import net.schmizz.sshj.userauth.method.AuthMethod;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/** User authentication API. See RFC 4252. */
|
||||
public interface UserAuth {
|
||||
|
||||
@@ -58,6 +60,6 @@ public interface UserAuth {
|
||||
boolean hadPartialSuccess();
|
||||
|
||||
/** The available authentication methods. This is only defined once an unsuccessful authentication has taken place. */
|
||||
Iterable<String> getAllowedMethods();
|
||||
Collection<String> getAllowedMethods();
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import net.schmizz.sshj.transport.TransportException;
|
||||
import net.schmizz.sshj.userauth.method.AuthMethod;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -99,7 +100,7 @@ public class UserAuthImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> getAllowedMethods() {
|
||||
public Collection<String> getAllowedMethods() {
|
||||
return Collections.unmodifiableList(allowedMethods);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.userauth.keyprovider;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||
import net.schmizz.sshj.common.Base64;
|
||||
import net.schmizz.sshj.common.ByteArrayUtils;
|
||||
@@ -140,7 +141,7 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
|
||||
ASN1Data asn = new ASN1Data(data = decrypt(Base64.decode(sb.toString()), cipher, iv));
|
||||
switch (type) {
|
||||
case RSA: {
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
|
||||
asn.readNext();
|
||||
BigInteger modulus = asn.readNext();
|
||||
BigInteger pubExp = asn.readNext();
|
||||
@@ -150,7 +151,7 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
|
||||
return new KeyPair(pubKey, prvKey);
|
||||
}
|
||||
case DSA: {
|
||||
KeyFactory factory = KeyFactory.getInstance("DSA");
|
||||
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
|
||||
asn.readNext();
|
||||
BigInteger p = asn.readNext();
|
||||
BigInteger q = asn.readNext();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.userauth.keyprovider;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
@@ -63,12 +64,16 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
|
||||
final Object o = r.readObject();
|
||||
|
||||
final JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
|
||||
pemConverter.setProvider(SecurityUtils.getSecurityProvider());
|
||||
if (SecurityUtils.getSecurityProvider() != null) {
|
||||
pemConverter.setProvider(SecurityUtils.getSecurityProvider());
|
||||
}
|
||||
|
||||
if (o instanceof PEMEncryptedKeyPair) {
|
||||
final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) o;
|
||||
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
|
||||
decryptorBuilder.setProvider(SecurityUtils.getSecurityProvider());
|
||||
if (SecurityUtils.getSecurityProvider() != null) {
|
||||
decryptorBuilder.setProvider(SecurityUtils.getSecurityProvider());
|
||||
}
|
||||
try {
|
||||
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
|
||||
kp = pemConverter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorBuilder.build(passphrase)));
|
||||
@@ -85,7 +90,7 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
|
||||
if (pwdf != null && pwdf.shouldRetry(resource))
|
||||
continue;
|
||||
else
|
||||
throw e;
|
||||
throw new KeyDecryptionFailedException(e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(r);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.userauth.keyprovider;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import net.schmizz.sshj.common.Base64;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
@@ -114,7 +115,7 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
|
||||
|
||||
final KeyFactory factory;
|
||||
try {
|
||||
factory = KeyFactory.getInstance("RSA");
|
||||
factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
|
||||
} catch (NoSuchAlgorithmException s) {
|
||||
throw new IOException(s.getMessage(), s);
|
||||
}
|
||||
@@ -141,7 +142,7 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
|
||||
|
||||
final KeyFactory factory;
|
||||
try {
|
||||
factory = KeyFactory.getInstance("DSA");
|
||||
factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
|
||||
} catch (NoSuchAlgorithmException s) {
|
||||
throw new IOException(s.getMessage(), s);
|
||||
}
|
||||
@@ -320,9 +321,6 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
|
||||
|
||||
private byte[] read() throws IOException {
|
||||
int len = di.readInt();
|
||||
if (len <= 0 || len > 513) {
|
||||
throw new IOException(String.format("Invalid length %d", len));
|
||||
}
|
||||
byte[] r = new byte[len];
|
||||
di.readFully(r);
|
||||
return r;
|
||||
|
||||
@@ -24,7 +24,7 @@ import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
|
||||
/**
|
||||
* Implements the {@code "publickey"} SSH authentication method.
|
||||
* <p/>
|
||||
* Requesteing authentication with this method first sends a "feeler" request with just the public key, and if the
|
||||
* Requesting authentication with this method first sends a "feeler" request with just the public key, and if the
|
||||
* server responds with {@code SSH_MSG_USERAUTH_PK_OK} indicating that the key is acceptable, it proceeds to send a
|
||||
* request signed with the private key. Therefore, private keys are not requested from the associated {@link
|
||||
* KeyProvider} unless needed.
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package net.schmizz.sshj.userauth.method;
|
||||
|
||||
import com.hierynomus.sshj.key.KeyAlgorithm;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.signature.Signature;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import net.schmizz.sshj.userauth.UserAuthException;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
|
||||
|
||||
@@ -47,9 +48,15 @@ public abstract class KeyedAuthMethod
|
||||
}
|
||||
|
||||
// public key as 2 strings: [ key type | key blob ]
|
||||
reqBuf.putString(KeyType.fromKey(key).toString())
|
||||
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
|
||||
return reqBuf;
|
||||
KeyType keyType = KeyType.fromKey(key);
|
||||
try {
|
||||
KeyAlgorithm ka = params.getTransport().getClientKeyAlgorithm(keyType);
|
||||
reqBuf.putString(ka.getKeyAlgorithm())
|
||||
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
|
||||
return reqBuf;
|
||||
} catch (IOException ioe) {
|
||||
throw new UserAuthException("No KeyAlgorithm configured for key " + keyType);
|
||||
}
|
||||
}
|
||||
|
||||
protected SSHPacket putSig(SSHPacket reqBuf)
|
||||
@@ -61,17 +68,20 @@ public abstract class KeyedAuthMethod
|
||||
throw new UserAuthException("Problem getting private key from " + kProv, ioe);
|
||||
}
|
||||
|
||||
final String kt = KeyType.fromKey(key).toString();
|
||||
Signature signature = Factory.Named.Util.create(params.getTransport().getConfig().getSignatureFactories(), kt);
|
||||
if (signature == null)
|
||||
throw new UserAuthException("Could not create signature instance for " + kt + " key");
|
||||
final KeyType kt = KeyType.fromKey(key);
|
||||
Signature signature;
|
||||
try {
|
||||
signature = params.getTransport().getClientKeyAlgorithm(kt).newSignature();
|
||||
} catch (TransportException e) {
|
||||
throw new UserAuthException("No KeyAlgorithm configured for key " + kt);
|
||||
}
|
||||
|
||||
signature.initSign(key);
|
||||
signature.update(new Buffer.PlainBuffer()
|
||||
.putString(params.getTransport().getSessionID())
|
||||
.putBuffer(reqBuf) // & rest of the data for sig
|
||||
.getCompactData());
|
||||
reqBuf.putSignature(kt, signature.encode(signature.sign()));
|
||||
reqBuf.putSignature(signature.getSignatureName(), signature.encode(signature.sign()));
|
||||
return reqBuf;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,11 @@ public class LoggingTransferListener
|
||||
public void reportProgress(long transferred)
|
||||
throws IOException {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("transferred {}% of `{}`", ((transferred * 100) / size), path);
|
||||
long percent = 100;
|
||||
if (size > 0) {
|
||||
percent = (transferred * 100)/size;
|
||||
}
|
||||
log.trace("transferred {}% of `{}`", percent, path);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package com.hierynomus.sshj.common
|
||||
|
||||
import com.hierynomus.sshj.userauth.certificate.Certificate
|
||||
import net.schmizz.sshj.common.Base64
|
||||
import net.schmizz.sshj.common.Buffer
|
||||
import net.schmizz.sshj.common.KeyType
|
||||
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile
|
||||
import spock.lang.Specification
|
||||
@@ -30,16 +33,88 @@ class KeyTypeSpec extends Specification {
|
||||
|
||||
expect:
|
||||
KeyType.fromKey(kf.getPublic()) == type
|
||||
KeyType.fromKey(kf.getPrivate()) == type
|
||||
KeyType.fromKey(kf.getPrivate()) == privateType
|
||||
|
||||
where:
|
||||
privKey << ["""-----BEGIN EC PRIVATE KEY-----
|
||||
privKey << [
|
||||
"""-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIGhcvG8anyHew/xZJfozh5XIc1kmZZs6o2f0l3KFs4jgoAoGCCqGSM49
|
||||
AwEHoUQDQgAEDUA1JYVD7URSoOGdwPxjea+ETD6IABMD9CWfk3NVTNkdu/Ksn7uX
|
||||
cLTQhx4N16z1IgW2bRbSbsmM++UKXmeWyg==
|
||||
-----END EC PRIVATE KEY-----"""]
|
||||
pubKey << ["""ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBA1ANSWFQ+1EUqDhncD8Y3mvhEw+iAATA/Qln5NzVUzZHbvyrJ+7l3C00IceDdes9SIFtm0W0m7JjPvlCl5nlso= SSH Key"""]
|
||||
type << [KeyType.ECDSA256]
|
||||
-----END EC PRIVATE KEY-----""",
|
||||
|
||||
// ssh-keygen -f ca_key -N '' -t rsa -b 1024 \
|
||||
// && ssh-keygen -f id_rsa_test -N '' -t rsa -b 1024 -m pem \
|
||||
// && cat id_rsa_test
|
||||
"""-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQDNBlUYU5KebH8PBf5i58zFI7xW2CLwUrzKA3949+0wnA9JzrX1
|
||||
XWiVJG3gNgQEgUZIIrPfPAMk9x6hVdBKL9gi3tRUfGTQfQZ7JN9rMFmlbLmEFHd4
|
||||
RY86OHe2h7SO+xfYBieR7aL0DsbwcBZIWm0xlotHzR6d3QodMLYZh0jleQIDAQAB
|
||||
AoGAdzHIRRVJN0tSbxSH+U5T8QS+mSqc3WTslvGDqXtR7SG9jaZciOKeS57bNi+R
|
||||
FGFnz8ZFFnJYTaRRrXArYQYBu/Fp5EtMJQ52G04gGI5mov+4vACaJ3J98wieBMZh
|
||||
miQXZUXjmCIkE+3/SxIIE1Cf2J8Epa012FZ5vcu+kfjOZZECQQDwTm2Hdp8PTpTm
|
||||
BX802Z7zO/ZfW+oGK4LIqvcUqf/2NnA3zpgjAwl9FtxpSzzq4lvcNMTANk3ZkJIg
|
||||
VZsn5Ar9AkEA2moMF2AvWuW7LN5aDKpyPqJJv9fxM1HOkrYWu6aYyl/r5/RaIwSp
|
||||
Bl/5W1Elg5xgYbnms1wtgLIpipfAofoDLQJAG0XrbGpsFwKmJ40MKOViAt0VUzFN
|
||||
WDHr//ZXYIMCx+DZz5uk7KRVmVrU3SZq3YWfQ1jB08bWAxFDZGQS3e4lyQJBANki
|
||||
bkza6ZkjJFax4rIOzS7pZgob4wWTAZum/KinMeSXQc7ChM2ld2gIB705yeKylrrw
|
||||
9qI/NFlqRZQr02z0QS0CQQDVnk224ebhcFlUG6AFcQW/b3O9yqKh6Qj7q9AK5CbV
|
||||
8T8wEvB6yg929UbNSzqtKFHD2+oq/VQF7sym8BTCA9jp
|
||||
-----END RSA PRIVATE KEY-----""",
|
||||
|
||||
// ssh-keygen -f ca_key -N '' -t rsa -b 1024 \
|
||||
// && ssh-keygen -f id_dsa_test -N '' -t dsa -b 1024 -m pem \
|
||||
// && cat id_dsa_test
|
||||
"""-----BEGIN DSA PRIVATE KEY-----
|
||||
MIIBuwIBAAKBgQDCY3/O8Locj4ByO/tFiGi83rQNFlCeD44ZBiZgCMCh687H49OC
|
||||
r1G8Z6Ga55uLp87DXhdEjq8HXgMhDXQakqF5huOyTkPibdsOlv86l3sw8NBBGGMF
|
||||
OE4t6Qj2pVA2YrvUKY7UiyZpOdLVzrXAcouE/32259vDIbX6Y84F+TzZKwIVAOR3
|
||||
zWOxnbR8wR9xKdcMczNlxOtBAoGAVBDn7jKB76oTzcmCnDyoBNISNNiknuhyM0xp
|
||||
hfVMGMgUw72WLWE9k9LYBZMRWBLAeI3C5PuV0cJuSXOz1maY9B+NHr9z7AmMfPF0
|
||||
eK3Fr7IHyr1DcZNZ3yE7UX4UPB/9blgxj/m1/Kvgvl/l9F+wDIH/uAJ+Awuv5a8w
|
||||
jVAaZPcCgYB/vvvduepAMBYSHB3/J7a9GeEAhIlu69fGhiwUmrD1hTWKa4skVKDT
|
||||
PcJ/j1urFglMdh/hKB3DdjT5Fs+95VJYvBBuOwhZ/XwQCQcFLlSXi/CvbaV63f8d
|
||||
f26VSnEypH3G3cmPYfpVcXL63bCb0E4sNJwENM4tQGZa5YGz3CxMdgIVAJUv4z9+
|
||||
2AE1NF07cGZ4Zs9euh9y
|
||||
-----END DSA PRIVATE KEY-----""",
|
||||
]
|
||||
pubKey << [
|
||||
"""ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBA1ANSWFQ+1EUqDhncD8Y3mvhEw+iAATA/Qln5NzVUzZHbvyrJ+7l3C00IceDdes9SIFtm0W0m7JjPvlCl5nlso= SSH Key""",
|
||||
|
||||
// ssh-keygen -s ca_key -I sshj_test -n user -V +240m id_rsa_test.pub \
|
||||
// && cat id_rsa_test-cert.pub
|
||||
"""ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgKXsDoXKTlSaN3N/z6OwYQwCWMAf6N5CidrRJMe+IUOwAAAADAQABAAAAgQDNBlUYU5KebH8PBf5i58zFI7xW2CLwUrzKA3949+0wnA9JzrX1XWiVJG3gNgQEgUZIIrPfPAMk9x6hVdBKL9gi3tRUfGTQfQZ7JN9rMFmlbLmEFHd4RY86OHe2h7SO+xfYBieR7aL0DsbwcBZIWm0xlotHzR6d3QodMLYZh0jleQAAAAAAAAAAAAAAAQAAAAlzc2hqX3Rlc3QAAAAIAAAABHVzZXIAAAAAXuczLAAAAABe52vUAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAN4tykUwkwobduBrqTJW7HNJP1Z6bAhzNNA1P2KY+0aO0iBhISHcaWS0vKsgQ4BYbbVfTkrc0rEuRnajl9PhhEyZgJs8oR7sGndxAlDY6UJH4wSL3j7A+2bogoSbpOfWZiPp/tJohPBvmsN6Uv89Axs0L7V338yH3UE4bX6eIQDZVjKjL7evOYz7sOdV7pUYvgzErqzVxxmF7t/yd61rNOOLm0PY4O1HpW2ZYGGtG8YIC8asFYY2EouLy8OsP2z7U8DLFokTuMsTA6ADN5tYbzBqxUCMsuUPqdGz4xnlTja7jm4FjBFa91KmpQSOWhveaQeb91dwMeomyplYH8W6tkDuXR7acncxYAjB955ws/T9qWrACWFryskUGI1oupM5QVXveO793oWoSqtDi6BOw763Z+7oekmScOOSpeRgsNvSPN+RMyDDuT0Sf4Tc/aLABow6t5UfULAwv9xqLg9QH6CS9ZSkIWNiEOkhCa1uH2srSF6e9m2XN0f5EawZ81EYBQAAAZQAAAAMcnNhLXNoYTItNTEyAAABgFrJp0zHjElwO+PV/hd5zXydySLLKI6A/UzCnCcceSY46oO94Uzn3Bc8UKdxMctlMOG1LGeiuYE/CMMGwnuo1TLWrfY1anVigPx0CrHzjE7ZT4qhCJsVmuwpU3qcZOrom+bDbco2N30/K81rrU5KlmVG6zGHNOpwRr2zRIusXGQ3/e+dOzsMh1cmO/XntGmxWXYPIHs4TUXE1d5C2hZFN1jkBkvlG6n6ZMq7Z+6V5oVc2bqETJv6zxypt1II7l5ObZJP26x75yb4VEbycHLENYSgSVBoN7pyc7QqRYbTnfdxJBKyjDmjOXAMlAzFch5BVHazw1PNS+BkKLZaDb/dlTfeKjDqIywb97zT4XJ70HUTZB3P/q6OwiTtrKOW9no5C9Wka2EkOliNwvsJyxYxNmS8SEzT/Ezk7vhxzS8C17+uBX55o0nJ1bjPw0E02mAoIu5o0sXm/J+3dW+s1TG1XzXdksUthG8EozQfr5M0MuOeONkwebtAo/JzSF73rlGBlA== sshj@example.com""",
|
||||
|
||||
// ssh-keygen -s ca_key -I sshj_test -n user -V +240m id_dsa_test.pub \
|
||||
// && cat id_dsa_test-cert.pub
|
||||
"""ssh-dss-cert-v01@openssh.com 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 sshj@example.com""",
|
||||
]
|
||||
|
||||
type << [
|
||||
KeyType.ECDSA256,
|
||||
KeyType.RSA_CERT,
|
||||
KeyType.DSA_CERT,
|
||||
]
|
||||
|
||||
privateType << [
|
||||
KeyType.ECDSA256,
|
||||
KeyType.RSA,
|
||||
KeyType.DSA,
|
||||
]
|
||||
}
|
||||
|
||||
def "should read signed certificate (public)"() {
|
||||
given:
|
||||
def key = """ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg10JjXqWoxeMUyik45jr0cIZ/hFmYZQO27ilcjFHDVpgAAAADAQABAAAAgQCbFJRT+YwxvPCuHjWfEzeMs/Xkp6ay/N9J4XgSgHcyB6yeYw0KMW4Rs6OWJZcvj5ejuUonS9Kbwyf0YaylZxlfxt/K06rtHgKjwY3SSAQz4Y6woo4QI+pNP6SciJ/XymnE8+ZC6bJxwF5VtX1ivPn8o5am6LiDUk7kQoPAkBfs5/IE+w5UvO4zAAAAAQAAAE52YXVsdC1hcHByb2xlLWRiZDY5ZGExNmNiOTYwYjRhYTJkMzdlODcwZTA5OTdkN2M3ZjhhMjVlZTcyYmEwNTU0NTQ0NjBmNDA1YWU3ZjIAAAAPAAAAC2JyZWFrLWdsYXNzAAAAAF7B1hkAAAAAXsHdPwAAAAAAAAASAAAACnBlcm1pdC1wdHkAAAAAAAAAAAAAAhcAAAAHc3NoLXJzYQAAAAMBAAEAAAIBAOZSr+o8qpoAxBByB3dB0uRbaP9TMKU7W/j/RTIrVINeMJFzzkrqX6Bwwvn6+cAUjuLMz7L8H9Wqjivp48XWWPyyYbJ64bPdlPFzAzWbdCfY4Nd2akFLDTKqd5i0+iu+06suMEmvKVMQraNvjPccshrtcQYHTB0qmq6G1PjFaUxMeeSdv+U8+AqKIVMcuvnVfidUnaaVXn3ie9R616l3JwtvW/XS1SeAqrMqovvMMmZrIB9Jq9WPyV5hriYeWwFPbZUhsQsSj78IMxLdp0P318+6LUkuLuGZ9QGrQxWeOM2Y1/4PA3CHUBIRiA7+UGafkwn2y3L9XtxHXdQvcotV1zWq805YDw7frQJTXEI028+NPE0sE0aLbNM8/RibXgukiL8nP9M9ZfSlB8tUDO5Kuq5nWEUQ6hODW+RLf3qQFlCCF1mLy4HnwjY70jyLankVThIK2ZMoUqujVlBo5Fh8ptwzQ+VvJ9EUcz9rnoGtP0qIxutcTROKvUMcCKNimu7K+PP5+U6GJK4UbPqJSKGO7z85BBsNoqU50S2DP7lEhqd/9Ty+zXICu/Fjtio2uM4GfFIFChqTR2QuJ25dw9+v9yCVcqwozWeb0MOLm3i/bpxNqbici8ol1ZNBDyoI9gyi3vuxPkzerse7IplqUraINvXXQxrqR7/WNehayruAwYcNAAACDwAAAAdzc2gtcnNhAAACAMoIDDhDC2TCOEYE46tKL/lD4eODb4MxVJBWxBPcdUtDf24L7TuOoeRAoOIpKtv5JKU2du3ElUMnheyX+3IWuh4H8AuHGU4AyFc+cXvyGNAE7mppUb637cPNCOVgfUdImHtPRiP6MdILFeMAHNPpfkh8i+qqszum2QuukexF+7wieuaaOtlBNeuZ0/2oi6I2KGOzEnL54w0PKXgQY2AVWgeOQ2S7c+XFjxI0ospE0XVjut/Kn1VSK3cO3sI4W14gb9agaF/XTUegtrpHuPvW4Q3OQbbGHG9J/8/WGQB/HlexDkXh2GaNupC4+vwXb4//P1OV9bMUYPqMH6j2DAs8cSMdfh14utmmwrIWIgoKD3Htiby6XDkNs0EQdzPHeVgE6wbL0hO2oV3VRgcQ2CUxD+Z8j/S/q/mWS+PrfMkVBTPFojdrmxy0fJeZnkqhqK/N5+zkcP2RCwPiy2DQ/NOSrpJFqwxSctse7kbrwESdtT4cUIuofN1UOceRBhk2DoSJgLag9JflYeWez6rBW7GzJEtTLbObkRGWacWNZsyVHZlQFd1rhA2+6Yn42FFJQm9E6Eh+Higd2u7HmSrAVKiqZuwdGLCuUut+QhHIShBN0WBQAZTKtvv8/IQGk84jHy7UZeVrmNFM57AsSLIw3kTcebrRP+vFLYPun/KzbnTP3yrC"""
|
||||
def parts = key.split("\\s+")
|
||||
def keyType = KeyType.fromString(parts[0])
|
||||
|
||||
when:
|
||||
def pubKey = new Buffer.PlainBuffer(Base64.decode(parts[1])).readPublicKey()
|
||||
|
||||
then:
|
||||
KeyType.fromKey(pubKey) == keyType
|
||||
pubKey.getClass() == Certificate.class
|
||||
((Certificate)pubKey).getSignature().length > 0
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.signature
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm
|
||||
import spock.lang.Unroll;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -47,7 +48,7 @@ import spock.lang.Specification
|
||||
|
||||
class SignatureDSASpec extends Specification {
|
||||
|
||||
def keyFactory = KeyFactory.getInstance("DSA")
|
||||
def keyFactory = KeyFactory.getInstance(KeyAlgorithm.DSA)
|
||||
|
||||
private PublicKey createPublicKey(final byte[] y, final byte[] p, final byte[] q, final byte[] g) throws Exception {
|
||||
final BigInteger publicKey = new BigInteger(y);
|
||||
|
||||
@@ -44,6 +44,6 @@ public class StreamCopierTest {
|
||||
long copied = streamCopier.copy();
|
||||
assertThat(copied, is(1024L));
|
||||
|
||||
verify(logger).debug(contains("1.0 KiB transferred"));
|
||||
verify(logger).debug(matches("^1[.,]0 KiB transferred.*$"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.keyprovider;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
|
||||
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
@@ -46,10 +47,7 @@ import java.security.interfaces.RSAPublicKey;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
@@ -69,6 +67,44 @@ public class OpenSSHKeyFileTest {
|
||||
final char[] correctPassphrase = "test_passphrase".toCharArray();
|
||||
final char[] incorrectPassphrase = new char[]{' '};
|
||||
|
||||
private static class WipeTrackingPasswordFinder implements PasswordFinder {
|
||||
private int reqCounter = 0;
|
||||
|
||||
final private String password;
|
||||
final private boolean withRetry;
|
||||
final private ArrayList<char[]> toWipe = new ArrayList<>();
|
||||
|
||||
WipeTrackingPasswordFinder(String password, Boolean withRetry) {
|
||||
this.password = password;
|
||||
this.withRetry = withRetry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] reqPassword(Resource<?> resource) {
|
||||
char[] passwordChars;
|
||||
if (withRetry && reqCounter < 3) {
|
||||
reqCounter++;
|
||||
// Return an incorrect password three times before returning the correct one.
|
||||
passwordChars = (password + "incorrect").toCharArray();
|
||||
} else {
|
||||
passwordChars = password.toCharArray();
|
||||
}
|
||||
toWipe.add(passwordChars);
|
||||
return passwordChars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRetry(Resource<?> resource) {
|
||||
return withRetry && reqCounter <= 3;
|
||||
}
|
||||
|
||||
public void assertWiped() {
|
||||
for (char[] passwordChars : toWipe) {
|
||||
assertArrayEquals(new char[passwordChars.length], passwordChars);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final PasswordFinder onlyGivesWhenReady = new PasswordFinder() {
|
||||
@Override
|
||||
public char[] reqPassword(Resource resource) {
|
||||
@@ -200,12 +236,34 @@ public class OpenSSHKeyFileTest {
|
||||
|
||||
@Test
|
||||
public void shouldLoadProtectedED25519PrivateKeyAes256CTR() throws IOException {
|
||||
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_protected", "sshjtest");
|
||||
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_protected", "sshjtest", false);
|
||||
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_protected", "sshjtest", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLoadProtectedED25519PrivateKeyAes256CBC() throws IOException {
|
||||
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_aes256cbc.pem", "foobar");
|
||||
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_aes256cbc.pem", "foobar", false);
|
||||
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_aes256cbc.pem", "foobar", true);
|
||||
}
|
||||
|
||||
@Test(expected = KeyDecryptionFailedException.class)
|
||||
public void shouldFailOnIncorrectPassphraseAfterRetries() throws IOException {
|
||||
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
|
||||
keyFile.init(new File("src/test/resources/keytypes/ed25519_aes256cbc.pem"), new PasswordFinder() {
|
||||
private int reqCounter = 0;
|
||||
|
||||
@Override
|
||||
public char[] reqPassword(Resource<?> resource) {
|
||||
reqCounter++;
|
||||
return "incorrect".toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRetry(Resource<?> resource) {
|
||||
return reqCounter <= 3;
|
||||
}
|
||||
});
|
||||
keyFile.getPrivate();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -224,21 +282,13 @@ public class OpenSSHKeyFileTest {
|
||||
assertThat(aPrivate.getAlgorithm(), equalTo("ECDSA"));
|
||||
}
|
||||
|
||||
private void checkOpenSSHKeyV1(String key, final String password) throws IOException {
|
||||
private void checkOpenSSHKeyV1(String key, final String password, boolean withRetry) throws IOException {
|
||||
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
|
||||
keyFile.init(new File(key), new PasswordFinder() {
|
||||
@Override
|
||||
public char[] reqPassword(Resource<?> resource) {
|
||||
return password.toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRetry(Resource<?> resource) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
WipeTrackingPasswordFinder pwf = new WipeTrackingPasswordFinder(password, withRetry);
|
||||
keyFile.init(new File(key), pwf);
|
||||
PrivateKey aPrivate = keyFile.getPrivate();
|
||||
assertThat(aPrivate.getAlgorithm(), equalTo("EdDSA"));
|
||||
pwf.assertWiped();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -28,6 +28,89 @@ import static org.junit.Assert.assertNull;
|
||||
|
||||
public class PuTTYKeyFileTest {
|
||||
|
||||
final static String ppk8192 = "PuTTY-User-Key-File-2: ssh-rsa\n" +
|
||||
"Encryption: none\n" +
|
||||
"Comment: imported-openssh-key\n" +
|
||||
"Public-Lines: 22\n" +
|
||||
"AAAAB3NzaC1yc2EAAAADAQABAAAEAQCcasi2SDVGvty6az32C3Uc3F4d8icjefnN\n" +
|
||||
"YCaDnBIRQjczX118dT/nG2rEMygR/cgCxmZgcySC7vo5KUNjJhxCMHa5u4H0CVdy\n" +
|
||||
"Raey2AOZBfLECjzuXSaakeMCIqyT6IywUBEFnkN6aUesyQtUUf1hR5iWHwPUmJPO\n" +
|
||||
"uYLlE4uYnK5hkeH8fSEbYVPcPiBnrHtRk+zh9MF0RR6tK0Gcms5eLfF2V2MNytvU\n" +
|
||||
"FnAySqX8mYISeJrg7v41PxtoEsAhGE88h4XAYX57uB4ewwTWQOlbBVgAutLybyLG\n" +
|
||||
"rxbw+cDuC3ZOuxU78u5PykcS/mkE2wu1jUtdnCzAmNN8XobAft0wggiEZUBc+t9D\n" +
|
||||
"2NmezZFU62SEkjxOWX/idDQrCQ8au8RQZhIgLYusGXDeeYFoPDk/4ObBxz3YkuTu\n" +
|
||||
"UqzVTYwoUslTe8cz5J+hDGPeTudkt1K4uXa+3weXrzj0BnSYvGb01bfoam8lShdl\n" +
|
||||
"MBg5hmow0ZjE6AvJgdttu+9SKvIp+jGQ2v2fv/m/LmGBKgZ5yslGJb6hhNf7MA5S\n" +
|
||||
"ewgHuAk8kfZ9yZIa3UcQDim8yxOkB/Y3885MFpdZqg3XNPCNo0s1SimGGRbngWwg\n" +
|
||||
"AxhKT24OzQ+WZn+rU7mlXHT4RehrYNKNukZlwqnSksg+TJ1ZGoj8mfUbAHmz0UnB\n" +
|
||||
"DQ7dpNP1DhAKxiFjgHfkDfmF4Bic7I1eHSesigCKImH7Zoomp1NcH0bub3h+Owyp\n" +
|
||||
"2fk5evgMBtuGvGGFuCzgyZeeiX6hzOgKyaqCML88OgNSjSMFkdiBYd0rwufimkID\n" +
|
||||
"v+vH1uIEcVZ69sn8xg0Vh7U/0aB2mai0EYcDuTa78gqkeSGp8AS+IgahgdwV/HQX\n" +
|
||||
"aLC/QFRgFb/NX2YmzKsVYWdObBamkbaJAOfrXb5vEuAyU2aRQouqKH4tYDNpkBYg\n" +
|
||||
"8KCq9A/8z8sS1Gwe3UHU9gZOEuTAI7JQQCN7E3U3JuuCFks2jAoh7WE3KxqEu9Lq\n" +
|
||||
"sMJn9YRobGyPPMMcQJSAqMUpwEyup8ovI/3v5NRvw+ZSiM4wHyYqzODJu/U6H5Cj\n" +
|
||||
"wq+MFCg4JcalRA/qKG4P9QVD9MfyqcX/AYWhdYj18BqstwUVtonhT0kMkKBx9ggU\n" +
|
||||
"g/TvVKePf/wX0glqXXw59I1EIzCnxL8QWMkULDkk5GvzSrGFpR04IdOzsz5DMdL3\n" +
|
||||
"p8bXOHK+04Rd/VG8w/f7eLfYid875B7m+kG9TKQzAT3lc8cmJ98gRzCG+pTIpzVB\n" +
|
||||
"QM2nj4f8DenS1uAO23cXICR9Zyo98/dCv0xYc7g0Gp5HxppRuNLga9bBSg5dferT\n" +
|
||||
"QvmP/MTgeNxiKepKFLakVT0MiM6QUlGfV35F6vDL1oQnQlp4OD7H\n" +
|
||||
"Private-Lines: 54\n" +
|
||||
"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\n" +
|
||||
"Private-MAC: 5405ff514dd17380c68d08f371a9497e827a1054\n";
|
||||
|
||||
final static String ppk2048 = "PuTTY-User-Key-File-2: ssh-rsa\n" +
|
||||
"Encryption: none\n" +
|
||||
"Comment: \n" +
|
||||
@@ -155,6 +238,14 @@ public class PuTTYKeyFileTest {
|
||||
assertNotNull(key.getPublic());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test8192() throws Exception {
|
||||
PuTTYKeyFile key = new PuTTYKeyFile();
|
||||
key.init(new StringReader(ppk8192));
|
||||
assertNotNull(key.getPrivate());
|
||||
assertNotNull(key.getPublic());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorrectPassphraseRsa() throws Exception {
|
||||
PuTTYKeyFile key = new PuTTYKeyFile();
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.security.spec.DSAPrivateKeySpec;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -35,7 +36,7 @@ public class SignatureDSATest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws NoSuchAlgorithmException {
|
||||
keyFactory = KeyFactory.getInstance("DSA");
|
||||
keyFactory = KeyFactory.getInstance(KeyAlgorithm.DSA);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.util;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyAlgorithm;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -46,7 +47,7 @@ public class KeyUtil {
|
||||
/** Creates a DSA private key. */
|
||||
public static PrivateKey newDSAPrivateKey(String x, String p, String q, String g)
|
||||
throws GeneralSecurityException {
|
||||
return SecurityUtils.getKeyFactory("DSA").generatePrivate(new DSAPrivateKeySpec(new BigInteger(x, 16),
|
||||
return SecurityUtils.getKeyFactory(KeyAlgorithm.DSA).generatePrivate(new DSAPrivateKeySpec(new BigInteger(x, 16),
|
||||
new BigInteger(p, 16),
|
||||
new BigInteger(q, 16),
|
||||
new BigInteger(g, 16))
|
||||
@@ -56,7 +57,7 @@ public class KeyUtil {
|
||||
/** Creates a DSA public key. */
|
||||
public static PublicKey newDSAPublicKey(String y, String p, String q, String g)
|
||||
throws GeneralSecurityException {
|
||||
return SecurityUtils.getKeyFactory("DSA").generatePublic(new DSAPublicKeySpec(new BigInteger(y, 16),
|
||||
return SecurityUtils.getKeyFactory(KeyAlgorithm.DSA).generatePublic(new DSAPublicKeySpec(new BigInteger(y, 16),
|
||||
new BigInteger(p, 16),
|
||||
new BigInteger(q, 16),
|
||||
new BigInteger(g, 16))
|
||||
@@ -66,7 +67,7 @@ public class KeyUtil {
|
||||
/** Creates an RSA private key. */
|
||||
public static PrivateKey newRSAPrivateKey(String modulus, String exponent)
|
||||
throws GeneralSecurityException {
|
||||
return SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(new BigInteger(modulus, 16),
|
||||
return SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(new RSAPrivateKeySpec(new BigInteger(modulus, 16),
|
||||
new BigInteger(exponent, 16))
|
||||
);
|
||||
}
|
||||
@@ -74,7 +75,7 @@ public class KeyUtil {
|
||||
/** Creates an RSA public key. */
|
||||
public static PublicKey newRSAPublicKey(String modulus, String exponent)
|
||||
throws GeneralSecurityException {
|
||||
return SecurityUtils.getKeyFactory("RSA").generatePublic(new RSAPublicKeySpec(new BigInteger(modulus, 16),
|
||||
return SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePublic(new RSAPublicKeySpec(new BigInteger(modulus, 16),
|
||||
new BigInteger(exponent, 16)));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user