며칠 삽질의 기억을.. 잊지 않기 위해 남긴다. 미래의 나에게 알리기 위해..

 

수정:

인증서 인증기관(CA)의 인증서를 google에서 다운로드 받는 부분 정정

 

문제의 발단..

 

배포 중인 앱을 사용하는 고객사 중 https를 사용하는 고객사들이 늘어나고 있다.

SSL 관련 에러 runtime 에러 중 handshake 관련 에러, CertPathValidation 관련 에러.. 

구글링해보면 가장 많이 나오는 내용이다. 하나같이 보안이 취약해짐을 경고한다.

/**
     * Trust every server - don't check for any certificate
     */
    private void trustAllHosts() {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[] {};
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }
        }};

        // Install the all-trusting trust manager
        try {
            SSLContext sc = SSLContext.getInstance("SSL");//TLS
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            });
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

그런데, 웹페이지에서 첨부 파일을 다운로드 하려고 하면, 에러 없이 해당 코드 다 지나가는데 정작 다운로드가 안된다.

 

2022-02-18 09:36:43.932 5444-18175/? E/CONSCRYPT: ------------------Untrusted chain: ----------------------
2022-02-18 09:36:43.932 5444-18175/? E/CONSCRYPT: == Chain0 == 
     Version:   3
2022-02-18 09:36:43.932 5444-18175/? E/CONSCRYPT:  AuthorityKeyIdentifier:   41830168014f5cdd53c0850f96a4f3ab797da5683e669d268f7
2022-02-18 09:36:43.934 5444-18175/? E/CONSCRYPT:  SubjectKeyIdentifier:   41604144ddfc81a25282145b8a8d443e77730654eaf2141
2022-02-18 09:36:43.934 5444-18175/? E/CONSCRYPT:  Serial Number:   57a4fb9c8db5886950fea7c6
2022-02-18 09:36:43.934 5444-18175/? E/CONSCRYPT:  SubjectDN:   CN=work.synopex.com
2022-02-18 09:36:43.935 5444-18175/? E/CONSCRYPT:  IssuerDN:   CN=AlphaSSL CA - SHA256 - G2, O=GlobalSign nv-sa, C=BE
2022-02-18 09:36:43.935 5444-18175/? E/CONSCRYPT:  Get not before:   Tue Dec 07 13:34:04 GMT+09:00 2021
2022-02-18 09:36:43.936 5444-18175/? E/CONSCRYPT:  Get not after:   Sun Jan 08 13:34:04 GMT+09:00 2023
2022-02-18 09:36:43.936 5444-18175/? E/CONSCRYPT:  Sig ALG name:   SHA256withRSA
2022-02-18 09:36:43.936 5444-18175/? E/CONSCRYPT:  Signature:   3f5733ba696f079777d75c5689643ea51dd77397a52783e0929ce8e059c8a54368a05fbebc4930ad2348eaf390165aaacfaa430240643cd1581faf58335f20deff423905fe9b5caa1b9e80cafbdfe738079d8db973b3d993019fece0106f2b58bac3dfd72870d2a669d27f5a93e3ff53ff609b287a7135e5c2afbc35cec764af022479eee2c4e6703fded4cd1464d1fc6497bb9717ea37fd1ab66e970588df32c30f13e4d790a3fa90be2f3b997694bcea3868575d065f58c4606beb5d66cf3c21cb03d1126aeeefd63523b38b5c5949be2a04adbcf263d7822cf7519641c9b0b61ccf032363a1e351a12af8d71c35dc42f440902066bc9662af5c93bc491df7
2022-02-18 09:36:43.981 5444-18175/? E/CONSCRYPT:  Public key:
     
     30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03
     82 01 0f 00 30 82 01 0a 02 82 01 01 00 bb 53 2d 60 f9 db ce
     17 45 aa 55 20 b9 92 ed 67 aa 31 1a 74 14 16 14 5f a3 43 fb
     f5 72 0f f9 fc 2d d7 75 51 04 58 a5 9d d7 23 6c 60 fa 43 40
     53 45 a8 00 61 e4 80 5c 08 a7 42 d7 74 bc 1e af ee cd 2b aa
     ce 3d 73 4a c1 78 c7 d9 f1 9f fd 13 e8 18 6f f5 33 f6 23 48
     46 cd 0c eb 14 c5 99 7d ff a3 93 9e 02 bc ea 72 41 c0 d1 58
     b5 e9 e3 3e 79 79 b1 9f 25 ed 3b e0 85 49 a0 bd 45 04 6d fc
     86 fc 6e e9 0a bc be e1 23 f0 8f 8d 64 1d 3d 72 9b a7 01 c8
     b0 1e b6 60 ef 8a 08 6f a7 52 e3 9e 0e 3a de 73 56 30 03 a1
     62 93 78 59 6d 6f cd 07 e0 a3 a8 00 90 94 cb b6 1e 9b a3 8e
     c3 d9 a5 88 38 0b a0 6d 63 6e 7e c0 8a 3c 8e 17 39 ce ea 17
     29 65 c8 25 01 bd 67 01 88 f2 d1 c7 63 b4 db 67 32 a3 9e 5f
     0a ab fa 4f a2 3a 95 37 1d 18 b2 38 b4 df 9f b7 e5 8d 01 00
     ec 9d 37 af 08 18 76 27 77 02 03 01 00 01
2022-02-18 09:36:43.990 5444-18175/? W/DownloadManager: [402] Stop requested with status HTTP_DATA_ERROR: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
2022-02-18 09:36:44.041 4159-4159/? W/NotificationEntryMgr: removeNotification for unknown key: 0|com.android.providers.downloads|0|1:com.goom.goom|10050
2022-02-18 09:36:47.264 3312-3615/? E/BufferQueueProducer: [Toast[16744]#0] disconnect: not connected (req=1)
2022-02-18 09:36:47.265 16744-17078/com.goom. W/libEGL: EGLNativeWindowType 0x7f08928010 disconnect failed

Trust anchor for certification path not found..

Trust anchor?
https://ko.theastrologypage.com/trust-anchor

'뭐지? 앱이 문젠가? 서버 문젠가?

처음엔 서버 쪽을 의심했다.

에러도 없고 , 무엇보다 같은 앱에서 정상동작 하는, https 사용 고객사가 있었기 때문.. (안되려면 다 안되야지.. 그게 더 수상해..)

nginx 문제 인가 해서 , 바로 tomcat으로 연결해보고.. setHeader("Pragma","no-cache") 도 해보고..

그냥 구글링해서 조금이라도 관련된 내용이면 이것도 해보고 다 해보면서 멘붕 중에..

 

Logcat 에서 No Filter로 보니...

 

2022-02-17 16:56:18.289 6795-5125/? E/CONSCRYPT:  Public key:
     
     30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03
     82 01 0f 00 30 82 01 0a 02 82 01 01 00 bb 53 2d 60 f9 db ce
     17 45 aa 55 20 b9 92 ed 67 aa 31 1a 74 14 16 14 5f a3 43 fb
     f5 72 0f f9 fc 2d d7 75 51 04 58 a5 9d d7 23 6c 60 fa 43 40
     53 45 a8 00 61 e4 80 5c 08 a7 42 d7 74 bc 1e af ee cd 2b aa
     ce 3d 73 4a c1 78 c7 d9 f1 9f fd 13 e8 18 6f f5 33 f6 23 48
     46 cd 0c eb 14 c5 99 7d ff a3 93 9e 02 bc ea 72 41 c0 d1 58
     b5 e9 e3 3e 79 79 b1 9f 25 ed 3b e0 85 49 a0 bd 45 04 6d fc
     86 fc 6e e9 0a bc be e1 23 f0 8f 8d 64 1d 3d 72 9b a7 01 c8
     b0 1e b6 60 ef 8a 08 6f a7 52 e3 9e 0e 3a de 73 56 30 03 a1
     62 93 78 59 6d 6f cd 07 e0 a3 a8 00 90 94 cb b6 1e 9b a3 8e
     c3 d9 a5 88 38 0b a0 6d 63 6e 7e c0 8a 3c 8e 17 39 ce ea 17
     29 65 c8 25 01 bd 67 01 88 f2 d1 c7 63 b4 db 67 32 a3 9e 5f
     0a ab fa 4f a2 3a 95 37 1d 18 b2 38 b4 df 9f b7 e5 8d 01 00
     ec 9d 37 af 08 18 76 27 77 02 03 01 00 01
2022-02-17 16:56:18.295 6795-5125/? W/DownloadManager: [393] Stop requested with status HTTP_DATA_ERROR: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
2022-02-17 16:56:18.296 3837-3848/? D/ConnectivityService: filterNetworkStateForUid() uid: 10362, pid: 6795

DownloadManager 가 Stop 됐다고? 또 그 놈의 Trust anchor 때문에?

 

답답한 마음에 Android Developer 사이트를 둘러보던 중..

예전에도 많이 , 자주 봤던.. HTTPS 관련 페이지를 천천히 정독하기 시작..

 

https://developer.android.com/training/articles/security-ssl

 

HTTPS 및 SSL을 사용한 보안  |  Android 개발자  |  Android Developers

HTTPS 및 SSL을 사용한 보안 현재 기술적으로 전송 계층 보안(TLS)이라고 알려진 보안 소켓 레이어(SSL)는 클라이언트와 서버 간의 암호화된 통신을 위한 공통 기본 토대입니다. 애플리케이션이 SSL을

developer.android.com

https://developer.android.com/training/articles/security-config

 

네트워크 보안 구성  |  Android 개발자  |  Android Developers

앱 개발자가 안전한 구성 파일에서 네트워크 보안 설정을 사용자설정할 수 있는 기능입니다.

developer.android.com

 

그러다 눈에 들어온 부분..

인증서파일을 한번 추가해볼까?

 

 

한번 인증서  추가해보자

인증서 전달 받을 때, 보통 .key 및 (인증서).crt, (체인).crt, (루트).crt 받기도 하고, .key 제외한 인증서만 받는 경우도 있는데

 

여기서 사용할 파일은 (인증서).crt 

 

일단, AndroidManifest.xml 파일에서 아래 부분 추가 안되어 있으면 추가!

    <?xml version="1.0" encoding="utf-8"?>
    <manifest ... >
        <application android:networkSecurityConfig="@xml/network_security_config" //추가
                        ... > 
            ...
        </application>
    </manifest>

 

res>xml>network_security_config.xml 수정

 

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">(도메인 정보 ex)naver.com</domain>
        <trust-anchors>
            <certificates src="@raw/sectigo_org_ca"/>
        </trust-anchors>
    </domain-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">(도메인 정보 ex)google.com</domain>
        <trust-anchors>
            <certificates src="@raw/(인증서 파일 이름)"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

참고로, cleartextTrafficPermitted 옵션은 간단하게 말해 HTTP 연결을 허용할지 여부이다.

 

자세한 내용은 아래 링크 참고

https://developer.android.com/reference/android/security/NetworkSecurityPolicy?hl=ko#isCleartextTrafficPermitted()

 

NetworkSecurityPolicy  |  Android Developers

 

developer.android.com

public HttpsURLConnection postHttps(Context context,String url){
        HttpsURLConnection urlConnection = null;
        try{
            // Load CAs from an InputStream
            // (could be from a resource or ByteArrayInputStream or ...)
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            // From https://www.washington.edu/itconnect/security/ca/load-der.crt

            //인증서 경로 저장
             InputStream  is = context.getResources().openRawResource(R.raw.sectigo_org_ca);
            
            InputStream caInput = new BufferedInputStream(is);
            Certificate ca;
            try {
                ca = cf.generateCertificate(caInput);
            } finally {
                caInput.close();
            }

            // Create a KeyStore containing our trusted CAs
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);

            // Create a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);

            // Create an SSLContext that uses our TrustManager
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), null);

            // Tell the URLConnection to use a SocketFactory from our SSLContext
            URL toUrl = new URL(url);
            urlConnection =
                    (HttpsURLConnection)toUrl.openConnection();
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            urlConnection.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
                    return hv.verify("server_domain",session);
                }
            });
            //urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
            //InputStream in = urlConnection.getInputStream();
            /*InputStream in = urlConnection.getInputStream();
            IOUtils.copy(in, System.out);*/

        }catch (Exception e){
            e.printStackTrace();
        }
        return urlConnection;
    }

와! 이제 그 Trust anchor 도 안뜨고.. 첨부파일 다운로드도 정상적으로 되고..

 

역시.. 귀찮다고 시도도 안해보고 일찌감치 포기하면.. 더 많은 시간, 체력을 뺏긴다는걸 이번 경험으로 또 알게됨.

 

 

그 외 참고 사이트

SSL문제 해결기:
https://modelmaker.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-SSL-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%EA%B8%B0
인증서 정보 찾기 : https://info-lab.tistory.com/265

 

 

더 확인할 문제
특정 인증기관의 인증서(ex. setigo) 는 인증서 파일을 등록하지 않아도 접속 및 실행에 문제 없는데, 어떤 CA의 인증서는 꼭
등록해야만 앱이 실행되는 이유..

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

(삭제)

더보기

먼저, 추가하고자 하는 사이트의 인증서 정보를 알아보자.

openssl s_client -connect (사이트 도메인):443 | openssl x509 -noout -subject -issuer
-- ex)openssl s_client -connect google.com:443 | openssl x509 -noout -subject -issuer

 

 

CA(인증기관) 정보 Get

어디서 다운 받지? 그대로 구글에 치면 됨 이렇게..

AlphaSSL CA - SHA256 - G2 Download
Download Certificated(Binary/DER) 버튼 클릭

다른 인증서 다운로드 사이트도 유사하다 파일 다운로드/ Base64 보기..

 

거의 다왔다.. (정리하면서 보니.. 얼마 안되네..)

 

왜인지 몰라도.. android에 .cer 형식으로 하면 인식이 안되서.. .crt 형식으로 변환 

openssl x509 -inform PEM -in (.cer파일 경로) -out (.crt 파일 경로)

ex) openssl x509 -inform PEM -in gsalphasha2g2r1.cer -out gsalphasha2g2r1.crt

이제 res>raw> 경로에 넣어주기

 

androidManifes.xml 파일에 추가

+ Recent posts