[〜#〜] sftp [〜#〜] サーバーを実装するために使用できるJavaライブラリーはありますか?
SFTP経由でファイルを受信しようとしていますが、SFTPサーバーの実装を見つけることができないようです。 FTP/SFTP/FTPSclientライブラリ、およびFTP/FTPSサーバーライブラリが見つかりましたが、SFTP用のサーバーはありません。
明確にするために、SFTP経由でファイルをreceiveしようとしています。アプリケーションから既存の別のサーバーにファイルを「取得」または「配置」しないでください。
現在、私のアプリケーションでは、ユーザーがローカルlinux SFTPサーバーに接続してファイルをドロップし、次にディレクトリをポーリングできますが、これは実装が貧弱だと感じています。ディレクトリの「ポーリング」という考え方は嫌いですが、残念ながらSFTPを使用する必要があります。助言がありますか?
Apache Mina SSHD を使用してSFTPサーバーをセットアップする方法:
public void setupSftpServer(){
SshServer sshd = SshServer.setUpDefaultServer();
sshd.setPort(22);
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));
List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<NamedFactory<UserAuth>>();
userAuthFactories.add(new UserAuthNone.Factory());
sshd.setUserAuthFactories(userAuthFactories);
sshd.setCommandFactory(new ScpCommandFactory());
List<NamedFactory<Command>> namedFactoryList = new ArrayList<NamedFactory<Command>>();
namedFactoryList.add(new SftpSubsystem.Factory());
sshd.setSubsystemFactories(namedFactoryList);
try {
sshd.start();
} catch (Exception e) {
e.printStackTrace();
}
}
そして、それだけです。
SFTPはSSLを介したFTPではなく、SSHを介したFTPでもないことに注意してください。 SFTPサーバーのサポートには、JavaでのSSHDの実装が必要です。あなたの最善の策はApache SSHDです、
http://mina.Apache.org/sshd-project/
SFTPを使用したことはありませんが、基本的な機能であると聞きました。
上記でWindowsでMINA 0.10.1を試し、いくつかの問題を修正しました。さらに、より良い認証とPKサポートが必要です(実稼働環境での使用はお勧めしません)。
import Java.io.File;
import Java.io.ByteArrayOutputStream;
import Java.io.DataOutputStream;
import Java.io.PrintWriter;
import Java.util.Arrays;
import Java.util.Map;
import Java.util.HashMap;
import Java.util.Scanner;
import Java.math.BigInteger;
import Java.security.PublicKey;
import Java.security.interfaces.RSAPublicKey;
import Java.security.interfaces.DSAPublicKey;
import Java.security.KeyFactory;
import Java.security.spec.KeySpec;
import Java.security.spec.DSAPublicKeySpec;
import Java.security.spec.RSAPublicKeySpec;
import org.Apache.sshd.common.NamedFactory;
import org.Apache.sshd.SshServer;
import org.Apache.sshd.server.Command;
import org.Apache.sshd.server.command.ScpCommandFactory;
import org.Apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.Apache.sshd.server.PasswordAuthenticator;
import org.Apache.sshd.server.PublickeyAuthenticator;
import org.Apache.sshd.server.session.ServerSession;
import org.Apache.sshd.server.sftp.SftpSubsystem;
import org.Apache.sshd.server.Shell.ProcessShellFactory;
import org.Apache.sshd.server.UserAuth;
import org.Apache.sshd.server.auth.UserAuthPassword;
import org.Apache.sshd.server.auth.UserAuthPublicKey;
import org.Apache.sshd.common.KeyExchange;
//import org.Apache.sshd.server.kex.DHGEX;
//import org.Apache.sshd.server.kex.DHGEX256;
import org.Apache.sshd.server.kex.ECDHP256;
import org.Apache.sshd.server.kex.ECDHP384;
import org.Apache.sshd.server.kex.ECDHP521;
import org.Apache.sshd.server.kex.DHG1;
import org.Apache.mina.util.Base64;
/*
javac -classpath .;lib/sshd-core-0.10.1.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer.Java
java -classpath .;lib/sshd-core-0.10.1.jar;lib/slf4j-simple-1.7.6.jar;lib/slf4j-api-1.6.6.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer
*/
public class SFTPServer {
public void setupSftpServer() throws Exception {
class AuthorizedKeyEntry {
private String keyType;
private String pubKey;
private byte[] bytes;
private int pos;
private PublicKey key = null;
private int decodeInt() {
return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16)
| ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF);
}
private BigInteger decodeBigInt() {
int len = decodeInt();
byte[] bigIntBytes = new byte[len];
System.arraycopy(bytes, pos, bigIntBytes, 0, len);
pos += len;
return new BigInteger(bigIntBytes);
}
private void decodeType() {
int len = decodeInt();
keyType = new String(bytes, pos, len);
pos += len;
}
public PublicKey getPubKey() {
return key;
}
public void setPubKey(PublicKey key) throws Exception {
this.key = key;
ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(byteOs);
if (key instanceof RSAPublicKey) {
keyType = "ssh-rsa";
dos.writeInt(keyType.getBytes().length);
dos.write(keyType.getBytes());
RSAPublicKey rsakey = (RSAPublicKey)key;
BigInteger e = rsakey.getPublicExponent();
dos.writeInt(e.toByteArray().length);
dos.write(e.toByteArray());
BigInteger m = rsakey.getModulus();
dos.writeInt(m.toByteArray().length);
dos.write(m.toByteArray());
} else if (key instanceof DSAPublicKey) {
keyType = "ssh-dss";
dos.writeInt(keyType.getBytes().length);
dos.write(keyType.getBytes());
DSAPublicKey dsskey = (DSAPublicKey)key;
BigInteger p = dsskey.getParams().getP();
dos.writeInt(p.toByteArray().length);
dos.write(p.toByteArray());
BigInteger q = dsskey.getParams().getQ();
dos.writeInt(q.toByteArray().length);
dos.write(q.toByteArray());
BigInteger g = dsskey.getParams().getG();
dos.writeInt(g.toByteArray().length);
dos.write(g.toByteArray());
BigInteger y = dsskey.getY();
dos.writeInt(y.toByteArray().length);
dos.write(y.toByteArray());
} else {
throw new IllegalArgumentException("unknown key encoding " + key.getAlgorithm());
}
bytes = byteOs.toByteArray();
this.pubKey = new String(Base64.encodeBase64(bytes));
}
public void setPubKey(String pubKey) throws Exception {
this.pubKey = pubKey;
bytes = Base64.decodeBase64(pubKey.getBytes());
if (bytes == null)
return;
decodeType();
if (keyType.equals("ssh-rsa")) {
BigInteger e = decodeBigInt();
BigInteger m = decodeBigInt();
KeySpec spec = new RSAPublicKeySpec(m, e);
key = KeyFactory.getInstance("RSA").generatePublic(spec);
} else if (keyType.equals("ssh-dss")) {
BigInteger p = decodeBigInt();
BigInteger q = decodeBigInt();
BigInteger g = decodeBigInt();
BigInteger y = decodeBigInt();
KeySpec spec = new DSAPublicKeySpec(y, p, q, g);
key = KeyFactory.getInstance("DSA").generatePublic(spec);
} else {
throw new IllegalArgumentException("unknown type " + keyType);
}
}
}
final SshServer sshd = SshServer.setUpDefaultServer();
final Map<ServerSession, PublicKey> sessionKeys = new HashMap();
class AuthorizedKeys extends HashMap<String,AuthorizedKeyEntry> {
private File file;
public void load(File file) throws Exception {
this.file = file;
Scanner scanner = new Scanner(file).useDelimiter("\n");
while (scanner.hasNext())
decodePublicKey(scanner.next());
scanner.close();
}
public void save() throws Exception {
PrintWriter w = new PrintWriter(file);
for (String username : keySet()) {
AuthorizedKeyEntry entry = get(username);
w.print(entry.keyType + " " + entry.pubKey + " " + username + "\n");
}
w.close();
}
public void put(String username, PublicKey key) {
AuthorizedKeyEntry entry = new AuthorizedKeyEntry();
try {
entry.setPubKey(key);
} catch (Exception e) {
e.printStackTrace();
}
super.put(username,entry);
}
private void decodePublicKey(String keyLine) throws Exception {
AuthorizedKeyEntry entry = new AuthorizedKeyEntry();
String[] toks = keyLine.split(" ");
String username = toks[toks.length-1];
for (String part : toks) {
if (part.startsWith("AAAA")) {
entry.setPubKey(part);
//bytes = Base64.decodeBase64(part.getBytes());
break;
}
}
super.put(username,entry);
}
};
final AuthorizedKeys authenticUserKeys = new AuthorizedKeys(); // load authorized_keys
File file = new File("authorized_keys");
file.createNewFile(); // create if not exists
authenticUserKeys.load(file);
sshd.setPort(22);
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser"));
sshd.setShellFactory(new ProcessShellFactory(new String[] { "cmd.exe "}));
sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
public boolean authenticate(String username, String password, ServerSession session) {
boolean authentic = false;
try {
new waffle.windows.auth.impl.WindowsAuthProviderImpl().logonUser(username,password);
authentic = true;
//authentic = username != null && username.equals(password+password); // obsecurity :)
if (authentic) {
PublicKey sessionKey = sessionKeys.get(session);
if (sessionKey != null)
authenticUserKeys.put(username, sessionKey); //save entry to authorized_keys
}
} catch (Exception e) {
System.err.println(e);
}
return authentic;
}
});
sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() {
public boolean authenticate(String username, PublicKey key, ServerSession session) {
sessionKeys.put(session,key);
return key.equals(authenticUserKeys.get(username).getPubKey());
}
});
sshd.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(
new UserAuthPublicKey.Factory()
,new UserAuthPassword.Factory()));
sshd.setCommandFactory(new ScpCommandFactory());
sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(
new SftpSubsystem.Factory()));
//workaround for Apache sshd 10.0+ (PuTTY)
sshd.setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>>asList(
//new DHGEX256.Factory()
//,new DHGEX.Factory()
new ECDHP256.Factory()
,new ECDHP384.Factory()
,new ECDHP521.Factory()
,new DHG1.Factory()));
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
authenticUserKeys.save();
System.out.println("Stopping");
sshd.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
});
System.out.println("Starting");
try {
sshd.start();
Thread.sleep(Long.MAX_VALUE);
} catch (Exception e) {
e.printStackTrace();
}
}
static public void main(String[] args) throws Exception {
new SFTPServer().setupSftpServer();
}
}
SSHTools(j2ssh) を見てください。クライアントとサーバーが含まれます。
ただし、ディレクトリのポーリングはそれほど悪い考えではありません。j2sshを使用して独自のSFTPサーバーをセットアップするよりもおそらく信頼性が高いでしょう。この種のポーリングを行うアプリケーションの数は数え切れませんが、通常は非常にうまく機能します。
完全を期すために、 SecureBlackbox ライブラリは、Java(Androidを含む)で独自のSSH/SFTPサーバーを作成するためのクラスを提供します。