Android Https

http://frank-zhu.github.io/android/2014/12/26/android-https-ssl/

http://www.iyanran.com/blog/articles/2016/01/26/1453806487917.html

最近做android项目,用到了https的双向认证方面的知识,以为会很难,其实最后发现也挺简单的,记录如下。

一、HTTPS和HTTP的区别

1、https协议需要到ca申请证书,一般免费证书很少,需要交费。

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

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

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

二、SSL功能

1)客户对服务器的身份认证:
SSL服务器允许客户的浏览器使用标准的公钥加密技术和一些可靠的认证中心(CA)的证书,来确认服务器的合法性。

2)服务器对客户的身份认证:
也可通过公钥技术和证书进行认证,也可通过用户名,password来认证。

3)建立服务器与客户之间安全的数据通道:
SSL要求客户与服务器之间的所有发送的数据都被发送端加密、接收端解密,同时还检查数据的完整性。

SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层:

SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。

SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

三、生成密钥库和证书

可参考以下密钥生成脚本,根据实际情况做必要的修改,其中需要注意的是:服务端的密钥库参数“CN”必须与服务端的IP地址相同,否则会报错,客户端的任意。

1、生成服务器证书库
keytool -validity 3650 -genkey -v -alias server -keyalg RSA -keystore server.keystore -dname "CN=127.0.0.1,OU=fhpt,O=fhpt,L=Wuhan,ST=Hubei,c=cn" -storepass liujing -keypass liujing

2、生成客户端证书库
keytool -validity 3650 -genkeypair -v -alias client -keyalg RSA -storetype PKCS12 -keystore client.p12 -dname "CN=client,OU=fhpt,O=fhpt,L=Wuhan,ST=Hubei,c=cn" -storepass liujing -keypass liujing

3、从客户端证书库中导出客户端证书
keytool -export -v -alias client -keystore client.p12 -storetype PKCS12 -storepass liujing -rfc -file client.cer

4、从服务器证书库中导出服务器证书
keytool -export -v -alias server -keystore server.keystore -storepass liujing -rfc -file server.cer

5、生成客户端信任证书库(由服务端证书生成的证书库)
keytool -import -v -alias server -file server.cer -keystore client.truststore -storepass liujing

6、将客户端证书导入到服务器证书库(使得服务器信任客户端证书)
keytool -import -v -alias client -file client.cer -keystore server.keystore -storepass liujing

7、查看证书库中的全部证书
keytool -list -keystore server.keystore -storepass liujing

四、服务端tomcat配置

使用文本编辑器编辑${catalina.base}/conf/server.xml
找到Connector port="8443"的标签,取消注释,并修改成如下:

?


1

2

3

4

5

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"

     maxThreads="150" scheme="https" secure="true"

     clientAuth="true" sslProtocol="TLS"

     keystoreFile="${catalina.base}/key/server.keystore" keystorePass="123456"

     truststoreFile="${catalina.base}/key/server.keystore" truststorePass="123456"/>

keystoreFile:指定服务器密钥库,可以配置成绝对路径,如“D:/key/server.keystore”,本例中是在Tomcat目录中创建了一个名称为key的文件夹,仅供参考。
keystorePass:密钥库生成时的密码
truststoreFile:受信任密钥库,和密钥库相同即可
truststorePass:受信任密钥库密码

五、服务端web访问

服务器做如上的配置之后,web是没法访问的,因为服务器不信任客户端,所以必须把client.p12添加到本地的证书库中。双击client.p12即可。

添加之后,浏览器提示是否使用证书:

确定之后,正常访问web,不过浏览器并不信任服务器:

配置之后,本地可以访问服务器了,但是,本地的浏览器并不信任服务器,虽然也可以访问,但是还是不太完美,将server.cer添加到证书库中就可以了,至此,服务器的双向验证基本完成。

六、android端实现

通过上面的步骤生成的证书,客户端需要用到的是client.p12(客户端证书,用于请求的时候给服务器来验证身份之用)和client.truststore(客户端证书库,用于验证服务器端身份,防止钓鱼)这两个文件.其中安卓端的证书类型必须要求是BKS类型

一般客户端验证SSL有两种方式,一种是通过SSLSocketFactory方式创建,需要设置域名及端口号(适应于HttpClient请求方式),一种是通过SSLContext方式创建(适用于HttpsURLConnection请求方式)。

以下给出Android端的请求:

1、下面给出SslSocketFactory方式进行SSL认证的客户端代码

?

private static final String KEY_STORE_TYPE_BKS = "bks";//证书类型 固定值

private static final String KEY_STORE_TYPE_P12 = "PKCS12";//证书类型 固定值

private static final String KEY_STORE_CLIENT_PATH = "client.p12";//客户端要给服务器端认证的证书

private static final String KEY_STORE_TRUST_PATH = "client.truststore";//客户端验证服务器端的证书库

private static final String KEY_STORE_PASSWORD = "123456";// 客户端证书密码

private static final String KEY_STORE_TRUST_PASSWORD = "123456";//客户端证书库密码

/**

 * 获取SslSocketFactory

 *

 * @param context 上下文

 * @return SSLSocketFactory

 */

public static SSLSocketFactory getSslSocketFactory(Context context) {

    try {

        // 服务器端需要验证的客户端证书

        KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);

        // 客户端信任的服务器端证书

        KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_BKS);

        InputStream ksIn = context.getResources().getAssets().open(KEY_STORE_CLIENT_PATH);

        InputStream tsIn = context.getResources().getAssets().open(KEY_STORE_TRUST_PATH);

        try {

            keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());

            trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            try {

                ksIn.close();

            } catch (Exception ignore) {

            }

            try {

                tsIn.close();

            } catch (Exception ignore) {

            }

        }

        return new SSLSocketFactory(keyStore, KEY_STORE_PASSWORD, trustStore);

    } catch (KeyManagementException | UnrecoverableKeyException | KeyStoreException | FileNotFoundException | NoSuchAlgorithmException | ClientProtocolException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    }

    return null;

}

/**

 * 获取SSL认证需要的HttpClient

 *

 * @param context 上下文

 * @param port    端口号

 * @return HttpClient

 */

public static HttpClient getSslSocketFactoryHttp(Context context, int port) {

    HttpClient httpsClient = new DefaultHttpClient();

    SSLSocketFactory sslSocketFactory = getSslSocketFactory(context);

    if (sslSocketFactory != null) {

        Scheme sch = new Scheme("https", sslSocketFactory, port);

        httpsClient.getConnectionManager().getSchemeRegistry().register(sch);

    }

    return httpsClient;

}

2、下面给出SSLContext方式进行SSL认证的客户端代码

?

private static final String KEY_STORE_TYPE_BKS = "bks";//证书类型 固定值

    private static final String KEY_STORE_TYPE_P12 = "PKCS12";//证书类型 固定值

    private static final String KEY_STORE_CLIENT_PATH = "client.p12";//客户端要给服务器端认证的证书

    private static final String KEY_STORE_TRUST_PATH = "client.truststore";//客户端验证服务器端的证书库

    private static final String KEY_STORE_PASSWORD = "123456";// 客户端证书密码

    private static final String KEY_STORE_TRUST_PASSWORD = "123456";//客户端证书库密码

    

    /**

     * 获取SSLContext

     *

     * @param context 上下文

     * @return SSLContext

     */

    private static SSLContext getSSLContext(Context context) {

        try {

            // 服务器端需要验证的客户端证书

            KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);

            // 客户端信任的服务器端证书

            KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_BKS);

            InputStream ksIn = context.getResources().getAssets().open(KEY_STORE_CLIENT_PATH);

            InputStream tsIn = context.getResources().getAssets().open(KEY_STORE_TRUST_PATH);

            try {

                keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());

                trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());

            } catch (Exception e) {

                e.printStackTrace();

            } finally {

                try {

                    ksIn.close();

                } catch (Exception ignore) {

                }

                try {

                    tsIn.close();

                } catch (Exception ignore) {

                }

            }

            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(trustStore);

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");

            keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());

            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

            return sslContext;

        } catch (Exception e) {

            Log.e("tag", e.getMessage(), e);

        }

        return null;

    }

    

    /**

     * 获取SSL认证需要的HttpClient

     *

     * @param context 上下文

     * @return OkHttpClient

     */

    public static OkHttpClient getSSLContextHttp(Context context) {

        OkHttpClient client = new OkHttpClient();

        SSLContext sslContext = getSSLContext(context);

        if (sslContext != null) {

            client.setSslSocketFactory(sslContext.getSocketFactory());

        }

        return client;

    }

    

    /**

     * 获取HttpsURLConnection

     *

     * @param context 上下文

     * @param url     连接url

     * @param method  请求方式

     * @return HttpsURLConnection

     */

    public static HttpsURLConnection getHttpsURLConnection(Context context, String url, String method) {

        URL u;

        HttpsURLConnection connection = null;

        try {

            SSLContext sslContext = getSSLContext(context);

            if (sslContext != null) {

                u = new URL(url);

                connection = (HttpsURLConnection) u.openConnection();

                connection.setRequestMethod(method);//"POST" "GET"

                connection.setDoOutput(true);

                connection.setDoInput(true);

                connection.setUseCaches(false);

                connection.setRequestProperty("Content-Type", "binary/octet-stream");

                connection.setSSLSocketFactory(sslContext.getSocketFactory());

                connection.setConnectTimeout(30000);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return connection;

    }

七、bks文件的生成

keytool -importcert -v -trustcacerts -alias test -file client.cer -keystore test.bks -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath ./bcprov-jdk15on-146.jar -storepass liujing

至此,所有的步骤都介绍完成,bcprov-jdk15on-146.jar的下载链接点击这里

本网站的客户端证书和服务器信任证书下载地址

客户端证书     服务器证书

访问8443端口即可测试哦~

本文内容部分转载于:http://frank-zhu.github.io/android/2014/12/26/android-https-ssl/         http://www.blogjava.net/icewee/archive/2012/06/04/379947.html

时间: 2024-11-08 22:07:08

Android Https的相关文章

Android Https双向认证 + GRPC

keywords:android https 双向认证android GRPC https 双向认证 ManagedChannel channel = OkHttpChannelBuilder.forAddress("xxx",yyy) .overrideAuthority("zzz") .sslSocketFactory(sslFactory) .build(); 1.千万不要像官网案例那样设置setPlaintext(true),这个是设置明文,我们用的是密文

转 Android HTTPS详解

目录(?)[-] 前言 HTTPS原理 SSLTLS协议作用 基本的运行过程 握手阶段的详细过程 客户端发出请求ClientHello 服务器回应ServerHello 客户端回应 服务器的最后回应 握手结束 服务器基于Nginx搭建HTTPS虚拟站点 Android实现HTTPS通信 使用自定义证书并忽略验证的HTTPS连接方式 缺陷 使用自定义证书建立HTTPS连接 生成KeyStore 使用自定义KeyStore实现连接 参考文献 前言 最近有一个跟HTTPS相关的问题需要解决,因此花时间

【转】Android Https服务器端和客户端简单实例

转载地址:http://blog.csdn.net/gf771115/article/details/7827233 AndroidHttps服务器端和客户端简单实例 工具介绍 Eclipse3.7 Tomcat 6.0.18(免安装版) Android2.1开发环境(在Eclipse中配置好) 前提条件 JDK环境要使用我们自己安装的,笔者JDK安装目录为D:\Java\jdk1.6.0_22,在Eclipse的Window-preference-installedJREs中,只选用我们自己安

Android Https相关完全解析 当OkHttp遇到Https

转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/48129405: 本文出自:[张鸿洋的博客] 一.概述 其实这篇文章理论上不限于okhttp去访问自签名的网站,不过接上篇博文了,就叫这个了.首先要了解的事,okhttp默认情况下是支持https协议的网站的,比如https://www.baidu.com,https://github.com/hongyangAndroid/okhttp-utils等,你可以直接通过okhtt

Android HTTPS详解

前言 最近有一个跟HTTPS相关的问题需要解决,因此花时间学习了一下Android平台HTTPS的使用,同时也看了一些HTTPS的原理,这里分享一下学习心得. HTTPS原理 HTTPS(Hyper Text Transfer Protocol Secure),是一种基于SSL/TLS的HTTP,所有的HTTP数据都是在SSL/TLS协议封装之上进行传输的.HTTPS协议是在HTTP协议的基础上,添加了SSL/TLS握手以及数据加密传输,也属于应用层协议.所以,研究HTTPS协议原理,最终就是研

Android HTTPS如何10分钟实现自签名SSL证书

前言 去年公司内一个应用加了支付宝支付功能,为了保证安全,支付请求链接写成了https. 由于公司服务器使用的是的自签名证书,而在Android系统中自己签署的不能通过验证的,所以会抛出错误. 于是我网上查找了很多资料,也尝试过几种方法,过程都很繁琐,搞了一通宵都不行. 幸亏通过一个朋友找到了以下这个简便的开源库 https://github.com/lizhangqu/CoreUtil 然后用里面的SSLUtil,10分钟就解决了. 实现自签名SSL证书 1.访问以上开源库网址下载SSLUti

android https遇到自签名证书/信任证书

对于CA机构颁发的证书Okhttp默认支持 可以直接访问 但是对于自定义的证书就不可以了(如:https ://kyfw.12306.cn/otn/), 需要加入Trust 下面分两部分来写,一是信任所有证书,二是信任指定证书,访问自签名的网站 一.信任所有证书 1. 在你的module 下 build.gradule里添加 dependencies { ... compile 'com.zhy:okhttputils:2.6.2' ...} 2.新建MyApplication public c

Android + https 实现 文件上传

package com.example.wbdream.zigvine; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDataba

关于Android Https网络连接失败 我遇到最扯的一个问题

com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate: current time: Sun Jan 01 23:02:24 GMT+08:00 2012, validation time: We