mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 07:10:53 +03:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e15a8bd7d | ||
|
|
531eb97767 | ||
|
|
e36fd0fb3d | ||
|
|
382321deca | ||
|
|
7b75fb3d53 | ||
|
|
4d84d3f67c | ||
|
|
8eb7d1a2ad | ||
|
|
a03fa9ac63 | ||
|
|
bcb15e6ccd | ||
|
|
d85b22fe8d | ||
|
|
f4b71941a3 | ||
|
|
636f896850 | ||
|
|
56c0baf814 | ||
|
|
edfb069f2a | ||
|
|
65b3003e72 | ||
|
|
fbee0b3956 | ||
|
|
fd60139b98 | ||
|
|
0b397bc3d7 | ||
|
|
40f956b4b6 | ||
|
|
ef3f7a2eaf | ||
|
|
8134113510 | ||
|
|
c883c87963 | ||
|
|
920537dac9 | ||
|
|
356ec9ed08 | ||
|
|
aa47b0c5f7 | ||
|
|
d3ed3cfe0f | ||
|
|
786734ce26 | ||
|
|
9cb5bf4e10 | ||
|
|
0e3f7c2bbf | ||
|
|
66d4b34eba | ||
|
|
aafb9942a3 | ||
|
|
d1dff550ce | ||
|
|
ac2720becd | ||
|
|
48dd1fdc41 | ||
|
|
9826a71d2b | ||
|
|
936eb26e9e | ||
|
|
9438157b93 |
8
.bettercodehub.yml
Normal file
8
.bettercodehub.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
exclude:
|
||||
- /build-publishing.gradle
|
||||
- /build.gradle
|
||||
- /settings.gradle
|
||||
component_depth: 1
|
||||
languages:
|
||||
- groovy
|
||||
- java
|
||||
22
README.adoc
22
README.adoc
@@ -1,7 +1,7 @@
|
||||
= sshj - SSHv2 library for Java
|
||||
Jeroen van Erp
|
||||
:sshj_groupid: com.hierynomus
|
||||
:sshj_version: 0.19.0
|
||||
:sshj_version: 0.21.0
|
||||
:source-highlighter: pygments
|
||||
|
||||
image:https://travis-ci.org/hierynomus/sshj.svg?branch=master[link="https://travis-ci.org/hierynomus/sshj"]
|
||||
@@ -37,7 +37,7 @@ If you're building your project using Maven, you can add the following dependenc
|
||||
If your project is built using another build tool that uses the Maven Central repository, translate this dependency into the format used by your build tool.
|
||||
|
||||
== Building SSHJ
|
||||
. Clone the Overthere repository.
|
||||
. Clone the SSHJ repository.
|
||||
. Ensure you have Java6 installed with the http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html[Unlimited strength Java Cryptography Extensions (JCE)].
|
||||
. Run the command `./gradlew clean build`.
|
||||
|
||||
@@ -66,8 +66,13 @@ ciphers::
|
||||
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
|
||||
|
||||
key exchange::
|
||||
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`, `diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
|
||||
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`,
|
||||
`diffie-hellman-group14-sha256`, `diffie-hellman-group15-sha512`, `diffie-hellman-group16-sha512`, `diffie-hellman-group17-sha512`, `diffie-hellman-group18-sha512`
|
||||
`diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
|
||||
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
|
||||
SSHJ also supports the following extended (non official) key exchange algoriths:
|
||||
`diffie-hellman-group14-sha256@ssh.com`, `diffie-hellman-group15-sha256`, `diffie-hellman-group15-sha256@ssh.com`, `diffie-hellman-group15-sha384@ssh.com`,
|
||||
`diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com`
|
||||
|
||||
signatures::
|
||||
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519`
|
||||
@@ -79,7 +84,7 @@ compression::
|
||||
`zlib` and `zlib@openssh.com` (delayed zlib)
|
||||
|
||||
private key files::
|
||||
`pkcs5`, `pkcs8`, `openssh-key-v1`
|
||||
`pkcs5`, `pkcs8`, `openssh-key-v1`, `ssh-rsa-cert-v01@openssh.com`, `ssh-dsa-cert-v01@openssh.com`
|
||||
|
||||
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
|
||||
|
||||
@@ -99,6 +104,15 @@ Google Group: http://groups.google.com/group/sshj-users
|
||||
Fork away!
|
||||
|
||||
== Release history
|
||||
SSHJ 0.21.0 (2017-04-14)::
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/319[#319]: Added support for `ssh-rsa-cert-v01@openssh.com` and `ssh-dsa-cert-v01@openssh.com` certificate key files
|
||||
* Upgraded Gradle to 3.4.1
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/305[#305]: Added support for custom string encoding
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/312[#312]: Upgraded BouncyCastle to 1.56
|
||||
SSHJ 0.20.0 (2017-02-09)::
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/294[#294]: Reference ED25519 by constant instead of name
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/293[#293], https://github.com/hierynomus/sshj/pulls/295[#295] and https://github.com/hierynomus/sshj/pulls/301[#301]: Fixed OSGi packaging
|
||||
* Added new Diffie Hellman groups 15-18 for stronger KeyExchange algorithms
|
||||
SSHJ 0.19.1 (2016-12-30)::
|
||||
* Enabled PKCS5 Key files in DefaultConfig
|
||||
* Merged https://github.com/hierynomus/sshj/pulls/291[#291]: Fixed sshj.properties loading and chained exception messages
|
||||
|
||||
23
build.gradle
23
build.gradle
@@ -24,7 +24,7 @@ targetCompatibility = 1.6
|
||||
|
||||
configurations.compile.transitive = false
|
||||
|
||||
def bouncycastleVersion = "1.51"
|
||||
def bouncycastleVersion = "1.56"
|
||||
|
||||
dependencies {
|
||||
compile "org.slf4j:slf4j-api:1.7.7"
|
||||
@@ -37,7 +37,7 @@ dependencies {
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
||||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
testCompile "org.apache.sshd:sshd-core:1.1.0"
|
||||
testCompile "org.apache.sshd:sshd-core:1.2.0"
|
||||
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
||||
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
||||
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
|
||||
@@ -68,10 +68,12 @@ if (JavaVersion.current().isJava8Compatible()) {
|
||||
}
|
||||
}
|
||||
|
||||
task writeSshjVersionProperties << {
|
||||
project.file("${project.buildDir}/resources/main").mkdirs()
|
||||
project.file("${project.buildDir}/resources/main/sshj.properties").withWriter { w ->
|
||||
w.append("sshj.version=${version}")
|
||||
task writeSshjVersionProperties {
|
||||
doLast {
|
||||
project.file("${project.buildDir}/resources/main").mkdirs()
|
||||
project.file("${project.buildDir}/resources/main/sshj.properties").withWriter { w ->
|
||||
w.append("sshj.version=${version}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,16 +81,21 @@ jar.dependsOn writeSshjVersionProperties
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
// please see http://bnd.bndtools.org/chapters/390-wrapping.html
|
||||
instruction "Bundle-Description", "SSHv2 library for Java"
|
||||
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
instruction "Import-Package", "!net.schmizz.*"
|
||||
instruction "Import-Package", "!com.hierynomus.sshj.*"
|
||||
instruction "Import-Package", "javax.crypto*"
|
||||
instruction "Import-Package", "!net.i2p.crypto.eddsa.math"
|
||||
instruction "Import-Package", "net.i2p*"
|
||||
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
|
||||
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
|
||||
instruction "Import-Package", "org.bouncycastle*"
|
||||
instruction "Import-Package", "org.bouncycastle*;resolution:=optional"
|
||||
instruction "Import-Package", "org.bouncycastle.jce.provider;resolution:=optional"
|
||||
instruction "Import-Package", "*"
|
||||
instruction "Export-Package", "net.schmizz.*"
|
||||
instruction "Export-Package", "com.hierynomus.sshj.*;version=\"${project.jar.manifest.version}\""
|
||||
instruction "Export-Package", "net.schmizz.*;version=\"${project.jar.manifest.version}\""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,11 @@ package net.schmizz.sshj.examples;
|
||||
import net.schmizz.keepalive.KeepAliveProvider;
|
||||
import net.schmizz.sshj.DefaultConfig;
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
||||
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** This examples demonstrates how to setup keep-alive to detect connection dropping. */
|
||||
public class KeepAlive {
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
|
||||
|
||||
@@ -18,7 +18,8 @@ package com.hierynomus.sshj.backport;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
|
||||
public class Jdk7HttpProxySocket extends Socket {
|
||||
|
||||
@@ -48,7 +49,7 @@ public class Jdk7HttpProxySocket extends Socket {
|
||||
}
|
||||
InetSocketAddress isa = (InetSocketAddress) endpoint;
|
||||
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
|
||||
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
|
||||
getOutputStream().write(httpConnect.getBytes(IOUtils.UTF8));
|
||||
checkAndFlushProxyResponse();
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ public class Jdk7HttpProxySocket extends Socket {
|
||||
throw new SocketException("Empty response from proxy");
|
||||
}
|
||||
|
||||
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
|
||||
String proxyResponse = new String(tmpBuffer, 0, len, IOUtils.UTF8);
|
||||
|
||||
// Expecting HTTP/1.x 200 OK
|
||||
if (proxyResponse.contains("200")) {
|
||||
|
||||
@@ -62,8 +62,11 @@ public class IdentificationStringParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) {
|
||||
|
||||
private void logHeaderLine(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException {
|
||||
byte[] bytes = new byte[lineBuffer.available()];
|
||||
lineBuffer.readRawBytes(bytes);
|
||||
String header = new String(bytes, 0, bytes.length - 1);
|
||||
log.debug("Received header: {}", header);
|
||||
}
|
||||
|
||||
private String readIdentification(Buffer.PlainBuffer lineBuffer) throws Buffer.BufferException, TransportException {
|
||||
|
||||
@@ -28,6 +28,7 @@ import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
*
|
||||
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here.
|
||||
*/
|
||||
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||
public class BlockCiphers {
|
||||
|
||||
public static final String COUNTER_MODE = "CTR";
|
||||
|
||||
@@ -25,6 +25,7 @@ import static com.hierynomus.sshj.transport.cipher.BlockCiphers.COUNTER_MODE;
|
||||
*
|
||||
* - http://tools.ietf.org/id/draft-kanno-secsh-camellia-01.txt
|
||||
*/
|
||||
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||
public class ExtendedBlockCiphers {
|
||||
public static BlockCiphers.Factory Camellia128CTR() {
|
||||
return new BlockCiphers.Factory(16, 128, "camellia128-ctr", "Camellia", COUNTER_MODE);
|
||||
|
||||
@@ -19,7 +19,6 @@ import net.schmizz.sshj.transport.cipher.BaseCipher;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class StreamCipher extends BaseCipher {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
* - https://tools.ietf.org/html/rfc4253#section-6.3
|
||||
* - https://tools.ietf.org/html/rfc4345
|
||||
*/
|
||||
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||
public class StreamCiphers {
|
||||
|
||||
public static Factory Arcfour() {
|
||||
|
||||
44
src/main/java/com/hierynomus/sshj/transport/kex/DHG.java
Normal file
44
src/main/java/com/hierynomus/sshj/transport/kex/DHG.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.transport.kex;
|
||||
|
||||
import net.schmizz.sshj.transport.digest.Digest;
|
||||
import net.schmizz.sshj.transport.kex.AbstractDHG;
|
||||
import net.schmizz.sshj.transport.kex.DH;
|
||||
import net.schmizz.sshj.transport.kex.DHBase;
|
||||
|
||||
import javax.crypto.spec.DHParameterSpec;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DHG extends AbstractDHG {
|
||||
private BigInteger group;
|
||||
private BigInteger generator;
|
||||
|
||||
public DHG(BigInteger group, BigInteger generator, Digest digest) {
|
||||
super(new DH(), digest);
|
||||
this.group = group;
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDH(DHBase dh) throws GeneralSecurityException {
|
||||
dh.init(new DHParameterSpec(group, generator), trans.getConfig().getRandomFactory());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.transport.kex;
|
||||
|
||||
import net.schmizz.sshj.transport.digest.*;
|
||||
import net.schmizz.sshj.transport.kex.KeyExchange;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
||||
import static net.schmizz.sshj.transport.kex.DHGroupData.P16;
|
||||
import static net.schmizz.sshj.transport.kex.DHGroupData.P18;
|
||||
|
||||
/**
|
||||
* Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups
|
||||
*
|
||||
* - https://tools.ietf.org/html/rfc4253
|
||||
* - https://tools.ietf.org/html/draft-ietf-curdle-ssh-modp-dh-sha2-01
|
||||
*/
|
||||
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||
public class DHGroups {
|
||||
|
||||
public static DHGroups.Factory Group1SHA1() {
|
||||
return new DHGroups.Factory("diffie-hellman-group1-sha1", P1, G, new SHA1.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group14SHA1() {
|
||||
return new DHGroups.Factory("diffie-hellman-group14-sha1", P14, G, new SHA1.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group14SHA256() {
|
||||
return new DHGroups.Factory("diffie-hellman-group14-sha256", P14, G, new SHA256.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group15SHA512() {
|
||||
return new DHGroups.Factory("diffie-hellman-group15-sha512", P15, G, new SHA512.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group16SHA512() {
|
||||
return new DHGroups.Factory("diffie-hellman-group16-sha512", P16, G, new SHA512.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group17SHA512() {
|
||||
return new DHGroups.Factory("diffie-hellman-group17-sha512", P17, G, new SHA512.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group18SHA512() {
|
||||
return new DHGroups.Factory("diffie-hellman-group18-sha512", P18, G, new SHA512.Factory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Named factory for DHG1 key exchange
|
||||
*/
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||
|
||||
private String name;
|
||||
private BigInteger group;
|
||||
private BigInteger generator;
|
||||
private Factory.Named<Digest> digestFactory;
|
||||
|
||||
public Factory(String name, BigInteger group, BigInteger generator, Named<Digest> digestFactory) {
|
||||
this.name = name;
|
||||
this.group = group;
|
||||
this.generator = generator;
|
||||
this.digestFactory = digestFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyExchange create() {
|
||||
return new DHG(group, generator, digestFactory.create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hierynomus.sshj.transport.kex;
|
||||
|
||||
import net.schmizz.sshj.transport.digest.SHA256;
|
||||
import net.schmizz.sshj.transport.digest.SHA384;
|
||||
import net.schmizz.sshj.transport.digest.SHA512;
|
||||
|
||||
import static net.schmizz.sshj.transport.kex.DHGroupData.*;
|
||||
|
||||
/**
|
||||
* Set of KEX methods that are not in official RFCs but are supported by some SSH servers.
|
||||
*/
|
||||
@SuppressWarnings("PMD.MethodNamingConventions")
|
||||
public class ExtendedDHGroups {
|
||||
public static DHGroups.Factory Group14SHA256AtSSH() {
|
||||
return new DHGroups.Factory("diffie-hellman-group14-sha256@ssh.com", P14, G, new SHA256.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group15SHA256() {
|
||||
return new DHGroups.Factory("diffie-hellman-group15-sha256", P15, G, new SHA256.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group15SHA256AtSSH() {
|
||||
return new DHGroups.Factory("diffie-hellman-group15-sha256@ssh.com", P15, G, new SHA256.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group15SHA384AtSSH() {
|
||||
return new DHGroups.Factory("diffie-hellman-group15-sha384@ssh.com", P15, G, new SHA384.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group16SHA256() {
|
||||
return new DHGroups.Factory("diffie-hellman-group16-sha256", P16, G, new SHA256.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group16SHA384AtSSH() {
|
||||
return new DHGroups.Factory("diffie-hellman-group16-sha384@ssh.com", P16, G, new SHA384.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group16SHA512AtSSH() {
|
||||
return new DHGroups.Factory("diffie-hellman-group16-sha512@ssh.com", P16, G, new SHA512.Factory());
|
||||
}
|
||||
|
||||
public static DHGroups.Factory Group18SHA512AtSSH() {
|
||||
return new DHGroups.Factory("diffie-hellman-group18-sha512@ssh.com", P18, G, new SHA512.Factory());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
package com.hierynomus.sshj.userauth.certificate;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Copyright (C)2009 - SSHJ Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Certificate wrapper for public keys, created to help implement
|
||||
* protocol described here:
|
||||
*
|
||||
* https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
|
||||
*
|
||||
* Consumed primarily by net.shmizz.sshj.common.KeyType
|
||||
*
|
||||
* @param <T> inner public key type
|
||||
*/
|
||||
public class Certificate<T extends PublicKey> implements PublicKey {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final T publicKey;
|
||||
private final byte[] nonce;
|
||||
private final BigInteger serial;
|
||||
private final long type;
|
||||
private final String id;
|
||||
private final List<String> validPrincipals;
|
||||
private final Date validAfter;
|
||||
private final Date validBefore;
|
||||
private final Map<String, String> critOptions;
|
||||
private final Map<String, String> extensions;
|
||||
private final byte[] signatureKey;
|
||||
private final byte[] signature;
|
||||
|
||||
Certificate(Builder<T> builder) {
|
||||
this.publicKey = builder.getPublicKey();
|
||||
this.nonce = builder.getNonce();
|
||||
this.serial = builder.getSerial();
|
||||
this.type = builder.getType();
|
||||
this.id = builder.getId();
|
||||
this.validPrincipals = builder.getValidPrincipals();
|
||||
this.validAfter = builder.getValidAfter();
|
||||
this.validBefore = builder.getValidBefore();
|
||||
this.critOptions = builder.getCritOptions();
|
||||
this.extensions = builder.getExtensions();
|
||||
this.signatureKey = builder.getSignatureKey();
|
||||
this.signature = builder.getSignature();
|
||||
}
|
||||
|
||||
public static <P extends PublicKey> Builder<P> getBuilder() {
|
||||
return new Builder<P>();
|
||||
}
|
||||
|
||||
public byte[] getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public BigInteger getSerial() {
|
||||
return serial;
|
||||
}
|
||||
|
||||
public long getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<String> getValidPrincipals() {
|
||||
return validPrincipals;
|
||||
}
|
||||
|
||||
public Date getValidAfter() {
|
||||
return validAfter;
|
||||
}
|
||||
|
||||
public Date getValidBefore() {
|
||||
return validBefore;
|
||||
}
|
||||
|
||||
public Map<String, String> getCritOptions() {
|
||||
return critOptions;
|
||||
}
|
||||
|
||||
public Map<String, String> getExtensions() {
|
||||
return extensions;
|
||||
}
|
||||
|
||||
public byte[] getSignatureKey() {
|
||||
return signatureKey;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public T getKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return publicKey.getEncoded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return publicKey.getAlgorithm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return publicKey.getFormat();
|
||||
}
|
||||
|
||||
public static class Builder<T extends PublicKey> {
|
||||
private T publicKey;
|
||||
private byte[] nonce;
|
||||
private BigInteger serial;
|
||||
private long type;
|
||||
private String id;
|
||||
private List<String> validPrincipals;
|
||||
private Date validAfter;
|
||||
private Date validBefore;
|
||||
private Map<String, String> critOptions;
|
||||
private Map<String, String> extensions;
|
||||
private byte[] signatureKey;
|
||||
private byte[] signature;
|
||||
|
||||
public Certificate<T> build() {
|
||||
return new Certificate<T>(this);
|
||||
}
|
||||
|
||||
public T getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public Builder<T> publicKey(T publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public Builder<T> nonce(byte[] nonce) {
|
||||
this.nonce = nonce;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BigInteger getSerial() {
|
||||
return serial;
|
||||
}
|
||||
|
||||
public Builder<T> serial(BigInteger serial) {
|
||||
this.serial = serial;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Builder<T> type(long type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Builder<T> id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getValidPrincipals() {
|
||||
return validPrincipals;
|
||||
}
|
||||
|
||||
public Builder<T> validPrincipals(List<String> validPrincipals) {
|
||||
this.validPrincipals = validPrincipals;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getValidAfter() {
|
||||
return validAfter;
|
||||
}
|
||||
|
||||
public Builder<T> validAfter(Date validAfter) {
|
||||
this.validAfter = validAfter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getValidBefore() {
|
||||
return validBefore;
|
||||
}
|
||||
|
||||
public Builder<T> validBefore(Date validBefore) {
|
||||
this.validBefore = validBefore;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getCritOptions() {
|
||||
return critOptions;
|
||||
}
|
||||
|
||||
public Builder<T> critOptions(Map<String, String> critOptions) {
|
||||
this.critOptions = critOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getExtensions() {
|
||||
return extensions;
|
||||
}
|
||||
|
||||
public Builder<T> extensions(Map<String, String> extensions) {
|
||||
this.extensions = extensions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] getSignatureKey() {
|
||||
return signatureKey;
|
||||
}
|
||||
|
||||
public Builder<T> signatureKey(byte[] signatureKey) {
|
||||
this.signatureKey = signatureKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public Builder<T> signature(byte[] signature) {
|
||||
this.signature = signature;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import net.schmizz.sshj.connection.ConnectionException;
|
||||
import net.schmizz.sshj.connection.ConnectionImpl;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class KeepAlive extends Thread {
|
||||
protected final Logger log;
|
||||
|
||||
@@ -18,6 +18,9 @@ package net.schmizz.sshj;
|
||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
|
||||
import com.hierynomus.sshj.transport.kex.DHGroups;
|
||||
import com.hierynomus.sshj.transport.kex.ExtendedDHGroups;
|
||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||
import net.schmizz.keepalive.KeepAliveProvider;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.LoggerFactory;
|
||||
@@ -27,14 +30,15 @@ import net.schmizz.sshj.signature.SignatureECDSA;
|
||||
import net.schmizz.sshj.signature.SignatureRSA;
|
||||
import net.schmizz.sshj.transport.cipher.*;
|
||||
import net.schmizz.sshj.transport.compression.NoneCompression;
|
||||
import net.schmizz.sshj.transport.kex.*;
|
||||
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
|
||||
import net.schmizz.sshj.transport.kex.DHGexSHA1;
|
||||
import net.schmizz.sshj.transport.kex.DHGexSHA256;
|
||||
import net.schmizz.sshj.transport.kex.ECDHNistP;
|
||||
import net.schmizz.sshj.transport.mac.*;
|
||||
import net.schmizz.sshj.transport.random.BouncyCastleRandom;
|
||||
import net.schmizz.sshj.transport.random.JCERandom;
|
||||
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
||||
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||
|
||||
import net.schmizz.sshj.userauth.keyprovider.PKCS5KeyFile;
|
||||
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
|
||||
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
|
||||
@@ -110,10 +114,23 @@ public class DefaultConfig
|
||||
new ECDHNistP.Factory384(),
|
||||
new ECDHNistP.Factory256(),
|
||||
new DHGexSHA1.Factory(),
|
||||
new DHG14.Factory(),
|
||||
new DHG1.Factory());
|
||||
DHGroups.Group1SHA1(),
|
||||
DHGroups.Group14SHA1(),
|
||||
DHGroups.Group14SHA256(),
|
||||
DHGroups.Group15SHA512(),
|
||||
DHGroups.Group16SHA512(),
|
||||
DHGroups.Group17SHA512(),
|
||||
DHGroups.Group18SHA512(),
|
||||
ExtendedDHGroups.Group14SHA256AtSSH(),
|
||||
ExtendedDHGroups.Group15SHA256(),
|
||||
ExtendedDHGroups.Group15SHA256AtSSH(),
|
||||
ExtendedDHGroups.Group15SHA384AtSSH(),
|
||||
ExtendedDHGroups.Group16SHA256(),
|
||||
ExtendedDHGroups.Group16SHA384AtSSH(),
|
||||
ExtendedDHGroups.Group16SHA512AtSSH(),
|
||||
ExtendedDHGroups.Group18SHA512AtSSH());
|
||||
} else {
|
||||
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
|
||||
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package net.schmizz.sshj;
|
||||
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.common.LoggerFactory;
|
||||
import net.schmizz.sshj.common.SSHException;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
@@ -61,6 +62,7 @@ import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
@@ -128,6 +130,9 @@ public class SSHClient
|
||||
|
||||
private final List<LocalPortForwarder> forwarders = new ArrayList<LocalPortForwarder>();
|
||||
|
||||
/** character set of the remote machine */
|
||||
protected Charset remoteCharset = IOUtils.UTF8;
|
||||
|
||||
/** Default constructor. Initializes this object using {@link DefaultConfig}. */
|
||||
public SSHClient() {
|
||||
this(new DefaultConfig());
|
||||
@@ -440,6 +445,15 @@ public class SSHClient
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the character set used to communicate with the remote machine for certain strings (like paths).
|
||||
*
|
||||
* @return remote character set
|
||||
*/
|
||||
public Charset getRemoteCharset() {
|
||||
return remoteCharset;
|
||||
}
|
||||
|
||||
/** @return a {@link RemotePortForwarder} that allows requesting remote forwarding over this connection. */
|
||||
public RemotePortForwarder getRemotePortForwarder() {
|
||||
synchronized (conn) {
|
||||
@@ -708,12 +722,22 @@ public class SSHClient
|
||||
doKex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the character set used to communicate with the remote machine for certain strings (like paths)
|
||||
*
|
||||
* @param remoteCharset
|
||||
* remote character set or {@code null} for default
|
||||
*/
|
||||
public void setRemoteCharset(Charset remoteCharset) {
|
||||
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session startSession()
|
||||
throws ConnectionException, TransportException {
|
||||
checkConnected();
|
||||
checkAuthenticated();
|
||||
final SessionChannel sess = new SessionChannel(conn);
|
||||
final SessionChannel sess = new SessionChannel(conn, remoteCharset);
|
||||
sess.open();
|
||||
return sess;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
@@ -57,6 +57,11 @@ public class Buffer<T extends Buffer<T>> {
|
||||
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
|
||||
public static final int MAX_SIZE = (1 << 30);
|
||||
|
||||
/** Maximum size of a uint64 */
|
||||
private static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
|
||||
.shiftLeft(64)
|
||||
.subtract(BigInteger.ONE);
|
||||
|
||||
protected static int getNextPowerOf2(int i) {
|
||||
int j = 1;
|
||||
while (j < i) {
|
||||
@@ -311,7 +316,7 @@ public class Buffer<T extends Buffer<T>> {
|
||||
public T putUInt32(long uint32) {
|
||||
ensureCapacity(4);
|
||||
if (uint32 < 0 || uint32 > 0xffffffffL)
|
||||
throw new RuntimeException("Invalid value: " + uint32);
|
||||
throw new IllegalArgumentException("Invalid value: " + uint32);
|
||||
data[wpos++] = (byte) (uint32 >> 24);
|
||||
data[wpos++] = (byte) (uint32 >> 16);
|
||||
data[wpos++] = (byte) (uint32 >> 8);
|
||||
@@ -343,10 +348,29 @@ public class Buffer<T extends Buffer<T>> {
|
||||
return uint64;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public BigInteger readUInt64AsBigInteger()
|
||||
throws BufferException {
|
||||
byte[] magnitude = new byte[8];
|
||||
readRawBytes(magnitude);
|
||||
return new BigInteger(1, magnitude);
|
||||
}
|
||||
|
||||
public T putUInt64(long uint64) {
|
||||
if (uint64 < 0)
|
||||
throw new RuntimeException("Invalid value: " + uint64);
|
||||
throw new IllegalArgumentException("Invalid value: " + uint64);
|
||||
return putUInt64Unchecked(uint64);
|
||||
}
|
||||
|
||||
public T putUInt64(BigInteger uint64) {
|
||||
if (uint64.compareTo(MAX_UINT64_VALUE) > 0 ||
|
||||
uint64.compareTo(BigInteger.ZERO) < 0) {
|
||||
throw new IllegalArgumentException("Invalid value: " + uint64);
|
||||
}
|
||||
return putUInt64Unchecked(uint64.longValue());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T putUInt64Unchecked(long uint64) {
|
||||
data[wpos++] = (byte) (uint64 >> 56);
|
||||
data[wpos++] = (byte) (uint64 >> 48);
|
||||
data[wpos++] = (byte) (uint64 >> 40);
|
||||
@@ -361,24 +385,31 @@ public class Buffer<T extends Buffer<T>> {
|
||||
/**
|
||||
* Reads an SSH string
|
||||
*
|
||||
* @param cs the charset to use for decoding
|
||||
*
|
||||
* @return the string as a Java {@code String}
|
||||
*/
|
||||
public String readString()
|
||||
public String readString(Charset cs)
|
||||
throws BufferException {
|
||||
int len = readUInt32AsInt();
|
||||
if (len < 0 || len > 32768)
|
||||
throw new BufferException("Bad item length: " + len);
|
||||
ensureAvailable(len);
|
||||
String s;
|
||||
try {
|
||||
s = new String(data, rpos, len, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
String s = new String(data, rpos, len, cs);
|
||||
rpos += len;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an SSH string using {@code UTF8}
|
||||
*
|
||||
* @return the string as a Java {@code String}
|
||||
*/
|
||||
public String readString()
|
||||
throws BufferException {
|
||||
return readString(IOUtils.UTF8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an SSH string
|
||||
*
|
||||
@@ -397,8 +428,12 @@ public class Buffer<T extends Buffer<T>> {
|
||||
return putBytes(str, offset, len);
|
||||
}
|
||||
|
||||
public T putString(String string, Charset cs) {
|
||||
return putString(string.getBytes(cs));
|
||||
}
|
||||
|
||||
public T putString(String string) {
|
||||
return putString(string.getBytes(IOUtils.UTF8));
|
||||
return putString(string, IOUtils.UTF8);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -15,12 +15,26 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import com.hierynomus.sshj.secg.SecgUtils;
|
||||
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import java.math.BigInteger;
|
||||
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.ECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
@@ -29,20 +43,19 @@ import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.util.Arrays;
|
||||
import com.hierynomus.sshj.secg.SecgUtils;
|
||||
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
||||
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import net.schmizz.sshj.common.Buffer.BufferException;
|
||||
|
||||
/** Type of key e.g. rsa, dsa */
|
||||
public enum KeyType {
|
||||
|
||||
|
||||
/** SSH identifier for RSA keys */
|
||||
RSA("ssh-rsa") {
|
||||
@Override
|
||||
@@ -60,18 +73,16 @@ public enum KeyType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
final RSAPublicKey rsaKey = (RSAPublicKey) pk;
|
||||
buf.putString(sType)
|
||||
.putMPInt(rsaKey.getPublicExponent()) // e
|
||||
.putMPInt(rsaKey.getModulus()); // n
|
||||
buf.putMPInt(rsaKey.getPublicExponent()) // e
|
||||
.putMPInt(rsaKey.getModulus()); // n
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/** SSH identifier for DSA keys */
|
||||
@@ -93,10 +104,9 @@ public enum KeyType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
final DSAPublicKey dsaKey = (DSAPublicKey) pk;
|
||||
buf.putString(sType)
|
||||
.putMPInt(dsaKey.getParams().getP()) // p
|
||||
buf.putMPInt(dsaKey.getParams().getP()) // p
|
||||
.putMPInt(dsaKey.getParams().getQ()) // q
|
||||
.putMPInt(dsaKey.getParams().getG()) // g
|
||||
.putMPInt(dsaKey.getY()); // y
|
||||
@@ -161,12 +171,11 @@ public enum KeyType {
|
||||
|
||||
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
final ECPublicKey ecdsa = (ECPublicKey) pk;
|
||||
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
|
||||
|
||||
buf.putString(sType)
|
||||
.putString(NISTP_CURVE)
|
||||
buf.putString(NISTP_CURVE)
|
||||
.putBytes(encoded);
|
||||
}
|
||||
|
||||
@@ -192,7 +201,7 @@ public enum KeyType {
|
||||
);
|
||||
}
|
||||
|
||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
|
||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512);
|
||||
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(p, ed25519);
|
||||
return new Ed25519PublicKey(publicSpec);
|
||||
|
||||
@@ -202,9 +211,9 @@ public enum KeyType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
EdDSAPublicKey key = (EdDSAPublicKey) pk;
|
||||
buf.putString(sType).putBytes(key.getAbyte());
|
||||
buf.putBytes(key.getAbyte());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,6 +222,44 @@ public enum KeyType {
|
||||
}
|
||||
},
|
||||
|
||||
/** Signed rsa certificate */
|
||||
RSA_CERT("ssh-rsa-cert-v01@openssh.com") {
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
return CertUtils.readPubKey(buf, RSA);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
CertUtils.writePubKeyContentsIntoBuffer(pk, RSA, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return CertUtils.isCertificateOfType(key, RSA);
|
||||
}
|
||||
},
|
||||
|
||||
/** Signed dsa certificate */
|
||||
DSA_CERT("ssh-dss-cert-v01@openssh.com") {
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
return CertUtils.readPubKey(buf, DSA);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
CertUtils.writePubKeyContentsIntoBuffer(pk, DSA, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return CertUtils.isCertificateOfType(key, DSA);
|
||||
}
|
||||
},
|
||||
|
||||
/** Unrecognized */
|
||||
UNKNOWN("unknown") {
|
||||
@Override
|
||||
@@ -226,6 +273,11 @@ public enum KeyType {
|
||||
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return false;
|
||||
@@ -244,7 +296,11 @@ public enum KeyType {
|
||||
public abstract PublicKey readPubKeyFromBuffer(Buffer<?> buf)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||
protected abstract void writePubKeyContentsIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
writePubKeyContentsIntoBuffer(pk, buf.putString(sType));
|
||||
}
|
||||
|
||||
protected abstract boolean isMyType(Key key);
|
||||
|
||||
@@ -266,4 +322,129 @@ public enum KeyType {
|
||||
public String toString() {
|
||||
return sType;
|
||||
}
|
||||
|
||||
static class CertUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends PublicKey> Certificate<T> readPubKey(Buffer<?> buf, KeyType innerKeyType) throws GeneralSecurityException {
|
||||
Certificate.Builder<T> builder = Certificate.getBuilder();
|
||||
|
||||
try {
|
||||
builder.nonce(buf.readBytes());
|
||||
builder.publicKey((T) innerKeyType.readPubKeyFromBuffer(buf));
|
||||
builder.serial(buf.readUInt64AsBigInteger());
|
||||
builder.type(buf.readUInt32());
|
||||
builder.id(buf.readString());
|
||||
builder.validPrincipals(unpackList(buf.readBytes()));
|
||||
builder.validAfter(dateFromEpoch(buf.readUInt64()));
|
||||
builder.validBefore(dateFromEpoch(buf.readUInt64()));
|
||||
builder.critOptions(unpackMap(buf.readBytes()));
|
||||
builder.extensions(unpackMap(buf.readBytes()));
|
||||
buf.readString(); // reserved
|
||||
builder.signatureKey(buf.readBytes());
|
||||
builder.signature(buf.readBytes());
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new GeneralSecurityException(be);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static void writePubKeyContentsIntoBuffer(PublicKey publicKey, KeyType innerKeyType, Buffer<?> buf) {
|
||||
Certificate<PublicKey> certificate = toCertificate(publicKey);
|
||||
buf.putBytes(certificate.getNonce());
|
||||
innerKeyType.writePubKeyContentsIntoBuffer(certificate.getKey(), buf);
|
||||
buf.putUInt64(certificate.getSerial())
|
||||
.putUInt32(certificate.getType())
|
||||
.putString(certificate.getId())
|
||||
.putBytes(packList(certificate.getValidPrincipals()))
|
||||
.putUInt64(epochFromDate(certificate.getValidAfter()))
|
||||
.putUInt64(epochFromDate(certificate.getValidBefore()))
|
||||
.putBytes(packMap(certificate.getCritOptions()))
|
||||
.putBytes(packMap(certificate.getExtensions()))
|
||||
.putString("") // reserved
|
||||
.putBytes(certificate.getSignatureKey())
|
||||
.putBytes(certificate.getSignature());
|
||||
}
|
||||
|
||||
static boolean isCertificateOfType(Key key, KeyType innerKeyType) {
|
||||
if (!(key instanceof Certificate)) {
|
||||
return false;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Key innerKey = ((Certificate<PublicKey>) key).getKey();
|
||||
return innerKeyType.isMyType(innerKey);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Certificate<PublicKey> toCertificate(PublicKey key) {
|
||||
if (!(key instanceof Certificate)) {
|
||||
throw new UnsupportedOperationException("Can't convert non-certificate key " +
|
||||
key.getAlgorithm() + " to certificate");
|
||||
}
|
||||
return ((Certificate<PublicKey>) key);
|
||||
}
|
||||
|
||||
private static Date dateFromEpoch(long seconds) {
|
||||
return new Date(seconds * 1000);
|
||||
}
|
||||
|
||||
private static long epochFromDate(Date date) {
|
||||
return date.getTime() / 1000;
|
||||
}
|
||||
|
||||
private static String unpackString(byte[] packedString) throws BufferException {
|
||||
if (packedString.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return new Buffer.PlainBuffer(packedString).readString();
|
||||
}
|
||||
|
||||
private static List<String> unpackList(byte[] packedString) throws BufferException {
|
||||
List<String> list = new ArrayList<String>();
|
||||
Buffer<?> buf = new Buffer.PlainBuffer(packedString);
|
||||
while (buf.available() > 0) {
|
||||
list.add(buf.readString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Map<String, String> unpackMap(byte[] packedString) throws BufferException {
|
||||
Map<String, String> map = new LinkedHashMap<String, String>();
|
||||
Buffer<?> buf = new Buffer.PlainBuffer(packedString);
|
||||
while (buf.available() > 0) {
|
||||
String name = buf.readString();
|
||||
String data = unpackString(buf.readStringAsBytes());
|
||||
map.put(name, data);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static byte[] packString(String data) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return "".getBytes();
|
||||
}
|
||||
return new Buffer.PlainBuffer().putString(data).getCompactData();
|
||||
}
|
||||
|
||||
private static byte[] packList(Iterable<String> strings) {
|
||||
Buffer<?> buf = new Buffer.PlainBuffer();
|
||||
for (String string : strings) {
|
||||
buf.putString(string);
|
||||
}
|
||||
return buf.getCompactData();
|
||||
}
|
||||
|
||||
private static byte[] packMap(Map<String, String> map) {
|
||||
Buffer<?> buf = new Buffer.PlainBuffer();
|
||||
List<String> keys = new ArrayList<String>(map.keySet());
|
||||
Collections.sort(keys);
|
||||
for (String key : keys) {
|
||||
buf.putString(key);
|
||||
String value = map.get(key);
|
||||
buf.putString(packString(value));
|
||||
}
|
||||
return buf.getCompactData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ public class SecurityUtils {
|
||||
throw new SSHRuntimeException("Failed to register BouncyCastle as the defaut JCE provider");
|
||||
}
|
||||
}
|
||||
registrationDone = true;
|
||||
}
|
||||
registrationDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import net.schmizz.sshj.common.LoggerFactory;
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.concurrent.ExceptionChainer;
|
||||
import org.slf4j.Logger;
|
||||
@@ -68,8 +67,11 @@ public class StreamCopier {
|
||||
}
|
||||
|
||||
public StreamCopier listener(Listener listener) {
|
||||
if (listener == null) listener = NULL_LISTENER;
|
||||
this.listener = listener;
|
||||
if (listener == null) {
|
||||
this.listener = NULL_LISTENER;
|
||||
} else {
|
||||
this.listener = listener;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -126,11 +128,13 @@ public class StreamCopier {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
if (length == -1) {
|
||||
while ((read = in.read(buf)) != -1)
|
||||
count = write(buf, count, read);
|
||||
while ((read = in.read(buf)) != -1) {
|
||||
count += write(buf, count, read);
|
||||
}
|
||||
} else {
|
||||
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1)
|
||||
count = write(buf, count, read);
|
||||
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1) {
|
||||
count += write(buf, count, read);
|
||||
}
|
||||
}
|
||||
|
||||
if (!keepFlushing)
|
||||
@@ -146,14 +150,13 @@ public class StreamCopier {
|
||||
return count;
|
||||
}
|
||||
|
||||
private long write(byte[] buf, long count, int read)
|
||||
private long write(byte[] buf, long curPos, int len)
|
||||
throws IOException {
|
||||
out.write(buf, 0, read);
|
||||
count += read;
|
||||
out.write(buf, 0, len);
|
||||
if (keepFlushing)
|
||||
out.flush();
|
||||
listener.reportProgress(count);
|
||||
return count;
|
||||
listener.reportProgress(curPos + len);
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -126,10 +126,9 @@ public class ConnectionImpl
|
||||
@Override
|
||||
public void handle(Message msg, SSHPacket buf)
|
||||
throws SSHException {
|
||||
if (msg.in(91, 100))
|
||||
if (msg.in(91, 100)) {
|
||||
getChannel(buf).handle(msg, buf);
|
||||
|
||||
else if (msg.in(80, 90))
|
||||
} else if (msg.in(80, 90)) {
|
||||
switch (msg) {
|
||||
case REQUEST_SUCCESS:
|
||||
gotGlobalReqResponse(buf);
|
||||
@@ -142,10 +141,11 @@ public class ConnectionImpl
|
||||
break;
|
||||
default:
|
||||
super.handle(msg, buf);
|
||||
break;
|
||||
}
|
||||
|
||||
else
|
||||
} else {
|
||||
super.handle(msg, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,11 +174,11 @@ public class ConnectionImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void join()
|
||||
throws InterruptedException {
|
||||
public void join() throws InterruptedException {
|
||||
synchronized (internalSynchronizer) {
|
||||
while (!channels.isEmpty())
|
||||
while (!channels.isEmpty()) {
|
||||
internalSynchronizer.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -51,6 +52,8 @@ public abstract class AbstractChannel
|
||||
private final int id;
|
||||
/** Remote recipient ID */
|
||||
private int recipient;
|
||||
/** Remote character set */
|
||||
private final Charset remoteCharset;
|
||||
|
||||
private boolean eof = false;
|
||||
|
||||
@@ -78,12 +81,16 @@ public abstract class AbstractChannel
|
||||
private volatile boolean autoExpand = false;
|
||||
|
||||
protected AbstractChannel(Connection conn, String type) {
|
||||
this(conn, type, null);
|
||||
}
|
||||
protected AbstractChannel(Connection conn, String type, Charset remoteCharset) {
|
||||
this.conn = conn;
|
||||
this.loggerFactory = conn.getTransport().getConfig().getLoggerFactory();
|
||||
this.type = type;
|
||||
this.log = loggerFactory.getLogger(getClass());
|
||||
this.trans = conn.getTransport();
|
||||
|
||||
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
|
||||
id = conn.nextID();
|
||||
|
||||
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize(), loggerFactory);
|
||||
@@ -135,6 +142,11 @@ public abstract class AbstractChannel
|
||||
return recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Charset getRemoteCharset() {
|
||||
return remoteCharset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoteMaxPacketSize() {
|
||||
return rwin.getMaxPacketSize();
|
||||
@@ -189,7 +201,7 @@ public abstract class AbstractChannel
|
||||
|
||||
default:
|
||||
gotUnknown(msg, buf);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +341,8 @@ public abstract class AbstractChannel
|
||||
|
||||
protected void gotUnknown(Message msg, SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
log.warn("Got unknown packet with type {}", msg);
|
||||
|
||||
}
|
||||
|
||||
protected void handleRequest(String reqType, SSHPacket buf)
|
||||
|
||||
@@ -24,6 +24,7 @@ import net.schmizz.sshj.transport.TransportException;
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -123,6 +124,9 @@ public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
|
||||
*/
|
||||
int getRecipient();
|
||||
|
||||
/** @return the character set used to communicate with the remote machine for certain strings (like paths). */
|
||||
Charset getRemoteCharset();
|
||||
|
||||
/**
|
||||
* @return the maximum packet size as specified by the remote end.
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,6 @@ import net.schmizz.sshj.connection.ConnectionException;
|
||||
import net.schmizz.sshj.transport.Transport;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -27,9 +27,7 @@ import java.io.OutputStream;
|
||||
* {@link OutputStream} for channels. Buffers data upto the remote window's maximum packet size. Data can also be
|
||||
* flushed via {@link #flush()} and is also flushed on {@link #close()}.
|
||||
*/
|
||||
public final class ChannelOutputStream
|
||||
extends OutputStream
|
||||
implements ErrorNotifiable {
|
||||
public final class ChannelOutputStream extends OutputStream implements ErrorNotifiable {
|
||||
|
||||
private final Channel chan;
|
||||
private final Transport trans;
|
||||
@@ -56,8 +54,7 @@ public final class ChannelOutputStream
|
||||
dataOffset = packet.wpos();
|
||||
}
|
||||
|
||||
int write(byte[] data, int off, int len)
|
||||
throws TransportException, ConnectionException {
|
||||
int write(byte[] data, int off, int len) throws TransportException, ConnectionException {
|
||||
final int bufferSize = packet.wpos() - dataOffset;
|
||||
if (bufferSize >= win.getMaxPacketSize()) {
|
||||
flush(bufferSize, true);
|
||||
@@ -69,15 +66,13 @@ public final class ChannelOutputStream
|
||||
}
|
||||
}
|
||||
|
||||
boolean flush(boolean canAwaitExpansion)
|
||||
throws TransportException, ConnectionException {
|
||||
boolean flush(boolean canAwaitExpansion) throws TransportException, ConnectionException {
|
||||
return flush(packet.wpos() - dataOffset, canAwaitExpansion);
|
||||
}
|
||||
|
||||
boolean flush(int bufferSize, boolean canAwaitExpansion)
|
||||
throws TransportException, ConnectionException {
|
||||
while (bufferSize > 0) {
|
||||
|
||||
boolean flush(int bufferSize, boolean canAwaitExpansion) throws TransportException, ConnectionException {
|
||||
int dataLeft = bufferSize;
|
||||
while (dataLeft > 0) {
|
||||
long remoteWindowSize = win.getSize();
|
||||
if (remoteWindowSize == 0) {
|
||||
if (canAwaitExpansion) {
|
||||
@@ -91,7 +86,7 @@ public final class ChannelOutputStream
|
||||
// a) how much data we have
|
||||
// b) the max packet size
|
||||
// c) what the current window size will allow
|
||||
final int writeNow = Math.min(bufferSize, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
|
||||
final int writeNow = Math.min(dataLeft, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
|
||||
|
||||
packet.wpos(headerOffset);
|
||||
packet.putMessageID(Message.CHANNEL_DATA);
|
||||
@@ -99,7 +94,7 @@ public final class ChannelOutputStream
|
||||
packet.putUInt32(writeNow);
|
||||
packet.wpos(dataOffset + writeNow);
|
||||
|
||||
final int leftOverBytes = bufferSize - writeNow;
|
||||
final int leftOverBytes = dataLeft - writeNow;
|
||||
if (leftOverBytes > 0) {
|
||||
leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes);
|
||||
}
|
||||
@@ -115,7 +110,7 @@ public final class ChannelOutputStream
|
||||
leftOvers.clear();
|
||||
}
|
||||
|
||||
bufferSize = leftOverBytes;
|
||||
dataLeft = leftOverBytes;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -140,10 +135,12 @@ public final class ChannelOutputStream
|
||||
public synchronized void write(final byte[] data, int off, int len)
|
||||
throws IOException {
|
||||
checkClose();
|
||||
while (len > 0) {
|
||||
final int n = buffer.write(data, off, len);
|
||||
off += n;
|
||||
len -= n;
|
||||
int length = len;
|
||||
int offset = off;
|
||||
while (length > 0) {
|
||||
final int n = buffer.write(data, offset, len);
|
||||
offset += n;
|
||||
length -= n;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,8 +179,7 @@ public final class ChannelOutputStream
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public synchronized void flush()
|
||||
throws IOException {
|
||||
public synchronized void flush() throws IOException {
|
||||
checkClose();
|
||||
buffer.flush(true);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import net.schmizz.sshj.connection.channel.Channel;
|
||||
import net.schmizz.sshj.connection.channel.OpenFailException;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Base class for direct channels whose open is initated by the client. */
|
||||
@@ -41,6 +42,15 @@ public abstract class AbstractDirectChannel
|
||||
conn.attach(this);
|
||||
}
|
||||
|
||||
protected AbstractDirectChannel(Connection conn, String type, Charset remoteCharset) {
|
||||
super(conn, type, remoteCharset);
|
||||
|
||||
/*
|
||||
* We expect to receive channel open confirmation/rejection and want to be able to next this packet.
|
||||
*/
|
||||
conn.attach(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open()
|
||||
throws ConnectionException, TransportException {
|
||||
|
||||
@@ -22,6 +22,7 @@ import net.schmizz.sshj.connection.channel.ChannelInputStream;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -47,6 +48,10 @@ public class SessionChannel
|
||||
super(conn, "session");
|
||||
}
|
||||
|
||||
public SessionChannel(Connection conn, Charset remoteCharset) {
|
||||
super(conn, "session", remoteCharset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allocateDefaultPTY()
|
||||
throws ConnectionException, TransportException {
|
||||
@@ -93,7 +98,7 @@ public class SessionChannel
|
||||
throws ConnectionException, TransportException {
|
||||
checkReuse();
|
||||
log.debug("Will request to exec `{}`", command);
|
||||
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
|
||||
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command, getRemoteCharset()))
|
||||
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
|
||||
usedUp = true;
|
||||
return this;
|
||||
|
||||
@@ -21,7 +21,6 @@ import net.schmizz.sshj.connection.channel.Channel;
|
||||
import net.schmizz.sshj.connection.channel.OpenFailException;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ public class RemotePortForwarder
|
||||
// Addresses match up
|
||||
return true;
|
||||
}
|
||||
if ("localhost".equals(address) && (channelForward.address.equals("127.0.0.1") || channelForward.address.equals("::1"))) {
|
||||
if ("localhost".equals(address) && ("127.0.0.1".equals(channelForward.address) || "::1".equals(channelForward.address))) {
|
||||
// Localhost special case.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,17 +18,17 @@ package net.schmizz.sshj.sftp;
|
||||
import net.schmizz.concurrent.Promise;
|
||||
import net.schmizz.sshj.common.SSHException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PacketReader
|
||||
extends Thread {
|
||||
public class PacketReader extends Thread {
|
||||
|
||||
/** Logger */
|
||||
/**
|
||||
* Logger
|
||||
*/
|
||||
private final Logger log;
|
||||
|
||||
private final InputStream in;
|
||||
@@ -64,7 +64,7 @@ public class PacketReader
|
||||
| lenBuf[3] & 0x000000ffL);
|
||||
|
||||
if (len > SFTPPacket.MAX_SIZE) {
|
||||
throw new SSHException(String.format("Indicated packet length %d too large", len));
|
||||
throw new SSHException(String.format("Indicated packet length %d too large", len));
|
||||
}
|
||||
|
||||
return (int) len;
|
||||
@@ -100,7 +100,7 @@ public class PacketReader
|
||||
log.debug("Received {} packet", resp.getType());
|
||||
if (promise == null)
|
||||
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
|
||||
+ ", no such request was made");
|
||||
+ ", no such request was made");
|
||||
else
|
||||
promise.deliver(resp);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class PathHelper {
|
||||
if (path.equals(pathSep))
|
||||
return getComponents("", "");
|
||||
|
||||
if (path.isEmpty() || path.equals(".") || path.equals("." + pathSep))
|
||||
if (path.isEmpty() || ".".equals(path) || ("." + pathSep).equals(path))
|
||||
return getComponents(getDotDir());
|
||||
|
||||
final String withoutTrailSep = trimTrailingSeparator(path);
|
||||
@@ -81,7 +81,7 @@ public class PathHelper {
|
||||
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep);
|
||||
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length());
|
||||
|
||||
if (name.equals(".") || name.equals("..")) {
|
||||
if (".".equals(name) || "..".equals(name)) {
|
||||
return getComponents(canonicalizer.canonicalize(path));
|
||||
} else {
|
||||
return getComponents(parent, name);
|
||||
|
||||
@@ -32,6 +32,7 @@ public class RemoteDirectory
|
||||
public List<RemoteResourceInfo> scan(RemoteResourceFilter filter)
|
||||
throws IOException {
|
||||
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
|
||||
// TODO: Remove GOTO!
|
||||
loop:
|
||||
for (; ; ) {
|
||||
final Response res = requester.request(newRequest(PacketType.READDIR))
|
||||
@@ -41,13 +42,14 @@ public class RemoteDirectory
|
||||
case NAME:
|
||||
final int count = res.readUInt32AsInt();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final String name = res.readString();
|
||||
final String name = res.readString(requester.sub.getRemoteCharset());
|
||||
res.readString(); // long name - IGNORED - shdve never been in the protocol
|
||||
final FileAttributes attrs = res.readFileAttributes();
|
||||
final PathComponents comps = requester.getPathHelper().getComponents(path, name);
|
||||
final RemoteResourceInfo inf = new RemoteResourceInfo(comps, attrs);
|
||||
if (!(name.equals(".") || name.equals("..")) && (filter == null || filter.accept(inf)))
|
||||
if (!(".".equals(name) || "..".equals(name)) && (filter == null || filter.accept(inf))) {
|
||||
rri.add(inf);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -81,10 +81,10 @@ public class RemoteFile
|
||||
protected Promise<Response, SFTPException> asyncWrite(long fileOffset, byte[] data, int off, int len)
|
||||
throws IOException {
|
||||
return requester.request(newRequest(PacketType.WRITE)
|
||||
.putUInt64(fileOffset)
|
||||
// TODO The SFTP spec claims this field is unneeded...? See #187
|
||||
.putUInt32(len)
|
||||
.putRawBytes(data, off, len)
|
||||
.putUInt64(fileOffset)
|
||||
// TODO The SFTP spec claims this field is unneeded...? See #187
|
||||
.putUInt32(len)
|
||||
.putRawBytes(data, off, len)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -194,10 +194,10 @@ public class RemoteFile
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
final long fileLength = length();
|
||||
final Long previousFileOffset = fileOffset;
|
||||
fileOffset = Math.min(fileOffset + n, fileLength);
|
||||
return fileOffset - previousFileOffset;
|
||||
final long fileLength = length();
|
||||
final Long previousFileOffset = fileOffset;
|
||||
fileOffset = Math.min(fileOffset + n, fileLength);
|
||||
return fileOffset - previousFileOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -341,7 +341,7 @@ public class RemoteFile
|
||||
public int available() throws IOException {
|
||||
boolean lastRead = true;
|
||||
while (!eof && (pending.available() <= 0) && lastRead) {
|
||||
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
|
||||
lastRead = retrieveUnconfirmedRead(false /*blocking*/);
|
||||
}
|
||||
return pending.available();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -20,7 +20,7 @@ import net.schmizz.sshj.common.Buffer;
|
||||
public final class Response
|
||||
extends SFTPPacket<Response> {
|
||||
|
||||
public static enum StatusCode {
|
||||
public enum StatusCode {
|
||||
UNKNOWN(-1),
|
||||
OK(0),
|
||||
EOF(1),
|
||||
@@ -122,6 +122,7 @@ public final class Response
|
||||
return ensurePacketTypeIs(PacketType.STATUS).ensureStatusIs(StatusCode.OK);
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
||||
public Response ensureStatusIs(StatusCode acceptable)
|
||||
throws SFTPException {
|
||||
final StatusCode sc = readStatusCode();
|
||||
|
||||
@@ -19,7 +19,6 @@ import net.schmizz.sshj.xfer.FilePermission;
|
||||
import net.schmizz.sshj.xfer.LocalDestFile;
|
||||
import net.schmizz.sshj.xfer.LocalSourceFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
import net.schmizz.concurrent.Promise;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.common.LoggerFactory;
|
||||
import net.schmizz.sshj.common.SSHException;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||
@@ -25,6 +26,7 @@ import org.slf4j.Logger;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -137,7 +139,7 @@ public class SFTPEngine
|
||||
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
|
||||
throws IOException {
|
||||
final byte[] handle = doRequest(
|
||||
newRequest(PacketType.OPEN).putString(path).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
|
||||
newRequest(PacketType.OPEN).putString(path, sub.getRemoteCharset()).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
|
||||
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
|
||||
return new RemoteFile(this, path, handle);
|
||||
}
|
||||
@@ -155,7 +157,7 @@ public class SFTPEngine
|
||||
public RemoteDirectory openDir(String path)
|
||||
throws IOException {
|
||||
final byte[] handle = doRequest(
|
||||
newRequest(PacketType.OPENDIR).putString(path)
|
||||
newRequest(PacketType.OPENDIR).putString(path, sub.getRemoteCharset())
|
||||
).ensurePacketTypeIs(PacketType.HANDLE).readBytes();
|
||||
return new RemoteDirectory(this, path, handle);
|
||||
}
|
||||
@@ -163,7 +165,7 @@ public class SFTPEngine
|
||||
public void setAttributes(String path, FileAttributes attrs)
|
||||
throws IOException {
|
||||
doRequest(
|
||||
newRequest(PacketType.SETSTAT).putString(path).putFileAttributes(attrs)
|
||||
newRequest(PacketType.SETSTAT).putString(path, sub.getRemoteCharset()).putFileAttributes(attrs)
|
||||
).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
@@ -173,13 +175,13 @@ public class SFTPEngine
|
||||
throw new SFTPException("READLINK is not supported in SFTPv" + operativeVersion);
|
||||
return readSingleName(
|
||||
doRequest(
|
||||
newRequest(PacketType.READLINK).putString(path)
|
||||
));
|
||||
newRequest(PacketType.READLINK).putString(path, sub.getRemoteCharset())
|
||||
), sub.getRemoteCharset());
|
||||
}
|
||||
|
||||
public void makeDir(String path, FileAttributes attrs)
|
||||
throws IOException {
|
||||
doRequest(newRequest(PacketType.MKDIR).putString(path).putFileAttributes(attrs)).ensureStatusPacketIsOK();
|
||||
doRequest(newRequest(PacketType.MKDIR).putString(path, sub.getRemoteCharset()).putFileAttributes(attrs)).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
public void makeDir(String path)
|
||||
@@ -192,21 +194,21 @@ public class SFTPEngine
|
||||
if (operativeVersion < 3)
|
||||
throw new SFTPException("SYMLINK is not supported in SFTPv" + operativeVersion);
|
||||
doRequest(
|
||||
newRequest(PacketType.SYMLINK).putString(linkpath).putString(targetpath)
|
||||
newRequest(PacketType.SYMLINK).putString(linkpath, sub.getRemoteCharset()).putString(targetpath, sub.getRemoteCharset())
|
||||
).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
public void remove(String filename)
|
||||
throws IOException {
|
||||
doRequest(
|
||||
newRequest(PacketType.REMOVE).putString(filename)
|
||||
newRequest(PacketType.REMOVE).putString(filename, sub.getRemoteCharset())
|
||||
).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
public void removeDir(String path)
|
||||
throws IOException {
|
||||
doRequest(
|
||||
newRequest(PacketType.RMDIR).putString(path)
|
||||
newRequest(PacketType.RMDIR).putString(path, sub.getRemoteCharset())
|
||||
).ensureStatusIs(Response.StatusCode.OK);
|
||||
}
|
||||
|
||||
@@ -225,7 +227,7 @@ public class SFTPEngine
|
||||
if (operativeVersion < 1)
|
||||
throw new SFTPException("RENAME is not supported in SFTPv" + operativeVersion);
|
||||
doRequest(
|
||||
newRequest(PacketType.RENAME).putString(oldPath).putString(newPath)
|
||||
newRequest(PacketType.RENAME).putString(oldPath, sub.getRemoteCharset()).putString(newPath, sub.getRemoteCharset())
|
||||
).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
@@ -233,8 +235,8 @@ public class SFTPEngine
|
||||
throws IOException {
|
||||
return readSingleName(
|
||||
doRequest(
|
||||
newRequest(PacketType.REALPATH).putString(path)
|
||||
));
|
||||
newRequest(PacketType.REALPATH).putString(path, sub.getRemoteCharset())
|
||||
), sub.getRemoteCharset());
|
||||
}
|
||||
|
||||
public void setTimeoutMs(int timeoutMs) {
|
||||
@@ -258,20 +260,32 @@ public class SFTPEngine
|
||||
|
||||
protected FileAttributes stat(PacketType pt, String path)
|
||||
throws IOException {
|
||||
return doRequest(newRequest(pt).putString(path))
|
||||
return doRequest(newRequest(pt).putString(path, sub.getRemoteCharset()))
|
||||
.ensurePacketTypeIs(PacketType.ATTRS)
|
||||
.readFileAttributes();
|
||||
}
|
||||
|
||||
protected static String readSingleName(Response res)
|
||||
private static byte[] readSingleNameAsBytes(Response res)
|
||||
throws IOException {
|
||||
res.ensurePacketTypeIs(PacketType.NAME);
|
||||
if (res.readUInt32AsInt() == 1)
|
||||
return res.readString();
|
||||
return res.readStringAsBytes();
|
||||
else
|
||||
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
|
||||
}
|
||||
|
||||
/** Using UTF-8 */
|
||||
protected static String readSingleName(Response res)
|
||||
throws IOException {
|
||||
return readSingleName(res, IOUtils.UTF8);
|
||||
}
|
||||
|
||||
/** Using any character set */
|
||||
protected static String readSingleName(Response res, Charset charset)
|
||||
throws IOException {
|
||||
return new String(readSingleNameAsBytes(res), charset);
|
||||
}
|
||||
|
||||
protected synchronized void transmit(SFTPPacket<Request> payload)
|
||||
throws IOException {
|
||||
final int len = payload.available();
|
||||
|
||||
@@ -60,14 +60,12 @@ public class SFTPFileTransfer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(LocalSourceFile localFile, String remotePath)
|
||||
throws IOException {
|
||||
public void upload(LocalSourceFile localFile, String remotePath) throws IOException {
|
||||
new Uploader(localFile, remotePath).upload(getTransferListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, LocalDestFile dest)
|
||||
throws IOException {
|
||||
public void download(String source, LocalDestFile dest) throws IOException {
|
||||
final PathComponents pathComponents = engine.getPathHelper().getComponents(source);
|
||||
final FileAttributes attributes = engine.stat(source);
|
||||
new Downloader().download(getTransferListener(), new RemoteResourceInfo(pathComponents, attributes), dest);
|
||||
@@ -91,10 +89,10 @@ public class SFTPFileTransfer
|
||||
|
||||
private class Downloader {
|
||||
|
||||
@SuppressWarnings("PMD.MissingBreakInSwitch")
|
||||
private void download(final TransferListener listener,
|
||||
final RemoteResourceInfo remote,
|
||||
final LocalDestFile local)
|
||||
throws IOException {
|
||||
final LocalDestFile local) throws IOException {
|
||||
final LocalDestFile adjustedFile;
|
||||
switch (remote.getAttributes().getType()) {
|
||||
case DIRECTORY:
|
||||
@@ -104,8 +102,7 @@ public class SFTPFileTransfer
|
||||
log.warn("Server did not supply information about the type of file at `{}` " +
|
||||
"-- assuming it is a regular file!", remote.getPath());
|
||||
case REGULAR:
|
||||
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()),
|
||||
remote, local);
|
||||
adjustedFile = downloadFile(listener.file(remote.getName(), remote.getAttributes().getSize()), remote, local);
|
||||
break;
|
||||
default:
|
||||
throw new IOException(remote + " is not a regular file or directory");
|
||||
|
||||
@@ -84,7 +84,7 @@ public abstract class AbstractSignature
|
||||
| sig[i++] & 0x000000ff;
|
||||
byte[] newSig = new byte[j];
|
||||
System.arraycopy(sig, i, newSig, 0, j);
|
||||
sig = newSig;
|
||||
return newSig;
|
||||
}
|
||||
return sig;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class SignatureECDSA
|
||||
throw new SSHRuntimeException(String.format("Signature :: ecdsa-sha2-nistp256 expected, got %s", algo));
|
||||
}
|
||||
final int rsLen = sigbuf.readUInt32AsInt();
|
||||
if (!(sigbuf.available() == rsLen)) {
|
||||
if (sigbuf.available() != rsLen) {
|
||||
throw new SSHRuntimeException("Invalid key length");
|
||||
}
|
||||
r = sigbuf.readBytes();
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import net.schmizz.sshj.common.*;
|
||||
import net.schmizz.sshj.transport.Transport;
|
||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
import net.schmizz.sshj.transport.compression.Compression;
|
||||
import net.schmizz.sshj.transport.mac.MAC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/** Decodes packets from the SSH binary protocol per the current algorithms. */
|
||||
final class Decoder
|
||||
|
||||
@@ -39,19 +39,6 @@ final class Encoder
|
||||
log = loggerFactory.getLogger(getClass());
|
||||
}
|
||||
|
||||
private SSHPacket checkHeaderSpace(SSHPacket buffer) {
|
||||
if (buffer.rpos() < 5) {
|
||||
log.warn("Performance cost: when sending a packet, ensure that "
|
||||
+ "5 bytes are available in front of the buffer");
|
||||
SSHPacket nb = new SSHPacket(buffer.available() + 5);
|
||||
nb.rpos(5);
|
||||
nb.wpos(5);
|
||||
nb.putBuffer(buffer);
|
||||
buffer = nb;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void compress(SSHPacket buffer) {
|
||||
compression.compress(buffer);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import net.schmizz.sshj.transport.mac.MAC;
|
||||
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
||||
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
@@ -158,6 +157,7 @@ final class KeyExchanger
|
||||
"Key exchange packet received when key exchange was not ongoing");
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
||||
private static void ensureReceivedMatchesExpected(Message got, Message expected)
|
||||
throws TransportException {
|
||||
if (got != expected)
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
@@ -33,7 +33,9 @@ import java.io.OutputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/** A thread-safe {@link Transport} implementation. */
|
||||
/**
|
||||
* A thread-safe {@link Transport} implementation.
|
||||
*/
|
||||
public final class TransportImpl
|
||||
implements Transport, DisconnectListener {
|
||||
|
||||
@@ -49,11 +51,11 @@ public final class TransportImpl
|
||||
static final class ConnInfo {
|
||||
|
||||
final String host;
|
||||
|
||||
final int port;
|
||||
final InputStream in;
|
||||
final OutputStream out;
|
||||
public ConnInfo(String host, int port, InputStream in, OutputStream out) {
|
||||
|
||||
ConnInfo(String host, int port, InputStream in, OutputStream out) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.in = in;
|
||||
@@ -88,24 +90,32 @@ public final class TransportImpl
|
||||
|
||||
private final Event<TransportException> close;
|
||||
|
||||
/** Client version identification string */
|
||||
/**
|
||||
* Client version identification string
|
||||
*/
|
||||
private final String clientID;
|
||||
|
||||
private volatile int timeoutMs = 30 * 1000; // Crazy long, but it was the original default
|
||||
|
||||
private volatile boolean authed = false;
|
||||
|
||||
/** Currently active service e.g. UserAuthService, ConnectionService */
|
||||
/**
|
||||
* Currently active service e.g. UserAuthService, ConnectionService
|
||||
*/
|
||||
private volatile Service service;
|
||||
|
||||
private DisconnectListener disconnectListener;
|
||||
|
||||
private ConnInfo connInfo;
|
||||
|
||||
/** Server version identification string */
|
||||
/**
|
||||
* Server version identification string
|
||||
*/
|
||||
private String serverID;
|
||||
|
||||
/** Message identifier of last packet received */
|
||||
/**
|
||||
* Message identifier of last packet received
|
||||
*/
|
||||
private Message msg;
|
||||
|
||||
private final ReentrantLock writeLock = new ReentrantLock();
|
||||
@@ -115,9 +125,9 @@ public final class TransportImpl
|
||||
this.loggerFactory = config.getLoggerFactory();
|
||||
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
|
||||
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
|
||||
this.nullService = new NullService(this);
|
||||
this.nullService = new NullService(this);
|
||||
this.service = nullService;
|
||||
this.log = loggerFactory.getLogger(getClass());
|
||||
this.log = loggerFactory.getLogger(getClass());
|
||||
this.disconnectListener = this;
|
||||
this.reader = new Reader(this);
|
||||
this.encoder = new Encoder(config.getRandomFactory().create(), writeLock, loggerFactory);
|
||||
@@ -138,7 +148,7 @@ public final class TransportImpl
|
||||
this.serviceAccept = new Event<TransportException>("service accept", TransportException.chainer, loggerFactory);
|
||||
this.close = new Event<TransportException>("transport close", TransportException.chainer, loggerFactory);
|
||||
this.log = loggerFactory.getLogger(getClass());
|
||||
this.nullService = new NullService(this);
|
||||
this.nullService = new NullService(this);
|
||||
this.service = nullService;
|
||||
this.disconnectListener = this;
|
||||
this.reader = new Reader(this);
|
||||
@@ -186,14 +196,18 @@ public final class TransportImpl
|
||||
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||
while ((serverID = readIdentification(buf)).isEmpty()) {
|
||||
int b = connInfo.in.read();
|
||||
if (b == -1)
|
||||
if (b == -1) {
|
||||
log.error("Received end of connection, but no identification received. ");
|
||||
throw new TransportException("Server closed connection during identification exchange");
|
||||
|
||||
}
|
||||
buf.putByte((byte) b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the server identification string.
|
||||
*
|
||||
* @throws IOException If there was an error writing to the outputstream.
|
||||
*/
|
||||
private void sendClientIdent() throws IOException {
|
||||
@@ -212,9 +226,7 @@ public final class TransportImpl
|
||||
* This is not efficient but is only done once.
|
||||
*
|
||||
* @param buffer The buffer to read from.
|
||||
*
|
||||
* @return empty string if full ident string has not yet been received
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private String readIdentification(Buffer.PlainBuffer buffer)
|
||||
@@ -226,7 +238,7 @@ public final class TransportImpl
|
||||
|
||||
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
|
||||
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
|
||||
"Server does not support SSHv2, identified as: " + ident);
|
||||
"Server does not support SSHv2, identified as: " + ident);
|
||||
|
||||
return ident;
|
||||
}
|
||||
@@ -337,7 +349,6 @@ public final class TransportImpl
|
||||
* Sends a service request for the specified service
|
||||
*
|
||||
* @param serviceName name of the service being requested
|
||||
*
|
||||
* @throws TransportException if there is an error while sending the request
|
||||
*/
|
||||
private void sendServiceRequest(String serviceName)
|
||||
@@ -456,9 +467,9 @@ public final class TransportImpl
|
||||
log.debug("Sending SSH_MSG_DISCONNECT: reason=[{}], msg=[{}]", reason, message);
|
||||
try {
|
||||
write(new SSHPacket(Message.DISCONNECT)
|
||||
.putUInt32(reason.toInt())
|
||||
.putString(message)
|
||||
.putString(""));
|
||||
.putUInt32(reason.toInt())
|
||||
.putString(message)
|
||||
.putString(""));
|
||||
} catch (IOException worthless) {
|
||||
log.debug("Error writing packet: {}", worthless.toString());
|
||||
}
|
||||
@@ -476,7 +487,6 @@ public final class TransportImpl
|
||||
*
|
||||
* @param msg the message identifer
|
||||
* @param buf buffer containg rest of the packet
|
||||
*
|
||||
* @throws SSHException if an error occurs during handling (unrecoverable)
|
||||
*/
|
||||
@Override
|
||||
@@ -494,32 +504,27 @@ public final class TransportImpl
|
||||
|
||||
else
|
||||
switch (msg) {
|
||||
case DISCONNECT: {
|
||||
case DISCONNECT:
|
||||
gotDisconnect(buf);
|
||||
break;
|
||||
}
|
||||
case IGNORE: {
|
||||
case IGNORE:
|
||||
log.debug("Received SSH_MSG_IGNORE");
|
||||
break;
|
||||
}
|
||||
case UNIMPLEMENTED: {
|
||||
case UNIMPLEMENTED:
|
||||
gotUnimplemented(buf);
|
||||
break;
|
||||
}
|
||||
case DEBUG: {
|
||||
case DEBUG:
|
||||
gotDebug(buf);
|
||||
break;
|
||||
}
|
||||
case SERVICE_ACCEPT: {
|
||||
case SERVICE_ACCEPT:
|
||||
gotServiceAccept();
|
||||
break;
|
||||
}
|
||||
case USERAUTH_BANNER: {
|
||||
case USERAUTH_BANNER:
|
||||
log.debug("Received USERAUTH_BANNER");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sendUnimplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +557,7 @@ public final class TransportImpl
|
||||
try {
|
||||
if (!serviceAccept.hasWaiters())
|
||||
throw new TransportException(DisconnectReason.PROTOCOL_ERROR,
|
||||
"Got a service accept notification when none was awaited");
|
||||
"Got a service accept notification when none was awaited");
|
||||
serviceAccept.set();
|
||||
} finally {
|
||||
serviceAccept.unlock();
|
||||
@@ -563,7 +568,6 @@ public final class TransportImpl
|
||||
* Got an SSH_MSG_UNIMPLEMENTED, so lets see where we're at and act accordingly.
|
||||
*
|
||||
* @param packet The 'unimplemented' packet received
|
||||
*
|
||||
* @throws TransportException
|
||||
*/
|
||||
private void gotUnimplemented(SSHPacket packet)
|
||||
|
||||
@@ -46,10 +46,12 @@ public class NoneCipher
|
||||
|
||||
@Override
|
||||
public void init(Mode mode, byte[] bytes, byte[] bytes1) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int inputOffset, int inputLen) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,14 +16,10 @@
|
||||
package net.schmizz.sshj.transport.kex;
|
||||
|
||||
import net.schmizz.sshj.transport.digest.SHA256;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
public class Curve25519SHA256 extends AbstractDHG {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Curve25519SHA256.class);
|
||||
|
||||
/** Named factory for Curve25519SHA256 key exchange */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||
|
||||
@@ -40,7 +40,7 @@ public class DH extends DHBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
|
||||
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
|
||||
if (!(params instanceof DHParameterSpec)) {
|
||||
throw new SSHRuntimeException("Wrong algorithm parameters for Diffie Hellman");
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
abstract class DHBase {
|
||||
public abstract class DHBase {
|
||||
protected final KeyPairGenerator generator;
|
||||
protected final KeyAgreement agreement;
|
||||
|
||||
@@ -44,7 +44,7 @@ abstract class DHBase {
|
||||
|
||||
abstract void computeK(byte[] f) throws GeneralSecurityException;
|
||||
|
||||
protected abstract void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException;
|
||||
public abstract void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException;
|
||||
|
||||
void setE(byte[] e) {
|
||||
this.e = e;
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.security.GeneralSecurityException;
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</a>
|
||||
*
|
||||
* TODO refactor away the (unneeded) class
|
||||
* @deprecated Replaced by {@link com.hierynomus.sshj.transport.kex.DHG} with {@link com.hierynomus.sshj.transport.kex.DHGroups}
|
||||
*/
|
||||
public class DHG1
|
||||
extends AbstractDHG {
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.security.GeneralSecurityException;
|
||||
* <p/>
|
||||
* DHG14 does not work with the default JCE implementation provided by Sun because it does not support 2048 bits
|
||||
* encryption. It requires BouncyCastle to be used.
|
||||
*
|
||||
* @deprecated Replaced by {@link com.hierynomus.sshj.transport.kex.DHG} with {@link com.hierynomus.sshj.transport.kex.DHGroups}
|
||||
*/
|
||||
public class DHG14
|
||||
extends AbstractDHG {
|
||||
|
||||
@@ -17,26 +17,139 @@ package net.schmizz.sshj.transport.kex;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/** Simple class holding the data for DH group key exchanges. */
|
||||
/**
|
||||
* Simple class holding the data for DH group key exchanges.
|
||||
*/
|
||||
public final class DHGroupData {
|
||||
|
||||
public static final BigInteger G =
|
||||
new BigInteger("2");
|
||||
|
||||
/**
|
||||
* First Oakley Group (https://tools.ietf.org/html/rfc2409) - P1
|
||||
* prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
|
||||
*/
|
||||
public static final BigInteger P1 =
|
||||
new BigInteger("1797693134862315907708391567937874531978602960487560117064444236841971802161585193" +
|
||||
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
|
||||
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
|
||||
"727629299387668709205606050270810842907692932019128194467627007");
|
||||
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
|
||||
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
|
||||
"727629299387668709205606050270810842907692932019128194467627007");
|
||||
|
||||
/**
|
||||
* 2048-bit MODP Group - P14 (https://tools.ietf.org/html/rfc3526#section-3)
|
||||
* prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
|
||||
*/
|
||||
public static final BigInteger P14 =
|
||||
new BigInteger("3231700607131100730033891392642382824881794124114023911284200975140074170663435422" +
|
||||
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
|
||||
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
|
||||
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
|
||||
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
|
||||
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
|
||||
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
|
||||
"9448652468262416972035911852507045361090559");
|
||||
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
|
||||
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
|
||||
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
|
||||
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
|
||||
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
|
||||
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
|
||||
"9448652468262416972035911852507045361090559");
|
||||
|
||||
/**
|
||||
* 3072-bit MODP Group - P15 (https://tools.ietf.org/html/rfc3526#section-4)
|
||||
* prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
|
||||
*/
|
||||
public static final BigInteger P15 =
|
||||
new BigInteger("5809605995369958062791915965639201402176612226902900533702900882779736177890990861" +
|
||||
"4720947744773395811473734101856463783280437298007504700982109244878669350591643715" +
|
||||
"8816804754094398164451663275506750162643455639819318662899007124866081936120511979" +
|
||||
"3693985433297036118232914410171876807536457391277857011849897410207519105333355801" +
|
||||
"1211093568974594262718454713979526759594407934930716283941227805101246184882326024" +
|
||||
"6464987685045886124578424092925842628769970531258450962541951346360515542801716571" +
|
||||
"4465363094021609290561084025893662561222573202082865797821865270991145082200656978" +
|
||||
"1771928270245389902399691755461907706456858934380117144304264093386763147435711545" +
|
||||
"3714203157300427642870143303638180170530865983075119035294602548205993130657100472" +
|
||||
"7362479688415574702596946457770284148435989129632853918392117997472632693078113129" +
|
||||
"8864873993477969827727846158652326212896569442842168246113187097645351525073541163" +
|
||||
"44703769998514148343807");
|
||||
|
||||
/**
|
||||
* 4096-bit MODP Group - P16 (https://tools.ietf.org/html/rfc3526#section-5)
|
||||
* prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
|
||||
*/
|
||||
public static final BigInteger P16 =
|
||||
new BigInteger("10443888814131525066796027198465295458312690609921350090225887564443381720223226907" +
|
||||
"10444046669809783930111585737890362691860127079270495454517218673016928427459146001" +
|
||||
"86688577976298222932119236830334623520436805101030915567415569746034717694639407653" +
|
||||
"51572849948952848216337009218117167389724518349794558970103063334685907513583651387" +
|
||||
"82250372269117968985194322444535687415522007151638638141456178420621277822674995027" +
|
||||
"99027867345862954439173691976629900551150544617766815444623488266596168079657690319" +
|
||||
"91160893476349471877789065280080047566925716669229641225661745827767073324523710012" +
|
||||
"72163776841229318324903125740713574141005124561965913888899753461735347970011693256" +
|
||||
"31675166067895083002751025580484610558346505544661509044430958305077580850929704003" +
|
||||
"96800574353422539265662408981958636315888889363641299200593084556694540340103914782" +
|
||||
"38784189888594672336242763795138176353222845524644040094258962433613354036104643881" +
|
||||
"92523848922401019419308891166616558422942466816544168892779046060826486420423771700" +
|
||||
"20547443379889419746612146996897065215430062626045358909981257522759426087721743761" +
|
||||
"07314217749233048217904944409836238235772306749874396760463376480215133461333478395" +
|
||||
"682746608242585133953883882226786118030184028136755970045385534758453247");
|
||||
|
||||
/**
|
||||
* 6144-bit MODP Group - P17 (https://tools.ietf.org/html/rfc3526#section-6)
|
||||
* prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
|
||||
*/
|
||||
public static final BigInteger P17 =
|
||||
new BigInteger("3375152182143856118451852315996741233006489780574184654817389047442942990132667244" +
|
||||
"5203235101919165483964194359460994881062089387893762814044257438204432573941083014" +
|
||||
"8270060902589258751610180963277323358005958319159760142088223040073278481327349332" +
|
||||
"9788580321367526156496260334045722077682632250005809131096725397661997398803366366" +
|
||||
"6385188155212656268079501726223369693427999804134467810120772356498596945532366527" +
|
||||
"4005175754719693358549052745041195095923660137119541482588848792245999152034563158" +
|
||||
"8103477655308367699571833559858639559116999957082451503501754353335269752528775333" +
|
||||
"2500527176569576894926734950469293596134095086603716860086302051544539652689091299" +
|
||||
"0997845889190523834630577894405654606814419024423999564190605216296046973478790246" +
|
||||
"5431380018607831652696452928806274087901103517592005919217856147319900620589671943" +
|
||||
"5014765345518490882366607110905303449152556221163232127426440691921134648766635695" +
|
||||
"8502392313045917442156109850296368954067188807663082492273159842675422662594896843" +
|
||||
"7222391644541101590050623941926790971632033120898897818086898743162371034761799235" +
|
||||
"6201449023892203230133009421463914291201346063125219636964261683591541014344239275" +
|
||||
"3407356909977322220697587739633908763605465157552805170421605254873028981223116697" +
|
||||
"9967944753045360039934269703271445854959128593945394903498124811432232236723864504" +
|
||||
"2515984447890788917823576330019151696568654314153058547592091366014550143819685170" +
|
||||
"0683437001046776090411663697600809334136054989623820777788455998349074759534307874" +
|
||||
"4620138456732853067527579296235488377080690082718368571835346957473168052062194454" +
|
||||
"0947734619035177180057973022652571032196598229259194875709994709721793154158686515" +
|
||||
"7485072742241813169487971046010682120152329216914824963468544136987197501906011027" +
|
||||
"0527448105054323981513068607360107630451228454921845984604608225359676243382741906" +
|
||||
"0089029417044871218316020923109988915707117567");
|
||||
|
||||
/**
|
||||
* 8192-bit MODP Group - P18 (https://tools.ietf.org/html/rfc3526#section-7)
|
||||
* prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
|
||||
*/
|
||||
public static final BigInteger P18 =
|
||||
new BigInteger("10907481356194159294502949293597845003481551249531722117741011069661501689227856390" +
|
||||
"28532473848836817769712164169076432969224698752674677662739994265785437233596157045" +
|
||||
"97092233804069810050786103304731233182398243527947570019986097161273254052879655450" +
|
||||
"28679197467769837593914759871425213158787195775191488118308799194269399584870875409" +
|
||||
"65716419167467499326156226529675209172277001377591248147563782880558861083327174154" +
|
||||
"01497513489312511601577631889029596069801161415772128252753946881651931933333750311" +
|
||||
"47771923604122817210189558343776154804684792527488673203623853555966017951228067562" +
|
||||
"17713579819870634321561907813255153703950795271232652404894983869492174481652303803" +
|
||||
"49888136621050864726366837651413103110233683748899977574404673365182723939535354034" +
|
||||
"84148728546397192946943234501868841898225445406472269872921606931847346549419069366" +
|
||||
"46576130260972193280317171696418971553954161446191759093719524951116705577362073481" +
|
||||
"31929604120128351615426904438925772770028968411946028348045230620413002491387998113" +
|
||||
"59080269838682059693181678196808509986496944169079527129049624049377757896989172073" +
|
||||
"56355227455066183815847669135530549755439819480321732925869069136146085326382334628" +
|
||||
"74545639807160305805163420938670870330654590319960852382451372962513665912822110096" +
|
||||
"77354505199524042481982628138310973742616503800172779169753241348465746813073370173" +
|
||||
"80830353680623216336949471306191686438249305686413380231046096450953594089375540285" +
|
||||
"03729247092939511402830554745258496207430943815182543790297601289174935519867842060" +
|
||||
"37220349003113648930464957614043339386861400378480309162925432736845336400326376391" +
|
||||
"00774502371542479302473698388692892420946478947733800387782741417786484770190108867" +
|
||||
"87977899163321862864053398261932246615488301145229189025233648723608665439609385389" +
|
||||
"86288058131775591620763631544364944775078712941198416378677017221666098312018454840" +
|
||||
"78070518041336869808398454625586921201308185638888082699408686536045192649569198110" +
|
||||
"35365994311180230063610650986502394366182943642656300791728205089442938884174888539" +
|
||||
"82907077430529736053592775157496197308237732158947551217614678878653277071155738042" +
|
||||
"64519206349215850195195364813387526811742474131549802130246506341207020335797706780" +
|
||||
"70540694527543880626597851620970679570257924407538049023174103086261496878330620786" +
|
||||
"96878681084236399719832090776247580804999882755913927872676271824428928096468742282" +
|
||||
"63172435642368588260139161962836121481966092745325488641054238839295138992979335446" +
|
||||
"110090325230955276870524611359124918392740353154294858383359");
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ECDH extends DHBase {
|
||||
super("EC", "ECDH");
|
||||
}
|
||||
|
||||
protected void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
|
||||
public void init(AlgorithmParameterSpec params, Factory<Random> randomFactory) throws GeneralSecurityException {
|
||||
generator.initialize(params);
|
||||
KeyPair keyPair = generator.generateKeyPair();
|
||||
agreement.init(keyPair.getPrivate());
|
||||
|
||||
@@ -32,7 +32,9 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** {@link UserAuth} implementation. */
|
||||
/**
|
||||
* {@link UserAuth} implementation.
|
||||
*/
|
||||
public class UserAuthImpl
|
||||
extends AbstractService
|
||||
implements UserAuth {
|
||||
@@ -111,22 +113,20 @@ public class UserAuthImpl
|
||||
try {
|
||||
switch (msg) {
|
||||
|
||||
case USERAUTH_BANNER: {
|
||||
case USERAUTH_BANNER:
|
||||
banner = buf.readString();
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case USERAUTH_SUCCESS: {
|
||||
case USERAUTH_SUCCESS:
|
||||
// In order to prevent race conditions, we immediately set the authenticated flag on the transport
|
||||
// And change the service before delivering the authenticated promise.
|
||||
// Should fix https://github.com/hierynomus/sshj/issues/237
|
||||
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
|
||||
trans.setService(nextService); // We aren't in charge anymore, next service is
|
||||
authenticated.deliver(true);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case USERAUTH_FAILURE: {
|
||||
case USERAUTH_FAILURE:
|
||||
allowedMethods = Arrays.asList(buf.readString().split(","));
|
||||
partialSuccess |= buf.readBoolean();
|
||||
if (allowedMethods.contains(currentMethod.getName()) && currentMethod.shouldRetry()) {
|
||||
@@ -134,18 +134,16 @@ public class UserAuthImpl
|
||||
} else {
|
||||
authenticated.deliver(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default: {
|
||||
default:
|
||||
log.debug("Asking `{}` method to handle {} packet", currentMethod.getName(), msg);
|
||||
try {
|
||||
currentMethod.handle(msg, buf);
|
||||
} catch (UserAuthException e) {
|
||||
authenticated.deliverError(e);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
authenticated.unlock();
|
||||
|
||||
@@ -34,7 +34,7 @@ public class KeyProviderUtil {
|
||||
public static KeyFormat detectKeyFileFormat(File location)
|
||||
throws IOException {
|
||||
return detectKeyFileFormat(new FileReader(location),
|
||||
new File(location + ".pub").exists());
|
||||
new File(location + ".pub").exists() || new File(location + "-cert.pub").exists());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,10 +57,14 @@ public class OpenSSHKeyFile
|
||||
|
||||
@Override
|
||||
public void init(File location) {
|
||||
final File f = new File(location + ".pub");
|
||||
if (f.exists())
|
||||
// try cert key location first
|
||||
File pubKey = new File(location + "-cert.pub");
|
||||
if (!pubKey.exists()) {
|
||||
pubKey = new File(location + ".pub");
|
||||
}
|
||||
if (pubKey.exists())
|
||||
try {
|
||||
initPubKey(new FileReader(f));
|
||||
initPubKey(new FileReader(pubKey));
|
||||
} catch (IOException e) {
|
||||
// let super provide both public & private key
|
||||
log.warn("Error reading public key file: {}", e.toString());
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/** @see <a href="http://blogs.sun.com/janp/entry/how_the_scp_protocol_works">SCP Protocol</a> */
|
||||
/** @see <a href="https://blogs.oracle.com/janp/entry/how_the_scp_protocol_works">SCP Protocol</a> */
|
||||
class SCPEngine {
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ class SCPEngine {
|
||||
|
||||
void sendMessage(String msg) throws IOException {
|
||||
log.debug("Sending message: {}", msg);
|
||||
scp.getOutputStream().write((msg + LF).getBytes(IOUtils.UTF8));
|
||||
scp.getOutputStream().write((msg + LF).getBytes(scp.getRemoteCharset()));
|
||||
scp.getOutputStream().flush();
|
||||
check("Message ACK received");
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -39,11 +41,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class RemotePortForwarderTest {
|
||||
|
||||
// Credentials for an remote SSH Server to test against.
|
||||
private static final String REMOTE_HOST = "x.x.x.x";
|
||||
private static final String USER = "xxxx";
|
||||
private static final String PASSWORD = "yyyy";
|
||||
private static final Logger log = LoggerFactory.getLogger(RemotePortForwarderTest.class);
|
||||
|
||||
private static final PortRange RANGE = new PortRange(9000, 9999);
|
||||
private static final InetSocketAddress HTTP_SERVER_SOCKET_ADDR = new InetSocketAddress("127.0.0.1", 8080);
|
||||
@@ -55,7 +53,7 @@ public class RemotePortForwarderTest {
|
||||
public HttpServer httpServer = new HttpServer();
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
public void setUp() throws IOException {
|
||||
fixture.getServer().setTcpipForwardingFilter(new AcceptAllForwardingFilter());
|
||||
File file = httpServer.getDocRoot().newFile("index.html");
|
||||
FileUtil.writeToFile(file, "<html><head/><body><h1>Hi!</h1></body></html>");
|
||||
@@ -64,49 +62,49 @@ public class RemotePortForwarderTest {
|
||||
@Test
|
||||
public void shouldHaveWorkingHttpServer() throws IOException {
|
||||
// Just to check that we have a working http server...
|
||||
httpGet("127.0.0.1", 8080);
|
||||
assertThat(httpGet("127.0.0.1", 8080), equalTo(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDynamicallyForwardPortForLocalhost() throws IOException {
|
||||
SSHClient sshClient = getFixtureClient();
|
||||
RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", new SinglePort(0));
|
||||
httpGet("127.0.0.1", bind.getPort());
|
||||
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDynamicallyForwardPortForAllIPv4() throws IOException {
|
||||
SSHClient sshClient = getFixtureClient();
|
||||
RemotePortForwarder.Forward bind = forwardPort(sshClient, "0.0.0.0", new SinglePort(0));
|
||||
httpGet("127.0.0.1", bind.getPort());
|
||||
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDynamicallyForwardPortForAllProtocols() throws IOException {
|
||||
SSHClient sshClient = getFixtureClient();
|
||||
RemotePortForwarder.Forward bind = forwardPort(sshClient, "", new SinglePort(0));
|
||||
httpGet("127.0.0.1", bind.getPort());
|
||||
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldForwardPortForLocalhost() throws IOException {
|
||||
SSHClient sshClient = getFixtureClient();
|
||||
RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", RANGE);
|
||||
httpGet("127.0.0.1", bind.getPort());
|
||||
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldForwardPortForAllIPv4() throws IOException {
|
||||
SSHClient sshClient = getFixtureClient();
|
||||
RemotePortForwarder.Forward bind = forwardPort(sshClient, "0.0.0.0", RANGE);
|
||||
httpGet("127.0.0.1", bind.getPort());
|
||||
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldForwardPortForAllProtocols() throws IOException {
|
||||
SSHClient sshClient = getFixtureClient();
|
||||
RemotePortForwarder.Forward bind = forwardPort(sshClient, "", RANGE);
|
||||
httpGet("127.0.0.1", bind.getPort());
|
||||
assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200));
|
||||
}
|
||||
|
||||
private RemotePortForwarder.Forward forwardPort(SSHClient sshClient, String address, PortRange portRange) throws IOException {
|
||||
@@ -127,12 +125,12 @@ public class RemotePortForwarderTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void httpGet(String server, int port) throws IOException {
|
||||
private int httpGet(String server, int port) throws IOException {
|
||||
HttpClient client = HttpClientBuilder.create().build();
|
||||
String urlString = "http://" + server + ":" + port;
|
||||
System.out.println("Trying: GET " + urlString);
|
||||
log.info("Trying: GET " + urlString);
|
||||
HttpResponse execute = client.execute(new HttpGet(urlString));
|
||||
assertThat(execute.getStatusLine().getStatusCode(), equalTo(200));
|
||||
return execute.getStatusLine().getStatusCode();
|
||||
}
|
||||
|
||||
private SSHClient getFixtureClient() throws IOException {
|
||||
|
||||
@@ -61,8 +61,7 @@ public class KeepAliveThreadTerminationTest {
|
||||
for (long l : threadMXBean.getAllThreadIds()) {
|
||||
ThreadInfo threadInfo = threadMXBean.getThreadInfo(l);
|
||||
if (threadInfo.getThreadName().equals("keep-alive") && threadInfo.getThreadState() != Thread.State.TERMINATED) {
|
||||
System.err.println("Found thread in state " + threadInfo.getThreadState());
|
||||
throw new RuntimeException("Found alive keep-alive thread");
|
||||
fail("Found alive keep-alive thread in state " + threadInfo.getThreadState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,25 +42,17 @@ public abstract class BaseAlgorithmTest {
|
||||
|
||||
@Test
|
||||
public void shouldVerifyAlgorithm() throws IOException {
|
||||
attempt(100);
|
||||
}
|
||||
|
||||
private void attempt(int times) throws IOException {
|
||||
for (int i = 0; i < times; i++) {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
logger.info("--> Attempt {}", i);
|
||||
verify();
|
||||
}
|
||||
}
|
||||
|
||||
private void verify() throws IOException {
|
||||
configureServer(fixture.getServer());
|
||||
fixture.start();
|
||||
Config config = getClientConfig(new DefaultConfig());
|
||||
SSHClient sshClient = fixture.connectClient(fixture.setupClient(config));
|
||||
assertThat("should be connected", sshClient.isConnected());
|
||||
sshClient.disconnect();
|
||||
configureServer(fixture.getServer());
|
||||
fixture.start();
|
||||
Config config = getClientConfig(new DefaultConfig());
|
||||
SSHClient sshClient = fixture.connectClient(fixture.setupClient(config));
|
||||
assertThat("should be connected", sshClient.isConnected());
|
||||
sshClient.disconnect();
|
||||
// fixture.stopServer();
|
||||
fixture.stopClient();
|
||||
fixture.stopClient();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Config getClientConfig(DefaultConfig defaultConfig);
|
||||
|
||||
@@ -27,9 +27,6 @@ public class HttpServer extends ExternalResource {
|
||||
|
||||
private TemporaryFolder docRoot = new TemporaryFolder();
|
||||
|
||||
public HttpServer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void before() throws Throwable {
|
||||
docRoot.create();
|
||||
|
||||
@@ -34,7 +34,7 @@ public class FileUtil {
|
||||
FileInputStream fileInputStream = new FileInputStream(f);
|
||||
try {
|
||||
ByteArrayOutputStream byteArrayOutputStream = IOUtils.readFully(fileInputStream);
|
||||
return byteArrayOutputStream.toString("UTF-8");
|
||||
return byteArrayOutputStream.toString(IOUtils.UTF8.displayName());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(fileInputStream);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class AuthPasswordTest {
|
||||
fixture.getServer().setPasswordAuthenticator(new PasswordAuthenticator() {
|
||||
@Override
|
||||
public boolean authenticate(String username, String password, ServerSession session) {
|
||||
if (password.equals("changeme")) {
|
||||
if ("changeme".equals(password)) {
|
||||
throw new PasswordChangeRequiredException("Password was changeme", "Please provide your updated password", "en_US");
|
||||
} else {
|
||||
return password.equals(username);
|
||||
@@ -84,6 +84,7 @@ public class AuthPasswordTest {
|
||||
SSHClient sshClient = fixture.setupConnectedDefaultClient();
|
||||
expectedException.expect(UserAuthException.class);
|
||||
sshClient.authPassword("jeroen", "changeme");
|
||||
assertThat("Should not have authenticated", !sshClient.isAuthenticated());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,10 +15,14 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer.BufferException;
|
||||
import net.schmizz.sshj.common.Buffer.PlainBuffer;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class BufferTest {
|
||||
|
||||
@@ -44,4 +48,103 @@ public class BufferTest {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowOnPutNegativeLongUInt64() {
|
||||
try {
|
||||
new PlainBuffer().putUInt64(-1l);
|
||||
fail("Added negative uint64 to buffer?");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowOnReadNegativeLongUInt64() {
|
||||
byte[] negativeLong = new byte[] { (byte) 0x80,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x01 };
|
||||
Buffer<?> buff = new PlainBuffer(negativeLong);
|
||||
|
||||
try {
|
||||
buff.readUInt64();
|
||||
fail("Read negative uint64 from buffer?");
|
||||
} catch (BufferException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowOnPutNegativeBigIntegerUInt64() {
|
||||
try {
|
||||
new PlainBuffer().putUInt64(new BigInteger("-1"));
|
||||
fail("Added negative uint64 to buffer?");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveCorrectValueForMaxUInt64() {
|
||||
byte[] maxUInt64InBytes = new byte[] { (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF };
|
||||
BigInteger maxUInt64 = new BigInteger(1, maxUInt64InBytes);
|
||||
new PlainBuffer().putUInt64(maxUInt64); // no exception
|
||||
|
||||
BigInteger tooBig = maxUInt64.add(BigInteger.ONE);
|
||||
try {
|
||||
new PlainBuffer().putUInt64(tooBig);
|
||||
fail("Added 2^64 (too big) as uint64 to buffer?");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlyEncodeAndDecodeUInt64Types() throws BufferException {
|
||||
// This number fits into a unsigned 64 bit integer but not a signed one.
|
||||
BigInteger bigUint64 = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE).add(BigInteger.ONE);
|
||||
assertEquals(0x8000000000000001l, bigUint64.longValue());
|
||||
|
||||
Buffer<PlainBuffer> buff = new PlainBuffer();
|
||||
buff.putUInt64(bigUint64);
|
||||
byte[] data = buff.getCompactData();
|
||||
assertEquals(8, data.length);
|
||||
assertEquals((byte) 0x80, data[0]);
|
||||
assertEquals((byte) 0x00, data[1]);
|
||||
assertEquals((byte) 0x00, data[2]);
|
||||
assertEquals((byte) 0x00, data[3]);
|
||||
assertEquals((byte) 0x00, data[4]);
|
||||
assertEquals((byte) 0x00, data[5]);
|
||||
assertEquals((byte) 0x00, data[6]);
|
||||
assertEquals((byte) 0x01, data[7]);
|
||||
|
||||
byte[] asBinary = new byte[] { (byte) 0x80,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x00,
|
||||
(byte) 0x01 };
|
||||
buff = new PlainBuffer(asBinary);
|
||||
assertEquals(bigUint64, buff.readUInt64AsBigInteger());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSameUInt64EncodingForBigIntegerAndLong() {
|
||||
long[] values = { 0l, 1l, 232634978082517765l, Long.MAX_VALUE - 1, Long.MAX_VALUE };
|
||||
for (long value : values) {
|
||||
byte[] bytesBigInt = new PlainBuffer().putUInt64(BigInteger.valueOf(value)).getCompactData();
|
||||
byte[] bytesLong = new PlainBuffer().putUInt64(value).getCompactData();
|
||||
assertArrayEquals("Value: " + value, bytesLong, bytesBigInt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +57,10 @@ public class KeyProviderUtilTest {
|
||||
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs8-blanks"));
|
||||
assertEquals(KeyFormat.PKCS8, format);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenSshSigned() throws IOException {
|
||||
KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "signed"));
|
||||
assertEquals(KeyFormat.OpenSSH, format);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,32 +15,44 @@
|
||||
*/
|
||||
package net.schmizz.sshj.keyprovider;
|
||||
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
||||
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
import net.schmizz.sshj.userauth.password.Resource;
|
||||
import net.schmizz.sshj.util.KeyUtil;
|
||||
import org.apache.sshd.common.util.SecurityUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Scanner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
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 org.apache.sshd.common.util.SecurityUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.hierynomus.sshj.userauth.certificate.Certificate;
|
||||
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
|
||||
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
||||
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
||||
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
import net.schmizz.sshj.userauth.password.Resource;
|
||||
import net.schmizz.sshj.util.KeyUtil;
|
||||
|
||||
public class OpenSSHKeyFileTest {
|
||||
|
||||
@@ -161,6 +173,68 @@ public class OpenSSHKeyFileTest {
|
||||
assertThat(aPrivate.getAlgorithm(), equalTo("EdDSA"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSuccessfullyLoadSignedRSAPublicKey() throws IOException {
|
||||
FileKeyProvider keyFile = new OpenSSHKeyFile();
|
||||
keyFile.init(new File("src/test/resources/keytypes/certificate/test_rsa"),
|
||||
PasswordUtils.createOneOff(correctPassphrase));
|
||||
assertNotNull(keyFile.getPrivate());
|
||||
PublicKey pubKey = keyFile.getPublic();
|
||||
assertEquals("RSA", pubKey.getAlgorithm());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Certificate<RSAPublicKey> certificate = (Certificate<RSAPublicKey>) pubKey;
|
||||
|
||||
assertEquals(new BigInteger("9223372036854775809"), certificate.getSerial());
|
||||
assertEquals("testrsa", certificate.getId());
|
||||
|
||||
assertEquals(2, certificate.getValidPrincipals().size());
|
||||
assertTrue(certificate.getValidPrincipals().contains("jeroen"));
|
||||
assertTrue(certificate.getValidPrincipals().contains("nobody"));
|
||||
|
||||
assertEquals(parseDate("2017-04-11 17:38:00 -0400"), certificate.getValidAfter());
|
||||
assertEquals(parseDate("2017-04-11 18:09:27 -0400"), certificate.getValidBefore());
|
||||
|
||||
assertEquals(0, certificate.getCritOptions().size());
|
||||
|
||||
Map<String, String> extensions = certificate.getExtensions();
|
||||
assertEquals(5, extensions.size());
|
||||
assertEquals("", extensions.get("permit-X11-forwarding"));
|
||||
assertEquals("", extensions.get("permit-agent-forwarding"));
|
||||
assertEquals("", extensions.get("permit-port-forwarding"));
|
||||
assertEquals("", extensions.get("permit-pty"));
|
||||
assertEquals("", extensions.get("permit-user-rc"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSuccessfullyLoadSignedDSAPublicKey() throws IOException {
|
||||
FileKeyProvider keyFile = new OpenSSHKeyFile();
|
||||
keyFile.init(new File("src/test/resources/keytypes/certificate/test_dsa"),
|
||||
PasswordUtils.createOneOff(correctPassphrase));
|
||||
assertNotNull(keyFile.getPrivate());
|
||||
PublicKey pubKey = keyFile.getPublic();
|
||||
assertEquals("DSA", pubKey.getAlgorithm());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Certificate<RSAPublicKey> certificate = (Certificate<RSAPublicKey>) pubKey;
|
||||
|
||||
assertEquals(new BigInteger("123"), certificate.getSerial());
|
||||
assertEquals("testdsa", certificate.getId());
|
||||
|
||||
assertEquals(1, certificate.getValidPrincipals().size());
|
||||
assertTrue(certificate.getValidPrincipals().contains("jeroen"));
|
||||
|
||||
assertEquals(parseDate("2017-04-11 17:37:00 -0400"), certificate.getValidAfter());
|
||||
assertEquals(parseDate("2017-04-12 03:38:49 -0400"), certificate.getValidBefore());
|
||||
|
||||
assertEquals(1, certificate.getCritOptions().size());
|
||||
assertEquals("10.0.0.0/8", certificate.getCritOptions().get("source-address"));
|
||||
|
||||
assertEquals(1, certificate.getExtensions().size());
|
||||
assertEquals("", certificate.getExtensions().get("permit-pty"));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
@@ -182,4 +256,13 @@ public class OpenSSHKeyFileTest {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Date parseDate(String date) {
|
||||
DateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
||||
try {
|
||||
return f.parse(date);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ public class PacketReaderTest {
|
||||
|
||||
private DataOutputStream dataout;
|
||||
private PacketReader reader;
|
||||
private SFTPEngine engine;
|
||||
private Subsystem subsystem;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -42,8 +40,8 @@ public class PacketReaderTest {
|
||||
PipedInputStream pipedin = new PipedInputStream(pipedout);
|
||||
dataout = new DataOutputStream(pipedout);
|
||||
|
||||
engine = Mockito.mock(SFTPEngine.class);
|
||||
subsystem = Mockito.mock(Subsystem.class);
|
||||
SFTPEngine engine = Mockito.mock(SFTPEngine.class);
|
||||
Subsystem subsystem = Mockito.mock(Subsystem.class);
|
||||
Mockito.when(engine.getLoggerFactory()).thenReturn(LoggerFactory.DEFAULT);
|
||||
Mockito.when(engine.getSubsystem()).thenReturn(subsystem);
|
||||
Mockito.when(subsystem.getInputStream()).thenReturn(pipedin);
|
||||
|
||||
@@ -30,9 +30,9 @@ public class PathHelperTest {
|
||||
@Override
|
||||
public String canonicalize(String path)
|
||||
throws IOException {
|
||||
if (path.equals("") || path.equals(".") || path.equals("./"))
|
||||
if ("".equals(path) || ".".equals(path) || "./".equals(path))
|
||||
return "/home/me";
|
||||
if (path.equals("..") || path.equals("../"))
|
||||
if ("..".equals(path) || "../".equals(path))
|
||||
return "/home";
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
package net.schmizz.sshj.util;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -51,7 +53,7 @@ public class BufferTest {
|
||||
public void setUp()
|
||||
throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
// for position test
|
||||
byte[] data = "Hello".getBytes("UTF-8");
|
||||
byte[] data = "Hello".getBytes(IOUtils.UTF8);
|
||||
posBuf = new Buffer.PlainBuffer(data);
|
||||
handyBuf = new Buffer.PlainBuffer();
|
||||
}
|
||||
|
||||
@@ -17,11 +17,12 @@ package net.schmizz.sshj.util.gss;
|
||||
|
||||
import org.ietf.jgss.*;
|
||||
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static net.schmizz.sshj.util.gss.BogusGSSManager.unavailable;
|
||||
@@ -34,7 +35,7 @@ public class BogusGSSContext
|
||||
private static final byte[] MIC = fromString("LGTM");
|
||||
|
||||
private static byte[] fromString(String s) {
|
||||
return s.getBytes(Charset.forName("UTF-8"));
|
||||
return s.getBytes(IOUtils.UTF8);
|
||||
}
|
||||
|
||||
private boolean initialized = false;
|
||||
@@ -70,7 +71,9 @@ public class BogusGSSContext
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() throws GSSException {}
|
||||
public void dispose() throws GSSException {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWrapSizeLimit(int qop, boolean confReq, int maxTokenSize) throws GSSException {
|
||||
|
||||
@@ -34,7 +34,9 @@ public class BogusGSSCredential
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() throws GSSException {}
|
||||
public void dispose() throws GSSException {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public GSSName getName() throws GSSException {
|
||||
|
||||
30
src/test/resources/keyformats/signed
Normal file
30
src/test/resources/keyformats/signed
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,DEF5F558DB233F512527D38775CF90A6
|
||||
|
||||
EQb59FGxEYNaLoYZ1GRtLdQVYe3DtQyAq42OvJyPF2xFpiV+U63TDzHBeSJnf4yK
|
||||
FWcWRbmFM5XL5jpuw7oUtg+bFOYsSjRMTGNpxcXDoByfRubLb3RPMlmVCENcwTXa
|
||||
pF1QuKQYj2+DXRam5y2w7A6rznd5lFDRM57kApGSMcrWwNz2WDyvuqlPTo6Wsj1j
|
||||
SWOQb9Te/ww1t8iEHryAITzUSRhbZG2epGh85QvuKhBebBd9TNRZwqwaZPx+j87/
|
||||
JGvq2RzttIydciLRBx3kYFI7JV1TGTbe+Hd10Yis9jBttqmEpB9Zyoug1Lubg3E2
|
||||
s45jk0CVAFp+44dKhk6K0uX4cjhC9uok6cAGZ7DYMokxEfCiJc7zJJgLvbil2lvK
|
||||
fbewUoiXGLtCPaDe1UXhmkYXBL2BqrBa2PTYlB0JQzFn//9qWW6RVqLpltWSFcFT
|
||||
nGQpRKZLSQhHLn+90X4lAuolUlrpWqgREiGSHlIgihv8mz9uAbHWSvSQE3q0dKSb
|
||||
OgU1CDVsxd0mdkb6ZNeS1iT50uwCpwiUw4Cx9xZp0xdzjiN15ED8eLI/nDFCZXhA
|
||||
jA0AK/cPzlO/Vc3uoM8+3PUkiMKd4glJzWkkE9pEiPlTQ3xivxUM4wQq6pgrjT13
|
||||
YI5PH5FkGNXYEeNxGnL+VXrWnxItd7ZVctG+3p6OKr9ShDxfYfw7WHk2n2o/s9rP
|
||||
j8eSq0G/Dr5ZoaMipPX0aP/BXzZZVVsnFc0SmGfcuIGDug+hjs5OKcrvi6QteJ6u
|
||||
dlsZJUy1YYnc/7T43TMllnouCHQ4TN01JTJSFS0IuKUrDoXI2DBSf2nE4J+04Cno
|
||||
bC5WZCmThM2tWdFiqsRn4I5oZ8vEl6ffhzgwLs/8fJSwzwCwLraMSMWJMJibnG/8
|
||||
cn3/Mwzm6aDMpRqu7h6s2tDctdZJEdRcwjD5tdPg09CLsNvG7nfJWi8RL/PxSuC1
|
||||
m5KKK9rbXVRzg011QELrxTBUAGcH/YHEsOZNrIexyWG99eJ5Y3tEpyaxHVVIsT/o
|
||||
+bsC8SEhADWKmfQmzZz8UbUQLs5SOxa6mutxudzmvdLnGmHk8fsBO7MbxRyBjqau
|
||||
+o5/ClbNzwSUSQQ8W3dpEpU/7udYAtHjwIWwVk7lwUqe/s7p2G9f5LDegJfJAXiU
|
||||
zGetpnYYFd1n4xQs22UPHS0+RaFLsYszvSv+LUEpVJ+zIWSB5hp7OrWLiQpGnQVH
|
||||
YydQUrxt8AYnhdrBbsxk652XFhBZzzbA9AlEHLhiMXDIh7XamFNk/S6fVGgsACGo
|
||||
hu2Ui50lHIRgNKds2tp41G74Vgv20lu3htU0wN9nDwjxATu+sX/0IaWwZIj1iuFf
|
||||
8YK6P0yP5rWyzAfDQWnh15SHE6zAvKYwyZtG7OWBYmd3whyR+VGuzCKw2uLh6JBx
|
||||
5GdNScf5szD1KSxqWwfY5tVLSn/gsSgCAptp5tiIdazOID1OkuM24yYRWoCvxT52
|
||||
qlfG5LHIhYUCzOdRK9DDSgSBqXCaiN3VD3KLOBfeSKSoen0h+CsIz9kmL73liisM
|
||||
+V8knyffjXfb/yCtV1b2xD9bds2si49Mif4aS1SX6bnBT+A5z/f/N3BGrdtIaMFl
|
||||
-----END RSA PRIVATE KEY-----
|
||||
1
src/test/resources/keyformats/signed-cert.pub
Normal file
1
src/test/resources/keyformats/signed-cert.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg3ZotKIydji2P4WBzJjEHH1+n0VBVEyeZDb1AHj4HbUoAAAADAQABAAABAQDu1ngN8Oe+N32oDQ9CSw59fEM4Xo2HaAM/LFZffSj6gwS+BrKVYcupZCoUsLjNaOXudeivrI7+MTtfpPeCXKfNw6Bhmh16OXDdkFuEraLotSkibqNleGPrJW5KB6OBgTv7bSx249oobPLZ9Xog2fO888lsCFMCLMuoiEQM1Y3ZDcoqVRGBSAjki/QLtqWFeouiYdfCdzk80TccxORqNanK6ePQ1pTcOR/OtGToXeJEqqxX51wdSvWjCHGXmjjuQSM/Eznb86LbQtG79koqL4tuDVYvtrnIXUX7/aXicVmCT8gNFxZBP3uX6OJLT0CCZol2F/mu6pq7JLbhhfRCv6/NgAAAAAAAAAEAAAABAAAADHVzZXJfamVyb2VucwAAABYAAAAHamVyb2VuMQAAAAdqZXJvZW4yAAAAAFjtLVAAAAAAWO00lQAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQDZ0a09cp+hsK7F5eg4etwemWyJjA97LlSxAsWOxNNOskNi2FDrhYCpMejr1rdrB1yHy3UYlG0AG3bmzX10uyad17/AHlKvNMQh6rysbe6DDgdPOVQtZv5UCCuvPDnnUAn++SXqxYbvVFSK48BJXeRk2mKrnC2iWsnJCW81j9ICA2S4LEtYE0DiYRrM2UT9lUgmcwxLGqL6JkWbJTF+5KLyRvPGtb06Uo5KOdFlyjEVZ437j/wNTrn2tbQJaMT5qaNuh4Py8WV7aCnX73YfJHdrnCbhOP1MBX9nT+dpMsLYXnCi0K/LLCKJIWn8yNwxPmrUc0OkqOYQWbjGkKn2dt/3AAABDwAAAAdzc2gtcnNhAAABACvwMQl9HsmjpLEt2MMhwyyTcwYsy6lUNa0lWs/YJlR70e/EzriZZVwXGpNYnXW5KgXOA020bhXgpPijBqWOxOAoHyE1CpLAuMDpYAxjnlNy391GzcUrQM74nPeBuE1TGpatS314Gx+iv98EaPdlvtommgh/Ggsb48n9xNrOuonYXwgUOpjvPYlDWvB3klUea4qvhru/23xzKf7DuIGSffB++IANkm1+TJ8vskE44F/VvVo4CCKEY7HaRHTSvfQ4yaNJCEWg0KcFbI79ICbB3mpHRKeDaqjJj4+6858D4/SYrBPOoCr8QNWR/kvYjrWD5qvHZMLO6x+3lEDuh76MNbk= choover@ma-lt-choover
|
||||
15
src/test/resources/keytypes/certificate/test_dsa
Normal file
15
src/test/resources/keytypes/certificate/test_dsa
Normal file
@@ -0,0 +1,15 @@
|
||||
-----BEGIN DSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,F30B96BBE72D14E7E408E43D484642E1
|
||||
|
||||
A+fs86aamKouxGIQLbJc8dF6wzUqXo5stco6SjGFA78mutESj6EWWnU49/JAyWmK
|
||||
t20qKnnOlRNVVGw8h+FoRR1ukA9hkcc4Yg8bvHw21B45bswd165gJPLoOsacocr+
|
||||
487GztRao44+bT5tzkS/pCo0ianjJpfmlRPr8tVkrM0lA9SYWuVjhzm20mKfW8Q0
|
||||
iQk4xZMDS01BZ2BM1cEs8YsR5xXwV84i9iS9Evr5J3+V3xhilZiNSiYLT2kqQ+u7
|
||||
ccqVgybUb7OF2nd1GDY75E3hY0V+pXjPzFn0Er2hK8o76W61s52baVr2xOTq/wmH
|
||||
Ra3FCzj8M7xagprOYsVqza7oLt6lOK4VJQzFntoCDNpAqZDL77vFJz+0E2ZI/+cG
|
||||
1HSt889w0obu6D1XorsBx+LuNJZqwMtwYQMbjr1fXvRLktM4E0gUfyFgeGYJqvl+
|
||||
4AV24MII/+D1K5pnA3Q0Ban+dpLUqH9dGd7dplol12gzSpRtLHRgjv/GggZUIUjh
|
||||
MBTGdLkMHjteph0VFxeNiahydV707Gz9oc35e1MzeAi8dDqPNM7T1XDLV6Tqm2+x
|
||||
j2KBlXpkVhHEJ3IvDxO+1g==
|
||||
-----END DSA PRIVATE KEY-----
|
||||
@@ -0,0 +1 @@
|
||||
ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAguS/sL4az6garEmUIM9ELqcaEfRJlPICG3lGthEJsWwEAAACBAJuvlcNNkgWfJV8y4HWxzith+6EZLGfd3jYqfXmITYDW8830cAmU7xC/aXK5r0LaSKxpyW/G/Z1396j69yxN1R1EC4fDvcEjTzmkcxKTrm8FQzFew7rkpm5H6MZq7PfrA/ph1nrDNLzO8Pzbw4trrr5yHuh3pQHt/WBSz+PiZPULAAAAFQDyrWY0qq7RR2NOslTkL7iBRVihKwAAAIA68Qp50ZqMvlGbnQQdYlH5HG1+tTs56Arc1Dgau8npyH9kTb0txpdijIwXhbwRN2eq0KQ3iF1XOzJSI5nWH2ONaaxK3Da4o8yt7/PQUFBDYqDoE+59XGOsupVKwpFiRPJURigoT943XdYaTRP5lX9nztVb/lFqppG/hdH8iMVJVwAAAIAbgIwWCMroqlEdsXKyM3wzYEHj8jTPTdr00WpV/WBghYittcfV3Cwp8HNSe1OyiA240CMFVLaKGr9PdoqU/an9WHb1skTuz2UNBs8uKJY3pISAZXHiOkza07iwTUcvQTZS+CctIlL1kxj2LTkI/Auy+Eiup40lrbIm87F6+LwSXgAAAAAAAAB7AAAAAQAAAAd0ZXN0ZHNhAAAACgAAAAZqZXJvZW4AAAAAWO1MfAAAAABY7dmJAAAAJAAAAA5zb3VyY2UtYWRkcmVzcwAAAA4AAAAKMTAuMC4wLjAvOAAAABIAAAAKcGVybWl0LXB0eQAAAAAAAAAAAAABsgAAAAdzc2gtZHNzAAAAgQCVc14O81iWe8XfsffXLYwzGr39QrlPQum2WLr3TfRCT+yKkyu3sLBO+3d25XpD1U2MQ1RJ7aJGET0m3QrO3U4CZGV2Fl3e6aLVrzvppR+LHoZa+kOl1bHfiCJ6NLib2bYNa1lIJ5L74lwYK9ZZ85M0Sun0DbNyNOgUX3OcG7oZiQAAABUA1x8Y8ZPR70C/BtQDnjq5Lx+p/cMAAACAGcSOjhD111FJMmi2T1nqyAjMreZx7CfQyfmg+s3dsePm8qsBZNiGRUVgCen14293O1AcMiBsGxI2a2+Kb4V3VzuT64zucrw51ZuqXu79T+KXDdTifgNoNFVPg+sorXyuzc0Z33BTdY8NAkpjJbjhKCYs3FVa4ygGNDC9pyh7nHsAAACBAIxQmlDKKcu0chmM9WjDqSosKJxADRtLmD1Drv1ag2DF3cKFPH5xtFqSpF9bsKeuhIhWeXiwytfeeHufdvno2VTvBJkOFFj5b4jsB2s3GyyJT9Z9TWFYecIj3t8t3QLlmDnGr8cHwmHpcbZmPBnm1hM9DA9OWkQaJWHccsjwSLI0AAAANwAAAAdzc2gtZHNzAAAAKDyWWSqjzoS2RmaBcm1CXVU7ZIx4yg3SAJEGP+ss0Jw27PjDhe54OJ4= choover@ma-lt-choover
|
||||
30
src/test/resources/keytypes/certificate/test_rsa
Normal file
30
src/test/resources/keytypes/certificate/test_rsa
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,5F1AEDBE1E9E4D83EBD9C7B71CD84DB5
|
||||
|
||||
N8SuKdWK3dEA7K6dNodjE++qqXPpVXJlDhiJbrXm1vbs2u+hckzZflK9OQZU1SsY
|
||||
hs6ZyFg/jagdMNEKKrCEK60QMcMg1iPlkMvTwEvQmdWSWtMiTFes9Ec4njUbcbgN
|
||||
qHzixGfWjpTXCRq3QC2Bydq7+njvIEUeLIjTaCIXqwoZoMcBc0N6Aj529gbgDD/i
|
||||
JC0auaSfejSZQm4Ppn+OuO1JKgNkwsjoTrrZjbKHuayYQaioaR5OcAKgSSs5YDdo
|
||||
SJI+PDeIOVb6aMcXwYP5J7C9zzkZU1Ml3dGHk2JMs0Omu2gICRm3mXXmoHRkiBKj
|
||||
2h7EsghiV/Be4lkOF+T3HN19F4G44EIUj7DotbtqNPEFRZa+SSpN3OZmLJQSwBXD
|
||||
A0zek5BxDu7M4VfbLryxNWcHinLe8fBKLQJe3F2/c5ZbMA4Z9098FIL1xWOqvbzS
|
||||
SjiuWhIDxdYVeVe579mUlWjPrMF3NnD4BP9oQlYmbSTxL7B486Ckh7ACFzQhUXVV
|
||||
t2JNZC2FcxM2WU7cHABvGUQNeq/Y2pB5Xq8AmzdpdAs2VZLHtgOvmnPjfo+Q3s1Y
|
||||
rlRe5sCkyn0Ju4M09sMBIQfwszVKa3/l/ZVrG5LCOEHkF+35ex6Gz8ePRlm5jhnU
|
||||
ZQVuLjxbnjnDMaJ0c0IoW7s44cdEDkv3fDzfQHrPzM50oFUN5WOKKWFnT4V1DBLJ
|
||||
9YW8AcNL6bSlgZNkLntn5Wvl3BBKbf5SMmqegfR3eqz50KNwzKKQU8dcIwkJsK2s
|
||||
Z5fNWbydgNDZhrebJL/jpuXpXcvfO0cVMja7y/O0bQ6M37Arpk8bQhTBKZZSaXeF
|
||||
I0Bx3IOxsodWpA9O3Yum8fqqPGrCxsAsP/mWNG0Ov29Myi3MeZ0zgQH283En0Pnx
|
||||
8hvDlw8vNcFe22Jycgg4846FlIWzkT7VfDVxDoYCyjlMTZ2ASys8llH5in4i1w4m
|
||||
j0ZREJE/+evCXBHLoTO96Rgnjt7hp9g62FHJ+ivRlCQVY6sTB03GNv//4v7xGMPF
|
||||
S4eBB9gnGKEEe8zRcOPgUEhUOrFv9cUpdhYz/SLuTZIPPiNwkAMZ6zM77FMsrdWw
|
||||
18wiemOhizCd/JZDlH9COue6EXnr7rexmTp7rUsJOTq8q3rpYmz2s0rGJbqxyxlq
|
||||
BiCjJqN004ZmtFCTD0wCGuNsVLiNyWSspIfzWXkfCyO0SbiH8QjlkUZVr456/5sI
|
||||
cvCQ3ltAFfxR8wMZlfgtT3mEu8JAQCml89yMbttisdfz48XnLeXAzZuZzEr4OllN
|
||||
AMQ8RINlZC2fxqV25jJi+E1da0yvwM9x0NPaM/ZUQqGzMf+dGldcVWdlNFBsthx0
|
||||
wULaGfTYmmn6rBdbeGlt0JKGGL8Ak8+adNSHHxaJuL9W/5wLH41HsE89ZldlTHuI
|
||||
GeHM1lDA/DwO85c4Fk7Ai0Ny44PjdGn27pLNhAfoT2HivF7zMNV+SUXU57iL/qdR
|
||||
yJYGZJyXdreK4qaSCx7BdaK0AFvhBTXW/uRXJCX/XW6zdzdqy5/wkqWt1sWHKTxf
|
||||
Z0CBKWd/se6JCQoixG2Cpo87SeLZsJEscK8eDdLDQwJdKdmIqgw6LddF7RPpq/5D
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1 @@
|
||||
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb0+Llm+3BKq6n5YoMepI1/Iqp4WnSFYYbpm9Y0jLbJMAAAADAQABAAABAQDF9lt1iAGZWGqNtKR05HRMSyRSG2li0Hq732U2yQ9MmWSAEwltBXILCUrTnkJOWIMYyKY4Y22K+aIWMmnJdE8sehfZ6d+gz20yZ8kHndB7CnKn6oxh6kW5NAGye3kDR808L836U2VxMMgpTH/lBi4909tKf6vAY2qoG/LW4ftFWcf0jU7U8lwlvJ4mjJ9Ld5ObaJOa1h3URHWpiXuSa/JFVTMVAz8Z7h6hXpwruv2HQlKJGs2axP71pGR6Vni2fDZ4kWn5dObv2OA5VLiTD+YZtQ0edAEKORyETaCDwgCgX4qNwdGE/A37c1B6K6tGZrWGjXaPACpOFjwjVVhEI8TPgAAAAAAAAAEAAAABAAAAB3Rlc3Ryc2EAAAAUAAAABmplcm9lbgAAAAZub2JvZHkAAAAAWO1MuAAAAABY7VQXAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBANnRrT1yn6GwrsXl6Dh63B6ZbImMD3suVLECxY7E006yQ2LYUOuFgKkx6OvWt2sHXIfLdRiUbQAbdubNfXS7Jp3Xv8AeUq80xCHqvKxt7oMOB085VC1m/lQIK688OedQCf75JerFhu9UVIrjwEld5GTaYqucLaJayckJbzWP0gIDZLgsS1gTQOJhGszZRP2VSCZzDEsaovomRZslMX7kovJG88a1vTpSjko50WXKMRVnjfuP/A1Oufa1tAloxPmpo26Hg/LxZXtoKdfvdh8kd2ucJuE4/UwFf2dP52kywthecKLQr8ssIokhafzI3DE+atRzQ6So5hBZuMaQqfZ23/cAAAEPAAAAB3NzaC1yc2EAAAEA0uyAwWCFNFEZXLuy1+lwd9VtgCcC50HKMyHywKzkyKlDIbsKbCLcQG74f0nDP7c+8jAFRCxLfaGZ55SYXkPoXMOPqFsln7YvidC7nlKhF2Q7FgOBd04SoriAvi1M24EAE+Y9LUz6idLEuegXt32cobv+fqVKRBZ0V6UpDc5W58/6lDk2YMNOIbQQNy34Lf1HqIhxaWC6pmnT3rPcZylvZI7wP58DdhInWoEVMZawZ0m0Py2C2Pb7zOwFY81nXucCHUfCL4UngN1QOLg9/0vwGZmUVkTnPsbeH6gcBoQY6XdjbPB8HP/a1Ugr2wsTyFJKdc1+5r/KtcCLiXe1SmmHQA== choover@ma-lt-choover
|
||||
1
src/test/resources/keytypes/certificate/test_rsa.pub
Normal file
1
src/test/resources/keytypes/certificate/test_rsa.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDF9lt1iAGZWGqNtKR05HRMSyRSG2li0Hq732U2yQ9MmWSAEwltBXILCUrTnkJOWIMYyKY4Y22K+aIWMmnJdE8sehfZ6d+gz20yZ8kHndB7CnKn6oxh6kW5NAGye3kDR808L836U2VxMMgpTH/lBi4909tKf6vAY2qoG/LW4ftFWcf0jU7U8lwlvJ4mjJ9Ld5ObaJOa1h3URHWpiXuSa/JFVTMVAz8Z7h6hXpwruv2HQlKJGs2axP71pGR6Vni2fDZ4kWn5dObv2OA5VLiTD+YZtQ0edAEKORyETaCDwgCgX4qNwdGE/A37c1B6K6tGZrWGjXaPACpOFjwjVVhEI8TP choover@ma-lt-choover
|
||||
Reference in New Issue
Block a user