HTTPS双向验证

HTTPS双向验证在实际项目中的使用

  • 目录
    • HTTPS实际应用场景
    • 双向验证在Springboot中的配置
    • 发送httpsPost请求
    • 证书介绍
    • 自签生成客户端+服务端证书
    • 导入证书进证书库
    • 测试接口:火墙
    • 测试接口:curl命令模拟报文
    • 测试接口: postman发送
      • 接口测试:自己启个端口让别人来访问

目录

HTTPS实际应用场景

https双向验证原理,请自行查找,简单来说,服务端与客户端交互式时,要对客户端进行身份验证。这里的客户端和服务端没有严格区分。可视为请求发起方为客户端,接收方为服务端。

简单的理解,在一个单向通讯时:
客户端向服务器发送请求时,服务端需要验证客户端的证书。
而客户端需要信任服务端的证书。此时需要客户端和服务端各提供一个证书。
两个系统角色互换,则需要另外两个证书,
所以在双向的通讯中总共需要4个证书,双方各两个。(当然也可以客户端、服务端证书一起用,这时双方各一个,相当于你作为客户端和服务端时的证书相同;也可以配置不校验客户端证书等等,)。

双向验证在Springboot中的配置

	#这个证书一会我们自己签发
	server.ssl.key-store=classpath:server.p12
	server.ssl.key-store-password=pwd
	server.ssl.keyStoreType=PKCS12
	# 开启双向验证 即对客户端的证书也需要校验
	server.ssl.client-auth=need
	# 通讯连接时会在信任库中检查对方证书是否我们信任
	server.ssl.trust-store=classpath:truststore.keystore
	server.ssl.trust-store-password=pwd
	server.ssl.trust-store-type=JKS

若单向认证,则删除这项配置:

	#检验客户端证书
	server.ssl.client-auth=need

单向认证相当于给系统加了一个安全保护,报文密文传输,防劫持等等。访问时需要改为https://xxx.xxx.xxx.xxx
若自签证书则会提示
HTTPS双向验证_第1张图片
说明我们的证书不是权威机构颁发的。点击高级信任它就好。

发送httpsPost请求

网上代码很多,随便找,这边提供一个工具类

public class HttpsUtils {
    // 主秘钥绝对路径,这些参数都是绝对路径,配置化即可
    private static final String keystore = "D:\\https-demo\\src\\main\\resources\\server.keystore";
    // 主密钥密码
    private static final String keystorePassword = "pwd";
    // 信任密钥绝对路径
    private static final String truststore = "D:\\https-demo\\src\\main\\resources\\server.keystore";
    // 信任密钥密码
    private static final String truststorePassword = "pwd";

    private static SSLContext sslContext;

    private  final OkHttpClient httpClient;

    public HttpsUtils() throws Exception {
        this.httpClient = new OkHttpClient
                .Builder()
                .hostnameVerifier((s, session) -> true)
                .sslSocketFactory(getSSLContext(keystorePassword,keystore,truststorePassword).getSocketFactory())
                .build();;
    }

    /**
     * 加载keystore
     */
    public static KeyStore getKeyStore(final String filePath, final String password) throws KeyStoreException,
            NoSuchAlgorithmException, CertificateException, IOException {
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream is = null;
            // is = new FileInputStream(new File(filePath));
            is = HttpsUtils.class.getResourceAsStream(filePath);
            keystore.load(is, password != null ? password.toCharArray() : null);
        return keystore;
    }

    /**
     * 获得 SSLSocketFactory
     */
    public static SSLContext getSSLContext(String password,
                                           String keyStorePath, String trustStorePath) throws Exception {
        // 实例化密钥库
        KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        // 获得密钥库
        KeyStore keyStore = getKeyStore(password, keyStorePath);
        // 初始化密钥工厂
        keyManagerFactory.init(keyStore, password.toCharArray());

        // 实例化信任库
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        // 获得信任库
        KeyStore trustStore = getKeyStore(password, trustStorePath);
        // 初始化信任库
        trustManagerFactory.init(trustStore);
        // 实例化SSL上下文
        SSLContext ctx = SSLContext.getInstance("TLS");
        // 初始化SSL上下文
        ctx.init(keyManagerFactory.getKeyManagers(),
                trustManagerFactory.getTrustManagers(), null);
        // 获得SSLSocketFactory
        return ctx;
    }

    private static final MediaType APPLICATION_JSON = MediaType.parse("application/json;charset=utf-8");
    public  String post(String url, String json) throws IOException {
        RequestBody body = RequestBody.create(APPLICATION_JSON, json == null ? "" : json);
        Request request = new Request
                .Builder()
                .url(url)
                .post(body)
                .build();
        return httpClient.newCall(request).execute().body().string();
    }

    public static void main(String[] args) throws Exception {
        HttpsUtils httpsUtils = new HttpsUtils();
        final String s1 = httpsUtils.post("https://127.0.0.1:8080//http/hello","123123");
        System.out.println(s1);
    }
}

证书介绍

HTTPS双向验证_第2张图片

P12 DER PEM
编码格式 二进制 二进制 Base64
私钥

既然P12内含私钥便可以导出私钥:
#客户端个人证书的公钥
openssl pkcs12 -clcerts -nokeys -in cert.pfx -out client.pem
#客户端个人证书的私钥
openssl pkcs12 -nocerts -nodes -in cert.pfx -out key.pem
#也可以公私钥一起导出
openssl pkcs12 -in cert.pfx -out all.pem -nodes

  • 交换证书时,一般交换个人证书的公钥,所以用.p12转化为pem,或者.der后缀导出公钥即可。

自签生成客户端+服务端证书

生成两个证书,一个作为客户端证书、一个作为服务端证书。避免区分谁是客户端谁是服务端麻烦,将两个证书全部导入信任库中即可。

1、生成证书1
keytool -genkey -v -alias server-key -keyalg RSA -storetype PKCS12 -keystore D:\server.p12 -validity 36500

2、生成证书2
keytool -genkey -v -alias client-key -keyalg RSA -storetype PKCS12 -keystore D:\client.p12 -validity 36500

3、生成证书库
keytool -keyalg RSA -keysize 1024 -validity 36500 -keystore trust.keystore -storepass 123123
-keyalg RSA RSA算法
-validity 36500 有效期单位天
-keystore trust.keystore 生成文件名
-storepass 信任库密码

导入证书进证书库

导入:
keytool -import -file client-cert.pem -alias alias_name -keystore truststore.jks
删除:
keytool -delete -alias emailcert -keystore truststore.jks
查看:
keytool -v -list -keystore https_keystore.jks

测试接口:火墙

双向验证都有了,火墙肯定也有限制,检查检查。
一般情况下:connection refused 就是端口没通
报错和证书有关的,证书路径找不到、证书算法不对啊之类的。报错含有证书字眼的,100%证书导入错了,去确认一下证书对不对,确认后重新导入即可。

这里给两个常用命令吧
查看所有规则:
iptables -L -n

查看入访规则:
iptables -L INPUT -n --line-number

删除规则:
iptables -D INPUT 1

给123.123.123.123,开8081端口,ACCEPT可以看出这是入访ip(别人访问你的):
iptables -I INPUT 1 -p tcp --dport 8081 -s 123.123.123.123 -j ACCEPT

给123.123.123.123 端口全开了:
iptables -I INPUT 1 -s 123.123.123.123 -j ACCEPT

测试接口:curl命令模拟报文

curl -m 10 -k --cert ./all.pem https://123.123.123.123:8443/ -d '{"xxx":"xxx"}'
解释:
-d 发送post请求,如果方法不对就给你返回405了呗
-m 10 等待10秒,接收不到没有回应算了
-k 不校验服务端证书
-k --cert :-k不校验服务端证书了,可是服务端要检查我的啊,所以我带上之前导出的 all.pem

all.pem 导出方法:
openssl pkcs12 -in cert.p12 -out all.pem -nodes

还有一种方法:
curl -k --cert client.pem --key client.pem https://123.123.123.123:8443/ -d ‘{}’
all.pem == client.pem+client.key

client.pem、client.key导出方法:(引用自大佬,这边放一起,做一个总结)
#客户端个人证书的公钥
openssl pkcs12 -clcerts -nokeys -in cert.pfx -out client.pem

#客户端个人证书的私钥
openssl pkcs12 -nocerts -nodes -in cert.pfx -out key.pem

测试接口: postman发送

首先咱们捋一下,我用postman发请求,我是客户端,应用是服务端。

首先,服务端的证书我不检查了
HTTPS双向验证_第3张图片
这个开关开一下

接着,你服务端要检查我的,那我这边导入一下

HTTPS双向验证_第4张图片
把两个证书全导入进去吧,省事

接口测试:自己启个端口让别人来访问

python -m SimpleHTTPServer 8081
起好了之后是这样:
HTTPS双向验证_第5张图片
别人curl xxx.xxx.xxx:8081 8081端口是通的返回

HTTPS双向验证_第6张图片

还可以通过telnet ip port 测试端口连通性

你可能感兴趣的:(实战)