网络请求是我们日常工作中比不可少的部分,http一般大家都熟练掌握,但https可能有不少同学有一些疑惑。先结合我在使用https的过程中遇到的问题来浅析一下https。主要问题有以下几点:
1.SSL和TLS的区别:
SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。
TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。
SSL于2014年4月8日曝出严重的安全漏洞。这个漏洞使攻击者能够从内存中读取多达64 KB的数据。所以我们可以简单的认为TLS是SSL的升级版本。
2.https请求过程中客户端和服务端的握手
首先来看一组https请求的抓包:
从上图可以清晰的看出客户端和服务端的握手过程:
1.客户端向服务器发送一个Client Hello的请求,请求中包含一个随机数以及协议版本号等信息,服务器会保存这个随机数;
2.服务器收到请求后,会发送一个server Hello响应给客户端,响应信息也包含一个随机数,此时服务器和客户端都有两个随机数;
3.服务器会继续发送一个证书给客户端,客户端拿到证书后会做一下几个动作:
① 客户端拿到证书后会提取证书中的公钥;
② 客户端拿到公钥后会随机生成一个随机数,并且会用公钥对随机数加密;
4.客户端会把加密后的随机数发送给服务器,服务器用私钥解密也获取到随机数;这样客户端和服务端都有3个随机数,并且用这三个随机数生成发送数据的会话密码;
5.客户端和服务器就可以用会话密码进行数据交互;
通过上面的分析,我们再用一个图来描述,让大家能更形象的理解:
3.单向验证与双向认证
单向认证:简单可以理解为客户端信任服务器端的证书,服务器不用验证客户端;
双向认证:客户端信任服务端证书,服务端也信任客户端证书;
4.加载自制证书和使用默认预置证书的代码
① 使用默认的预置证书
@Test
public void doHttps()throws Exception{
String path ="https://restapi.amap.com/v3/weather/weatherInfo?city=深圳&key=13eb58f5884f9749287abbead9c658f2";
HttpUrl httpUrl =new HttpUrl(path);
//如果证书是Android手机已经预置的则只需要一下操作:
SSLContext sslContext = SSLContext.getDefault();
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
Socket socket = sslSocketFactory.createSocket(httpUrl.getHost(), httpUrl.getPort());
final InputStream ins = socket.getInputStream();
OutputStream os = socket.getOutputStream();
new Thread(){
@Override
public void run() {
//获取服务器数据
BufferedReader br =new BufferedReader(new InputStreamReader(ins));
String line;
try{
while ((line = br.readLine()) !=null){
System.out.println(line);
}
}catch (Exception e){
e.printStackTrace();
}
}
}.start();
os.write(("GET " + httpUrl.geFile() +" http/1.1\r\n").getBytes());
os.write(("Host: " + httpUrl.geFile() +"\r\n\r\n").getBytes());
os.flush();
//单元测试不会等线程
while (true){}
}
②自制证书加载的过程:
//@Test
public void doCustomHttps()throws Exception{
String path ="http://restapi.amap.com/v3/weather/weatherInfo?city=深圳&key=13eb58f5884f9749287abbead9c65";
HttpUrl httpUrl =new HttpUrl(path);
//自制证书加载的过程:
SSLContext sslContext = SSLContext.getInstance("TLS");
//信任管理工厂
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
//证书工厂
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
FileInputStream fis =new FileInputStream("D:\\xx.cer");
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(fis);
keyStore.setCertificateEntry("xinlian", certificate);
trustManagerFactory.init(keyStore);
//第一个参数为客服端证书 第二个参数为服务器证书 第三个参数是随机数
//此处我们为单向认证所以第一个参数不用传
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
Socket socket = sslSocketFactory.createSocket(httpUrl.getHost(), httpUrl.getPort());
final InputStream ins = socket.getInputStream();
OutputStream os = socket.getOutputStream();
new Thread(){
@Override
public void run() {
//获取服务器数据
BufferedReader br =new BufferedReader(new InputStreamReader(ins));
String line;
try{
while ((line = br.readLine()) !=null){
System.out.println(line);
}
}catch (Exception e){
e.printStackTrace();
}
}
}.start();
os.write(("GET " + httpUrl.geFile() +" http/1.1\r\n").getBytes());
os.write(("Host: " + httpUrl.geFile() +"\r\n\r\n").getBytes());
os.flush();
//单元测试不会等线程
while (true){}
}
以上就是对https的一点理解,如果有不正确的地方请大家指正,谢谢!