因项目需要,需要在服务端实现SFTP功能。网上找了通过sshd来实现SFTP功能的例子,在本地环境上SFTP服务能够正常启动,但是SFTP客户端却怎么也连不上。于是有了如下的调试过程:
<!-- 项目用的jdk版本是1.7,所以选了个用jdk1.7编译的sshd-core版本 -->
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.14.0</version>
</dependency>
<!-- 不引入会报异常,但是不影响功能使用。这里引入主要为了查看日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
debug: true
public class NfmsSftpServer {
private static NfmsSftpServer nfmsSftpServer = new NfmsSftpServer();
public static NfmsSftpServer getInstance() {
return nfmsSftpServer;
}
private NfmsSftpServer() {
init();
}
private void init() {
SshServer sshd = SshServer.setUpDefaultServer();
// 设置sftp绑定端口
sshd.setPort(2222);
// 设置密钥文件,不存在会自动创建
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("D:\\key"));
sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
// 用户名密码校验
sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
@Override
public boolean authenticate(String username, String password, ServerSession session) {
return "nfms".equals(username) && "nfms".equals(password);
}
});
// 设置sftp默认的访问目录
sshd.setFileSystemFactory(new VirtualFileSystemFactory("D:\\"));
sshd.setCommandFactory(new ScpCommandFactory());
sshd.setShellFactory(new ProcessShellFactory());
//启动ssh服务
try {
sshd.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
错误1:Unable to negotiate with ::1 port 2222: no matching host key type found. Their offer: ssh-dss
解决方法:加上-oHostKeyAlgorithms=+ssh-dss
PS C:\Users\farghost> sftp -oPort=2222 nfms@localhost
Unable to negotiate with ::1 port 2222: no matching host key type found. Their offer: ssh-dss
Connection closed
PS C:\Users\farghost>
错误2:Connection closed by ::1 port 2222
PS C:\Users\farghost> sftp -oPort=2222 -oHostKeyAlgorithms=+ssh-dss nfms@localhost
Connection closed by ::1 port 2222
Connection closed
PS C:\Users\farghost>
查看日志,打印如下异常
java.security.InvalidKeyException: The security strength of SHA-1 digest algorithm is not sufficient for this key size
at sun.security.provider.DSA.checkKey(DSA.java:110)
at sun.security.provider.DSA.engineInitSign(DSA.java:142)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1329)
at java.security.Signature.initSign(Signature.java:621)
at org.apache.sshd.common.signature.AbstractSignature.init(AbstractSignature.java:47)
at org.apache.sshd.server.kex.AbstractDHGServer.next(AbstractDHGServer.java:91)
at org.apache.sshd.common.session.AbstractSession.doHandleMessage(AbstractSession.java:425)
at org.apache.sshd.common.session.AbstractSession.handleMessage(AbstractSession.java:326)
at org.apache.sshd.common.session.AbstractSession.decode(AbstractSession.java:780)
at org.apache.sshd.common.session.AbstractSession.messageReceived(AbstractSession.java:308)
at org.apache.sshd.common.AbstractSessionIoHandler.messageReceived(AbstractSessionIoHandler.java:54)
at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:184)
at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:170)
at org.apache.sshd.common.io.nio2.Nio2CompletionHandler$1.run(Nio2CompletionHandler.java:32)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.sshd.common.io.nio2.Nio2CompletionHandler.completed(Nio2CompletionHandler.java:30)
at sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:126)
at sun.nio.ch.Invoker$2.run(Invoker.java:218)
at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
面向百度后得知:DSA的keySize是1024,但是我们的key文件是sshd自己生成的,为什么keySize会不对呢?
带着上面的问题我去翻了sshd的源码,最终找到了解决方法!
从密钥文件传入代码开始看sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("D:\key"));
org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider
public SimpleGeneratorHostKeyProvider(String path) {
super(path);
}
org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider
// 默认DSA算法
private String algorithm = "DSA";
protected AbstractGeneratorHostKeyProvider(String path) {
this.path = path;
}
// 找到path的代码
public synchronized Iterable<KeyPair> loadKeys() {
if (keyPair == null) {
if (path != null) {
File f = new File(path);
if (f.exists() && f.isFile()) {
keyPair = readKeyPair(f);
}
}
if (keyPair == null) {
keyPair = generateKeyPair(algorithm);
if (keyPair != null && path != null) {
writeKeyPair(keyPair, new File(path));
}
}
if (keyPair == null) {
return Collections.emptyList();
}
}
return Collections.singleton(keyPair);
}
// 第一次进入的时候,我们是没有密钥文件的,所以进入generateKeyPair方法查看密钥文件生成过程
// 在这里看到了keySize字段,很熟悉有没有?这不就是我们在找的keySize嘛!自己传入1024文件不就解决了嘛!
private KeyPair generateKeyPair(String algorithm) {
try {
KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
if (keySpec != null) {
generator.initialize(keySpec);
} else if (keySize != 0) {
generator.initialize(keySize);
}
log.info("Generating host key...");
KeyPair kp = generator.generateKeyPair();
return kp;
} catch (Exception e) {
log.warn("Unable to generate keypair", e);
return null;
}
}
// 往上查找keySize赋值的方法,找到了!构造方法可以直接传入
protected AbstractGeneratorHostKeyProvider(String path, String algorithm, int keySize) {
this.path = path;
this.algorithm = algorithm;
this.keySize = keySize;
}
修改测试代码
// sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("D:\\key"));
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("D:\\key", "DSA", 1024));
删除原来生成的key,重新执行测试代码,成啦!
PS C:\Users\farghost> sftp -oPort=2222 -oHostKeyAlgorithms=+ssh-dss nfms@localhost
The authenticity of host ‘[localhost]:2222 ([::1]:2222)‘ can‘t be established.
DSA key fingerprint is SHA256:bGlOA2L0nWJ8muE+2h9utgw3WGldrzFnHw3unH6+KDA.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Warning: Permanently added ‘[localhost]:2222‘ (DSA) to the list of known hosts.
Password authentication
Password:
Connected to localhost.
sftp>
sftp> ls -l
drwxrwxrwx 1 nfms nfms 0 Jun 16 14:57 $RECYCLE.BIN
drwxrwxrwx 1 nfms nfms 0 Jun 16 15:47 Code
drwxrwxrwx 1 nfms nfms 0 Jul 12 09:24 Database
drwxrwxrwx 1 nfms nfms 4096 Jul 12 09:59 Genew
drwxrwxrwx 1 nfms nfms 0 Jul 9 17:01 Java
drwxrwxrwx 1 nfms nfms 0 Jun 17 17:20 MyLife
drwxrwxrwx 1 nfms nfms 4096 Jul 10 11:38 Software
drwxrwxrwx 1 nfms nfms 0 Jul 9 16:55 SpringCloud
drwxrwxrwx 1 nfms nfms 0 Jul 9 16:55 SpringFramework
drwxrwxrwx 1 nfms nfms 4096 Jun 22 21:30 System Volume Information
drwxrwxrwx 1 nfms nfms 4096 Jul 9 10:51 Tools
-rwxrwxrwx 1 nfms nfms 1201 Jul 19 19:01 key
drwxrwxrwx 1 nfms nfms 0 Jul 12 10:01 oradb
drwxrwxrwx 1 nfms nfms 0 Jul 10 11:12 temp
sftp>
原文:https://www.cnblogs.com/farghost/p/15031822.html