Android SSL HTTP-запрос с использованием самозаверяющего сертификата и CA
У меня есть приложение для android, которое подключается к веб-службе SSL, которую мы размещаем. Веб-сервер apache и имеет свой собственный CA, который мы создали, и самозаверяющий сертификат SSL.
я импортировал наш сертификат CA на планшет Android в разделе доверенные сертификаты пользователя в безопасности.
Я проверил доступ к веб-серверу и могу подтвердить, что сертификат веб-службы отображается как действительный (скриншот ниже)
вот сертификат в настройках безопасности:
теперь, когда я пытаюсь получить доступ к веб-сервису в своем приложении, я получаю исключение "без однорангового сертификата".
это упрощенная реализация 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 в журналах. Но я все равно получаю " нет сертификата Peer" исключение.
10-17 18:29:01.234: I / Система.out (4006): нет однорангового сертификата
должен ли я вручную загрузить мой сертификат CA как-то в этой реализации?
2 ответов
решено с помощью: HttpsURLConnection
URLConnection conn = null;
URL url = new URL(strURL);
conn = url.openConnection();
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
это, кажется, отлично работает с установленными пользователем сертификатами CA.
вы можете выполнить задачу также с помощью 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;
}