04. Code Signing — Authenticode와 Developer ID
사용자 디스크에 binary를 까는 순간, OS는 묻는다 — “이 binary가 누구의 것이냐”. 답은 코드 서명이다. 누군가가 돈을 내고 발급받은 인증서로 binary에 서명하고, OS는 그 서명을 공인된 CA의 루트까지 추적해 신뢰한다. 이 문서는 인증서의 비용·발급·갱신·실제 트러블슈팅까지 다룬다.
한 줄 답
코드 서명은 binary의 출처를 OS에게 증명하는 신뢰 사슬이다. Windows는 Authenticode (EV vs OV 인증서로 평판 차이), macOS는 Developer ID (Apple Developer Program $99/yr). 둘 다 없으면 사용자에게 “이 앱은 신뢰할 수 없습니다” 경고가 뜬다.
Why — 서명이 운영 필수 인프라가 된 이유
웹은 TLS 인증서 한 장으로 끝난다 — 서버 신뢰. 그런데 디스크에 깔리는 binary는 다른 문제다.
| 위협 | 서명이 막는 것 |
|---|---|
| MITM 변조 | 다운로드 도중 누가 binary에 malware 주입 → 서명 깨짐 → OS 차단 |
| 출처 위조 | ”Microsoft 발 업데이트”라고 위장한 binary → CA가 그 이름으로 안 발급 |
| 사후 변조 | 디스크의 .exe를 다른 프로세스가 패치 → 서명 검증 실패 |
| 사용자 사회공학 | 사용자가 “이상한 다운로드”를 받았을 때, OS가 명확한 발행자 이름을 보여줌 |
즉, 코드 서명은 그 자체로 보안이 아니라 — 공격 비용을 올리고, 사용자에게 신뢰 신호를 준다. 서명 없는 binary는 2025년 OS에서 사실상 배포 불가.
How — Windows Authenticode
인증서 두 종류 — OV vs EV
| 종류 | 풀네임 | 비용 (연) | SmartScreen 평판 | 키 보관 |
|---|---|---|---|---|
| OV | Organization Validation | 400 | 천천히 누적 (수천 다운로드 + 수 주) | 보통 USB token (2023~) |
| EV | Extended Validation | 700 | 즉시 부여 | 하드웨어 토큰 강제 (FIPS 140-2) |
2023년 6월 이후, OV도 하드웨어 키 보관이 강제됐다 (CA/Browser Forum의 Baseline Requirements 업데이트). 즉 클라우드 CI에서 자동 서명이 어려워졌다 — Azure Key Vault 등 HSM 서비스로 우회.
SmartScreen — Windows의 평판 시스템
- Windows Defender SmartScreen은 어떤 서명자가 얼마나 다운로드되었는지로 평판을 매긴다.
- 낮은 평판: 사용자에게 “이 앱은 잘 알려지지 않았습니다 — 더 많은 정보” 경고. 절반 이상이 닫음.
- EV 인증서는 이 단계를 스킵 — 발급 즉시 평판 부여.
- 그래서 신규 배포에 EV가 거의 필수.
서명 명령 (signtool.exe)
# Windows SDK의 signtool — electron-builder가 내부적으로 호출
signtool sign ^
/tr http://timestamp.digicert.com ^ /* timestamping (필수!) */
/td sha256 ^
/fd sha256 ^
/sha1 <인증서 thumbprint> ^
myapp-setup.exetimestamping: 서명 시점의 신뢰할 만한 timestamp를 함께 박는다. 인증서가 5년 뒤 만료되더라도, 서명 당시 유효했음이 증명되면 OS는 binary를 영구적으로 신뢰한다. 빼먹으면 인증서 만료 시 과거 binary가 갑자기 안 열린다.
electron-builder 설정
win:
target: nsis
publisherName: "My Co Inc."
signtoolOptions:
sign: ./scripts/sign.js # 커스텀 서명 함수 (HSM 호출 등)
signingHashAlgorithms: [sha256]
additionalCertificateFile: build/cert.pemscripts/sign.js (HSM/cloud 서명):
exports.default = async function (config) {
const { path: filePath } = config;
// Azure Key Vault, AWS CloudHSM, DigiCert KeyLocker 등 호출
await runSignCommand({
keyVaultName: 'my-kv',
certName: 'my-cert',
file: filePath,
timestampUrl: 'http://timestamp.digicert.com'
});
};Windows 인증서 발급 흐름
- CA 선택 — DigiCert / Sectigo / SSL.com / GlobalSign
- organization 검증 — 사업자등록증, 전화 인증 (영어 응답), DUNS number
- 하드웨어 토큰 수령 — 국제 배송. 시간 걸림 (1~3주)
- PIN 설정 + signtool 등록
- CI 통합 — USB 토큰을 CI 서버에 꽂거나, Azure Key Vault/eSigner로 클라우드 서명
이 과정이 1~3주 걸린다. 출시 일정 잡을 때 반드시 미리 시작.
How — macOS Developer ID
Apple Developer Program
- 연 $99 USD (organization 또는 individual).
- organization은 D-U-N-S number 필요 → 신청 후 전화 인증 (북미 office hours).
- individual은 본인의 이름이 그대로 노출 — 보통 organization 권장.
인증서 종류
| 인증서 | 용도 |
|---|---|
| Developer ID Application | .app 본체 서명 (필수) |
| Developer ID Installer | .pkg installer 서명 (pkg 빌드 시) |
| Mac App Distribution | Mac App Store 빌드 |
| Mac Installer Distribution | Mac App Store pkg |
| Apple Development | 로컬 개발용 (CI에는 부적합) |
서명 명령 (codesign)
# .app 본체 서명
codesign --force --deep --options runtime \
--entitlements build/entitlements.mac.plist \
--sign "Developer ID Application: My Co Inc. (XXXXXXXXXX)" \
MyApp.app
# 검증
codesign --verify --deep --strict --verbose=2 MyApp.app
spctl -a -t exec -vv MyApp.app # Gatekeeper 시뮬레이션
--deep은.app안의 모든 내부 binary (frameworks, helpers, native modules)에 재귀 서명. 하나라도 안 된 게 있으면 전체 실패.--options runtime은 hardened runtime 적용 — notarization 전제조건. 05 notarization에서 자세히.
electron-builder 설정
mac:
target:
- target: dmg
arch: [x64, arm64]
- target: zip
arch: [x64, arm64]
identity: "Developer ID Application: My Co Inc. (XXXXXXXXXX)"
hardenedRuntime: true
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
gatekeeperAssess: false # CI 환경에서 시도 안 함 (notarize에서 함)build/entitlements.mac.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
{/* Electron이 V8 JIT에 필요 */}
<key>com.apple.security.cs.allow-jit</key>
<true/>
{/* node native modules의 unsigned 코드 허용 */}
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
{/* dyld env vars (electron 빌드 도구가 필요할 수 있음) */}
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
{/* 필요한 경우만 */}
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>entitlements가 너무 관대하면 Apple 리뷰가 까다로워질 수 있고, 너무 좁으면 앱이 일부 기능에서 죽는다. 균형이 어려움.
What — 비용 한눈에
| 항목 | 비용 (연) | 비고 |
|---|---|---|
| Apple Developer Program | $99 | organization 권장 |
| Windows OV 인증서 | 400 | DigiCert/Sectigo. HSM token 별도 |
| Windows EV 인증서 | 700 | SmartScreen 즉시 평판. 신규 배포는 거의 필수 |
| HSM 클라우드 서비스 (Azure Key Vault, DigiCert KeyLocker) | 100/mo | CI 자동 서명용 (USB token 대안) |
| Microsoft Store 개인 | $19 (1회) | organization은 $99/년 |
| Mac App Store | (Apple Dev Program에 포함) | 추가 비용 없음 |
| 합계 (최소) | ~$400/년 | Apple + Win OV (UI 우회) |
| 합계 (권장) | ~1,000/년 | Apple + Win EV + 클라우드 HSM |
출시 결정의 연 운영 비용이 시작되는 지점이다. 1인 개발자가 가장 놀라는 부분.
What-if — 실제 실패 사례
| 함정 | 증상 | 해결 |
|---|---|---|
| timestamping 누락 | 인증서 만료 후 과거 binary가 모두 안 열림 | signtool /tr 옵션, electron-builder는 기본 자동 |
| EV 인증서가 USB token에 있는데 CI에서 못 씀 | CI 매번 사람이 PIN 입력 | DigiCert KeyLocker / Azure Key Vault로 클라우드 HSM 마이그레이션 |
--deep 빼먹음 | 내부 native module 한 개가 unsigned → Gatekeeper 거부 | electron-builder는 자동. 직접 codesign 호출 시 명시 |
| entitlements에 allow-jit 빠짐 | macOS 10.15+에서 V8이 segfault | entitlements에 com.apple.security.cs.allow-jit |
| 인증서 이름이 정확히 일치 안 함 | "Developer ID Application: My Co" vs "Developer ID Application: My Co Inc." | security find-identity -v -p codesigning로 정확한 이름 확인 |
| 새 도메인에 새 EV 인증서 발급 → SmartScreen이 그래도 경고 | EV가 평판을 즉시 부여한다는 건 Microsoft 평판 DB가 인증서를 인지한 이후 | 인증서 발급 후 수 시간~수일 기다림. 첫 다운로드는 경고 가능 |
| macOS 13+ 에서 quarantine 비트 때문에 안 열림 | dmg에서 끌어다 놓은 .app이 xattr -p com.apple.quarantine 보유 | notarization + stapler로 ticket을 박아야 함 → 다음 문서 |
| organization 이름이 바뀌었는데 인증서 갱신 안 함 | 사용자에게 다른 발행자로 보임 → 신뢰 신호 깨짐 | 사명 변경 시 인증서 재발급 (1~3주) |
Insight — 흥미로운 이야기
“2023년 6월 — Windows 코드 서명의 Big Bang”
CA/Browser Forum의 Baseline Requirements 업데이트로 모든 코드 서명 인증서에 하드웨어 키 보관이 강제됐다. 그 이전엔 OV는 PFX 파일로 받아 CI 어디든 export 가능했고, GitHub Actions에서 base64로 secret에 박아 완전 자동 서명이 가능했다. 이제는 Azure Key Vault, DigiCert KeyLocker, SSL.com eSigner 같은 클라우드 HSM 서비스를 반드시 끼워야 한다. CI 파이프라인이 전 세계에서 한꺼번에 새로 짜였다.
“Apple Notarization은 macOS Catalina(10.15, 2019)에서 사실상 필수가 됐다”
그 이전엔 Developer ID 서명만 있으면 macOS가 열어줬다. Catalina부터 notarization 없는 새 binary에 경고 + 우회 강제가 들어왔다. 즉, 서명 ≠ 신뢰가 됐다 — Apple은 서명자가 누구든 Apple이 한 번 더 검토한다. 이게 다음 문서의 주제.
“Sectigo의 EV 인증서 가격 인하 — 2022년”
한때 EV 인증서는 연 $700+ 가 흔했다. Sectigo 등 후발 CA가 연 $250까지 내리며 시장이 흔들렸고, 지금은 연 $300~$400이 일반적. 그러나 DigiCert는 여전히 프리미엄 가격을 유지 — 그 추가 비용은 SLA·고객 지원·CI 통합 도구의 값이라는 입장이다.
요약 + Mermaid
코드 서명은 binary의 출처를 OS에 증명하는 신뢰 사슬이다. Windows는 Authenticode + SmartScreen 평판 (EV가 즉시 평판), macOS는 Developer ID + Apple Developer Program ($99/yr). timestamping과
--deep서명 같은 디테일이 5년 뒤 binary가 살아남는지를 결정한다.