創建https通訊

基於SSL的https如何進行雙方驗證?咱們能夠參考 一樣基於SSL的SSH的驗證過程 html

參考百度百科 SSH java

依靠密匙,也就是你必須爲本身建立一對密匙-公鑰,並把公匙放在須要訪問的服務器上。如你要鏈接到SSH服務器上,客戶端軟件就會向服務器發出請求,請求用你的密匙進行安全驗證。服務器收到請求以後,先在該服務器上你的主目錄下尋找你的公用密匙,而後把它和你發送過來的公用密匙進行比較。若是兩個密匙一致,服務器就用公用密匙加密「質詢」(challenge)並把它發送給客戶端軟件。客戶端軟件收到「質詢」以後就能夠用你的私人密匙解密再把它發送給服務器。 web

非對稱加密算法中 公鑰用來加密 私鑰用來解密 算法


首先要 確立幾個概念 apache

keyStore 證書密鑰 瀏覽器

keyStore(java鍵庫) 是 由java的keytool命令生成的,鍵庫是一個用來集中存放 私鑰公鑰的地方 tomcat

在keystore裏,包含兩種數據: 安全

        (1)密鑰實體(Key entity):密鑰(secret key)又或者是私鑰和配對公鑰(採用非對稱加密)
        (2)可信任的證書實體(trusted certificate entries):只包含公鑰,別人的公鑰 服務器

     Java KeyStore的類型常見的包括了 JKS, JCEKS, PKCS12, BKS,UBER 測試

     JKS是默認的類型

咱們開始動手利用keytool生成相關私鑰和證書了

一、生成服務器證書庫(CN=127.0.0.1  這個 域 須要是server被訪問的地址)

keytool -validity 365 -genkey -v -alias server -keyalg RSA -keystore D:\ssl\server.keystore -dname "CN=127.0.0.1" -storepass 123456 -keypass 123456

二、生成客戶端證書庫

keytool -validity 365 -genkeypair -v -alias client -keyalg RSA -storetype PKCS12 -keystore D:\ssl\client.p12 -dname "CN=client" -storepass 123456 -keypass 123456

三、從客戶端證書庫中導出客戶端證書

keytool -export -v -alias client -keystore D:\ssl\client.p12 -storetype PKCS12 -storepass 123456 -rfc -file D:\ssl\client.cer

4、從服務器證書庫中導出服務器證書

keytool -export -v -alias server -keystore D:\ssl\server.keystore -storepass 123456 -rfc -file D:\ssl\server.cer

5、生成客戶端信任證書庫(由服務端證書生成的證書庫)

keytool -import -v -alias server -file D:\ssl\server.cer -keystore D:\ssl\client.truststore -storepass 123456

6、將客戶端證書導入到服務器證書庫(使得服務器信任客戶端證書)

keytool -import -v -alias client -file D:\ssl\client.cer -keystore D:\ssl\server.keystore -storepass 123456

7、查看證書庫中的所有證書

keytool -list -keystore D:\ssl\server.keystore -storepass 123456


如今咱們來看看咱們生成了哪些文件:

client.p12 (客戶端的keyStore,它的storetype 是 PKCS12)  client.truststore(客戶端keyStore 存放了server端的公鑰 server.cer) client.cer (客戶端公鑰)  

server.keystore(服務端keyStore 存放了client.cer 和本身的私鑰)  server.cer (server 端公鑰)

涉及keytool有幾個關鍵命令參數解釋一下:-genkeypair 生成密鑰對 你能夠理解爲 雖然公鑰私鑰都生成了 可是 list查看 keyStore顯示的是私鑰 你要使用公鑰須要命令 -export 產生 .cer文件,而導出的公鑰須要 -import 命令導入到別人的keyStore裏

簡單的記憶就是keyStore裏存放的是本身的私鑰 和 導入的別人的公鑰 (受信任的別人),從keyStore中也能夠生成本身的公鑰

-storepass 指定了 keyStore的密碼,-keypass 指定了生成的那一條私鑰的密碼


接下來修改 服務器端,實現https與servlet代碼無關,與java web容器有關,以tomcat爲例

修改 {tomcat}/conf/server.xml,找到Connector port="8443"的標籤,取消註釋,並修改

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"          SSLEnabled="true"      maxThreads="150" scheme="https" secure="true"
 clientAuth="true" sslProtocol="TLS" keystoreFile="D:\ssl\server.keystore" keystorePass="123456"    truststoreFile="D:\ssl\server.keystore" truststorePass="123456"/>

幾個修改點注意 protocol修改成org.apache.coyote.http11.Http11NioProtocol 避免了配置APR相關

clientAuth="true" 使得 server端 對 client端來的請求進行驗證

sslProtocol="TLS" 指明瞭採用TLS做爲SSL實現的具體協議(參考TLS和SSL的關係

keystoreFile 和 truststoreFile 指明keyStore

而後編寫servlet代碼例子測試下,其中還額外指明瞭如何獲取證書信息(clientAuth="true" server已經對client進行驗證了,只有被server信任的client才能握手成功


package com.icesoft.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.cert.X509Certificate;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>
 * SSL Servlet
 * </p>
 * 
 * @author IceWee
 * @date 2012-6-4
 * @version 1.0
 */
public class SSLServlet extends HttpServlet {

    private static final long serialVersionUID = 1601507150278487538L;
    private static final String ATTR_CER = "javax.servlet.request.X509Certificate";
    private static final String CONTENT_TYPE = "text/plain;charset=UTF-8";
    private static final String DEFAULT_ENCODING = "UTF-8";
    private static final String SCHEME_HTTPS = "https";

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType(CONTENT_TYPE);
        response.setCharacterEncoding(DEFAULT_ENCODING);
        PrintWriter out = response.getWriter();
        X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTR_CER);
        if (certs != null) {
            int count = certs.length;
            out.println("共檢測到[" + count + "]個客戶端證書");
            for (int i = 0; i < count; i++) {
                out.println("客戶端證書 [" + (++i) + "]: ");
                out.println("校驗結果:" + verifyCertificate(certs[--i]));
                out.println("證書詳細:\r" + certs[i].toString());
            }
        } else {
            if (SCHEME_HTTPS.equalsIgnoreCase(request.getScheme())) {
                out.println("這是一個HTTPS請求,可是沒有可用的客戶端證書");
            } else {
                out.println("這不是一個HTTPS請求,所以沒法得到客戶端證書列表 ");
            }
        }
        out.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
    
    /**
     * <p>
     * 校驗證書是否過時
     * </p>
     * 
     * @param certificate
     * @return
     */
    private boolean verifyCertificate(X509Certificate certificate) {
        boolean valid = true;
        try {
            certificate.checkValidity();
        } catch (Exception e) {
            e.printStackTrace();
            valid = false;
        }
        return valid;
    }

}


若是咱們給瀏覽器安裝了client.p12,就能經過瀏覽器訪問server了 https://127.0.0.1:8443/ssl

咱們若是須要本身編寫代碼 以https的方式訪問server應該如何實現呢?這裏以apache的http client做爲示例


KeyStore myKey=KeyStore.getInstance("pkcs12");
            myKey.load(new FileInputStream("d:/ssl/client.p12"),"123456".toCharArray());
            KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            myTrustStore.load(new FileInputStream("d:/ssl/client.truststore"),"123456".toCharArray());
            SSLContext sslContext = SSLContexts.custom()
                    .useTLS()
                    .loadKeyMaterial(myKey,"123456".toCharArray())
                    .loadTrustMaterial(myTrustStore)
                    .build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
            CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();

            HttpGet get = new HttpGet();
            get.setURI(new URI("https://127.0.0.1:8443/ssl"));
            CloseableHttpResponse response = client.execute(get);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            response.getEntity().writeTo(out);
            System.out.println(new String(out.toByteArray(),"utf8"));
            out.close();
            response.close();



KeyStore.getInstance("pkcs12") 指明瞭 keyStore的類型,而後用它來加載這個類型的 client.p12纔不會出錯。而client.truststore是默認的JKS類型


  useTLS 指明瞭所使用的協議類型    、

  loadKeyMaterial(myKey,"123456".toCharArray())   加載client的私鑰,注意這裏的123456不是keyStore的密碼 而是私鑰的密碼 由-keypass 123456指定的 ,也就是keyStore有一個密碼 而這個store內每一條密鑰還能夠指定單獨密碼 、

  loadTrustMaterial 加載受信任的證書  包含server的公鑰


整個過程打通,經過https 來實現接口 1,能夠利用http協議自己的成熟簡單 開發成本低。2 https的數據傳輸過程是被保護的,數據不會被截取。3 能夠經過控制 server端的 受信任證書庫 keyStore 添加或刪除  公鑰來控制訪問方的訪問權限  


參考

(全程參考)http://www.blogjava.net/icewee/archive/2012/06/04/379947.html

(java keytool命令總結)http://gwh-08.iteye.com/blog/1779667

TLS和SSL的關係)http://www.360doc.com/content/09/1231/02/495229_12346441.shtml

(不一樣格式的keyStore)http://blog.chinaunix.net/uid-15473693-id-87588.html

相關文章
相關標籤/搜索