CertPathValidatorException : 인증서 경로에 대한 신뢰 앵커를 찾을 수 없음-Retrofit Android
https
서버와의 통신에 사용하는 Android 응용 프로그램을 만들고 있습니다. 나는 요청을 위해 사용 retrofit
하고 OkHttp
있습니다. 이들은 표준 http
요청에 대해 잘 작동 합니다. 다음은 내가 따라 간 단계입니다.
1 단계 : 명령을 사용하여 서버에서 인증서 파일 가져 오기
echo -n | openssl s_client -connect api.****.tk:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > gtux.cert
2 단계 : 다음 명령을 사용하여 인증서를 BKS 형식으로 변환
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
암호를 요청했고 파일이 성공적으로 생성되었습니다.
3 단계 :
OkHttpClient를 만들고 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;
}
}
4 단계 :
RestAdapter를 만들어야합니다.
RestAdapter.Builder()
.setRequestInterceptor(intercept)
.setEndpoint("https://api.****.tk")
.setClient(new OkClient(this))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog("RETROFIT"))
.build();
그러나 마지막으로 앱을 실행하면 나를 던지고 CertPathValidatorException : Trust anchor for certificate path not found
있습니다. 이 문제를 해결하도록 도와주세요. 감사합니다.
기타 실패 시도 : 내 Xperia Z2에 인증서를 설치 하려고 시도 했는데 파일이 설치되었다고 표시되지만 앱을 실행하면 동일한 예외가 발생합니다.
오류 로그 다음은 실행 중 발생한 오류 로그입니다.
읽기 쉽도록 붙여 넣었습니다 ..
면책 조항 : 이 답변은 2015 년 7 월의 것이며 그 당시부터 Retrofit 및 OkHttp 를 사용합니다.
확인 이 링크를 더 개조 v2의 정보와 위해 이 일 현재 OkHttp 방법에 대한.
알겠습니다 . Android 개발자 가이드를 사용하여 작업했습니다 .
OP 와 마찬가지로 Retrofit 및 OkHttp 를 사용하여 자체 서명 된 SSL 사용 서버에 연결 하려고 합니다.
작동하는 코드는 다음과 같습니다 (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();
}
디버깅을 돕기 위해 .setLogLevel(RestAdapter.LogLevel.FULL)
RestAdapter 생성 명령 도 추가 했으며 연결되어 서버에서 응답을받는 것을 볼 수있었습니다.
.crt에 저장된 원본 .crt 파일 만 있으면됩니다 main/res/raw
. 에 .crt 파일은 인증서 일명, 당신이 사용하여 인증서를 만들 때 생성 된 두 파일 중 하나입니다 openssl
. 일반적으로 .crt 또는 .cert 파일이고 다른 하나는 .key 파일입니다.
Afaik, .crt 파일은 공개 키 이고 .key 파일은 개인 키입니다.
내가 볼 수 있듯이 이미 .cert 파일이 있으므로 동일한 파일을 사용하십시오.
추신 : 미래에 그것을 읽고 .pem 파일 만 가진 사람들의 경우이 답변 에 따르면 하나를 다른 것으로 변환하는 데 필요합니다.
openssl x509 -outform der -in your-cert.pem -out your-cert.crt
PS² : 파일이 전혀없는 경우 다음 명령 (bash)을 사용하여 모든 서버에서 공개 키 (인증서)를 추출 할 수 있습니다.
echo -n | openssl s_client -connect your.server.com:443 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/my_cert.crt
your.server.com
및 포트 (표준 HTTPS가 아닌 경우)를 바꾸고 생성 할 출력 파일의 유효한 경로를 선택하기 만하면 됩니다.
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);
}
}
자세한 내용은 https://mobikul.com/android-retrofit-handling-sslhandshakeexception/을 방문 하십시오.
나는 Retrofit을 사용하지 않으며 OkHttp 의 경우 나를 위해 일한 자체 서명 인증서에 대한 유일한 솔루션입니다.
Gowtham의 질문과 같이 우리 사이트에서 인증서를 가져 와서 프로젝트의 res / raw dir에 넣습니다 .
echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt
Paulo 응답 을 사용하여 ssl 팩토리를 설정 하십시오 (현재 OkHttpClient.Builder () 사용 ) 그러나 RestAdapter 생성은 없습니다.
그런 다음 수정 하려면 다음 솔루션을 추가 하십시오. SSLPeerUnverifiedException : 호스트 이름이 확인되지 않았습니다.
따라서 나를 위해 작동하는 Paulo의 코드 ( sslContext 초기화 후)의 끝은 다음과 같습니다.
...
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();
개조 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();
If you have a certificate then you can provide but few webservices will not have the certificate for them please follow below.
// creating a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
// 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);
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
// creating a RestAdapter using the custom client
return new RestAdapter.Builder()
.setEndpoint(UrlRepository.API_BASE)
.setClient(new OkClient(okHttpClient))
.build();
You are converting cert into BKS Keystore, why aren't you using .cert
directly, from 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());
After a long reserch and digging too deep i found the solution of certificate pinning in android and yes its different from iOS where we need a certificate itself but in android we just need a hash pin and that's it.
How to get hash pin for certificate?
Initially just use a wrong hash pin and your java class will throw an error with correct hash pins or pin chain, just copy and paste into your code thats it.
This solution fixed my problem : https://stackoverflow.com/a/45853669/3448003
Implementation in Kotlin : 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
}
Here is Kotlin version.
Thanks you :)
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
}
}
ReferenceURL : https://stackoverflow.com/questions/29273387/certpathvalidatorexception-trust-anchor-for-certificate-path-not-found-retro
'IT Share you' 카테고리의 다른 글
datatable jquery-테이블 헤더 너비가 본문 너비와 정렬되지 않음 (0) | 2021.01.10 |
---|---|
Bash 자동화를 위해 명령 줄에서 Postgres 용 사용자를 만드는 방법 (0) | 2021.01.10 |
Swift에서 증가 된 값으로 배열을 만드는 방법은 무엇입니까? (0) | 2021.01.10 |
std :: begin 및 std :: end가 "메모리 안전하지 않은"이유는 무엇입니까? (0) | 2021.01.10 |
JPA / EJB3 지속성 컨텍스트에서 엔티티 분리 (0) | 2021.01.10 |