All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.vfs.JsseSSLFactory Maven / Gradle / Ivy

/*
 * Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.vfs;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import com.caucho.config.ConfigException;
import com.caucho.env.service.RootDirectorySystem;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.util.IoUtil;
import com.caucho.util.L10N;

/**
 * Abstract socket to handle both normal sockets and bin/resin sockets.
 */
public class JsseSSLFactory implements SSLFactory {
  private static final Logger log
    = Logger.getLogger(JsseSSLFactory.class.getName());
  
  private static final L10N L = new L10N(JsseSSLFactory.class);

  private static Method _honorCipherOrderMethod;
  private static Method _getSSLParametersMethod;
  private static final Method _setSSLParameters;
  
  private Path _keyStoreFile;
  private String _alias;
  private String _password;
  private String _verifyClient;
  
  private String _keyStorePassword;
  private String _keyStoreType = "jks";
  
  private String _keyManagerFactory = "SunX509";
  private String _keyManagerAlgorithm;
  private String _keyManagerProvider;
  
  private Path _trustStoreFile;
  private String _trustStorePassword;
  private String _trustStoreType = "jks";
  private String _trustStoreAlgorithm;
  private String _trustStoreProvider;
    
  private String _sslContext = "TLS";
  private String []_cipherSuites;
  private String []_cipherSuitesForbidden;
  private String []_protocols;

  private String _selfSignedName;

  private Boolean _isHonorCipherOrder;

  private KeyStore _keyStore;
  private KeyStore _trustStore;
  
  /**
   * Creates a ServerSocket factory without initializing it.
   */
  public JsseSSLFactory()
  {
  }

  /**
   * Sets the enabled cipher suites
   */
  public void setCipherSuites(String []ciphers)
  {
    _cipherSuites = ciphers;
  }

  /**
   * Sets the enabled cipher suites
   */
  public void setCipherSuitesForbidden(String []ciphers)
  {
    _cipherSuitesForbidden = ciphers;
  }

  /**
   * Sets the key store
   */
  public void setKeyStoreFile(Path keyStoreFile)
  {
    _keyStoreFile = keyStoreFile;
  }
 

  /**
   * Returns the certificate file.
   */
  public Path getKeyStoreFile()
  {
    return _keyStoreFile;
  }

  /**
   * Sets the password.
   */
  public void setPassword(String password)
  {
    _password = password;
  }

  /**
   * Returns the key file.
   */
  public String getPassword()
  {
    return _password;
  }

  /**
   * Sets the certificate alias
   */
  public void setAlias(String alias)
  {
    _alias = alias;
  }

  /**
   * Returns the alias.
   */
  public String getAlias()
  {
    return _alias;
  }

  /**
   * Sets the verifyClient.
   */
  public void setVerifyClient(String verifyClient)
  {
    _verifyClient = verifyClient;
  }

  /**
   * Returns the key file.
   */
  public String getVerifyClient()
  {
    return _verifyClient;
  }

  /**
   * Sets the key-manager-factory
   */
  public void setKeyManagerFactory(String keyManagerFactory)
  {
    _keyManagerFactory = keyManagerFactory;
  }

  /**
   * Sets the KeyManagerFactory algorithm
   */
  public void setKeyManagerAlgorithm(String keyManagerAlgorithm)
  {
    if (! "".equals(keyManagerAlgorithm)) {
      _keyManagerAlgorithm = keyManagerAlgorithm;
    }
    else {
      _keyManagerAlgorithm = null;
    }
  }

  /**
   * Sets the KeyManagerFactory provider
   */
  public void setKeyManagerProvider(String keyManagerProvider)
  {
    if (! "".equals(keyManagerProvider)) {
      _keyManagerProvider = keyManagerProvider;
    }
    else {
      _keyManagerProvider = null;
    }
  }

  /**
   * Sets the self-signed certificate name
   */
  public void setSelfSignedCertificateName(String name)
  {
    _selfSignedName = name;
  }

  /**
   * Sets the ssl-context
   */
  public void setSSLContext(String sslContext)
  {
    _sslContext = sslContext;
  }

  /**
   * Sets the key-store
   */
  public void setKeyStoreType(String keyStore)
  {
    _keyStoreType = keyStore;
  }

  /**
   * Sets the KeyStore password
   */
  public void setKeyStorePassword(String password)
  {
    if (! "".equals(password)) {
      _keyStorePassword = password;
    }
    else {
      _keyStorePassword = null;
    }
  }

  /**
   * Sets the TrustStore type
   */
  public void setTrustStoreAlgorithm(String value)
  {
    if (! "".equals(value)) {
      _trustStoreAlgorithm = value;
    }
    else {
      _trustStoreAlgorithm = null;
    }
  }

  /**
   * Sets the TrustStore provider
   */
  public void setTrustStoreProvider(String value)
  {
    if (! "".equals(value)) {
      _trustStoreProvider = value;
    }
    else {
      _trustStoreProvider = null;
    }
  }

  /**
   * Sets the TrustStore password
   */
  public void setTrustStorePassword(String value)
  {
    if (! "".equals(value)) {
      _trustStorePassword = value;
    }
    else {
      _trustStorePassword = null;
    }
  }
  
  /**
   * Sets the TrustStore type
   */
  public void setTrustStoreType(String value)
  {
    _trustStoreType = value;
  }
  
  /**
   * Sets the TrustStore store
   */
  public void setTrustStoreFile(Path trustStoreFile)
  {
    _trustStoreFile = trustStoreFile;
  }

  /**
   * Sets the protocol
   */
  public void setProtocol(String protocol)
  {
    _protocols = protocol.split("[\\s,]+");
  }

  public Boolean getHonorCipherOrder()
  {
    return _isHonorCipherOrder;
  }

  public void setHonorCipherOrder(Boolean isHonorCipherOrder)
  {
    if (_honorCipherOrderMethod == null)
      log.log(Level.WARNING, "honor-cipher-order requires JDK 1.8");

    _isHonorCipherOrder = isHonorCipherOrder;
  }
  
  /**
   * Sets the key store instance
   */
  public void setKeyStoreInstance(KeyStore keyStore) {
    _keyStore = keyStore;
  }
  
  /**
   * Sets the trust store instance
   */
  public void setTrustStoreInstance(KeyStore trustStore) {
    _trustStore = trustStore;
  }

  /**
   * Initialize
   */
  @PostConstruct
  public void init()
    throws ConfigException, IOException, GeneralSecurityException
  {
    String keyStorePassword = _keyStorePassword;
    if (keyStorePassword == null) {
      keyStorePassword = _password;
    }
    
    if (_keyStore == null) {
      if (_keyStoreFile != null
          && _password == null
          && _keyStorePassword == null) {
        throw new ConfigException(L.l("'password' or 'key-store-password' is required for JSSE."));
      }
      
      if (_password != null && _keyStoreFile == null)
        throw new ConfigException(L.l("'key-store-file' is required for JSSE."));

      if (_alias != null && _keyStoreFile == null)
        throw new ConfigException(L.l("'alias' requires a key store for JSSE."));

      if (_keyStoreFile == null && _selfSignedName == null)
        throw new ConfigException(L.l("JSSE requires a key-store-file or a self-signed-certificate-name."));

      if (_keyStoreFile == null)
        return;
      
      _keyStore = createKeyStore(_keyStoreType, _keyStoreFile, keyStorePassword);
    }

    if (_alias != null) {      
      String keyPassword = _password;
      if (keyPassword == null) {
        keyPassword = _keyStorePassword;
      }
      
      Key key = _keyStore.getKey(_alias, getPasswordChars(keyPassword));

      if (key == null)
        throw new ConfigException(L.l("JSSE alias '{0}' does not have a corresponding key.",
                                  _alias));

      Certificate []certChain = _keyStore.getCertificateChain(_alias);
      
      if (certChain == null)
        throw new ConfigException(L.l("JSSE alias '{0}' does not have a corresponding certificate chain.",
                                  _alias));

      _keyStore = KeyStore.getInstance(_keyStoreType);
      _keyStore.load(null, getPasswordChars(keyStorePassword));

      _keyStore.setKeyEntry(_alias, key, getPasswordChars(keyPassword), certChain);
    }
  }
  
  private static KeyStore createKeyStore(String keyStoreType,
                                         Path keyStoreFile,
                                         String keyStorePassword)
    throws IOException, GeneralSecurityException
  {
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    
    InputStream is = keyStoreFile.openRead();
    try {
      keyStore.load(is, getPasswordChars(keyStorePassword));
    } finally {
      is.close();
    }
    
    return keyStore;
  }
  
  private static char[] getPasswordChars(String password) {
    return password != null ? password.toCharArray() : null;
  }

  /**
   * Creates the SSL ServerSocket.
   */
  public QServerSocket create(InetAddress host, int port)
    throws IOException, GeneralSecurityException
  {
    SSLServerSocketFactory factory = null;
    
    if (_keyStore != null) {
      SSLContext sslContext = SSLContext.getInstance(_sslContext);

      KeyManagerFactory kmf;
      
      if (_keyManagerAlgorithm != null || _keyManagerProvider != null) {
        kmf = KeyManagerFactory.getInstance(_keyManagerAlgorithm,
                                            _keyManagerProvider);
      }
      else {
        kmf = KeyManagerFactory.getInstance(_keyManagerFactory);
      }
      
      String keyPassword = _password;
      if (keyPassword == null) {
        keyPassword = _keyStorePassword;
      }
      
      kmf.init(_keyStore, getPasswordChars(keyPassword));
      
      sslContext.init(kmf.getKeyManagers(), createTrustStore(), null);

      /*
      if (_cipherSuites != null)
        sslContext.createSSLEngine().setEnabledCipherSuites(_cipherSuites);

      if (_protocols != null)
        sslContext.createSSLEngine().setEnabledProtocols(_protocols);
      */

      factory = sslContext.getServerSocketFactory();
    }
    else {
      factory = createAnonymousFactory(host, port);
    }

    ServerSocket serverSocket;

    int listen = 100;

    if (host == null)
      serverSocket = factory.createServerSocket(port, listen);
    else
      serverSocket = factory.createServerSocket(port, listen, host);

    SSLServerSocket sslServerSocket = (SSLServerSocket) serverSocket;

    if (_cipherSuites != null) {
      sslServerSocket.setEnabledCipherSuites(_cipherSuites);
    }
    
    if (_cipherSuitesForbidden != null) {
      String []cipherSuites = sslServerSocket.getEnabledCipherSuites();
      
      if (cipherSuites == null)
        cipherSuites = sslServerSocket.getSupportedCipherSuites();
      
      ArrayList cipherList = new ArrayList();
      
      for (String cipher : cipherSuites) {
        if (! isCipherForbidden(cipher, _cipherSuitesForbidden)) {
          cipherList.add(cipher);
        }
      }
      
      cipherSuites = new String[cipherList.size()];
      cipherList.toArray(cipherSuites);
      
      sslServerSocket.setEnabledCipherSuites(cipherSuites);
    }

    if (_protocols != null) {
      try {
        sslServerSocket.setEnabledProtocols(_protocols);
      } catch (Exception e) {
        throw ConfigException.create(L.l("Invalid protocols '{0}', expected from list '{1}'\n  {2}",
                                  Arrays.asList(_protocols),
                                  Arrays.asList(sslServerSocket.getSupportedProtocols()),
                                  e.toString()),
                              e);
      }
    }
    
    if ("required".equals(_verifyClient))
      sslServerSocket.setNeedClientAuth(true);
    else if ("optional".equals(_verifyClient))
      sslServerSocket.setWantClientAuth(true);

    setHonorCipherOrder(sslServerSocket);

    return new QServerSocketWrapper(serverSocket);
  }
  
  private TrustManager []createTrustStore()
      throws IOException, GeneralSecurityException
  {    
    if (_trustStore == null && _trustStoreFile == null) {
      return null;
    }
    
    KeyStore trustStore = _trustStore;
    
    if (trustStore == null) {
      trustStore
        = createKeyStore(_trustStoreType, _trustStoreFile, _trustStorePassword);
    }
        
    String algorithm = _trustStoreAlgorithm;
    if (algorithm == null) {
      algorithm = TrustManagerFactory.getDefaultAlgorithm();
    }
    
    TrustManagerFactory tmf;
    
    if (_trustStoreProvider != null) {
      tmf = TrustManagerFactory.getInstance(algorithm, _trustStoreProvider);
    }
    else {
      tmf = TrustManagerFactory.getInstance(algorithm);
    }

    tmf.init(trustStore);
    
    return tmf.getTrustManagers();
  }

  private void setHonorCipherOrder(SSLServerSocket serverSocket)
  {
    if (_isHonorCipherOrder == null)
      return;

    if (_honorCipherOrderMethod == null)
      return;

    try {
      SSLParameters params
        = (SSLParameters) _getSSLParametersMethod.invoke(serverSocket);

      _honorCipherOrderMethod.invoke(params, _isHonorCipherOrder);
      
      if (_setSSLParameters != null) {
        _setSSLParameters.invoke(serverSocket, params);
      }
      
      log.log(Level.FINER, L.l("setting honor-cipher-order {0}",
                               _isHonorCipherOrder));
    } catch (Throwable t) {
      log.log(Level.WARNING, t.getMessage(), t);
    }
  }
  
  private boolean isCipherForbidden(String cipher,
                                    String []forbiddenList)
  {
    for (String forbidden : forbiddenList) {
      if (cipher.indexOf(forbidden) >= 0) {
        return true;
      }
    }
    
    return false;
  }

  private SSLServerSocketFactory createAnonymousFactory(InetAddress hostAddr,
                                                        int port)
    throws IOException, GeneralSecurityException
  {
    SSLContext sslContext = SSLContext.getInstance(_sslContext);

    String []cipherSuites = _cipherSuites;

    /*
    if (cipherSuites == null) {
      cipherSuites = sslContext.createSSLEngine().getSupportedCipherSuites();
    }
    */

    String selfSignedName = _selfSignedName;

    if (selfSignedName == null
        || "".equals(selfSignedName)
        || "*".equals(selfSignedName)) {
      if (hostAddr != null)
        selfSignedName = hostAddr.getHostName();
      else {
        InetAddress addr = InetAddress.getLocalHost();

        selfSignedName = addr.getHostAddress();
      }
    }
    
    SelfSignedCert cert = createSelfSignedCert(selfSignedName, cipherSuites);

    if (cert == null)
      throw new ConfigException(L.l("Cannot generate anonymous certificate"));
      
    sslContext.init(cert.getKeyManagers(), null, null);

    // SSLEngine engine = sslContext.createSSLEngine();

    SSLServerSocketFactory factory = sslContext.getServerSocketFactory();

    return factory;
  }
  
  private SelfSignedCert createSelfSignedCert(String name, 
                                              String []cipherSuites)
  {
    Path dataDir = RootDirectorySystem.getCurrentDataDirectory();
    Path certDir = dataDir.lookup("certs");
    
    SelfSignedCert cert = null;
        
    try {
      Path certPath = certDir.lookup(name + ".cert");
            
      if (certPath.canRead()) {
        ReadStream is = certPath.openRead();
        
        try {
          Hessian2Input hIn = new Hessian2Input(is);
          
          cert = (SelfSignedCert) hIn.readObject(SelfSignedCert.class);
          
          hIn.close();

          if (! cert.isExpired())
            return cert;
        } finally {
          IoUtil.close(is);
        }
      }
    } catch (Exception e) {
      log.log(Level.FINER, e.toString(), e);      
    }
      
    cert = SelfSignedCert.create(name, cipherSuites);
        
    try {
      certDir.mkdirs();
      
      Path certPath = certDir.lookup(name + ".cert");
      
      WriteStream os = certPath.openWrite();
        
      try {
        Hessian2Output hOut = new Hessian2Output(os);
        
        hOut.writeObject(cert);
        
        hOut.close();
      } finally {
        IoUtil.close(os);
      }
    } catch (Exception e) {
      log.log(Level.FINER, e.toString(), e);      
    }
        
    return cert;
  }
  
  /**
   * Creates the SSL ServerSocket.
   */
  public QServerSocket bind(QServerSocket ss)
    throws ConfigException, IOException, GeneralSecurityException
  {
    throw new ConfigException(L.l("jsse is not allowed here"));
  }

  static {
    Method setSSLParameters = null;
    
    try {
      Method method = SSLServerSocket.class.getMethod("getSSLParameters");

      method.setAccessible(true);
      _getSSLParametersMethod = method;

      method = SSLParameters.class.getMethod("setUseCipherSuitesOrder",
                                             boolean.class);
      method.setAccessible(true);

      _honorCipherOrderMethod = method;
      
      setSSLParameters = SSLServerSocket.class.getMethod("setSSLParameters", SSLParameters.class);
    } catch (Exception e) {
      log.log(Level.FINER, e.getMessage(), e);
    }
    
    _setSSLParameters = setSSLParameters;
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy