I have an android application that is connecting to an SSL web service that we host. The Web server is apache and has its own CA that we created and a self signed SSL certificate.
I have imported our CA certificate onto the Android tablet in the User Trusted certificates section in Security.
I have tested access to the web server and can confirm that the web service certificate shows as valid (screenshot below)
Here is the certificate in the security settings:
Now when I try and access the webservice in my application I get the "No Peer Certificate" exception being triggered.
This is the SSL implementation simplified:
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);
}
}
}
The while loop just spits out the certificates and I can see my own CA in the logs. But I still get the "No Peer Certificate" exception.
10-17 18:29:01.234: I/System.out(4006): No peer certificate
Do I have to manually load my CA certificate somehow in this implementation?
Solved by using: HttpsURLConnection
URLConnection conn = null;
URL url = new URL(strURL);
conn = url.openConnection();
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
This seems to work fine with user installed CA certificates.
You can accomplished the task also using DefaultHttpClient
, even though here is suggested to:
Prefer HttpURLConnection for new code
Pay attention also in importing or adding certificate to your application since you may have problems in updating the certificate when it will expire.
Here how to get a DefaultHttpClient
trusting a self-signed certificate:
* 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;
}
and here how to get a 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;
}
来源:https://stackoverflow.com/questions/19433058/android-ssl-http-request-using-self-signed-cert-and-ca