HTTPS过程以及详细案例

Wesley13
• 阅读 737

1.HTTPS的过程

  HTTPS过程以及详细案例

1.客户端向服务端发送请求,客户端主要向服务器提供以下信息:

  •  支持的协议版本,比如TLS 1.0版。
  • 一个客户端生成的随机数,稍后用于生成"对话密钥"。
  • 支持的加密方法,比如RSA公钥加密。
  •  支持的压缩方法。

2.服务器端收到请求后,向客户端做出回应,回应的内容包括:

  •  确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。
  •  一个服务器生成的随机数,稍后用于生成"对话密钥"。
  • 确认使用的加密方法,比如RSA公钥加密。
  • 服务器证书。

3.客户端收到服务器回应以后,首先验证服务器证书。

如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,如下

HTTPS过程以及详细案例  

   如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息:

  •  一个随机数。该随机数用服务器公钥加密,防止被窃听。
  • 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
  • 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验

 证书的验证过程如下:

  CA机构在签发证书的时候,都会使用自己的私钥对证书进行签名,如果我们使用的是购买的证书,那么很有可能,颁发这个证书的CA机构的公钥已经预置在操作系统中。这样浏览器就可以使用CA机构的公钥对服务器的证书进行验签,验签之后得到的是CA机构使用sha256得到的证书摘要,客户端就会对服务器发送过来的证书使用sha256进行哈希计算得到一份摘要,然后对比之前由CA得出来的摘要,就可以知道这个证书是不是正确的,是否被修改过。

4. 服务端回应

 服务器收到客户端的第三个随机数之后,计算生成本次会话所用的"会话密钥"。然后,向客户端最后发送下面信息:

  • 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
  • 服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。

 会话秘钥是根据前面几次对话过程中产生的三个随机数以及一些其他算法产生的,后面服务器与客户端的交互都是通过这对话秘钥进行加密解密处理的,其他的都和HTTP协议一样。

2.HTTPS中自签名证书的生成

  需要注意的是接下来几种文件的类型:

  • key是服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到数据的解密
  • csr是证书签名请求文件(公钥),用于提交给证书颁发机构(CA)对证书签名
  • crt是由证书颁发机构(CA)签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签署者的签名等信息
  • keystore 包含证书的文件,可以自己去导入证书
  • PEM 文件格式存储证书和密钥,用于导出,导入证书时候的证书的格式,有证书开头,结尾的格式。

 还有就是X.509是一个标准,规范了公开秘钥认证、证书吊销列表、授权凭证、凭证路径验证算法等。

a. 服务器端用户证书的生成过程:

1. 生成私钥(.key)文件

# private key  
$openssl genrsa -des3 -out server.key 1024

  2. 生成证书请求(.csr)文件(公钥)

# generate csr  
$openssl req -new -key server.key -out server.csr

  Country Name (2 letter code) [XX]:CN----------------------------------- 证书持有者所在国家

  State or Province Name (full name) []:BJ------------------------------- 证书持有者所在州或省份(可省略不填)

  Locality Name (eg, city) []:BJ----------------------------------------- 证书持有者所在城市(可省略不填)

  Organization Name (eg, company) []:NH---------------------------------- 证书持有者所属组织或公司

  Organizational Unit Name (eg, section) []:.---------------------------- 证书持有者所属部门(可省略不填)

  Common Name (eg, your name or your server's hostname) []:www.hellobcdb.com----- 必须要填写域名或者ip地址

  Email Address []:------------------------------------------------------ 邮箱(可省略不填)

  challenge password:............................................................自定义密码

  An optional company name:.............................................(可选)公司名称

3.  自签名的证书文件

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

  4.本案例需要用到pem格式的证书,可以用以下方式合并证书文件(crt)和私钥文件(key)来生成

cat server.crt server.key > server.pem

b. 客户端证书文件的生成

这里采用的是自签名的方式,所以客户端需要有一个导入服务端证书的文件,以供客户端去验证服务段的证书过程。

1.生成.keystore文件

keytool -genkey -validity 36000 -alias www.hellobcdb.com -keyalg RSA -keystore client.keystore

  2.导入服务端证书

keytool -import -alias serverkey -file server.crt -keystore client.keystore

3.HTTPS案例

环境及工具:ubuntu16.04,QtCreator,mongoose(cesanta),java环境。

服务端代码:

#include "mongoose.h"

static const char *s_http_port = "8443";
static const char *s_ssl_cert = "/home/gqx/workplace/TestHttps/server.pem";
static const char *s_ssl_key = "/home/gqx/workplace/TestHttps/server.key";
static struct mg_serve_http_opts s_http_server_opts;

static void ev_handler(struct mg_connection *nc, int ev, void *p) {
  if (ev == MG_EV_HTTP_REQUEST) {
    mg_serve_http(nc, (struct http_message *) p, s_http_server_opts);
  }
}

int main(void) {
  struct mg_mgr mgr;
  struct mg_connection *nc;
  struct mg_bind_opts bind_opts;
  const char *err;

  mg_mgr_init(&mgr, NULL);
  memset(&bind_opts, 0, sizeof(bind_opts));
  bind_opts.ssl_cert = s_ssl_cert;
  bind_opts.ssl_key = s_ssl_key;
  bind_opts.error_string = &err;

  printf("Starting SSL= server on port %s, cert from %s, key from %s\n",
         s_http_port, bind_opts.ssl_cert, bind_opts.ssl_key);
  nc = mg_bind_opt(&mgr, s_http_port, ev_handler, bind_opts);
  if (nc == NULL) {
    printf("Failed to create listener: %s\n", err);
    return 1;
  }

  // Set up HTTP server parameters
  mg_set_protocol_http_websocket(nc);
  s_http_server_opts.document_root = ".";  // Serve current directory
  s_http_server_opts.enable_directory_listing = "yes";

  for (;;) {
    mg_mgr_poll(&mgr, 1000);
  }
  mg_mgr_free(&mgr);

  return 0;
}

 QT中C++项目的pro文件内容如下,注意要添加相应的库:

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
LIBS += -lpthread
LIBS += -lssl -lcrypto
LIBS += -L /usr/local/bin -lcryptopp -ldl -lz
SOURCES += main.cpp \
    mongoose.cpp
HEADERS += \
    mongoose.h

 java客户端代码(也可以直接在浏览器端直接访问,不过会出现证书不安全的警告提示) 

  HttpsClient类

package com.gqx.test;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

public class HttpsClient {

    private static final int SET_CONNECTION_TIMEOUT = 5 * 1000;  
    private static final int SET_SOCKET_TIMEOUT = 20 * 1000;  
      
    public static HttpClient getNewHttpClient() {  
        try {  
            FileInputStream fis = new FileInputStream("/home/gqx/文档/oscar/client.keystore");
            String storepass = "starchain";
            
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());  
            trustStore.load(fis, storepass.toCharArray());  
  
            SSLSocketFactory sf = new MySSLSocketFactory(trustStore);  
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);  
  
            HttpParams params = new BasicHttpParams();  
  
            HttpConnectionParams.setConnectionTimeout(params, 10000);  
            HttpConnectionParams.setSoTimeout(params, 10000);  
  
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);  
            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);  
  
            SchemeRegistry registry = new SchemeRegistry();  
            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));  
            registry.register(new Scheme("https", sf, 443));  
  
            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);  
  
            HttpConnectionParams.setConnectionTimeout(params, SET_CONNECTION_TIMEOUT);  
            HttpConnectionParams.setSoTimeout(params, SET_SOCKET_TIMEOUT);  
            DefaultHttpClient client = new DefaultHttpClient(ccm, params);  
  
            return client;  
        } catch (Exception e) {  
            return new DefaultHttpClient();  
        }  
    }
    
    
    
    
    private static class MySSLSocketFactory extends SSLSocketFactory {  
        SSLContext sslContext = SSLContext.getInstance("TLS");  
  
        public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException,  
                KeyManagementException, KeyStoreException, UnrecoverableKeyException {  
            super(truststore);  
  
            TrustManager tm = new X509TrustManager() {  
                public void checkClientTrusted(X509Certificate[] chain, String authType)  
                        throws CertificateException {  
                }  
  
                public void checkServerTrusted(X509Certificate[] chain, String authType)  
                        throws CertificateException {  
                }  
  
                public X509Certificate[] getAcceptedIssuers() {  
                    return null;  
                }  
            };  
  
            sslContext.init(null, new TrustManager[] { tm }, null);  
        }  
  
        @Override  
        public Socket createSocket(Socket socket, String host, int port, boolean autoClose)  
                throws IOException, UnknownHostException {  
            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);  
        }  
  
        @Override  
        public Socket createSocket() throws IOException {  
            return sslContext.getSocketFactory().createSocket();  
        }  
    } 
}

  测试类,post请求

package com.gqx.test;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;

public class TestMain {

    public static void main(String[] args) {
        String urlStr = "https://www.hellobcdb.com:7999/fff"; 
        Map<String,String> params = new HashMap<>();
        params.put("value", "publish.1666,'Database','computer science','Alice'");
        params.put("name", "gqx");
        params.put("password", "111");
        try {
            //DefaultHttpClient client = new DefaultHttpClient();
            HttpClient client = HttpsClient.getNewHttpClient();
            HttpPost httpPost = new HttpPost(urlStr);
        
            HttpEntity entity;

            ArrayList<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
            if(params != null){
                Set<String> keys = params.keySet();
                for(Iterator<String> i = keys.iterator(); i.hasNext();) {
                    String key = (String)i.next();
                    pairs.add(new BasicNameValuePair(key, params.get(key)));
                }
            }
            entity = new UrlEncodedFormEntity(pairs, "utf-8");
            httpPost.setEntity(entity);
            //Log.i(TAG, "post总字节数:"+entity.getContentLength());
            HttpResponse response = client.execute(httpPost);
            try {
                // 获取响应实体
                HttpEntity entitys = response.getEntity();
                System.out.println("--------------------------------------");
                // 打印响应状态
                System.out.println(response.getStatusLine());
                if (entitys != null) {
                    // 打印响应内容长度
                    String result = new BufferedReader(new InputStreamReader(entitys.getContent()))
                            .lines().collect(Collectors.joining(System.lineSeparator()));
                   System.out.println(result);
                }
                System.out.println("------------------------------------");
            } catch (Exception e) {
                e.printStackTrace();
            } 
        }catch(Exception e){
            e.printStackTrace();
        } 
    }
}

  相关jar包下载 

参考资料

数字签名是什么

Https流程和原理

OPENSSL生成SSL自签证书

mongoose的ssl案例

SSL/TLS协议运行机制的概述

数字签名是什么?

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这