728x90

 

TLS 1.3 Handshake 전체 개요

 

TLS 1.3 Handshake는 클라이언트와 서버가 암호화 통신을 설정하기 위해 서로 협상하고, 인증하고, 비밀 키를 공유하는 과정입니다. 크게 보면 다음과 같은 단계로 구성됩니다:

1. ClientHello
2. ServerHello
3. (선택) 서버 인증 및 키 교환
4. Finished 및 암호화된 데이터 전송 시작

 

 

1단계: ClientHello

클라이언트가 서버에 연결을 시도하며 최초로 보내는 메시지입니다.

  • 포함 내용:
    • 지원하는 프로토콜 버전: supported_versions 확장을 통해 "TLS 1.3" 지원을 명시.
    • 지원하는 암호 스위트: 예: TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256 등.
    • 키 공유 정보: 클라이언트의 ephemeral (임시) public key (예: ECDHE, X25519 사용).
    • 랜덤 값: client_random라 불리는 32바이트 난수.
    • 확장 필드:
      • SNI (Server Name Indication): 어떤 도메인으로 접속하는지 알려줌.
      • ALPN (Application-Layer Protocol Negotiation): HTTP/2, HTTP/3 같은 상위 프로토콜 협상.
      • Supported Groups, Signature Algorithms 등.

 핵심 변화: TLS 1.3에서는 클라이언트가 첫 메시지에서 키 교환을 위해 필요한 정보를 미리 다 포함시켜 전송합니다.

 

 2단계: ServerHello

서버가 클라이언트의 요청에 응답합니다.

  • 포함 내용:
    • 선택된 프로토콜 버전: 예: TLS 1.3.
    • 선택된 암호 스위트: 클라이언트가 제안한 것 중 하나 선택.
    • 키 공유 정보: 서버의 ephemeral public key.
    • 랜덤 값: server_random (32바이트).

 핵심 변화: TLS 1.2와 달리, TLS 1.3에서는 이 시점에서 암호 스위트, 키 공유 방식이 결정되고 handshake 메시지들이 암호화되기 시작합니다.

 

3단계: (선택) 서버 인증 및 키 교환

  • 서버 인증 (필요한 경우):
    • Certificate: 서버의 인증서 (일반적으로 X.509).
    • CertificateVerify: 서버가 자신의 private key로 서명해 인증서의 진위를 증명.
  • 키 교환:
    • 클라이언트와 서버는 서로의 ephemeral public key로 shared secret (pre-master secret)을 계산.
    • 이 secret을 기반으로 handshake 및 application traffic용 암호화 키가 생성됨.

 핵심 변화:

  • TLS 1.3에서는 static RSA key exchange가 제거되고, 반드시 forward secrecy가 보장되는 ephemeral (임시) 키만 사용.
  • ServerKeyExchange 메시지가 없고, Certificate와 CertificateVerify로 통합됨.

 

4단계: Finished 및 암호화된 데이터 전송 시작

  • 클라이언트 Finished:
    • 클라이언트는 자신의 Finished 메시지를 보냄 (HMAC 기반의 검증 데이터 포함).
    • 이 메시지는 handshake traffic secret으로 암호화됨.
  • 서버 Finished:
    • 서버는 자신의 Finished 메시지를 보냄.
  • Application Data 전송:
    • 이 시점부터 양쪽 모두 application traffic secret을 사용해 데이터 암호화.
    • TLS record layer를 통해 실제 HTTP 요청/응답 같은 데이터가 전송됨.

 핵심 변화:

  • TLS 1.3에서는 handshake가 끝나기 전에 application data를 전송할 수 있는 0-RTT 모드가 존재 (선택적, 세션 재사용 시에만).

 

핸드셰이크 단계의 큰 차이점 요약

구분 TLS 1.2 TLS 1.3
Round Trip 수 최소 2 RTT 1 RTT (0-RTT 모드 지원)
키 교환 방식 RSA, ECDHE, DHE, PSK 등 다양 ECDHE, DHE, PSK (forward secrecy 필수)
서버 인증 방식 인증서 + (optional) ServerKeyExchange 인증서 + CertificateVerify (simple)
암호화 범위 Finished 이후에만 암호화 ServerHello 이후부터 handshake 암호화
메시지 구조 여러 개의 개별 메시지 (복잡) 단순화된 메시지 구조, 불필요한 단계 제거
0-RTT 지원 불가능 가능 (재연결 시 빠른 전송)
암호 스위트 설계 RSA, RC4, SHA1 등 legacy 포함 최신 알고리즘만 포함, legacy 제거

 

 핸드셰이크 단계별 비교

 

1. ClientHello

  • TLS 1.2:
    • 클라이언트가 지원하는 프로토콜, 암호 스위트, 압축 방식, random 값 전송.
    • (옵션) 세션 재개용 session ID 포함.
  • TLS 1.3:
    • 지원하는 프로토콜에 TLS 1.3 명시.
    • 암호 스위트에서 TLS 1.3 전용 스위트만 보냄.
    • 클라이언트 ephemeral key (예: X25519, ECDHE) 포함 (핸드셰이크 속도 향상).
    • 0-RTT 재연결 시 early data 전송.

 

 2. ServerHello

  • TLS 1.2:
    • 선택한 프로토콜, 암호 스위트, random 값 전송.
    • 세션 재개용 session ID.
    • ServerKeyExchange (ECDHE 키 제공), Certificate 메시지 등 추가 메시지 필요.
  • TLS 1.3:
    • 선택된 암호 스위트, ephemeral key 전송.
    • 이후부터 handshake 메시지 암호화.
    • ServerKeyExchange가 따로 존재하지 않고 Certificate 메시지와 함께 처리됨.

 

3. 키 교환 및 인증

  • TLS 1.2:
    • RSA key exchange: 서버 public key로 pre-master secret 암호화.
    • 또는 ECDHE/DHE: 서버의 ephemeral key 제공 + ServerKeyExchange.
    • 서버 인증: Certificate + ServerKeyExchange + CertificateRequest + ServerHelloDone.
  • TLS 1.3:
    • ECDHE/DHE (forward secrecy 필수).
    • 서버 인증: Certificate + CertificateVerify (private key로 서명).
    • ServerHello 이후 모두 암호화된 채로 전송.

 

4. Finished 메시지

  • TLS 1.2:
    • 서버와 클라이언트가 각자 Finished 메시지를 전송해 integrity 확인.
    • 이후 application data 암호화 시작.
  • TLS 1.3:
    • Finished 메시지는 ServerHello 이후로 암호화됨.
    • 키 유도 과정이 단순화 (HKDF 사용).
    • Finished 후 바로 application data 암호화.

 

5. 성능 및 보안 개선

항목 TLS 1.2 TLS 1.3
성능 2 RTT 이상 1 RTT (0-RTT 재연결 시 거의 0 RTT)
암호 강제 조건 RC4, SHA1 등 legacy 지원 최신 강력한 암호화 및 forward secrecy 강제
공격 대응 취약점 많음 (BEAST, CRIME, FREAK 등) 보안성 강화 (취약 알고리즘 제거, simpler design)

 

728x90
728x90

 

OpenSSL Provider란?

기존 OpenSSL 1.1.x까지는 암호 알고리즘이 libcrypto 안에 하드코딩되어 있었습니다.
하지만 OpenSSL 3.0부터는 이런 암호 알고리즘 구현을 플러그인(Provider) 형태로 분리했습니다.

즉, Provider는 OpenSSL에서 사용할 수 있는 암호 알고리즘, 키 관리, 서명, 해시 등 암호 서비스들의 집합입니다.
OpenSSL 라이브러리 본체는 이 Provider들로부터 필요한 기능을 가져와서 실행합니다.

 

 왜 Provider를 도입했나?

  • 유연성
    새로운 알고리즘이나 하드웨어 가속 기능을 추가할 때 OpenSSL을 직접 수정하거나 재컴파일할 필요 없이, 별도 Provider만 추가하면 됩니다.
  • 정책 분리
    예: FIPS 인증된 알고리즘만 사용해야 할 경우, FIPS Provider만 로드하면 됩니다. 반대로 개발용에선 Default Provider를 쓰면 됩니다.
  • 사용자 정의
    개발자가 직접 자신만의 Provider를 작성해 OpenSSL에 플러그인처럼 붙일 수 있습니다.

 

OpenSSL 기본 제공 Provider 종류

OpenSSL 3.0 설치 시 기본으로 제공되는 Provider들은 다음과 같습니다:

Provider 이름 설명
default 대부분의 표준 알고리즘 (RSA, AES, SHA 등) 포함
fips FIPS 140-2 인증용 알고리즘 제공 (엄격한 보안 요구사항에 맞춤)
legacy 오래되었거나 약한 보안의 알고리즘 제공 (MD5, RC4 등)
null 어떤 암호 기능도 제공하지 않는 빈 Provider (주로 테스트용)

 

Provider의 동작 방식

OpenSSL은 실행 시 또는 설정 파일 (openssl.cnf)에서 로드할 Provider를 지정합니다.

  • 특정 알고리즘 요청 → OpenSSL은 등록된 Provider 목록을 탐색 → 해당 알고리즘을 제공하는 Provider에서 구현을 가져옴
  • 따라서 어떤 Provider를 로드하느냐에 따라 OpenSSL의 기능과 보안 수준이 달라질 수 있습니다.

 

간단한 예: Provider 사용

# FIPS Provider 로드 예제
export OPENSSL_MODULES=/usr/local/lib/ossl-modules
openssl list -providers

# 특정 Provider를 명시적으로 로드하는 코드 예제 (C)
OSSL_PROVIDER *provider = OSSL_PROVIDER_load(ctx, "fips");
if (provider == NULL) {
    fprintf(stderr, "FIPS provider load failed\n");
}

 

 

외부 Provider 예시

1. OpenSSL FIPS Provider (OpenSSL 공식)

  • OpenSSL 팀이 직접 제공하는 FIPS 140-2 인증용 Provider
  • 기존에는 별도 FIPS 모듈로 제공되었지만, 3.x부터는 Provider 형태로 통합됨.

2. Open Quantum Safe (OQS) Provider

  • 양자내성암호(Post-Quantum Cryptography) 알고리즘을 OpenSSL에서 쓸 수 있게 해주는 Provider
  • https://openquantumsafe.org
  • Dilithium, Kyber 등 NIST PQC 최종 후보 알고리즘을 OpenSSL에서 바로 실험할 수 있도록 지원.

3. SoftHSM + PKCS#11 Provider

  • 하드웨어 보안 모듈(HSM)이나 소프트웨어 HSM에서 제공하는 PKCS#11 인터페이스와 연결되는 Provider
  • 예: OpenSC 프로젝트, SoftHSM 등과 연동해 키 관리.

4. Intel QAT (QuickAssist) Provider

  • Intel QAT 하드웨어 가속 기능을 OpenSSL에서 사용하도록 만든 Provider
  • AES, RSA, ECDSA 등 연산을 QAT 장치로 오프로드.

5. NVIDIA GPU Provider (실험적)

  • NVIDIA GPU를 이용해 OpenSSL 암호 연산을 가속하는 Provider (주로 연구용, 아직 널리 사용되지는 않음).

 

 

커스텀 Provider 구현 방법

 

커스텀 Provider의 기본 구성

커스텀 Provider를 만들려면 크게 다음 요소가 필요합니다:

1. Provider 엔트리 포인트
→ OpenSSL이 이 모듈을 로드할 때 호출할 OSSL_provider_init() 함수를 구현해야 합니다.

int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,
                       const OSSL_DISPATCH *in,
                       const OSSL_DISPATCH **out,
                       void **provctx);

 

이 함수는:

  • OpenSSL 코어 핸들 handle과
  • Provider가 코어에 제공할 함수 목록 out을 세팅하고
  • Provider 내부 상태를 담은 컨텍스트 provctx를 초기화합니다.

 

2. DISPATCH 테이블
→ Provider가 제공하는 기능들을 OSSL_DISPATCH 배열로 선언합니다.

예:

static const OSSL_DISPATCH my_provider_dispatch_table[] = {
    { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void)) my_gettable_params },
    { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void)) my_get_params },
    { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void)) my_query_operation },
    { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void)) my_teardown },
    { 0, NULL }
};

 

 

3. 알고리즘 등록
Provider는 my_query_operation() 같은 함수에서 OpenSSL에게
“나는 이 연산(digest, cipher, signature, …)에 대해 이런 알고리즘들을 제공해”라고 알려줍니다.

예:

static const OSSL_ALGORITHM my_digests[] = {
    { "MYHASH", "provider=myprovider", myhash_functions },
    { NULL, NULL, NULL }
};

각 항목은:

  • 알고리즘 이름 (예: MYHASH)
  • 프로퍼티 스트링 (예: provider=myprovider)
  • 함수 테이블 (예: myhash_functions)
    로 구성됩니다.

 

4. 알고리즘 함수 구현
예를 들어 digest를 제공한다면:

  • newctx, freectx, init, update, final, get_params 같은 함수들을 정의해 OSSL_DISPATCH 테이블로 묶습니다.
static const OSSL_DISPATCH myhash_functions[] = {
    { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void)) myhash_newctx },
    { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void)) myhash_update },
    { OSSL_FUNC_DIGEST_FINAL, (void (*)(void)) myhash_final },
    { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void)) myhash_freectx },
    { 0, NULL }
};

 

 

 

개발 흐름 요약

1️⃣ .c 파일로 Provider 코드 작성
2️⃣ OSSL_provider_init()과 필요한 DISPATCH, ALGORITHM 테이블 정의
3️⃣ gcc -shared -fPIC -o myprovider.so myprovider.c -lcrypto로 공유 라이브러리 빌드
4️⃣ OpenSSL에서 환경 변수 또는 openssl.cnf로 모듈 경로 등록
5️⃣ OSSL_PROVIDER_load() 또는 openssl list -digest-algorithms 같은 명령으로 테스트

 

예제 구조

myprovider/
├── Makefile
├── myprovider.c      ← 메인 구현
├── myhash.c          ← 예: custom digest 알고리즘 구현
├── myprovider.so     ← 빌드 후 생성되는 동적 라이브러리

 주의할 점

  •  OpenSSL 내부 API는 잘 문서화되어 있지 않으므로, 기본 제공 Provider 소스 (예: default, legacy)를 참고하는 게 중요합니다.
  •  OpenSSL 버전 간 호환성 문제에 주의해야 합니다 (예: 구조체 변경, DISPATCH 상수 추가 등).
  •  Provider는 OpenSSL Core ↔ Module 형태로 통신하므로, 반드시 명세된 API 범위 내에서만 동작해야 합니다.
728x90

+ Recent posts