From 6da706e459eeab05e19f06d9633404d01d1e0c67 Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Mon, 5 Dec 2011 15:47:10 +0000
Subject: [PATCH] LMS-2575 KeystoreBasedKeyPairProvider introduced

SVN: 23880
---
 .../dss/generic/server/ftp/FtpServer.java     | 120 ++++++++++++++++--
 .../generic/server/ftp/FtpServerConfig.java   |  32 +++--
 2 files changed, 132 insertions(+), 20 deletions(-)

diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java
index b920d3eaa24..82c4bb10e83 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java
@@ -16,15 +16,27 @@
 
 package ch.systemsx.cisd.openbis.dss.generic.server.ftp;
 
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.Properties;
 
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
+
 import org.apache.commons.io.IOUtils;
 import org.apache.ftpserver.ConnectionConfigFactory;
 import org.apache.ftpserver.DataConnectionConfigurationFactory;
@@ -47,17 +59,19 @@ import org.apache.ftpserver.ssl.SslConfigurationFactory;
 import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication;
 import org.apache.log4j.Logger;
 import org.apache.sshd.SshServer;
+import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Session;
 import org.apache.sshd.common.Session.AttributeKey;
+import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.PasswordAuthenticator;
 import org.apache.sshd.server.SshFile;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.sftp.SftpSubsystem;
 
 import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.filesystem.FileUtilities;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
@@ -116,13 +130,11 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
             operationLog.info(String.format(startingMessage, "S"));
             sshServer.start();
             operationLog.info("SFTP server started.");
-        } else
-        {
-            server = creatFtpServer();
-            operationLog.info(String.format(startingMessage, ""));
-            server.start();
-            operationLog.info("FTP server started.");
         }
+        server = creatFtpServer();
+        operationLog.info(String.format(startingMessage, ""));
+        server.start();
+        operationLog.info("FTP server started.");
     }
 
     private org.apache.ftpserver.FtpServer creatFtpServer()
@@ -187,9 +199,9 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
     private SshServer createSftpServer()
     {
         SshServer s = SshServer.setUpDefaultServer();
-        // TODO keystore based provider needed
-        s.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
-        s.setPort(config.getPort());
+        KeyPairProvider keyPairProvider = new KeystoreBasedKeyPairProvider(config, operationLog);
+        s.setKeyPairProvider(keyPairProvider);
+        s.setPort(config.getSftpPort());
         s.setSubsystemFactories(creatSubsystemFactories());
         s.setFileSystemFactory(this);
         s.setPasswordAuthenticator(new PasswordAuthenticator()
@@ -423,4 +435,92 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
             inputStreams.clear();
         }
     }
+    
+    private static final class KeystoreBasedKeyPairProvider extends AbstractKeyPairProvider
+    {
+        private final KeyPair[] keyPairs;
+
+        private KeystoreBasedKeyPairProvider(FtpServerConfig config, Logger operationLog)
+        {
+            File keyStoreFile = config.getKeyStore();
+            String keyStorePassword = config.getKeyStorePassword();
+            String keyPassword = config.getKeyPassword();
+            KeyStore keystore = loadKeystore(keyStoreFile, keyStorePassword);
+            X509ExtendedKeyManager keyManager = getKeyManager(keystore, keyStorePassword, keyPassword);
+            List<KeyPair> list = new ArrayList<KeyPair>();
+            try
+            {
+                Enumeration<String> aliases = keystore.aliases();
+                while (aliases.hasMoreElements())
+                {
+                    String alias = aliases.nextElement();
+                    if (keystore.isKeyEntry(alias))
+                    {
+                        Certificate certificate = keystore.getCertificate(alias);
+                        PublicKey publicKey = certificate.getPublicKey();
+                        PrivateKey privateKey = keyManager.getPrivateKey(alias);
+                        list.add(new KeyPair(publicKey, privateKey));
+                    }
+                }
+                keyPairs = list.toArray(new KeyPair[list.size()]);
+                operationLog.info(keyPairs.length + " key pairs loaded from keystore " + keyStoreFile);
+            } catch (Exception ex)
+            {
+                throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+            }
+        }
+
+        @Override
+        protected KeyPair[] loadKeys()
+        {
+            return keyPairs;
+        }
+
+        private KeyStore loadKeystore(File keyStoreFile, String keyStorePassword)
+        {
+            InputStream stream = null;
+            try
+            {
+                KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+                stream = new FileInputStream(keyStoreFile);
+                keystore.load(stream, keyStorePassword.toCharArray());
+                return keystore;
+            } catch (Exception e)
+            {
+                throw CheckedExceptionTunnel.wrapIfNecessary(e);
+            }finally{
+                IOUtils.closeQuietly(stream);
+            }
+        }
+        
+        private X509ExtendedKeyManager getKeyManager(KeyStore keystore, String keyStorePassword,
+                String keyPassword)
+        {
+            try
+            {
+                String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+                KeyManagerFactory factory = KeyManagerFactory.getInstance(defaultAlgorithm);
+                char[] password = (keyPassword == null ? keyStorePassword : keyPassword).toCharArray();
+                factory.init(keystore, password);
+                KeyManager[] keyManagers = factory.getKeyManagers();
+                if (keyManagers.length != 1)
+                {
+                    throw new ConfigurationFailureException(
+                            "Only one key manager expected instead of " + keyManagers.length + ".");
+                }
+                KeyManager keyManager = keyManagers[0];
+                if (keyManager instanceof X509ExtendedKeyManager == false)
+                {
+                    throw new ConfigurationFailureException("Key manager is not of type "
+                            + X509ExtendedKeyManager.class.getSimpleName() + ": "
+                            + keyManager.getClass().getName());
+                }
+                return (X509ExtendedKeyManager) keyManager;
+            } catch (Exception ex)
+            {
+                throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+            }
+            
+        }
+    }
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java
index 86a9a58015c..a89d6aa03c7 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java
@@ -43,7 +43,7 @@ public class FtpServerConfig
 
     final static String ENABLE_KEY = PREFIX + "enable";
     
-    final static String SFTP_KEY = PREFIX + "sftp";
+    final static String SFTP_PORT_KEY = PREFIX + "sftp-port";
 
     final static String PORT_KEY = PREFIX + "port";
 
@@ -115,6 +115,8 @@ public class FtpServerConfig
 
     private boolean sftpMode;
 
+    private int sftpPort;
+
     public FtpServerConfig(Properties props) {
         this.startServer = PropertyUtils.getBoolean(props, ENABLE_KEY, false);
         if (startServer)
@@ -125,7 +127,8 @@ public class FtpServerConfig
 
     private void initializeProperties(Properties props)
     {
-        sftpMode = PropertyUtils.getBoolean(props, SFTP_KEY, false);
+        sftpPort = PropertyUtils.getInt(props, SFTP_PORT_KEY, 0);
+        sftpMode = sftpPort > 0;
         port = PropertyUtils.getPosInt(props, PORT_KEY, DEFAULT_PORT);
         useSSL = PropertyUtils.getBoolean(props, USE_SSL_KEY, DEFAULT_USE_SSL);
         if (useSSL)
@@ -181,6 +184,11 @@ public class FtpServerConfig
         return sftpMode;
     }
 
+    public int getSftpPort()
+    {
+        return sftpPort;
+    }
+
     public boolean isStartServer()
     {
         return startServer;
@@ -246,20 +254,24 @@ public class FtpServerConfig
      */
     public void logStartupInfo()
     {
-        operationLog.info("Ftp Server port: " + port);
-        operationLog.info("Ftp Server using SSL: " + useSSL);
-        operationLog.info("Ftp Server data set display template : " + dataSetDisplayTemplate);
-        operationLog.info("Ftp Server passive ports: " + passivePortsRange);
-        operationLog.info("Ftp Server enable active mode: " + activeModeEnabled);
+        operationLog.info("FTP Server port: " + port);
+        operationLog.info("FTP Server using SSL: " + useSSL);
+        operationLog.info("FTP Server passive ports: " + passivePortsRange);
+        operationLog.info("FTP Server enable active mode: " + activeModeEnabled);
         if (activeModeEnabled)
         {
-            operationLog.info("Ftp Server active mode port: " + activePort);
+            operationLog.info("FTP Server active mode port: " + activePort);
+        }
+        if (sftpMode)
+        {
+            operationLog.info("SFTP Server port: " + sftpPort);
         }
+        operationLog.info("SFTP/FTP Server data set display template : " + dataSetDisplayTemplate);
 
         for (Entry<String, String> subpathEntry : fileListSubPaths.entrySet())
         {
             String message =
-                    String.format("Ftp Server subpath configuration for data "
+                    String.format("SFTP/FTP Server subpath configuration for data "
                             + "set type '%s' : '%s'", subpathEntry.getKey(),
                             subpathEntry.getValue());
             operationLog.info(message);
@@ -267,7 +279,7 @@ public class FtpServerConfig
         for (Entry<String, String> filterEntry : fileListFilters.entrySet())
         {
             String message =
-                    String.format("Ftp Server file filter configuration for data "
+                    String.format("SFTP/FTP Server file filter configuration for data "
                             + "set type '%s' : '%s'", filterEntry.getKey(), filterEntry.getValue());
             operationLog.info(message);
         }
-- 
GitLab