https原理(三)双向实践(curl)
接 https代理服务器(三)实践,实践双向ssl
本文采用客户端与服务端不同密钥对
1 mkcert myclient
生成客户端公钥 私钥
2 mkcert -pkcs12 myclient
也可以直接生成p12 非对称钥匙对
请注意,根据https原理(四)双向实践(java客户端+tcp代理) 9 的结论,1与2是两套证书,1的私钥加密的内容不能被2的公钥解密
本文只有用到1的file
3 keytool -import -file myclient.pem -keystore myclient.jks
密码123456
将公钥导入jks (https://www.jianshu.com/p/e1aaa5e9de17)
也可以从p12提取公钥pem
PKCS12是一袋X509证书和密钥。 袋子中的证书之一就是您想要的X509证书。
https://www.codenong.com/46842957/
因为之前对trustStore的理解不够深刻,因此,在项目中配置server.ssl.trust-store时候,直接将PKCS12密钥交换文件转换过来的JKS设置成为trustStore。这里其实是有问题的,trustStore是服务器的信任密钥存储库,存CA的证书(操作系统管理的所有受信任的根证书),有一部分人存的是客户端证书集合(比如我们内部自己的自签名证书,必须手动设置为信任)不算特别规范,但是trustStore里是绝对不能有私钥信息的。否则在加载trustStore的时候会报类似错误(spring-boot-2.1.0+内嵌tomcat):
Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
at java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:200) ~[na:1.8.0_181]
at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:157) ~[na:1.8.0_181]
at java.security.cert.PKIXBuilderParameters.<init>(PKIXBuilderParameters.java:130) ~[na:1.8.0_181]
at org.apache.tomcat.util.net.jsse.JSSEUtil.getParameters(JSSEUtil.java:390) ~[tomcat-embed-core-9.0.12.jar:9.0.12]
at org.apache.tomcat.util.net.jsse.JSSEUtil.getTrustManagers(JSSEUtil.java:314) ~[tomcat-embed-core-9.0.12.jar:9.0.12]
at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:112) ~[tomcat-embed-core-9.0.12.jar:9.0.12]
... 24 common frames omitted
因此我有理由相信,这个trustStore的内容一定出问题了。所以,我尝试只用证书链来生成trustStore:
导入我们的证书链(从根证书到应用证书)
但是在启动的时候,却一直报错:the trustAnchors parameter must be non-empty
经排查,是trust-store有问题,可以看看解释的文章:
https://blog.csdn.net/HD243608836/article/details/118555240
总体来说,就是trust-store只需要包含公钥的信息,而之前配置的JKS文件同时含有公私钥的信息而导致出错,如下文章讲述了如何生成只有公钥的jks:
http://t.zoukankan.com/Amos-Turing-p-7111499.html
https://www.cnblogs.com/zgz21/p/16824929.html
服务端有客户端私钥就不合理
4
server.ssl.trust-store=classpath:mkcert/myclient.jks
server.ssl.trust-store-password=123456
server.ssl.client-auth=need
5 https://blog.csdn.net/u013187531/article/details/124508774
import java.security.cert.X509Certificate;
import javax.servlet.http.HttpServletRequest;
@RequestMapping(value = "/test", method = RequestMethod.GET)
@ResponseBody
public String get(HttpServletRequest request){
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
if(certs != null && certs.length > 0) {
X509Certificate iX509Cert = certs[0];
String dn = iX509Cert.getSubjectDN().toString();
System.out.println("个人证书信息:" + dn);
String username = "";
String[] dnArray = dn.split(",");
for (String dnItem : dnArray) {
String[] dnInfo = dnItem.split("=");
String key = dnInfo[0];
String value = dnInfo[1];
if("cn".equalsIgnoreCase(key.trim())) {
username = value;
break;
}
}
}
return "DONE";
}
6
curl -k --cert myclient.pem --key myclient-key.pem https://myhost.com:8080/test/test -v
https://www.jianshu.com/p/e1aaa5e9de17
控制台打印:个人证书信息:OU=mac@macdeMacBook.local, O=mkcert development certificate
curl未指定truststore,信任一切服务端公钥
7 第2步的p12,本想让浏览器信息然后在浏览器中访问6,双击-信任所有,重启chrome,但是并不如windows版直接弹框让选择
https://blog.csdn.net/a82514921/article/details/104587416/
在http原理(五)双向加代理实践中通过加代理的方式实现
其他:
1
http://m.juming.com/zx/13583.html
为什么要双向ssl
SSL 单向验证过程中,客户端会验证自己访问的服务器端,服务器端对客户端不做验证。如果服务器端验证客户端,则需要开启服务器端验证,这就是双向验证。
而在一般的浏览器以及网站中都是采用单向认证的,虽然安全性能不及证书双向认证高,但是它的用户数目非常的广泛。没有需要做太多的用户身份验证。
如果是一些公司企业需要做网站对接的话。一般建议使用SSL双向证书认证。这样的话,对客户端进行身份验证。做了证书双向认证之后,就可以让信息传递更加的安全以及加密,防止敏感信息被第三方泄露,而加密方案也会相对的好。
2
https://www.codenong.com/cs81486147/
Trust Store:密钥文件可以存放私钥和公钥,具体就是可以存放自己的私钥、自己的公钥和别人的公钥(也可以存放别人的私钥,但这不合理,私钥必须私有)。一般地我们把自己的(私钥和公钥)存放在Key Store里,而把别人的公钥存放在Trust Store里。
3
双向的对称密钥一定要用客户端公钥加密的协商和传输一定要有客户端公钥参与,更本质的,握手过程一定要involve中间人没有的客户端私钥
为什么:因为如果中间人只代理服务端公钥,不代理客户端公钥,握手仍能顺利进行,因为客户端公钥没有参与,只有服务端公钥决定,则凭借中间人私钥即可破解对称密钥
服务端的公钥给中间人,而客户掌握着客户端,可以手动信任中间人替换的仿冒服务端公钥
客户端的公钥给中间人,服务端对中间替换的仿冒客户端公钥具有绝对控制权
3+如果客户端与服务端用同一个密钥对,这可能是第一个漏洞,客户端与服务端用同一个公钥
漏洞真的存在?
图来自:https://www.cnblogs.com/larrydpk/p/12830365.html
ssl握手将失败在第8步,中间人拿到客户端公钥加密的加密方案,没有私钥解密
ssl即使不断,中间人拿着自己的私钥解密第9步客户端用“服务端公钥”即中间人公钥加密的对称密钥,也不知道加密方案
所以漏洞不存在
4 双向为什么能抵抗中间人
客户端有客户端的私钥公钥,但是只会响应服务端对公钥的请求,所以中间人只能截到客户端公钥
服务端用involve客户端公钥的方式加密对称密钥,则中间人没有私钥无法解密
中间人如果像对服务器公钥那样掉包用自己的一对密钥冒充客户端,则无法通过服务端对客户端公钥的验证
5 奇怪现象
need时java 客户端可访问,但没客户端证书;want时有
后来发现,没有给truststore,猜测为,当springboot读到need,没有读到truststore时,没有强制检验的手段,直接放弃双向握手
后证实如果不给truststore,会用java cacerts文件
而当want时,虽然tomcat没有检验手段,但tomcat不能排除用户没有检验手段,兴许用户就是不用 struststore检验,所以没有放弃双向握手,把客户端公钥传下去
这可能是第二个漏洞,没有使用tomcat专业级验证,使用自己的java代码简单验证客户端公钥cn,所以只需要自签名一个相同cn的公钥,就能绕开服务端验证
漏洞存在?
在https原理(四)双向实践(java客户端+tcp代理)中7有实践,结果自签名证书无法突破,ca证书直接ssl失败,tomcat的want肯定做了一些基础检验,即使在没有truststore的情况下