ОБНОВЛЕНИЕ ДЛЯ ОХТТП 3.0
OKHTTP 3.0 имеет встроенную поддержку для закрепления сертификатов. Начните с вставки следующего кода:
String hostname = "yourdomain.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
client.newCall(request).execute();
Это не удастся, потому что AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
не является допустимым хэшем вашего сертификата. Вызванное исключение будет иметь правильные хэши вашего сертификата:
javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
Peer certificate chain:
sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=: CN=publicobject.com, OU=PositiveSSL
sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=: CN=COMODO RSA Secure Server CA
sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=: CN=COMODO RSA Certification Authority
sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=: CN=AddTrust External CA Root
Pinned certificates for publicobject.com:
sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
at okhttp3.CertificatePinner.check(CertificatePinner.java)
at okhttp3.Connection.upgradeToTls(Connection.java)
at okhttp3.Connection.connect(Connection.java)
at okhttp3.Connection.connectAndSetOwner(Connection.java)
Убедитесь, что вы добавили их в свой объект CertificatePinner, и вы успешно закрепили свой сертификат:
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
.add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
.add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
.build();
ВСЕ ПРОШЛОЕ ЗДЕСЬ ДЛЯ СТАРЫХ (2.x) ВЕРСИЙ OKHTTP
Прочитав эту запись в блоге Мне удалось изменить концепцию для использования с OkHttp. Вам следует использовать как минимум версию 2.0, если вы хотите избежать использования глобального контекста SSL.
Это изменение применяется только к текущему экземпляру OkHttp и изменяет этот экземпляр таким образом, что он только принимает сертификаты из указанного сертификата. Если вы хотите, чтобы принимались другие сертификаты (например, сертификат из Twitter), вам просто нужно создать новый экземпляр OkHttp без модификаций, описанных ниже.
1. Создание хранилища доверия
Чтобы закрепить сертификат, вам сначала нужно создать хранилище доверенных сертификатов, содержащее этот сертификат. Для создания доверенного хранилища воспользуемся вот этим удобным скриптом от nelenkov, немного модифицированным для наших целей:
#!/bin/bash
if [ "$#" -ne 3 ]; then
echo "Usage: importcert.sh <CA cert PEM file> <bouncy castle jar> <keystore pass>"
exit 1
fi
CACERT=$1
BCJAR=$2
SECRET=$3
TRUSTSTORE=mytruststore.bks
ALIAS=`openssl x509 -inform PEM -subject_hash -noout -in $CACERT`
if [ -f $TRUSTSTORE ]; then
rm $TRUSTSTORE || exit 1
fi
echo "Adding certificate to $TRUSTSTORE..."
keytool -import -v -trustcacerts -alias $ALIAS \
-file $CACERT \
-keystore $TRUSTSTORE -storetype BKS \
-providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath $BCJAR \
-storepass $SECRET
echo ""
echo "Added '$CACERT' with alias '$ALIAS' to $TRUSTSTORE..."
Для запуска этого скрипта вам нужно 3 вещи:
- Убедитесь, что
keytool
(входит в Android SDK) находится в вашей переменной $PATH.
- Убедитесь, что у вас есть последний загружаемый файл jar BouncyCastle в том же каталоге, что и скрипт. (Скачать здесь)
- Сертификат, который вы хотите закрепить.
Теперь запустите скрипт
./gentruststore.sh your_cert.pem bcprov-jdk15on-150.jar your_secret_pass
Введите «да», чтобы доверять сертификату, и по завершении mytruststore.bks
будет сгенерирован в вашем текущем каталоге.
2. Примените TrustStore к своему проекту Android
Создайте каталог raw
в папке res
. Скопируйте mytruststore.bks
сюда.
Вот очень простой класс, который прикрепляет ваш сертификат к OkHttp.
import android.content.Context;
import android.util.Log;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.InputStream;
import java.io.Reader;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
/**
* Created by martin on 02/06/14.
*/
public class Pinning {
Context context;
public static String TRUST_STORE_PASSWORD = "your_secret";
private static final String ENDPOINT = "https://api.yourdomain.com/";
public Pinning(Context c) {
this.context = c;
}
private SSLSocketFactory getPinnedCertSslSocketFactory(Context context) {
try {
KeyStore trusted = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.mytruststore);
trusted.load(in, TRUST_STORE_PASSWORD.toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trusted);
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
return sslContext.getSocketFactory();
} catch (Exception e) {
Log.e("MyApp", e.getMessage(), e);
}
return null;
}
public void makeRequest() {
try {
OkHttpClient client = new OkHttpClient();
client.setSslSocketFactory(getPinnedCertSslSocketFactory(context));
Request request = new Request.Builder()
.url(ENDPOINT)
.build();
Response response = client.newCall(request).execute();
Log.d("MyApp", response.body().string());
} catch (Exception e) {
Log.e("MyApp", e.getMessage(), e);
}
}
}
Как видите, мы создаем новый экземпляр OkHttpClient
и вызываем setSslSocketFactory
, передавая SSLSocketFactory
с нашим пользовательским хранилищем доверенных сертификатов. Убедитесь, что вы установили TRUST_STORE_PASSWORD
на пароль, который вы передали в сценарий оболочки. Теперь ваш экземпляр OkHttp должен принимать только указанный вами сертификат.
person
Martin Konecny
schedule
03.06.2014