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