暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Tomcat深入研究文章之七(Userguide之十二SSL/TLS中文翻译)

中间件技术讨论圈 2016-03-13
347

SSL/TLS 配置


提纲

  1. Prepare the Certificate Keystore

  2. Edit the Tomcat Configuration File

  • Installing a Certificate from a Certificate Authority

    1. Create a local Certificate Signing Request (CSR)

    2. Importing the Certificate

  • Troubleshooting

  • Using the SSL for session tracking in your application

  • Miscellaneous Tips and Bits

  • Quick Start

    下面的步骤都需要以$CATALINA_BASE作为基础的相对路径的,如果你没有设置$CATALINA_BASE为多个Tomcat实例的话,那么$CATALINA_BASE就会指向$CATALINA_HOME.


    如果想配置SSL/TLS支持在Tomcat中,那么你就需要按照下面以下几个步骤.


    1. 创建keystore文件,这个keystore文件是存储服务器端的私钥和自签名证书的:


      Windows:

      "%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA

      Unix:

      $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA

      上述的密码需要给出 "changeit" 作为默认密码.


    2. 解注释$CATALINA_BASE/conf/server.xml文件中的 "SSL HTTP/1.1 Connector" 的配置,按照下面的 Configuration section 这一节进行配置,SSL/TLS就配置完毕了.(具体见下面)

    Introduction to SSL/TLS

    TLS和TLS的前驱 SSL,这两种协议是保证浏览器和服务器端安全连接的,设置这个就意味着发送的数据在一段进行加密,传输,然后再另一端执行之前进行解密,这是一个对端的过程,浏览器和服务器端都需要加密发送数据并且机密.


    另外的一个重要的方面是SSL,TLS协议认证,也就是说作为客户端初始化与服务器的安全连接的时候,服务器会出示给你客户端一系列的证书,这个证书代表这个站点的情况。在一些场景下,服务器端也会需要验证客户端的证书,这个就是客户端认证,类似于B2B这种business-to-business的模式。不过,大多数服务器还是不需要客户端认证的(损耗性能太大,得不偿失)。

    SSL/TLS and Tomcat

    如果服务器的stand-alone webserver就是Tomcat的话,Tomcat本身就可以配置基于安全的socket,细节在 Security Considerations Document 中。


    而如果Tomcat仅仅是作为一个Servlet/JSP容器,放置在其他的web server,如Apache或者微软的IIS后面的时候,首先需要注意的是,这个web server必须得配置SSL连接才行。这种情况,这个web server会协调SSL的相关机制,在对请求解密后再传递给tomcat容器,而tomcat做的是返回响应,注意这个响应也不是加密过得,传递到Web server中再进行加密。在这种情况下,Tomcat知道Web server主要负责SSL处理,而Tomcat作为后端的一个处理环节,并不参与到加密和解密中.

    Certificates

    要想实现这个SSL,web server必须要与自身IP对应的安全连接匹配的证书。这么设计的原因是服务器有很多的安全问题,并且有很多敏感信息需要去证明,你可以理解为本文中的证书就类似于"digital driver's license"(数字驾照)对应当前的IP。这个“数字驾照”中包含当前站点是哪个机构创建的,有一些站点所有人和管理员的基本信息。


    这个“数字驾照”一般都是自己给自己签个名,很难被其它人复制。但是对于一些电子商务站点或者其它涉及到钱的非常重要的事务交易,这些证书一般都得从CA机构中购买,类似于VeriSign 或者Thawte,这些购买的证书非常的可信,因为这个CA机构可以作为担保,所以作为客户端来讲,你也可以相信这个证书是有效的。


    在一些场景下,认证并并不意味着需要购买证书。对于管理员来讲关注的内容是传输的过程中,只要数据是隐私的,并且是不被窃取即可。JAVA就提供了一个 keytool 的命令行工具,可以方便的创建“自签名”的证书。这个自签名的证书很简单就可以做出来,但是并没有被上述的这些著名的CA机构,如VeriSign 或者Thawte认证,而这个是否关键取决于你的项目需求。

    General Tips on Running SSL

    当用户第一次访问站点中的安全页面,可能会提示一个弹出框,问你是否确认证书(含有公司和姓名)是否有效,让你是否点击确认进行交易。很多浏览器会提供一个存储证书的功能,这样每一次访问这个页面就不用每一次弹出提示框了,但是有的浏览器并没有这个选项。对于证书一旦被客户端接收了,那么这个证书在整个的浏览器生命周期中就有效,并起到重要的作用。


    我们需要注意的是,SSL协议是一个呗设计成一个足够安全的协议,但是站在性能的角度上来说,加密,解密,传输等过程却是非常耗时的过程。没有必要将整个站点都至于SSL协议,对于一个web开发人员来说,需要选择哪一些页面需要安全连接,哪一些不需要。以一个购买网站为例,仅仅有部分敏感页面在SSL协议即可,例如登录页面,个人信息页面,购物篮页面,信用卡页面等等。这些页面的采用安全scoket,并在访问地址的前缀使用https: 来替代http: 。


    如果使用虚拟主机对于安全连接是个问题,这就是SSL协议设计的限制。SSL的握手,客户端接收到服务器端的证书,是发生过在http请求之前,因此,http请求信息中包含虚拟主机的名字无法在这个环节被用于认证,也就是意味着一个IP不能设置多个证书。如果对应一个IP上的所有的虚拟主机都需要认证同一个证书,那么服务器端的SSL链接将无法进行。需要知道的是,对于客户端浏览器会比对证书中对应的域名字段与服务器的域名字段,如果是上述的秦光,浏览器就会提出警告信息告知该证书中的域名并不匹配。总结一下,这也就是说只有IP地址和虚拟主机一一对应的服务器才能用SSL。

    Configuration

    Prepare the Certificate Keystore

    Tomcat通常用 JKS
    PKCS11
     or PKCS12
     这三种格式的 keystore。JKS 格式的是java标准keystore格式,它是被keytools工具创建出来的,这个工具是含盖在JDK中的。PKCS11
      格式是国际通用标准,可以被openssl,或者微软的Key-Manager 所管理。


    keystore中的每一条记录都对应一个别名字符串,大多数的keystore实现都对这个字符串是大小写敏感的。对于PKCS11
     规范,要求这个字符串也是大小写敏感的。所以,为了避免过多的关于大小写的问题,推荐尽量别名尽量采用不同字符,而不采用相同字符的大小写进行区分。


    要导入现有的证书到JKS的keystore,请阅读相关的keytool的文档。需要注意的是,openssl中通常会在证书中加上一些可读的注释字段,但是keytool并不会这么做,所以当你用keytool的时候,你需要实现去掉这个注释的字段。


    以openssl为例,导入你的CA中心证书到PKCS12
     的keystore中的命令行如下 :

    openssl pkcs12 -export -in mycert.crt -inkey mykey.key
                           -out mycert.p12 -name tomcat -CAfile myCA.crt
                           -caname root -chain

    更多的用法请参考 OpenSSL documentation.

    创建一个新的jks keystore,包含单个的自签名证书,执行下面的命令:

    Windows:

    "%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA

    Unix:

    $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA

    (在上述的命令中,采用的是RSA算法作为签名算法,服务器端和客户端二者都是采用这个算法.)

    这个命令将会创建一个新文件,在你执行的目录下,名字为"xxx.keystore
    " , 如果你要给定不同的生成位置和文件名,那么就需要加上-keystore 参数,这个参数的内容就是包含你keystore文件名的全路径名,这个路径名也是在后面你在server.xml中配置的内容。


    举例:

    Windows:

    "%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA
      -keystore \path\to\my\keystore

    Unix:

    $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA
      -keystore /path/to/my/keystore

    执行完这个命令,命令行首先提示你输入这个keystore的密码。这个默认的密码就是前面说的"changeit
    " ,而这个密码也是后面server.xml配置文件中重要的配置项,稍后就可以看到。


    下一步,命令行会让你输入一些证书相关的信息,例如公司,联系方式等等,这些信息是展示给客户端的,客户端访问你应用中的安全页面,客户端通过这些信息确信这个“服务器”是对应的服务器。


    最后,命令行提示你需要输入key password ,这个密码和前面的keystore的密码不一样,是证书的密码。如果你直接按了Enter键的话,那么keytool工具就会默认使用keystore相同的密码给这个证书,你可以对这个密码进行任意的定制,一旦你选择了不同的密码,你就需要在后面的server.xml中配置中将这个配置一一对应。


    如果上述都很顺利,那么这个keystore file 就可以被你的服务器所使用了。

    Edit the Tomcat Configuration File

    Tomcat有两种不同的SSL实现:

    • JSSE实现 (since 1.4)

    • APR实现(默认采用的是openssl开源项目的引擎中的重要部分).


    对应的配置取决于你采用哪一个实现。如果你配置了protocol="HTTP/1.1" 的连接器,那么Tomcat自动选择JSSE作为实现。如果你安装了APR(也就是Tomcat Native),那么它会默认采用APR SSL的实现,否则还是会使用JSSE的实现。


    APR和JSSE的配置实现是不同,最好的建议是你需要明确的在server.xml中指出来,避免Tomcat自动进行选择


    例如下面,使用的是JSSE的Connector,无论你的APR的库load进没进来:

    <!-- Define a HTTP/1.1 Connector on port 8443, JSSE NIO implementation -->
    <Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
               port="8443" .../>
    <!-- Define a HTTP/1.1 Connector on port 8443, JSSE NIO2 implementation -->
    <Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
               port="8443" .../>
    <!-- Define a HTTP/1.1 Connector on port 8443, JSSE BIO implementation -->
    <Connector protocol="org.apache.coyote.http11.Http11Protocol"
               port="8443" .../>

    下面是APR的连接器配置:

    <!-- Define a HTTP/1.1 Connector on port 8443, APR implementation -->
    <Connector protocol="org.apache.coyote.http11.Http11AprProtocol"
               port="8443" .../>

    如果你使用APR,前面说过是默认采用的openssl的引擎,下面可以看到是可以配置SSLEngine.

    <Listener className="org.apache.catalina.core.AprLifecycleListener"
              SSLEngine="someengine" SSLRandomSeed="somedevice" />

    默认的就是openssl了:

    <Listener className="org.apache.catalina.core.AprLifecycleListener"
              SSLEngine="on" SSLRandomSeed="builtin" />

    所以,当你更换SSLEngine的时候,一定确信这个名称是有效的,否则SSL整个功能都会不生效。


    SSLRandomSeed 是种子,特别是生产系统中需要这个随机数保证整个Tomcat的安全可靠性。


    最后一步就是配置$CATALINA_BASE/conf/server.xml
     文件了。 $CATALINA_BASE 代表着Tomcat的实例的地址,<Connector> 元素需要进行配置,以JSSE实现为例,你需要将原来server.xml中的注释去掉,然后编辑如下:



    <!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
    <Connector
               protocol="org.apache.coyote.http11.Http11NioProtocol"
               port="8443" maxThreads="200"
               scheme="https" secure="true" SSLEnabled="true"
               keystoreFile="${user.home}/.keystore" keystorePass="changeit"
               clientAuth="false" sslProtocol="TLS"/>



    APR的连接器对于SSL有很多的属性可以设置,下面是一个比较全的APR配置:

    <!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
    <Connector
               protocol="org.apache.coyote.http11.Http11AprProtocol"
               port="8443" maxThreads="200"
               scheme="https" secure="true" SSLEnabled="true"
               SSLCertificateFile="/usr/local/ssl/server.crt"
               SSLCertificateKeyFile="/usr/local/ssl/server.pem"
               SSLVerifyClient="optional" SSLProtocol="TLSv1+TLSv1.1+TLSv1.2"/>

    上述对于Connector的配置不能出错,这些属性都在 HTTP connector 的配置文档中详细的描述过来,务必配置为正确的属性。这里再最后总结一下,对于BIO,NIO,NIO2的Connector 使用的是JSSE的实现,对于配置成APR的Connector,这里使用的是APR。


    port属性是监听安全连接的端口,当然你也可以更换这个端口,默认为8443,当然1024以下的端口尽量不要用,有的操作系统禁止使用1024以下的端口。


    如果你已经换了port属性的话,那么Connector同时如果要支持non-SSL,redirectPort
     
     属性应该被修改:



    如果完成了上述的修改后,你必须重启Tomcat,然后访问



    https://localhost:8443/



    这个时候,你就会看到Tomcat的主页,不过这回你并不是采用HTTP访问的,而是都采用HTTPS访问的。

    Installing a Certificate from a Certificate Authority

    如果需要获得和安装一个证书,从一些可信的CA机构,如verisign.com, thawte.com 或者trustcenter.de,那么就需要按照下面的指令:

    Create a local Certificate Signing Request (CSR)

    如果要从CA机构获得一个证书,你首先需要创建一个叫做CSR(Certificate Signing Request)的东西,这个CSR提交到对应的CA中心,然后被CA机构所“认证”。

    创建一个CSR按照下面的步骤:


    • 创建一个本地的自签名的证书(上面的步骤就是创建自签名的):

      keytool -genkey -alias tomcat -keyalg RSA
          -keystore <your_keystore_filename>

      注意,你需要为你自己的网站加上域名,并且填上 "first- and lastname".

    • 然后基于你的创建的自签名证书,来创建CSR:

      keytool -certreq -keyalg RSA -alias tomcat -file certreq.csr
          -keystore <your_keystore_filename>

    这个时候,你的CSR的文件为 certreq.csr
      ,然后你需要拿着这个CSR文件到CA机构中去申请签名,CA机构就如同上述的三大CA厂商,然后CA机构经过审核之后,批准你的CSR申请,返回给你一个证书,这个证书就是CA机构签名过的.

    Importing the Certificate

    现在你手头上又一个CA机构认证的证书,你需要将这个证书导入到你的keystore中。

    首先,你需要导入证书链或者叫做根证书到你的keystore中,这个是必须做的,然后你才能导入CA中心给你发的证书。


    Troubleshooting

    在Tomcat配置SSL链接的时候,有很多的问题如下。



    • 当Tomcat启动后出现异常:"java.io.FileNotFoundException: {some-directory}/{some-file} not found"
      这个问题就是Tomcat没有找到对应的keystore文件,Tomcat期望ketstore文件以.keystore 为结尾,并且指向keystoreFile
        为属性的路径下。

    • 当Tomcat启动后出现异常:"java.io.FileNotFoundException: Keystore was tampered with, or password was incorrect"
      出现这个问题的原因有人篡改了keystore的文件,这就导致了这个keystore的密码和Tomcat配置的密码不对应了,因此如果想要解决的话,就需要重新创建keystore文件,创建完后你需要更新keystorePass
       属性。(注意大小写敏感) 

    • 当Tomcat启动后出现异常:"java.net.SocketException: SSL handshake error javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled."
      出现这个问题很大可能是Tomcat没有找到keystore中的keyalias别名,你需要详细的检验<Connector>
        元素下面的keystoreFile
       and keyAlias
       是否是正确的。

    • java客户端在于Tomcat的SSL握手的时候,可能出现"java.lang.RuntimeException: Could not generate DH keypair" and "java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)"
      当你使用APR/Native的Connector,版本号为1.1.34,DH keypair的强度取决于RSA证书的长度。举个例子,2048位的RSA可以产生2048位的DH keypair。但是不幸的是,JAVA 6仅仅支持768位,JAVA 7仅仅支持1024位。所以,你的证书如果有更强的key的时候,java客户端就会产生SSL握手失败。当然你可以选择配置SSLCipherSuite
       和 SSLHonorCipherOrder
       激活你的证书中的弱DH参数,效果是直接会降低DH密钥对的强度,但是也会降低SSL连接的安全性。



    Using the SSL for session tracking in your application

    Servlet 3.0规范中有一个新特性,可以使用SSL 的Session ID去关联客户端和服务端的链接:

    • 需要设置Tomcat的Connector的 isSecure 的属性为 true
      .

    • 如果SSL链接中有静态代理(类似于Apache,Nginx)或者硬件转发(F5),他们可能会过滤一些SSL 请求头,你必须配置SSLValve,导致SSL Session ID必须对Tomcat是可见的.

    • If Tomcat terminates the SSL connection, it will not be possible to use session replication as the SSL session IDs will be different on each node(待翻译)。


    如果想要启动这个SSL session tracking ,你需要配置一个Context Listener,设置tracking mode 为SSL,以下面为例:

    package org.apache.tomcat.example;
    import java.util.EnumSet;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.SessionTrackingMode;
    public class SessionTrackingModeListener implements ServletContextListener {
        @Override
        public void contextDestroyed(ServletContextEvent event) {
            // Do nothing
        }
        @Override
        public void contextInitialized(ServletContextEvent event) {
            ServletContext context = event.getServletContext();        EnumSet<SessionTrackingMode> modes =
                EnumSet.of(SessionTrackingMode.SSL);
            context.setSessionTrackingModes(modes);
        }
    }

    注意: SSL session tracking 只能用在 the BIO, NIO and NIO2 connectors(也就是JSSE的实现上). 在APR connector是不能用的。

    Miscellaneous Tips and Bits

    在request中访问SSL 的Session ID :

    String sslID = (String)request.getAttribute("javax.servlet.request.ssl_session_id");

    终结SSL Session:

    // Standard HTTP session invalidation
    session.invalidate();
    // Invalidate the SSL Session
    org.apache.tomcat.util.net.SSLSessionManager mgr =
        (org.apache.tomcat.util.net.SSLSessionManager)
        request.getAttribute("javax.servlet.request.ssl_session_mgr");mgr.invalidateSession();// Close the connection since the SSL session will be active until the connection
    // is closedresponse.setHeader("Connection", "close");

    注意: 上述的这段代码是使用了Tomcat的内部类 SSLSessionManager class,还是和上面一样 这个功能 只能用在 the BIO, NIO and NIO2 connectors(也就是JSSE的实现上). 在APR connector是不能用的。


    文章转载自中间件技术讨论圈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论