关于JAVA与nginx双向认证的总结
目录
一、 研究目的和简要
二、 Openssl生成级联证书
三、 Nginx配置双向认证
四、 Java实现双向认证
五、 证书关系图
。
一、 研究目的和简要
看着简单的SSL证书,可以说绝对被SSL证书的外表欺骗了。为什么写这么多?我也很讨厌要看一大堆东西才实现的功能!但SSL双向认证真需要这么多字来说明(虽然废话也挺多,例如这句)。笔者研究了1个星期,最终是实现了双向认证,但对ssl认证过程和加密过程还是没有完全明白。网上是有很多关于生成证书和java代码实例,但大多都不能在实际工程场景中发挥作用。内容是挺多的,请耐心看,虽然理解方面存在我的脑洞,但可以保证这个文章绝对不是从网上简单的复制粘贴,是真材实料,杠杠的研究。
目的:
熟悉https请求双向认证的原理和运用方法,并在实际环境中得到正确使用
简要:
由于程序研究过程以及网络培训视频、网络文章等多以自己生成非正规CA发布的证书,绝大部分生成的证书与实际服务器使用的证书差别较大,在笔者生成大量证书(其实也就10套左右)后,感谢地址
https://blog.csdn.net/langeldep/article/details/54675898?utm_source=blogxgwz8
生成的证书经笔者测试可实现双向验证。
测试包括(细节将在“Nginx配置双向认证”和“Java实现双向认证”中体现):同一套证书验证可通过验证,客户端不信任或者信任另一套证书不可通过验证等。
证书生成后,nginx需要正确配置和Java代码正确配置才可达到双向认证成功的效果。Nginx配置错误或者无效,多出现无论以什么证书,只要有证书就可通过验证的情况。在nginx配置正确后,这时Java代码只要没有正确配置就会出现验证失败。
以上是关于“Openssl生成级联证书”、“Nginx配置双向认证”、“Java实现双向认证”等小节之必要的说明。
二、 Openssl生成级联证书
参考来源: https://blog.csdn.net/langeldep/article/details/54675898?utm_source=blogxgwz8
以下所有步骤都是在root用户下执行。
第一步:生成相关目录和文件
笔者使用的是linux虚拟机研究,openssl安装后与参考网站的一样,配置文件在/etc/pki/tls,所以跟着作了:
mkdir /etc/pki/ca_linvo
cd /etc/pki/ca_linvo
mkdir root server client newcerts
echo 01 > serial
echo 01 > crlnumber
touch index.txt
经过思考,生成这些文件可能是为了区分原文件夹,避免证书管理混乱。而笔者在生成第二套证书时,发现用原来的文件夹保存第二套也是有问题的,这涉及到第二步为什么要修改openssl配置文件。所以还是乖乖生成这些文件夹和文件,每新建一套都创建一个。
第二步:修改openssl配置
修改配置之前,笔者担忧出错,所以拷贝了一份配置文件的备份,参考操作为:
cp /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl.cnf_bak
vi /etc/pki/tls/openssl.cnf
说明一下,上面的openssl.cnf就是openssl的配置文件
[ ca ]下的default_ca改为自己的ca名字,例如参考网站中的[ CA_linvo ]
操作方式:注释掉原来的,添加自己的
复制ca设置并改成自己的。原设置是[ CA_default ],赋值一遍粘贴一下就可以了
将自己复制的那一份设置中的下面三个参数改为对应参数,具体的参数根据创建的文件夹而定:
dir = /etc/pki/ca_linvo
certificate = $dir/root/ca.crt
private_key = $dir/root/ca.key
如果只是创建一套,可以忽略这一段话→→→这里certificatec和private_key的参数说明了根证书只能是ca.crt,根私钥只能是ca.key,所以笔者在创建第二套级联证书的根证书时,想到要去修改配置文件怕会造成证书管理混乱,所以在生成第二套时,又创建了一个证书文件夹ca_lan并在配置文件中加入了另一个CA_lan的设置,这样两套证书就分开管理了。
修改完后,shift+ZZ或者:wq保存修改
(tips: vi或者vim在按入i键后进入编辑模式,编辑模式中按Esc退出编辑模式,在这种情况下使用shift+ZZ或者:wq才是退出并保存修改)
第三步:创建根证书
创建根证书私钥:
openssl genrsa -des3 -out /etc/pki/ca_linvo/root/ca.key 2048
创建根证书请求文件:
openssl req -new -key /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/root/ca.csr
创建根证书:
openssl x509 -req -days 3650 -in /etc/pki/ca_linvo/root/ca.csr -signkey /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/root/ca.crt
创建证书吊销列表:
openssl ca -gencrl -out /etc/pki/ca_linvo/root/ca.crl -crldays 7
需要注意这里的路径需要改为自己创建的文件夹路径,例如笔者创建第二套证书时,创建秘钥是使用:openssl genrsa -des3 -out /etc/pki/ca_lan/root/ca.key 2048,不同点是-out后面的路径不同。
ca.key为私钥,csr是请求文件,crt是证书,crl证书吊销列表。
笔者理解如下:
最终目的是得到crt证书。形象地讲,有了私钥是一种身份证明,就像我是有钥匙的人了,我用这个钥匙可以去锁东西了,想打开我锁的东西,那就请得到我允许打开的证明(crt证书),证书怎么来?把我承认的请求(csr)发给我,我就给你证书。这个想象式理解只是笔者自己的脑洞,可能跟实际作用有出入。
可以说笔者并没有深入研究,这里大部分是笔者脑洞,包括所有小节。
第四步:生成服务器证书
生成服务器证书私钥:
openssl genrsa -des3 -out /etc/pki/ca_linvo/server/server.key 2048
生成服务器证书请求文件:
openssl req -new -key /etc/pki/ca_linvo/server/server.key -out /etc/pki/ca_linvo/server/server.csr
生成服务器证书:
openssl ca -in /etc/pki/ca_linvo/server/server.csr -cert /etc/pki/ca_linvo/root/ca.crt -keyfile /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/server/server.crt -days 3650
同样要注意修改一下路径未自己创建的文件夹路径。
同样脑洞开始:证书比根证书多了一个根证书的签名。就像这个证书虽然是一个私钥生成的,但这个有证书还被那个人的老板签了个字,这样它老板可以解释这个证书是不是被认可的。客户端拿着这个证书用根证书验证就可以通过。
第五步:生成客户端证书
生成客户端证书私钥:
openssl genrsa -des3 -out /etc/pki/ca_linvo/client/client.key 2048
生成客户端证书请求文件:
openssl req -new -key /etc/pki/ca_linvo/client/client.key -out /etc/pki/ca_linvo/client/client.csr
生成客户端证书:
openssl ca -in /etc/pki/ca_linvo/client/client.csr -cert /etc/pki/ca_linvo/root/ca.crt -keyfile /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/client/client.crt -days 3650
同样要注意修改一下路径未自己创建的文件夹路径。
方式跟第四步服务器证书生成一样。参考网站中提到服务器似乎不用根证书参与,所以有以下脑洞:
浏览器访问会从服务器中获取服务器证书,所以浏览器只添加客户端证书就可以。
Tips:其它有帮助的命令,以下命令自己注意一下路径和文件名
生成客户端证书安装pfx文件,双击就可安装到电脑中,浏览器就可识别:
openssl pkcs12 -export -inkey /etc/pki/ca_linvo/client/client.key -in /etc/pki/ca_linvo/client/client.crt -out /etc/pki/ca_linvo/client/client.pfx
生成java可用的p12文件:
openssl pkcs12 -export -clcerts -in client-cert.cer -inkey client-key.key -out client.p12
秘钥转非秘秘钥:可以用来转服务器证书,转了非秘秘钥后,nginx重启就不用输入秘钥密码
openssl rsa -in danfeng.key -out danfeng_nopass.key
三、 Nginx配置双向认证
以下内容在nginx配置文件中设置,在主体中设置。
server{
}
其它设置自行去学习,这里只列出配置部分的设置
server{
…
ssl on;
ssl_certificate /xxxxxxxxxxxxxx/ssl/myssl/server.crt;
ssl_certificate_key /xxxxxxxxxxxxxx/ssl/myssl/server_nopass.key;
ssl_client_certificate /xxxxxxxxxxxxxx/ssl/myssl/ca.crt;
ssl_verify_depth 2;
ssl_verify_client on;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
…
}
主要由以下几个参数需要理解:
ssl on : 设置启动https请求
ssl_certificate :设置服务器证书
ssl_certificate_key :设置服务器证书秘钥
ssl_client_certificate :设置根证书,用于验证客户端
ssl_verify_depth :设置验证程度
ssl_verify_client :设置是否开启客户端验证
其它的为什么不写出来?因为我也没有去详细研究
测试过程:
客户端带证书访问可正常访问
客户端不带证书不可访问
注释掉ssl_verify_client那一行,不管有没有客户端证书都可以访问,并且同时注释掉ca证书那一行也一样,而待客户端证书访问的响应数据也是正常。这里怀疑客户端发送内容是不是没有加密?
四、 Java实现双向认证
java需要一个信任服务器证书和客户端证书,下面用两种方式处理证书和信任库(应该针对不同的证书有不同的方式)。
第一种方式:使用httpclient处理:
java代码:参考来源:https://www.cnblogs.com/dreamingodd/p/7491098.html
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
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.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
/**
* #1
* HTTPS 双向认证 - direct into cacerts
* @Author Ye_Wenda
* @Date 7/11/2017
*/
public class HttpsDemo {
// 客户端证书路径,用了本地绝对路径,需要修改
private final static String TRUST_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\server\\tserver.keystore";
private final static String PFX_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\client\\client.p12";
private final static String PFX_PWD = "333333"; //客户端证书密码及**库密码
public static String sslRequestGet(String url) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream instream = new FileInputStream(new File(PFX_PATH));
try {
// 这里就指的是KeyStore库的密码
keyStore.load(instream, PFX_PWD.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, PFX_PWD.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext
, new String[] { "TLSv1" } // supportedProtocols ,这里可以按需要设置
, null // supportedCipherSuites
, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
// HttpGet httpget = new HttpGet(url);
HttpPost httpget = new HttpPost(url);
// httpost.addHeader("Connection", "keep-alive");// 设置一些heander等
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
// 返回结果
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
public static void main(String[] args) throws Exception {
char SEP = File.separatorChar;
System.setProperty("javax.net.ssl.trustStore", TRUST_PATH);
// System.out.println(System.getProperty("java.home") + SEP + "lib" + SEP + "security" + SEP + "cacerts");
// System.setProperty("javax.net.ssl.trustStore", System.getProperty("java.home") + SEP + "lib" + SEP + "security" + SEP + "cacerts");
System.out.println(sslRequestGet("https://ip:6644/posttest?inputname=23442432424"));
}
}
其中需要一个插件,若需要jar包类型,自己去网上找:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
类中属性:三个常量字符串,一个为服务器证书信任库文件,一个是客户端p12证书文件,一个是客户端证书密码
// 客户端证书路径,用了本地绝对路径,需要修改
private final static String CER_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\server\\tserver.keystore";
private final static String PFX_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\client\\client.p12";
private final static String PFX_PWD = "333333"; //客户端证书密码及**库密码
sslRequestGet方法里的代码:
用于将客户端p12证书添加到请求中
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream instream = new FileInputStream(new File(PFX_PATH));
try {
// 这里就指的是KeyStore库的密码
keyStore.load(instream, PFX_PWD.toCharArray());
} finally {
instream.close();
}
利用生成的keystore生成SSLContext再生成Httpsclinet
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, PFX_PWD.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext
, new String[] { "TLSv1" } // supportedProtocols ,这里可以按需要设置
, null // supportedCipherSuites
, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
在main函数中,需要指定信任库的位置javax.net.ssl.trustStore。
System.setProperty("javax.net.ssl.trustStore", TRUST_PATH);
如果不是main函数执行,则可以通过手动添加到JDK或JRE里面的信任库。
下面是关于手动导入、查看、删除JDK或JRE信任库的命令:
1、添加证书到信任库:
这其中遇到一个问题,电脑有好几JDK版本,甚至一个版本甚至装了一个JRE和JDK里面JRE,要添加的是哪个JRE里面。答案是根据运行的情况或者开发使用的情况,到相应的地方,例如我使用JDK里面的JRE作为开发,那么使用的lib是xxxxx/JDK1.8.0/jre/lib。
记得修改下面命令中,lib/security/cacerts的路径。默认密码是changeit。
keytool -import -alias FIDDLER -keystore ../lib/security/cacerts -file 代理工具证书路径\证书文件名
2、查看信任库信息:
同样记得注意下面的路径。
keytool -list -keystore "xxxxx/jre/lib/security/cacerts" -storepass changeit
如果要根据名称部分匹配,可以在后面加上,看清楚前面有个|按键哦
|findstr xxx
可以根据查出来的别名alias删除
keytool -delete -alias taobao -keystore "xxxx/lib/security/cacerts" -storepass changeit
第二种方式使用JDK的类处理:
java代码:
https部分参考来源 https://blog.csdn.net/hellozpc/article/details/82077763
http很久之前在网上找的,很悲伤的事情是没有记录下来从哪里抄来的了,往原作者有怪莫怪。
import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;
import java.util.List;
import java.util.Map;
public class URLConnClientUtil {
private final static String charset = "UTF-8";
protected static String clientKeyType = "JKS";
protected static String truestKeyType = "JKS";
public String getTruestKeyType() {
return truestKeyType;
}
public void setTruestKeyType(String truestKeyType) {
this.truestKeyType = truestKeyType;
}
public String getClientKeyType() {
return clientKeyType;
}
public void setClientKeyType(String clientKeyType) {
this.clientKeyType = clientKeyType;
}
/**
* @param urlCon
*/
private static void closeUrlCon(HttpsURLConnection urlCon) {
if (urlCon != null) {
urlCon.disconnect();
}
}
/**
* @param is
* @param br
* @throws IOException
*/
private static void closeInputStream(InputStream is, BufferedReader br)
throws IOException {
if (br != null) {
br.close();
}
if (is != null) {
is.close();
}
}
/**
* @param os
* @param fis
* @param bis
* @throws IOException
*/
private static void closeOutputStream(OutputStream os, InputStream fis,
BufferedInputStream bis) throws IOException {
if (os != null) {
os.flush();
}
if (bis != null) {
bis.close();
}
if (fis != null) {
fis.close();
}
if (os != null) {
os.close();
}
}
public static String sendHttpsGet(String param, String requestAddr,String clientCertPath,String clientCertPwd,String trustCertPath,String truestCertPwd) {
SSLContext sslContext = null;
String requestData = param;
requestAddr = requestAddr + "?" + requestData;
StringBuffer sb = null;
HttpsURLConnection urlCon = null;
OutputStream os = null;
InputStream fis = null;
BufferedInputStream bis = null;
InputStream is = null;
BufferedReader br = null;
try {
if (sslContext == null) {
sslContext = SSLContext.getInstance("SSL");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyManager[] keyManagers = null;
TrustManager[] trustManagers = null;
if(clientCertPath != null){
KeyStore keyStore = KeyStore.getInstance(clientKeyType);
keyStore.load(new FileInputStream(clientCertPath),clientCertPwd == null?null:clientCertPwd.toCharArray());
kmf.init(keyStore, clientCertPwd.toCharArray());
keyManagers = kmf.getKeyManagers();
}
if(trustCertPath != null){
KeyStore trustKeyStore = KeyStore.getInstance(truestKeyType);
trustKeyStore.load(new FileInputStream(trustCertPath),truestCertPwd == null?null:truestCertPwd.toCharArray());
tmf.init(trustKeyStore);
trustManagers = tmf.getTrustManagers();
}
sslContext.init(keyManagers, trustManagers, null);
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
}
URL url = new URL(requestAddr);
urlCon = (HttpsURLConnection) url.openConnection();
urlCon.setRequestMethod("GET");
//urlCon.setRequestProperty("Content-type", "text/xml;charset=" + charset);
urlCon.setSSLSocketFactory(sslContext.getSocketFactory());
urlCon.connect();
closeOutputStream(os, fis, bis);
is = urlCon.getInputStream();
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
closeOutputStream(os, fis, bis);
closeInputStream(is, br);
closeUrlCon(urlCon);
} catch (Exception e2) {
e2.printStackTrace();
}
}
return null;
}
public static String sendHttpsPost(String param, String requestAddr,String clientCertPath,String clientCertPwd,String trustCertPath,String truestCertPwd) {
SSLContext sslContext = null;
String requestData = param;
StringBuffer sb = null;
HttpsURLConnection urlCon = null;
OutputStream os = null;
InputStream fis = null;
BufferedInputStream bis = null;
InputStream is = null;
BufferedReader br = null;
try {
if (sslContext == null) {
sslContext = SSLContext.getInstance("SSL");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyManager[] keyManagers = null;
TrustManager[] trustManagers = null;
if(clientCertPath != null){
KeyStore keyStore = KeyStore.getInstance(clientKeyType);
keyStore.load(new FileInputStream(clientCertPath),clientCertPwd == null?null:clientCertPwd.toCharArray());
kmf.init(keyStore, clientCertPwd.toCharArray());
keyManagers = kmf.getKeyManagers();
}
if(trustCertPath != null){
KeyStore trustKeyStore = KeyStore.getInstance(truestKeyType);
trustKeyStore.load(new FileInputStream(trustCertPath),truestCertPwd == null?null:truestCertPwd.toCharArray());
tmf.init(trustKeyStore);
trustManagers = tmf.getTrustManagers();
}
sslContext.init(keyManagers, trustManagers, null);
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
}
URL url = new URL(requestAddr);
urlCon = (HttpsURLConnection) url.openConnection();
urlCon.setDoOutput(true);
urlCon.setDoInput(true);
urlCon.setRequestMethod("POST");
//urlCon.setRequestProperty("Content-type", "text/xml;charset=" + charset);
urlCon.setSSLSocketFactory(sslContext.getSocketFactory());
os = urlCon.getOutputStream();
fis = new ByteArrayInputStream(requestData.getBytes(charset));
bis = new BufferedInputStream(fis);
byte[] bytes = new byte[1024];
int len = -1;
while ((len = bis.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
closeOutputStream(os, fis, bis);
is = urlCon.getInputStream();
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
closeOutputStream(os, fis, bis);
closeInputStream(is, br);
closeUrlCon(urlCon);
} catch (Exception e2) {
e2.printStackTrace();
}
}
return null;
}
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
Map<String, List<String>> map = connection.getHeaderFields();
for (String key : map.keySet()) {
System.out.println(key + "--->" + map.get(key));
}
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("Sending GET exception!" + e);
e.printStackTrace();
}
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("Sending POST exception!"+e);
e.printStackTrace();
}
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
}
这个java文本包括了使用https和http分别用post和get方法请求。其中有涉及到需要设置请求头等需要修改的可以自行在工具类中改,或者自己再封装一层。
https处使用的是两个证书库,一个是添加了服务器证书的信任库,一个是使用p12做成的证书库(应该直接使用p12也可以)
其中代码:
生成客户端证书库对象,修改了参考网站中的代码为了适配单向认证。
if(clientCertPath != null){
KeyStore keyStore = KeyStore.getInstance(clientKeyType);
keyStore.load(new FileInputStream(clientCertPath),clientCertPwd == null?null:clientCertPwd.toCharArray());
kmf.init(keyStore, clientCertPwd.toCharArray());
keyManagers = kmf.getKeyManagers();
}
生成信任库对象:
if(trustCertPath != null){
KeyStore trustKeyStore = KeyStore.getInstance(truestKeyType);
trustKeyStore.load(new FileInputStream(trustCertPath),truestCertPwd == null?null:truestCertPwd.toCharArray());
tmf.init(trustKeyStore);
trustManagers = tmf.getTrustManagers();
}
使用证书库和信任库生成SSLContext:
sslContext.init(keyManagers, trustManagers, null);
下面的代码应该是为了匹配证书中域名和服务器域名不匹配问题:
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
其中get和post连接和发送参数的不同点就请自行看java文件。
笔者使用的是第二种,因为工程运行过程不是main执行,而且第二种方式可以省去手动添加信任库的过程。
下面是如何利用p12文件生成客户端证书库和生成服务器证书信任库的命令,使用的是keytool。
生成客户端证书库,其中changeit(123456)换成你自己的密码,payment.keystore是生成后的文件名,可以改成自己想要的文件名。
keytool -importkeystore -v -srckeystore client.p12 -srcstoretype pkcs12 -srcstorepass changeit(123456) -destkeystore payment.keystore -deststoretype jks -deststorepass changeit(123456)
生成服务器证书信任库
keytool -import -alias clientkey -file client.crt -keystore tserver.keystore
有了这些基本就完成了。
五、 证书关系图
简单点理解:
ca根秘钥生成证书后就隐藏身份了,只提供证书给外界。
下级证书都有ca证书参与。
下级有了证书后,根据自己所需和自己的秘钥生成对应证书文件。
结合之前的图,客户端有证书可以跟服务器的CA证书进行验证,服务器有证书跟客户端的信任库进行验证。