Android平台实现SSL单双向验证

标签: android 平台 ssl | 发表时间:2015-02-12 00:19 | 作者:u011467537
出处:http://blog.csdn.net

环境:服务器:apache服务器,openssl。

           客户端:PC、java平台、android平台。

思路:

1、先搞定ssl单向验证,再解决双向。

2、先PC,再java平台,再android,不一定非得这样,自由选择,个人是为了弄清整个流程,多走了些路。

过程步骤:

1、在pc上用apache搭建了一个http服务器,用openssl建立自签名的CA证书ca.crt,签发服务器证书server.crt,签发客户端证书client.crt。(apache+openssl配置ssl通信网上资料很多)

2、安装ca.crt,配置服务器,开启单向验证,用浏览器测试验证单向ssl通信。

3、将client.crt和client.key打包生成pkcs12格式的client.pfx文件。

4、配置服务器,开启双向验证,通过浏览器导入client.pfx文件,测试验证双向ssl通信。


重点:

Java平台默认识别jks格式的证书文件,但是android平台只识别bks格式的证书文件,需要在java中配置BC库,个人推荐参考: http://hi.baidu.com/yaming/item/980f253e17f585be124b142d,配置好BC库,看看有没有keytool工具,没有自己弄个Keytool工具

代码参考: http://momoch1314.iteye.com/blog/540613,由于服务端有apache,上面的代码就不用了,此处列出客户端,此文中是通过socket通信的,建议改成https通信,效果会更好,因为和apache服务器打交道,处理起来会更方便。(里面有些东西需要微调的,希望各位自己改一下,对于某些人来说,可能会有些坑,有疑问,请留言,本屌尽力解答)

  1. public class MySSLSocket extends Activity {  
  2.     private static final int SERVER_PORT = 50030;//端口号  
  3.     private static final String SERVER_IP = "218.206.176.146";//连接IP  
  4.     private static final String CLIENT_KET_PASSWORD = "123456";//私钥密码  
  5.     private static final String CLIENT_TRUST_PASSWORD = "123456";//信任证书密码  
  6.     private static final String CLIENT_AGREEMENT = "TLS";//使用协议  
  7.     private static final String CLIENT_KEY_MANAGER = "X509";//密钥管理器  
  8.     private static final String CLIENT_TRUST_MANAGER = "X509";//  
  9.     private static final String CLIENT_KEY_KEYSTORE = "BKS";//密库,这里用的是BouncyCastle密库  
  10.     private static final String CLIENT_TRUST_KEYSTORE = "BKS";//  
  11.     private static final String ENCONDING = "utf-8";//字符集  
  12.     private SSLSocket Client_sslSocket;  
  13.     private Log tag;  
  14.     private TextView tv;  
  15.     private Button btn;  
  16.     private Button btn2;  
  17.     private Button btn3;  
  18.     private EditText et;  
  19.       
  20.     /** Called when the activity is first created. */  
  21.     @Override  
  22.     public void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         setContentView(R.layout.main);  
  25.         tv = (TextView) findViewById(R.id.TextView01);  
  26.         et = (EditText) findViewById(R.id.EditText01);  
  27.         btn = (Button) findViewById(R.id.Button01);  
  28.         btn2 = (Button) findViewById(R.id.Button02);  
  29.         btn3 = (Button) findViewById(R.id.Button03);  
  30.           
  31.         btn.setOnClickListener(new Button.OnClickListener(){  
  32.             @Override  
  33.             public void onClick(View arg0) {  
  34.                 if(null != Client_sslSocket){  
  35.                     getOut(Client_sslSocket, et.getText().toString());  
  36.                     getIn(Client_sslSocket);  
  37.                     et.setText("");  
  38.                 }  
  39.             }  
  40.         });  
  41.         btn2.setOnClickListener(new Button.OnClickListener(){  
  42.             @Override  
  43.             public void onClick(View arg0) {  
  44.                 try {  
  45.                     Client_sslSocket.close();  
  46.                     Client_sslSocket = null;  
  47.                 } catch (IOException e) {  
  48.                     e.printStackTrace();  
  49.                 }  
  50.             }  
  51.         });  
  52.         btn3.setOnClickListener(new View.OnClickListener(){  
  53.             @Override  
  54.             public void onClick(View arg0) {  
  55.                 init();  
  56.                 getIn(Client_sslSocket);  
  57.             }  
  58.         });  
  59.     }  
  60.       
  61.     public void init() {  
  62.         try {  
  63.             //取得SSL的SSLContext实例  
  64.             SSLContext sslContext = SSLContext.getInstance(CLIENT_AGREEMENT);  
  65.             //取得KeyManagerFactory和TrustManagerFactory的X509密钥管理器实例  
  66.             KeyManagerFactory keyManager = KeyManagerFactory.getInstance(CLIENT_KEY_MANAGER);  
  67.             TrustManagerFactory trustManager = TrustManagerFactory.getInstance(CLIENT_TRUST_MANAGER);  
  68.             //取得BKS密库实例  
  69.             KeyStore kks= KeyStore.getInstance(CLIENT_KEY_KEYSTORE);  
  70.             KeyStore tks = KeyStore.getInstance(CLIENT_TRUST_KEYSTORE);  
  71.             //加客户端载证书和私钥,通过读取资源文件的方式读取密钥和信任证书  
  72.             kks.load(getBaseContext()  
  73.                     .getResources()  
  74.                     .openRawResource(R.drawable.kclient),CLIENT_KET_PASSWORD.toCharArray());  
  75.             tks.load(getBaseContext()  
  76.                     .getResources()  
  77.                     .openRawResource(R.drawable.lt_client),CLIENT_TRUST_PASSWORD.toCharArray());  
  78.             //初始化密钥管理器  
  79.             keyManager.init(kks,CLIENT_KET_PASSWORD.toCharArray());  
  80.             trustManager.init(tks);  
  81.             //初始化SSLContext  
  82.             sslContext.init(keyManager.getKeyManagers(),trustManager.getTrustManagers(),null);  
  83.             //生成SSLSocket  
  84.             Client_sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(SERVER_IP,SERVER_PORT);  
  85.         } catch (Exception e) {  
  86.             tag.e("MySSLSocket",e.getMessage());  
  87.         }  
  88.     }  
  89.           
  90.     public void getOut(SSLSocket socket,String message){  
  91.         PrintWriter out;  
  92.         try {  
  93.             out = new PrintWriter(  
  94.                     new BufferedWriter(  
  95.                             new OutputStreamWriter(  
  96.                                     socket.getOutputStream()  
  97.                                     )  
  98.                             ),true);  
  99.             out.println(message);  
  100.         } catch (IOException e) {  
  101.             e.printStackTrace();  
  102.         }  
  103.     }  
  104.       
  105.     public void getIn(SSLSocket socket){  
  106.         BufferedReader in = null;  
  107.         String str = null;  
  108.         try {  
  109.             in = new BufferedReader(  
  110.                     new InputStreamReader(  
  111.                             socket.getInputStream()));  
  112.             str = new String(in.readLine().getBytes(),ENCONDING);  
  113.         } catch (UnsupportedEncodingException e) {  
  114.             e.printStackTrace();  
  115.         } catch (IOException e) {  
  116.             e.printStackTrace();  
  117.         }  
  118.         new AlertDialog  
  119.         .Builder(MySSLSocket.this)  
  120.         .setTitle("服务器消息")  
  121.         .setNegativeButton("确定", null)  
  122.         .setIcon(android.R.drawable.ic_menu_agenda)  
  123.         .setMessage(str)  
  124.         .show();  
  125.     }  
  126. }  


单向:

1、用keytool将ca.crt导入到bks格式的证书库ca.bks,用于验证服务器的证书,命令如下:

keytool -import -alias ca -file ca.crt -keystore ca.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider

2、服务器配置成单向验证,将ca.bks放到android工程的assets或raw下,对应的读取就是代码中的

  1. kks.load(getBaseContext()  
  2.                     .getResources()  
  3.                     .openRawResource(R.drawable.kclient),CLIENT_KET_PASSWORD.toCharArray()); 

不一定是R.drawable.kclient,自己根据实际做修改,读取文件,不懂网上查,不啰嗦了。

至此,单向ssl通信应该是OK了。

(PS:  针对2中的操作不一定非得这么做,也可以把ca.bks导入到android平台下的cacerts.bks文件中,然后从这个文件读取认证,怎么导入,网上资料很多,如: http://blog.csdn.net/haijun286972766/article/details/6247675


调试中遇到的问题,提一下:

一般在模拟器中能通过,在真实平台上就没问题了。

这里需要注意的是证书的有效期,一定要在证书的有效期内操作

双向:

双向在单向的基础上实现,不过要先生成android平台能识别的客户端证书,这个玩意也伤脑筋,网上提到生成bks格式客户端证书的资料很少,鲜有借鉴之用。

在这个点上,太伤脑筋了,估计很多伙计也在这儿卡得蛋疼,一开始是毫无头绪,在PC、JAVA平台上生成客户端证书,都能测通,但是转到android平台就傻眼了,用keytool将其它工具生成的crt证书,导成bks格式,不通;用keytool工具新生成bks格式证书,也不通;

各种能想的方法试尽,一度怀疑自己是不是哪个细节出错了,理论上肯定能做的东西,怎么看不到一点可实现性,找资料连续几天,一点进展都没。

后面看国外的资料上提到先用openssl生成pkcs12的.pfx格式证书,然后用工具portecle转换成BKS格式,在android平台上使用,一开始是直接强制性转换,出错,怎么转都转不成功,但是转换成jks格式又没问题,只能根据提示错误,找解决方案,试了好多还是不行,又迷茫了;

1、最后看到国外的资料上的一句话,顿悟灵光,用portecle工具,先建立一个bks格式的keystore,然后将client.pfx中的key pair导入(import key pair),再保存bks文件,测试成功,事实证明:二了一点。

PS:用portecle直接转应该是可以的,只是我一直没转成功过,可能是我的java环境有问题,老提示illegal key size。

2、将服务器配置成双向验证,将ca.bks放到android工程的assets或raw下,对应的读取就是代码中的

  1. tks.load(getBaseContext()  
  2.                     .getResources()  
  3.                     .openRawResource(R.drawable.lt_client),CLIENT_TRUST_PASSWORD.toCharArray());
作者:u011467537 发表于2015/2/11 16:19:56 原文链接
阅读:0 评论:0 查看评论

相关 [android 平台 ssl] 推荐:

Android平台实现SSL单双向验证

- - CSDN博客推荐文章
环境:服务器:apache服务器,openssl.            客户端:PC、java平台、android平台. 1、先搞定ssl单向验证,再解决双向. 2、先PC,再java平台,再android,不一定非得这样,自由选择,个人是为了弄清整个流程,多走了些路. 1、在pc上用apache搭建了一个http服务器,用openssl建立自签名的CA证书ca.crt,签发服务器证书server.crt,签发客户端证书client.crt.

[Android]用WebView访问证书有问题的SSL网页

- - idv2
在WebView里加载SSL网页很正常,也没什么难度. 但如果要加载的SSL页面的证书有问题,比如过期、信息不正确、发行机关不被信任等,WebView就会拒绝加载该网页. PC上的浏览器会弹出证书错误的对话框,提示你是否要无视错误继续浏览. 实际上在WebView里也可以这样做,以实现加载证书有问题的页面.

nginx配置ssl

- - 邢红瑞的blog
先生成网关证书 ,仿照CA模式.

Android平台遗弃史

- fyits0 - Solidot
Google上周宣布了Android 4.0 Ice Cream Sandwich和Galaxy Nexus. 对第一代Nexus One用户来说,坏消息是他们的手机不能升级到ICS. 然而相比其它Android用户,他们则要幸运得多. Michael Degusta制作了一幅示意图,显示大部分Android手机运行的系统都较陈旧,远落后于最新版本,而且一到二年之后就不再有官方更新了.

SSL原理笔记

- - CSDN博客推荐文章
搜索SSL握手的原理,最多是网上转载的Alice和Bob的对话. 用生动、形象的场景说明了一个复杂SSL握手原理. 用公钥加密的数据只有私钥才能解密,相反的,用私钥加密的数据只有公钥才能解密,正是这种不对称性才使得公用密钥密码系统那么有用. 是一个验证身份的过程,目的是使一个实体能够确信对方是他所声称的实体.

SSL工作原理

- - 互联网 - ITeye博客
SSL 是一个安全协议,它提供使用 TCP/IP 的通信应用程序间的隐私与完整性. 超文本传输协议 (HTTP)使用 SSL 来实现安全的通信. 在客户端与服务器间传输的数据是通过使用对称算法(如 DES 或 RC4)进行加密的. 公用密钥算法(通常为 RSA)是用来获得加密密钥交换和数字签名的,此算法使用服务器的SSL数字证书中的公用密钥.

Android 4.0平台交互简析

- Shark - 互联网的那点事
Android4.0继承了3.0的设计多任务、丰富的通知、可定制的主界面、可调整大小的控件、用于交互和分享的互动性元素. 应用程序的结构日趋成熟,几乎没有发生变化,看看系统主要界面是如何体现其设计目标:简洁、美观和更为智能. 从解锁界面可以看出,4.0使用视觉化元素引导用户进行简单直观的手势操作,精致的动画和反馈增加了系统的互动参与感和趣味性,全新的字体提高了高分辨率界面的可读性,显得更为优雅和现代.

DroidVPN-Android平台的VPN应用软件

- - 无名小卒
       DroidVPN是一款在Android平台的VPN应用,可以解除封锁地区的互联网限制和匿名浏览网页,用户可以实现在手机和平板电脑上玩穿越,使用DroidVPN高级服务可能需要付费. 需要注意的是这个程序的apk文件只运行在有root的安卓设备上. 1、给你无限制的速度,但是有流量限制.

Mifare Classic Tool (MCT),Android平台RFID工具

- - FreebuF.COM
最近逛国外坛子的时候发现一新RFID神器,把介绍翻一下. Mifare Classic Tool (MCT) - 一款用来读取/编辑/分析Mifare Classic卡片的安卓软件. 大家好, 这几个月我开发了一款安卓NFC工具, 希望大家用的高兴, 同时希望大家能在github 上一同完善这款工具.

android平台消息推送机制

- - ITeye博客
方案1、使用GCM服务(Google Cloud Messaging). 简介:Google推出的云消息服务,即第二代的G2DM. 优点:Google提供的服务、原生、简单,无需实现和部署服务端. 缺点:Android版本限制(必须大于2.2版本),该服务在国内不够稳定、需要用户绑定Google帐号,受限于Google.