본문 바로가기
Android/Features

[Android] android java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 에러 원인 및 해결 방법

by startSW 2023. 11. 13.

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 에러는 일반적으로 안드로이드 앱이 SSL/TLS 연결을 설정하려고 할 때 발생합니다. 이 에러의 주요 원인은 앱이 서버의 SSL/TLS 인증서를 신뢰할 수 없거나 인증서 체인을 검증할 수 없을 때입니다.

 

저는 서버에서 사설 인증서를 사용했을 때 발생했습니다.

해결 방법은 서버에서 공인 인증서를 사용하도록 수정하는 것이 완벽한 해결책인데요.

가끔씩은 서버쪽 개발 리소스가 부족해서 그 상태가 며칠간 유지되는 경우가 있을 수 있습니다.

이럴때 해결방법은 앱이 사설 인증서도 사용할 수 있게끔 임시로 수정해 주는 것입니다.

여기서 그 방법을 알아 보겠습니다.

 

앱 수전의 build.gradle 에 아래와 같이 라이브러리를 추가합니다.

implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.5'

 

그리고 Utile 패키지에 아래와 같은 유틸 클래스를 만듭니다.

import okhttp3.OkHttpClient
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager


object UnsafeOkHttpClient {

    val unsafeOkHttpClient: OkHttpClient.Builder
        get() = try {
            // Create a trust manager that does not validate certificate chains
            val trustAllCerts = arrayOf<TrustManager>(
                object : X509TrustManager {
                    @Throws(CertificateException::class)
                    override fun checkClientTrusted(
                        chain: Array<X509Certificate>,
                        authType: String
                    ) {
                    }

                    @Throws(CertificateException::class)
                    override fun checkServerTrusted(
                        chain: Array<X509Certificate>,
                        authType: String
                    ) {
                    }

                    override fun getAcceptedIssuers(): Array<X509Certificate> {
                        return 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: SSLSocketFactory = sslContext.socketFactory
            val builder = OkHttpClient.Builder()
            builder.sslSocketFactory(
                sslSocketFactory,
                (trustAllCerts[0] as X509TrustManager)
            )
            builder.hostnameVerifier { hostname, session -> true }
            builder
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
}

 

 

위 유틸 클래스는 실제로 다음과 같이 사용될 수 있습니다.

 

1. Network Moduel 에서 사용하는 방법

	@Singleton
        @Provides
        fun provideOkHttpClient(
            addHeaderInterceptor: Interceptor,
            httpLoggingInterceptor: HttpLoggingInterceptor
        ): OkHttpClient {

    /*
            // Original Code        
            return OkHttpClient.Builder()
                .addInterceptor(addHeaderInterceptor)
                .addInterceptor(httpLoggingInterceptor)
                .build()

     */
            return unsafeOkHttpClient
                .addInterceptor(addHeaderInterceptor)
                .addInterceptor(httpLoggingInterceptor)
                .build()
    }

 

2. Glide 라이브러리에서 사용하는 방법

    - registerComponents 함수를 오버라이딩 해줍니다.

@GlideModule
class FDGlideApp : AppGlideModule() {
    override fun applyOptions(context: Context, builder: GlideBuilder) {
        super.applyOptions(context, builder)
        builder.apply {
            RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL).signature(
                ObjectKey("NCAA_" + System.currentTimeMillis().toShort())
            )
        }
    }


    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
        val okHttpClient = unsafeOkHttpClient.build()//getUnsafeOkHttpClient()
        registry.replace<GlideUrl, InputStream>(
            GlideUrl::class.java,
            InputStream::class.java, OkHttpUrlLoader.Factory(okHttpClient)
        )
    }
}

 

위 방법으로 서버에서 공인 인증서로 교체하기 전까지 개발을 계속할 수 있습니다.