https双向验证原理,请自行查找,简单来说,服务端与客户端交互式时,要对客户端进行身份验证。这里的客户端和服务端没有严格区分。可视为请求发起方为客户端,接收方为服务端。
简单的理解,在一个单向通讯时:
客户端向服务器发送请求时,服务端需要验证客户端的证书。
而客户端需要信任服务端的证书。此时需要客户端和服务端各提供一个证书。
两个系统角色互换,则需要另外两个证书, 所以在双向的通讯中总共需要4个证书,双方各两个。(当然也可以客户端、服务端证书一起用,这时双方各一个,相当于你作为客户端和服务端时的证书相同;也可以配置不校验客户端证书等等,)。
#这个证书一会我们自己签发
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
若自签证书则会提示
说明我们的证书不是权威机构颁发的。点击高级信任它就好。
网上代码很多,随便找,这边提供一个工具类
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);
}
}
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
生成两个证书,一个作为客户端证书、一个作为服务端证书。避免区分谁是客户端谁是服务端麻烦,将两个证书全部导入信任库中即可。
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 -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发请求,我是客户端,应用是服务端。
接着,你服务端要检查我的,那我这边导入一下
python -m SimpleHTTPServer 8081
起好了之后是这样:
别人curl xxx.xxx.xxx:8081 8081端口是通的返回
还可以通过telnet ip port 测试端口连通性