转自: https://blog.csdn.net/u012977486/article/details/88815621
一、Http
HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准。HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。
使用TCP端口为:80
二、Https
Hyper Text Transfer Protocol over Secure Socket Layer,安全的超文本传输协议,网景公式设计了SSL(Secure Sockets Layer)协议用于对Http协议传输的数据进行加密,保证会话过程中的安全性。
使用TCP端口默认为443
三、SSL协议加密方式
SSL协议即用到了对称加密也用到了非对称加密(公钥加密),在建立传输链路时,SSL首先对对称加密的密钥使用公钥进行非对称加密,链路建立好之后,SSL对传输内容使用对称加密。
对称加密
速度高,可加密内容较大,用来加密会话过程中的消息
公钥加密
加密速度较慢,但能提供更好的身份认证技术,用来加密对称加密的密钥
四、单向认证
Https在建立Socket连接之前,需要进行握手,具体过程如下:

1、客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
2、服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
3、客户端使用服务端返回的信息验证服务器的合法性,包括:
证书是否过期
发型服务器证书的CA是否可靠
返回的公钥是否能正确解开返回证书中的数字签名
服务器证书上的域名是否和服务器的实际域名相匹配
验证通过后,将继续进行通信,否则,终止通信
4、客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
5、服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。
6、服务器将选择好的加密方案通过明文方式返回给客户端
7、客户端接收到服务端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的密钥,使用服务端返回的公钥进行加密,将加密后的随机码发送至服务器
8、服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密密钥。 在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。
五、双向认证
双向认证和单向认证原理基本差不多,只是除了客户端需要认证服务端以外,增加了服务端对客户端的认证,具体过程如下:

1、客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
2、服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
3、客户端使用服务端返回的信息验证服务器的合法性,包括:
证书是否过期
发型服务器证书的CA是否可靠
返回的公钥是否能正确解开返回证书中的数字签名
服务器证书上的域名是否和服务器的实际域名相匹配
验证通过后,将继续进行通信,否则,终止通信
4、服务端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端
5、验证客户端的证书,通过验证后,会获得客户端的公钥
6、客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
7、服务器端在客户端提供的加密方案中选择加密程度最高的加密方式
8、将加密方案通过使用之前获取到的公钥进行加密,返回给客户端
9、客户端收到服务端返回的加密方案密文后,使用自己的私钥进行解密,获取具体加密方式,而后,产生该加密方式的随机码,用作加密过程中的密钥,使用之前从服务端证书中获取到的公钥进行加密后,发送给服务端
10、服务端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。
六. 代码实现
1. 多个springboot项目,以两个springboot项目为例,这里暂且叫Client项目,Server项目。
2. Client项目所在的服务器,Client.p12(客户端证书库) ,Client.cer(客户端公钥);
Server项目所在的服务器,Server.p12(服务端证书库),Server.cer(服务端公钥);
4.实现步骤
1. 创建springboot项目,Client ,Server 此处,请自行百度创建如何创建springboot项目。
2. 使用jdk自带的keytool工具,生成Client和Server端相应的证书,步骤如下:
a. Client端证书生成步骤:
1.生成客户端 Client.p12文件
keytool -genkey -v -alias Client -keyalg RSA -storetype PKCS12 -keystore C:\D\jdk1.8.0_161\Client.p12
设置密码: lq123456
注意事项:生成证书,您的名字与姓氏一项,应该填写服务器的ip(此处应该是域名,但是没有域名,故此处填写服务器ip)
2 . 导出客户端公钥Client.cer 文件
keytool -keystore C:\D\jdk1.8.0_161\Client.p12 -export -alias Client -file C:\D\jdk1.8.0_161\Client.cer
b. Server端证书生成步骤
1. 生成服务端Server.p12文件
keytool -genkey -v -alias Server -keyalg RSA -storetype PKCS12 -keystore C:\D\jdk1.8.0_161\Server.p12
设置密码: lq123456
注意事项:生成证书,您的名字与姓氏一项,应该填写服务器的ip(此处应该是域名,但是没有域名,故此处填写服务器ip)
2. 导出服务端公钥Server.cer 文件
keytool -keystore C:\D\jdk1.8.0_161\Server.p12 -export -alias Server -file C:\D\jdk1.8.0_161\Server.cer
c. 将Client端和Server端的公钥文件(.cer文件)导入双方系统的jre运行环境的cacerts证书库
1. 将客户端公钥导入的服务端jdk信任库
keytool -import -file Client.cer -keystore C:\D\jdk1.8.0_161\jre\lib\security\cacerts –v
2. 将服务端公钥导入到客户端的jdk信任库
keytool -import -file Server.cer -keystore C:\D\jdk1.8.0_161\jre\lib\security\cacerts –v
3. 将客户端公钥导入到服务端Server.p12证书库
keytool -import -v -file C:\D\jdk1.8.0_161\Client.cer -keystore C:\D\jdk1.8.0_161\server.p12
注意事项:此处导入的密码为changeit,默认密码
至此,证书生成完成,证书库导入完成!
d. 代码实现
1.Server端
第一步:在application.properties中添加如下配置:包括本地证书库和受信任证书配置
server.port=8090
server.address=10.119.165.171
server.ssl.key-store=classpath:server.p12
server.ssl.key-store-password=lq123456
server.ssl.key-alias=server
server.ssl.keyStoreType=JKS
server.ssl.trust-store=classpath:server.p12
server.ssl.trust-store-password=lq123456
server.ssl.client-auth=need
server.ssl.trust-store-type=JKS
server.ssl.trust-store-provider=SUN
第二步:服务端接口开放
package com.example.server1.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lucasliang
* @date 01/03/2019 3:21 下午
* @Description
*/
@RestController
@RequestMapping("/server")
public class ServerController {
@RequestMapping("/hello")
public String getUrlInfo() {
return "************request https success************";
}
}
2. Client端
第一步:在application.properties中添加如下配置:
server.port=8091
server.address=10.119.165.171
server.ssl.key-store=classpath:client.p12
server.ssl.key-store-password=lq123456
server.ssl.key-alias=client
server.ssl.keyStoreType=JKS
第二步:单元测试:
package com.example.client;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author lucasliang
* @date 04/03/2019 2:13 下午
* @Description
*/
@SpringBootTest(classes = {Client1ApplicationTests.class})
@RunWith(SpringRunner.class)
public class P12CertTest {
private final static String TEST_URL = "https://10.119.165.171:8090/server/hello";
@Test
public void getHKVesselTrip() throws Exception {
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore
.load(new FileInputStream("C:\\D\\jdk1.8.0_161\\shuangxiang\\client_original.p12"),
"lq123456".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, "lq123456".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("C:\\D\\jdk1.8.0_161\\jre\\lib\\security\\cacerts"),
"changeit".toCharArray());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
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();
}
}
}
此时,Client端发送请求到Server端,请求成功,https双向认证完成!