https://www.jianshu.com/p/64172ccfb73b
一.单向认证:
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)服务端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,
服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
public class SafeOkHttp {
/**
* 访问在认证机构有认证的https访问
* @param hostHome 需要访问的地址
* @return OkHttpClient
*/
public static OkHttpClient getUnsafeOkHttpClient(final String hostHome) {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory).hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
if (hostname.equals(hostHome))
return true;
else
return false;
}
}).build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获取带有制定输入流的认证证书的 OkHttpClient
* 构造CertificateFactory对象,通过它的generateCertificate(is)方法得到Certificate。
* 然后讲得到的Certificate放入到keyStore中。
* 接下来利用keyStore去初始化我们的TrustManagerFactory
* 由trustManagerFactory.getTrustManagers获得TrustManager[]初始化我们的SSLContext
* 最后,设置我们mOkHttpClient.setSslSocketFactory即可。
* @param certificates 证书输入流
* @return OkHttpClient
* @throws Exception
*/
public static OkHttpClient setCertificates(final URL url,InputStream... certificates) throws Exception {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream is : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, factory.generateCertificate(is));
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
SSLContext sslContent = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContent.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(sslContent.getSocketFactory()).hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
if (hostname.equals(url.getHost())){
return true;
}
return false;
}
}).build();
return client;
}
/**
* 双向证书验证
* 详见:Https的证书生成和相关代码
* 获取带有制定输入流的认证证书的 OkHttpClient
* @param certificates 证书输入流
* @return OkHttpClient
* @throws Exception
*/
public static OkHttpClient setCertificatesTwo(final URL url,InputStream keyClient,String password,InputStream... certificates) throws Exception {
//初始化公钥
CertificateFactory factory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream is : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, factory.generateCertificate(is));
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//初始化Android端的密钥
KeyStore keyClientStore = KeyStore.getInstance("PKCS12");
keyClientStore.load(keyClient, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyClientStore,password.toCharArray());
// KeyManager keyManager = new MyX509KeyManager(keyClientStore,password,keyManagerFactory.getKeyManagers());
// KeyManager[] keyManagerArray = keyManagerFactory.getKeyManagers();
// keyManagerArray[0] = keyManager;
SSLContext sslContent = SSLContext.getInstance("TLS");
sslContent.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(sslContent.getSocketFactory()).hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
if (hostname.equals(url.getHost())){
return true;
}
return false;
}
}).build();
return client;
}
}
Java平台默认识别jks格式的证书文件,但是android平台只识别bks格式的证书文件
去Portecle下载Download portecle-1.9.zip (3.4 MB)。
解压后,里面包含bcprov.jar
文件,使用jave -jar bcprov.jar即可打开GUI界面