使用自签名证书和CA的Android SSL HTTP请求

9
我有一个连接到我们托管的SSL Web服务的Android应用程序。Web服务器是Apache,具有我们创建的自己的CA和自签名的SSL证书。
我已将我们的CA证书导入Android平板电脑的"安全性"中的用户受信任证书部分。
我已测试访问Web服务器,并可以确认Web服务证书显示为有效(如下所示的屏幕截图)。
以下是安全设置中的证书:
现在当我尝试在我的应用程序中访问Web服务时,触发了"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循环只是输出证书,我可以在日志中看到我的CA。但是我仍然收到“没有对等证书”的异常。

10-17 18:29:01.234:I / System.out(4006):没有对等证书

在此实现中,我是否需要手动加载我的CA证书?


你尝试在这个实现中手动加载你的CA证书了吗? - BON
你可以从http://www.startssl.com/获取免费且可信赖的SSL证书,为你的域名提供保护(我在一些应用中使用它们),这样你就不必在每个想要使用你的应用程序的设备上添加你的CA。 - Juan Sánchez
2个回答

3
使用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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接