转 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是最简单的布局了.所有放在布局里的 ...
随机推荐
- 重识 CSS
- ios设置textField只能输入数字用于电话号码
首先在.xib中将UITextField的Keyboard设置为Number Pad,但是使用时键盘会切回别的键盘无法对内容进行校验.通过神奇的百度我知道了通过以下方法可以解决这样的问题: 首先让.x ...
- Windows线程同步(下)
线程同步三:事件 CreateEvent:Creates or opens a named or unnamed event object. HANDLE WINAPI CreateEvent( _I ...
- ios中的关键词retain release
内存分析 在函数中只要用new alloc copy 这样的分配空间时 则计算器retain就要为一 每调用一次就要加一 在.m文件中引用手动计数时 一定要调用[super dealloc]这 ...
- Ansible3:ansible.cfg配置说明【转】
Ansible默认安装好后有一个配置文件/etc/ansible/ansible.cfg,该配置文件中定义了ansible的主机的默认配置部分,如默认是否需要输入密码.是否开启sudo认证.actio ...
- 依赖注入(DI)和控制反转(IOC)【回顾】
在java开发中广泛的使用了IOC的思想,在PHP中同样也在广泛使用. interface Coder { public function coding(); } 实现类Javaer class Ja ...
- Linq中max min sum avarage count的使用
一.Max最大值 static void Main(string[] args) { //Max求最大值 ,,,,,,,,,}; //方法1 Linq语句+Linq方法 var result = (f ...
- 让您的Xcode键字如飞
手指在键盘上飞速跳跃,终端上的代码也随着飞舞,是的这确实很酷.优秀的程序员总是这么一群人,他们不拘于现状,不固步自封,他们喜欢新奇的事,他们把自己发挥到极致. 指法攻略 放下您钟爱的鼠标吧,在前行之中 ...
- 怎样让pl sql developer 界面视图复位
tools->preferences->user interface->appearance->reset docking工具-首选项-用户界面-外观-复位停放
- hash随笔
hash属性是一个可读可写的字符串,是url的锚部分(从#开始).多用于单页面应用中,使其包含多个页面. 定位:通过id来定位 eg: <div id= "part1"> ...