비대칭키 암호화: 공개 키와 개인 키 한 쌍의 약속

비대칭키 암호화: 공개 키와 개인 키 한 쌍의 약속

대칭키 암호화는 빠르고 단순합니다. 하지만 한 가지 풀기 어려운 문제를 안고 있는데요. “암호화하는 쪽과 복호화하는 쪽이 비밀 키를 어떻게 안전하게 공유할 것인가”입니다. 공격자가 통신을 모두 보고 있는 상태에서 비밀 키를 그대로 보낼 수는 없는데, 그렇다고 매번 사람이 직접 만나서 키를 교환할 수도 없는 노릇이죠.

이 닭과 달걀 같은 문제를 풀기 위해 등장한 것이 비대칭키 암호화입니다. 비대칭키 암호 기술의 주된 목적은 미리 비밀을 공유하지 않은 상대와 안전한 통신을 시작하는 데 있습니다. 이 기술을 디지털 서명에 적용하면 개인 키의 소유 여부와 메시지의 출처도 검증할 수 있죠. 한 사람이 한 쌍의 키를 갖고, 한쪽은 모두에게 공개하고 다른 한쪽은 자기만 가진다는 살짝 마법 같은 발상이에요. 이번 글에서는 비대칭키가 어떻게 동작하는지, RSA와 ECC가 어떤 차이를 가지는지, 그리고 디지털 서명과 인증서까지 어떻게 이어지는지를 차근차근 살펴보겠습니다.

두 개의 키, 한 쌍의 약속

비대칭키의 핵심은 “한 키로 잠그면 다른 키로만 풀 수 있다”는 수학적 성질입니다. 한 쌍의 키 가운데 한쪽을 공개 키(public key), 다른 한쪽을 개인 키(private key)라고 부르는데요. 공개 키는 누구에게나 알려도 되는 값이고, 개인 키는 본인만 알아야 하는 비밀입니다.

여기서 개인 키와 비밀 키는 같은 말이 아닙니다. 비밀 키(secret key)는 대칭키 암호화에서 두 참여자가 함께 사용하는 키이고, 개인 키는 공개 키와 쌍을 이루며 소유자만 보관하는 비대칭키입니다. 이 글에서는 두 용어를 이 기준으로 구분해서 사용하겠습니다.

flowchart TB
    accTitle: 공개 키로 암호화하고 개인 키로 복호화하는 흐름
    accDescr: 보내는 사람이 받는 사람의 공개 키로 비밀 메시지를 암호화해 전송하면, 받는 사람은 자기 개인 키로 암호문을 복호화해 원래 메시지를 얻는다.

    plaintext[/"비밀 메시지"/] --> encrypt["보내는 사람<br/>받는 사람의 공개 키로 암호화"]
    encrypt --> ciphertext["암호문<br/>네트워크로 전송"]
    ciphertext --> decrypt["받는 사람<br/>자신의 개인 키로 복호화"]
    decrypt --> restored[/"비밀 메시지"/]

    classDef operation stroke:#0284c7,stroke-width:2px
    classDef encrypted stroke:#7c3aed,stroke-width:2px
    classDef result stroke:#16a34a,stroke-width:3px

    class encrypt,decrypt operation
    class ciphertext encrypted
    class restored result

흥미로운 점은 누구든 받는 사람의 공개 키로 메시지를 잠글 수는 있지만, 그걸 풀 수 있는 사람은 개인 키를 가진 본인뿐이라는 거죠. 공격자가 통신을 모두 들여다보고 있어도, 개인 키 없이는 암호문을 평문으로 되돌릴 수 없습니다. 이 비대칭성이 키 교환 문제를 풀어 주는 토대가 됩니다.

여기서 디지털 서명을 “개인 키로 잠그고 공개 키로 푸는 암호화”라고 설명하기도 하는데요. 교과서적인 RSA 기본 연산을 뒤집어 표현한 비유일 뿐, 실제 서명은 별도의 연산입니다. RFC 8017도 RSA-OAEP 암호화와 RSA-PSS 서명을 서로 다른 방식으로 정의하므로, 키의 방향만 바꾸면 암호화가 서명이 되는 것은 아닙니다. RFC 8032가 정의한 Ed25519처럼 암호화 기능이 없는 서명 전용 알고리즘에서는 이 비유 자체가 성립하지 않습니다.

RSA, 가장 널리 알려진 알고리즘

비대칭키 알고리즘 가운데 가장 오래되고 가장 널리 알려진 것이 RSA입니다. 세 명의 발명자(Rivest, Shamir, Adleman)의 이름에서 따왔고, 1977년에 등장한 이래 지금까지도 광범위하게 쓰이고 있죠.

RSA의 보안성은 큰 수의 소인수분해가 어렵다는 수학적 사실에 기반합니다. 큰 두 소수를 곱하는 것은 쉽지만, 그 결과로 나온 수에서 다시 두 소수를 찾아내는 것은 매우 어려운데요. 공개 키는 곱한 결과를 포함한 정보이고, 개인 키는 그 두 소수를 알고 있어야 만들 수 있는 정보라, 공개 키만으로는 개인 키를 역으로 알아낼 수 없습니다.

RSA 키의 크기는 보통 2048비트나 4096비트로 잡습니다. 같은 보안 강도를 얻기 위해 대칭키보다 훨씬 큰 키가 필요한 게 비대칭키의 특징인데요. RSA-2048이 대칭키 AES-112와 비슷한 보안 강도라고 알려져 있어, 큰 차이를 한눈에 보여 주는 비교가 됩니다.

RSA를 안전하게 쓰려면 패딩 방식도 함께 신경 써야 합니다. 순수한 RSA 연산은 결정적이라서 같은 평문에 같은 암호문이 나오는데, 이게 그대로 노출되면 공격에 취약하거든요. 그래서 실무에서는 암호화에 RSA-OAEP, 서명에 RSA-PSS 같은 패딩 방식을 함께 사용하는 것이 표준입니다. 옛 PKCS#1 v1.5 패딩은 일부 측면 채널 공격에 약점이 알려져 있어, 새로 만드는 시스템에서는 OAEP/PSS를 골라 쓰는 것이 안전합니다.

ECC, 더 작은 키로 더 강한 보안

요즘 점점 자리를 넓혀 가고 있는 알고리즘이 타원곡선 암호(ECC, Elliptic Curve Cryptography)입니다. 타원곡선 위의 점들에 대한 수학적 성질을 활용하는데, 같은 보안 강도를 훨씬 작은 키로 얻을 수 있다는 장점이 있죠.

256비트짜리 ECC 키가 RSA-3072와 비슷한 보안 강도를 제공한다고 알려져 있어, 키 크기도 연산량도 모두 작아집니다. 모바일이나 IoT 환경처럼 자원이 빠듯한 자리에 특히 잘 어울리는데요. HTTPS의 키 교환에 자주 등장하는 ECDHE도 이 타원곡선의 한 응용이고, 비트코인이 트랜잭션 서명에 쓰는 secp256k1도 ECC 곡선의 한 종류입니다.

같은 25519 계열에서도 용도에 따라 알고리즘이 나뉩니다. X25519는 두 참여자가 같은 공유 비밀(shared secret)을 만드는 데 쓰고, Ed25519는 디지털 서명에 쓰는데요. 이름은 비슷하지만 키 형식과 연산 목적이 다르므로 서로 바꿔 쓸 수는 없습니다.

ECC가 모든 면에서 RSA를 대체하는 것은 아닙니다. 오랫동안 검증된 안정성이라는 측면에서는 RSA가 여전히 강력하고, 일부 옛 시스템과의 호환성 때문에 RSA를 유지해야 하는 자리도 있죠. 다만 새로 만드는 시스템에서는 ECC 계열을 기본 선택지로 두는 흐름이 점점 강해지고 있습니다.

키 교환: Diffie-Hellman과 ECDHE

비대칭키의 또 다른 큰 쓰임새가 키 교환입니다. 두 참여자가 서로 다른 비밀값을 가지고 시작하지만, 공개된 통신 채널만으로 같은 공유 비밀을 만들어 내는 마법 같은 절차죠. 이 절차의 원형이 Diffie-Hellman 키 교환입니다.

flowchart TB
    accTitle: Diffie-Hellman으로 같은 공유 비밀을 만드는 흐름
    accDescr: 앨리스와 밥은 각자 비밀값으로 공개 값을 만들어 교환하고, 받은 공개 값과 자기 비밀값을 계산해 통신으로 보내지 않은 같은 공유 비밀을 얻는다.

    subgraph alice["앨리스"]
        direction TB
        secretA["비밀값 a"] --> publicA["공개 값<br/>A = g^a mod p"]
        secretA --> calculateA["B^a mod p 계산"]
    end

    subgraph bob["밥"]
        direction TB
        secretB["비밀값 b"] --> publicB["공개 값<br/>B = g^b mod p"]
        secretB --> calculateB["A^b mod p 계산"]
    end

    publicA -- "A 전송" --> calculateB
    publicB -- "B 전송" --> calculateA
    calculateA --> shared(["같은 공유 비밀<br/>g^(ab) mod p"])
    calculateB --> shared

    classDef secret stroke:#e11d48,stroke-width:2px
    classDef public stroke:#0284c7,stroke-width:2px
    classDef operation stroke:#7c3aed,stroke-width:2px
    classDef result stroke:#16a34a,stroke-width:3px

    class secretA,secretB secret
    class publicA,publicB public
    class calculateA,calculateB operation
    class shared result

공격자가 A와 B를 모두 보아도, 비밀값 a와 b를 알지 못하면 g^(ab)를 계산할 수 없습니다. 이 한 번의 교환으로 두 참여자가 같은 공유 비밀을 얻습니다. 실제 프로토콜은 이 값을 키 유도 함수(Key Derivation Function, KDF)에 넣어 대칭키 통신에 사용할 세션 키를 만드는데요. 처음부터 비밀 키를 공유하지 않고도 안전한 통신을 시작할 수 있는 셈이죠.

타원곡선 위에서 같은 일을 하는 변형이 ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)입니다. “Ephemeral”이 붙은 이유는 매번 새로운 임시 키 쌍을 만들어 쓰기 때문인데요. 이 덕분에 서버의 장기 개인 키가 나중에 새더라도 과거 통신 내용까지 풀리지 않게 되는데, 이를 순방향 비밀성(forward secrecy)이라고 부릅니다. HTTPS가 기본으로 ECDHE를 쓰는 이유이기도 합니다.

디지털 서명: 해시와 비대칭키의 조합

비대칭키의 또 한 가지 핵심 쓰임새가 디지털 서명입니다. 서명은 메시지를 숨기는 대신, 개인 키 보유자가 이 메시지에 서명했고 서명 뒤 메시지가 바뀌지 않았음을 확인하는 도구인데요. 그 개인 키가 실제로 누구의 것인지는 뒤에서 살펴볼 인증서처럼 신뢰할 수 있는 연결 장치가 있어야 알 수 있습니다.

서명 알고리즘은 메시지와 개인 키를 입력받아 서명값을 만듭니다. 검증 알고리즘은 메시지, 서명값, 공개 키를 입력받아 서명이 유효한지만 판단하죠. 많은 알고리즘이 이 과정에서 해시를 사용하지만, 해시를 적용하는 방식은 알고리즘마다 다릅니다.

flowchart TB
    accTitle: 디지털 서명을 만들고 검증하는 흐름
    accDescr: 보내는 사람은 메시지와 개인 키를 서명 알고리즘에 넣어 서명값을 만들고, 받는 사람은 메시지, 서명값, 공개 키를 검증 알고리즘에 넣어 서명의 유효 여부를 판단한다.

    subgraph signing["서명 생성"]
        direction TB
        message[/"메시지"/] --> sign["서명 알고리즘"]
        privateKey(["개인 키"]) --> sign
        sign --> signature["서명값"]
    end

    subgraph verification["서명 검증"]
        direction TB
        receivedMessage[/"받은 메시지"/] --> verify["검증 알고리즘"]
        receivedSignature["받은 서명값"] --> verify
        publicKey(["공개 키"]) --> verify
    end

    message -- "메시지 전달" --> receivedMessage
    signature -- "서명값 전달" --> receivedSignature
    verify --> valid{"검증에<br/>성공했는가?"}
    valid -- "예" --> accepted(["서명 유효<br/>메시지 무결성 확인"])
    valid -- "아니요" --> rejected(["서명 무효<br/>메시지 거부"])

    classDef key stroke:#7c3aed,stroke-width:2px
    classDef operation stroke:#0284c7,stroke-width:2px
    classDef signedValue stroke:#7c3aed,stroke-width:2px
    classDef decision stroke:#0d9488,stroke-width:3px
    classDef success stroke:#16a34a,stroke-width:3px
    classDef failure stroke:#e11d48,stroke-width:3px

    class privateKey,publicKey key
    class sign,verify operation
    class signature,receivedSignature signedValue
    class valid decision
    class accepted success
    class rejected failure

대표적인 서명 방식으로 RSA-PSS, ECDSA, EdDSA가 있습니다. Ed25519는 EdDSA를 25519 계열의 에드워즈 곡선(Edwards curve)에 적용한 방식인데요. 키와 서명이 작다는 장점이 있습니다. 서명할 때 별도의 무작위 값을 만들지 않아도 되므로, 난수 처리 실수로 개인 키가 노출될 위험도 줄어듭니다.

가까운 사례가 GitHub의 SSH 인증입니다. SSH용 Ed25519 키를 만들 때 ssh-keygen -t ed25519를 실행하면 개인 키와 공개 키가 만들어집니다. 접속할 때 SSH 클라이언트는 현재 연결의 세션 식별자와 사용자 인증 요청을 묶은 데이터에 로컬의 개인 키로 서명합니다. GitHub는 계정에 등록된 공개 키로 서명을 검증해 요청자가 해당 개인 키를 가지고 있는지 확인하죠. 이 과정에서 Ed25519 키는 소스 코드나 통신 데이터를 직접 암호화하지 않습니다. JWT 서명 알고리즘 비교에서 만나는 EdDSA도 같은 계열의 활용입니다.

RSA-PSS와 ECDSA는 메시지의 해시를 서명 계산에 사용합니다. 반면 Ed25519는 메시지를 입력받아 내부에서 SHA-512를 사용하며, 메시지를 먼저 해시해서 넣는 Ed25519ph는 별도의 변형인데요. 따라서 디지털 서명을 “메시지를 해시한 뒤 개인 키로 암호화하는 과정”으로 외우면 곤란합니다. 서명 생성은 메시지와 개인 키를 받고, 서명 검증은 메시지, 서명값, 공개 키를 받는 별개의 연산이라고 이해하는 편이 정확합니다.

인증서: 공개 키에 신원 묶기

여기까지 오면 한 가지 의문이 남습니다. “공개 키로 누구에게나 메시지를 잠글 수 있고, 누구든 공개 키로 서명을 검증할 수 있다면, 그 공개 키가 정말 내가 통신하려는 그 사람의 것인지 어떻게 확인할까요?” 공격자가 가짜 공개 키를 자기 것이라고 주장하면 모든 보안이 무너지는데요.

이 문제를 푸는 도구가 디지털 인증서입니다. 인증서는 “이 공개 키는 example.com의 주인이 맞다”는 사실을 신뢰할 수 있는 제3자(인증 기관, CA)가 자기 개인 키로 서명해 발급한 문서입니다. 브라우저나 운영 체제는 미리 신뢰할 만한 CA의 공개 키를 들고 있어서, 그 서명을 검증하면 인증서를 믿을 수 있는지 판단할 수 있죠.

HTTPS 글에서 다룬 신뢰의 사슬이 정확히 이 구조입니다. 내 사이트 인증서를 중간 인증서가 보증하고, 중간 인증서를 루트 인증서가 보증하는 식으로 사슬을 따라 올라가다 보면, 결국 브라우저가 알고 있는 루트에 도달하는데요. 이 사슬이 한 군데라도 깨지면 브라우저는 빨간 경고 화면을 띄워 가짜 서버임을 알려 줍니다.

느린 비대칭키, 그래서 하이브리드

비대칭키의 마지막 특징은 느리다는 점입니다. 같은 양의 데이터를 처리할 때 대칭키에 비해 훨씬 많은 연산이 필요해서, 큰 데이터를 직접 비대칭키로 암호화하는 것은 비효율적이죠.

그래서 현실의 시스템은 거의 항상 두 방식을 섞어 씁니다. HTTPS가 좋은 예인데요. 세션을 시작할 때 ECDHE로 공유 비밀을 합의하고, KDF를 거쳐 대칭키 세션 키를 유도합니다. 그 뒤로는 이 세션 키와 AES-GCM 같은 대칭키 암호화로 본 통신을 흘려보내죠.

이 패턴을 하이브리드 암호화라고 부릅니다. 비대칭키의 키 교환 우아함과 대칭키의 빠른 처리 속도를 모두 챙길 수 있는데요. “비대칭키는 첫 인사, 대칭키는 본 대화”라고 비유하면 두 도구가 어떻게 짝을 이루는지가 또렷해집니다.

양자 컴퓨터와 비대칭키의 미래

비대칭키 암호의 안전성은 결국 “특정 수학 문제가 어렵다”는 가정 위에 서 있습니다. RSA는 큰 수의 소인수분해, ECC는 타원곡선 위의 이산 로그 문제가 어렵다는 가정인데요. 그런데 충분히 강한 양자 컴퓨터가 등장하면 이 두 문제 모두 효율적으로 풀릴 수 있다는 것이 이론적으로 알려져 있습니다.

당장 위협이 되는 단계는 아니지만, 학계와 표준 기관은 미리 대비를 시작했습니다. 양자 컴퓨터에도 강한 새로운 비대칭키 알고리즘을 모아 둔 분야를 포스트 양자 암호(Post-Quantum Cryptography, PQC)라고 부르는데요. NIST가 2024년에 첫 표준을 발표했고, ML-KEM(키 교환용)과 ML-DSA(서명용) 같은 알고리즘이 그 첫 결과물입니다.

당장 대부분의 시스템이 옮겨갈 시기는 아니지만, 새 시스템을 설계할 때 알고리즘을 교체하기 쉬운 구조로 만들어 두면 미래의 전환이 한결 부드러워집니다. 인증서나 키 형식이 알고리즘 식별자를 명시적으로 담도록 설계하는 것이 좋은 출발점이죠.

마치며

비대칭키 암호화는 한 쌍의 키로 키 교환과 디지털 서명이라는 큰 문제를 한꺼번에 풀어내는 도구입니다. 공개 키를 누구에게나 알려도 안전하다는 마법 같은 성질 덕분에, 처음 만나는 두 사람도 안전한 통신을 시작할 수 있게 되었고, 메시지가 누구로부터 왔는지를 수학적으로 증명할 수 있게 되었죠.

해시, 대칭키, 비대칭키 세 도구를 한자리에 두고 보면, 현대 암호학이 어떻게 이 셋을 엮어 HTTPSJWT 같은 시스템을 만들어 냈는지가 한눈에 들어옵니다. 비대칭키 알고리즘의 자세한 명세가 궁금하다면 RFC 8017 (RSA)을 출발점으로 추천합니다.

This work is licensed under CC BY 4.0CCBY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord