diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java index b38cb18bff..bdee7ac3f3 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java @@ -126,9 +126,7 @@ public class DefaultSftpSessionFactory implements SessionFactory, 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, Share * The port over which the SFTP connection shall be established. If not specified, * this value defaults to 22. 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, 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, 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, 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). - * + *

+ * Required if {@link #setAllowUnknownKeys(boolean) allowUnknownKeys} is + * false (default). * @param knownHosts The known hosts. - * * @see JSch#setKnownHosts(String) */ public void setKnownHosts(String knownHosts) { @@ -189,14 +182,12 @@ public class DefaultSftpSessionFactory implements SessionFactory, 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, 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, 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, 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, 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, Share * The timeout property is used as the socket timeout parameter, as well as * the default connection timeout. Defaults to 0, 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, Share /** * Allows you to set the client version property. It's default depends on the * underlying JSch version but it will look like SSH-2.0-JSCH-0.1.45 - * * @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, 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 1. - * * @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, Share * underlying {@link com.jcraft.jsch.Session} using * {@link com.jcraft.jsch.Session#setDaemonThread(boolean)}. There, this * property will default to false, 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, Share *

* If {@link #setPassword(String) setPassword} is invoked with a non-null password, it will * override any password in the supplied {@link UserInfo}. - * + *

+ * 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}. * @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, 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, 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, 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, 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, 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, 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, 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; } } diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpServer.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpServer.java index 20e7ca1709..7b65933fe2 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpServer.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/TestSftpServer.java @@ -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(); } diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpServerTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpServerTests.java index 08a59c9d19..c2830ae65c 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpServerTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpServerTests.java @@ -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 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(); diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java index b5e3f4d414..97d09309a8 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java @@ -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); diff --git a/spring-integration-sftp/src/test/resources/id_rsa_pp b/spring-integration-sftp/src/test/resources/id_rsa_pp new file mode 100644 index 0000000000..7c7cba1655 --- /dev/null +++ b/spring-integration-sftp/src/test/resources/id_rsa_pp @@ -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----- diff --git a/spring-integration-sftp/src/test/resources/id_rsa_pp.pub b/spring-integration-sftp/src/test/resources/id_rsa_pp.pub new file mode 100644 index 0000000000..50cf189afa --- /dev/null +++ b/spring-integration-sftp/src/test/resources/id_rsa_pp.pub @@ -0,0 +1 @@ +AAAAB3NzaC1yc2EAAAADAQABAAABAQC6MIzgyVi8G1+HRhFHPWRH+3w/8/uxtiuIfb4puVPjHI53Lvf5odzfhv0T6Z2/jSXmI3I6dpjbsgiptdCTX4kqUFLXxkuJR4LHatNtgO1w32aVIdAvfj7KtrL3SmP2XWqQGVcUWHEn2H1RHFHKdC6ArYFb1X8p5N/BHSQjuttaeVi9FsDxvC5euIbtDEEJmmvjjfWlI1m/6qCqMYxDWA9i9APU/rB0QwFNUQ6HuZ2QzEaU/hQMGmqgW5o1I/W8JR0bqis8wZQDLv1fwCkXpWG5BAuiJH+FJMxRAkfEMBpVwO7Sl0ufePVuSM2BMAAe+4a75sVp8ahbOId6y0GUTeJl diff --git a/src/reference/asciidoc/sftp.adoc b/src/reference/asciidoc/sftp.adoc index 23dd4dcef0..869a4d3477 100644 --- a/src/reference/asciidoc/sftp.adoc +++ b/src/reference/asciidoc/sftp.adoc @@ -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]] diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 85e853f9a7..f347eb7420 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -309,9 +309,12 @@ See <> and <> 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 <> for more information. ==== Websocket Changes