Ответ 1
ОБНОВЛЕНИЕ ДЛЯ OKHTTP 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. Создание TrustStore
Чтобы связать сертификат, сначала необходимо создать доверительный магазин, содержащий этот сертификат. Чтобы создать доверительный магазин, мы будем использовать этот удобный script из неленков, слегка модифицированных для наших целей:
#!/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..."
Для запуска этого script вам понадобятся 3 вещи:
- Убедитесь, что
keytool
(входит в Android SDK) находится на вашем $PATH. - Удостоверьтесь, что у вас есть последняя версия файла jar файла BouncyCastle в том же каталоге, что и script. (Загрузить здесь)
- Сертификат, который вы хотите вывести.
Теперь запустите script
./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
пароль, который вы передали в оболочку script. Ваш экземпляр OkHttp теперь должен принимать только указанный вами сертификат.