创建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

相关文章
相关标签/搜索