IT Share you

CertPathValidatorException : 인증서 경로에 대한 신뢰 앵커를 찾을 수 없음-Retrofit Android

shareyou 2021. 1. 10. 19:20
반응형

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 월의 것이며 그 당시부터 RetrofitOkHttp 를 사용합니다.
확인 이 링크를 더 개조 v2의 정보와 위해 이 일 현재 OkHttp 방법에 대한.

알겠습니다 . Android 개발자 가이드를 사용하여 작업했습니다 .

OP 마찬가지로 RetrofitOkHttp 를 사용하여 자체 서명 된 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 의 경우 나를 위해 일한 자체 서명 인증서에 대한 유일한 솔루션입니다.

  1. 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
    
  2. Paulo 응답 을 사용하여 ssl 팩토리를 설정 하십시오 (현재 OkHttpClient.Builder () 사용 ) 그러나 RestAdapter 생성은 없습니다.

  3. 그런 다음 수정 하려면 다음 솔루션을 추가 하십시오. 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

반응형