Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » tomcat » util » net » jsse » [javadoc | source]
    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   
   18   package org.apache.tomcat.util.net.jsse;
   19   
   20   import java.io.File;
   21   import java.io.FileInputStream;
   22   import java.io.FileNotFoundException;
   23   import java.io.IOException;
   24   import java.io.InputStream;
   25   import java.net.InetAddress;
   26   import java.net.ServerSocket;
   27   import java.net.Socket;
   28   import java.net.SocketException;
   29   import java.security.KeyStore;
   30   import java.security.SecureRandom;
   31   import java.security.cert.CRL;
   32   import java.security.cert.CRLException;
   33   import java.security.cert.CertPathParameters;
   34   import java.security.cert.CertStore;
   35   import java.security.cert.CertStoreParameters;
   36   import java.security.cert.CertificateException;
   37   import java.security.cert.CertificateFactory;
   38   import java.security.cert.CollectionCertStoreParameters;
   39   import java.security.cert.PKIXBuilderParameters;
   40   import java.security.cert.X509CertSelector;
   41   import java.util.Collection;
   42   import java.util.Vector;
   43   
   44   import javax.net.ssl.CertPathTrustManagerParameters;
   45   import javax.net.ssl.KeyManager;
   46   import javax.net.ssl.KeyManagerFactory;
   47   import javax.net.ssl.ManagerFactoryParameters;
   48   import javax.net.ssl.SSLContext;
   49   import javax.net.ssl.SSLException;
   50   import javax.net.ssl.SSLServerSocket;
   51   import javax.net.ssl.SSLServerSocketFactory;
   52   import javax.net.ssl.SSLSocket;
   53   import javax.net.ssl.TrustManager;
   54   import javax.net.ssl.TrustManagerFactory;
   55   import javax.net.ssl.X509KeyManager;
   56   
   57   import org.apache.tomcat.util.res.StringManager;
   58   
   59   /*
   60     1. Make the JSSE's jars available, either as an installed
   61        extension (copy them into jre/lib/ext) or by adding
   62        them to the Tomcat classpath.
   63     2. keytool -genkey -alias tomcat -keyalg RSA
   64        Use "changeit" as password ( this is the default we use )
   65    */
   66   
   67   /**
   68    * SSL server socket factory. It _requires_ a valid RSA key and
   69    * JSSE. 
   70    *
   71    * @author Harish Prabandham
   72    * @author Costin Manolache
   73    * @author Stefan Freyr Stefansson
   74    * @author EKR -- renamed to JSSESocketFactory
   75    * @author Jan Luehe
   76    * @author Bill Barker
   77    */
   78   public class JSSESocketFactory
   79       extends org.apache.tomcat.util.net.ServerSocketFactory {
   80   
   81       private static StringManager sm =
   82           StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
   83   
   84       // defaults
   85       static String defaultProtocol = "TLS";
   86       static boolean defaultClientAuth = false;
   87       static String defaultKeystoreType = "JKS";
   88       private static final String defaultKeystoreFile
   89           = System.getProperty("user.home") + "/.keystore";
   90       private static final String defaultKeyPass = "changeit";
   91       static org.apache.juli.logging.Log log =
   92           org.apache.juli.logging.LogFactory.getLog(JSSESocketFactory.class);
   93   
   94       protected boolean initialized;
   95       protected String clientAuth = "false";
   96       protected SSLServerSocketFactory sslProxy = null;
   97       protected String[] enabledCiphers;
   98   
   99       /**
  100        * Flag to state that we require client authentication.
  101        */
  102       protected boolean requireClientAuth = false;
  103   
  104       /**
  105        * Flag to state that we would like client authentication.
  106        */
  107       protected boolean wantClientAuth    = false;
  108   
  109   
  110       public JSSESocketFactory () {
  111       }
  112   
  113       public ServerSocket createSocket (int port)
  114           throws IOException
  115       {
  116           if (!initialized) init();
  117           ServerSocket socket = sslProxy.createServerSocket(port);
  118           initServerSocket(socket);
  119           return socket;
  120       }
  121       
  122       public ServerSocket createSocket (int port, int backlog)
  123           throws IOException
  124       {
  125           if (!initialized) init();
  126           ServerSocket socket = sslProxy.createServerSocket(port, backlog);
  127           initServerSocket(socket);
  128           return socket;
  129       }
  130       
  131       public ServerSocket createSocket (int port, int backlog,
  132                                         InetAddress ifAddress)
  133           throws IOException
  134       {   
  135           if (!initialized) init();
  136           ServerSocket socket = sslProxy.createServerSocket(port, backlog,
  137                                                             ifAddress);
  138           initServerSocket(socket);
  139           return socket;
  140       }
  141       
  142       public Socket acceptSocket(ServerSocket socket)
  143           throws IOException
  144       {
  145           SSLSocket asock = null;
  146           try {
  147                asock = (SSLSocket)socket.accept();
  148                configureClientAuth(asock);
  149           } catch (SSLException e){
  150             throw new SocketException("SSL handshake error" + e.toString());
  151           }
  152           return asock;
  153       }
  154   
  155       public void handshake(Socket sock) throws IOException {
  156           ((SSLSocket)sock).startHandshake();
  157       }
  158   
  159       /*
  160        * Determines the SSL cipher suites to be enabled.
  161        *
  162        * @param requestedCiphers Comma-separated list of requested ciphers
  163        * @param supportedCiphers Array of supported ciphers
  164        *
  165        * @return Array of SSL cipher suites to be enabled, or null if none of the
  166        * requested ciphers are supported
  167        */
  168       protected String[] getEnabledCiphers(String requestedCiphers,
  169                                            String[] supportedCiphers) {
  170   
  171           String[] enabledCiphers = null;
  172   
  173           if (requestedCiphers != null) {
  174               Vector vec = null;
  175               String cipher = requestedCiphers;
  176               int index = requestedCiphers.indexOf(',');
  177               if (index != -1) {
  178                   int fromIndex = 0;
  179                   while (index != -1) {
  180                       cipher = requestedCiphers.substring(fromIndex, index).trim();
  181                       if (cipher.length() > 0) {
  182                           /*
  183                            * Check to see if the requested cipher is among the
  184                            * supported ciphers, i.e., may be enabled
  185                            */
  186                           for (int i=0; supportedCiphers != null
  187                                        && i<supportedCiphers.length; i++) {
  188                               if (supportedCiphers[i].equals(cipher)) {
  189                                   if (vec == null) {
  190                                       vec = new Vector();
  191                                   }
  192                                   vec.addElement(cipher);
  193                                   break;
  194                               }
  195                           }
  196                       }
  197                       fromIndex = index+1;
  198                       index = requestedCiphers.indexOf(',', fromIndex);
  199                   } // while
  200                   cipher = requestedCiphers.substring(fromIndex);
  201               }
  202   
  203               if (cipher != null) {
  204                   cipher = cipher.trim();
  205                   if (cipher.length() > 0) {
  206                       /*
  207                        * Check to see if the requested cipher is among the
  208                        * supported ciphers, i.e., may be enabled
  209                        */
  210                       for (int i=0; supportedCiphers != null
  211                                    && i<supportedCiphers.length; i++) {
  212                           if (supportedCiphers[i].equals(cipher)) {
  213                               if (vec == null) {
  214                                   vec = new Vector();
  215                               }
  216                               vec.addElement(cipher);
  217                               break;
  218                           }
  219                       }
  220                   }
  221               }           
  222   
  223               if (vec != null) {
  224                   enabledCiphers = new String[vec.size()];
  225                   vec.copyInto(enabledCiphers);
  226               }
  227           } else {
  228               enabledCiphers = sslProxy.getDefaultCipherSuites();
  229           }
  230   
  231           return enabledCiphers;
  232       }
  233        
  234       /*
  235        * Gets the SSL server's keystore password.
  236        */
  237       protected String getKeystorePassword() {
  238           String keyPass = (String)attributes.get("keypass");
  239           if (keyPass == null) {
  240               keyPass = defaultKeyPass;
  241           }
  242           String keystorePass = (String)attributes.get("keystorePass");
  243           if (keystorePass == null) {
  244               keystorePass = keyPass;
  245           }
  246           return keystorePass;
  247       }
  248   
  249       /*
  250        * Gets the SSL server's keystore.
  251        */
  252       protected KeyStore getKeystore(String type, String pass)
  253               throws IOException {
  254   
  255           String keystoreFile = (String)attributes.get("keystore");
  256           if (keystoreFile == null)
  257               keystoreFile = defaultKeystoreFile;
  258   
  259           return getStore(type, keystoreFile, pass);
  260       }
  261   
  262       /*
  263        * Gets the SSL server's truststore.
  264        */
  265       protected KeyStore getTrustStore(String keystoreType) throws IOException {
  266           KeyStore trustStore = null;
  267   
  268           String trustStoreFile = (String)attributes.get("truststoreFile");
  269           if(trustStoreFile == null) {
  270               trustStoreFile = System.getProperty("javax.net.ssl.trustStore");
  271           }
  272           if(log.isDebugEnabled()) {
  273               log.debug("Truststore = " + trustStoreFile);
  274           }
  275           String trustStorePassword = (String)attributes.get("truststorePass");
  276           if( trustStorePassword == null) {
  277               trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
  278           }
  279           if( trustStorePassword == null ) {
  280               trustStorePassword = getKeystorePassword();
  281           }
  282           if(log.isDebugEnabled()) {
  283               log.debug("TrustPass = " + trustStorePassword);
  284           }
  285           String truststoreType = (String)attributes.get("truststoreType");
  286           if( truststoreType == null) {
  287               truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
  288           }
  289           if(truststoreType == null) {
  290               truststoreType = keystoreType;
  291           }
  292           if(log.isDebugEnabled()) {
  293               log.debug("trustType = " + truststoreType);
  294           }
  295           if (trustStoreFile != null && trustStorePassword != null){
  296               trustStore = getStore(truststoreType, trustStoreFile,
  297                                     trustStorePassword);
  298           }
  299   
  300           return trustStore;
  301       }
  302   
  303       /*
  304        * Gets the key- or truststore with the specified type, path, and password.
  305        */
  306       private KeyStore getStore(String type, String path, String pass)
  307               throws IOException {
  308   
  309           KeyStore ks = null;
  310           InputStream istream = null;
  311           try {
  312               ks = KeyStore.getInstance(type);
  313               if(! "PKCS11".equalsIgnoreCase(type) ) {
  314                   File keyStoreFile = new File(path);
  315                   if (!keyStoreFile.isAbsolute()) {
  316                       keyStoreFile = new File(System.getProperty("catalina.base"),
  317                                               path);
  318                   }
  319                   istream = new FileInputStream(keyStoreFile);
  320               }
  321   
  322               ks.load(istream, pass.toCharArray());
  323           } catch (FileNotFoundException fnfe) {
  324               throw fnfe;
  325           } catch (IOException ioe) {
  326               throw ioe;      
  327           } catch(Exception ex) {
  328               log.error("Exception trying to load keystore " +path,ex);
  329               throw new IOException("Exception trying to load keystore " +
  330                                     path + ": " + ex.getMessage() );
  331           } finally {
  332               if (istream != null) {
  333                   try {
  334                       istream.close();
  335                   } catch (IOException ioe) {
  336                       // Do nothing
  337                   }
  338               }
  339           }
  340   
  341           return ks;
  342       }
  343   
  344       /**
  345        * Reads the keystore and initializes the SSL socket factory.
  346        */
  347       void init() throws IOException {
  348           try {
  349   
  350               String clientAuthStr = (String) attributes.get("clientauth");
  351               if("true".equalsIgnoreCase(clientAuthStr) ||
  352                  "yes".equalsIgnoreCase(clientAuthStr)) {
  353                   requireClientAuth = true;
  354               } else if("want".equalsIgnoreCase(clientAuthStr)) {
  355                   wantClientAuth = true;
  356               }
  357   
  358               // SSL protocol variant (e.g., TLS, SSL v3, etc.)
  359               String protocol = (String) attributes.get("protocol");
  360               if (protocol == null) {
  361                   protocol = defaultProtocol;
  362               }
  363   
  364               // Certificate encoding algorithm (e.g., SunX509)
  365               String algorithm = (String) attributes.get("algorithm");
  366               if (algorithm == null) {
  367                   algorithm = KeyManagerFactory.getDefaultAlgorithm();;
  368               }
  369   
  370               String keystoreType = (String) attributes.get("keystoreType");
  371               if (keystoreType == null) {
  372                   keystoreType = defaultKeystoreType;
  373               }
  374   
  375           String trustAlgorithm = (String)attributes.get("truststoreAlgorithm");
  376           if( trustAlgorithm == null ) {
  377               trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
  378           }
  379               // Create and init SSLContext
  380               SSLContext context = SSLContext.getInstance(protocol); 
  381               context.init(getKeyManagers(keystoreType, algorithm,
  382                                           (String) attributes.get("keyAlias")),
  383                            getTrustManagers(keystoreType, trustAlgorithm),
  384                            new SecureRandom());
  385   
  386               // create proxy
  387               sslProxy = context.getServerSocketFactory();
  388   
  389               // Determine which cipher suites to enable
  390               String requestedCiphers = (String)attributes.get("ciphers");
  391               enabledCiphers = getEnabledCiphers(requestedCiphers,
  392                                                  sslProxy.getSupportedCipherSuites());
  393   
  394           } catch(Exception e) {
  395               if( e instanceof IOException )
  396                   throw (IOException)e;
  397               throw new IOException(e.getMessage());
  398           }
  399       }
  400   
  401       /**
  402        * Gets the initialized key managers.
  403        */
  404       protected KeyManager[] getKeyManagers(String keystoreType,
  405                                             String algorithm,
  406                                             String keyAlias)
  407                   throws Exception {
  408   
  409           KeyManager[] kms = null;
  410   
  411           String keystorePass = getKeystorePassword();
  412   
  413           KeyStore ks = getKeystore(keystoreType, keystorePass);
  414           if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
  415               throw new IOException(sm.getString("jsse.alias_no_key_entry", keyAlias));
  416           }
  417   
  418           KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
  419           kmf.init(ks, keystorePass.toCharArray());
  420   
  421           kms = kmf.getKeyManagers();
  422           if (keyAlias != null) {
  423               if (JSSESocketFactory.defaultKeystoreType.equals(keystoreType)) {
  424                   keyAlias = keyAlias.toLowerCase();
  425               }
  426               for(int i=0; i<kms.length; i++) {
  427                   kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], keyAlias);
  428               }
  429           }
  430   
  431           return kms;
  432       }
  433   
  434       /**
  435        * Gets the intialized trust managers.
  436        */
  437       protected TrustManager[] getTrustManagers(String keystoreType, String algorithm)
  438           throws Exception {
  439           String crlf = (String) attributes.get("crlFile");
  440           
  441           TrustManager[] tms = null;
  442           
  443           String truststoreType = (String) attributes.get("truststoreType");
  444           if (truststoreType == null) {
  445               truststoreType = keystoreType;
  446           }
  447           KeyStore trustStore = getTrustStore(truststoreType);
  448           if (trustStore != null) {
  449               if (crlf == null) {
  450                   TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
  451                   tmf.init(trustStore);
  452                   tms = tmf.getTrustManagers();
  453               } else {
  454                   TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
  455                   CertPathParameters params = getParameters(algorithm, crlf, trustStore);
  456                   ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
  457                   tmf.init(mfp);
  458                   tms = tmf.getTrustManagers();
  459               }
  460           }
  461           
  462           return tms;
  463       }
  464       
  465       /**
  466        * Return the initialization parameters for the TrustManager.
  467        * Currently, only the default <code>PKIX</code> is supported.
  468        * 
  469        * @param algorithm The algorithm to get parameters for.
  470        * @param crlf The path to the CRL file.
  471        * @param trustStore The configured TrustStore.
  472        * @return The parameters including the CRLs and TrustStore.
  473        */
  474       protected CertPathParameters getParameters(String algorithm, 
  475                                                   String crlf, 
  476                                                   KeyStore trustStore)
  477           throws Exception {
  478           CertPathParameters params = null;
  479           if("PKIX".equalsIgnoreCase(algorithm)) {
  480               PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore, 
  481                                                                        new X509CertSelector());
  482               Collection crls = getCRLs(crlf);
  483               CertStoreParameters csp = new CollectionCertStoreParameters(crls);
  484               CertStore store = CertStore.getInstance("Collection", csp);
  485               xparams.addCertStore(store);
  486               xparams.setRevocationEnabled(true);
  487               String trustLength = (String)attributes.get("trustMaxCertLength");
  488               if(trustLength != null) {
  489                   try {
  490                       xparams.setMaxPathLength(Integer.parseInt(trustLength));
  491                   } catch(Exception ex) {
  492                       log.warn("Bad maxCertLength: "+trustLength);
  493                   }
  494               }
  495   
  496               params = xparams;
  497           } else {
  498               throw new CRLException("CRLs not supported for type: "+algorithm);
  499           }
  500           return params;
  501       }
  502   
  503   
  504       /**
  505        * Load the collection of CRLs.
  506        * 
  507        */
  508       protected Collection<? extends CRL> getCRLs(String crlf) 
  509           throws IOException, CRLException, CertificateException {
  510   
  511           File crlFile = new File(crlf);
  512           if( !crlFile.isAbsolute() ) {
  513               crlFile = new File(System.getProperty("catalina.base"), crlf);
  514           }
  515           Collection<? extends CRL> crls = null;
  516           InputStream is = null;
  517           try {
  518               CertificateFactory cf = CertificateFactory.getInstance("X.509");
  519               is = new FileInputStream(crlFile);
  520               crls = cf.generateCRLs(is);
  521           } catch(IOException iex) {
  522               throw iex;
  523           } catch(CRLException crle) {
  524               throw crle;
  525           } catch(CertificateException ce) {
  526               throw ce;
  527           } finally { 
  528               if(is != null) {
  529                   try{
  530                       is.close();
  531                   } catch(Exception ex) {
  532                   }
  533               }
  534           }
  535           return crls;
  536       }
  537   
  538       /**
  539        * Set the SSL protocol variants to be enabled.
  540        * @param socket the SSLServerSocket.
  541        * @param protocols the protocols to use.
  542        */
  543       protected void setEnabledProtocols(SSLServerSocket socket, String []protocols){
  544           if (protocols != null) {
  545               socket.setEnabledProtocols(protocols);
  546           }
  547       }
  548   
  549       /**
  550        * Determines the SSL protocol variants to be enabled.
  551        *
  552        * @param socket The socket to get supported list from.
  553        * @param requestedProtocols Comma-separated list of requested SSL
  554        * protocol variants
  555        *
  556        * @return Array of SSL protocol variants to be enabled, or null if none of
  557        * the requested protocol variants are supported
  558        */
  559       protected String[] getEnabledProtocols(SSLServerSocket socket,
  560                                              String requestedProtocols){
  561           String[] supportedProtocols = socket.getSupportedProtocols();
  562   
  563           String[] enabledProtocols = null;
  564   
  565           if (requestedProtocols != null) {
  566               Vector vec = null;
  567               String protocol = requestedProtocols;
  568               int index = requestedProtocols.indexOf(',');
  569               if (index != -1) {
  570                   int fromIndex = 0;
  571                   while (index != -1) {
  572                       protocol = requestedProtocols.substring(fromIndex, index).trim();
  573                       if (protocol.length() > 0) {
  574                           /*
  575                            * Check to see if the requested protocol is among the
  576                            * supported protocols, i.e., may be enabled
  577                            */
  578                           for (int i=0; supportedProtocols != null
  579                                        && i<supportedProtocols.length; i++) {
  580                               if (supportedProtocols[i].equals(protocol)) {
  581                                   if (vec == null) {
  582                                       vec = new Vector();
  583                                   }
  584                                   vec.addElement(protocol);
  585                                   break;
  586                               }
  587                           }
  588                       }
  589                       fromIndex = index+1;
  590                       index = requestedProtocols.indexOf(',', fromIndex);
  591                   } // while
  592                   protocol = requestedProtocols.substring(fromIndex);
  593               }
  594   
  595               if (protocol != null) {
  596                   protocol = protocol.trim();
  597                   if (protocol.length() > 0) {
  598                       /*
  599                        * Check to see if the requested protocol is among the
  600                        * supported protocols, i.e., may be enabled
  601                        */
  602                       for (int i=0; supportedProtocols != null
  603                                    && i<supportedProtocols.length; i++) {
  604                           if (supportedProtocols[i].equals(protocol)) {
  605                               if (vec == null) {
  606                                   vec = new Vector();
  607                               }
  608                               vec.addElement(protocol);
  609                               break;
  610                           }
  611                       }
  612                   }
  613               }           
  614   
  615               if (vec != null) {
  616                   enabledProtocols = new String[vec.size()];
  617                   vec.copyInto(enabledProtocols);
  618               }
  619           }
  620   
  621           return enabledProtocols;
  622       }
  623   
  624       /**
  625        * Configure Client authentication for this version of JSSE.  The
  626        * JSSE included in Java 1.4 supports the 'want' value.  Prior
  627        * versions of JSSE will treat 'want' as 'false'.
  628        * @param socket the SSLServerSocket
  629        */
  630       protected void configureClientAuth(SSLServerSocket socket){
  631           if (wantClientAuth){
  632               socket.setWantClientAuth(wantClientAuth);
  633           } else {
  634               socket.setNeedClientAuth(requireClientAuth);
  635           }
  636       }
  637   
  638       /**
  639        * Configure Client authentication for this version of JSSE.  The
  640        * JSSE included in Java 1.4 supports the 'want' value.  Prior
  641        * versions of JSSE will treat 'want' as 'false'.
  642        * @param socket the SSLSocket
  643        */
  644       protected void configureClientAuth(SSLSocket socket){
  645           // Per JavaDocs: SSLSockets returned from 
  646           // SSLServerSocket.accept() inherit this setting.
  647       }
  648       
  649       /**
  650        * Configures the given SSL server socket with the requested cipher suites,
  651        * protocol versions, and need for client authentication
  652        */
  653       private void initServerSocket(ServerSocket ssocket) {
  654   
  655           SSLServerSocket socket = (SSLServerSocket) ssocket;
  656   
  657           if (enabledCiphers != null) {
  658               socket.setEnabledCipherSuites(enabledCiphers);
  659           }
  660   
  661           String requestedProtocols = (String) attributes.get("protocols");
  662           setEnabledProtocols(socket, getEnabledProtocols(socket, 
  663                                                            requestedProtocols));
  664   
  665           // we don't know if client auth is needed -
  666           // after parsing the request we may re-handshake
  667           configureClientAuth(socket);
  668       }
  669   
  670   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » tomcat » util » net » jsse » [javadoc | source]