歡迎您光臨本站 註冊首頁

J2ME使用Socket通過cmwap接入點訪問安全HTTPS

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  這個問題是在我升級J2ME版XHTML瀏覽器的時候被引入的,我嘗試了很多方法、發了很多帖子、問了很多人都沒有找到可行的解決方案.最后我在PC上通用OTA連接方式使用WireShark攔截數據包才解決了問題,接下來我和大家分享一下解決問題的過程.

  所涉及的知識點:

  1、如何使用J2ME連接cmwap代理

  2、基於安全套接字的HTTPS

  嘗試方法一:

  新建一個Midlet應用程序,在StartApp方法中加入如下代碼:


1 HttpConnection conn = null;
  2 InputStream is = null;
  3 try {
  4 // url: https://ebs.95559.com.cn/corporbank/es_logon.jsp
  5 conn = (HttpConnection) Connector.open("http://10.0.0.172:80/corporbank/es_logon.jsp", Connector.READ, true);
  6 String host = "ebs.95559.com.cn";
  7 conn.setRequestProperty("x-online-host", host);
  8 conn.setRequestMethod(HttpConnection.GET);
  9
  10 int code = conn.getResponseCode();
  11 System.out.println("Response Code: " code);
  12
  13 is = conn.openDataInputStream();
  14
  15 System.out.println("Response Stream:");
  16 byte[] buf = new byte[128];
  17 while (true) {
  18 int availSize = is.read(buf, 0, buf.length);
  19 if (availSize == -1) {
  20 break;
  21 }
  22 System.out.println(new String(buf, 0, availSize));
  23 }
  24 } catch (Exception ex) {
  25 ex.printStackTrace();
  26 } finally {
  27 if (is != null) {
  28 try {
  29 is.close();
  30 } catch (IOException ex) {
  31 ex.printStackTrace();
  32 }
  33 }
  34
  35 if (conn != null) {
  36 try {
  37 conn.close();
  38 } catch (IOException ex) {
  39 ex.printStackTrace();
  40 }
  41 }
  42 }
  43

  此代碼的作用是:使用cmwap代理,請求我們制定的頁面,並在控制台中輸出返回的狀態碼及內容.很顯然,這是一種普通的cmwap連接方式,並沒有考慮https因素的加入,輸出的結果也在意料之內:


Response Code: 502
Response Stream:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml><card>
<p><do type="prev" name="Back" label="Back"><prev/></do>Connection to host failed. Check your settings and try again. If the pro
blem persists contact your operator.</p></card></wml>

  原因分析:我們要請求的是安全HTTP,埠443,而在我們的代碼中並沒有體現出我們要連接HTTPS的意圖,我們只是告訴了cmwap代理我們要連接地址「ebs.95559.com.cn/corporbank/es_logon.jsp」,而並沒有告訴cmwap代理我們所要訪問的地址是基於HTTPS的,cmwap代理會按照地址「http://ebs.95559.com.cn/corporbank/es_logon.jsp」去連接,恰好這個地址是不支持HTTP:80的,cmwap代理返回了網關錯誤.

  嘗試方法二:

  針對以上設想,我們將代碼修改一下,想方設法讓cmwap代理知道我們要訪問的目標地址屬於HTTPS:443.

  修改程序的第6行,使其變成:


String host = "ebs.95559.com.cn:443";

  運行,控制台還是輸出了以下錯誤:


Response Code: 500
Response Stream:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml><card>
<p><do type="prev" name="Back" label="Back"><prev/></do>Your request for a service could not be fulfilled. Please try again or c
ontact your operator if the problem persists. </p></card></wml>
     原因分析:不詳.

  嘗試方法三:

  以上兩種方法都行不通,有沒有可能cmwap代理伺服器開通了SSL埠,來讓應用程序通過此埠來訪問HTTPS呢?

  將代碼第5行修改如下:


conn = (HttpConnection) Connector.open("https://10.0.0.172:443/corporbank/es_logon.jsp", Connector.READ, true);

  模擬器長時間沒有響應.

  原因分析:一般手機自帶的瀏覽器都支持訪問HTTPS,而手機設置的cmwap代理地址都是10.0.0.172:80.可以肯定的是手機是可以通過10.0.0.172:80這個代理訪問HTTPS而並不是10.0.0.172:443.上網查了資料,證明移動針對cmwap代理只開通了80 埠,而並沒有開通443埠.

  嘗試方法四(成功):

  經過了以上三次失敗,我不得不好好靜下心來想想解決方法.我想到了使用OTA的方式,讓PC連接到GPRS上進行調試.使用數據線連接電腦和手機,將電腦的本地連接禁用,把手機當貓用,創建基於手機撥號的網路連接,輸入號碼「*99#」,撥號,連接.給瀏覽器設置好代理 10.0.0.172:80后,能夠正常瀏覽HTTPS地址.我忽然想到,既然HttpConnection不能夠正常通過cmwap連接HTTPS,那我使用Socket模擬HTTP請求能成功嗎?帶著這個疑問,我開始進行準備:

  必備工具:

  Wireshark或HttpWatch或Openware Simulator

  最開始我是使用Firefox Wireshark攔截80埠的包的,但是瀏覽器向伺服器發出SSL Hello後傳輸的內容都會被加密,Wireshark所看到的都是密文,相當的不方便.如圖:

  

  後來我發現,Openware Simulator提供瀏覽器,且本身就提供了類似於Wireshark的功能,可以截獲到HTTP層所傳輸的內容,能夠很清楚的看見客戶端與伺服器端來往的過程.,我們以Openware Simulator的截圖作為說明.Openware Simulator下載地址:這裡(免費,需要郵件註冊)

  還可以使用HttpWatch 6.0,新版HttpWatch已經可以作為Firefox的擴展嵌入.不過這個要收大洋,還是Openware Simulator來得直接一些.

  實現方法:

  打開Openwave V7 Simulator,在模擬器地址欄輸入網址,截圖如下:

  

  通過右邊的控制台輸出窗口,我們可以很清楚的看到Openware Simulator通過向cmwap代理所發出的請求格式.並且,通過Socket連接,是不需要添加「x-online-host」請求頭的.

  既然HttpConnection行不通,我們自己就使用Socket實現Http連接,向cmwap代理髮出請求.

  此處要先說明以下兩點:

  1、J2ME中的SocketConnection是MIDP 2.0的可選包,並不是每個手機都提供Socket的支持;

  2、我們必須使用Socket連接cmwap代理10.0.0.172:80,而MIDP 2.0對Socket訪問80埠加了限制,在真機上使用必須得使用證書籤署生成的JAD,否則會拋出安全異常.

  在此我不按照Openware Simulator控制台中顯示的連接順序(先Connect后Get)進行連接,使用Openware Simulator只是證明我想法的可行性,我將直接使用Get 完整URL的方式進行代碼的編寫.

  新建一個Midp應用程序,在startApp中加入以下代碼:


1 SocketConnection conn = null;
  2 OutputStream os = null;
  3 InputStream is = null;
  4 try {
  5 // url: https://ebs.95559.com.cn/corporbank/es_logon.jsp
  6 conn = (SocketConnection) Connector.open("socket://10.0.0.172:80", Connector.READ_WRITE, true);
  7 conn.setSocketOption(SocketConnection.DELAY, 0);
  8 conn.setSocketOption(SocketConnection.KEEPALIVE, 300);
  9
  10 // 發送和回復的請求
  11 os = conn.openDataOutputStream();
  12 is = conn.openDataInputStream();
  13
  14 // 在此我們使用HTTP 1.0,HTTP 1.1自己還需要處理chunk,比較麻煩
  15 String reqStr =
  16 "GET https://ebs.95559.com.cn/corporbank/es_logon.jsp HTTP/1.0rn"
  17 "Host: ebs.95559.com.cn:443rn"
  18 "User-Agent: FelixBrowserrn"
  19 "Accept-Charset: utf-8;rn"
  20 "Connection: close;rn"
  21 "Accept-Language: zh-cnrnrn";
  22
  23 os.write(reqStr.getBytes());
  24 os.flush();
  25
  26 System.out.println("Response Stream:");
  27 byte[] buf = new byte[128];
  28 while (true) {
  29 int availSize = is.read(buf, 0, buf.length);
  30 if (availSize == -1) {
  31 break;
  32 }
  33 System.out.println(new String(buf, 0, availSize));
  34 }
  35 } catch (Exception ex) {
  36 ex.printStackTrace();
  37 } finally {
  38 if (is != null) {
  39 try {
  40 is.close();
  41 } catch (IOException ex) {
  42 ex.printStackTrace();
  43 }
  44 }
  45
  46 if (os != null) {
  47 try {
  48 os.close();
  49 } catch (IOException ex) {
  50 ex.printStackTrace();
  51 }
  52 }
  53
  54 if (conn != null) {
  55 try {
  56 conn.close();
  57 } catch (IOException ex) {
  58 ex.printStackTrace();
  59 }
  60 }
  61 }
  62

  輸出結果:

  

  OK,這就是我們想要的結果.

  封裝並重構

  平時我們用習慣了HttpConnection,對於使用SocketConnection訪問HTTP來說,太多的設置和屬性拼湊給編碼帶來了不少麻煩.在完善J2ME-XHTML瀏覽器時,我將Socket訪問HTTP這一塊代碼封裝成一個類SocketHttpConnection,此類繼承自HttpConnection,實現了介面方法.

  並且,我還構建了一個工廠類,可以通過枚舉的方式創建SocketHttpConnection和純HttpConnection,有工廠創建的類還可以制定是以代理的方式連接還是直連.在此,我將代碼貢獻出來給大家.下載地址:這裡.

  幾個類之間的關係如下:

  

  使用方法很簡單:

 


 1 // 請求地址
  2 String url = "https://ebs.95559.com.cn/corporbank/es_logon.jsp";
  3
  4 // 連接方式:SocketConnection/HttpConnection
  5 byte connType = HttpConnectionFactory.CONNTYPE_SOCKET_HTTP;
  6
  7 // 是否使用代理及代理地址、埠
  8 boolean isUseProxy = true;
  9 String proxyHost = "10.0.0.172";
  10 int proxyPort = 80;
  11
  12 HttpConnection conn = null;
  13 InputStream is = null;
  14 try {
  15 // 調用工廠創建
  16 conn = HttpConnectionFactory.getConnection(url, connType, isUseProxy, proxyHost, proxyPort);
  17
  18 // 像原生HttpConnection一樣使用
  19 int code = conn.getResponseCode();
  20 System.out.println("Response Code: " code);
  21
  22 is = conn.openDataInputStream();
  23
  24 System.out.println("Response Stream:");
  25 byte[] buf = new byte[128];
  26 while (true) {
  27 int availSize = is.read(buf, 0, buf.length);
  28 System.out.println(new String(buf, 0, availSize));
  29
  30 if (availSize < buf.length) {
  31 break;
  32 }
  33 }
  34 } catch (IOException ex) {
  35 ex.printStackTrace();
  36 }
  37


[火星人 ] J2ME使用Socket通過cmwap接入點訪問安全HTTPS已經有826次圍觀

http://coctec.com/docs/java/show-post-61053.html