赞
踩
加密方式分两种,对称加密和非对称加密。这两种方式都有自己的优劣势, https中这两种方式都采用了。 我们约定S是服务端,C是客户端,客户端需要从服务端获取信息;
这种加密方式比较简单,就是双方都持有密匙。S和C都持有密匙, S通过密匙加密明文传递给C,C获取加密后的信息,用密匙解密信息。
常见的对称加密算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。
非对称加密中,S和C端都有自己的公钥和私钥。公钥是公开的,私钥是私有的,私钥需要保密的。 这套公钥和私钥的有个两种加密解密流程:
在https中信息传递的密匙的传递是采用非对称加密传递的.
C端需要把信息传递给S端, 需要分几步.
常用的非对称加密算法有RSA、Elgamal、Rabin、D-H、ECC(椭圆曲线加密算法)等。
问:既然对称加密和非对称加密都需要保密好自己的私钥, 那有什么区别呢?
对称加密中,私钥不仅需要自己知道也需要解密方知道。 这样私钥就有一个传递的流程, 这个流程就会有很大风险。 而非对称加密只需要自己保密好自己的私钥就好了。 公钥大家都知道,不需要保密,就少了一个私钥传递的过程。 少了很大的风险。
HTTPS为了兼顾安全与效率,同时使用了对称加密和非对称加密。
HTTPS通信流程
一个HTTPS请求实际上包含了两次HTTP传输,可以细分为8步。
1. 第一次HTTP请求:
1.客户端向服务器发起HTTPS请求,连接到服务器的443端口。
2.服务器端配置存放着权威CA机构颁发的证书,CA证书是用CA机构的私钥加密过的,里面包含服务器端提交给CA机构的公钥,网站域名、有效时长等等信息。服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人,此公钥包含在了加密过的CA证书里。
3.服务器端将自己配置在服务器上且加过密的CA证书发送给客户端。
4.客户端收到服务器端的CA证书之后,世界上的网站是无限多的,而CA机构总共就那么几家。任何正版操作系统都会将所有主流CA机构的公钥内置到操作系统当中,所以我们不用额外获取,解密时只需遍历系统中所有内置的CA机构的公钥,只要有任何一个公钥能够正常解密出数据,就说明它是合法的。客户端用CA机构的公钥对证书进行解密,获取到服务器端的公钥,对公钥进行检查,验证其合法性,如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。
2. 第二次HTTP请求:
1.客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。
2.服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
然后服务器将加密后的密文发送给客户端。
3.客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。
在https中需要证书,证书的作用是为了防止"中间人攻击"的。 如果有个中间人M拦截客户端请求,然后M向客户端提供自己的公钥,M再向服务端请求公钥,作为"中介者" 这样客户端和服务端都不知道,信息已经被拦截获取了。这时候就需要证明服务端的公钥是正确的.
怎么证明呢?
就需要权威第三方机构来公正了.这个第三方机构就是CA. 也就是说CA是专门对公钥进行认证,进行担保的,也就是专门给公钥做担保的担保公司。 全球知名的CA也就100多个,这些CA都是全球都认可的,比如VeriSign、GlobalSign等,国内知名的CA有WoSign。
不论什么平台,设备的操作系统中都会内置100多个全球公认的CA,说具体点就是设备中存储了这些知名CA的公钥。当客户端接收到服务器的数字证书的时候,会进行如下验证:
1.首先客户端会用设备中内置的CA的公钥尝试解密数字证书,如果所有内置的CA的公钥都无法解密该数字证书,说明该数字证书不是由一个全球知名的CA签发的,这样客户端就无法信任该服务器的数字证书。
2.如果有一个CA的公钥能够成功解密该数字证书,说明该数字证书就是由该CA的私钥签发的,因为被私钥加密的密文只能被与其成对的公钥解密。
3.除此之外,还需要检查客户端当前访问的服务器的域名是与数字证书中提供的“颁发给”这一项吻合,还要检查数字证书是否过期等。
证书链
一般CA不会直接去使用自己的私钥去签名某网站的证书, 一般CA会签发一个子证书, 然后用这子证书去签网站的证书. 有可能有多个子证书. 如果父证书是可以被信任的,那么这个子证书就是可以被信任的.
这里只讨论单向通信的情况并且是服务器自签名的证书,权威机构颁发过证书的https使用和普通http使用法师没有什么区别。
Android中使用Https一般有两种写法,
接口X509TrustManager有下述三个公有的方法需要我们实现:
⑴ void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。
⑵ void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。
⑶ X509Certificate[] getAcceptedIssuers() 返回受信任的X509证书数组。
主机域名验证
public static class SafeHostnameVerifier implements HostnameVerifier{ private String hostName; public SafeHostnameVerifier(String hostName){ this.hostName = hostName; } @Override public boolean verify(String hostname, SSLSession session) { // 使用Java自带的HttpsURLConnection // HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); // return hostnameVerifier.verify(this.hostName,session); if (hostname.equals(this.hostName)){ return true; }else { return false; } } }
这里是简单实现,完整的写法可以参看Okhttp的OkHostnameVerifier
证书信任管理
将assets文件夹中的服务器证书文件流转化成证书的方法
//拿到自己的证书
X509Certificate getX509Certificate(Context context) throws IOException, CertificateException {
InputStream in = context.getAssets().open("srca.cer");
CertificateFactory instance = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) instance.generateCertificate(in);
return certificate;
}
import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; public class MyX509TrustManager implements X509TrustManager { //如果需要对证书进行校验,需要这里去实现,如果不实现的话是不安全 X509Certificate mX509Certificate; public MyX509TrustManager(X509Certificate mX509Certificate) { this.mX509Certificate = mX509Certificate; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { for (X509Certificate certificate:chain){ //检查证书是否有效 certificate.checkValidity(); try { certificate.verify(mX509Certificate.getPublicKey()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } } } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }
这里需要注意下错误的写法
public static class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
// 没有验证服务器端的域名
return true;
}
}
private static class UnSafeTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 没有实现服务器端证书验证 @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]{}; } }
使用方式
URL url = new URL(path); //1.改成s HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); //2.SSLContext 初始化 SSLContext tls = SSLContext.getInstance("TLS"); MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context)); TrustManager[] trustManagers={myX509TrustManager}; tls.init(null,trustManagers,new SecureRandom()); //3.ssl工厂 SSLSocketFactory factory = tls.getSocketFactory(); //4.添加一个主机名称校验器 conn.setHostnameVerifier((new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); conn.setSSLSocketFactory(factory);
//1.SSLContext 初始化 SSLContext tls = SSLContext.getInstance("TLS"); MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context)); TrustManager[] trustManagers={myX509TrustManager}; tls.init(null,trustManagers,new SecureRandom()); //3.ssl工厂 SSLSocketFactory factory = tls.getSocketFactory(); OkHttpClient okhttpClient = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .retryOnConnectionFailure(true) //设置出现错误进行重新连接。 .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(60 * 1000, TimeUnit.MILLISECONDS) .sslSocketFactory(factory) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }) .build();
获取本地的服务器端自签名证书
public static SSLSocketFactory getSSlFactory(Context context) { // 从assets中加载证书,取到证书的输入流 InputStream is = getApplicationContext().getAssets().open("srca.cer"); // 证书工厂 CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate ca = cf.generateCertificate(is); // 加载证书到密钥库中 String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null); keyStore.setCertificateEntry("cert", ca); // 加载密钥库到信任管理器 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); // 用 TrustManager 初始化一个 SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, null); return sslContext.getSocketFactory(); }
若server使用的证书是由认证的CA机构颁发的,则如此配置即可(OkHttp等均已支持),或者根本不需要配置,就和普通http一样使用即可
private X509TrustManager systemDefaultTrustManager() { try { TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init((KeyStore) null); 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]; } catch (GeneralSecurityException e) { throw new AssertionError(); // The system has no TLS. Just give up. } } private SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) { try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[] { trustManager }, null); return sslContext.getSocketFactory(); } catch (GeneralSecurityException e) { throw new AssertionError(); // The system has no TLS. Just give up. } }
参考文章:
写一篇最好懂的HTTPS讲解
Android Https心得
android https简介和证书认证
android 使用https请求
深入理解 Android Https
Android安全开发之安全使用HTTPS
Android 使用 HTTPS
android HTTPS认证
Android使用https
Android 上 Https 双向通信— 深入理解KeyManager 和 TrustManagers
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。