zoukankan      html  css  js  c++  java
  • httpd设置HTTPS双向认证

    去年用tomcat、jboss配置过HTTPS双向认证,那时候主要用的是JDK自带的keytool工具。这次是用httpd + openssl,区别比较大

    在网上搜索了很多文章,发现全面介绍的不多,或者就是版本比较旧了。所以把我配置的过程完整地记录下来,以供参考

    首先要说明一下,HTTPS涉及到的内容非常繁杂,包括各种术语、命令、算法,我现在也没有完全搞清楚。本文会尽量把我知道的解释一下,但是深入的内容,暂时不打算深究了

    一、环境

    httpd: 2.4.4
    openssl:1.0.1
    os:ubuntu 12.04 LTS

    二、场景

    我准备在httpd上配置一个HTTPS双向认证,既向客户端表明自己的身份,也只允许特定的客户端访问。本文说的主要是作为server的角色的配置,至于作为client的配置,最后也会稍微介绍一下,但是不会详细说明

    一般来说,互联网站不会去配置双向认证,因为客户端证书的分发和管理会比较麻烦,会把用户挡在门外,所以一般是不能这么做的。当然,像银行等对安全要求很高的网站,也会采用双向认证,比如U盾、安全控件什么的,其实就是固化的客户端证书

    但是对于企业应用来说,客户一般是固定的,比如两个已知的系统对接、内部系统集成等。所以在企业应用领域,双向认证还是比较常见的

    三、背景知识

    证书(Certificate)是HTTPS的核心,但是其实证书并不是一个单一的东西,而是几种技术的综合

    为了网络传输的安全,有很多种技术,最主要的是以下3种:

    1、加密/解密

    避免消息明文传输,对消息进行加密。早期一般是用对称加密算法,现在一般都是不对称加密,最常见的算法就是RSA。在后面的介绍中,也会多次看到RSA这个词

    2、消息摘要

    这个技术主要是为了避免消息被篡改。消息摘要是把一段信息,通过某种算法,得出一串字符串。这个字符串就是消息的摘要。如果消息被篡改(发生了变化),那么摘要也一定会发生变化(一般是这样的。如果2个不同的消息生成的摘要是一样的,那么这就叫发生了碰撞)

    消息摘要的算法主要有MD5和SHA,在证书领域,一般都是用SHA(安全哈希算法)

    3、数字签名

    数字签名是为了验证双方的身份,避免身份伪造

    以上三个技术结合起来,就是在HTTPS中广泛应用的证书(certificate),证书本身携带了加密/解密的信息,并且可以标识自己的身份,也自带消息摘要

    四、在httpd中配置单向HTTPS

    首先在%HTTPD_HOME%/conf/目录下,修改httpd.conf文件,加载必要的模块

    Httpd代码  收藏代码
    1. LoadModule socache_shmcb_module modules/mod_socache_shmcb.so  
    2. LoadModule socache_dbm_module modules/mod_socache_dbm.so  
    3. LoadModule socache_memcache_module modules/mod_socache_memcache.so  
    4. LoadModule ssl_module modules/mod_ssl.so  


    这里的前提是,在编译httpd的时候,已经编译了ssl模块。这个步骤看另一篇文档:
    http://kyfxbl.iteye.com/blog/1902299

    然后再导入默认的SSL配置文件,当然也可以选择不导入,在httpd.conf直接配置。但是导入默认的可以节省很多时间,并且默认的文件是用vhost配置的,不会跟main server冲突,可以算是一种最佳实践

    Httpd代码  收藏代码
    1. # Secure (SSL/TLS) connections  
    2. Include conf/extra/httpd-ssl.conf  
    3.   
    4. <IfModule ssl_module>  
    5. SSLRandomSeed startup builtin  
    6. SSLRandomSeed connect builtin  
    7. </IfModule>  


    然后打开%HTTPD_HOME%/conf/extra/目录,看一下httpd-ssl.conf,主要有以下几个配置

    Httpd代码  收藏代码
    1. SSLEngine on  
    2. SSLCertificateFile "/usr/local/httpd/conf/server.cer"  
    3. SSLCertificateKeyFile "/usr/local/httpd/conf/server.key.pem"  
    4. #SSLCACertificateFile "/usr/local/httpd/conf/ca.cer"  
    5. #SSLVerifyClient require  
    6. #SSLVerifyDepth  10  


    只要开启前3个,单向的HTTPS认证就配置好了。后面3个目前先注释掉,是后面双向认证才用到

    然后重启一下httpd,会发现报错:

    AH00526: Syntax error on line 106 of /usr/local/httpd/conf/extra/httpd-ssl.conf:
    SSLCertificateFile: file '/usr/local/httpd/conf/server.cer' does not exist or is empty

    这是因为httpd需要一个服务端的私钥(.key.pem),和一个服务端证书(.cer)。前面已经配置了这2个文件的路径,但是还没有创建。下一步就要创建这些文件

    五、创建CA(Certificate Authority)

    这个CA,也叫“根证书”

    服务端做了一个证书,但是这是没有法律效力的,谁都可以自己做证书,就根本达不到安全的目的。所以就要有一个机构,负责来确认服务端的身份,然后统一的签发证书。这样才能有权威性

    当浏览器通过HTTPS协议访问一个网站,网站首先会发过来一个自己的证书(certificate)。接下来浏览器就会到权威机构(CA),去 验证一下这个证书是不是它签发的。如果是的话,就信任这个网站的证书,继续访问;如果不是的话,要怎么处理就依赖于实现了。一般的浏览器会弹出一个警告, 让用户自己决定要不要继续访问。当然直接拒绝也是可以的

    现在国际上有3大CA机构,如果是要自己做一个网站的话,如上所述,一般是需要请这些权威机构帮忙签发证书的。现在所有的主流浏览器,默认都安装 了这些CA的根证书,所以如果网站的证书是这些权威机构签发的,浏览器就不会发出警告了。比如支付宝,它的证书是由VeriSign签发的,所以访问支付 宝,浏览器不会发出警告



    这里还有一个链条的关系,比如我有10个子网站,如果每个都要去找CA签发证书,就很麻烦。我可以找CA给我签发一个次级根证书,然后再用这个次 级根证书给自己签发10个证书。那么只要客户的浏览器里有CA根证书就可以了,这10个证书都可以通过认证,不要求客户安装次级根证书,原文见下:

    引用
    Intermediate CA certificates lie between the root CA certificate (which is installed in the browsers) and the server certificate (which you installed on the server).



    如果是企业应用,那完全可以自己给自己当CA,因为可以要求目标用户(系统)安装自己的CA根证书,效果是一样的,还可以省下请权威CA签发证书的费用(互联网应用分发自己的CA到无数的互联网用户上,难度很大)

    下面就介绍如何创建自己的CA

    1、准备工作

    先在随便一个目录,创建以下几个子目录:
    /private
    /certificates

    其中private放的是私钥和CSR(后面会介绍),certificates里放的就是证书了

    2、创建CA私钥

    Openssl代码  收藏代码
    1. openssl genrsa -aes256 -out private/ca.key.pem 2048  


    最后的参数是RSA密钥的长度,默认是512。2048其实长了一点,老的浏览器稍后会不支持,不过现在的主流浏览器都是支持的,所以问题不大

    通过这个命令,私钥就创建好了,文件名是ca.key.pem

    用这个命令,可以看一下刚才创建的这个私钥的信息

    Openssl代码  收藏代码
    1. openssl rsa -noout -text -in private/ca.key.pem  


    不过基本上,看也是白看,反正我是看不懂。只知道私钥里其实有2组数字,是用来形成公钥的,最后也会包含在证书里

    引用

    A private key contains a series of numbers. Two of these numbers form the "public key", the others are part of the "private key". The "public key" bits are included when you generate a CSR, and subsequently form part of the associated Certificate.



    另外,最后的.pem扩展名,是表示该私钥用PEM编码。实际上私钥和证书都是用PEM编码的,PEM只是一种编码格式,不需要太在意。 httpd可以直接处理这种编码格式,但是浏览器和JAVA都不行,所以在需要的时候,会把编码从PEM改成PKCS,后面会介绍。只要知道证书和私钥都 有编码,只是编码是PEM还是PKCS的区别而已。就像"你好"可以用UTF-8编码,也可以用GBK编码一样,内容是不变的

    3、创建CA签名请求

    Openssl代码  收藏代码
    1. openssl req -new -key private/ca.key.pem -out private/ca.csr -subj "/C=CN/ST=SZ/L=SZ/O=kyfxbl/OU=kyfxbl/CN=*.kyfxbl.net"  


    这里要注意的是,如果不用-subj参数,那么就会在命令行交互输入签发目标的身份识别信息,这叫DN(Distinguished Name)。其中别的都不要紧,最重要的是CN那一行,因为我这里是根证书,所以我设置为*.kyfxbl.net,这样我后面用这个CA签发的 www.kyfxbl.net、game.kyfxbl.net、news.kyfxbl.net……,全都是有效的

    生成的签名请求文件,是ca.csr

    Openssl代码  收藏代码
    1. openssl req -noout -text -in private/ca.csr  


    同上,看不懂

    4、自己签发CA根证书

    Openssl代码  收藏代码
    1. openssl x509 -req -days 3650 -sha1 -extensions v3_ca -signkey private/ca.key.pem -in private/ca.csr -out certificates/ca.cer  


    这里参数很复杂,我也不太清楚准确的意思是什么,可以用openssl x509 -help自己研究一下

    生成的ca.cer,就是最终的根证书了!这个文件非常重要,因为后续的服务端证书、客户端证书,都是用这个CA签发的,也要把它分发给客户,让他们导入到自己的浏览器或者系统中

    查看的命令是:

    Openssl代码  收藏代码
    1. openssl x509 -noout -text -in certificates/ca.cer  



    5、把根证书从PEM编码转为PKCS编码

    这步其实不是必选的,但是前面说过,JAVA环境是不能直接用PEM编码的证书的,很多浏览器也不行,所以有时候也需要转一下编码

    Openssl代码  收藏代码
    1. openssl pkcs12 -export -cacerts -inkey private/ca.key.pem -in certificates/ca.cer -out certificates/ca.p12  


    得到的ca.p12就是转码后的CA根证书,在不能直接用ca.cer的时候,就用ca.p12代替

    六、签发服务端证书

    现在CA根证书和私钥都有了,就可以开始签发服务端证书了(签发请求ca.csr是过程文件,有了cer就不再需要它了,要删掉也可以)。下面的命令和签发CA证书时都差不多,但是参数上有区别

    1、创建服务端私钥

    Openssl代码  收藏代码
    1. openssl genrsa -aes256 -out private/server.key.pem 2048  


    2、创建服务端证书签发请求

    Openssl代码  收藏代码
    1. openssl req -new -key private/server.key.pem -out private/server.csr -subj "/C=CN/ST=SZ/L=SZ/O=kyfxbl/OU=kyfxbl/CN=www.kyfxbl.net"  


    和ca.csr的区别在于,这里的CN不是*.kyfxbl.net,而是www.kyfxbl.net,因为我现在是在为www.kyfxbl.net申请证书

    3、利用CA根证书,签发服务端证书

    Openssl代码  收藏代码
    1. openssl x509 -req -days 3650 -sha1 -extensions v3_req -CA certificates/ca.cer -CAkey private/ca.key.pem -CAserial ca.srl -CAcreateserial -in private/server.csr -out certificates/server.cer  


    这里和前面自己签发CA证书时,参数区别就比较大了,最后得到的server.cer,就是服务端证书

    七、测试单向认证

    把server.key.pem和server.cer拷贝到%HTTPD_HOME%/conf/目录下,然后重新启动httpd,会要求输入一个密码



    然后访问http://localhost:443/,会报400错误:



    接下来用https://localhost:443来访问,浏览器报警:



    这里就是前面创建CSR时,输入的CN的作用,这个证书是为www.kyfxbl.net申请的,这里请求的地址却是localhost,不匹配所以报错。为了能用www.kyfxbl.net这个主机名来访问,就需要改一下/etc/hosts文件:

    127.0.0.1       localhost
    192.168.1.102   www.kyfxbl.net

    然后就可以用www.kyfxbl.net来访问了,再试一下:https://www.kyfxbl.net/

    这次浏览器还是告警,但是告警信息变了:



    证书信息如下:



    可以看到这个证书是由*.kyfxbl.net这个CA颁发的,浏览器不认识,所以不信任由这个CA签发的所有证书。接下来就需要把ca.cer 导入浏览器。这里直接导入server.cer也是可以的,但是后面如果又创建一个网站比如说www2.kyfxbl.net,那么又不行了。所以最好的 办法是直接导入CA根证书,那么后续只要是用这个根证书签发的证书,浏览器都会信任

    导入前:



    导入后:



    再次访问,可以看到成功了,浏览器不告警,并且URL栏前面打了一个绿勾



    八、配置双向认证

    如果要配置服务器只允许合法的用户访问,就需要配置双向认证

    配置为双向认证之后,除了服务端要发证书给客户端之外,客户端也要发客户端证书到服务端,服务端认证通过,才允许访问

    Httpd代码  收藏代码
    1. SSLCACertificateFile "/usr/local/httpd/conf/ca.cer"  
    2. SSLVerifyClient require  
    3. SSLVerifyDepth  10  


    在单项认证的基础上,再配置以上3个参数

    SSLCACertificateFile,这个意思是当客户端发来客户端证书的时候,httpd用哪个CA根证书校验它

    配置好了,还不能重启,因为现在客户端证书还没做好

    这里要说明一下,客户端证书是怎么来的

    有2种方式:

    第一种,客户端也自己CA,然后签发证书给自己。把客户端的CA根证书发过来,配置成SSLCACertificateFile。这在互联网应用里基本是不可能的,安全和管理都是问题。但是在企业应用里,还是比较常见的,双方互相交换CA根证书

    第二种,就用刚才服务端的CA根证书,签发一个客户端证书,发给用户,用户每次用这个证书来发请求,像银行,支付宝等等,用的是这种方式

    当然理论上其实还有一种办法,就是客户自己去找权威CA签证书,但是这个是不可能的,因为很麻烦,找CA签也非常贵

    本文用的是第2种方法。其实都是一样的。关键还是在CA根证书上,所以前面也说过了,CA根证书非常重要

    九、签发客户端证书

    1、创建客户端私钥

    Openssl代码  收藏代码
    1. openssl genrsa -aes256 -out private/client.key.pem 2048  


    2、创建客户端证书签发请求

    Openssl代码  收藏代码
    1. openssl req -new -key private/client.key.pem -out private/client.csr -subj "/C=CN/ST=SZ/L=SZ/O=kyfxbl/OU=kyfxbl/CN=kyfxbl"  


    这里的不同在于,这里的CN不是*.kyfxbl.net,也不是www.kyfxbl.net,随便填一个kyfxbl就好了,或者干脆叫user都没问题,反正是一个客户端证书

    3、利用CA根证书,签发客户端证书

    Openssl代码  收藏代码
    1. openssl x509 -req -days 3650 -sha1 -extensions v3_req -CA certificates/ca.cer -CAkey private/ca.key.pem -CAserial ca.srl -CAcreateserial -in private/client.csr -out certificates/client.cer  


    这里和签发server.cer基本是一样的

    4、把客户端证书转换成p12格式

    Openssl代码  收藏代码
    1. openssl pkcs12 -export -clcerts -inkey private/client.key.pem -in certificates/client.cer -out certificates/client.p12  


    这步是必须的,因为稍后就需要把客户端证书导入到浏览器里,但是一般浏览器都不能直接使用PEM编码的证书

    十、测试双向认证

    把ca.cer拷贝到%HTTPD_HOME%/conf/目录下,重启httpd

    然后再次访问https://www.kyfxbl.net/,结果这次不是警告,而是直接报错:



    接下来要把client.p12导入到浏览器里

    导入前:



    导入的时候会要求输入密码,这是为了避免有人偷偷拷贝了别人的客户端证书,伪装成合法用户:



    导入后:



    然后再次访问,浏览器会要求选择证书。这个步骤是通过双向认证访问网站时必须的,但是平时访问银行、支付宝的时候貌似没有,这是因为这些网站为了简化用户的操作,都会要求用户安装什么“安全控件”,控件自动选择了证书



    点击确定,访问成功!

    十一、JKS等

    到这里,用httpd配置双向HTTPS认证就完成了

    本文说的是作为server的角色,要如何配置。但是在JAVA环境下,如果要以client的角色,通过双向认证发起请求,则还有些不同

    这里实际上是要充当一个类似浏览器的角色,需要校验server certificate,还需要在发起请求的时候,把client certificate发过去

    在JAVA里,是通过keystore和truststore来实现的,不在本文的讨论范围内。可以看一下其他的几篇博客,在HTTPS分类里

    十二、参考资料

    http://httpd.apache.org/docs/2.4/en/ssl/
    http://linux.chinaunix.net/techdoc/net/2008/01/08/976172.shtml
    《JAVA加密与解密的艺术》——作者梁栋

  • 相关阅读:
    (Java实现) 洛谷 P1603 斯诺登的密码
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1553 数字反转(升级版)
    8.4 确定两个日期之间的月份数或年数
    (Java实现) 洛谷 P1553 数字反转(升级版)
  • 原文地址:https://www.cnblogs.com/kabi/p/5623198.html
Copyright © 2011-2022 走看看