Я столкнулся с этой проблемой вчера, когда переносил RESTful API нашей компании на HTTPS, но использовал самозаверяющие SSL-сертификаты.
Я искал повсюду, но все "правильные" помеченные ответы, которые я нашел, состояли в отключении проверки сертификата, явно переопределяя весь смысл SSL.
Я наконец пришел к решению:
Создание локального хранилища ключей
Чтобы приложение могло проверять свои самозаверяющие сертификаты, необходимо предоставить пользовательское хранилище ключей с сертификатами таким образом, чтобы Android мог доверять вашей конечной точке.
Формат таких пользовательских хранилищ ключей - "BKS" из BouncyCastle , поэтому вам нужна версия 1.46 BouncyCastleProvider, которую вы можете загрузить здесь .
Вам также нужен самоподписанный сертификат, я предполагаю, что он называется self_cert.pem
.
Теперь команда для создания вашего хранилища ключей:
<!-- language: lang-sh -->
$ keytool -import -v -trustcacerts -alias 0 \
-file *PATH_TO_SELF_CERT.PEM* \
-keystore *PATH_TO_KEYSTORE* \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath *PATH_TO_bcprov-jdk15on-146.jar* \
-storepass *STOREPASS*
PATH_TO_KEYSTORE
указывает на файл, в котором будет создано ваше хранилище ключей. Это НЕ ДОЛЖНО СУЩЕСТВОВАТЬ .
PATH_TO_bcprov-jdk15on-146.jar.JAR
- это путь к загруженному .jar-библиотеке.
STOREPASS
- ваш вновь созданный пароль хранилища ключей.
- Включить KeyStore в ваше приложение
Скопируйте только что созданное хранилище ключей из PATH_TO_KEYSTORE
в res/raw/certs.bks
( certs.bks - это просто имя файла; вы можете использовать любое имя, какое пожелаете)
Создайте ключ в res/values/strings.xml
с помощью
<!-- language: lang-xml -->
<resources>
...
<string name="store_pass">*STOREPASS*</string>
...
</resources>
Создать этот класс, который наследует DefaultHttpClient
import android.content.Context;
import android.util.Log;
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.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
public class MyHttpClient extends DefaultHttpClient {
private static Context appContext = null;
private static HttpParams params = null;
private static SchemeRegistry schmReg = null;
private static Scheme httpsScheme = null;
private static Scheme httpScheme = null;
private static String TAG = "MyHttpClient";
public MyHttpClient(Context myContext) {
appContext = myContext;
if (httpScheme == null || httpsScheme == null) {
httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
httpsScheme = new Scheme("https", mySSLSocketFactory(), 443);
}
getConnectionManager().getSchemeRegistry().register(httpScheme);
getConnectionManager().getSchemeRegistry().register(httpsScheme);
}
private SSLSocketFactory mySSLSocketFactory() {
SSLSocketFactory ret = null;
try {
final KeyStore ks = KeyStore.getInstance("BKS");
final InputStream inputStream = appContext.getResources().openRawResource(R.raw.certs);
ks.load(inputStream, appContext.getString(R.string.store_pass).toCharArray());
inputStream.close();
ret = new SSLSocketFactory(ks);
} catch (UnrecoverableKeyException ex) {
Log.d(TAG, ex.getMessage());
} catch (KeyStoreException ex) {
Log.d(TAG, ex.getMessage());
} catch (KeyManagementException ex) {
Log.d(TAG, ex.getMessage());
} catch (NoSuchAlgorithmException ex) {
Log.d(TAG, ex.getMessage());
} catch (IOException ex) {
Log.d(TAG, ex.getMessage());
} catch (Exception ex) {
Log.d(TAG, ex.getMessage());
} finally {
return ret;
}
}
}
Теперь просто используйте экземпляр **MyHttpClient**
, как если бы вы использовали **DefaultHttpClient**
для выполнения запросов HTTPS, и он будет правильно использовать и проверять ваши самозаверяющие сертификаты SSL.
HttpResponse httpResponse;
HttpPost httpQuery = new HttpPost("https://yourserver.com");
... set up your query ...
MyHttpClient myClient = new MyHttpClient(myContext);
try{
httpResponse = myClient.(peticionHttp);
// Check for 200 OK code
if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {
... do whatever you want with your response ...
}
}catch (Exception ex){
Log.d("httpError", ex.getMessage());
}