Programming > Java

JAVA SSL(https)인증신뢰 - Java PKIX path building failed 문제해결

#인증서#도메인#domain#ssl#등록하기

자바에서 https 적용된 사이트를 접속하기 위한방법

신뢰하지 못한 상태에서의 에러

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
...
...

keytool 사용법

신뢰된 인증서 보기 (default password = changeit)

keytool -list -trustcacerts -keystore "%JAVA_HOME%jre\lib\security\cacerts" -storepass changeit

인증서 추가하기

keytool -trustcacerts -keystore "%JAVA_HOME%jre\lib\security\cacerts" -importcert -alias <Alias명> -file "D:\저장된인증서파일.cer"

인증서 삭제하기

keytool -delete -alias <Alias명> -keystore "%JAVA_HOME%jre\lib\security\cacerts" -storepass changeit

 

 

웹브라우저에서 인증서 파일 출력해 적용

1) 브라우저 자물쇠 > 인증서보기에서 인증서 파일을 복사 실행

DER로 인코딩된 바이너리 X.509(.CER)(D)  선택 후 파일 저장

2) 추가할 JAVA 홈을 지정해 인증서 파일을 추가한다.

keytool -trustcacerts -keystore "%JAVA_HOME%jre\lib\security\cacerts" -importcert -alias polcyber -file "D:\저장된인증서파일.cer"

 

서버인증서를 검색하는 JAVA프로그램을 통한 인증서 생성

참조 :: https://github.com/escline/InstallCert

 JAVA 파일 소스는 아래쪽 별도 참조

JAVA 프로그램을 실행해 스토어생성

javac InstallCert.java
java -cp ./ InstallCert emunhi.com

 

....
...
Certificate Extensions: 1
[1]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: emunhi.com
]

]
  Algorithm: [SHA256withRSA]
  Signature:
0000: 31 95 E9 D4 4B FE B6 58   B7 25 E2 24 8C 6D E0 EB  1...K..X.%.$.m..
0010: A6 40 0E BE 8E E6 C6 39   76 FF 1E B6 E7 26 4B D7  .@.....9v....&K.
0020: 99 9A 11 25 F5 2F 25 5F   10 C9 D9 09 D5 C6 EB 9F  ...%./%_........
0030: 18 FC A8 6D 28 AE FE BE   78 DE 91 A3 1F F7 3C 23  ...m(...x.....<#
0040: 74 BF 6D 3B 6B 9C BD 42   55 F7 06 43 D2 F7 91 3F  t.m;k..BU..C...?
0050: F7 A9 ED FF 13 5B 72 FC   AD 77 4E 96 0E A8 8E DE  .....[r..wN.....
0060: C1 C6 C3 5D 24 7B 63 BF   63 F7 7B 2D 30 F1 A4 8A  ...]$.c.c..-0...
0070: DA E1 9B 27 AD FA B1 FA   3C DF AF 1E 0C F5 49 75  ...'....<.....Iu
0080: 0C 94 6B 3F 91 EA 3E 1B   79 58 01 44 E7 32 98 42  ..k?..>.yX.D.2.B
0090: 83 29 53 07 7C 4F 23 75   34 36 4C 63 9D 24 7B 91  .)S..O#u46Lc.$..
00A0: 82 21 82 90 91 A4 AC 43   C6 83 55 3B E6 84 2A 49  .!.....C..U;..*I
00B0: E8 DB A9 F4 4F 7C EF 69   4B 26 4F E8 49 1F 9A A8  ....O..iK&O.I...
00C0: 1B C9 5D 82 3E D8 97 B0   AE 3C 6D 38 12 73 51 F1  ..].>....<m8.sQ.
00D0: B1 C0 A4 ED 38 73 E5 12   7B 49 38 AC 0F 90 BD E4  ....8s...I8.....
00E0: 58 34 A4 85 FA D1 14 60   4F 96 EC 32 10 F9 CD D6  X4.....`O..2....
00F0: 1C 6F 2F 3F 25 A2 ED 80   55 B3 44 7F CF 34 B4 2A  .o/?%...U.D..4.*

]

Added certificate to keystore 'jssecacerts' using alias 'emunhi.com-1'

java파일을 컴파일 후 실행하면 키스토어 jssecacerts 파일과 alias가 생성된다.

실행결과 파일로 부터 인증서 파일 추출

keytool -exportcert -keystore "<파일위치>\jssecacerts" -storepass changeit -file <출력인증서파일명.cert> -alias <emunhi.com-1>

출력인증서 파일을 JAVA인증에 추가한다.

keytool -importcert -keystore "%JAVA_HOME%jre\lib\security\cacerts" -file <출력인증서파일명.cert> -alias <emunhi.com-1>

 

모든인증서를 허용하는 JAVA 소스를 통한 해결방법

/**
 * SSL 인증서에 대해 모두 신뢰하도록 처리를 한다.
 */
public static void trustSSL() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[] { 
            new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
                @Override
                public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                    //return null;
                }
            } 
        };
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

 

 

 

* 인증서 취득 소스

/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * check out this {@link https://www.lesstif.com/pages/viewpage.action?pageId=12451848}
 *
 * Usage:
 *
 * 1. download source
 * curl -O https://gist.githubusercontent.com/lesstif/cd26f57b7cfd2cd55241b20e05b5cd93/raw/InstallCert.java
 *
 * 2. compiling 
 * javac InstallCert.java
 *
 * 3. Run 
 * java -cp . InstallCert https://google.com:443
 *
 * 4. save peer's ssl cert to keystore(name is jssecacerts)
 *
 * 5. extract cert from saved keystore
 * keytool -exportcert -keystore jssecacerts -storepass changeit -file output.cert -alias letsencrypt
 *
 * 6. import cert into JDK's keystore
 * keytool -importcert -keystore ${JAVA_HOME}/jre/lib/security/cacerts -storepass changeit -file output.cert -alias letsencrypt
 *
 */
 
/**
 * http://blogs.sun.com/andreas/resource/InstallCert.java
 * Use:
 * java InstallCert hostname
 * Example:
 *% java InstallCert ecc.fedora.redhat.com
 */
import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
 * Class used to add the server's certificate to the KeyStore
 * with your trusted certificates.
 */
public class InstallCert {
    public static void main(String[] args) throws Exception {
        args= new String[] {"emunhi.com"};  << sample 테스트용
        String host;
        int port;
        char[] passphrase;
        if ((args.length == 1) || (args.length == 2)) {
            String[] c = args[0].split(":");
            host = c[0];
            port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
            String p = (args.length == 1) ? "changeit" : args[1];
            passphrase = p.toCharArray();
        } else {
            System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
            return;
        }
        File file = new File("jssecacerts");
        if (file.isFile() == false) {
            char SEP = File.separatorChar;
            File dir = new File(System.getProperty("java.home") + SEP
                    + "lib" + SEP + "security");
            file = new File(dir, "jssecacerts");
            if (file.isFile() == false) {
                file = new File(dir, "cacerts");
            }
        }
        System.out.println("Loading KeyStore " + file + "...");
        InputStream in = new FileInputStream(file);
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(in, passphrase);
        in.close();
        SSLContext context = SSLContext.getInstance("TLS");
        TrustManagerFactory tmf =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory factory = context.getSocketFactory();
        System.out.println("Opening connection to " + host + ":" + port + "...");
        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
        socket.setSoTimeout(10000);
        try {
            System.out.println("Starting SSL handshake...");
            socket.startHandshake();
            socket.close();
            System.out.println();
            System.out.println("No errors, certificate is already trusted");
        } catch (SSLException e) {
            System.out.println();
            e.printStackTrace(System.out);
        }
        X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }
        BufferedReader reader =
                new BufferedReader(new InputStreamReader(System.in));
        System.out.println();
        System.out.println("Server sent " + chain.length + " certificate(s):");
        System.out.println();
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        for (int i = 0; i < chain.length; i++) {
            X509Certificate cert = chain[i];
            System.out.println
                    (" " + (i + 1) + " Subject " + cert.getSubjectDN());
            System.out.println("   Issuer  " + cert.getIssuerDN());
            sha1.update(cert.getEncoded());
            System.out.println("   sha1    " + toHexString(sha1.digest()));
            md5.update(cert.getEncoded());
            System.out.println("   md5     " + toHexString(md5.digest()));
            System.out.println();
        }
        System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
        String line = reader.readLine().trim();
        int k;
        try {
            k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
        } catch (NumberFormatException e) {
            System.out.println("KeyStore not changed");
            return;
        }
        X509Certificate cert = chain[k];
        String alias = host + "-" + (k + 1);
        ks.setCertificateEntry(alias, cert);
        OutputStream out = new FileOutputStream("jssecacerts");
        ks.store(out, passphrase);
        out.close();
        System.out.println();
        System.out.println(cert);
        System.out.println();
        System.out.println
                ("Added certificate to keystore 'jssecacerts' using alias '"
                        + alias + "'");
    }
    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 3);
        for (int b : bytes) {
            b &= 0xff;
            sb.append(HEXDIGITS[b >> 4]);
            sb.append(HEXDIGITS[b & 15]);
            sb.append(' ');
        }
        return sb.toString();
    }
    private static class SavingTrustManager implements X509TrustManager {
        private final X509TrustManager tm;
        private X509Certificate[] chain;
        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }
     
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
     
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            throw new UnsupportedOperationException();
        }
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted(chain, authType);
        }
    }
}