https nginx 双向认证

最近要做个https 双向认证的,就做了个demo,踩了不少坑,记录一下.

主要还是对原理理解不够透彻,刚开始直接从网上搬个例子就用,结果大部分都不适用.


1.单向,双向认证过程详解

2.证书格式说明

3.使用openssl生成证书

4.nginx配置(服务器端)

5.浏览器访问(客户端)

6.java代码访问(客户端)

7.使用购买的证书(信任机构签发的)


1.单向,双向认证过程详解

过程参考博客 http://blog.chinaunix.net/uid-26335251-id-3508651.html

单向认证只是客户端认证服务端, 双向认证就是相互都要认证(双向认证这种,在浏览器一般都体现为需要证书)


2.证书格式说明

在使用openssl自己生成证书的时候,会发现网上很多例子生成的证书格式都不同,同一篇文章里也会有很多种格式.

所以就需要了解下 不同格式有什么区别和联系

参考博客http://blog.csdn.net/justinjing0612/article/details/7770301

参考博客http://www.cnblogs.com/lzjsky/archive/2010/11/14/1877143.html


der,cer文件一般是二进制格式的,只放证书,不含私钥
crt文件可能是二进制的,也可能是文本格式的,应该以文本格式居多,功能同der/cer
pem文件一般是文本格式的,可以放证书或者私钥,或者两者都有
pem如果只含私钥的话,一般用.key扩展名,而且可以有密码保护
pfx,p12文件是二进制格式,同时含私钥和证书,通常有保护密码


3.使用openssl生成证书

创建一个文件夹存放生成的证书

[html] view plain copy
 print?
  1. 1)创建根证私钥  
  2. openssl genrsa -out root-key.key 1024  

[html] view plain copy
 print?
  1. 2)创建根证书请求文件  
[html] view plain copy
 print?
  1. openssl req -new -out root-req.csr -key root-key.key  
  2.   
  3. 具体如下  
  4. [root@localhost sslKey]# openssl req -new -out root-req.csr -key root-key.key  
  5. You are about to be asked to enter information that will be incorporated  
  6. into your certificate request.  
  7. What you are about to enter is what is called a Distinguished Name or a DN.  
  8. There are quite a few fields but you can leave some blank  
  9. For some fields there will be a default value,  
  10. If you enter '.', the field will be left blank.  
  11. -----  
  12. Country Name (2 letter code) [XX]:cn  
  13. State or Province Name (full name) []:bj  
  14. Locality Name (eg, city) [Default City]:bj  
  15. Organization Name (eg, company) [Default Company Ltd]:dc  
  16. Organizational Unit Name (eg, section) []:dc  
  17. Common Name (eg, your name or your server's hostname) []:root  
  18. Email Address []:  
  19.   
  20. Please enter the following 'extra' attributes  
  21. to be sent with your certificate request  
  22. A challenge password []:  
  23. An optional company name []:  
[html] view plain copy
 print?
  1. 7)生成服务端证书(root证书,rootkey,服务端key,服务端请求文件这4个生成服务端证书)  
  2. openssl x509 -req -in server-req.csr -out server-cert.cer -signkey server-key.key -CA root-cert.cer -CAkey root-key.key -CAcreateserial -days 3650  



在2)的时候,会让填写信息,其中国家,省市,公司等需要和后面的证书保持一致.后面challenge password的地方直接回车就好

[html] view plain copy
 print?
  1. 3)自签根证书  
  2. openssl x509 -req -in root-req.csr -out root-cert.cer -signkey root-key.key -CAcreateserial -days 3650  

[html] view plain copy
 print?
  1. 4)生成p12格式根证书,密码填写123456  
  2. openssl pkcs12 -export -clcerts -in root-cert.cer -inkey root-key.key -out root.p12  

[html] view plain copy
 print?
  1. 5)生成服务端key  
  2. openssl genrsa -out server-key.key 1024  

[html] view plain copy
 print?
  1. 6)生成服务端请求文件  
  2. openssl req -new -out server-req.csr -key server-key.key  
  3.   
  4. 具体如下  
  5. [root@localhost sslKey]# openssl req -new -out server-req.csr -key server-key.key  
  6. You are about to be asked to enter information that will be incorporated  
  7. into your certificate request.  
  8. What you are about to enter is what is called a Distinguished Name or a DN.  
  9. There are quite a few fields but you can leave some blank  
  10. For some fields there will be a default value,  
  11. If you enter '.', the field will be left blank.  
  12. -----  
  13. Country Name (2 letter code) [XX]:cn  
  14. State or Province Name (full name) []:bj  
  15. Locality Name (eg, city) [Default City]:bj  
  16. Organization Name (eg, company) [Default Company Ltd]:dc  
  17. Organizational Unit Name (eg, section) []:dc  
  18. Common Name (eg, your name or your server's hostname) []:*.ttt.com  
  19. Email Address []:  
  20.   
  21. Please enter the following 'extra' attributes  
  22. to be sent with your certificate request  
  23. A challenge password []:  
  24. An optional company name []:  

在6)的时候,国家省市公司和2)保持一致, Common Name 要特别注意, 要用你服务器的域名,我们测试用ttt.com

[html] view plain copy
 print?
  1. 7)生成服务端证书(root证书,rootkey,服务端key,服务端请求文件这4个生成服务端证书)  
  2. openssl x509 -req -in server-req.csr -out server-cert.cer -signkey server-key.key -CA root-cert.cer -CAkey root-key.key -CAcreateserial -days 3650  

[html] view plain copy
 print?
  1. 8)生成客户端key  
  2. openssl genrsa -out client-key.key 1024  

[html] view plain copy
 print?
  1. 9)生成客户端请求文件  
  2. openssl req -new -out client-req.csr -key client-key.key  
  3.   
  4. 具体如下  
  5. [root@localhost sslKey]# openssl req -new -out client-req.csr -key client-key.key  
  6. You are about to be asked to enter information that will be incorporated  
  7. into your certificate request.  
  8. What you are about to enter is what is called a Distinguished Name or a DN.  
  9. There are quite a few fields but you can leave some blank  
  10. For some fields there will be a default value,  
  11. If you enter '.', the field will be left blank.  
  12. -----  
  13. Country Name (2 letter code) [XX]:cn  
  14. State or Province Name (full name) []:bj  
  15. Locality Name (eg, city) [Default City]:bj  
  16. Organization Name (eg, company) [Default Company Ltd]:dc  
  17. Organizational Unit Name (eg, section) []:dc  
  18. Common Name (eg, your name or your server's hostname) []:client common  
  19. Email Address []:  
  20.   
  21. Please enter the following 'extra' attributes  
  22. to be sent with your certificate request  
  23. A challenge password []:  
  24. An optional company name []:  

[html] view plain copy
 print?
  1. 10)生成客户端证书(root证书,rootkey,客户端key,客户端请求文件这4个生成客户端证书)  
  2. openssl x509 -req -in client-req.csr -out client-cert.cer -signkey client-key.key -CA root-cert.cer -CAkey root-key.key -CAcreateserial -days 3650  

[html] view plain copy
 print?
  1. 11)生成客户端p12格式根证书(密码设置123456)  
  2. openssl pkcs12 -export -clcerts -in client-cert.cer -inkey client-key.key -out client.p12  

4.nginx配置(服务器端)

第3步证书生成完毕,就可以使用这些证书了,首先在服务端进行配置,nginx的安装就不多说了,参考http://blog.csdn.net/qq315737546/article/details/51834866

注意下 ./configure 的时候,要增加ssl支持,需要换成 ./configure--with-http_ssl_module

下面贴出nginx的简易版配置文件

[html] view plain copy
 print?
  1. worker_processes  1;  
  2. events {  
  3.     worker_connections  1024;  
  4. }  
  5.   
  6. http {  
  7.     include       mime.types;  
  8.     default_type  application/octet-stream;  
  9.     sendfile        on;  
  10.     keepalive_timeout  65;  
  11.   
  12.     server {  
  13.         listen       443 ssl;  
  14.         server_name  ttt.com;  
  15.         ssl                  on;    
  16.         ssl_certificate      /data/sslKey/server-cert.cer;  #server证书公钥  
  17.         ssl_certificate_key  /data/sslKey/server-key.key;  #server私钥  
  18.         ssl_client_certificate /data/sslKey/root-cert.cer;  #根级证书公钥,用于验证各个二级client  
  19.         ssl_verify_client on;  #开启客户端证书验证    
  20.   
  21.         location / {  
  22.             root   html;  
  23.             index  index.html index.htm;  
  24.         }  
  25.     }  
  26. }  

启动nginx,然后在我们浏览器访问测试下.


5.浏览器访问(客户端)

(先在C:\Windows\System32\drivers\etc\hosts 里面做好 域名和ip的映射192.168.234.132  www.ttt.com)



点击继续浏览此网站,则如下图



出现400错误,是因为我们没有带客户端证书的原因, 将client.p12 导入浏览器,则如下图

可以访问,但是证书会有红色x号.因为是我们自己签的.浏览器不信任



这时候我们将我们的root.p12也导入,证书存储不用默认的个人,选择 受信任的根证书颁发机构, 如果再访问如下图(可能要重启浏览器)



6.java代码访问(客户端)

根据我们在第1点里面的了解,双向认证是需要互相认证证书的. 所以客户端需要认证服务器证书,也要把客户端证书发送给服务器. 用浏览器做客户端的时候,认证服务器证书自动进行,提交客户端证书也是自动进行(需要导入证书到浏览器)

当我们用java代码来做的时候, 也是需要这些步骤.

1)首先是认证服务器证书, jdk有默认的信任证书列表$JRE/lib/security/cacerts

也会默认信任$JRE/lib/security/jssecacerts 里的证书.

如果你把证书放到别的地方,则需要在代码中指定

(理论上如果是买的根机构签发的证书,是不需要导入到java自己的库里,但是java的和操作系统的信任库可能不一样,我们买的在浏览器就OK,在java中就必须手动导入服务端证书到信任列表中)


用keytool导入的时候注意下 keystore的路径. cacerts的默认密码是changeit

[html] view plain copy
 print?
  1. D:\>cd jdk1.7.0_80\jre7\lib\security  
  2.   
  3. D:\jdk1.7.0_80\jre7\lib\security>keytool -import -alias ttt -keystore cacerts -file e:/HttpsDemo/server-cert.cer  
  4. 输入密钥库口令:  
  5. 所有者: CN=*.ttt.com, OU=dcO=dcL=bjST=bjC=cn  
  6. 发布者: CN=rootOU=dcO=dcL=bjST=bjC=cn  
  7. 序列号: a034f5e5d4b1c825  
  8. 有效期开始日期: Thu Oct 20 00:01:52 CST 2016, 截止日期: Sun Oct 18 00:01:52 CST 2026  
  9. 证书指纹:  
  10.          MD5: 65:CB:C9:0D:C4:E7:66:F9:09:3D:B4:17:E6:6B:E5:AB  
  11.          SHA1: 41:AD:9E:EB:61:88:AE:1B:A3:76:CE:F8:2C:BB:5D:74:C8:0D:2D:0D  
  12.          SHA256: 0D:17:D4:EF:2E:9D:89:EA:3A:1F:32:44:D5:12:DF:E0:EE:58:61:04:1A:28:BC:91:D4:7C:3F:AF:FE:99:79:16  
  13.          签名算法名称: SHA1withRSA  
  14.          版本: 1  
  15. 是否信任此证书? [否]:  y  
  16. 证书已添加到密钥库中  
  17.   
  18. D:\jdk1.7.0_80\jre7\lib\security>  


2) java代码(包含加载客户端证书)

[java] view plain copy
 print?
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.InputStream;  
  4. import java.security.KeyStore;  
  5.   
  6. import javax.net.ssl.SSLContext;  
  7.   
  8. import org.apache.http.HttpEntity;  
  9. import org.apache.http.client.methods.CloseableHttpResponse;  
  10. import org.apache.http.client.methods.HttpGet;  
  11. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;  
  12. import org.apache.http.impl.client.CloseableHttpClient;  
  13. import org.apache.http.impl.client.HttpClients;  
  14. import org.apache.http.ssl.SSLContexts;  
  15. import org.apache.http.util.EntityUtils;  
  16.   
  17. public class HttpsDemo {  
  18.     private final static String PFX_PATH = "e:/HttpsDemo/client.p12";   //客户端证书路径  
  19.     private final static String PFX_PWD = "123456"//客户端证书密码  
  20.       
  21.       
  22.     public static String sslRequestGet(String url) throws Exception {  
  23.         KeyStore keyStore = KeyStore.getInstance("PKCS12");  
  24.         InputStream instream = new FileInputStream(new File(PFX_PATH));  
  25.         try {  
  26.             keyStore.load(instream, PFX_PWD.toCharArray());  
  27.         } finally {  
  28.             instream.close();  
  29.         }  
  30.   
  31.         SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, PFX_PWD.toCharArray()).build();  
  32.         SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext  
  33.                 , new String[] { "TLSv1" }  // supportedProtocols ,这里可以按需要设置  
  34.                 , null  // supportedCipherSuites  
  35.                 , SSLConnectionSocketFactory.getDefaultHostnameVerifier());   
  36.           
  37.         CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();  
  38.         try {  
  39.             HttpGet httpget = new HttpGet(url);   
  40. //          httpost.addHeader("Connection", "keep-alive");// 设置一些heander等  
  41.             CloseableHttpResponse response = httpclient.execute(httpget);  
  42.             try {  
  43.                 HttpEntity entity = response.getEntity();  
  44.                 String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");//返回结果  
  45.                 EntityUtils.consume(entity);  
  46.                 return jsonStr;  
  47.             } finally {  
  48.                 response.close();  
  49.             }  
  50.         } finally {  
  51.             httpclient.close();  
  52.         }  
  53.     }  
  54.   
  55.     public static void main(String[] args) throws Exception {  
  56.         System.out.println(System.getProperty("java.home"));  
  57.         System.out.println(sslRequestGet("https://www.ttt.com/"));  
  58.     }  
  59. }  

我这里使用了 httpClient的包,具体如下


3) 编译运行2)里面的java文件

[html] view plain copy
 print?
  1. E:\HttpsDemo>javac -encoding utf-8 -cp commons-logging-1.1.1.jar;httpclient-4.5.jar;httpcore-4.4.1.jar; HttpsDemo.java  
  2.   
  3. E:\HttpsDemo>java -cp commons-logging-1.1.1.jar;httpclient-4.5.jar;httpcore-4.4.1.jar; HttpsDemo  
  4. D:\jdk1.7.0_80\jre7  
  5. <!DOCTYPE html>  
  6. <html>  
  7. <head>  
  8. <title>Welcome to nginx!</title>  
  9. <style>  
  10.     body {  
  11.         width: 35em;  
  12.         margin: 0 auto;  
  13.         font-family: Tahoma, Verdana, Arial, sans-serif;  
  14.     }  
  15. </style>  
  16. </head>  
  17. <body>  
  18. <h1>Welcome to nginx!</h1>  
  19. <p>If you see this page, the nginx web server is successfully installed and  
  20. working. Further configuration is required.</p>  
  21.   
  22. <p>For online documentation and support please refer to  
  23. <a href="http://nginx.org/">nginx.org</a>.<br/>  
  24. Commercial support is available at  
  25. <a href="http://nginx.com/">nginx.com</a>.</p>  
  26.   
  27. <p><em>Thank you for using nginx.</em></p>  
  28. </body>  
  29. </html>  
  30.   
  31.   
  32. E:\HttpsDemo>  

可以看到能正常访问了. 大功告成.(代码中增加了jre的输出,因为eclipse和cmd可能用的不是同一个,证书只导入了其中一个jre里,另一个运行不正常)


7.使用购买的证书(信任机构签发的)

如果是公司使用的话,证书一般是从信任机构那里买的.所以就不需要上面测试的root证书.

信任机构提供 服务端证书和私钥,客户端证书 就可以了.

如果报错

[html] view plain copy
 print?
  1. Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  

则检查下 客户端证书是否加载正确,服务端证书是否导入本地信任库.运行的jre和证书导入的那个jre是否一致.
posted @ 2017-09-08 11:11  海尚书  阅读(222)  评论(0编辑  收藏  举报