• Android平台实现SSL单双向验证


    环境:服务器: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());
  • 相关阅读:
    UWP关于图片缓存的那些破事儿
    UWP中的文件相关操作
    数据结构-快速排序(C#实现)
    C#与Swift异步操作的差异
    Windows环境下使用Clover四叶草引导双硬盘安装OSX 10.11.5原版镜像
    Winform以任意角度旋转PictureBox中的图片的方法
    Xcode调用旧版本库出现Undefined symbols for architecture x86_64: ld: symbol(s) not found for architecture x86_64
    做WP程序时遇到的一些问题及解决方法
    WInform关闭程序的几种方法以及区别。
    显示在标题上的进度条
  • 原文地址:https://www.cnblogs.com/lechance/p/4373173.html
Copyright © 2020-2023  润新知