https self-signed certificate bidirectional authentication

Foreword

This article describes that OpenSSL to generate a self-signed certificate, https achieve mutual authentication.

First, in linux using OpenSSL to generate a CA certificate, client certificate, the server certificate

View OpenSSL version number openssl version -a
if there is no need to install the OpenSSL
Download: www.openssl.org/source/openssl-1.0.2p.tar.gz

tar -zxv openssl-1.0.2p.tar.gz
cd openssl-1.0.2p/
./config
make && make install
./config shared 
make clean
make  && make install
1. Create a directory under nginx ssl directory, enter ssl, run the following script, and finally enter the client password
#!/bin/bash
# function: 创建 nginx https ,双向认证证书
#
# BEGIN
#
# 网站域名
# 在签发服务(客户)端证书的时候
# 这个域名必须跟 subj 中的 CN 对应
# 否则浏览器会报不安全的链接
# 查找所有javatest.hqxapp.com替换成域名运行即可
domain="javatest.hqxapp.com"
#
# ---------- CA ----------
#
# 准备CA密钥
echo "创建CA密钥.."
openssl genrsa -out $domain.CA.key 2048
# 生成CA证书请求
# 证书请求都是根据私钥来生成的
echo "生成CA证书请求.."
openssl req -new -key $domain.CA.key -out $domain.CA.csr -days 365 -subj /C=CN/ST=GuangDong/L=GuangZhou/O=javatest.hqxapp.com/OU=javatest.hqxapp.com/CN=opcenter/emailAddress=javatest.hqxapp.com -utf8
 
# 签名CA证书请求
# 使用自己的私钥来给这个CA证书请求签名
# 经过多次测试得知: 这个时间如何设置的太长,如 3650(10年) 
# chrome浏览器会报, 该网站使用的安全设置已过期
# 所以https不会显示是绿色, 而是带一个黄色三角形的图标
# 奇怪的是: 如果设成1年,也就是365天,不会提示该网站使用的安全设置已过期
# 而且也是绿色的标识
# 但如果是2年, 也是绿色的标识,但是会提示该网站使用的安全设置已过期
# 这个应该chrome浏览器的问题
echo "创建CA证书.."
openssl x509 -req -in $domain.CA.csr -signkey $domain.CA.key -out $domain.CA.crt -days 365
 
# CA证书转换为DER格式,
# DER格式似乎更加通用
openssl x509 -in $domain.CA.crt -out $domain.CA.der -outform DER
# 现在, 终于拿到了自己做 CA 需要的几个文件了, 
# 密钥: $domain.CA.key
# 证书: $domain.CA.crt
# 系统使用的: $domain.CA.der
# 接下来, 要创建一个网站, 就需要让 CA 给他签名一个证书了
 
#
 # --------- SERVER ----------
 #
 # 准备网站密钥
 echo "创建网站(服务端)密钥.."
 openssl genrsa -out $domain.server.key 2048
 # 生成网站证书请求
 # CN 一定要是网站的域名, 否则会通不过安全验证
 echo "生成网站(服务端)证书请求.."
 openssl req -new -key $domain.server.key -out $domain.server.csr -days 365 -subj /C=CN/ST=GuangDong/L=GuangZhou/O=javatest.hqxapp.com/OU=javatest.hqxapp.com/CN=$domain/emailAddress=javatest.hqxapp.com -utf8
  
  # CA签名网站证书请求
  # 不是拿到 CA 的证书了就可以说自己是 CA 的, 最重要的是, 签名需要有 CA 密钥
  # 如果客户端(个人浏览器)信任 CA 的证书的话, 那么他也就会信任由 CA 签名的网站证书
  # 因此让浏览器信任 CA 的证书之后, 客户端就自然信任服务端了, 只要做单向认证的话, 到这一步证书这一类材料就已经准备好了
  # 但是双向认证就还要给客户端(个人的浏览器)准备一份证书
  # 让服务端可以知道客户端也是合法的。
  # 假如让服务端也信任 CA 的证书
  # 那 CA 签名的客户端证书也就能被信任了。
  echo "通过CA证书签名, 创建网站(服务端)证书.."
  openssl x509 -req -in $domain.server.csr -out $domain.server.crt -CA $domain.CA.crt -CAkey $domain.CA.key -CAcreateserial -days 365
   
#
# --------- CLIENT ----------
#
# 准备客户端私钥
echo "创建浏览器(客户端)密钥.."
openssl genrsa -out $domain.client.key 2048
# 生成客户端证书请求
echo "生成浏览器(客户端)证书请求.."
openssl req -new -key $domain.client.key -out $domain.client.csr -days 3650 -subj /C=CN/ST=GuangDong/L=GuangZhou/O=javatest.hqxapp.com/OU=javatest.hqxapp.com/CN=$domain/emailAddress=javatest.hqxapp.com -utf8
# CA签名客户端证书请求
echo "通过CA证书签名, 创建浏览器(客户端)证书.."
openssl x509 -req -in $domain.client.csr -out $domain.client.crt -CA $domain.CA.crt -CAkey $domain.CA.key -CAcreateserial -days 365 
# 客户端证书转换为DER格式
openssl x509 -in $domain.client.crt -out $domain.client.der -outform DER
# 客户端证书转换为 PKCS, 即12格式
# 全称应该叫做 Personal Information Exchange
# 通常以 p12 作为后缀
echo "转换客户端证书为p12格式.."
openssl pkcs12 -export -in $domain.client.crt -inkey $domain.client.key -out $domain.client.p12

You need to give permission before running the script:chmod 777 ssl.sh

Generate a certificate:

2. Configure nginx in

vim nginx/conf/nginx.conf

server {
        listen       443;
        server_name  localhost;
        ssi on;
        ssi_silent_errors on;
        ssi_types text/shtml;
 
         ssl on;
         ssl_certificate ../ssl/javatest.hqxapp.com.server.crt;
         ssl_certificate_key  ../ssl/javatest.hqxapp.com.server.key;
         ssl_client_certificate ../ssl/javatest.hqxapp.com.CA.crt;
         ssl_session_timeout  5m;
         ssl_verify_client on;
         ssl_protocols  SSLv2 SSLv3 TLSv1;
         ssl_ciphers  RC4:HIGH:!aNULL:!MD5;
         ssl_prefer_server_ciphers   on;
        
     location / {
            index index.html index.htm;
            proxy_pass http://127.0.0.1:8080;
        }
    }
3.报错nginx: [emerg] unknown directive “ssl” in /usr/local/nginx/conf/nginx.conf

The reason: Because this configuration nginx SSL certificates need to refer to this module in SSL, but a compilation of Nginx start time has not translated into the SSL module together, resulting in the errors.
Solution:
1 ./configure --with-ssl http_ssl_module // re-add this module
2 make command, but do not execute make install, because the compiler is used to make, and make install to install, and then re-cover the entire nginx of.
3 After we do execute the command, we can see in the nginx to extract the directory, objs folder nginx one more document, this is a new version of the program. First, we put before nginx back up a bit, and then copy the new program can be covered before in the past. (Stop nginx program)
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
cp objs/nginx /usr/local/nginx/sbin/nginx
4 and finally came under Nginx installation directory, to see if there ssl module installed successfully. Execution ./sbin/nginx -V to see.
Finally, enter nginx / sbin,. / Nginx -s reload, restart nginx.

Second, use a client certificate to access https (certificate with changes (generate a new certificate), need to restart nginx)

1. Browser

Double-click javatest.hqxapp.com.client.p12, install the certificate, until the next step has been completed, the password to be entered during a password when you run the script generated certificate, the final step input. Then restart the browser.
Do not install the certificate access, it will be reported the following error

2. postman

Close to verify

Add keys

Request Interface

3. java Code
1. Obtain a certificate (server.crt converted to server.bks)

keytool -importcert -v -trustcacerts -alias ttt -file 位置1 -keystore 位置2 -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath 位置3 -storepass 位置4

1 position: absolute path certificate server.crt
position 2: server.bks absolute path of the file to be generated in
the position 3: Conversion jar package absolute path
certificate password to set: the position 4

举例:keytool -importcert -v -trustcacerts -alias ttt -file C:\Users\20180030\Desktop\ssl\javatest.hqxapp.com.server.crt -keystore C:\Users\20180030\Desktop\ssl\javatest.hqxapp.com.server.bks -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath D:\SSL\bcprov-jdk15on-163.jar -storepass 密码

2.server.bks certificate converted to server.jks certificate

server.jks java certificate is the final use to server certificates.
The two certificate file into the root directory of the project to create the key folder. Open portecle.jar, start the tool.

Open server.bks file


enter the password set when converting bks command

convert jks format

Save As, and obtain a certificate jks

call java code

package com.hqx.util;

import com.alibaba.fastjson.JSONObject;
import org.junit.Test;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Map;


public class HttpsClient {
    public static String KEY_STORE_FILE="key/javatest.hqxapp.com.client.p12";
    public static String KEY_STORE_PASS="密码";
    /**
     * server.crt转换bks,再转为jks格式
     */
    public static String TRUST_STORE_FILE="key/javatest.hqxapp.com.server.jks";
    public static String TRUST_STORE_PASS="密码";


    private static SSLContext sslContext;
    /**
     * 向指定URL发送GET方法的请求 
     *
     * @param url
     *            发送请求的URL 
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 
     * @return URL 所代表远程资源的响应结果 
     *
     */
    public static String sendGet(String url, String param) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接  
            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
            // 打开和URL之间的连接  
            if(connection instanceof HttpsURLConnection){
                ((HttpsURLConnection)connection)
                        .setSSLSocketFactory(getSSLContext().getSocketFactory());
            }

            // 设置通用的请求属性  
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接  
            connection.connect();
            // 获取所有响应头字段  
            Map<String, List<String>> map = connection.getHeaderFields();
            // 遍历所有的响应头字段  
            for (String key : map.keySet()) {
                //System.out.println(key + "--->" + map.get(key));  
            }
            // 定义 BufferedReader输入流来读取URL的响应  

            if(connection.getResponseCode()==200){
                in = new BufferedReader(new InputStreamReader(
                        connection.getInputStream(),"utf8"));
            }else{
                in = new BufferedReader(new InputStreamReader(
                        connection.getErrorStream(),"utf8"));
            }
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }

        } catch (Exception e) {
            System.out.println("发送GET请求出现异常!" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流  
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 向指定 URL 发送POST方法的请求  
     *
     * @param url
     *            发送请求的 URL  
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。  
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接  
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
            if(conn instanceof HttpsURLConnection){
                ((HttpsURLConnection)conn)
                        .setSSLSocketFactory(getSSLContext().getSocketFactory());
            }
            // 设置通用的请求属性  
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行  
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流  
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数  
            out.print(param);
            // flush输出流的缓冲  
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应  
            if(conn.getResponseCode()==200){
                in = new BufferedReader(
                        new InputStreamReader(conn.getInputStream(),"utf8"));
            }else{
                in = new BufferedReader(
                        new InputStreamReader(conn.getErrorStream(),"utf8"));
            }
            String line="";
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流  
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }

    public static SSLContext getSSLContext(){
        long time1=System.currentTimeMillis();
        if(sslContext==null){
            try {
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                kmf.init(getkeyStore(),KEY_STORE_PASS.toCharArray());
                KeyManager[] keyManagers = kmf.getKeyManagers();

                TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance("SunX509");
                trustManagerFactory.init(getTrustStore());
                TrustManager[]  trustManagers= trustManagerFactory.getTrustManagers();

                sslContext = SSLContext.getInstance("TLS");
                sslContext.init(keyManagers, trustManagers, new SecureRandom());
                HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (UnrecoverableKeyException e) {
                e.printStackTrace();
            } catch (KeyStoreException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        }
        long time2=System.currentTimeMillis();
        System.out.println("SSLContext 初始化时间:"+(time2-time1));
        return sslContext;
    }


    public static KeyStore getkeyStore(){
        KeyStore keySotre=null;
        try {
            keySotre = KeyStore.getInstance("PKCS12");
            File file = new File(KEY_STORE_FILE);
            System.out.println(file.getAbsolutePath());
            FileInputStream fis = new FileInputStream(new File(KEY_STORE_FILE));
            keySotre.load(fis, KEY_STORE_PASS.toCharArray());
            fis.close();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return keySotre;
    }
    public static KeyStore getTrustStore() throws IOException{
        KeyStore trustKeyStore=null;
        FileInputStream fis=null;
        try {
            trustKeyStore=KeyStore.getInstance("JKS");
            fis = new FileInputStream(new File(TRUST_STORE_FILE));
            trustKeyStore.load(fis, TRUST_STORE_PASS.toCharArray());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            fis.close();
        }
        return trustKeyStore;
    }

    @Test
    public static void main(String[] args) throws UnsupportedEncodingException {
        String result=sendGet("https://javatest.hqxapp.com/demo/", "");
        System.out.println(result);
    }

    @Test
    public void t() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("content", "111");
        String result=sendPost("https://javatest.hqxapp.com/demo/", jsonObject.toJSONString());
        System.out.println(result);
    }
}  

Used above jar package, the script, the code can be downloaded from the resource module.

CSDN: https: //blog.csdn.net/qq_27682773
Jane book: https: //www.jianshu.com/u/e99381e6886e
blog Park: https: //www.cnblogs.com/lixianguo
personal blog: https: // www.lxgblog.com

Guess you like

Origin www.cnblogs.com/lixianguo/p/12522557.html