INT-3796: More SFTP Refinements

JIRA: https://jira.spring.io/browse/INT-3796

When a `UserInfo` is provided, use it for the password and passphrase.
Disallow local properies when a `UserInfo` is provided.

Simple polishing and fixing `TestSftpServer` for the `this.server.stop(true);`
to avoid `port is already in use` for other tests which use default SSH port.
This commit is contained in:
Gary Russell
2015-08-08 11:16:00 -04:00
committed by Artem Bilan
parent 56bfe6b0ac
commit e82bfdc9a6
8 changed files with 158 additions and 98 deletions

View File

@@ -126,9 +126,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* The url of the host you want connect to. This is a mandatory property.
*
* @param host The host.
*
* @see JSch#getSession(String, String, int)
*/
public void setHost(String host) {
@@ -139,9 +137,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
* The port over which the SFTP connection shall be established. If not specified,
* this value defaults to <code>22</code>. If specified, this properties must
* be a positive number.
*
* @param port The port.
*
* @see JSch#getSession(String, String, int)
*/
public void setPort(int port) {
@@ -150,9 +146,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* The remote user to use. This is a mandatory property.
*
* @param user The user.
*
* @see JSch#getSession(String, String, int)
*/
public void setUser(String user) {
@@ -161,11 +155,11 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* The password to authenticate against the remote host. If a password is
* not provided, then the {@link DefaultSftpSessionFactory#privateKey} is
* not provided, then a {@link DefaultSftpSessionFactory#setPrivateKey(Resource) privateKey} is
* mandatory.
*
* Not allowed if {@link #setUserInfo(UserInfo) userInfo} is provided - the password is obtained
* from that object.
* @param password The password.
*
* @see com.jcraft.jsch.Session#setPassword(String)
*/
public void setPassword(String password) {
@@ -175,11 +169,10 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* Specifies the filename that will be used for a host key repository.
* The file has the same format as OpenSSH's known_hosts file.
* Required if {@link #setAllowUnknownKeys(boolean) allowUnknownKeys} is
* false (default).
*
* <p>
* <b>Required if {@link #setAllowUnknownKeys(boolean) allowUnknownKeys} is
* false (default).</b>
* @param knownHosts The known hosts.
*
* @see JSch#setKnownHosts(String)
*/
public void setKnownHosts(String knownHosts) {
@@ -189,14 +182,12 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* Allows you to set a {@link Resource}, which represents the location of the
* private key used for authenticating against the remote host. If the privateKey
* is not provided, then the {@link DefaultSftpSessionFactory#setPassword(String)}
* property is mandatory.
*
* is not provided, then the {@link DefaultSftpSessionFactory#setPassword(String) password}
* property is mandatory (or {@link #setUserInfo(UserInfo) userInfo} that returns a
* password.
* @param privateKey The private key.
*
* @see JSch#addIdentity(String)
* @see JSch#addIdentity(String, String)
*
*/
public void setPrivateKey(Resource privateKey) {
this.privateKey = privateKey;
@@ -204,9 +195,9 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* The password for the private key. Optional.
*
* Not allowed if {@link #setUserInfo(UserInfo) userInfo} is provided - the passphrase is obtained
* from that object.
* @param privateKeyPassphrase The private key passphrase.
*
* @see JSch#addIdentity(String, String)
*/
public void setPrivateKeyPassphrase(String privateKeyPassphrase) {
@@ -216,9 +207,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* Using {@link Properties}, you can set additional configuration settings on
* the underlying JSch {@link com.jcraft.jsch.Session}.
*
* @param sessionConfig The session configuration properties.
*
* @see com.jcraft.jsch.Session#setConfig(Properties)
*/
public void setSessionConfig(Properties sessionConfig) {
@@ -228,12 +217,10 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* Allows for specifying a JSch-based {@link Proxy}. If set, then the proxy
* object is used to create the connection to the remote host.
*
* @param proxy The proxy.
*
* @see com.jcraft.jsch.Session#setProxy(Proxy)
*/
public void setProxy(Proxy proxy){
public void setProxy(Proxy proxy) {
this.proxy = proxy;
}
@@ -241,12 +228,10 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
* Allows you to pass in a {@link SocketFactory}. The socket factory is used
* to create a socket to the target host. When a {@link Proxy} is used, the
* socket factory is passed to the proxy. By default plain TCP sockets are used.
*
* @param socketFactory The socket factory.
*
* @see com.jcraft.jsch.Session#setSocketFactory(SocketFactory)
*/
public void setSocketFactory(SocketFactory socketFactory){
public void setSocketFactory(SocketFactory socketFactory) {
this.socketFactory = socketFactory;
}
@@ -254,9 +239,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
* The timeout property is used as the socket timeout parameter, as well as
* the default connection timeout. Defaults to <code>0</code>, which means,
* that no timeout will occur.
*
* @param timeout The timeout.
*
* @see com.jcraft.jsch.Session#setTimeout(int)
*/
public void setTimeout(Integer timeout) {
@@ -266,36 +249,30 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* Allows you to set the client version property. It's default depends on the
* underlying JSch version but it will look like <code>SSH-2.0-JSCH-0.1.45</code>
*
* @param clientVersion The client version.
*
* @see com.jcraft.jsch.Session#setClientVersion(String)
*/
public void setClientVersion(String clientVersion){
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
}
/**
* Sets the host key alias, used when comparing the host key to the known
* hosts list.
*
* @param hostKeyAlias The host key alias.
*
* @see com.jcraft.jsch.Session#setHostKeyAlias(String)
*/
public void setHostKeyAlias(String hostKeyAlias){
public void setHostKeyAlias(String hostKeyAlias) {
this.hostKeyAlias = hostKeyAlias;
}
/**
* Sets the timeout interval (milliseconds) before a server alive message is
* sent, in case no message is received from the server.
*
* @param serverAliveInterval The server alive interval.
*
* @see com.jcraft.jsch.Session#setServerAliveInterval(int)
*/
public void setServerAliveInterval(Integer serverAliveInterval){
public void setServerAliveInterval(Integer serverAliveInterval) {
this.serverAliveInterval = serverAliveInterval;
}
@@ -303,12 +280,10 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
* Specifies the number of server-alive messages, which will be sent without
* any reply from the server before disconnecting. If not set, this property
* defaults to <code>1</code>.
*
* @param serverAliveCountMax The server alive count max.
*
* @see com.jcraft.jsch.Session#setServerAliveCountMax(int)
*/
public void setServerAliveCountMax(Integer serverAliveCountMax){
public void setServerAliveCountMax(Integer serverAliveCountMax) {
this.serverAliveCountMax = serverAliveCountMax;
}
@@ -318,12 +293,10 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
* underlying {@link com.jcraft.jsch.Session} using
* {@link com.jcraft.jsch.Session#setDaemonThread(boolean)}. There, this
* property will default to <code>false</code>, if not explicitly set.
*
* @param enableDaemonThread true to enable a daemon thread.
*
* @see com.jcraft.jsch.Session#setDaemonThread(boolean)
*/
public void setEnableDaemonThread(Boolean enableDaemonThread){
public void setEnableDaemonThread(Boolean enableDaemonThread) {
this.enableDaemonThread = enableDaemonThread;
}
@@ -342,7 +315,10 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
* <p>
* If {@link #setPassword(String) setPassword} is invoked with a non-null password, it will
* override any password in the supplied {@link UserInfo}.
*
* <p>
* <b>NOTE: When this is provided, the {@link #setPassword(String) password} and
* {@link #setPrivateKeyPassphrase(String) passphrase} are not allowed because those values
* will be obtained from the {@link UserInfo}.</b>
* @param userInfo the UserInfo.
* @see com.jcraft.jsch.Session#setUserInfo(com.jcraft.jsch.UserInfo)
* @since 4.1.7
@@ -356,7 +332,6 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
* connecting to an unknown host or when a host's key has changed (see
* {@link #setKnownHosts(String) knownHosts}). Default false (since 4.2).
* Set to true if a knownHosts file is not provided.
*
* @param allowUnknownKeys true to allow connecting to unknown hosts.
* @since 4.1.7
*/
@@ -369,7 +344,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
Assert.hasText(this.host, "host must not be empty");
Assert.hasText(this.user, "user must not be empty");
Assert.isTrue(this.port >= 0, "port must be a positive number");
Assert.isTrue(StringUtils.hasText(this.password) || this.privateKey != null,
Assert.isTrue(StringUtils.hasText(this.userInfoWrapper.getPassword()) || this.privateKey != null,
"either a password or a private key is required");
try {
JSchSessionWrapper jschSession;
@@ -421,49 +396,53 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
// private key
if (this.privateKey != null) {
byte[] keyByteArray = StreamUtils.copyToByteArray(this.privateKey.getInputStream());
if (StringUtils.hasText(this.privateKeyPassphrase)) {
this.jsch.addIdentity(this.user, keyByteArray, null, this.privateKeyPassphrase.getBytes());
String passphrase = this.userInfoWrapper.getPassphrase();
if (StringUtils.hasText(passphrase)) {
this.jsch.addIdentity(this.user, keyByteArray, null, passphrase.getBytes());
}
else {
this.jsch.addIdentity(this.user, keyByteArray, null, null);
}
}
com.jcraft.jsch.Session jschSession = this.jsch.getSession(this.user, this.host, this.port);
if (this.sessionConfig != null){
if (this.sessionConfig != null) {
jschSession.setConfig(this.sessionConfig);
}
if (StringUtils.hasText(this.password)) {
jschSession.setPassword(this.password);
String password = this.userInfoWrapper.getPassword();
if (StringUtils.hasText(password)) {
jschSession.setPassword(password);
}
jschSession.setUserInfo(this.userInfoWrapper);
try {
if (proxy != null){
if (proxy != null) {
jschSession.setProxy(proxy);
}
if (socketFactory != null){
if (socketFactory != null) {
jschSession.setSocketFactory(socketFactory);
}
if (timeout != null){
if (timeout != null) {
jschSession.setTimeout(timeout);
}
if (StringUtils.hasText(clientVersion)){
if (StringUtils.hasText(clientVersion)) {
jschSession.setClientVersion(clientVersion);
}
if (StringUtils.hasText(hostKeyAlias)){
if (StringUtils.hasText(hostKeyAlias)) {
jschSession.setHostKeyAlias(hostKeyAlias);
}
if (serverAliveInterval != null){
if (serverAliveInterval != null) {
jschSession.setServerAliveInterval(serverAliveInterval);
}
if (serverAliveCountMax != null){
if (serverAliveCountMax != null) {
jschSession.setServerAliveCountMax(serverAliveCountMax);
}
if (enableDaemonThread != null){
if (enableDaemonThread != null) {
jschSession.setDaemonThread(enableDaemonThread);
}
} catch (Exception e) {
throw new BeanCreationException("Attempt to set additional properties of the com.jcraft.jsch.Session resulted in error: " + e.getMessage(), e);
}
catch (Exception e) {
throw new BeanCreationException("Attempt to set additional properties of " +
"the com.jcraft.jsch.Session resulted in error: " + e.getMessage(), e);
}
return jschSession;
}
@@ -498,7 +477,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
/**
* Convenience to retrieve enclosing factory's UserInfo.
* @return
* @return the {@link #userInfo} or null if not present.
*/
private UserInfo getDelegate() {
return DefaultSftpSessionFactory.this.userInfo;
@@ -507,27 +486,25 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
@Override
public String getPassphrase() {
if (hasDelegate()) {
Assert.state(!StringUtils.hasText(DefaultSftpSessionFactory.this.privateKeyPassphrase),
"When a 'UserInfo' is provided, 'privateKeyPassphrase' is not allowed");
return getDelegate().getPassphrase();
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No UserInfo provided for passphrase, returning: null");
}
return null;
return DefaultSftpSessionFactory.this.privateKeyPassphrase;
}
}
@Override
public String getPassword() {
if (hasDelegate()) {
if (DefaultSftpSessionFactory.this.password != null) {
logger.debug("Password is obtained from the factory, not the supplied UserInfo");
}
else {
return getDelegate().getPassword();
}
Assert.state(!StringUtils.hasText(DefaultSftpSessionFactory.this.password),
"When a 'UserInfo' is provided, 'password' is not allowed");
return getDelegate().getPassword();
}
else {
return DefaultSftpSessionFactory.this.password;
}
return DefaultSftpSessionFactory.this.password;
}
@Override
@@ -558,6 +535,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
@Override
public boolean promptYesNo(String message) {
logger.info(message);
if (hasDelegate()) {
return getDelegate().promptYesNo(message);
}
@@ -582,18 +560,18 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
@Override
public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt,
boolean[] echo) {
if (hasDelegate()) {
if (getDelegate() instanceof UIKeyboardInteractive) {
return ((UIKeyboardInteractive) getDelegate()).promptKeyboardInteractive(destination, name,
instruction, prompt, echo);
boolean[] echo) {
if (hasDelegate() && getDelegate() instanceof UIKeyboardInteractive) {
return ((UIKeyboardInteractive) getDelegate()).promptKeyboardInteractive(destination, name,
instruction, prompt, echo);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No UIKeyboardInteractive provided - " + destination + ":" + name + ":" + instruction
+ ":" + Arrays.asList(prompt) + ":" + Arrays.asList(echo));
}
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("No UserInfo provided - " + destination + ":" + name + ":" + instruction + ":"
+ Arrays.asList(prompt) + ":" + Arrays.asList(echo));
}
return null;
}
}

View File

@@ -139,7 +139,7 @@ public class TestSftpServer implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
this.server.stop();
this.server.stop(true);
this.sftpFolder.delete();
this.localFolder.delete();
}

View File

@@ -59,7 +59,6 @@ import com.jcraft.jsch.ChannelSftp.LsEntry;
*/
public class SftpServerTests {
@SuppressWarnings("unchecked")
@Test
public void testUcPw() throws Exception {
final int port = SocketUtils.findAvailableServerSocket();
@@ -95,11 +94,20 @@ public class SftpServerTests {
}
@Test
@SuppressWarnings("unchecked")
public void testPubPrivKey() throws Exception {
testKeyExchange("id_rsa.pub", "id_rsa", null);
}
@Test
public void testPubPrivKeyPassphrase() throws Exception {
testKeyExchange("id_rsa_pp.pub", "id_rsa_pp", "secret");
}
private void testKeyExchange(String pubKey, String privKey, String passphrase)
throws Exception, IOException, InterruptedException {
final int port = SocketUtils.findAvailableServerSocket();
SshServer server = SshServer.setUpDefaultServer();
final PublicKey allowedKey = decodePublicKey();
final PublicKey allowedKey = decodePublicKey(pubKey);
try {
server.setPublickeyAuthenticator(new PublickeyAuthenticator() {
@@ -122,8 +130,9 @@ public class SftpServerTests {
f.setPort(port);
f.setUser("user");
f.setAllowUnknownKeys(true);
InputStream stream = new ClassPathResource("id_rsa").getInputStream();
InputStream stream = new ClassPathResource(privKey).getInputStream();
f.setPrivateKey(new ByteArrayResource(StreamUtils.copyToByteArray(stream)));
f.setPrivateKeyPassphrase(passphrase);
Session<LsEntry> session = f.getSession();
doTest(server, session);
}
@@ -132,8 +141,8 @@ public class SftpServerTests {
}
}
private PublicKey decodePublicKey() throws Exception {
InputStream stream = new ClassPathResource("id_rsa.pub").getInputStream();
private PublicKey decodePublicKey(String key) throws Exception {
InputStream stream = new ClassPathResource(key).getInputStream();
byte[] decodeBuffer = Base64.decodeBase64(StreamUtils.copyToByteArray(stream));
ByteBuffer bb = ByteBuffer.wrap(decodeBuffer);
int len = bb.getInt();

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.sftp.session;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -44,6 +45,7 @@ import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.integration.test.util.SocketUtils;
import org.springframework.integration.test.util.TestUtils;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.UserInfo;
@@ -117,6 +119,41 @@ public class SftpSessionFactoryTests {
}
}
@Test
public void testPasswordPassPhraseViaUserInfo() throws Exception {
DefaultSftpSessionFactory f = new DefaultSftpSessionFactory();
f.setHost("localhost");
f.setPort(9999);
f.setUser("user");
f.setAllowUnknownKeys(true);
UserInfo ui = mock(UserInfo.class);
when(ui.getPassword()).thenReturn("pass");
when(ui.getPassphrase()).thenReturn("pp").thenReturn(null);
f.setUserInfo(ui);
UserInfo userInfo = TestUtils.getPropertyValue(f, "userInfoWrapper", UserInfo.class);
assertEquals("pass", userInfo.getPassword());
f.setPassword("foo");
try {
userInfo.getPassword();
fail("expected Exception");
}
catch (IllegalStateException e) {
assertThat(e.getMessage(), startsWith("When a 'UserInfo' is provided, 'password' is not allowed"));
}
assertEquals("pp", userInfo.getPassphrase());
f.setPrivateKeyPassphrase("bar");
try {
userInfo.getPassphrase();
fail("expected Exception");
}
catch (IllegalStateException e) {
assertThat(e.getMessage(), startsWith("When a 'UserInfo' is provided, 'privateKeyPassphrase' is not allowed"));
}
f.setUserInfo(null);
assertEquals("foo", userInfo.getPassword());
assertEquals("bar", userInfo.getPassphrase());
}
@Test
public void testDefaultUserInfoFalse() throws Exception {
SshServer server = SshServer.setUpDefaultServer();
@@ -143,7 +180,7 @@ public class SftpSessionFactoryTests {
}
@Test
public void testCustomtUserInfoFalse() throws Exception {
public void testCustomUserInfoFalse() throws Exception {
SshServer server = SshServer.setUpDefaultServer();
try {
DefaultSftpSessionFactory f = createServerAndClient(server);
@@ -171,7 +208,7 @@ public class SftpSessionFactoryTests {
}
@Test
public void testCustomtUserInfoTrue() throws Exception {
public void testCustomUserInfoTrue() throws Exception {
SshServer server = SshServer.setUpDefaultServer();
try {
DefaultSftpSessionFactory f = createServerAndClient(server);

View File

@@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,26D1755B05980BA01B1E8D3B65EF98D3
J5fMQDf0HrwcnfYujq+q05GEOSEMVWMU0vr0hBtz2WvUeaFBBVAvUWbJo7PHTdDV
vEdSv+k8FazOkIZpeOW26pKNaLSuFfN+lgtAW4p4yqGQhbL8byh+Ka5uGaPH1xQj
o4exMFqbwFaHG10LJoPp5NnK+T64w8McJEihBPxv/qwtsz0YhhDVl/1eSwKsGa5y
Usfdv0QbjNDtpV7+sy7OunpaaKjb8iQ/PbFDsX0TSiy6jJflPwCYVUoh3jCEJze3
OUKwoQu7AiHmUrsnLtDuL49Q5hV+f9+IPJlzSqU5Fu8PlfCowH+e8WWLVQkh7Hht
iwOQs5UIWH+Nzbguu3Gbph5lqMtgkQwK6/PSFLQXuJmf9l6eo7E+Wh5mFWwoIENT
mKUlgVU0ymajPHcA7uOVXl5XX+Mt0DiMdGiB4N6RJ1OjWgqlRSJbVmGPrnZWmjdW
VltNux6JrO1bOgwtApCouDZhnP7/JDhM2PCa/F2+XCc6s8Hnmmt+gBw6IM4fb+YH
2gf1jx5Onp1yuBGe6tvbrfPAdXYU1R3kd2+Yf5mdsu2+xiC4wBKSfjleoHN4xJ/b
wopjQvGV5dj/VjdUt99lMHGWPr+p4NmdEBoriCgjYuRjRDvCiTV2qKZe2MU+0CVE
/jCA8iMRQnx3LMjnmJXP/96j2fyXHMowClTF43Cvdc5jh622WITOOFAIq9RfIXdS
47G6FAN54V+Qt0pXEgIOmvG+B2C24A041fo3jUPZxFRSYYuv9vG6QJby3kCsaqqt
ngYs2JSKx9CfOfGfyPbNt2/CO/bBsXgYzLR7REx5My1Mp9YsDmeIgbcd2V5hw/Me
rlSXrG4Eqs9gRDrBUvsydUOJFC1PlnIXH3VSBc6X9o4n2H6XECOsJQSeXLCaeMav
deKb0r1HvdbAYrdqw6mRM8Ok3fpSoD3mUsZQ8fp3luO3tHL1lddxHb7EsKzt/ubh
B9lEzDTcILlINlCl41X0OZKr/c+Ec8EdaSvITYJj0fvaZmDF7Wcs/dDWNft2XUaD
VcptOKbQVE1ufbrc2s1BdKOriAC6dSVKVDrAUQD/MlhT3p/YwbjSY2MqwRKztWRv
sA1Kqjg1IdUQzQizRKuHa322qnduLjHy0rb0ElrMpFe3B+OcIPs1E7Gvo4BVQ5jU
5GqHm83iaFIQaXmEsrVtCOBygVf00+WNRV3WFTOP9UEWFgtxsaHAU8UZhsDbKcZ5
l/w6kQdNElQuJA+1n1OxRZ5wIpfrRIMaxBQg2plUTVb9Tgz1qTZxHIEYPAAePfvX
tDn9SBiktj/8dhy1T0ko89yXCekpGxkU9rbmAd4Lrp6Wbc6+bKt4KzAqw319qq9U
Pslq7EKNM0Zq5pfwn1MRjzvmyHxz2sYHeij0CmuZY7xuOa4NzXt1vr6nSlIpX77W
ng/Rnd7qpyG2IWYihi6ztFHyj9h3FEcBHMk4JINLcdYOxXICz3KsRMfntrwQ4E6O
NFJ3fPpVNkk4GZcxy6idNkUBz1s8ixVWC8yi36byxE+TTRtcvqXQnJVgs63vMUlC
HVwaGau4YeUp4Nj2ZO44Srd/kQRy8yuCOPDdlJEiO6eDD4+XKedJQg1LuGeoXMVn
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1 @@
AAAAB3NzaC1yc2EAAAADAQABAAABAQC6MIzgyVi8G1+HRhFHPWRH+3w/8/uxtiuIfb4puVPjHI53Lvf5odzfhv0T6Z2/jSXmI3I6dpjbsgiptdCTX4kqUFLXxkuJR4LHatNtgO1w32aVIdAvfj7KtrL3SmP2XWqQGVcUWHEn2H1RHFHKdC6ArYFb1X8p5N/BHSQjuttaeVi9FsDxvC5euIbtDEEJmmvjjfWlI1m/6qCqMYxDWA9i9APU/rB0QwFNUQ6HuZ2QzEaU/hQMGmqgW5o1I/W8JR0bqis8wZQDLv1fwCkXpWG5BAuiJH+FJMxRAkfEMBpVwO7Sl0ufePVuSM2BMAAe+4a75sVp8ahbOId6y0GUTeJl

View File

@@ -108,8 +108,7 @@ The file has the same format as OpenSSH's _known_hosts_ file and is required and
The password to authenticate against the remote host.
If a _password_ is not provided, then the _privateKey_ property is mandatory.
Overrides the password in a custom `UserInfo`.
To use that password, leave this as the default `null`.
Not allowed if `userInfo` is set; the password is obtained from that object.
*port*
@@ -125,6 +124,7 @@ If the _privateKey_ is not provided, then the _password_ property is mandatory.
*privateKeyPassphrase*
The password for the private key.
Not allowed if `userInfo` is set; the passphrase is obtained from that object.
Optional.
*proxy*
@@ -173,8 +173,10 @@ If false, a pre-populated knownHosts file is required.
*userInfo*
Set a custom `UserInfo` used during authentication.
In particular, `promptYesNo()` is invoked when an unknown (or changed) host key is received.
In particular, be aware that `promptYesNo()` is invoked when an unknown (or changed) host key is received.
Also see `allowUnknownHosts`.
When a `UserInfo` is provided, the `password` and private key `passphrase` is obtained from it, and discrete
`password` and `privateKeyPassprase` properties cannot be set.
[[sftp-dsf]]

View File

@@ -309,9 +309,12 @@ See <<ftp-dsf>> and <<sftp-dsf>> for more information.
===== DefaultSftpSessionFactory
Previously, the `DefaultSftpSessionFactory` allowed connections to unknown hosts.
Previously, the `DefaultSftpSessionFactory` unconditionally allowed connections to unknown hosts.
This is now configurable (default false).
The factory now requires a configured `knownHosts` file unless the `allowUnknownKeys` property is `true` (default
false).
See <<sftp-unk-hosts>> for more information.
==== Websocket Changes