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());


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM