#android #ssl #bouncycastle #okhttp
#Android #ssl #bouncycastle #okhttp
Вопрос:
Я использую OkHttp для отправки HTTPS-запросов на сервер в моем приложении для Android. Это нормально с версией Android> Jelly Bean, но следующее исключение выдается с помощью Jelly Bean :
javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Certificate has unsupported critical extension
10-04 12:45:01.391 7570-7735/mop.orange.com.moplibapptest5 I/System.out: at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
10-04 12:45:01.391 7570-7735/mop.orange.com.moplibapptest5 I/System.out: at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:241)
Похоже, это связано с сертификатом сервера SSL / TLS, который содержит 2 расширения с тегом «критический». Я не могу изменить этот сертификат, потому что сервер не мой.
Я попытался включить / отключить поддержку расширений TLS, но поведение такое же: (
ConnectionSpec connSpec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
.supportsTlsExtensions(false)
.build();
OkHttpClient.Builder()
.connectionSpecs(Collections.singletonList(connSpec))
Знаете ли вы, как я могу отключить проверку критического расширения в OkHttp (или в Bouncy Castle, который, кажется, находится под), пожалуйста?
Ответ №1:
Я использую способ OkHttp для настройки доверенных сертификатов, затем я оборачиваю TrustManager и сертификат, чтобы вернуть пустой список критических расширений.
Вот перенос TrustManager, когда это класс Jelly Bean :
if(trustManagers[0].getClass().toString().equals("class org.apache.harmony.xnet.provider.jsse.TrustManagerImpl")) {
logger.debug("Wrapping TrustManager to return no critical extension");
return new TrustManagerNoCritical((X509TrustManager) trustManagers[0]);
}
Вот класс переноса TrustManager для переноса сертификатов в цепочку :
public class TrustManagerNoCritical implements X509TrustManager {
protected X509TrustManager trustManager;
public TrustManagerNoCritical(X509TrustManager realTrustManager) {
trustManager = realTrustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
trustManager.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
List<X509Certificate> certifs = new ArrayList<X509Certificate>();
for (X509Certificate certif : chain) {
certifs.add(new CertificateNoCritical(certif));
}
X509Certificate[] newChain = new X509Certificate[certifs.size()];
newChain = certifs.toArray(newChain);
trustManager.checkServerTrusted(newChain, authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
}
Вот класс сертификата, который переносит реальный сертификат и не возвращает критическое расширение :
public class CertificateNoCritical extends X509Certificate {
protected X509Certificate certif;
public CertificateNoCritical(X509Certificate certificateToWrap) {
certif = certificateToWrap;
}
@Override
public boolean hasUnsupportedCriticalExtension() {
return certif.hasUnsupportedCriticalExtension();
}
@Override
public Set<String> getCriticalExtensionOIDs() {
// Return empty Set to avoid "critical extension" Exception
return new HashSet<String>();
}
@Override
public Set<String> getNonCriticalExtensionOIDs() {
return certif.getNonCriticalExtensionOIDs();
}
@Override
public byte[] getExtensionValue(String oid) {
return certif.getExtensionValue(oid);
}
@Override
public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
certif.checkValidity();
}
@Override
public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {
certif.checkValidity(date);
}
@Override
public int getVersion() {
return certif.getVersion();
}
@Override
public BigInteger getSerialNumber() {
return certif.getSerialNumber();
}
@Override
public Principal getIssuerDN() {
return certif.getIssuerDN();
}
@Override
public Principal getSubjectDN() {
return certif.getSubjectDN();
}
@Override
public Date getNotBefore() {
return certif.getNotBefore();
}
@Override
public Date getNotAfter() {
return certif.getNotAfter();
}
@Override
public byte[] getTBSCertificate() throws CertificateEncodingException {
return certif.getTBSCertificate();
}
@Override
public byte[] getSignature() {
return certif.getSignature();
}
@Override
public String getSigAlgName() {
return certif.getSigAlgName();
}
@Override
public String getSigAlgOID() {
return certif.getSigAlgOID();
}
@Override
public byte[] getSigAlgParams() {
return certif.getSigAlgParams();
}
@Override
public boolean[] getIssuerUniqueID() {
return certif.getIssuerUniqueID();
}
@Override
public boolean[] getSubjectUniqueID() {
return certif.getSubjectUniqueID();
}
@Override
public boolean[] getKeyUsage() {
return certif.getKeyUsage();
}
@Override
public int getBasicConstraints() {
return certif.getBasicConstraints();
}
@Override
public byte[] getEncoded() throws CertificateEncodingException {
return certif.getEncoded();
}
@Override
public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
NoSuchProviderException, SignatureException {
certif.verify(key);
}
@Override
public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException {
certif.verify(key, sigProvider);
}
@Override
public String toString() {
return certif.toString();
}
@Override
public PublicKey getPublicKey() {
return certif.getPublicKey();
}
}