首页 > 编程语言 > 详细

Java代码实现SFTP服务

时间:2021-07-19 23:01:14      阅读:55      评论:0      收藏:0      [点我收藏+]

前言

因项目需要,需要在服务端实现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>

Java代码实现SFTP服务

原文:https://www.cnblogs.com/farghost/p/15031822.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!