살구월드

잠깐, 리눅스, C 언어, Java, Python 등
각종 예제 검색하기

목차

  1. 자바 소스코드 컴파일 및 실행 예제 이전 포스팅
  2. 자바 HTTPS 통신 예제 이전 포스팅
  3. 자바에서 서버 CA 인증서 피닝 예제(CA Pinning)
  4. 잘못된 CA 인증서 피닝 후 HTTPS 서버 요청 예제
  5. 정상 CA 인증서 피닝 후 HTTPS 서버 요청 예제

 

 

 

오늘 포스팅에서는 HTTPS 통신시 서버의 CA 인증서를 피닝하여 통신하는 방법에 대해 알아보겠습니다.

 

 

 

 

자바 소스코드 컴파일 및 실행 예제 이전 포스팅

오늘 포스팅을 따라하기에 앞서 리눅스 환경에서 자바 소스코드 컴파일 및 실행 방법을 아직 모르신다면 예제를 따라하기 어렵습니다. 우선 아래 이전 포스팅 링크를 참조하여 자바 소스코드 컴파일 방법을 배운 후 진행해주세요.

 

2023.08.10 - [Linux] - [Linux/Java] 리눅스 우분투에서 java 컴파일 및 실행 예제(javac)

 

[Linux/Java] 리눅스 우분투에서 java 컴파일 및 실행 예제(javac)

목차 리눅스 OpenJDK 8 설치 관련 예제 이전 포스팅 간단한 자바 예제 코드 설명 javac 활용 자바 소스 코드 컴파일 예제 자바 클래스 파일 실행 예제 리눅스 OpenJDK 8 설치 관련 예제 이전 포스팅 우선

salguworld.tistory.com

 

 

 

 

 

자바 HTTPS 통신 예제 이전 포스팅

그리고 SSLContext를 활용한 CA 인증서 피닝 후에 이전 포스팅에서 다뤘던 HTTP 커넥션을 이용하여 HTTPS 암호화 통신 예제를 진행하려합니다. 따라서 커넥션 생성 및 HTTPS 통신 방법에 대해 학습해주세요.

 

2023.08.10 - [Java] - [Java] HTTPS 클라이언트 암호화 통신 예제(HttpURLConnection)

 

[Java] HTTPS 클라이언트 암호화 통신 예제(HttpURLConnection)

목차 자바에서 사용자에게 입력 받기 관련 예제 이전 포스팅 자바 HTTP 클라이언트 요청 관련 예제 이전 포스팅 HTTPS 요청 및 응답 출력 예제 기존 HTTP 요청과 차이점 자바에서 사용자에게 입력 받

salguworld.tistory.com

 

 

 

 

 

자바에서 서버 CA 인증서 피닝 예제(CA Pinning)

아래는 자바에서 내가 요청하려는 서버의 CA 인증서를 미리 설정해놓는 예제입니다. 각 서버들은 사전에 신뢰할 수 있는 인증기관(CA)에게 자신들의 인증서를 서명 받습니다. 간혹 fake 웹 서버들이 다른 서버의 인증서를 자기들의 서명으로 위장하여 통신하는 경우가 있습니다. 이를 MITM 공격이라 합니다.

 

이 공격에 당하면 서버와 통신하는 내용이 유출될 수 있습니다. 따라서 클라이언트에 서버의 CA 인증서를 미리 저장하여 비정상적인 CA로 서명된 인증서가 오면 통신을 하지 않도록 설계합니다. 이러한 보안 기술을 SSL CA 피닝이라 합니다.

 

뒤에 나올 예제에서는 "example.com" 이라는 https 웹사이트에 접속합니다. 이 사이트의 CA 인증서는 아래와 같습니다.

 

example.com 서버의 CA 인증서

 

저 인증서 파일을 "/tmp/pinning_ca.cert" 파일에 저장했습니다. 이제 아래 코드를 진행합니다.

 

import java.io.FileInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class Example {
    public static void main(String[] args) {
        try {
            // Load the CA certificate
            String caCertificatePath = "/tmp/pinning_ca.cert"; // Specify the path to your CA certificate
            FileInputStream caInputStream = new FileInputStream(caCertificatePath);
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            X509Certificate caCertificate = (X509Certificate) certificateFactory.generateCertificate(caInputStream);
            caInputStream.close();

            // Create a trust manager that checks against the CA certificate
            TrustManager[] trustManager = new TrustManager[] {
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[] { caCertificate };
                    }
                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }
                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                        for (X509Certificate cert : certs) {
                            try {
                                cert.verify(caCertificate.getPublicKey());
                            } catch (Exception e) {
                                throw new SecurityException("Certificate verification failed: " + e.getMessage());
                            }
                        }
                    }
                }
            };

            // Install the trust manager to the SSL context
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManager, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
		} catch (Exception e) {
            e.printStackTrace();
        }
	}
}

 

정상적으로 ssl context에 ca인증서가 로드되었다.

 

 

 

 

 

잘못된 CA 인증서 피닝 후 HTTPS 서버 요청 예제

그러면 위의 pinning_ca.cert는 정상 CA인증서이지만 잘못된 다른 서버 인증서, 즉 네이버 인증서(pinning_wrong_ca.cert ) 파일을 로드한 후 HTTP 커넥션을 하고 example.com 서버에 요청을 해보겠습니다.

 

import java.io.FileInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class Example {
    public static void main(String[] args) {
        try {
            // Load the CA certificate
            String caCertificatePath = "/tmp/pinning_wrong_ca.cert"; // Specify the path to your CA certificate
            FileInputStream caInputStream = new FileInputStream(caCertificatePath);
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            X509Certificate caCertificate = (X509Certificate) certificateFactory.generateCertificate(caInputStream);
            caInputStream.close();

            // Create a trust manager that checks against the CA certificate
            TrustManager[] trustManager = new TrustManager[] {
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[] { caCertificate };
                    }
                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }
                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                        for (X509Certificate cert : certs) {
                            try {
                                cert.verify(caCertificate.getPublicKey());
                                break;
                            } catch (Exception e) {
                                throw new SecurityException("Certificate verification failed: " + e.getMessage());
                            }
                        }
                    }
                }
            };

            // Install the trust manager to the SSL context
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManager, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
			
            // Create a URL for the HTTPS request
            URL url = new URL("https://example.com");

            // Open a connection to the URL
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            // Send a GET request
            connection.setRequestMethod("GET");

            // Read the response
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            StringBuilder response = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();

            // Print the response
            System.out.println("Response:\n" + response.toString());

            // Close the connection
            connection.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

서명이 맞지않다며 오류를 발생시키며 서버와의 통신을 종료하는 모습

 

 

 

 

정상 CA 인증서 피닝 후 HTTPS 서버 요청 예제

아래는 정상적인 CA 인증서 파일인 pinning_ca.cert 파일로 다시 지정하여 example.com 서버와 HTTPS 통신을 진행한 예제 결과입니다.

 

서버와 정상 통신하여 응답이 출력되는 모습

 

 

잠깐, 리눅스, C 언어, Java, Python 등
각종 예제 검색하기

공유하기

facebook twitter kakaoTalk kakaostory naver band