SSL相互认证的Andr​​oid客户端FAIL接受服务器证书,但服务器不会获得客户端证书 [英] SSL mutual authentication FAIL on Android Client accepts servers certificate but server does not get the client cert

查看:153
本文介绍了SSL相互认证的Andr​​oid客户端FAIL接受服务器证书,但服务器不会获得客户端证书的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想建立一个Linux服务器和Android APP之间的相互认证的SSL。
到目前为止,我已经能够获取应用程序与服务器证书的工作通过SSL通信,但一旦我的服务器设置为只接受它停止工作,客户端证书。服务器配置似乎确定,但我有种被卡住。我最好的猜测是,客户端证书没有被正确presented到服务器,但还是不知道接下来该怎么进行测试。我试着用客户端的质子交换膜在我的OS X的钥匙串,但浏览器似乎并没有与该证书的工作。然后再次服务器证书完美的作品,因为我可以实现HTTPS连接和应用程序接受我的签名服务器证书。

I am trying to set up a mutually authenticated SSL between a Linux Server and Android APP. So far, I have been able to get the app to work with the server certificate communicate via SSL but once I set the server to only accept client certificates it stops working. The server configuration seems ok, but I am a kind of stuck. My best guess is that the client certificate is not correctly being presented to the server but got no idea how to test it next. I tried using the .pem for the client in my OS X keychain but the browsers does not seem to work with that certificate. Then again the Server certificate works perfectly because I can achieve https connection and the APP Accepts my unsigned server certificate.

在code我一直在我现在用的就是各种教程的结合,回答这是主要的我已经收藏:

The code I have been I am using is the combination of various tutorials, answers this is the main ones I have bookmarked:

  • http://www.androidhive.info/2012/05/how-to-connect-android-with-php-mysql/
  • Self Signed SSL acceptance Android
  • Securing communication [Authenticity, Privacy & Integrity] with mobile app?
  • (For the Server Config) http://security.stackexchange.com/questions/34897/configure-ssl-mutual-two-way-authentication

这是我使用的连接两大类:
1)本类处理JSON解析并执行请求

This are the two main classes I am using for the connection: 1) This class handles the JSON parsing and does the REQUESTS

package edu.hci.additional;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.List;

import android.content.Context;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

public class JSONParser {

    static InputStream is = null;
    static JSONObject jObj = null;
    static String json = "";

    // constructor
    public JSONParser() {

    }

    // function get json from url
    // by making HTTP POST or GET mehtod
    public JSONObject makeHttpRequest(String url, String method,
            List<NameValuePair> params, Context context) {

        // Making HTTP request
        try {

            // check for request method
            if(method == "POST"){
                // request method is POST
                // defaultHttpClient
                SecureHttpClient httpClient = new SecureHttpClient(context);
                HttpPost httpPost = new HttpPost(url);
                httpPost.setEntity(new UrlEncodedFormEntity(params));

                HttpResponse httpResponse = httpClient.execute(httpPost);
                HttpEntity httpEntity = httpResponse.getEntity();
                is = httpEntity.getContent();

            }else if(method == "GET"){
                // request method is GET
                SecureHttpClient httpClient = new SecureHttpClient(context);
                String paramString = URLEncodedUtils.format(params, "utf-8");
                url += "?" + paramString;
                HttpGet httpGet = new HttpGet(url);

                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                is = httpEntity.getContent();
            }           


        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    is, "iso-8859-1"), 8);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            is.close();
            json = sb.toString();
        } catch (Exception e) {
            Log.e("Buffer Error", "Error converting result " + e.toString());
        }

        // try parse the string to a JSON object
        try {
            jObj = new JSONObject(json);
        } catch (JSONException e) {
            Log.e("JSON Parser", "Error parsing data " + e.toString());
        }

        // return JSON String
        return jObj;

    }
}

这第二类处理SSL验证:

This second class handles the SSL Authentication:

package edu.hci.additional;

import android.content.Context;
import android.util.Log;
import edu.hci.R;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;

import java.io.IOException;
import java.io.InputStream;
import java.security.*;


public class SecureHttpClient extends DefaultHttpClient {

    private static Context appContext = null;
    private static HttpParams params = null;
    private static SchemeRegistry schmReg = null;
    private static Scheme httpsScheme = null;
    private static Scheme httpScheme = null;
    private static String TAG = "MyHttpClient";

    public SecureHttpClient(Context myContext) {

        appContext = myContext;

        if (httpScheme == null || httpsScheme == null) {
            httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
            httpsScheme = new Scheme("https", mySSLSocketFactory(), 443);
        }

        getConnectionManager().getSchemeRegistry().register(httpScheme);
        getConnectionManager().getSchemeRegistry().register(httpsScheme);

    }

    private SSLSocketFactory mySSLSocketFactory() {
        SSLSocketFactory ret = null;
        try {

            final KeyStore clientCert = KeyStore.getInstance("BKS");
            final KeyStore serverCert = KeyStore.getInstance("BKS");

            final InputStream client_inputStream = appContext.getResources().openRawResource(R.raw.authclientcerts);
            final InputStream server_inputStream = appContext.getResources().openRawResource(R.raw.certs);

            clientCert.load(client_inputStream, appContext.getString(R.string.client_store_pass).toCharArray());
            serverCert.load(server_inputStream, appContext.getString(R.string.server_store_pass).toCharArray());

            String client_password = appContext.getString(R.string.client_store_pass);

            server_inputStream.close();
            client_inputStream.close();

            ret = new SSLSocketFactory(clientCert,client_password,serverCert);
        } catch (UnrecoverableKeyException ex) {
            Log.d(TAG, ex.getMessage());
        } catch (KeyStoreException ex) {
            Log.d(TAG, ex.getMessage());
        } catch (KeyManagementException ex) {
            Log.d(TAG, ex.getMessage());
        } catch (NoSuchAlgorithmException ex) {
            Log.d(TAG, ex.getMessage());
        } catch (IOException ex) {
            Log.d(TAG, ex.getMessage());
        } catch (Exception ex) {
            Log.d(TAG, ex.getMessage());
        } finally {
            return ret;
        }
    }

}

要创建我用这个命令的OpenSSL键:

To Create the keys I used openssl with this command:

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 500

要拿到钥匙BKS为Android我用充气城堡bcprov-jdk15on-150.jar位于:的 http://www.bouncycastle.org/latest_releases.html

To get the keys to BKS for Android I used the bouncy castle bcprov-jdk15on-150.jar located at: http://www.bouncycastle.org/latest_releases.html

和使用的命令:

keytool -import -v -trustcacerts -alias 0 -file ~/cert.pem -keystore ~/Downloads/authclientcerts.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath ~/Downloads/bcprov-jdk15on-150.jar -storepass passWORD

我加了

最后行/etc/httpd/conf.d/ssl.conf,要求客户端证书,并检查证书的有效性(匹配我创建了客户端证书,)在Fedora的19是:

Finally the lines I added to /etc/httpd/conf.d/ssl.conf to require the client certificate and check for the certificate validity (which match the client certificate I created) in Fedora 19 are:

...
SSLVerifyClient require
SSLVerifyDepth  5
...
<Location />
SSLRequire (    %{SSL_CLIENT_S_DN_O} eq "Develop" \
            and %{SSL_CLIENT_S_DN_OU} in {"Staff", "Operations", "Dev"} )
</Location>
...
SSLOptions +FakeBasicAuth +StrictRequire

我尝试了很多组合对这个配置文件,并在同一个结果结束twrowing我一个SSLPeerUnverifiedException:没有对方的证书异常。我评论此线路上的服务器的SSL配置文件和所有的伟大工程,但服务器接受所有客户端这不是我所需要的。

I tried a lot of combinations on this config file and all ended in the same result twrowing me a "SSLPeerUnverifiedException: No peer certificate" Exception. I comment this lines on the SSL config file of the server and all works great but the server accepts all clients which is not what I need.

在此先感谢:)

更新

@EJP 的答案的伎俩

首先,我用的证书添加到正确的(在/ etc / PKI / TLS /证书/)路径并加载它:
重命名证书:MV CA-andr.pem CA-andr.crt
而现在加载证书:

First I had to add the certificate to the correct ( /etc/pki/tls/certs/ )path and load it by using: rename the cert: mv ca-andr.pem ca-andr.crt And now load the certificate:

 ln -s ca-andr.crt $( openssl x509 -hash -noout -in ca-andr.crt )".0"

这将创建一个类似f3f24175.0

This will create a openSSL readable symlink with a name similar to "f3f24175.0"

然后我在/etc/httpd/conf.d/ssl.conf配置文件中设置新的证书文件。

Then I set the new Certificate file in the /etc/httpd/conf.d/ssl.conf Configuration file.

…
SSLCACertificateFile /etc/pki/tls/certs/f2f62175.0
…

现在重新启动HTTP服务和
如果证书加载测试:

Now restart the http service and test the if the certificate is loaded with:

openssl verify -CApath /etc/pki/tls/certs/ f2f62175.0

如果一切正常,你应该看到:

If all is ok you should see:

f3f24175.0:OK

f3f24175.0: OK

和可以结束与测试:

openssl s_client -connect example.com:443 -CApath /etc/pki/tls/certs

这应该返回信任的客户端证书列表(如果你看到你添加的,是工作)

This should return the list of trusted client certificates (If you see the one you added, is working)

现在问题的第二部分是,我的authclientcerts.BKS din't包含私钥,所以我提供的密码从未使用和服务器不会验证证书。所以我出口我的密钥和证书以PKCS12并相应更新了JAVA code。

Now the second part of the problem was that my authclientcerts.BKS din’t contain the private key, so the password I supplied was never used and the server would not authenticate the cert. So I exported my key and cert to pkcs12 and updated the JAVA code accordingly.

导出命令:

openssl pkcs12 -export -in ~/cert.pem -inkey ~/key.pem > android_client_p12.p12

然后,我改变了SecureHttpClient.java类的各个部分,使与PKCS12代替BKS客户端证书。

Then the I changed the parts of the SecureHttpClient.java Class to make the client certificate with PKCS12 instead of BKS.

要改变从BKS密钥库类型PKCS12
我取代:

To change the key store type from BKS to PKCS12 I Replaced:

final KeyStore clientCert = KeyStore.getInstance("BKS");

有关这一点:

final KeyStore clientCert = KeyStore.getInstance("PKCS12");

然后我更新到位于水库的实际密钥存储文件的引用/生/
通过更换:

And then I updated the references to the actual key store files located on res/raw/ By Replacing:

final InputStream client_inputStream = appContext.getResources().openRawResource(R.raw.authclientcerts);

有关这一点:

final InputStream client_inputStream = appContext.getResources().openRawResource(R.raw.android_client_p12);

这奏效了:D

推荐答案

在服务器要求客户端证书,它提供了CA的列表,它会接受签名的证书。如果客户端不具有由其中的一个签名的证书,它不会在答复发送证书。如果服务器配置为要求客户端证书,而不是只是想之一,它就会关闭连接。

When the server asks for the client certificate, it provides a list of CAs that it will accept certificates signed by. If the client doesn't have a certificate signed by one of them, it won't send a certificate in reply. If the server is configured to require a client certificate, as opposed to just wanting one, it will then close the connection.

所以,确保客户拥有一个证书是可以接受的服务器的信任。

So, ensure that the client has a certificate which is acceptable to the server's truststore.

这篇关于SSL相互认证的Andr​​oid客户端FAIL接受服务器证书,但服务器不会获得客户端证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆