赞
踩
说道HTTPS,不得不提HTTP,HTTP最大的缺陷就是明文传输,数据传输过程中很容易被篡改,所以美国网景公司提出来HTTPS协议,相对HTTP,HTTPS多了一个S,这个S,其实就是SSL/TSL,SSL全称安全套接字层,TSL1.0(传输层安全协议)是SSL3.0的升级版,是用于服务器和客户端加密通信的,所以可以认为两者是同一种协议,SSL因为自身的不安全性,在Android8.0已经被弃用了,以上可以看出HTTPS=HTTP+SSL/TLS
1.HTTPS发起SSL连接,链接到服务器的443端口
2.服务器向客户端发送公钥和数字证书
3.客户端通过随机算法生成私钥,然后通过服务器公钥对该私钥加密,生成对称密钥
4.客户端向服务器发送对称密钥
5.服务器通过对称密钥对数据进行加密
6.客户端通过对称密钥来对数据解密
那问题来了,在第二步,如果有好事者截获了服务器对客户端发送的公钥,然后伪造成服务器与客户端通信,这可如何是好呢,如何判断该公钥是合法的呢,数字证书就排上用场了
数字证书是由CA签发,全世界权威的CA一共100多个,数字证书里包含一对非对称密钥,公钥和私钥以及颁发给、颁发者等信息,里面的公钥对服务器端传输的公钥进行加密,生成密文,然后由客户端的数字证书里的私钥进行解密,从而获得服务器的公钥并确认该公钥是合法的
以浏览器的www.baidu.com为例,通过点击链接旁的锁标志来打开数字证书
证书路径展示的是证书链,根证书的权利最大,依次为根证书>A>B>C,如果www.baidu.com 信任了根证书,则意味着A、B、C都信任
通过详细信息可以看到公钥、公钥参数、证书策略等证书的详细信息 手机作为访问网络的客户端,当然也会有内置证书,以小米手机为例,通过设置-更多设置-系统安全-加密与凭据-信任的凭据来打开内置的CA证书
系统证书是内置的,个人证书是自定义的,比如charles的抓包证书是放在个人证书下面的,那Android是如何发起HTTPS请求的呢?
平常使用的抓包工具,无论是fidder和charles都能解析客户端和服务器的HTTPS数据,是如何做到的呢?其实抓包工具就充当了一个中间人代理的角色,参照2.https的工具原理,抓包的工作原理如下:
Android中如何访问HTTPS呢,其实Retrofit、OkHttp均支持HTTPS的访问 项目中引入网络库,以**implementation 'com.squareup.okhttp3:okhttp:4.2.0'**为例,
- final OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
- final Request request = new Request.Builder()
- .url("https://www.baidu.com/robots.txt")
- .build();
- final Response execute = okHttpClient.newCall(request).execute();
- final String bodyStr = execute.body().string();
- Log.d(TAG, bodyStr);
- 复制代码
那如果关闭客户端的CA证书,GlobalSign Root CA-R1,相当于不信任百度服务器的数字证书,会导致报错
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
通过手动将GlobalSign Root CA-R1.cer
放入项目中的assets文件夹,则可避免这一错误,如何引用项目中集成的证书呢? 通过
- SSLContext sslContext;
- try {
- InputStream inputStream = getAssets().open("");
- sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, new TrustManager[]{OkhttpU.trustManagerForCertificates(inputStream)}, null);
- SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
- OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, OkhttpU.trustManagerForCertificates(inputStream)).build();
- final Request request = new Request.Builder()
- .url("https://www.baidu.com/robots.txt")
- .build();
- final Response execute = okHttpClient.newCall(request).execute();
- final String bodyStr = execute.body().string();
- Log.d(TAG, bodyStr);
- } catch (Exception e) {
- e.printStackTrace();
- }
- 复制代码
- public class OkhttpU {
-
- public static 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];
- }
-
- private static 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);
- }
- }
- }
-
- 复制代码
就可以正常访问https了
综上,通过引入自定义证书,然后给OkHttp设置sslSocketFactory可以有效的防止抓包,但是.cer放到assets下很容易被反编译,可以通过jdk下的命令keytool -printcert -rfc -file srca.cer导出字符串,然后通过
- OkHttpClientManager.getInstance()
- .setCertificates(new Buffer()
- .writeUtf8(CER_STRING) //CER_STRING是到处的string常量
- .inputStream());
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。