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 범위 내에서만 동작해야 합니다.
'Programming > C, C++' 카테고리의 다른 글
블록암호 PKCS#7 패딩 기능 C언어 구현 (0) | 2025.04.20 |
---|---|
C언어 공유라이브러리(so, dll) API 심볼 외부노출 방법 (0) | 2025.04.17 |