tomcat 配置 https

1. 单向认证和双向认证的区别

一般web应用都是采用单向认证的,原因很简单,用户数目广泛,且无需做在通讯层做用户身份验证,一般都在应用逻辑层来保证用户的合法登入。
但如果是企业应用对接,情况就不一样,可能会要求对client(相对而言)做身份验证。这时需要做双向认证。

2. SSL安全证书包括:

  1.   CA证书,也叫根证书或中间级证书。单向认证的https,CA证书是可选的。主要目的是使证书构成一个证书链,以达到浏览器信任证书的目的。如果使用了CA证书,服务器证书和客户证书都使用CA证书来签名。如果不安装CA证书,浏览器默认认为是不安全的。
    
  1.   服务器证书。必选。通过服务器私钥,生成证书请求文件CSR,再通过CA证书签名生成服务器证书。
    
  2.   客户证书。可选。如果有客户证书,就是双向认证的HTTPS,否则就是单向认证的HTTPS。生成步骤和服务器证书类似。
    

上面几种证书都可以自己生成。商业上,一般自己提供服务器或客户证书端的私钥和证书请求CSR,向第三方机构付费申请得到通过CA证书签名的服务器证书和客户证书。

为什么需要CA认证,因为如果不进行CA认证,第三方攻击可以伪装,无法确认服务器的身份,百度、阿里、google走了https,进行了CA认证,就相当于公民去公安局办理了身份证,进行了登记,如果验证身份证的合法性时,可以看签发机关,以及根据签发号向公安机关核对真实性,这是一个道理,就是走了https的向CA机构进行登记。

3. 自定义证书

CA证书是大家都公认的机构颁发的,像:百度、阿里、Google使用的https就是通过CA认证的,如果不进行CA认证,也可以自定义证书。 jdk中提供了生成自定义证书的工具:如下,就是生成自定义证书的命令:关键字解释:

keytool -genkey -keyalg RSA -dname "cn=118.144.155.21,ou=inspur,o=none,l=beijing,st=chaoyang,c=cn" -alias server -keypass 111111 -keystore server.keystore -storepass 111111 -validity 3650

-genkeypair:生成一对非对称密钥。-alias:指定密钥对的别名,该别名是公开的。-keyalg:指定加密算法,本例中的采用通用的RAS加密算法
keypass 为密码
-keystore 代表证书存储位置和名称,以上命令是当前目录存储
OU 表示组织单位名称
O 表示组织名称
L 您所在城市或区域
ST 表示省份或州
c 表示国家代码

4. 什么是CA认证,为什么要CA认证?

自定义的证书没给第三方认证,肯定弹warn, 于是出现如下图:

tomcat 配置 https_第1张图片
https第一次链接.jpg

如果继续前往:

tomcat 配置 https_第2张图片
6.pic.jpg

如果是通过CA认证的,显示如下:

tomcat 配置 https_第3张图片
7.pic.jpg

5. 自定义证书如何显示如上呢?

需要通过JDK命令生成自定义证书,导入到浏览器中,浏览器认为是一个受信任的证书,所以需要安装证书: 即可:

keytool -export -alias server -keystore server.keystore -file ca.cer -storepass 111111

这个ca.cer是为了解决不信任时要导入的
如果导入了,就会产生如上绿色安全锁标志

6. SSL 认证通信的步骤:

① 客户端的浏览器向服务器传送客户端 SSL 协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息。

② 服务器向客户端传送 SSL 协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己的证书。

③ 客户利用服务器传过来的信息验证服务器的合法性,服务器的合法性包括:证书是否过期,发行服务器证书的 CA 是否可靠,发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配。如果合法性验证没有通过,通讯将断开;如果合法性验证通过,将继续进行第四步。

④ 用户端随机产生一个用于后面通讯的“对称密码”,然后用服务器的公钥(服务器的公钥从步骤②中的服务器的证书中获得)对其加密,然后将加密后的“预主密码”传给服务器。

⑤ 如果服务器要求客户的身份认证(在握手过程中为可选),用户可以建立一个随机数然后对其进行数据签名,将这个含有签名的随机数和客户自己的证书以及加密过的“预主密码”一起传给服务器。

⑥ 如果服务器要求客户的身份认证,服务器必须检验客户证书和签名随机数的合法性,具体的合法性验证过程包括:客户的证书使用日期是否有效,为客户提供证书的 CA 是否可靠,发行 CA 的公钥能否正确解开客户证书的发行 CA 的数字签名,检查客户的证书是否在证书废止列表(CRL)中。检验如果没有通过,通讯立刻中断;如果验证通过,服务器将用自己的私钥解开加密的“预主密码”,然后执行一系列步骤来产生主通讯密码(客户端也将通过同样的方法产生相同的主通讯密码)。

⑦ 服务器和客户端用相同的主密码即“通话密码”,一个对称密钥用于 SSL 协议的安全数据通讯的加解密通讯。同时在 SSL 通讯过程中还要完成数据通讯的完整性,防止数据通讯中的任何变化。 
 
⑧ 客户端向服务器端发出信息,指明后面的数据通讯将使用的步骤⑦中的主密码为对称密钥,同时通知服务器客户端的握手过程结束。

⑨ 服务器向客户端发出信息,指明后面的数据通讯将使用的步骤⑦中的主密码为对称密钥,同时通知客户端服务器端的握手过程结束。

⑩ SSL 的握手部分结束,SSL 安全通道的数据通讯开始,客户和服务器开始使用相同的对称密钥进行数据通讯,同时进行通讯完整性的检验。

上面所述的是双向认证 SSL 协议的具体通讯过程,这种情况要求服务器和用户双方都有证书。单向认证 SSL 协议不需要客户拥有 CA 证书,具体的过程相对于上面的步骤,只需将服务器端验证客户证书的过程去掉,以及在协商对称密码方案,对称通话密钥时,服务器发送给客户的是没有加过密的(这并不影响 SSL 过程的安全性)密码方案。 这样,双方具体的通讯内容,就是加过密的数据,如果有第三方攻击,获得的只是加密的数据,第三方要获得有用的信息,就需要对加密的数据进行解密,这时候的安全就依赖于密码方案的安全。而幸运的是,目前所用的密码方案,只要通讯密钥长度足够的长,就足够的安全。这也是我们强调要求使用 128 位加密通讯的原因。

以上已经生成了认证,而且说明了认证的理论,下面谈谈实际应用操作:
7. tomcat中配置 ssl

打开 server.xml 文件,修改配置为:


    

其中: keystoreFile代表了生成认证文件的存放地址:

9.pic.jpg

8. 服务器端进行访问认证:


public class HttpHandle {    // server ip request  base url    private static final String BASE_URL = "https://118.144.155.21:8443/api/v1/accounts/";    private static final String USERS = "/users/";    private static final String SLASH = "/";    private static final String SIGN = "?sign=";    private static final String VALIDATE = "/validate";    /**     * assemble //http://127.0.0.1:8080/api/v1/accounts/10/users/3/validate?sign=3E50C44AAC99B222072A08EF2AD938F6     * @param sign  use secret and MD5 generate     * @param accountId account id     * @param userId   user id     * @return response infomation     */    public static String validateSecret(String sign, String accountId, String userId) {        String targetUrl = BASE_URL + accountId + USERS + userId + VALIDATE;        //System.out.println("target url:"+targetUrl);        URL url = null;        HttpsURLConnection connection = null;        String  params = "sign=" +sign;        try {            // Create connection            url = new URL(targetUrl);            connection = (HttpsURLConnection) url.openConnection();            connection.setRequestMethod("POST");            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");            connection.setRequestProperty("Content-Length", "" + Integer.toString(params.getBytes().length));            connection.setRequestProperty("Content-Language", "en-US");            connection.setConnectTimeout(1*1000);            connection.setUseCaches(false);            connection.setDoInput(true);            connection.setDoOutput(true);            // Send request//            OutputStream os = connection.getOutputStream();//            BufferedWriter writer = new BufferedWriter(//                    new OutputStreamWriter(os, "UTF-8"));//            writer.write(URLEncoder.encode(params, "UTF-8")//            );////            writer.flush();//            writer.close();//            os.close();            DataOutputStream wr = new DataOutputStream(                    connection.getOutputStream());            wr.writeBytes(params);            wr.flush();            wr.close();            // Get Response            int responseCode=connection.getResponseCode();            // the normal response http return 200            if (responseCode == HttpsURLConnection.HTTP_OK){                InputStream is = connection.getInputStream();                BufferedReader rd = new BufferedReader(new InputStreamReader(is));                String line;                StringBuffer response = new StringBuffer();                while ((line = rd.readLine()) != null) {                    response.append(line);                    response.append('\r');                }                rd.close();                String responseStr = response.toString();                return responseStr;            }else{                return new String("false : "+responseCode);            }        } catch (Exception e) {            e.printStackTrace();            return new String("Exception: " + e.getMessage());        } finally {            if (connection != null) {                connection.disconnect();            }        }    }    /**     * this is a single test methon     * @param args no args     * @throws Exception  connection http will occur exception     */    public static void main(String[] args) throws Exception {        String key ="583590FF9FCED6A333099981CA6C5BE5D716E85A5918F9AD927B404C4C8E205D60E47A09CF5C8FCCF86AE6AF4F2A2327A2ECD83FAA77D654071BCBFC8B6A1648D75F965F5DCD7F2EC2EC615BDCEC18121D0F0A92AE9F9EEE4783E4228E86C06CF6DC74B6B1C0A6788EA49181C4A5EA6106DCF95DCFA7ED4DC8F405D0FDF0C34F";        TreeMap map = new TreeMap<>();        map.put("account_id","10");        map.put("user_id","3");        String sign = SignUtil.createSign(map,key);        System.out.println("sign: " + sign);        System.out.println("vvvvvv"+validateSecret(sign,"10","3"));    }    /**     * not verification ssl validity     */    static {        disableSslVerification();    }    private static void disableSslVerification() {        try        {            // Create a trust manager that does not validate certificate chains            TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {                public java.security.cert.X509Certificate[] getAcceptedIssuers() {                    return null;                }                public void checkClientTrusted(X509Certificate[] certs, String authType) {                }                public void checkServerTrusted(X509Certificate[] certs, String authType) {                }            }            };            // Install the all-trusting trust manager            SSLContext sc = SSLContext.getInstance("SSL");            sc.init(null, trustAllCerts, new java.security.SecureRandom());            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());            // Create all-trusting host name verifier            HostnameVerifier allHostsValid = new HostnameVerifier() {                public boolean verify(String hostname, SSLSession session) {                    return true;                }            };            // Install the all-trusting host verifier            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();        } catch (KeyManagementException e) {            e.printStackTrace();        }    }}

其中有一段代码是不进行验证的,因为当用浏览器访问的时候,会弹出是不安全的链接(因为是自定义证书没有进行认证,所以浏览器会提示是不安全的链接,点击继续可以继续访问,如果消除这个提示,需要在浏览器中导入自定义的证书即可,以上有说明:在代码中如果不需要验证,需要把验证disable掉,代码如下:)

这段代码为:参考文章

tomcat 配置 https_第4张图片
10.pic_hd.jpg

显示结果如下:

tomcat 配置 https_第5张图片
8.pic.jpg

如果去掉以上代码会出现如下异常:

9.pic_hd.jpg

以上就是在程序端或者浏览器端进行https的整个过程。

参考

  1. 重要参考
  2. 破解Gmail的新方式
  3. 12306为什么需要根证书
  4. 通过生成自定义证书,在程序中指定进行Https测试
  5. https单双向认证
  6. 单双向认证
  7. https原理
    7.https原理介绍

你可能感兴趣的:(tomcat 配置 https)