Nginx https 雙向認證


最近要做個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生成證書

創建一個文件夾存放生成的證書

1)創建根證私鑰

openssl genrsa -out root-key.key 1024

2)創建根證書請求文件

openssl req -new -out root-req.csr -key root-key.key

具體如下

[root@localhost sslKey]# openssl req -new -out root-req.csr -key root-key.key

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:cn

State or Province Name (full name) []:bj

Locality Name (eg, city) [Default City]:bj

Organization Name (eg, company) [Default Company Ltd]:dc

Organizational Unit Name (eg, section) []:dc

Common Name (eg, your name or your server's hostname) []:root

Email Address []:

 

Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:

 

在2)的時候,會讓填寫信息,其中國家,省市,公司等需要和后面的證書保持一致.后面challenge password的地方直接回車就好

3)自簽根證書

openssl x509 -req -in root-req.csr -out root-cert.cer -signkey root-key.key -CAcreateserial -days 3650



4)生成p12格式根證書,密碼填寫123456

openssl pkcs12 -export -clcerts -in root-cert.cer -inkey root-key.key -out root.p12



5)生成服務端key

openssl genrsa -out server-key.key 1024



6)生成服務端請求文件

openssl req -new -out server-req.csr -key server-key.key

 

具體如下

[root@localhost sslKey]# openssl req -new -out server-req.csr -key server-key.key

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:cn

State or Province Name (full name) []:bj

Locality Name (eg, city) [Default City]:bj

Organization Name (eg, company) [Default Company Ltd]:dc

Organizational Unit Name (eg, section) []:dc

Common Name (eg, your name or your server's hostname) []:*.ttt.com

Email Address []:

 

Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:

 

在6)的時候,國家省市公司和2)保持一致, Common Name 要特別注意, 要用你服務器的域名,我們測試用ttt.com

7)生成服務端證書(root證書,rootkey,服務端key,服務端請求文件這4個生成服務端證書)

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


8)生成客戶端key

openssl genrsa -out client-key.key 1024




9)生成客戶端請求文件

openssl req -new -out client-req.csr -key client-key.key

 

具體如下

[root@localhost sslKey]# openssl req -new -out client-req.csr -key client-key.key

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:cn

State or Province Name (full name) []:bj

Locality Name (eg, city) [Default City]:bj

Organization Name (eg, company) [Default Company Ltd]:dc

Organizational Unit Name (eg, section) []:dc

Common Name (eg, your name or your server's hostname) []:client common

Email Address []:

 

Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:



10)生成客戶端證書(root證書,rootkey,客戶端key,客戶端請求文件這4個生成客戶端證書)

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



11)生成客戶端p12格式根證書(密碼設置123456)

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的簡易版配置文件

worker_processes  1;
events {
    worker_connections  1024;
} 
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       443 ssl;
        server_name  ttt.com;
        ssl                  on;  
        ssl_certificate      /data/sslKey/server-cert.cer;  #server證書公鑰
        ssl_certificate_key  /data/sslKey/server-key.key;  #server私鑰
        ssl_client_certificate /data/sslKey/root-cert.cer;  #根級證書公鑰,用於驗證各個二級client
        ssl_verify_client on;  #開啟客戶端證書驗證  

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
} 

啟動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

D:\>cd jdk1.7.0_80\jre7\lib\security
D:\jdk1.7.0_80\jre7\lib\security>keytool -import -alias ttt -keystore cacerts -file e:/HttpsDemo/server-cert.cer
輸入密鑰庫口令:
所有者: CN=*.ttt.com, OU=dc, O=dc, L=bj, ST=bj, C=cn
發布者: CN=root, OU=dc, O=dc, L=bj, ST=bj, C=cn
序列號: a034f5e5d4b1c825
有效期開始日期
: Thu Oct 20 00:01:52 CST 2016, 截止日期: Sun Oct 18 00:01:52 CST 2026 證書指紋: MD5: 65:CB:C9:0D:C4:E7:66:F9:09:3D:B4:17:E6:6B:E5:AB SHA1: 41:AD:9E:EB:61:88:AE:1B:A3:76:CE:F8:2C:BB:5D:74:C8:0D:2D:0D 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 簽名算法名稱: SHA1withRSA 版本: 1 是否信任此證書? [否]: y 證書已添加到密鑰庫中 D:\jdk1.7.0_80\jre7\lib\security>

  

2) java代碼(包含加載客戶端證書)

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
 
public class HttpsDemo {
    private final static String PFX_PATH = "e:/HttpsDemo/client.p12";    //客戶端證書路徑
    private final static String PFX_PWD = "123456";    //客戶端證書密碼
    
   public static String sslRequestGet(String url) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        InputStream instream = new FileInputStream(new File(PFX_PATH));
        try {
            keyStore.load(instream, PFX_PWD.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, PFX_PWD.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext
                , new String[] { "TLSv1" }    // supportedProtocols ,這里可以按需要設置
                , null    // supportedCipherSuites
                , SSLConnectionSocketFactory.getDefaultHostnameVerifier());    
  
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpGet httpget = new HttpGet(url); 
//            httpost.addHeader("Connection", "keep-alive");// 設置一些heander等
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");//返回結果
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
 
    public static void main(String[] args) throws Exception {
        System.out.println(System.getProperty("java.home"));
        System.out.println(sslRequestGet("https://www.ttt.com/"));
    }
}
我這里使用了 httpClient的包,具體如下


 

3) 編譯運行2)里面的java文件

E:\HttpsDemo>javac -encoding utf-8 -cp commons-logging-1.1.1.jar;httpclient-4.5.jar;httpcore-4.4.1.jar; HttpsDemo.java
E:\HttpsDemo>java -cp commons-logging-1.1.1.jar;httpclient-4.5.jar;httpcore-4.4.1.jar; HttpsDemo
D:\jdk1.7.0_80\jre7
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
E:\HttpsDemo>

可以看到能正常訪問了. 大功告成.(代碼中增加了jre的輸出,因為eclipse和cmd可能用的不是同一個,證書只導入了其中一個jre里,另一個運行不正常)

 

7.使用購買的證書(信任機構簽發的)

如果是公司使用的話,證書一般是從信任機構那里買的.所以就不需要上面測試的root證書.

信任機構提供 服務端證書和私鑰,客戶端證書 就可以了.

如果報錯 

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是否一致.

 

本文結束。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM