Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android實踐:實現https通信

Android實踐:實現https通信

編輯:關於Android編程

近期由於公司的工作需要,需要將原有的http接口切換到https,故做了如下學習和整理。本文先簡要說明https協議原理,然後https協議在浏覽器和App的實踐兩方面進行講述;
一、https協議原理
待整理... ...

二、https協議實踐
理解上面的相關原理後,我們就開始實現HttpsServlet來模擬簡單登錄接口,然後通過浏覽器和app的訪問該https接口;
1.服務端http實現
我們首先實現服務端http協議的get和post通信,項目的結構和主要實現代碼如下:
HttpsServlet.java:
public class HttpsServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPost");
        doLoginRequest(req, resp);
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");
        doLoginRequest(req, resp);
    }


    //實現簡單的登錄邏輯
    private void doLoginRequest(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        PrintStream printStream = new PrintStream(resp.getOutputStream());
        HttpsResponse httpsResponse = new HttpsResponse();
        String userName = req.getParameter("userName");
        String passWord = req.getParameter("passWord");
        if ("123".equals(userName) && "123".equals(passWord)) {
            httpsResponse.setCode("000");
            httpsResponse.setMessage("login success!");
        } else {
            httpsResponse.setCode("004");
            httpsResponse.setMessage("login faild!");
        }
        printStream.println(JSON.toJSONString(httpsResponse));
    }
}
web.xml:

    Archetype Created Web Application
    
        HttpsServlet
        main.com.chengxiang.servlet.HttpsServlet
    
    
        HttpsServlet
        /HttpsServlet
    

實現客戶端登錄的get和post請求,項目目錄結構如下:
 

NextActivity.java:

public class NextActivity extends AppCompatActivity {
    private EditText userNameEditText;
    private EditText passWorldEditText;
    private Button loginButton;
    private TextView responseTextView;


    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    Bundle bundle = msg.getData();
                    HttpsResponse httpsResponse = (HttpsResponse) bundle.getSerializable("result");
                    responseTextView.setText(httpsResponse.toString());
                    break;
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_next);
        userNameEditText = (EditText) findViewById(R.id.next_username_edittext);
        passWorldEditText = (EditText) findViewById(R.id.next_password_password);
        loginButton = (Button) findViewById(R.id.next_login_button);
        responseTextView = (TextView) findViewById(R.id.next_response_text);


        assert loginButton != null;
        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                responseTextView.setText("");
                final String userName = userNameEditText.getText().toString();
                final String passWorld = passWorldEditText.getText().toString();


                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        doLoginGet(userName, passWorld);
//                        doLoginPost(userName,passWorld);
                    }
                }).start();
            }
        });
    }


    /**
     * 執行登錄Get請求
     * @param userName 用戶名
     * @param passWorld 用戶密碼
     */
    private void doLoginGet(String userName, String passWorld) {
        try {
            //服務器的ip地址根據你自己的歡迎修改
            URL url = new URL("http://100.80.28.137:8080/qserver/HttpsServlet?userName=" + userName + "&passWord=" + passWorld);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("GET");
            if (httpURLConnection.getResponseCode() == 200) {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
                String result = new String();
                String readLine;
                if ((readLine = bufferedReader.readLine()) != null) {
                    result += readLine;
                }
                bufferedReader.close();
                httpURLConnection.disconnect();


                Message message = handler.obtainMessage();
                message.what = 1;
                Bundle bundle = new Bundle();
                HttpsResponse httpsResponse = JSON.parseObject(result, HttpsResponse.class);
                bundle.putSerializable("result", httpsResponse);
                message.setData(bundle);
                handler.sendMessage(message);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 執行登錄Post請求
     * @param userName 用戶名
     * @param passWorld 用戶密碼
     */
    private void doLoginPost(String userName, String passWorld) {
        try {
            //服務器的ip地址根據你自己的歡迎修改
            URL url = new URL("http://100.80.28.137:8080/qserver/HttpsServlet");
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");
            String params = "userName=" + userName + "&passWord=" + passWorld;
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(httpURLConnection.getOutputStream()));
            bufferedWriter.write(params.toString());
            bufferedWriter.flush();


            if (httpURLConnection.getResponseCode() == 200) {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
                String result = new String();
                String readLine;
                if ((readLine = bufferedReader.readLine()) != null) {
                    result += readLine;
                }
                bufferedReader.close();
                httpURLConnection.disconnect();


                Message message = handler.obtainMessage();
                message.what = 1;
                Bundle bundle = new Bundle();
                HttpsResponse httpsResponse = JSON.parseObject(result, HttpsResponse.class);
                bundle.putSerializable("result", httpsResponse);
                message.setData(bundle);
                handler.sendMessage(message);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
activity_next.xml:


    
        
        
    
    
        
        
    
AndroidManifext.xml:


    //增加網絡訪問權限
    
   
        
        //登錄Activity聲明
        
    
啟動服務器tomcat,運行客戶端app效果如下:

啟動Fiddler抓包如下,Get請求(Post請求可以自行實踐)數據包如下,為明文傳輸!

接下來,我們就開始將當前http連接修改成https,並查看修改後的抓包情況。

2.生成密鑰對、證書和信任證書庫(詳情請查閱http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html)
在生成該操作過程中,我們將主要用Keytool工具:
keytool:是一個Java數據證書的管理工具,keytool將密鑰(key)和證書(certificates)存在一個稱為keystore的文件中;
keystore:在keystore裡,包含兩種數據:密鑰實體(key entity)-密鑰(secret key)又或者是公私鑰密鑰對、可信任的證書實體(trusted certificate entries)-只包含公鑰;
生成服務器證書庫
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -genkeypair -v -alias tomcat -keyalg RSA -keystore D:\ssl\tomcat.keystore -dname "CN=www.qserver.com,OU=pengchengxiang,O=pengchengxiang,L=Beijing,ST=Beijing,c=cn" -storepass 123456 -keypass 123456 -validity 365
正在為以下對象生成 2,048 位RSA密鑰對和自簽名證書 (SHA256withRSA) (有效期為 365天):
         CN=www.qserver.com, OU=pengchengxiang, O=pengchengxiang, L=Beijing, ST=Beijing, C=cn
[正在存儲D:\ssl\tomcat.keystore]
-genkey:創建一個新的密鑰;
-alias:密鑰別名,每個keystore都關聯一個獨一無二的別名;
-keyalg:使用的加密算法,使用RSA;
-keystore:密鑰存儲目錄,保存在D:\ssl目錄下;
-dname:
CN(Common Name名字與姓氏)
  OU(Organization Unit組織單位名稱)
  O(Organization組織名稱)
  L(Locality城市或區域名稱)
  ST(State州或省份名稱)
  C(Country國家名稱)
-storepass:存取密碼,這個密碼供系統從keystore文件取信息的時候使用;
-keypass:私有密鑰的密碼;
-validity:有效期,365天;
注意1:生成服務端密鑰時,alias必須為tomcat,否則後續啟動tomcat服務的時候會報錯Caused by: java.io.IOException: Alias name tomcat does not identify a key entry ;
注意2:生成服務端密鑰庫時,CN必須與服務端的域名或者ip地址相同,否則正常獲取證書訪問;
從服務器證書庫中導出服務器證書
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -export -v -alias tomcat -keystore D:\ssl\tomcat.keystore -storepass 123456 -rfc -file D:\ssl\tomcat.cer
存儲在文件  中的證書
生成客戶端信任證書庫(由服務端證書生成的證書庫)
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -import -v -alias tomcat -file D:\ssl\tomcat.cer -keystore D:\ssl\qproject.truststore -storepass 123456  -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
所有者: CN=www.qserver.com, OU=pengchengxiang, O=pengchengxiang, L=Beijing, ST=Beijing, C=cn
發布者: CN=www.qserver.com, OU=pengchengxiang, O=pengchengxiang, L=Beijing, ST=Beijing, C=cn
序列號: 61c9f857
有效期開始日期: Sun Nov 06 18:14:44 CST 2016, 截止日期: Mon Nov 06 18:14:44 CST2017
證書指紋:
         MD5: 85:BE:DE:EF:43:1A:1B:BC:62:5A:D4:4C:BC:89:C3:E0
         SHA1: 6A:D5:5F:88:FE:DA:63:9C:A6:85:6E:47:6A:76:FA:C6:CE:D9:A4:BE
         SHA256: 3A:73:51:F3:75:06:E1:B5:DE:62:59:CB:18:60:BD:AE:F4:0F:2D:B0:7A:
02:CC:9D:37:27:87:AE:6F:7F:F6:AC
         簽名算法名稱: SHA256withRSA
         版本: 3
擴展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
    KeyIdentifier [       
        0000: F4 64 61 81 5B 41 4F C4   6F D6 CE 66 6D 02 84 17  .da.[AO.o..fm...
        0010: 29 4E 58 A5                                        )NX.
    ]
]
是否信任此證書? [否]:  是
證書已添加到密鑰庫中
[正在存儲D:\ssl\qproject.truststore]
注意3:-storetype BKS,是Android上面可以識別的格式。如果不指定,jdk默認生成的格式是JKS。 -provider org.bouncycastle.jce.provider.BouncyCastleProvider,需要下載jar包bcprov-jdk16-146.jar放到JDK_HOME\jre\lib\ext\目錄下。 注意需要jdk16,其他的版本android下面有版本不匹配的問題;
生成客戶端證書庫
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -genkeypair -alias qproject -keyalg RSA -storetype PKCS12 -keystore D:\ssl\qproject.p12 -dname "CN=www.qserver.com,OU=pengchengxiang,O=pengchengxiang,L=Beijing,ST=Beijing,c=cn" -storepass 123456 -keypass 123456 -validity 365
從客戶端證書庫中導出客戶端證書
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -export -v -alias qproject -keystore D:\ssl\qproject.p12 -storetype PKCS12 -storepass 123456 -rfc -file D:\ssl\qproject.cer
存儲在文件  中的證書
生成服務端信任證書庫(使得服務器信任客戶端證書)
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -import -v -alias qproject -file D:\ssl\qproject.cer -keystore D:\ssl\tomcat.keystore -storepass 123456
所有者: CN=www.qserver.com, OU=pengchengxiang, O=pengchengxiang, L=Beijing, ST=Beijing, C=cn
發布者: CN=www.qserver.com, OU=pengchengxiang, O=pengchengxiang, L=Beijing, ST=Beijing, C=cn
序列號: 6313eac6
有效期開始日期: Sun Nov 06 18:26:42 CST 2016, 截止日期: Mon Nov 06 18:26:42 CST2017
證書指紋:
         MD5: F6:1B:DB:98:DF:64:5E:8C:77:F1:6F:A7:DC:5D:B3:EB
         SHA1: 78:7F:C2:30:A5:A7:60:91:5C:3E:D4:01:F1:C3:5B:CC:3E:09:5B:2E
         SHA256: 8F:84:4D:DD:E6:1A:E7:68:CA:08:07:CC:45:75:D2:2F:CA:03:33:C3:E6:
95:DF:3C:2F:37:8E:6F:39:3F:47:A3
         簽名算法名稱: SHA256withRSA
         版本: 3
擴展:
#1: ObjectId: 2.5.29.14 Criticality=false
    SubjectKeyIdentifier [
        KeyIdentifier [
            0000: 76 F0 FF 0B 04 02 05 3F   AA E8 5B 68 7A B1 DF 22  v......?..[hz.."
            0010: 7F 04 CB E2                                        ....
        ]
    ]
是否信任此證書? [否]:  是
證書已添加到密鑰庫中
[正在存儲D:\ssl\tomcat.keystore]
查看證書庫中的全部證書(服務器密鑰庫,客戶端密鑰庫和信任庫)
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -list -keystore D:\ssl\tomcat.keystore -storepass 123456
密鑰庫類型: JKS
密鑰庫提供方: SUN
您的密鑰庫包含 2 個條目
qproject, 2016-11-6, trustedCertEntry,
證書指紋 (SHA1): 78:7F:C2:30:A5:A7:60:91:5C:3E:D4:01:F1:C3:5B:CC:3E:09:5B:2E
tomcat, 2016-11-6, PrivateKeyEntry,
證書指紋 (SHA1): 6A:D5:5F:88:FE:DA:63:9C:A6:85:6E:47:6A:76:FA:C6:CE:D9:A4:BE
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -list -keystore D:\ssl\qproject.p12 -storepass 123456
密鑰庫類型: JKS
密鑰庫提供方: SUN
您的密鑰庫包含 1 個條目
qproject, 2016-11-6, PrivateKeyEntry,
證書指紋 (SHA1): 78:7F:C2:30:A5:A7:60:91:5C:3E:D4:01:F1:C3:5B:CC:3E:09:5B:2E
C:\Users\chengxiang.peng.QUNARSERVERS>keytool -list -keystore D:\ssl\qproject.truststore -storepass 123456 -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
密鑰庫類型: BKS
密鑰庫提供方: BC
您的密鑰庫包含 1 個條目
tomcat, 2016-11-6, trustedCertEntry,
證書指紋 (SHA1): 6A:D5:5F:88:FE:DA:63:9C:A6:85:6E:47:6A:76:FA:C6:CE:D9:A4:BE
運行完成後,D:\ssl\目錄下生成如下文件:

qproject.cer:客戶端證書;
qproject.p12:客戶端密鑰;
qproject.truststore:客戶端信任證書庫;
tomcat.cer:服務端證書;
tomcat.keysotre:服務端密鑰和信任證書庫;

3.Tomcat的配置(詳細請查閱:https://tomcat.apache.org/tomcat-9.0-doc/ssl-howto.html)
修改tomcat的配置文件${catalina.base}/conf/server.xml;
port:https訪問的端口;
SSLEnabled:true,開啟https服務;
scheme:https;
secure:true,開啟服務端安全通信,客戶端獲取服務器端證書;
keystoreFile:D:\ssl\tomcat.keystore;
keystorePass:123456,服務器證書庫密碼;
clientAuth:true,開啟驗證客戶端;
sslProtocol:TLS,使用的協議;
truststoreFile:D:\ssl\tomcat.keystore,服務器證書庫(已導入客戶端證書) ;
truststorePass:123456;
提示1:啟動tomcat服務器,報錯如下:
Caused by: java.io.IOException: Alias name tomcat does not identify a key entry
at org.apache.tomcat.util.net.jsse.JSSEUtil.getKeyManagers(JSSEUtil.java:306)
at org.apache.tomcat.util.net.AbstractJsseEndpoint.initialiseSsl(AbstractJsseEndpoint.java:90)
at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:245)
at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:839)
at org.apache.coyote.AbstractProtocol.init(AbstractProtocol.java:558)
at org.apache.coyote.http11.AbstractHttp11Protocol.init(AbstractHttp11Protocol.java:65)
at org.apache.catalina.connector.Connector.initInternal(Connector.java:1010)
... 13 more
處理1:檢查你生成的服務器證書的-alias是否為tomcat

由於我們是在本機測試,並且在生成證書的時候CN配置了www.qserver.com,故我們在測試之前添加Hosts配置如下;
# httpstest
127.0.0.1 www.qserver.com
啟動tomcat服務器,我們先用浏覽器以get的方式測試下服務端https接口功能。訪問成功!服務器接口修改完畢,接下來我們來修改客戶端;

 

提示2:初次訪問接口時,會出現如下錯誤:


處理2:由於服務器Https握手過程中,交換的證書不在浏覽器信任的范圍,故提示錯誤。

Internet選項->內容->證書->受信任的根證書頒發機構->導入->tomcat.cer->存入受信任的根證書頒發機構->確認安裝此證書;


4.客戶端修改
添加項目目錄asset,並在該目錄下添加服務端驗證客戶端的證書qproject.p12,和客戶端驗證服務端的信任證書庫qproject.truststore;

添加客戶端https身份驗證等相關邏輯;
HttpsHelper.java:
public class HttpsHelper {
    //p12證書類型
    private static final String KEY_STORE_TYPE_P12 = "PKCS12";
    //bks證書類型
    private static final String KEY_STORE_TYPE_BKS = "bks";
    //客戶端給服務器端認證的證書
    private static final String KEY_STORE_QPRPJECT_PATH = "qproject.p12";
    //客戶端驗證服務器端的信任證書庫
    private static final String KEY_STORE_QPROJECTTRUST_PATH = "qproject.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) {
        SSLContext sslContext = null;
        try {
            //初始化服務器端需要驗證的客戶端證書-qprojectKeyStore、客戶端信任的服務器端證書庫-trustKeyStore
            KeyStore qprojectKeyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
            KeyStore trustKeyStore = KeyStore.getInstance(KEY_STORE_TYPE_BKS);
            InputStream qprojectInPutStream = context.getResources().getAssets().open(KEY_STORE_QPRPJECT_PATH);
            InputStream trustInputStream = context.getResources().getAssets().open(KEY_STORE_QPROJECTTRUST_PATH);
            try {
                qprojectKeyStore.load(qprojectInPutStream, KEY_STORE_PASSWORD.toCharArray());
                trustKeyStore.load(trustInputStream, KEY_STORE_TRUST_PASSWORD.toCharArray());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    qprojectInPutStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
                    trustInputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }


            //初始化SSLContext上下文對象
            sslContext = SSLContext.getInstance("TLS");
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustKeyStore);
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
            keyManagerFactory.init(qprojectKeyStore, KEY_STORE_PASSWORD.toCharArray());
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sslContext;
    }


    /**
     * 獲取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.setSSLSocketFactory(sslContext.getSocketFactory());
                connection.setConnectTimeout(30000);
                //忽略請求域名和證書域名的校驗
                connection.setDefaultHostnameVerifier( new HostnameVerifier(){
                    public boolean verify(String string,SSLSession ssls) {
                        return true;
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }
}
NextActivity.java:修改獲取HttpsURLConnection的方式,和域名https,其它不變
public class NextActivity extends AppCompatActivity {
    ... ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ... ...
    }

    /**
     * 執行登錄Get請求
     * @param userName 用戶名
     * @param passWorld 用戶密碼
     */
    private void doLoginGet(String userName, String passWorld) {
        try {
            //修改獲取HttpsURLConnection的方式,和域名https
            String url = "https://192.168.1.103:8443/qserver/HttpsServlet?userName=" + userName + "&passWord=" + passWorld;
            HttpsURLConnection httpsURLConnection = HttpsHelper.getHttpsURLConnection(this,url,"GET");
            ... ... 
    }

    /**
     * 執行登錄Post請求
     * @param userName 用戶名
     * @param passWorld 用戶密碼
     */
    private void doLoginPost(String userName, String passWorld) {
        ... ...
    }
}
提示1:在請求https接口的時候,報錯如下:
11-06 17:53:36.244 32662-5351/com.qunar.home W/System.err: java.io.IOException: Wrong version of key store.
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi.engineLoad(BcKeyStoreSpi.java:805)
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at java.security.KeyStore.load(KeyStore.java:590)
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at com.qunar.hotel.ssl.HttpsHelper.getSSLContext(HttpsHelper.java:46)
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at com.qunar.hotel.ssl.HttpsHelper.getHttpsURLConnection(HttpsHelper.java:87)
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at com.qunar.hotel.NextActivity.doLoginGet(NextActivity.java:83)
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at com.qunar.hotel.NextActivity.access$300(NextActivity.java:27)
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at com.qunar.hotel.NextActivity$2$1.run(NextActivity.java:67)
11-06 17:53:36.245 32662-5351/com.qunar.home W/System.err: at java.lang.Thread.run(Thread.java:818)
處理:android系統只支持JKS默認證書格式,支持BKS,故生成客戶端證書的時候使用DKS格式生成,見上面章節;

提示2:在請求https接口的時候,報錯如下:
11-06 18:52:53.154 21260-23016/com.qunar.home W/System.err: java.io.IOException: Hostname '192.168.1.103' was not verified
11-06 18:52:53.154 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.Connection.upgradeToTls(Connection.java:205)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.Connection.connect(Connection.java:155)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:282)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:216)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:391)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:341)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:509)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.internal.http.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.qunar.hotel.NextActivity.doLoginGet(NextActivity.java:84)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.qunar.hotel.NextActivity.access$300(NextActivity.java:27)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at com.qunar.hotel.NextActivity$2$1.run(NextActivity.java:67)
11-06 18:52:53.155 21260-23016/com.qunar.home W/System.err: at java.lang.Thread.run(Thread.java:818)
處理:由於我們在測試環境,故請求url中使用了服務器的ip,到時和證書中的 CN(Common Name名字與姓氏)不匹配(在正式的環境不會有該問題)。那麼我們默認跳過該認證或者在正式線使用線上的域名訪問:
//忽略請求域名和證書域名的校驗
connection.setDefaultHostnameVerifier( new HostnameVerifier(){
public boolean verify(String string,SSLSession ssls) {
return true;
}
});


5.tomcat修改
原來的connector配置只有加解密,現在加上服務端身份驗證相關配置,修改server.xml文件如下:
clientAuth:啟動客戶端身份驗證;
truststoreFile:服務端信任的客戶端證書庫;
truststorePass:信任證書庫密碼;

6.測試Https接口
安裝app,並重新啟動tomcat服務,使用Fiddler抓取https請求使用加密傳輸!

7.代碼庫
QProject:https://github.com/Pengchengxiang/QProject 分支:feature/https
QServer:https://github.com/Pengchengxiang/QServer 分支:feature/https

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved