mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Merge branch 'master' into issue-358
This commit is contained in:
@@ -81,7 +81,7 @@ signatures::
|
|||||||
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519`
|
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519`
|
||||||
|
|
||||||
mac::
|
mac::
|
||||||
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`
|
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`, `hmac-ripemd160`
|
||||||
|
|
||||||
compression::
|
compression::
|
||||||
`zlib` and `zlib@openssh.com` (delayed zlib)
|
`zlib` and `zlib@openssh.com` (delayed zlib)
|
||||||
@@ -107,6 +107,9 @@ Google Group: http://groups.google.com/group/sshj-users
|
|||||||
Fork away!
|
Fork away!
|
||||||
|
|
||||||
== Release history
|
== Release history
|
||||||
|
SSHJ 0.24.0 (2018-??-??)::
|
||||||
|
* Added support for hmac-ripemd160
|
||||||
|
|
||||||
SSHJ 0.23.0 (2017-10-13)::
|
SSHJ 0.23.0 (2017-10-13)::
|
||||||
* Merged https://github.com/hierynomus/sshj/pulls/372[#372]: Upgrade to 'net.i2p.crypto:eddsa:0.2.0'
|
* Merged https://github.com/hierynomus/sshj/pulls/372[#372]: Upgrade to 'net.i2p.crypto:eddsa:0.2.0'
|
||||||
* Fixed https://github.com/hierynomus/sshj/issues/355[#355] and https://github.com/hierynomus/sshj/issues/354[#354]: Correctly decode signature bytes
|
* Fixed https://github.com/hierynomus/sshj/issues/355[#355] and https://github.com/hierynomus/sshj/issues/354[#354]: Correctly decode signature bytes
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ ADD id_rsa.pub /home/sshj/.ssh/authorized_keys
|
|||||||
|
|
||||||
ADD test-container/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key
|
ADD test-container/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key
|
||||||
ADD test-container/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ecdsa_key.pub
|
ADD test-container/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ecdsa_key.pub
|
||||||
|
ADD test-container/sshd_config /etc/ssh/sshd_config
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
echo "root:smile" | chpasswd && \
|
echo "root:smile" | chpasswd && \
|
||||||
|
|||||||
132
src/itest/docker-image/test-container/sshd_config
Normal file
132
src/itest/docker-image/test-container/sshd_config
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# $OpenBSD: sshd_config,v 1.101 2017/03/14 07:19:07 djm Exp $
|
||||||
|
|
||||||
|
# This is the sshd server system-wide configuration file. See
|
||||||
|
# sshd_config(5) for more information.
|
||||||
|
|
||||||
|
# This sshd was compiled with PATH=/bin:/usr/bin:/sbin:/usr/sbin
|
||||||
|
|
||||||
|
# The strategy used for options in the default sshd_config shipped with
|
||||||
|
# OpenSSH is to specify options with their default value where
|
||||||
|
# possible, but leave them commented. Uncommented options override the
|
||||||
|
# default value.
|
||||||
|
|
||||||
|
#Port 22
|
||||||
|
#AddressFamily any
|
||||||
|
#ListenAddress 0.0.0.0
|
||||||
|
#ListenAddress ::
|
||||||
|
|
||||||
|
#HostKey /etc/ssh/ssh_host_rsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_dsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ed25519_key
|
||||||
|
|
||||||
|
# Ciphers and keying
|
||||||
|
#RekeyLimit default none
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
#SyslogFacility AUTH
|
||||||
|
#LogLevel INFO
|
||||||
|
|
||||||
|
# Authentication:
|
||||||
|
|
||||||
|
#LoginGraceTime 2m
|
||||||
|
PermitRootLogin yes
|
||||||
|
#StrictModes yes
|
||||||
|
#MaxAuthTries 6
|
||||||
|
#MaxSessions 10
|
||||||
|
|
||||||
|
#PubkeyAuthentication yes
|
||||||
|
|
||||||
|
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
|
||||||
|
# but this is overridden so installations will only check .ssh/authorized_keys
|
||||||
|
AuthorizedKeysFile .ssh/authorized_keys
|
||||||
|
|
||||||
|
#AuthorizedPrincipalsFile none
|
||||||
|
|
||||||
|
#AuthorizedKeysCommand none
|
||||||
|
#AuthorizedKeysCommandUser nobody
|
||||||
|
|
||||||
|
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
|
||||||
|
#HostbasedAuthentication no
|
||||||
|
# Change to yes if you don't trust ~/.ssh/known_hosts for
|
||||||
|
# HostbasedAuthentication
|
||||||
|
#IgnoreUserKnownHosts no
|
||||||
|
# Don't read the user's ~/.rhosts and ~/.shosts files
|
||||||
|
#IgnoreRhosts yes
|
||||||
|
|
||||||
|
# To disable tunneled clear text passwords, change to no here!
|
||||||
|
#PasswordAuthentication yes
|
||||||
|
#PermitEmptyPasswords no
|
||||||
|
|
||||||
|
# Change to no to disable s/key passwords
|
||||||
|
#ChallengeResponseAuthentication yes
|
||||||
|
|
||||||
|
# Kerberos options
|
||||||
|
#KerberosAuthentication no
|
||||||
|
#KerberosOrLocalPasswd yes
|
||||||
|
#KerberosTicketCleanup yes
|
||||||
|
#KerberosGetAFSToken no
|
||||||
|
|
||||||
|
# GSSAPI options
|
||||||
|
#GSSAPIAuthentication no
|
||||||
|
#GSSAPICleanupCredentials yes
|
||||||
|
|
||||||
|
# Set this to 'yes' to enable PAM authentication, account processing,
|
||||||
|
# and session processing. If this is enabled, PAM authentication will
|
||||||
|
# be allowed through the ChallengeResponseAuthentication and
|
||||||
|
# PasswordAuthentication. Depending on your PAM configuration,
|
||||||
|
# PAM authentication via ChallengeResponseAuthentication may bypass
|
||||||
|
# the setting of "PermitRootLogin without-password".
|
||||||
|
# If you just want the PAM account and session checks to run without
|
||||||
|
# PAM authentication, then enable this but set PasswordAuthentication
|
||||||
|
# and ChallengeResponseAuthentication to 'no'.
|
||||||
|
#UsePAM no
|
||||||
|
|
||||||
|
#AllowAgentForwarding yes
|
||||||
|
#AllowTcpForwarding yes
|
||||||
|
#GatewayPorts no
|
||||||
|
#X11Forwarding no
|
||||||
|
#X11DisplayOffset 10
|
||||||
|
#X11UseLocalhost yes
|
||||||
|
#PermitTTY yes
|
||||||
|
#PrintMotd yes
|
||||||
|
#PrintLastLog yes
|
||||||
|
#TCPKeepAlive yes
|
||||||
|
#UseLogin no
|
||||||
|
#PermitUserEnvironment no
|
||||||
|
#Compression delayed
|
||||||
|
#ClientAliveInterval 0
|
||||||
|
#ClientAliveCountMax 3
|
||||||
|
#UseDNS no
|
||||||
|
#PidFile /run/sshd.pid
|
||||||
|
#MaxStartups 10:30:100
|
||||||
|
#PermitTunnel no
|
||||||
|
#ChrootDirectory none
|
||||||
|
#VersionAddendum none
|
||||||
|
|
||||||
|
# no default banner path
|
||||||
|
#Banner none
|
||||||
|
|
||||||
|
# override default of no subsystems
|
||||||
|
Subsystem sftp /usr/lib/ssh/sftp-server
|
||||||
|
|
||||||
|
# the following are HPN related configuration options
|
||||||
|
# tcp receive buffer polling. disable in non autotuning kernels
|
||||||
|
#TcpRcvBufPoll yes
|
||||||
|
|
||||||
|
# disable hpn performance boosts
|
||||||
|
#HPNDisabled no
|
||||||
|
|
||||||
|
# buffer size for hpn to non-hpn connections
|
||||||
|
#HPNBufferSize 2048
|
||||||
|
|
||||||
|
|
||||||
|
# Example of overriding settings on a per-user basis
|
||||||
|
#Match User anoncvs
|
||||||
|
# X11Forwarding no
|
||||||
|
# AllowTcpForwarding no
|
||||||
|
# PermitTTY no
|
||||||
|
# ForceCommand cvs server
|
||||||
|
|
||||||
|
|
||||||
|
macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com
|
||||||
@@ -15,22 +15,28 @@
|
|||||||
*/
|
*/
|
||||||
package com.hierynomus.sshj
|
package com.hierynomus.sshj
|
||||||
|
|
||||||
|
import net.schmizz.sshj.Config
|
||||||
import net.schmizz.sshj.DefaultConfig
|
import net.schmizz.sshj.DefaultConfig
|
||||||
import net.schmizz.sshj.SSHClient
|
import net.schmizz.sshj.SSHClient
|
||||||
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
|
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
class IntegrationBaseSpec extends Specification {
|
class IntegrationBaseSpec extends Specification {
|
||||||
protected static final int DOCKER_PORT = 2222;
|
protected static final int DOCKER_PORT = 2222
|
||||||
protected static final String USERNAME = "sshj";
|
protected static final String USERNAME = "sshj"
|
||||||
protected final static String SERVER_IP = System.getProperty("serverIP", "127.0.0.1");
|
protected static final String KEYFILE = "src/test/resources/id_rsa"
|
||||||
|
protected final static String SERVER_IP = System.getProperty("serverIP", "127.0.0.1")
|
||||||
|
|
||||||
|
protected static SSHClient getConnectedClient(Config config) {
|
||||||
|
SSHClient sshClient = new SSHClient(config)
|
||||||
|
sshClient.addHostKeyVerifier(new PromiscuousVerifier())
|
||||||
|
sshClient.connect(SERVER_IP, DOCKER_PORT)
|
||||||
|
|
||||||
|
return sshClient
|
||||||
|
}
|
||||||
|
|
||||||
protected static SSHClient getConnectedClient() throws IOException {
|
protected static SSHClient getConnectedClient() throws IOException {
|
||||||
SSHClient sshClient = new SSHClient(new DefaultConfig());
|
return getConnectedClient(new DefaultConfig())
|
||||||
sshClient.addHostKeyVerifier(new PromiscuousVerifier());
|
|
||||||
sshClient.connect(SERVER_IP, DOCKER_PORT);
|
|
||||||
|
|
||||||
return sshClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
|
|||||||
SSHClient client = getConnectedClient()
|
SSHClient client = getConnectedClient()
|
||||||
|
|
||||||
when:
|
when:
|
||||||
client.authPublickey("sshj", "src/test/resources/id_rsa")
|
client.authPublickey(USERNAME, KEYFILE)
|
||||||
|
|
||||||
then:
|
then:
|
||||||
client.isAuthenticated()
|
client.isAuthenticated()
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.mac
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.IntegrationBaseSpec
|
||||||
|
import net.schmizz.sshj.DefaultConfig
|
||||||
|
import net.schmizz.sshj.transport.mac.HMACRIPEMD160
|
||||||
|
import net.schmizz.sshj.transport.mac.HMACSHA2256
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
class MacSpec extends IntegrationBaseSpec {
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "should correctly connect with #mac MAC"() {
|
||||||
|
given:
|
||||||
|
def cfg = new DefaultConfig()
|
||||||
|
cfg.setMACFactories(macFactory)
|
||||||
|
def client = getConnectedClient(cfg)
|
||||||
|
|
||||||
|
when:
|
||||||
|
client.authPublickey(USERNAME, KEYFILE)
|
||||||
|
|
||||||
|
then:
|
||||||
|
client.authenticated
|
||||||
|
|
||||||
|
where:
|
||||||
|
macFactory << [new HMACSHA2256.Factory(), new HMACRIPEMD160.Factory()]
|
||||||
|
mac = macFactory.name
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,9 @@ import net.schmizz.sshj.signature.SignatureRSA;
|
|||||||
import net.schmizz.sshj.transport.random.JCERandom;
|
import net.schmizz.sshj.transport.random.JCERandom;
|
||||||
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers SpongyCastle as JCE provider.
|
||||||
|
*/
|
||||||
public class AndroidConfig
|
public class AndroidConfig
|
||||||
extends DefaultConfig {
|
extends DefaultConfig {
|
||||||
|
|
||||||
@@ -30,13 +33,6 @@ public class AndroidConfig
|
|||||||
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
|
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
|
||||||
}
|
}
|
||||||
|
|
||||||
public AndroidConfig(){
|
|
||||||
super();
|
|
||||||
initKeyExchangeFactories(true);
|
|
||||||
initRandomFactory(true);
|
|
||||||
initFileKeyProviderFactories(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't add ECDSA
|
// don't add ECDSA
|
||||||
protected void initSignatureFactories() {
|
protected void initSignatureFactories() {
|
||||||
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
|
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class DefaultConfig
|
|||||||
properties.load(DefaultConfig.class.getClassLoader().getResourceAsStream("sshj.properties"));
|
properties.load(DefaultConfig.class.getClassLoader().getResourceAsStream("sshj.properties"));
|
||||||
String property = properties.getProperty("sshj.version");
|
String property = properties.getProperty("sshj.version");
|
||||||
return "SSHJ_" + property.replace('-', '_'); // '-' is a disallowed character, see RFC-4253#section-4.2
|
return "SSHJ_" + property.replace('-', '_'); // '-' is a disallowed character, see RFC-4253#section-4.2
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
log.error("Could not read the sshj.properties file, returning an 'unknown' version as fallback.");
|
log.error("Could not read the sshj.properties file, returning an 'unknown' version as fallback.");
|
||||||
return "SSHJ_VERSION_UNKNOWN";
|
return "SSHJ_VERSION_UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -460,10 +460,13 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
|
|
||||||
public PublicKey readPublicKey()
|
public PublicKey readPublicKey()
|
||||||
throws BufferException {
|
throws BufferException {
|
||||||
|
KeyType keyType = KeyType.fromString(readString());
|
||||||
try {
|
try {
|
||||||
return KeyType.fromString(readString()).readPubKeyFromBuffer(this);
|
return keyType.readPubKeyFromBuffer(this);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new SSHRuntimeException(e);
|
throw new SSHRuntimeException(e);
|
||||||
|
} catch (UnsupportedOperationException uoe) {
|
||||||
|
throw new BufferException("Could not decode keytype " + keyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,21 @@ package net.schmizz.sshj.common;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.Signature;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.KeyAgreement;
|
import javax.crypto.KeyAgreement;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import java.security.*;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
|
|
||||||
@@ -37,12 +47,17 @@ public class SecurityUtils {
|
|||||||
*/
|
*/
|
||||||
public static final String BOUNCY_CASTLE = "BC";
|
public static final String BOUNCY_CASTLE = "BC";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier for the BouncyCastle JCE provider
|
||||||
|
*/
|
||||||
|
public static final String SPONGY_CASTLE = "SC";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Security provider identifier. null = default JCE
|
* Security provider identifier. null = default JCE
|
||||||
*/
|
*/
|
||||||
private static String securityProvider = null;
|
private static String securityProvider = null;
|
||||||
|
|
||||||
// relate to BC registration
|
// relate to BC registration (or SpongyCastle on Android)
|
||||||
private static Boolean registerBouncyCastle;
|
private static Boolean registerBouncyCastle;
|
||||||
private static boolean registrationDone;
|
private static boolean registrationDone;
|
||||||
|
|
||||||
@@ -82,6 +97,8 @@ public class SecurityUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static synchronized Cipher getCipher(String transformation)
|
public static synchronized Cipher getCipher(String transformation)
|
||||||
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
|
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
|
||||||
register();
|
register();
|
||||||
@@ -222,11 +239,11 @@ public class SecurityUtils {
|
|||||||
* Attempts registering BouncyCastle as security provider if it has not been previously attempted and returns
|
* Attempts registering BouncyCastle as security provider if it has not been previously attempted and returns
|
||||||
* whether the registration succeeded.
|
* whether the registration succeeded.
|
||||||
*
|
*
|
||||||
* @return whether BC registered
|
* @return whether BC (or SC on Android) registered
|
||||||
*/
|
*/
|
||||||
public static synchronized boolean isBouncyCastleRegistered() {
|
public static synchronized boolean isBouncyCastleRegistered() {
|
||||||
register();
|
register();
|
||||||
return BOUNCY_CASTLE.equals(securityProvider);
|
return BOUNCY_CASTLE.equals(securityProvider) || SPONGY_CASTLE.equals(securityProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
|
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.schmizz.sshj.transport.mac;
|
||||||
|
|
||||||
|
public class HMACRIPEMD160 extends BaseMAC {
|
||||||
|
/** Named factory for the HMAC-SHA1 <code>MAC</code> */
|
||||||
|
public static class Factory
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<MAC> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MAC create() {
|
||||||
|
return new HMACRIPEMD160();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "hmac-ripemd160";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public HMACRIPEMD160() {
|
||||||
|
super("HMACRIPEMD160", 20, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ public class ConsoleKnownHostsVerifier
|
|||||||
protected boolean hostKeyUnverifiableAction(String hostname, PublicKey key) {
|
protected boolean hostKeyUnverifiableAction(String hostname, PublicKey key) {
|
||||||
final KeyType type = KeyType.fromKey(key);
|
final KeyType type = KeyType.fromKey(key);
|
||||||
console.printf("The authenticity of host '%s' can't be established.\n" +
|
console.printf("The authenticity of host '%s' can't be established.\n" +
|
||||||
"%s key fingerprint is %s.\n", hostname, type, SecurityUtils.getFingerprint(key));
|
"%s key fingerprint is %s.\n", hostname, type, SecurityUtils.getFingerprint(key));
|
||||||
String response = console.readLine("Are you sure you want to continue connecting (yes/no)? ");
|
String response = console.readLine("Are you sure you want to continue connecting (yes/no)? ");
|
||||||
while (!(response.equalsIgnoreCase(YES) || response.equalsIgnoreCase(NO))) {
|
while (!(response.equalsIgnoreCase(YES) || response.equalsIgnoreCase(NO))) {
|
||||||
response = console.readLine("Please explicitly enter yes/no: ");
|
response = console.readLine("Please explicitly enter yes/no: ");
|
||||||
@@ -60,7 +60,7 @@ public class ConsoleKnownHostsVerifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hostKeyChangedAction(KnownHostEntry entry, String hostname, PublicKey key) {
|
protected boolean hostKeyChangedAction(String hostname, PublicKey key) {
|
||||||
final KeyType type = KeyType.fromKey(key);
|
final KeyType type = KeyType.fromKey(key);
|
||||||
final String fp = SecurityUtils.getFingerprint(key);
|
final String fp = SecurityUtils.getFingerprint(key);
|
||||||
final String path = getFile().getAbsolutePath();
|
final String path = getFile().getAbsolutePath();
|
||||||
|
|||||||
@@ -87,14 +87,23 @@ public class OpenSSHKnownHosts
|
|||||||
|
|
||||||
final String adjustedHostname = (port != 22) ? "[" + hostname + "]:" + port : hostname;
|
final String adjustedHostname = (port != 22) ? "[" + hostname + "]:" + port : hostname;
|
||||||
|
|
||||||
|
boolean foundApplicableHostEntry = false;
|
||||||
for (KnownHostEntry e : entries) {
|
for (KnownHostEntry e : entries) {
|
||||||
try {
|
try {
|
||||||
if (e.appliesTo(type, adjustedHostname))
|
if (e.appliesTo(type, adjustedHostname)) {
|
||||||
return e.verify(key) || hostKeyChangedAction(e, adjustedHostname, key);
|
foundApplicableHostEntry = true;
|
||||||
|
if (e.verify(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
log.error("Error with {}: {}", e, ioe);
|
log.error("Error with {}: {}", e, ioe);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (foundApplicableHostEntry) {
|
||||||
|
return hostKeyChangedAction(adjustedHostname, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hostKeyUnverifiableAction(adjustedHostname, key);
|
return hostKeyUnverifiableAction(adjustedHostname, key);
|
||||||
@@ -104,7 +113,7 @@ public class OpenSSHKnownHosts
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean hostKeyChangedAction(KnownHostEntry entry, String hostname, PublicKey key) {
|
protected boolean hostKeyChangedAction(String hostname, PublicKey key) {
|
||||||
log.warn("Host key for `{}` has changed!", hostname);
|
log.warn("Host key for `{}` has changed!", hostname);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -199,7 +208,7 @@ public class OpenSSHKnownHosts
|
|||||||
}
|
}
|
||||||
if(split.length < 3) {
|
if(split.length < 3) {
|
||||||
log.error("Error reading entry `{}`", line);
|
log.error("Error reading entry `{}`", line);
|
||||||
return null;
|
return new BadHostEntry(line);
|
||||||
}
|
}
|
||||||
final String hostnames = split[i++];
|
final String hostnames = split[i++];
|
||||||
final String sType = split[i++];
|
final String sType = split[i++];
|
||||||
@@ -209,7 +218,13 @@ public class OpenSSHKnownHosts
|
|||||||
|
|
||||||
if (type != KeyType.UNKNOWN) {
|
if (type != KeyType.UNKNOWN) {
|
||||||
final String sKey = split[i++];
|
final String sKey = split[i++];
|
||||||
key = new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
|
try {
|
||||||
|
byte[] keyBytes = Base64.decode(sKey);
|
||||||
|
key = new Buffer.PlainBuffer(keyBytes).readPublicKey();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
log.warn("Error decoding Base64 key bytes", ioe);
|
||||||
|
return new BadHostEntry(line);
|
||||||
|
}
|
||||||
} else if (isBits(sType)) {
|
} else if (isBits(sType)) {
|
||||||
type = KeyType.RSA;
|
type = KeyType.RSA;
|
||||||
// int bits = Integer.valueOf(sType);
|
// int bits = Integer.valueOf(sType);
|
||||||
@@ -220,11 +235,11 @@ public class OpenSSHKnownHosts
|
|||||||
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("Error reading entry `{}`, could not create key", line, ex);
|
log.error("Error reading entry `{}`, could not create key", line, ex);
|
||||||
return null;
|
return new BadHostEntry(line);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error("Error reading entry `{}`, could not determine type", line);
|
log.error("Error reading entry `{}`, could not determine type", line);
|
||||||
return null;
|
return new BadHostEntry(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HostEntry(marker, hostnames, type, key);
|
return new HostEntry(marker, hostnames, type, key);
|
||||||
@@ -310,7 +325,7 @@ public class OpenSSHKnownHosts
|
|||||||
protected final PublicKey key;
|
protected final PublicKey key;
|
||||||
private final KnownHostMatchers.HostMatcher matcher;
|
private final KnownHostMatchers.HostMatcher matcher;
|
||||||
|
|
||||||
HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException {
|
public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException {
|
||||||
this.marker = marker;
|
this.marker = marker;
|
||||||
this.hostPart = hostPart;
|
this.hostPart = hostPart;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -364,6 +379,44 @@ public class OpenSSHKnownHosts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class BadHostEntry implements KnownHostEntry {
|
||||||
|
private String line;
|
||||||
|
|
||||||
|
public BadHostEntry(String line) {
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyType getType() {
|
||||||
|
return KeyType.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFingerprint() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean appliesTo(String host) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean appliesTo(KeyType type, String host) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(PublicKey key) throws IOException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLine() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum Marker {
|
public enum Marker {
|
||||||
CA_CERT("@cert-authority"),
|
CA_CERT("@cert-authority"),
|
||||||
REVOKED("@revoked");
|
REVOKED("@revoked");
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package net.schmizz.sshj.userauth.keyprovider;
|
package net.schmizz.sshj.userauth.keyprovider;
|
||||||
|
|
||||||
import net.schmizz.sshj.common.IOUtils;
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||||
import org.bouncycastle.openssl.EncryptionException;
|
import org.bouncycastle.openssl.EncryptionException;
|
||||||
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
||||||
@@ -62,12 +63,12 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
|
|||||||
final Object o = r.readObject();
|
final Object o = r.readObject();
|
||||||
|
|
||||||
final JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
|
final JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
|
||||||
pemConverter.setProvider("BC");
|
pemConverter.setProvider(SecurityUtils.getSecurityProvider());
|
||||||
|
|
||||||
if (o instanceof PEMEncryptedKeyPair) {
|
if (o instanceof PEMEncryptedKeyPair) {
|
||||||
final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) o;
|
final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) o;
|
||||||
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
|
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
|
||||||
decryptorBuilder.setProvider("BC");
|
decryptorBuilder.setProvider(SecurityUtils.getSecurityProvider());
|
||||||
try {
|
try {
|
||||||
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
|
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
|
||||||
kp = pemConverter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorBuilder.build(passphrase)));
|
kp = pemConverter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorBuilder.build(passphrase)));
|
||||||
|
|||||||
@@ -15,65 +15,25 @@
|
|||||||
*/
|
*/
|
||||||
package com.hierynomus.sshj.transport.verification
|
package com.hierynomus.sshj.transport.verification
|
||||||
|
|
||||||
import net.schmizz.sshj.common.KeyType
|
import net.schmizz.sshj.common.Base64
|
||||||
|
import net.schmizz.sshj.common.Buffer
|
||||||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts
|
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts
|
||||||
import net.schmizz.sshj.util.KeyUtil
|
import net.schmizz.sshj.util.KeyUtil
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
import spock.lang.Unroll
|
import spock.lang.Unroll
|
||||||
|
|
||||||
import java.security.GeneralSecurityException
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.equalTo
|
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat
|
|
||||||
|
|
||||||
class OpenSSHKnownHostsSpec extends Specification {
|
class OpenSSHKnownHostsSpec extends Specification {
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder temp = new TemporaryFolder();
|
public TemporaryFolder temp = new TemporaryFolder();
|
||||||
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
def "should add comment lines"() {
|
|
||||||
given:
|
|
||||||
def file = writeKnownHosts(contents)
|
|
||||||
|
|
||||||
when:
|
|
||||||
OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(file)
|
|
||||||
|
|
||||||
then:
|
|
||||||
openSSHKnownHosts.entries().size() == 1
|
|
||||||
openSSHKnownHosts.entries()[0] instanceof OpenSSHKnownHosts.CommentEntry
|
|
||||||
|
|
||||||
where:
|
|
||||||
contents << ["", "# this is a comment"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should parse and verify plain host entry with RSA key"() {
|
|
||||||
given:
|
|
||||||
def f = writeKnownHosts("schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==")
|
|
||||||
final PublicKey key = KeyUtil
|
|
||||||
.newRSAPublicKey(
|
|
||||||
"e8ff4797075a861db9d2319960a836b2746ada3da514955d2921f2c6a6c9895cbd557f604e43772b6303e3cab2ad82d83b21acdef4edb72524f9c2bef893335115acacfe2989bcbb2e978e4fedc8abc090363e205d975c1fdc35e55ba4daa4b5d5ab7a22c40f547a4a0fd1c683dfff10551c708ff8c34ea4e175cb9bf2313865308fa23601e5a610e2f76838be7ded3b4d3a2c49d2d40fa20db51d1cc8ab20d330bb0dadb88b1a12853f0ecb7c7632947b098dcf435a54566bcf92befd55e03ee2a57d17524cd3d59d6e800c66059067e5eb6edb81946b3286950748240ec9afa4389f9b62bc92f94ec0fba9e64d6dc2f455f816016a4c5f3d507382ed5d3365",
|
|
||||||
"23");
|
|
||||||
|
|
||||||
when:
|
|
||||||
OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(f)
|
|
||||||
|
|
||||||
then:
|
|
||||||
openSSHKnownHosts.verify("schmizz.net", 22, key)
|
|
||||||
openSSHKnownHosts.verify("69.163.155.180", 22, key)
|
|
||||||
!openSSHKnownHosts.verify("69.163.155.18", 22, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should parse and verify hashed host entry"() {
|
def "should parse and verify hashed host entry"() {
|
||||||
given:
|
given:
|
||||||
def f = writeKnownHosts("|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==");
|
def f = knownHosts("|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==");
|
||||||
final PublicKey key = KeyUtil
|
final PublicKey key = KeyUtil
|
||||||
.newRSAPublicKey(
|
.newRSAPublicKey(
|
||||||
"e8ff4797075a861db9d2319960a836b2746ada3da514955d2921f2c6a6c9895cbd557f604e43772b6303e3cab2ad82d83b21acdef4edb72524f9c2bef893335115acacfe2989bcbb2e978e4fedc8abc090363e205d975c1fdc35e55ba4daa4b5d5ab7a22c40f547a4a0fd1c683dfff10551c708ff8c34ea4e175cb9bf2313865308fa23601e5a610e2f76838be7ded3b4d3a2c49d2d40fa20db51d1cc8ab20d330bb0dadb88b1a12853f0ecb7c7632947b098dcf435a54566bcf92befd55e03ee2a57d17524cd3d59d6e800c66059067e5eb6edb81946b3286950748240ec9afa4389f9b62bc92f94ec0fba9e64d6dc2f455f816016a4c5f3d507382ed5d3365",
|
"e8ff4797075a861db9d2319960a836b2746ada3da514955d2921f2c6a6c9895cbd557f604e43772b6303e3cab2ad82d83b21acdef4edb72524f9c2bef893335115acacfe2989bcbb2e978e4fedc8abc090363e205d975c1fdc35e55ba4daa4b5d5ab7a22c40f547a4a0fd1c683dfff10551c708ff8c34ea4e175cb9bf2313865308fa23601e5a610e2f76838be7ded3b4d3a2c49d2d40fa20db51d1cc8ab20d330bb0dadb88b1a12853f0ecb7c7632947b098dcf435a54566bcf92befd55e03ee2a57d17524cd3d59d6e800c66059067e5eb6edb81946b3286950748240ec9afa4389f9b62bc92f94ec0fba9e64d6dc2f455f816016a4c5f3d507382ed5d3365",
|
||||||
@@ -89,7 +49,7 @@ class OpenSSHKnownHostsSpec extends Specification {
|
|||||||
|
|
||||||
def "should parse and verify v1 host entry"() {
|
def "should parse and verify v1 host entry"() {
|
||||||
given:
|
given:
|
||||||
def f = writeKnownHosts("test.com,1.1.1.1 2048 35 22017496617994656680820635966392838863613340434802393112245951008866692373218840197754553998457793202561151141246686162285550121243768846314646395880632789308110750881198697743542374668273149584280424505890648953477691795864456749782348425425954366277600319096366690719901119774784695056100331902394094537054256611668966698242432417382422091372756244612839068092471592121759862971414741954991375710930168229171638843329213652899594987626853020377726482288618521941129157643483558764875338089684351824791983007780922947554898825663693324944982594850256042689880090306493029526546183035567296830604572253312294059766327")
|
def f = knownHosts("test.com,1.1.1.1 2048 35 22017496617994656680820635966392838863613340434802393112245951008866692373218840197754553998457793202561151141246686162285550121243768846314646395880632789308110750881198697743542374668273149584280424505890648953477691795864456749782348425425954366277600319096366690719901119774784695056100331902394094537054256611668966698242432417382422091372756244612839068092471592121759862971414741954991375710930168229171638843329213652899594987626853020377726482288618521941129157643483558764875338089684351824791983007780922947554898825663693324944982594850256042689880090306493029526546183035567296830604572253312294059766327")
|
||||||
def key = KeyUtil.newRSAPublicKey("ae6983ed63a33afc69fe0b88b4ba14393120a0b66e1460916a8390ff109139cd14f4e1701ab5c5feeb479441fe2091d04c0ba7d3fa1756b80ed103657ab53b5d7daa38af22f59f9cbfc16892d4ef1f8fd3ae49663c295be1f568a160d54328fbc2c0598f48d32296b1b9942336234952c440cda1bfac904e3391db98e52f9b1de229adc18fc34a9a569717aa9a5b1145e73b8a8394354028d02054ca760243fb8fc1575490607dd098e698e02b5d8bdf22d55ec958245222ef4c65b8836b9f13674a2d2895a587bfd4423b4eeb6d3ef98451640e3d63d2fc6a761ffd34446abab028494caf36d67ffd65298d69f19f2d90bae4c207b671db563a08f1bb9bf237",
|
def key = KeyUtil.newRSAPublicKey("ae6983ed63a33afc69fe0b88b4ba14393120a0b66e1460916a8390ff109139cd14f4e1701ab5c5feeb479441fe2091d04c0ba7d3fa1756b80ed103657ab53b5d7daa38af22f59f9cbfc16892d4ef1f8fd3ae49663c295be1f568a160d54328fbc2c0598f48d32296b1b9942336234952c440cda1bfac904e3391db98e52f9b1de229adc18fc34a9a569717aa9a5b1145e73b8a8394354028d02054ca760243fb8fc1575490607dd098e698e02b5d8bdf22d55ec958245222ef4c65b8836b9f13674a2d2895a587bfd4423b4eeb6d3ef98451640e3d63d2fc6a761ffd34446abab028494caf36d67ffd65298d69f19f2d90bae4c207b671db563a08f1bb9bf237",
|
||||||
"23")
|
"23")
|
||||||
when:
|
when:
|
||||||
@@ -99,28 +59,82 @@ class OpenSSHKnownHostsSpec extends Specification {
|
|||||||
knownHosts.verify("test.com", 22, key)
|
knownHosts.verify("test.com", 22, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
def "should ignore malformed line"() {
|
def "should check all host entries for key"() {
|
||||||
given:
|
given:
|
||||||
def f = writeKnownHosts("M36Lo+Ik5ukNugvvoNFlpnyiHMmtKxt3FpyEfYuryXjNqMNWHn/ARVnpUIl5jRLTB7WBzyLYMG7X5nuoFL9zYqKGtHxChbDunxMVbspw5WXI9VN+qxcLwmITmpEvI9ApyS/Ox2ZyN7zw==")
|
def f = knownHosts("""
|
||||||
|
host1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCiYp2IDgzDFhl8T4TRLIhEljvEixz1YN0XWh4dYh0REGK9T4QKiyb28EztPMdcOtz1uyX5rUGYXX9hj99S4SiU=
|
||||||
|
host1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLTjA7hduYGmvV9smEEsIdGLdghSPD7kL8QarIIOkeXmBh+LTtT/T1K+Ot/rmXCZsP8hoUXxbvN+Tks440Ci0ck=
|
||||||
|
""")
|
||||||
|
def pk = new Buffer.PlainBuffer(Base64.decode("AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLTjA7hduYGmvV9smEEsIdGLdghSPD7kL8QarIIOkeXmBh+LTtT/T1K+Ot/rmXCZsP8hoUXxbvN+Tks440Ci0ck=")).readPublicKey()
|
||||||
when:
|
when:
|
||||||
OpenSSHKnownHosts knownHosts = new OpenSSHKnownHosts(f)
|
def knownhosts = new OpenSSHKnownHosts(f)
|
||||||
|
|
||||||
then:
|
then:
|
||||||
knownHosts.entries().size() == 0
|
knownhosts.verify("host1", 22, pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
File writeKnownHosts(String line)
|
def "should not fail on bad base64 entry"() {
|
||||||
throws IOException {
|
given:
|
||||||
File known_hosts = temp.newFile("known_hosts");
|
def f = knownHosts("""
|
||||||
FileWriter fileWriter = new FileWriter(known_hosts);
|
host1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTIDgzDFhl8T4TRLIhEljvEixz1YN0XWh4dYh0REGK9T4QKiyb28EztPMdcOtz1uyX5rUGYXX9hj99S4SiU=
|
||||||
BufferedWriter writer = new BufferedWriter(fileWriter);
|
host1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLTjA7hduYGmvV9smEEsIdGLdghSPD7kL8QarIIOkeXmBh+LTtT/T1K+Ot/rmXCZsP8hoUXxbvN+Tks440Ci0ck=
|
||||||
writer.write(line);
|
""")
|
||||||
writer.write("\r\n");
|
def pk = new Buffer.PlainBuffer(Base64.decode("AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLTjA7hduYGmvV9smEEsIdGLdghSPD7kL8QarIIOkeXmBh+LTtT/T1K+Ot/rmXCZsP8hoUXxbvN+Tks440Ci0ck=")).readPublicKey()
|
||||||
writer.flush();
|
when:
|
||||||
writer.close();
|
def knownhosts = new OpenSSHKnownHosts(f)
|
||||||
return known_hosts;
|
|
||||||
|
then:
|
||||||
|
knownhosts.verify("host1", 22, pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "should mark bad line and not fail"() {
|
||||||
|
given:
|
||||||
|
def f = knownHosts("M36Lo+Ik5ukNugvvoNFlpnyiHMmtKxt3FpyEfYuryXjNqMNWHn/ARVnpUIl5jRLTB7WBzyLYMG7X5nuoFL9zYqKGtHxChbDunxMVbspw5WXI9VN+qxcLwmITmpEvI9ApyS/Ox2ZyN7zw==\n")
|
||||||
|
|
||||||
|
when:
|
||||||
|
def knownhosts = new OpenSSHKnownHosts(f)
|
||||||
|
|
||||||
|
then:
|
||||||
|
knownhosts.entries().size() == 1
|
||||||
|
knownhosts.entries().get(0) instanceof OpenSSHKnownHosts.BadHostEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "should add comment for #type line"() {
|
||||||
|
given:
|
||||||
|
def f = knownHosts(s)
|
||||||
|
|
||||||
|
when:
|
||||||
|
def knownHosts = new OpenSSHKnownHosts(f)
|
||||||
|
|
||||||
|
then:
|
||||||
|
knownHosts.entries().size() == 1
|
||||||
|
knownHosts.entries().get(0) instanceof OpenSSHKnownHosts.CommentEntry
|
||||||
|
|
||||||
|
where:
|
||||||
|
type << ["newline", "comment"]
|
||||||
|
s << ["\n", "#comment\n"]
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "should match any host name from multi-host line"() {
|
||||||
|
given:
|
||||||
|
def f = knownHosts("schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==")
|
||||||
|
def pk = new Buffer.PlainBuffer(Base64.decode("AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==")).readPublicKey()
|
||||||
|
|
||||||
|
when:
|
||||||
|
def knownHosts = new OpenSSHKnownHosts(f)
|
||||||
|
|
||||||
|
then:
|
||||||
|
knownHosts.verify(h, 22, pk)
|
||||||
|
|
||||||
|
where:
|
||||||
|
h << ["schmizz.net", "69.163.155.180"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def knownHosts(String s) {
|
||||||
|
def f = temp.newFile("known_hosts")
|
||||||
|
f.write(s)
|
||||||
|
return f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,6 @@ class FileSystemFileSpec extends Specification {
|
|||||||
def child = file.getChild("bar//etc/passwd")
|
def child = file.getChild("bar//etc/passwd")
|
||||||
|
|
||||||
then:
|
then:
|
||||||
child.getFile().getPath() endsWith "foo/bar/etc/passwd"
|
child.getFile().getPath().replace('\\', '/') endsWith "foo/bar/etc/passwd"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user