转 Android HTTPS详解
前言
HTTPS原理
SSL/TLS协议作用
基本的运行过程
握手阶段的详细过程
客户端发出请求(ClientHello)
服务器回应(ServerHello)
客户端回应
服务器的最后回应
握手结束
服务器基于Nginx搭建HTTPS虚拟站点
Android实现HTTPS通信
- package com.example.photocrop;
- import java.io.BufferedReader;
- import java.io.InputStreamReader;
- import org.apache.http.HttpResponse;
- import org.apache.http.HttpStatus;
- import org.apache.http.StatusLine;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.client.methods.HttpUriRequest;
- import android.app.Activity;
- import android.os.AsyncTask;
- import android.os.Bundle;
- import android.os.AsyncTask.Status;
- import android.text.TextUtils;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- private Button httpsButton;
- private TextView conTextView;
- private CreateHttpsConnTask httpsTask;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- httpsButton = (Button) findViewById(R.id.create_https_button);
- httpsButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- runHttpsConnection();
- }
- });
- conTextView = (TextView) findViewById(R.id.content_textview);
- conTextView.setText("初始为空");
- }
- private void runHttpsConnection() {
- if (httpsTask == null || httpsTask.getStatus() == Status.FINISHED) {
- httpsTask = new CreateHttpsConnTask();
- httpsTask.execute();
- }
- }
- private class CreateHttpsConnTask extends AsyncTask<Void, Void, Void> {
- private static final String HTTPS_EXAMPLE_URL = "自定义";
- private StringBuffer sBuffer = new StringBuffer();
- @Override
- protected Void doInBackground(Void... params) {
- HttpUriRequest request = new HttpPost(HTTPS_EXAMPLE_URL);
- HttpClient httpClient = HttpUtils.getHttpsClient();
- try {
- HttpResponse httpResponse = httpClient.execute(request);
- if (httpResponse != null) {
- StatusLine statusLine = httpResponse.getStatusLine();
- if (statusLine != null
- && statusLine.getStatusCode() == HttpStatus.SC_OK) {
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new InputStreamReader(
- httpResponse.getEntity().getContent(),
- "UTF-8"));
- String line = null;
- while ((line = reader.readLine()) != null) {
- sBuffer.append(line);
- }
- } catch (Exception e) {
- Log.e("https", e.getMessage());
- } finally {
- if (reader != null) {
- reader.close();
- reader = null;
- }
- }
- }
- }
- } catch (Exception e) {
- Log.e("https", e.getMessage());
- } finally {
- }
- return null;
- }
- @Override
- protected void onPostExecute(Void result) {
- if (!TextUtils.isEmpty(sBuffer.toString())) {
- conTextView.setText(sBuffer.toString());
- }
- }
- }
- }
HttpUtils.java
- package com.example.photocrop;
- import org.apache.http.HttpVersion;
- import org.apache.http.client.HttpClient;
- import org.apache.http.conn.ClientConnectionManager;
- import org.apache.http.conn.scheme.PlainSocketFactory;
- import org.apache.http.conn.scheme.Scheme;
- import org.apache.http.conn.scheme.SchemeRegistry;
- import org.apache.http.conn.ssl.SSLSocketFactory;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
- import org.apache.http.params.BasicHttpParams;
- import org.apache.http.params.HttpProtocolParams;
- import org.apache.http.protocol.HTTP;
- import android.content.Context;
- public class HttpUtils {
- public static HttpClient getHttpsClient() {
- BasicHttpParams params = new BasicHttpParams();
- HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
- HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
- HttpProtocolParams.setUseExpectContinue(params, true);
- SchemeRegistry schReg = new SchemeRegistry();
- schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
- ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg);
- return new DefaultHttpClient(connMgr, params);
- }
- public static HttpClient getCustomClient() {
- BasicHttpParams params = new BasicHttpParams();
- HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
- HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
- HttpProtocolParams.setUseExpectContinue(params, true);
- SchemeRegistry schReg = new SchemeRegistry();
- schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schReg.register(new Scheme("https", MySSLSocketFactory.getSocketFactory(), 443));
- ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg);
- return new DefaultHttpClient(connMgr, params);
- }
- public static HttpClient getSpecialKeyStoreClient(Context context) {
- BasicHttpParams params = new BasicHttpParams();
- HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
- HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
- HttpProtocolParams.setUseExpectContinue(params, true);
- SchemeRegistry schReg = new SchemeRegistry();
- schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schReg.register(new Scheme("https", CustomerSocketFactory.getSocketFactory(context), 443));
- ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg);
- return new DefaultHttpClient(connMgr, params);
- }
- }
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <Button
- android:id="@+id/create_https_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/hello_world"
- android:textSize="16sp" />
- <TextView
- android:id="@+id/content_textview"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textSize="16sp" />
- </LinearLayout>
- schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
加入对HTTPS的支持,就可以有效的建立HTTPS连接了,例如“https://www.google.com.hk”了,但是访问自己基于Nginx搭建的HTTPS服务器却不行,因为它使用了不被系统承认的自定义证书,会报出如下问题:No peer certificate。
使用自定义证书并忽略验证的HTTPS连接方式
- package com.example.photocrop;
- import java.io.IOException;
- import java.net.Socket;
- import java.net.UnknownHostException;
- import java.security.KeyManagementException;
- import java.security.KeyStore;
- import java.security.KeyStoreException;
- import java.security.NoSuchAlgorithmException;
- import java.security.UnrecoverableKeyException;
- import java.security.cert.CertificateException;
- import java.security.cert.X509Certificate;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.TrustManager;
- import javax.net.ssl.X509TrustManager;
- import org.apache.http.conn.ssl.SSLSocketFactory;
- public class MySSLSocketFactory extends SSLSocketFactory {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- public MySSLSocketFactory(KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException,
- KeyStoreException, UnrecoverableKeyException {
- super(truststore);
- TrustManager tm = new X509TrustManager() {
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
- @Override
- public void checkServerTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {
- }
- @Override
- public void checkClientTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {
- }
- };
- sslContext.init(null, new TrustManager[] { tm }, null);
- }
- @Override
- public Socket createSocket() throws IOException {
- return sslContext.getSocketFactory().createSocket();
- }
- @Override
- public Socket createSocket(Socket socket, String host, int port,
- boolean autoClose) throws IOException, UnknownHostException {
- return sslContext.getSocketFactory().createSocket(socket, host, port,
- autoClose);
- }
- public static SSLSocketFactory getSocketFactory() {
- try {
- KeyStore trustStore = KeyStore.getInstance(KeyStore
- .getDefaultType());
- trustStore.load(null, null);
- SSLSocketFactory factory = new MySSLSocketFactory(trustStore);
- return factory;
- } catch (Exception e) {
- e.getMessage();
- return null;
- }
- }
- }
同时,需要修改DefaultHttpClient的register方法,改为自己构建的sslsocket:
- public static HttpClient getCustomClient() {
- BasicHttpParams params = new BasicHttpParams();
- HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
- HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
- HttpProtocolParams.setUseExpectContinue(params, true);
- SchemeRegistry schReg = new SchemeRegistry();
- schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schReg.register(new Scheme("https", MySSLSocketFactory.getSocketFactory(), 443));
- ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg);
- return new DefaultHttpClient(connMgr, params);
- }
这样就可以成功的访问自己构建的基于Nginx的HTTPS虚拟站点了。
缺陷:
使用自定义证书建立HTTPS连接
生成KeyStore
要验证自定义证书,首先要把证书编译到应用中,这需要使用keytool工具生产KeyStore文件。这里的证书就是指目标服务器的公钥,可以从web服务器配置的.crt文件或.pem文件获得。同时,你需要配置bouncycastle,我下载的是bcprov-jdk16-145.jar,至于配置大家自行google就好了。
- keytool -importcert -v -trustcacerts -alias example -file www.example.com.crt -keystore example.bks -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /home/wzy/Downloads/java/jdk1.7.0_60/jre/lib/ext/bcprov-jdk16-145.jar -storepass pw123456
运行后将显示证书内容并提示你是否确认,输入Y回车即可。
生产KeyStore文件成功后,将其放在app应用的res/raw目录下即可。
使用自定义KeyStore实现连接
- package com.example.photocrop;
- import java.io.IOException;
- import java.io.InputStream;
- import java.security.KeyManagementException;
- import java.security.KeyStore;
- import java.security.KeyStoreException;
- import java.security.NoSuchAlgorithmException;
- import java.security.UnrecoverableKeyException;
- import org.apache.http.conn.ssl.SSLSocketFactory;
- import android.content.Context;
- public class CustomerSocketFactory extends SSLSocketFactory {
- private static final String PASSWD = "pw123456";
- public CustomerSocketFactory(KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException,
- KeyStoreException, UnrecoverableKeyException {
- super(truststore);
- }
- public static SSLSocketFactory getSocketFactory(Context context) {
- InputStream input = null;
- try {
- input = context.getResources().openRawResource(R.raw.example);
- KeyStore trustStore = KeyStore.getInstance(KeyStore
- .getDefaultType());
- trustStore.load(input, PASSWD.toCharArray());
- SSLSocketFactory factory = new CustomerSocketFactory(trustStore);
- return factory;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- } finally {
- if (input != null) {
- try {
- input.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- input = null;
- }
- }
- }
- }
同时,需要修改DefaultHttpClient的register方法,改为自己构建的sslsocket:
- public static HttpClient getSpecialKeyStoreClient(Context context) {
- BasicHttpParams params = new BasicHttpParams();
- HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
- HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
- HttpProtocolParams.setUseExpectContinue(params, true);
- SchemeRegistry schReg = new SchemeRegistry();
- schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schReg.register(new Scheme("https", CustomerSocketFactory.getSocketFactory(context), 443));
- ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg);
- return new DefaultHttpClient(connMgr, params);
- }
转 Android HTTPS详解的更多相关文章
- Android ConstraintLayout详解(from jianshu)
Android ConstraintLayout详解 https://www.jianshu.com/p/a8b49ff64cd3 1. 概述 在本篇文章中,你会学习到有关Constraint ...
- 最全面的Android Webview详解
转自:最全面的Android Webview详解 前言 现在很多App里都内置了Web网页(Hyprid App),比如说很多电商平台,淘宝.京东.聚划算等等,如下图 那么这种该如何实现呢?其实这是 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
- Android Notification 详解——基本操作
Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...
- 公钥与私钥,HTTPS详解
1.公钥与私钥原理1)鲍勃有两把钥匙,一把是公钥,另一把是私钥2)鲍勃把公钥送给他的朋友们----帕蒂.道格.苏珊----每人一把.3)苏珊要给鲍勃写一封保密的信.她写完后用鲍勃的公钥加密,就可以达到 ...
- Android ActionBar详解
Android ActionBar详解 分类: Android2014-04-30 15:23 1094人阅读 评论(0) 收藏 举报 androidActionBar 目录(?)[+] 第4 ...
- Android 签名详解
Android 签名详解 AndroidOPhoneAnt设计模式Eclipse 在Android 系统中,所有安装 到 系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程 ...
- Android编译系统详解(一)
++++++++++++++++++++++++++++++++++++++++++ 本文系本站原创,欢迎转载! 转载请注明出处: http://blog.csdn.net/mr_raptor/art ...
- Android布局详解之一:FrameLayout
原创文章,如有转载,请注明出处:http://blog.csdn.net/yihui823/article/details/6702273 FrameLayout是最简单的布局了.所有放在布局里的 ...
随机推荐
- mahout与nosql的两幅经典图形
- 初次使用IntelliJ IDEA 2016.2
换电脑的还有一个目的就是我准备采用新的IDE了 之前一直用的是myeclipse,但是现在准备尝试idea 这边做个记录,idea的破解参考下面这个网址:http://blog.csdn.net/u0 ...
- php数据排序---array_multisort
PHP代码 <?php $ar1 = array(10, 100, 100, 0); $ar2 = array(1, 3, 2, 4); array_multisort($ar1, $ar2); ...
- postgres-xl 集体搭建(1)
安装并编辑脚本 cd /opt/curl -O http://files.postgres-xl.org/postgres-xl95r1beta1.tar.gztar -zxvf postgres-x ...
- mysql中AES_ENCRYPT、AES_DNCRYPT及CONVERT的用法
这两天在弄公司的服务端项目的时候,发现mysql比较实用的3个函数,在这里给大家分享一下. 1.AES_ENCRYPT函数,这个函数的使用场景是对一些安全性要求比较高的数据使用AES加密,语法为: A ...
- js截取文件名
str = 'C:\fakepath\ll.doc'; str.substring(str.lastIndexOf("\\")+1,str.lastIndexOf(".& ...
- php的memcache和memcached扩展区别【转载】
老生长谈的问题了.我这里就整理一下. memcache的文档在:http://pecl.php.net/package/memcache memcached的文档在:http://pecl.php.n ...
- Eclipse如何自定义format代码
- AngularJS中如果ng-src 图片加载失败怎么办
我们知道AngularJS加载图片的方法是用技术分享加ng-src标签,例如: <img ng-src="{{currentUrl}}"/> 其中currentUrl为 ...
- sql语句操作表
"create table mytable (m_id integer identity(1,1) primary key,m_class varchar(50) not null defa ...