CertPathValidatorException: nie znaleziono kotwicy zaufania dla ścieżki certyfikatu - Retrofit Android
Tworzę aplikację na Androida, która wykorzystuje https
do komunikacji z serwerem. Używam retrofit
i OkHttp
do składania wniosków. Działa to dobrze w przypadku standardowych zapytań http
. Oto kroki, które wykonałem.
Krok 1: Pozyskanie pliku cert z serwera za pomocą polecenia
echo -n | openssl s_client -connect api.****.tk:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > gtux.cert
Krok 2: Konwertowanie cert do formatu BKS za pomocą następujących poleceń
keytool -importcert -v -trustcacerts -file "gtux.cert" -alias imeto_alias -keystore "my_keystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-146.jar" -storetype BKS
Poprosił mnie o hasło i plik został pomyślnie utworzony.
Krok 3 :
Utwórz OkHttpClient i użyj tego samego do tworzenia żądań https
public class MySSLTrust {
public static OkHttpClient trustcert(Context context){
OkHttpClient okHttpClient = new OkHttpClient();
try {
KeyStore ksTrust = KeyStore.getInstance("BKS");
InputStream instream = context.getResources().openRawResource(R.raw.my_keystore);
ksTrust.load(instream, "secret".toCharArray());
// TrustManager decides which certificate authorities to use.
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ksTrust);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
e.printStackTrace();
}
return okHttpClient;
}
}
Krok 4:
RestAdapter musi zostać utworzony
RestAdapter.Builder()
.setRequestInterceptor(intercept)
.setEndpoint("https://api.****.tk")
.setClient(new OkClient(this))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog("RETROFIT"))
.build();
Ale w końcu po uruchomieniu aplikacji rzuca mnie CertPathValidatorException : Trust anchor for certificate path not found
. Proszę, pomóż mi to rozwiązać. Dziękuję.
Inne próby niepowodzenia: Próbowałem zainstalować certyfikat w moim Xperia Z2 i mówi, że plik został zainstalowany, ale kiedy uruchamiam aplikację ten sam wyjątek jest wyrzucany.
Dziennik Błędów Oto dziennik błędów, który dostałem podczas wykonywania...
Wklejone tam, aby było łatwe do odczytania..
8 answers
Uwaga: ta odpowiedź pochodzi zJul 2015 i używaRetrofit iOkHttp od tego czasu.
Sprawdź ten link aby uzyskać więcej informacji na temat modernizacji v2 i ten dla obecnych metod OkHttp.
Okay, mam go działa Android Developers guide.
Tak samo jak OP, próbuję użyćRetrofit i OkHttp aby połączyć się z własnoręcznie podpisanym SSL-enabled serwer.
Oto kod, który działa (usunąłem bloki try/catch):
public static RestAdapter createAdapter(Context context) {
// loading CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream cert = context.getResources().openRawResource(R.raw.my_cert);
Certificate ca;
try {
ca = cf.generateCertificate(cert);
} finally { cert.close(); }
// creating a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// creating a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// creating an SSLSocketFactory that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
// creating an OkHttpClient that uses our SSLSocketFactory
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
// creating a RestAdapter that uses this custom client
return new RestAdapter.Builder()
.setEndpoint(UrlRepository.API_BASE)
.setClient(new OkClient(okHttpClient))
.build();
}
Aby pomóc w debugowaniu, dodałem również .setLogLevel(RestAdapter.LogLevel.FULL)
do moich poleceń tworzenia Restadaptera i widziałem, jak łączy się i otrzymuje odpowiedź z serwera.
Wystarczyło moje oryginalne .plik crt zapisany w main/res/raw
.
.plik crt , zwany także certyfikatem, jest jednym z dwóch plików utworzonych podczas tworzenia certyfikatu za pomocą openssl
. Ogólnie rzecz biorąc, jest to .crt lub .cert plik, podczas gdy drugi jest a .plik klucza.
Afaik, the .plik crt to twój klucz publiczny iplik klucza jest Twoim kluczem prywatnym.
Jak widzę, masz już .plik cert , który jest taki sam, więc spróbuj go użyć.
PS: dla tych, którzy przeczytają ją w przyszłości i mają tylko .pem Plik, zgodnie z ta odpowiedź , wystarczy, że przekonwertujesz jeden na drugi:
openssl x509 -outform der -in your-cert.pem -out your-cert.crt
PS2: dla tych, którzy nie ma żadnego pliku, możesz użyć następującego polecenia (bash), aby wyodrębnić klucz publiczny (aka certificate) z dowolnego serwera: {]}
echo -n | openssl s_client -connect your.server.com:443 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/my_cert.crt
Wystarczy zastąpić your.server.com
i port (jeśli nie jest to standardowy HTTPS) i wybrać prawidłową ścieżkę dla pliku wyjściowego do utworzenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-11 03:05:29
Use the below code to solve the CertPathValidatorException issue.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(YOUR_BASE_URL)
.client(getUnsafeOkHttpClient().build())
.build();
public static OkHttpClient.Builder getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Więcej szczegółów na stronie https://mobikul.com/android-retrofit-handling-sslhandshakeexception/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-02 05:15:22
Nie używam Retrofit i dla OkHttp Oto jedyne rozwiązanie dla własnoręcznie podpisanego certyfikatu, które zadziałało dla mnie:
-
Pobierz certyfikat z naszej strony jak w pytaniu Gowthama i umieść go w res/raw dir projektu:
echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt
Użyj Paulo answer aby ustawić fabrykę ssl (obecnie używając OkHttpClient.Builder () ) ale bez RestAdapter creation.
Następnie dodaj następujący rozwiązanie do naprawy: SSLPeerUnverifiedException: Nazwa hosta nie została zweryfikowana
Więc koniec kodu (po sslContext initialization) który działa u mnie wygląda następująco:
...
OkHttpClient.Builder builder = new OkHttpClient.Builder().sslSocketFactory(sslContext.getSocketFactory());
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return "secure.elkews.com".equalsIgnoreCase(hostname);
});
OkHttpClient okHttpClient = builder.build();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-05 14:15:38
Retrofit 2.3.0
// Load CAs from an InputStream
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
InputStream inputStream = context.getResources().openRawResource(R.raw.ssl_certificate); //(.crt)
Certificate certificate = certificateFactory.generateCertificate(inputStream);
inputStream.close();
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", certificate);
// Create a TrustManager that trusts the CAs in our KeyStore.
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
X509TrustManager x509TrustManager = (X509TrustManager) trustManagers[0];
// Create an SSLSocketFactory that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
//create Okhttp client
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory,x509TrustManager)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-24 19:36:47
Oto Wersja Kotlina.
Dzięki:)
fun unSafeOkHttpClient() :OkHttpClient.Builder {
val okHttpClient = OkHttpClient.Builder()
try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts: Array<TrustManager> = arrayOf(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?){}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
})
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
if (trustAllCerts.isNotEmpty() && trustAllCerts.first() is X509TrustManager) {
okHttpClient.sslSocketFactory(sslSocketFactory, trustAllCerts.first() as X509TrustManager)
okHttpClient.hostnameVerifier { _, _ -> true }
}
return okHttpClient
} catch (e: Exception) {
return okHttpClient
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-23 06:20:53
Konwertujesz cert na Keystore BKS, dlaczego nie używasz .cert
bezpośrednio, z https://developer.android.com/training/articles/security-ssl.html :
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream instream = context.getResources().openRawResource(R.raw.gtux_cert);
Certificate ca;
try {
ca = cf.generateCertificate(instream);
} finally {
caInput.close();
}
KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
kStore.load(null, null);
kStore.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(););
tmf.init(kStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
okHttpClient.setSslSocketFactory(context.getSocketFactory());
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-02 11:22:07
Implementacja w Kotlinie: Retrofit 2.3.0
private fun getUnsafeOkHttpClient(mContext: Context) :
OkHttpClient.Builder? {
var mCertificateFactory : CertificateFactory =
CertificateFactory.getInstance("X.509")
var mInputStream = mContext.resources.openRawResource(R.raw.cert)
var mCertificate : Certificate = mCertificateFactory.generateCertificate(mInputStream)
mInputStream.close()
val mKeyStoreType = KeyStore.getDefaultType()
val mKeyStore = KeyStore.getInstance(mKeyStoreType)
mKeyStore.load(null, null)
mKeyStore.setCertificateEntry("ca", mCertificate)
val mTmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val mTrustManagerFactory = TrustManagerFactory.getInstance(mTmfAlgorithm)
mTrustManagerFactory.init(mKeyStore)
val mTrustManagers = mTrustManagerFactory.trustManagers
val mSslContext = SSLContext.getInstance("SSL")
mSslContext.init(null, mTrustManagers, null)
val mSslSocketFactory = mSslContext.socketFactory
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(mSslSocketFactory, mTrustManagers[0] as X509TrustManager)
builder.hostnameVerifier { _, _ -> true }
return builder
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-02 08:37:28
Po długim reserchu i zbyt głębokim kopaniu znalazłem rozwiązanie przypinania certyfikatów w Androidzie i tak różni się od iOS, gdzie potrzebujemy samego certyfikatu, ale w Androidzie potrzebujemy tylko hash Pina i to wszystko.
Jak uzyskać hash pin dla certyfikatu?
Początkowo po prostu użyj niewłaściwego hash Pina, a twoja klasa java wyrzuci błąd z poprawnymi hash pinami lub łańcuchem pinów, po prostu skopiuj i wklej do kodu.
To rozwiązanie naprawiło mój problem : https://stackoverflow.com/a/45853669/3448003
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-24 07:25:57