прием HTTPS-соединений с самозаверяющими сертификатами
Я пытаюсь сделать HTTPS-соединения, используя HttpClient lib, но проблема в том, что, поскольку сертификат не подписан признанным центром сертификации (CA), как Verisign,GlobalSIgn и т. д., перечисленные в наборе доверенных сертификатов Android, я продолжаю получать javax.net.ssl.SSLException: Not trusted server certificate.
Я видел решения, где вы просто принимаете все сертификаты, но что если я хочу спросить у пользователя?
Я хочу получить диалог, подобный тому, что из браузер, позволяющий пользователю решить, продолжать или нет. Предпочтительно, я хотел бы использовать тот же certificatestore как браузер. Есть идеи?
12 ответов:
первое, что вам нужно сделать, это установить уровень проверки. Таких уровней не так уж и много:
- ALLOW_ALL_HOSTNAME_VERIFIER
- BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
- STRICT_HOSTNAME_VERIFIER
хотя метод setHostnameVerifier () устарел для новой библиотеки apache, но для версии в Android SDK это нормально. И вот мы берем
ALLOW_ALL_HOSTNAME_VERIFIERи установить его в метод фабрикиSSLSocketFactory.setHostnameVerifier().затем, вам нужно установить нашу фабрику для протокола к https. Для этого просто позвоните
SchemeRegistry.register()метод.тогда вам нужно создать
DefaultHttpClientСSingleClientConnManager. Также В приведенном ниже коде вы можете видеть, что по умолчанию также будет использоваться наш флаг (ALLOW_ALL_HOSTNAME_VERIFIER) методомHttpsURLConnection.setDefaultHostnameVerifier()ниже код работает для меня:
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; DefaultHttpClient client = new DefaultHttpClient(); SchemeRegistry registry = new SchemeRegistry(); SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory(); socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier); registry.register(new Scheme("https", socketFactory, 443)); SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry); DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams()); // Set verifier HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); // Example send http request final String url = "https://encrypted.google.com/"; HttpPost httpPost = new HttpPost(url); HttpResponse response = httpClient.execute(httpPost);
следующие основные шаги необходимы для достижения защищенного соединения от центров сертификации, которые не считаются доверенными платформой android.
по просьбе многих пользователей, я отразил наиболее важные части из моего статья в блоге здесь:
- возьмите все необходимые сертификаты (root и любые промежуточные CA)
- создать хранилище ключей с помощью keytool и BouncyCastle поставщик и импорт сертификатов
- загрузите хранилище ключей в свое приложение для android и используйте его для защищенных соединений (я рекомендую использовать Apache HttpClient вместо стандартного
java.net.ssl.HttpsURLConnection(легче понять, более производительный)возьмите сертификаты
вы должны получить все сертификаты, которые строят цепочку от сертификата конечной точки На всем пути до корневого ЦС. Это означает, что любые (если присутствуют) промежуточные сертификаты CA, а также корневой CA сертификат. Вам не нужно получать сертификат конечной точки.
создать хранилище ключей
скачать BouncyCastle Provider и хранить его в известном месте. Также убедитесь, что вы можете вызвать команду keytool (обычно она находится в папке bin вашей установки JRE).
теперь импортируйте полученные сертификаты (не импортируйте сертификат конечной точки) в хранилище ключей в формате BouncyCastle.
Я не проверял, но я думаю, что порядок импорт сертификатов имеет важное значение. Это означает, что сначала импортируйте самый нижний промежуточный сертификат CA, а затем вплоть до корневого сертификата CA.
С помощью следующей команды новое хранилище ключей (если уже не существует) с паролем mysecret будет создан и промежуточный сертификат CA будет импортирован. Я также определил поставщика BouncyCastle, где его можно найти в моей файловой системе и формате хранилища ключей. Выполните эту команду для каждого сертификат в цепочке.
keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecretпроверьте, правильно ли были импортированы сертификаты в хранилище ключей:
keytool -list -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecretдолжен выводить всю цепочку:
RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93 IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43теперь вы можете скопировать хранилище ключей в качестве исходного ресурса в вашем приложении для android под
res/raw/используйте хранилище ключей в вашем приложении
прежде всего, мы должны создать пользовательский Apache HttpClient, который использует наше хранилище ключей для HTTPS-соединений:
public class MyHttpClient extends DefaultHttpClient { final Context context; public MyHttpClient(Context context) { this.context = context; } @Override protected ClientConnectionManager createClientConnectionManager() { SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); // Register for port 443 our SSLSocketFactory with our keystore // to the ConnectionManager registry.register(new Scheme("https", newSslSocketFactory(), 443)); return new SingleClientConnManager(getParams(), registry); } private SSLSocketFactory newSslSocketFactory() { try { // Get an instance of the Bouncy Castle KeyStore format KeyStore trusted = KeyStore.getInstance("BKS"); // Get the raw resource, which contains the keystore with // your trusted certificates (root and any intermediate certs) InputStream in = context.getResources().openRawResource(R.raw.mykeystore); try { // Initialize the keystore with the provided trusted certificates // Also provide the password of the keystore trusted.load(in, "mysecret".toCharArray()); } finally { in.close(); } // Pass the keystore to the SSLSocketFactory. The factory is responsible // for the verification of the server certificate. SSLSocketFactory sf = new SSLSocketFactory(trusted); // Hostname verification from certificate // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506 sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); return sf; } catch (Exception e) { throw new AssertionError(e); } } }у нас есть создали пользовательский класс HttpClient, теперь мы можем просто использовать его для безопасных соединений. Например, когда мы делаем вызов GET для ресурса REST.
// Instantiate the custom HttpClient DefaultHttpClient client = new MyHttpClient(getApplicationContext()); HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23"); // Execute the GET call and obtain the response HttpResponse getResponse = client.execute(get); HttpEntity responseEntity = getResponse.getEntity();вот именно ;)
если у вас есть пользовательский/самозаверяющий сертификат на сервере, которого нет на устройстве, вы можете использовать класс ниже, чтобы загрузить его и использовать его на стороне клиента в Android:
место сертификат на
/res/rawТак что он доступен изR.raw.*используйте класс ниже, чтобы получить
HTTPClientилиHttpsURLConnectionкоторый будет иметь фабрику сокетов, используя этот сертификат:package com.example.customssl; import android.content.Context; import org.apache.http.client.HttpClient; 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.AllowAllHostnameVerifier; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; public class CustomCAHttpsProvider { /** * Creates a {@link org.apache.http.client.HttpClient} which is configured to work with a custom authority * certificate. * * @param context Application Context * @param certRawResId R.raw.id of certificate file (*.crt). Should be stored in /res/raw. * @param allowAllHosts If true then client will not check server against host names of certificate. * @return Http Client. * @throws Exception If there is an error initializing the client. */ public static HttpClient getHttpClient(Context context, int certRawResId, boolean allowAllHosts) throws Exception { // build key store with ca certificate KeyStore keyStore = buildKeyStore(context, certRawResId); // init ssl socket factory with key store SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore); // skip hostname security check if specified if (allowAllHosts) { sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); } // basic http params for client HttpParams params = new BasicHttpParams(); // normal scheme registry with our ssl socket factory for "https" SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", sslSocketFactory, 443)); // create connection manager ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry); // create http client return new DefaultHttpClient(cm, params); } /** * Creates a {@link javax.net.ssl.HttpsURLConnection} which is configured to work with a custom authority * certificate. * * @param urlString remote url string. * @param context Application Context * @param certRawResId R.raw.id of certificate file (*.crt). Should be stored in /res/raw. * @param allowAllHosts If true then client will not check server against host names of certificate. * @return Http url connection. * @throws Exception If there is an error initializing the connection. */ public static HttpsURLConnection getHttpsUrlConnection(String urlString, Context context, int certRawResId, boolean allowAllHosts) throws Exception { // build key store with ca certificate KeyStore keyStore = buildKeyStore(context, certRawResId); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); // Create a connection from url URL url = new URL(urlString); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.setSSLSocketFactory(sslContext.getSocketFactory()); // skip hostname security check if specified if (allowAllHosts) { urlConnection.setHostnameVerifier(new AllowAllHostnameVerifier()); } return urlConnection; } private static KeyStore buildKeyStore(Context context, int certRawResId) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { // init a default key store String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); // read and add certificate authority Certificate cert = readCert(context, certRawResId); keyStore.setCertificateEntry("ca", cert); return keyStore; } private static Certificate readCert(Context context, int certResourceId) throws CertificateException, IOException { // read certificate resource InputStream caInput = context.getResources().openRawResource(certResourceId); Certificate ca; try { // generate a certificate CertificateFactory cf = CertificateFactory.getInstance("X.509"); ca = cf.generateCertificate(caInput); } finally { caInput.close(); } return ca; } }ключевые моменты:
Certificateобъекты генерируется из.crtфайлы.- по умолчанию это.
keyStore.setCertificateEntry("ca", cert)добавить сертификат в хранилище ключей под псевдонимом "ка". Вы изменяете код, чтобы добавить больше сертификатов (промежуточный CA и т. д.).- основная цель состоит в том, чтобы создать
SSLSocketFactoryкоторый затем может быть использованHTTPClientилиHttpsURLConnection.SSLSocketFactoryможно настроить дополнительно, например, пропустить проверку имени хоста и т. д.Подробнее : http://developer.android.com/training/articles/security-ssl.html
Я был разочарован, пытаясь подключить мое приложение для Android к моей службе RESTful с помощью https. Также я был немного раздражен всеми ответами, которые предлагали полностью отключить проверку сертификатов. Если вы это сделаете, в чем смысл https?
после того, как погуглил по теме некоторое время, я наконец нашел этой решение, где внешние банки не нужны,просто Android API. Спасибо Эндрю Смиту, который опубликовал его в июле 2014 года
/** * Set up a connection to myservice.domain using HTTPS. An entire function * is needed to do this because myservice.domain has a self-signed certificate. * * The caller of the function would do something like: * HttpsURLConnection urlConnection = setUpHttpsConnection("https://littlesvr.ca"); * InputStream in = urlConnection.getInputStream(); * And read from that "in" as usual in Java * * Based on code from: * https://developer.android.com/training/articles/security-ssl.html#SelfSigned */ public static HttpsURLConnection setUpHttpsConnection(String urlString) { try { // Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...) CertificateFactory cf = CertificateFactory.getInstance("X.509"); // My CRT file that I put in the assets folder // I got this file by following these steps: // * Go to https://littlesvr.ca using Firefox // * Click the padlock/More/Security/View Certificate/Details/Export // * Saved the file as littlesvr.crt (type X.509 Certificate (PEM)) // The MainActivity.context is declared as: // public static Context context; // And initialized in MainActivity.onCreate() as: // MainActivity.context = getApplicationContext(); InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("littlesvr.crt")); Certificate ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); // Tell the URLConnection to use a SocketFactory from our SSLContext URL url = new URL(urlString); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); return urlConnection; } catch (Exception ex) { Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString()); return null; } }Он работал хорошо для моего макета приложения.
верхний ответ не работает для меня. После некоторого расследования я нашел необходимую информацию о " Android Developer": https://developer.android.com/training/articles/security-ssl.html#SelfSigned
создание пустой реализации X509TrustManager сделал трюк:
private static class MyTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } ... HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); try { // Create an SSLContext that uses our TrustManager SSLContext context = SSLContext.getInstance("TLS"); TrustManager[] tmlist = {new MyTrustManager()}; context.init(null, tmlist, null); conn.setSSLSocketFactory(context.getSocketFactory()); } catch (NoSuchAlgorithmException e) { throw new IOException(e); } catch (KeyManagementException e) { throw new IOException(e); } conn.setRequestMethod("GET"); int rcode = conn.getResponseCode();имейте в виду, что эта пустая реализация TustManager является всего лишь примером, и использование его в продуктивной среде приведет к серьезной безопасности угроза!
вот как вы можете добавить дополнительные сертификаты в хранилище ключей, чтобы избежать этой проблемы: доверие всем сертификатам с помощью HttpClient через HTTPS
Он не будет запрашивать пользователя, как вы просите, но это сделает его менее вероятным, что пользователь столкнется с ошибкой "не доверенный сертификат сервера".
Google рекомендует использовать Android Volley для HTTP/HTTPS соединений, так что
HttpClientустарела. Итак, вы знаете правильный выбор :).и Never NUKE SSL сертификаты (никогда!!!).
чтобы уничтожить SSL-сертификаты, полностью противоречит цели SSL, которая продвигает безопасность. Нет смысла использовать SSL, если вы планируете бомбить все сертификаты SSL, которые приходят. Лучшим решением будет, не используя SSL, или лучшее решение, будет создавать пользовательский
TrustManagerв вашем приложении + использование Android Volley для соединений HTTP/HTTPS.здесь суть который я создал, с базовым LoginApp, выполняя HTTPS-соединения, используя Самозаверяющий сертификат на стороне сервера, принятый в приложении.
вот еще суть это может помочь, для создания Самозаверяющих сертификатов SSL для настройки на вашем сервере, а также с помощью сертификат на ваше приложение. очень важно: вы должны скопировать .crt-файл, который был сгенерирован скриптом выше, в каталог" raw " из вашего проекта Android.
Я написал небольшую библиотеку ssl-utils-android доверять конкретному сертификату на Android.
вы можете просто загрузить любой сертификат, указав имя файла из каталога активов.
использование:
OkHttpClient client = new OkHttpClient(); SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer"); client.setSslSocketFactory(sslContext.getSocketFactory());
ни одно из этих исправлений не работало для моей платформы разработки, ориентированной на SDK 16, выпуск 4.1.2, поэтому я нашел обходной путь.
мое приложение хранит данные на сервере с помощью "http://www.example.com/page.php?data=somedata"
в последнее время страницы.php был перемещен в "https://www.secure-example.com/page.php "и я продолжаю получать" javax.net.ssl.SSLException: не доверенный сертификат сервера".
вместо того, чтобы принимать все сертификаты только для одного Пейдж,начиная с этого руководства я решил свою проблему, написав свою собственную страницу.php опубликовано на"http://www.example.com/page.php"
<?php caronte ("https://www.secure-example.com/page.php"); function caronte($url) { // build curl request $ch = curl_init(); foreach ($_POST as $a => $b) { $post[htmlentities($a)]=htmlentities($b); } curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post)); // receive server response ... curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $server_output = curl_exec ($ch); curl_close ($ch); echo $server_output; } ?>
может быть, это будет полезно... он работает на Java-клиентах с использованием самозаверяющих сертификатов (нет проверки сертификата). Будьте осторожны и используйте его только для случаев разработки, потому что это не безопасно вообще!!
как игнорировать ошибки SSL сертификата в Apache HttpClient 4.0
надеюсь, что он будет работать на Android, просто добавляя библиотеку HttpClient... удачи вам!!
это проблема, возникающая из-за отсутствия поддержки SNI(идентификации имени сервера) inA,ndroid 2.х. Я боролся с этой проблемой за неделю, пока я не наткнулся на следующий вопрос, который не только дает хороший фон проблемы, но и обеспечивает работу и эффективное лишена решение любых дыр в безопасности.
ошибка "нет сертификата сверстника" в Android 2.3, но не в 4
самый простой способ для создания SSL-сертификата
откройте Firefox (я полагаю, что это также возможно с Chrome, но мне легче с FF)
посетите сайт разработки с самозаверяющим сертификатом SSL.
нажмите на сертификат (рядом с названием сайта)
нажмите на кнопку "дополнительная информация"
нажмите на кнопку "Просмотр сертификата"
нажмите на кнопку "подробности"
нажмите на "Экспортный..."
выберите " X. 509 сертификат с цепочкой (PEM)", выберите папку и имя, чтобы сохранить его и нажмите кнопку"Сохранить"
перейдите в командную строку, в каталог, где вы загрузили файл pem и выполните "openssl x509-inform PEM-outform DM-in .Пем-аут .ЭЛТ"
скопировать .crt-файл в корень папки / sdcard внутри вашего устройства Android Внутри вашего устройства Android, Настройки > Безопасность > установить из хранилища.
Он должен обнаружить сертификат и предложит добавить его на устройство Перейдите на сайт разработки.
в первый раз он должен попросить вас Подтвердить исключение безопасности. Вот и все.
сертификат должен работать с любым браузером, установленным на вашем Android (браузер, Chrome, Opera, Dolphin...)
помните, что если вы обслуживаете свои статические файлы из другого домена (мы все суки скорости страницы), вам также нужно добавить сертификат для этого домена.
Comments