关于https的访问请求相关总结

来源:互联网 发布:天猫商城的网络推广 编辑:程序博客网 时间:2024/06/05 01:20

由于公司IOS的https改版要求,所以后台跟着做成了https的网络请求,因此有了Android的https改版需求。有看了鸿洋大神的文章,和一些相关的资料,总结https的知识如下

首先我们来说说https和http的差别:

参考文章: http://blog.csdn.net/mingli198611/article/details/8055261

我们的产品经理曾经戏称https和http请求,无非就是多了一个s的区别,仅此而已嘛,其实不然。就像文章中所说,http是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

请求方法(所有方法全为大写)有多种,各个方法的解释如下:
GET    请求获取Request-URI所标识的资源
POST    在Request-URI所标识的资源后附加新的数据
HEAD    请求获取由Request-URI所标识的资源的响应消息报头
PUT     请求服务器存储一个资源,并用Request-URI作为其标识
DELETE   请求服务器删除Request-URI所标识的资源
TRACE    请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT  保留将来使用
OPTIONS   请求查询服务器的性能,或者查询与资源相关的选项和需求

返回的代码状态:

状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx:指示信息--表示请求已接收,继续处理
2xx:成功--表示请求已被成功接收、理解、接受
3xx:重定向--要完成请求必须进行更进一步的操作
4xx:客户端错误--请求有语法错误或请求无法实现
5xx:服务器端错误--服务器未能实现合法的请求
常见状态代码、状态描述、说明:
200 OK      //客户端请求成功
400 Bad Request  //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 
403 Forbidden  //服务器收到请求,但是拒绝提供服务
404 Not Found  //请求资源不存在,eg:输入了错误的URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable  //服务器当前不能处理客户端的请求,一段时间后可能恢复正常

HTTPS(Hypertext Transfer Protocol over Secure Socket Layer,基于SSL的HTTP协议)使用了HTTP协议,但HTTPS使用不同于HTTP协议的默认端口及一个加密、身份验证层(HTTP与TCP之间)。

其实两者最大的不同,在于https的保密协议更严格,简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

HTTPS和HTTP的区别主要如下:

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

大概了解了http和https的区别之后,我们来说一下从Android方面,如何与https协议的服务器交互,调用接口数据,一般来说,大致有三种方式来处理:

一:在客户端默认信任所有的证书,这样做是可以调用加密https的接口数据,但是相对而言,也没有安全性可言,修改为https的作用也就不存在了,本身就是为了提高用户数据的安全性

  1. public class HttpsTestActivity extends Activity {  
  2.     /** Called when the activity is first created. */  
  3.     private TextView text;  
  4.     @Override  
  5.     public void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.main);  
  8.         text=(TextView)findViewById(R.id.textView1);  
  9.         GetHttps();  
  10.     }  
  11.       
  12.     private void GetHttps(){  
  13.         String https = "https://www.google.com.hk";  
  14.         try{  
  15.             SSLContext sc = SSLContext.getInstance("TLS");  
  16.             sc.init(nullnew TrustManager[]{new MyTrustManager()}, new SecureRandom());  
  17.             HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());  
  18.             HttpsURLConnection.setDefaultHostnameVerifier(new MyHostnameVerifier());  
  19.             HttpsURLConnection conn = (HttpsURLConnection)new URL(https).openConnection();  
  20.             conn.setDoOutput(true);  
  21.             conn.setDoInput(true);  
  22.             conn.connect();  
  23.             BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));   
  24.             StringBuffer sb = new StringBuffer();   
  25.             String line;   
  26.             while ((line = br.readLine()) != null)   
  27.                 sb.append(line);                  
  28.             text.setText(sb.toString());  
  29.            }catch(Exception e){  
  30.                 Log.e(this.getClass().getName(), e.getMessage());  
  31.            }        
  32.      }  
  33.   
  34.   
  35.   
  36.       private class MyHostnameVerifier implements HostnameVerifier{  
  37.             @Override  
  38.             public boolean verify(String hostname, SSLSession session) {  
  39.                     // TODO Auto-generated method stub  
  40.                     return true;  
  41.             }  
  42.   
  43.        }  
  44.   
  45.        private class MyTrustManager implements X509TrustManager{  
  46.             @Override  
  47.             public void checkClientTrusted(X509Certificate[] chain, String authType)  
  48.                             throws CertificateException {  
  49.                     // TODO Auto-generated method stub    
  50.             }  
  51.             @Override  
  52.             public void checkServerTrusted(X509Certificate[] chain, String authType)  
  53.   
  54.                             throws CertificateException {  
  55.                     // TODO Auto-generated method stub      
  56.             }  
  57.             @Override  
  58.             public X509Certificate[] getAcceptedIssuers() {  
  59.                     // TODO Auto-generated method stub  
  60.                     return null;  
  61.             }          
  62.   
  63.       }     
  64. }  

二:在客户端加入自己的证书,并且在访问时候提交服务器验证证书信息,调取接口,大致过程:

1、在自己的Assets文件下,加入证书的文件,一般后缀名为.CER的文件(证书文件)

2、在初始化的BaseApplication中,调用验证证书的方法

3、添加工具类,其中封装了关于https的请求的方法,关于httpsUtils的类,参考资料 http://www.jianshu.com/p/2f6ace079568

4、测试修改

public class HttpsUtil {


private static final String[] CERTIFICATES = new String[] { "xxx.cer" };


/**
* HttpUrlConnection支持Https验证(单向,足够满足大多数业务的需求)
* <p>
* 对安全有更高要求的业务如银行、金融等,需要双向验证,可自定义

* @param context
*/
public static void initHttpsUrlConnection(Context context) {
InputStream[] certificates = getCertificates(context, CERTIFICATES);
SSLSocketFactory sslSocketFactory = getSSLSocketFactory(certificates,
null, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
if (certificates == null) {
HttpsURLConnection
.setDefaultHostnameVerifier(getUnSafeHostnameVerifier());
}
}


/**
* 获取支持Https的OkHttpClient
* <p>
* 不需要的项目可注释

* @param context
* @return
*/
@SuppressWarnings("deprecation")
public static OkHttpClient getHttpsOkHttpClient(Context context) {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();


InputStream[] certificates = HttpsUtil.getCertificates(context,
CERTIFICATES);
SSLSocketFactory sslSocketFactory = HttpsUtil.getSSLSocketFactory(
certificates, null, null);
builder.sslSocketFactory(sslSocketFactory);
if (certificates == null) {
builder.hostnameVerifier(HttpsUtil.getUnSafeHostnameVerifier());
}
return builder.build();
}


/**
* 获取服务端证书
* <p>
* 默认放在Assets目录下

* @param context
* @return
*/
public static InputStream[] getCertificates(Context context,
String... fileNames) {
if (context == null || fileNames == null || fileNames.length <= 0) {
return null;
}
try {
InputStream[] certificates = new InputStream[fileNames.length];
for (int i = 0; i < fileNames.length; i++) {
certificates[i] = context.getAssets().open(fileNames[i]);
}
return certificates;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}


/**
* 获取自定义SSLSocketFactory
* <p>
* 单项验证时只需要certificates,其余null即可 双向验证时,3个参数均需要
* <p>
* 不验证,即信任所有证书时全部传null,同时配合getUnSafeHostnameVerifier() 有安全隐患,慎用!!!

* @param certificates
*            服务端证书(.crt)
* @param bksFile
*            客户端证书请求文件(.jsk -> .bks)
* @param password
*            生成jks时的密钥库口令
* @return
*/
public static SSLSocketFactory getSSLSocketFactory(
InputStream[] certificates, InputStream bksFile, String password) {
try {
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
if (trustManagers == null || trustManagers.length <= 0) {
trustManagers = new TrustManager[] { new UnSafeTrustManager() };
}
sslContext.init(keyManagers, trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new AssertionError(e);
}
}


private static TrustManager[] prepareTrustManager(
InputStream... certificates) {
if (certificates == null || certificates.length <= 0)
return null;
try {
CertificateFactory certificateFactory = 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++);
Certificate certificate = certificateFactory
.generateCertificate(is);
keyStore.setCertificateEntry(certificateAlias, certificate);
try {
if (is != null)
is.close();
} catch (IOException ignored) {
}
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
return trustManagerFactory.getTrustManagers();
// TODO: 2016/11/11 针对有效期异常导致校验失败的情况,目前没有完美的解决方案
// TrustManager[] keyStoreTrustManagers =
// trustManagerFactory.getTrustManagers();
// return getNotValidateTimeTrustManagers((X509TrustManager[])
// keyStoreTrustManagers);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


private static KeyManager[] prepareKeyManager(InputStream bksFile,
String password) {
try {
if (bksFile == null || password == null)
return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


private static NotValidateTimeTrustManager[] getNotValidateTimeTrustManagers(
X509TrustManager[] trustManagers) {
NotValidateTimeTrustManager[] notValidateTimeTrustManagers = new NotValidateTimeTrustManager[trustManagers.length];
for (int i = 0; i < trustManagers.length; i++) {
notValidateTimeTrustManagers[i] = new NotValidateTimeTrustManager(
trustManagers[i]);
}
return notValidateTimeTrustManagers;
}


/**
* 不校验证书有效期的TrustManager
* <p>
* 防止用户乱改手机时间导致校验失败 注意:由于校验证书时,对有效期的校验并不是最后一项,所以该TrustManager仍然存在安全隐患,并不推荐使用
*/
private static class NotValidateTimeTrustManager implements
X509TrustManager {


private X509TrustManager defaultTrustManager;


public NotValidateTimeTrustManager(X509TrustManager defaultTrustManager) {
this.defaultTrustManager = defaultTrustManager;
}


@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
defaultTrustManager.checkClientTrusted(chain, authType);
}


@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
e.printStackTrace();
Throwable t = e;
while (t != null) {
if (t instanceof CertificateExpiredException
|| t instanceof CertificateNotYetValidException)
return;
t = t.getCause();
}
throw e;
}
}


@Override
public X509Certificate[] getAcceptedIssuers() {
return defaultTrustManager.getAcceptedIssuers();
}
}


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 X509Certificate[] {};
}
}


private static class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}


/**
* 不验证,即信任所有证书时使用 有安全隐患,慎用!!!

* @return
*/
public static UnSafeHostnameVerifier getUnSafeHostnameVerifier() {
return new UnSafeHostnameVerifier();
}
}

(这里注意两个方法,适用于其他http网络jar包的验证方法initHttpsUrlConnection,适用于okhttp的验证方法getHttpsOkHttpClient)

三:关于https的双向认证,这样的情况,一般用于证书文件是自签名的文件,而且安全性相对要求较高的程序应用,需要适用双向认证,说白了,我的理解就是两个证书,一个客户端一个服务端,交互的时候互相认证,大致过程:

  • 构造CertificateFactory对象,通过它的generateCertificate(is)方法得到Certificate。
  • 然后讲得到的Certificate放入到keyStore中。
  • 接下来利用keyStore去初始化我们的TrustManagerFactory
  • trustManagerFactory.getTrustManagers获得TrustManager[]初始化我们的SSLContext
  • 最后,设置我们mOkHttpClient.setSslSocketFactory即可。
参考地址:http://blog.csdn.net/lmj623565791/article/details/48129405

另外几个相关的类的下载地址等待审核通过后更新在文章中,其中的工具lib类库是根据鸿杨项目修改的eclipse的lib类库,以及其中没有包含的在大神另外的一片文章中的OkHttpClientManager类,仅仅作为一个收集总结的文章,转载的相关资料已经标明


2 0
原创粉丝点击