Compare commits

..

10 Commits

Author SHA1 Message Date
Jeroen van Erp
8e15a8bd7d Updated README with release notes 2017-04-14 09:54:00 +02:00
Jeroen van Erp
531eb97767 Added log message for early identification termination 2017-04-14 09:14:52 +02:00
chqr
e36fd0fb3d Add support for authentication with DSA & RSA user certificates (#153) (#319)
* Add support for authentication with DSA & RSA user certificates (#153)

Updates:

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

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

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

Added new class Certificate, which represents certificate key

Reference:

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

* Use BigInteger for certificate serial numbers, address Codacy issues

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

* Code style, buffer methods with charsets

* assure remote charset isn't null
2017-03-09 13:58:51 +01:00
Jeroen van Erp
bcb15e6ccd Changed deprecated build notation 2017-03-01 21:53:42 +01:00
Jeroen van Erp
d85b22fe8d Upgrade to Gradle 3.4 2017-03-01 21:51:29 +01:00
31 changed files with 936 additions and 104 deletions

View File

@@ -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`.
@@ -84,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!)
@@ -104,6 +104,11 @@ 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

View File

@@ -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"
@@ -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}")
}
}
}

View File

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

View File

@@ -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")) {

View File

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

View File

@@ -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;
}

View File

@@ -15,8 +15,8 @@
*/
package net.schmizz.sshj.common;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
@@ -57,6 +57,11 @@ public class Buffer<T extends Buffer<T>> {
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
public static final int MAX_SIZE = (1 << 30);
/** Maximum size of a uint64 */
private static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
.shiftLeft(64)
.subtract(BigInteger.ONE);
protected static int getNextPowerOf2(int i) {
int j = 1;
while (j < i) {
@@ -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 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);
}
/**

View File

@@ -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);
}
@@ -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();
}
}
}

View File

@@ -26,6 +26,7 @@ import org.slf4j.Logger;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
@@ -51,6 +52,8 @@ public abstract class AbstractChannel
private final int id;
/** Remote recipient ID */
private int recipient;
/** Remote character set */
private final Charset remoteCharset;
private boolean eof = false;
@@ -78,12 +81,16 @@ public abstract class AbstractChannel
private volatile boolean autoExpand = false;
protected AbstractChannel(Connection conn, String type) {
this(conn, type, null);
}
protected AbstractChannel(Connection conn, String type, Charset remoteCharset) {
this.conn = conn;
this.loggerFactory = conn.getTransport().getConfig().getLoggerFactory();
this.type = type;
this.log = loggerFactory.getLogger(getClass());
this.trans = conn.getTransport();
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
id = conn.nextID();
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize(), loggerFactory);
@@ -135,6 +142,11 @@ public abstract class AbstractChannel
return recipient;
}
@Override
public Charset getRemoteCharset() {
return remoteCharset;
}
@Override
public int getRemoteMaxPacketSize() {
return rwin.getMaxPacketSize();

View File

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

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ 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);

View File

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

View File

@@ -196,8 +196,11 @@ 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);
}
}

View File

@@ -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());
}
/**

View File

@@ -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());

View File

@@ -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");
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}

View File

@@ -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;

View 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-----

View 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

View 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-----

View File

@@ -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

View 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-----

View File

@@ -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

View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDF9lt1iAGZWGqNtKR05HRMSyRSG2li0Hq732U2yQ9MmWSAEwltBXILCUrTnkJOWIMYyKY4Y22K+aIWMmnJdE8sehfZ6d+gz20yZ8kHndB7CnKn6oxh6kW5NAGye3kDR808L836U2VxMMgpTH/lBi4909tKf6vAY2qoG/LW4ftFWcf0jU7U8lwlvJ4mjJ9Ld5ObaJOa1h3URHWpiXuSa/JFVTMVAz8Z7h6hXpwruv2HQlKJGs2axP71pGR6Vni2fDZ4kWn5dObv2OA5VLiTD+YZtQ0edAEKORyETaCDwgCgX4qNwdGE/A37c1B6K6tGZrWGjXaPACpOFjwjVVhEI8TP choover@ma-lt-choover