Андроид HTTP HTTP-запрос с использованием самозаверяющего сертификата и CA

У меня есть приложение для Android, которое подключается к веб-сервису SSL, который мы размещаем. Веб-сервер является apache и имеет собственный CA, который мы создали, и самоподписанный сертификат SSL.

Я импортировал наш сертификат CA на планшет Android в разделе сертификатов доверенных пользователей в разделе Безопасность.

Я протестировал доступ к веб-серверу и могу подтвердить, что сертификат веб-службы отображается как действительный (снимок экрана ниже)

Valid certificate

Вот сертификат в настройках безопасности:

Trusted certificate

Теперь, когда я пытаюсь получить доступ к веб-сервису в своем приложении, я получаю исключение "No Peer Certificate".

Это упрощенная реализация SSL:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // allows network on main thread (temp hack)
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 
    StrictMode.setThreadPolicy(policy);

    SchemeRegistry schemeRegistry = new SchemeRegistry();
    //schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
    schemeRegistry.register(new Scheme("https", newSSLSocketFactory(), 443));


    HttpParams params = new BasicHttpParams();

    SingleClientConnManager mgr = new SingleClientConnManager(params, schemeRegistry);

    HttpClient client = new DefaultHttpClient(mgr, params);

    HttpPost httpRequest = new HttpPost("https://our-web-service.com");

    try {
        client.execute(httpRequest);
    } catch (Exception e) {
        e.printStackTrace(); //
    }
}

/* 
 * Standard SSL CA Store Setup //
 */
private SSLSocketFactory newSSLSocketFactory() {

    KeyStore trusted;

    try {
        trusted = KeyStore.getInstance("AndroidCAStore");
        trusted.load(null, null);
        Enumeration<String> aliases = trusted.aliases();

        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            X509Certificate cert = (X509Certificate) trusted.getCertificate(alias);
            Log.d("", "Alias="+alias);
            Log.d("", "Subject DN: " + cert.getSubjectDN().getName());
            Log.d("", "Issuer DN: " + cert.getIssuerDN().getName());
        }      

        SSLSocketFactory sf = new SSLSocketFactory(trusted);
        sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);

        return sf;

    } catch (Exception e) {
        // TODO Auto-generated catch block
        throw new AssertionError(e);
    }  
}

}

Цикл while просто выплевывает сертификаты, и я могу видеть свой собственный ЦС в журналах. Но я все еще получаю исключение "Нет равных сертификатов".

10-17 18: 29: 01.234: I/System.out(4006): Отсутствует сертификат сверстника

Нужно ли вручную загружать мой сертификат CA в эту реализацию?

Ответ 1

Решено с помощью: HttpsURLConnection

URLConnection conn = null;
URL url = new URL(strURL);
conn = url.openConnection();
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;

Это, похоже, отлично работает с установленными CA сертификатами.

Ответ 2

Вы также можете выполнить задачу, используя DefaultHttpClient, даже если здесь предлагается:

Предпочитает HttpURLConnection для нового кода

Обратите внимание также на импорт или добавление сертификата в приложение, поскольку у вас могут возникнуть проблемы при обновлении сертификата, когда он истечет.

Здесь, как получить DefaultHttpClient, доверяющий самозаверяющемуся сертификату:

 * This method returns the appropriate HttpClient.
 * @param isTLS Whether Transport Layer Security is required.
 * @param trustStoreInputStream The InputStream generated from the BKS keystore.
 * @param trustStorePsw The password related to the keystore.
 * @return The DefaultHttpClient object used to invoke execute(request) method.
private DefaultHttpClient getHttpClient(boolean isTLS, InputStream trustStoreInputStream, String trustStorePsw) 
    throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException, UnrecoverableKeyException {
    DefaultHttpClient client = null;        
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 8080);
    schemeRegistry.register(http);
    if(isTLS) {
        KeyStore trustKeyStore = null;
        char[] trustStorePswCharArray = null;
        if(trustStorePsw!=null) {
            trustStorePswCharArray = trustStorePsw.toCharArray();
        } 
        trustKeyStore = KeyStore.getInstance("BKS");
        trustKeyStore.load(trustStoreInputStream, trustStorePswCharArray);
        SSLSocketFactory sslSocketFactory = null;
        sslSocketFactory = new SSLSocketFactory(trustKeyStore);
        Scheme https = new Scheme("https", sslSocketFactory, 8443);
        schemeRegistry.register(https);
    }                
    HttpParams httpParams = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT);
    HttpConnectionParams.setSoTimeout(httpParams, SOCKET_TIMEOUT);        
    ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);        
    client = new DefaultHttpClient(clientConnectionManager, httpParams);        
    return client;
}

и здесь, как получить HttpsURLConnection:

 * This method set the certificate for the HttpsURLConnection
 * @param url The url to contact.
 * @param certificateInputStream The InputStream generated from the .crt certificate.
 * @param certAlias The alias for the certificate. 
 * @return The returned HttpsURLConnection
private HttpsURLConnection getHttpsURLConnection(URL url, InputStream certificateInputStream, String certAlias) 
    throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    HttpsURLConnection connection = null;
    CertificateFactory certFactory = null;
    Certificate cert = null;
    KeyStore keyStore = null;
    TrustManagerFactory tmFactory = null;
    SSLContext sslContext = null;
    // Load certificates from an InputStream
    certFactory = CertificateFactory.getInstance("X.509");
    cert = certFactory.generateCertificate(certificateInputStream);
    certificateInputStream.close();
    // Create a KeyStore containing the trusted certificates
    keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null, null);
    keyStore.setCertificateEntry(certAlias, cert);
    // Create a TrustManager that trusts the certificates in our KeyStore
    tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmFactory.init(keyStore);
    // Create an SSLContext that uses our TrustManager
    sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, tmFactory.getTrustManagers(), null);
    connection = (HttpsURLConnection)url.openConnection();
    connection.setSSLSocketFactory(sslContext.getSocketFactory());
    return connection;
}