Java代碼實現SFTP服務


前言

因項目需要,需要在服務端實現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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM