一.简介
1.HTTPS定义
HTTPS全称为Hyper Text Transfer Protocol over Secure Socket Layer或是Hypertext Transfer Protocol Secure
中文含义为“超文本传输安全协议” 。是以安全为目标的HTTP通道。在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性 。HTTPS 在HTTP 的基础下加入SSL,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。 HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP与 TCP 之间)。这个系统提供了身份验证与加密通讯方法。它被广泛用于万维网上安全敏感的通讯,例如交易支付等方面 。
2.HTTPS改进目标
HTTPS 协议是由 HTTP 加上 TLS/SSL 协议构建的可进行加密传输、身份认证的网络协议,主要通过数字证书、加密算法、非对称密钥等技术完成互联网数据传输加密,实现互联网传输安全保护。设计目标主要有三个。
<1> 数据保密性:保证数据内容在传输的过程中不会被第三方查看。就像快递员传递包裹一样,都进行了封装,别人无法获知里面装了什么 。
<2> 数据完整性:及时发现被第三方篡改的传输内容。就像快递员虽然不知道包裹里装了什么东西,但他有可能中途掉包,数据完整性就是指如果被掉包,我们能轻松发现并拒收 。
<3> 身份校验安全性:保证数据到达用户期望的目的地。就像我们邮寄包裹时,虽然是一个封装好的未掉包的包裹,但必须确定这个包裹不会送错地方,通过身份校验来确保送对了地方。
3.HTTPS的工作原理
<1> 双向的身份认证
客户端和服务端在传输数据之前,会通过基于X.509证书对双方进行身份认证 。
(1) 客户端发起 SSL 握手消息给服务端要求连接。
(2) 服务端将证书发送给客户端。
(3) 客户端检查服务端证书,确认是否由自己信任的证书签发机构签发。 如果不是,将是否继续通讯的决定权交给用户选择 ( 注意,这里将是一个安全缺陷 )。如果检查无误或者用户选择继续,则客户端认可服务端的身份。
(4) 服务端要求客户端发送证书,并检查是否通过验证。失败则关闭连接,认证成功则从客户端证书中获得客户端的公钥,一般为1024位或者 2048位。到此,服务器客户端双方的身份认证结束,双方确保身份都是真实可靠的。
<2> 数据传输的机密性
客户端和服务端在开始传输数据之前,会协商传输过程需要使用的加密算法。 客户端发送协商请求给服务端, 其中包含自己支持的非对称加密的密钥交换算法 ( 一般是RSA),,数据签名摘要算法 ( 一般是SHA或者MD5) ,加密传输数据的对称加密算法 ( 一般是DES),以及加密密钥的长度。 服务端接收到消息之后,选中安全性最高的算法,并将选中的算法发送给客户端,完成协商。
客户端生成随机的字符串,通过协商好的非对称加密算法,使用服务端的公钥对该字符串进行加密,发送给服务端。 服务端接收到之后,使用自己的私钥解密得到该字符串。在随后的数据传输当中,使用这个字符串作为密钥进行对称加密 。
二.代码实现
1.使用okhttp实现https请求
实现https我们需要一份ca证书,购买的证书,格式为.pfx,带有公钥和私钥,附带一个密码。还有一种格式为.cer的证书,这种证书是没有私钥的。 服务器会将证书配置到tomcat中,客户端则存放在本地,app启动的时候加载进去。
.pfx格式和.cer格式的区别:
<1> 带有私钥的证书
由Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形式,以pfx作为证书文件后缀名。
<2> 二进制编码的证书
证书中没有私钥,DER 编码二进制格式的证书文件,以cer作为证书文件后缀名。
<3> Base64编码的证书
证书中没有私钥,BASE64 编码格式的证书文件,也是以cer作为证书文件后缀名。
2.实现需要
OkHttp配置HTTPS访问。需要操作以下三个类
<1> SSLSocketFactory:ssl套接字工厂。
<2> X509TrustManager:证书信任器管理类。
<3> HostnameVerifier:验证主机名。
3.代码实现
OkHttp设置
mOkHttpClient=new OkHttpClient.Builder()
.connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//连接时间
.readTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//读时间
.writeTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//写时间
.retryOnConnectionFailure(true)//连接失败后是否重新连接
.sslSocketFactory(sslSocketFactory,trustManager)//Https证书
.build();
即:.sslSocketFactory(sslSocketFactory,trustManager)//Https证书
通过sslSocketFactory方法设置https证书
获取 SSLSocketFactory和 X509TrustManager 对象
下载相关证书放到asset目录下
代码
public class MainApplication extends Application {
private static SSLSocketFactory sslSocketFactory;
private static X509TrustManager x509TrustManager;
@Override
public void onCreate() {
super.onCreate();
initx509TrustManagerAndsslSocketFactory();
}
/**
* 初始化
* */
private void initx509TrustManagerAndsslSocketFactory(){
InputStream inputStream = null;
try {
inputStream = getApplicationContext().getAssets().open("srca.cer"); //得到证书的输入流
try {
if(null!=inputStream){
x509TrustManager = trustManagerForCertificates(inputStream);//以流的方式读入证书
if(null!=x509TrustManager){
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
}
}
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=inputStream){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MainApplication.setSslSocketFactory(sslSocketFactory);
MainApplication.setX509TrustManager(x509TrustManager);
}
/**
* 以流的方式添加信任证书
*/
private X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
// Put the certificates a key store.
char[] password = "password".toCharArray(); // Any password will work.
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
// Use it to build an X509 trust manager.
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}
/**
* 添加password
* @param password
* @return
* @throws GeneralSecurityException
*/
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // 这里添加自定义的密码,默认
InputStream in = null; // By convention, 'null' creates an empty key store.
keyStore.load(in, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static SSLSocketFactory getSslSocketFactory() {
return sslSocketFactory;
}
public static void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
MainApplication.sslSocketFactory = sslSocketFactory;
}
public static X509TrustManager getX509TrustManager() {
return x509TrustManager;
}
public static void setX509TrustManager(X509TrustManager x509TrustManager) {
MainApplication.x509TrustManager = x509TrustManager;
}
}
HostnameVerifier验证
public class OkHttpHostnameVerifier implements HostnameVerifier {
/**
* OkHttp验证主机名
*
* @return false 不信任主机
* 默认 false
*/
@Override
public boolean verify(String hostname, SSLSession session) {
/**
* 这里验证主机名是XXX.XXX.com就返回true。信任这个域名。也可以结合白名单等动态校验。
* */
if ("XXX.XXX.com".equals(hostname)) {
return true;
} else {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(hostname, session);
}
}
}
添加HostnameVerifier验签
//创建OkHttpClient对象
mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)//连接时间
.readTimeout(20, TimeUnit.SECONDS)//读时间
.writeTimeout(20, TimeUnit.SECONDS)//写时间
.retryOnConnectionFailure(true)//连接失败后是否重新连接
.dns(new OkHttpDNS())//DNS域名解析
.eventListener(new OkHttpEventListener())//EventListener监听 可以统计各种时间
.hostnameVerifier(new OkHttpHostnameVerifier())//验签
.build();
三.OkHttp防止抓包
1.描述
客户端请求接口数据时,可以使用一些apk完成抓包操作。会获取我们请求的参数和请求成功后的报文。常见的抓包工具有HttpCanary和Fiddler。那么我们在使用OkHttp时,如何防止被这些apk转包呢?其实OkHttp已经为我们提供了相应的方法。
2.原理
OkHttp使用ProxySelector来获取代理信息,在构造OkHttpClient时是可以设置的,其默认值是ProxySelector.getDefault(),该默认值会反应出系统的代理信息。
那么我们就可以提供自己的ProxySelector实现来达到绕过系统代理的能力。
3.代码
//配置okHttp的参数
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(nettimeout, TimeUnit.SECONDS)
//proxySelector方法设置防止抓包
.proxySelector(new ProxySelector() {
@Override
public List<Proxy> select(URI uri) {
return Collections.singletonList(Proxy.NO_PROXY);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
}
})
.build();
注:这里只是测试,用的12306以前的证书。现在的12306已经不需要这个证书了,使用OkHttp不用配置sslSocketFactory也能访问。