디지털 서명 한눈에: 누구나 검증하지만 나만 만드는 값
종이 계약서에는 도장을 찍고, 파일에는 «전자 서명» 을 합니다. 둘 다 «내가 했다» 는 표시입니다. 그런데 도장은 오려 붙일 수 있지만 디지털 서명은 그럴 수 없습니다. 무엇이 다를까요? 그리고 그 서명이 누구 것인지는 어떻게 알까요?
오려 붙인 도장
도장은 찍을 때마다 같은 자국을 남깁니다. 한 계약서의 도장 자국을 스캔해 다른 계약서에 붙이면, 자국만 보고는 가려내기 어렵습니다(그림 1). 파일에 붙이는 서명 그림도 마찬가지입니다.
파일마다 다른 서명
내려받는 프로그램에도 서명이 붙어 있습니다. Windows 의 Authenticode 와 macOS 의 코드 서명은 이 서명으로 만든 곳(게시자)을 밝히고, 그 뒤로 바뀌지 않았는지 확인합니다 [S1] [S2].
디지털 서명은 보통 파일 대신 파일의 해시 값에 만듭니다. 만드는 쪽은 개인 키를, 검증하는 쪽은 공개 키를 쓰고, 파일과 서명은 함께 보냅니다 [S3].
이제 브라우저의 서명 기능(Web Crypto)으로 실제 RSA 서명을 만들어 봅니다. 키는 2048비트, 표준이 허락하는 가장 작은 크기입니다 [S3]. 받은 파일을 고치거나, 이 서명을 다른 파일에 붙여 보세요.
회사 — 서명하는 쪽
해시 값 (SHA-256)
서명
받는 쪽 — 검증하는 쪽 (고쳐 보세요)
받은 파일의 해시 값
서명에서 꺼낸 해시 값
«서명에서 꺼낸 해시 값» 은 받은 서명을 공개 키로 계산한 값의 끝 32바이트입니다 [S4]. 이 값만 이 글이 직접 짠 계산(교육용 — 실제로 쓰지 말 것)이고, ✓·✗ 는 브라우저의 검증 기능이 정합니다.
받은 파일이 한 글자만 달라도 해시 값이 통째로 달라져, 서명에서 꺼낸 해시 값과 맞지 않습니다 [S2]. 서명은 그 파일의 해시 값에 묶여 있어서, 도장 자국처럼 떼어 다른 파일에 붙일 수 없습니다. 반대로 해시 값이 같은 두 파일은 서명 하나를 나눠 쓸 수 있습니다. SHA-1 충돌이 위험했던 까닭입니다.
공개 키로는 왜 서명을 못 만드나
그럼 위조자가 고친 파일에 맞는 서명을 새로 만들면 되지 않을까요? 서명은 개인 키를 가진 쪽만 만들 수 있고 [S3], 공개 키는 모두에게 알립니다. 그렇다면 공개 키에서 개인 키를 거꾸로 구할 수 있을까요?
RSA 의 공개 키에는 큰 수 n 이 들어 있습니다. n 은 두 소수 p 와 q 의 곱이고, p 와 q 는 비밀로 둡니다 [S3]. 그림 3 에서 시뮬레이터 ① 이 방금 만든 키의 실제 값을 볼 수 있습니다.
…
비밀 p …
비밀 q …
p 와 q 를 곱하는 일은 순식간입니다. 하지만 n 만 보고 p 와 q 를 찾는 소인수분해는 다릅니다. RSA 논문의 예 n = 2773 은 47 × 59 로 금방 쪼개지지만 [S5], 특별한 꼴이 아닌 수를 쪼갠 가장 큰 기록은 2020년 2월에 쪼갠 250자리이고, 컴퓨터 코어 하나로 치면 약 2,700년(Intel Xeon Gold 6130, 2.1GHz 기준)이 들었습니다 [S6] [S7].
p 와 q 를 알면 개인 키를 계산할 수 있습니다 [S5]. n 을 쪼개지 못하면 그 길이 막힙니다. 다만 RSA 를 깨는 모든 길이 소인수분해만큼 어렵다는 것은 증명되지 않았습니다 [S5].
그 공개 키는 누구 것인가
✓ 가 말해 주는 것은 «이 공개 키의 짝인 개인 키로 이 파일에 서명했다» 까지입니다. 키 쌍은 누구나 만들 수 있습니다. 위조자도 자기 키로 서명하고 «회사 A» 라고 써 붙일 수 있습니다. 표준도 키의 주인을 확인하지 않으면, 키 쌍을 가진 누구나 서명하고 서명한 이가 미국 대통령이라고 주장할 수 있다고 짚습니다 [S3].
그래서 이름과 공개 키를 묶어 인증 기관이 서명한 것이 인증서입니다 [S3] [S1]. 받는 쪽은 믿기로 정한 인증 기관의 공개 키로 인증서를 먼저 검증하고, 인증서 속 공개 키로 파일 서명을 검증합니다 [S8].
서명
인증서
이름만 써 붙이면 인증서가 없고, 스스로 만든 인증서는 인증 기관의 공개 키로 검증되지 않습니다. 회사 인증서를 오려 붙이면 파일 서명이 그 속 공개 키로 검증되지 않습니다. 인증 기관은 게시자가 누구인지 확인한 뒤에만 인증서를 내줍니다. 실제로는 인증서가 몇 단계 이어진 인증서 체인이고, 끝은 믿을 수 있는 루트 인증서입니다 [S1].
서명이 말해 주지 않는 것
시뮬레이터에서 서명과 인증서가 모두 ✓ 이면 «회사 A 의 인증서에 든 공개 키의 짝인 개인 키로 바로 이 파일에 서명했다» 까지 압니다. 실제 인증서는 유효 기간·용도 같은 칸도 함께 확인합니다 [S8]. 그래도 표 1 의 두 가지는 알 수 없습니다.
| 모르는 것 | 일어날 수 있는 일 | 서명 밖에서 채우는 것 |
|---|---|---|
| 프로그램이 안전한지 | 서명이 검증된 프로그램이라고 실행해도 안전하다는 보증은 아닙니다 [S9] | 서명이 알려 주는 것은 게시자가 믿을 만한 기관의 체계에 들어 있는지까지입니다 [S9]. 그 게시자를 믿을지는 받는 쪽이 정합니다 |
| 같은 글이 이미 쓰였는지 | «철수에게 1만 원을 보냅니다» 와 그 서명을 두 번 내밀어도 두 번 다 ✓ 입니다 (시뮬레이터 ① 에서 «파일 되돌리기» 를 몇 번 눌러도 ✓ 는 그대로입니다) | 이미 쓴 것을 여럿이 함께 적어 두는 장부 — 비트코인 백서는 서명된 거래만으로는 «같은 돈을 두 번 쓰는 문제» 를 막지 못한다고 보고 이 장부를 내놓았습니다 [S10] |
이 글의 계산과 확인
시뮬레이터의 서명·검증·해시 값은 브라우저에 들어 있는 Web Crypto 의 RSASSA-PKCS1-v1_5(2048비트, e = 65537, SHA-256)로 계산합니다. FIPS 186-5 가 승인한 두 RSA 서명 방식 가운데 하나입니다 [S3]. 같은 표준에는 EdDSA 처럼 해시 값을 따로 만들지 않고 글을 그대로 넣는 방식도 있어 본문에 «보통» 이라고 썼습니다 [S3]. 이 방식은 덧붙이는 무작위 값이 없어서, 같은 키·같은 파일이면 서명도 같습니다 [S4]. 키는 페이지를 열 때마다 새로 만들고, 어디에도 보내지 않습니다.
시뮬레이터 ② 의 인증서는 «이름 + 공개 키» 에 서명만 더한 이 글의 교육용 모양입니다. 실제 인증서(X.509)에는 유효 기간·용도 같은 칸이 더 있습니다 [S8].
같은 계산을 Node 의 crypto(OpenSSL)로 교차 확인했습니다. 우리가 만든 서명을 Node 가 검증했고, 같은 글·같은 키로 만든 두 서명은 바이트까지 같습니다. 서명을 공개 키로 계산한 값은 RFC 8017 이 정한 모양과 바이트까지 같고, 그 끝에 파일의 SHA-256 해시 값이 들어 있습니다. 다른 파일·파일 한 글자·서명 한 비트·다른 공개 키는 모두 실패합니다. RSA-250 발표의 두 소수를 곱하면 250자리 RSA-250 이 나옵니다 (verify.mjs 45개 통과).
출처
- [S1] Microsoft Learn — Authenticode 디지털 서명
- [S2] Apple — Code Signing Guide: Understanding the Code Signature
- [S3] NIST FIPS 186-5, Digital Signature Standard (2023) — §3, §5.1, §5.4
- [S4] RFC 8017, PKCS #1 v2.2 (2016) — §8.2.2, §9.2
- [S5] Rivest · Shamir · Adleman, A Method for Obtaining Digital Signatures and Public-Key Cryptosystems (1978) — §VIII, §IX
- [S6] Boudot 외, Factorization of RSA-250 (2020-02-28)
- [S7] Zimmermann, Integer Factoring Records (2026-10-05 기준)
- [S8] RFC 5280, Internet X.509 PKI Certificate and CRL Profile (2008) — §4.1·§4.2.1.3, §6
- [S9] Microsoft Learn — Introduction to Code Signing
- [S10] Nakamoto, Bitcoin: A Peer-to-Peer Electronic Cash System (2008) — §2