HTTPS和HTTPS证书

背景:我发现网上真的没有几个把https讲清楚的,更别说证书,以及证书安装,更是一堆没用的软文,所以在看了很多资料后做下记录

HTTPS协议

超文本传输安全协议(英语:Hypertext Transfer Protocol Secure,缩写:HTTPS,常称为HTTP over TLS,HTTP over SSL或HTTP Secure)是一种网络安全传输协议。具体介绍以前先来介绍一下以前常见的HTTP,HTTP就是我们平时浏览网页时候使用的一种协议。HTTP协议传输的数据都是未加密的,也就是明文,因此使用HTTP协议传输隐私信息非常不安全。HTTP使用80端口通讯,而HTTPS占用443端口通讯。在计算机网络上,HTTPS经由超文本传输协议(HTTP)进行通信,但利用SSL/TLS来加密数据包。HTTPS开发的主要目的,是提供对网络服务器的身份认证,保护交换数据的隐私与完整性。这个协议由网景公司(Netscape)在1994年首次提出,随后扩展到互联网上。

其主要特性(主要是因为):

  • https协议需要到ca申请证书,一般免费证书很少,需要交费。 (这里主要是利用了非对称加密的公钥加密来解决密钥传输问题)
  • http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。 
  • http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。 
  • http的连接很简单,是无状态的 。 
  • HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议, 要比http协议安全。

 HTTPS对应的通信时序图如下:

 

SSL 证书

从前面我们可以了解到HTTPS核心的一个部分是数据传输之前的握手,握手过程中确定了数据加密的密码。在握手过程中,网站会向浏览器发送SSL证书,SSL证书和我们日常用的身份证类似,是一个支持HTTPS网站的身份证明,SSL证书里面包含了网站的域名,证书有效期,证书的颁发机构以及用于加密传输密码的公私钥等信息,由于公钥加密的密码只能被在申请证书时生成的私钥解密,因此浏览器在生成密码之前需要先核对当前访问的域名与证书上绑定的域名是否一致,同时还要对证书的颁发机构进行验证,如果验证失败浏览器会给出证书错误的提示。在这一部分我将对SSL证书的验证过程以及个人用户在访问HTTPS网站时,对SSL证书的使用需要注意哪些安全方面的问题进行描述。服务端和客户端对证书的处理是不一样的,但是都需要证书。

握手过程的具体描述如下:

  • 1)浏览器将自己支持的一套加密规则发送给网站。 
  • 2)网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。  
  • 3)浏览器获得网站证书之后浏览器要做以下工作: 
a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。 
b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。 
c) 使用约定好的HASH算法计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。 
  1.  
4)网站接收浏览器发来的数据之后要做以下的操作: 
a) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。 
b) 使用密码加密一段握手消息,发送给浏览器。 
  2.  
5)浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。 

这里浏览器与网站互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据,为后续真正数据的传输做一次测试。(主要是保证加密真正加密信息的密钥的安全性)

 

这里为很疑惑服务器的私钥要怎么获得? 一般两种,如果是自己去带着公钥去申请证书,首先要自己生成密钥对,然后去请求,所以自己知道自己的私钥。还有一种是公司其它部门负责申请证书。那这个时候会以pfx的格式给你,里面就包含了你的私钥。

证书格式和转换

什么是证书?为什么要使用证书?

对数据进行签名(加密)是我们在网络中最常见的安全操作。签名有双重作用,作用一就是保证数据的完整性,证明数据并非伪造,而且在传输的过程中没有被篡改,作用二就是防止数据的发布者否认其发布了该数据。

签名同时使用了非对称性加密算法和消息摘要算法,对一块数据签名时,会先对这块数据进行消息摘要运算生成一个摘要,然后对该摘要使用发布者的私钥进行加密。 比如微信公众平台开发中最常见的调用api接口方法是将参数进行字典序排序,然后将参数名和参数值接成一个字符串,组合的字符串也就相当于我们这里说的摘要,然后将摘要用平台密钥加密。

接收者(客户端)接受到数据后,先使用发布者的公钥进行解密得到原数据的摘要,再对接收到的数据计算摘要,如果两个摘要相同,则说明数据没有被篡改。同时,因为发布者的私钥是不公开的,只要接收者通过发布者的公钥能成功对数据进行解密,就说明该数据一定来源于该发布者。

那么怎么确定某公钥一定是属于某发布者的呢?这就需要证书了。证书由权威认证机构颁发,其内容包含证书所有者的标识和它的公钥,并由权威认证机构使用它的私钥进行签名。信息的发布者通过在网络上发布证书来公开它的公钥,该证书由权威认证机构进行签名,认证机构也是通过发布它的证书来公开该机构的公钥,认证机构的证书由更权威的认证机构进行签名,这样就形成了证书链。证书链最顶端的证书称为根证书,根证书就只有自签名了。总之,要对网络上传播的内容进行签名和认证,就一定会用到证书。关于证书遵循的标准,最流行的是 X.509

主流的Web服务软件,通常都基于两种基础密码库:OpenSSL和Java。(一般在公司中我们不必自己生成证书文件,获取jks或者crt格式的证书。然后通过openssl和keytool工具转换成我们需要的格式即可)

证书的格式主要分成两类,其一为密钥库文件格式、其二为证书文件格式;(分类主要是依据加密方式和包含内容来区分的)

密钥库文件格式【Keystore】:java证书内容存在Keystore中,Keystore中可以是jks或者pk12

证书文件格式【Certificate】:

各种平台,各种语言,它们采用的证书格式与标准都不相同,多多少少存在一些差异。实际上证书仍然是那个证书,只是格式发生了变化。

  1. 公私钥 分开存储
  2. 公私钥合并为一个文件

  3. 有些采用二进制文件

  4. 有些事二进制文件做了BASE64编码

  5. 有些证书做了签名

  6. 有些证书加入了密码

  7. 不同组织有不同的编码。例如微软喜欢使用 x509

一般服务端证书会以.pfx的形式下发,如下

pfx是一种文件存储格式, pkcs12是它的实现, 通常里面同时包含私钥, 公钥, 证书, 方便存储和传播。

证书certificate中包括含了数字签名和公钥, 客户端可以通过CA来验证数字签名.
公钥/私钥用于数据加密, 公钥可以发布给任何人, 私钥必须保密,所以pfx不要随便传播
证书文件通常以.cer, .crt结尾  ---BEGIN CERTIFICATE-
公钥文件通常以.pem结尾    ---BEGIN CERTIFICATE-
私钥文件通常以.key结尾    BEGIN PRIVATE KEY    

注意:通常是大家一般这么干,但是不代表这些文件里面不能装私钥或者证书。
加密系统加载这些文件时, 并不是以后缀名的区分. 而是根据里面的内容一般公司申请证书如果不是自己生成,都是由的部门生成好后给你的一般是pfx格式:

我们来看一个例子:

一、生成key

openssl genrsa -out openssl.key 1024

openssl.key  是生产key的名称可随意 。 1024是生成密钥的长度。

二、生成cer证书

openssl req -new -x509 -key openssl.key -out openssl.cer -days 3650 -subj /CN=baidu.com

penssl.key为之前生成的key的名字,openssl.cer为生成的证书名字,3650为证书过期天数,CN的参数baidu.com是的你主机名或者IP地址。

三、生成pfx证书

  注意:生成pfx证书会提示设置密码,此密码要记住,提取密钥时要输入此密码。

openssl pkcs12 -export -out openssl.pfx -inkey openssl.key -in openssl.cer
一般我们在别的机构申请的证书pfx,就已经完成了前三步。我们直接拿到的是带有密钥对和ca机构用私钥签名的pfx格式的证书

四、从pfx里提取出私钥

openssl pkcs12 -in openssl.pfx -nocerts -nodes -out pri.key 

 五:从pfx中提取出公钥或者证书(如果是客户端证书公钥等价于客户端证书,因为证书就是由ca签名的公钥)

openssl pkcs12  -in openssl.pfx -clcerts -nokeys -nodes -out pub.key。 

or

openssl pkcs12  -in openssl.pfx -clcerts -nokeys -nodes -out cl.cert

pkcs12——用来处理pkcs#12格式的证书

-export——执行的是导出操作

-clcerts——导出的是客户端证书,-cacerts则表示导出的是ca证书. 

-inkey——证书的私钥路径

-onkeys——不包含私钥

-in——要导出的证书的路径

-out——输出的密钥库文件的路径

六:转换为java需要使用的格式:

如果你是作为服务端(同时需要自己的公钥和私钥):

keytool -importkeystore -srckeystore openssl.pfx -srcstoretype pkcs12 -destkeystore keystore.jks -deststoretype JKS

如果想把证书给客户端而不想暴露自己的私钥:

1、第五步之后

keytool -importkeystore -srckeystore cl.cert -srcstoretype pkcs12 -destkeystore truststore.jks -deststoretype JKS(使用ca证书和客户端证书可以保证证书的拆封)

关于证书和证书链的拆封原理:理解证书和证书链_求变,从思想开始-CSDN博客_证书链。简单来说我用root的那个ca机构的公钥通过证书链可以解开我任意子证书的签名。

证书的更多知识:

openssl介绍:openssl_百度百科
keytool介绍:keytool_百度百科
常用密钥格式转换:灵活多变的keytool和openssl生成证书,应用tomcat和nginx_陈袁的博客-CSDN博客_keytool nginx
证书中常用名词解释:OpenSSL中证书格式的区别以及格式的转换_Ztw的博客-CSDN博客_openssl证书类型
证书生成和查看:用openssl 和 keytool 生成 SSL证书 - 简书

使用openssl生成证书:使用OpenSSL生成证书_yine的专栏-CSDN博客

openssl证书转换: 使用openssl生成证书、cer文件、key公私钥、pem公私钥_Solyutian的博客-CSDN博客_生成cer文件

java使用证书和安装证书

Java 使用了一种叫 keystore(后缀一般 是.jks或者.keystore或.truststore等,千奇百怪。不管什么后缀,它就是一个容器,各个公司或机构叫法不同而已。比如把只包含”受 信任的公钥”的容器成.truststore文件等 的文件来存储证书 (默认是位于 $JAVA_HOME/lib/security/cacerts ) 。

该文件使用 keytool 工具去管理 (该工具默认位于 $JAVA_HOME/bin/keytool )。

首先需要理解什么是keystore:

keystore可以看成一个放key的库,key就是公钥,私钥,数字签名等组成的一个信息。

truststore是放信任的证书的一个store 
truststore和keystore的性质是一样的,都是存放key的一个仓库,区别在于,truststore里存放的是只包含公钥的数字证书,代表了可以信任的证书,而keystore是包含私钥的。 

keytool 工具的使用不在这里展开,网上有比较详细的说明。这里主要列举几个会用到的命令。

1). 列出 keystore 中的证书。

keytool -list

默认情况下,它会在你的 $HOME 目录下产生一个空的 .keystore 文件。如要指定 Java 正在用的 keystore 文件,使用以下参数

keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts
keytool -list -keystore "%JAVA_HOME%/jre/lib/security/cacerts"

注意一下, keystore 文件都受 密码 保护。生成新的 keystore 文件时,会要求你输入一个新密码;而当访问一个已有的 keystore 文件时,会要求你验证密码。

$JAVA_HOME/lib/security/cacerts 的默认密码为 “changeit” !!!

$JAVA_HOME/lib/security/cacerts 的默认密码为 “changeit” !!!

$JAVA_HOME/lib/security/cacerts 的默认密码为 “changeit” !!!

重要的事情说三遍!!!

keytool -importkeystore -srckeystore openssl.pfx -srcstoretype pkcs12 -destkeystore keystore.jks -deststoretype JKS

如果不指定 -destkeystore选项,则缺省密钥仓库将是宿主目录中名为 .keystore 的文件。如果该文件并不存在,则它将被创建。

创建密钥仓库时会要求输入访问口令,以后需要使用此口令来访问。可使用-list命令来查看密钥仓库里的内容:

      keytool -list -rfc -keystore keystore.jks

然后你需要把这个jks放到$JAVA_HOME/lib/security/cacerts这个文件夹下面,java通过两个系统属性来指向这个文件夹:

javax.net.ssl.keyStorejavax.net.ssl.trustStore

javax.net.ssl.keyStorejavax.net.ssl.trustStore用于指定要用于两个不同目的密钥存储库。

这个javax.net.ssl.keyStorejavax.net.ssl.trustStore参数是用于生成KeyManagerS和TrustManagerS(分别),然后用于构建SSLContext控件进行SSL/TLS连接时使用的SSL/TLS设置。SSLSocketFactory或者SSLEngine..这些系统属性正是默认值的来源,然后由SSLContext.getDefault(),它本身被SSLSocketFactory.getDefault()例如。(所有这些都可以通过API在许多地方进行自定义,如果您不想使用默认值和特定的SSLContextIt‘为某一特定目的而设。)

之间的差异KeyManagerTrustManager(因此在javax.net.ssl.keyStorejavax.net.ssl.trustStore)如下(引用自JSSE参考指南):

TrustManager:确定是否应该信任远程身份验证凭据(以及连接)。

KeyManager:确定要发送到远程主机的身份验证凭据(其他参数可用,其默认值在JSSE参考指南..请注意,虽然信任存储有默认值,但密钥存储没有默认值。)

本质上,密钥存储在javax.net.ssl.keyStore用于包含私钥和证书,而javax.net.ssl.trustStore意味着在远程方显示证书时包含您愿意信任的CA证书。在某些情况下,它们可以是同一个商店,尽管使用不同的存储(尤其是基于文件的存储)通常是更好的做法。

一般来说Djavax.net.ssl.keyStore和Djavax.net.ssl.trustStore一般就指向 $JAVA_HOME/lib/security/cacerts这个文件夹:
一种方式是通过在启动参数中指定,如下:

  • java -Djavax.net.ssl.trustStore=yourTruststore.jks -Djavax.net.ssl.trustStorePassword=12345

还是一种就是通过程序中指定Properties参数进行加载,不过一定要在请求发出之前进行加载,可以参考我的这篇文章

SpringBoot事件与监听机制_人工智的博客-CSDN博客使用监听实现,如下:

System.setProperty("javax.net.ssl.trustStore", "truststore的绝对路径或者相对路径");

System.setProperty("javax.net.ssl.trustStorePassword", "truststore密码");

System.setProperty("javax.net.ssl.keyStore", "keystore的绝对路径或者相对路径");

System.setProperty("javax.net.ssl.keyStorePassword", "keystore密码");

代码类似这样:

ath keystore = Files.createTempFile(null, null);
try (InputStream stream = getClass().getResourceAsStream("/cacerts")) {
    Files.copy(stream, keystore);
}

System.setProperty("javax.net.ssl.trustStore", keystore.toString());

怎么忽略ignore https 认证:

通过在httpClient call https接口之前先调用如下代码即可忽略https认证


private static DefaultHttpClient httpClientTrustingAllSSLCerts() throws NoSuchAlgorithmException, KeyManagementException {
    DefaultHttpClient httpclient = new DefaultHttpClient();
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, getTrustingManager(), new java.security.SecureRandom());
    SSLSocketFactory socketFactory = new SSLSocketFactory(sc);
    Scheme sch = new Scheme("https", 443, socketFactory);
    httpclient.getConnectionManager().getSchemeRegistry().register(sch);
    return httpclient;
}
private static TrustManager[] getTrustingManager() {
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {
        }
        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {
        }
        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    } };
    return trustAllCerts;
}

再补充一点单项认证和双向认证的区别

单向认证;

  1、客户端保存着服务端的证书并信任该证书即可

  2、https一般是单向认证,这样可以让绝大部分人都可以访问你的站点。

双向认证

  1、先决条件是有两个或两个以上的证书,一个是服务端证书,另一个或多个是客户端证书。

  2、服务端保存着客户端的证书并信任该证书,客户端保存着服务端的证书并信任该证书。这样,在证书验证成功的情况下即可完成请求响应。

  3、双向认证一般企业应用对接。

springboot配置安装证书

前面说那么多,springboot其实都给我们解决好了,因为springboot有自带的tomcat,直接把证书打到这个tomcat容器中了,不同于上面的java实现,tomcat容器有对应https的实现,将jks或者pfx文件复制到SpringBoot的resource目录中:

(2)在application.yml中配置以下信息(pkcs12格式)

server:
  port: 443
  ssl:
    key-store: classpath:***.pfx
    key-store-password: keystore的密码默认是changeit,每个人都有自己的,keytool生成jks时会要求你输入,那个时候记下来不要忘记了
    key-store-type: PKCS12

 如果是properties文件的话如下(这次以jks为例,也可以换成pkcs12):

# 单向认证开启(服务端证书,我自己作为服务端拿自己证书安装)
server.ssl.key-store=文件地址
server.ssl.key-store-password=123456
server.ssl.key-alias=sslServer
server.ssl.keyStoreType=JKS
# (我自己作为客户端,拿服务端证书安装(只包含服务端公钥的证书文件))
server.ssl.trust-store-password=123456
server.ssl.client-auth=need
server.ssl.trust-store-type=JKS
server.ssl.trust-store-provider=SUN

需要注意的是,server.ssl.client-auth有三个可配置的值:none、want和need。双向验证应该配置为need;none表示不验证客户端;want表示会验证,但不强制验证,即验证失败也可以成功建立连接。

客户端测试代码:

private final static String TEST_URL = "https://127.0.0.1:7090/server/ssl";
 
  @Test
  public void getHKVesselTrip() throws Exception {
    // 客户端证书类型
    KeyStore clientStore = KeyStore.getInstance("PKCS12");
    // 加载客户端证书,即自己的私钥
    clientStore
        .load(new FileInputStream("E:\\learning-demo\\sslClient.p12"),
            "123456".toCharArray());
    // 创建密钥管理工厂实例
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    // 初始化客户端密钥库
    kmf.init(clientStore, "123456".toCharArray());
    KeyManager[] kms = kmf.getKeyManagers();
    // 创建信任库管理工厂实例
    TrustManagerFactory tmf = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    // 信任库类型
    KeyStore trustStore = KeyStore.getInstance("JKS");
    // 加载信任库,即服务端公钥
    trustStore.load(new FileInputStream("D:\\jdk\\jre\\lib\\security\\cacerts"),
        "changeit".toCharArray());
    // 初始化信任库
    tmf.init(trustStore);
    TrustManager[] tms = tmf.getTrustManagers();
    // 建立TLS连接
    SSLContext sslContext = SSLContext.getInstance("TLS");
    // 初始化SSLContext
    sslContext.init(kms, tms, new SecureRandom());
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
    CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    try {
      HttpGet httpget = new HttpGet(TEST_URL);
      System.out.println("executing request" + httpget.getRequestLine());
      CloseableHttpResponse response = httpclient.execute(httpget);
      try {
        HttpEntity entity = response.getEntity();
        if (entity != null) {
          System.out.println(EntityUtils.toString(entity));
        }
      } finally {
        response.close();
      }
    } finally {
      httpclient.close();
    }
  }

具体可参考这篇文章:【HTTPS】Spring Boot客户端与服务端单向认证和双向认证实例_Dream it Possible-CSDN博客_客户端服务端双向认证

原理参考此文:springboot SSL双向验证原理及配置_爱学习的果子君的博客-CSDN博客_springboot ssl验证

使用java程序调用https绕开客户端证书可信校验:JAVA利用HttpClient进行HTTPS接口调用 - 路常有 - 博客园

httpclient能同时支持http和https的原理:HttpClient实现https调用 - 好奇成传奇 - 博客园

springboot同时支持http和https配置:spring cloud/spring boot同时支持http和https访问 - 凉瓜皮 - 博客园

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值