DID(탈중앙 신원)와 검증 가능한 자격증명, 실무에서는 어떻게 다루나
DID·VC의 문서 구조와 신뢰 모델, OIDC4VCI·DIDComm 등 상호운용 프로토콜, 키 수명주기와 검증 절차를 실무 관점에서 정리한다.
2026-08-12 · 최초 발행 2025-12-07
계정 모델의 한계와 DID의 등장
신원 확인의 신뢰·프라이버시·상호운용 요구는 디지털 전환과 함께 계속 커지고 있다. 서비스마다 계정을 새로 만들고 비밀번호를 관리하는 중앙 집중식 모델은 이 요구를 감당하기 어렵다. 분산 ID(DID, Decentralized Identity)는 개인·조직·디바이스가 자체적으로 통제 가능한 식별자와, 암호학적으로 검증 가능한 자격증명(VC, Verifiable Credentials)을 결합해 이 문제에 접근하는 방식이다.
DID(Decentralized Identifier)는 특정 DID 메서드 스킴에 따라 생성되는 글로벌 고유 식별자다. did:key, did:web, did:ion처럼 URL과 유사한 구문으로 메서드와 식별자를 조합하며, 중앙 등록소 의존을 최소화한다. 이 식별자가 실제로 작동하려면 DID 문서(DID Document)가 필요하다 — 해당 DID의 공개키, 인증·인가 메서드, 서비스 엔드포인트 등을 서술한 JSON-LD 문서로, 키 회전·폐기 같은 컨트롤 포인트를 제공한다.
여기에 발급자가 서명한 자격증명인 VC와, 보유자가 선택적으로 최소 정보만 제시하는 프레젠테이션인 VP(Verifiable Presentation)가 결합된다. 암호학적 검증으로 위·변조를 막는다. 이 구조는 발급자(Issuer) → 홀더(Holder/Wallet) → 검증자(Verifier)의 삼자 모델로 돌아가며, 발급자의 신뢰도는 신뢰 레지스트리와 거버넌스 정책으로 관리한다.
문서 구조와 신뢰 프레임워크
DID는 메서드와 식별자 조합으로 구성되어 중앙 IdP 없이도 참조할 수 있다. DID 문서는 Verification Method, Assertion/Authentication Method, Service Endpoint로 핵심 정보를 서술하며, 키 회전·복구·폐기 정책을 포함한다.
신뢰는 발급자-홀더-검증자 모델을 기반으로 하고, 발급자의 신뢰도는 거버넌스 정책과 트러스트 레지스트리로 관리된다. eIDAS2, NIST 800-63 같은 규제와 연동할 경우 신원 보증 수준(IAL/AAL/IALe) 매핑이 필요한데, 이 부분은 표준·규제 변화가 잦으므로 최신 정보를 확인해야 한다.
암호학적 메커니즘과 키 관리
서명은 JWS/JWT나 LD-Proofs(BBS+, Ed25519Signature2020 등) 기반 전자서명을 사용하며, 선택적 공개가 필요하면 ZKP로 프라이버시를 보호한다. 키는 생성 → 보관(지갑) → 회전 → 폐기 → 복구의 수명주기를 거쳐야 하고, 하드웨어 보안(HSM/TEE/Passkey)을 권장한다.
상호운용을 위한 프로토콜
발급에는 OIDC4VCI, 제시에는 OID4VP/SIOP2, 보안 메시징에는 DIDComm v2, 브라우저 상호작용에는 CHAPI 같은 표준이 쓰인다. OpenID4VC 프로파일이나 Trust Framework 같은 프로파일 합의가 있어야 실제로 상호운용이 된다 — 일부 스펙은 아직 안정화가 진행 중이므로 최신 정보를 확인해야 한다.
폐기와 상태 관리
VC의 상태(폐기·일시중지·만료)는 StatusList2021이나 Revocation List 같은 상태 벡터로 관리한다. 이를 정책 엔진(ABAC/RBAC/OPA)과 결합하면 접근 통제와 KYC/AML 규정 준수를 함께 구현할 수 있다.
검증이 실제로 일어나는 순서
검증자가 제시를 요청하고, 홀더 지갑이 VC를 선택·필터링하고, DID를 해석해 서명과 정책을 확인하는 흐름을 도식으로 보면 다음과 같다.
실무에서 쓰이는 곳
금융 KYC/온보딩에서는 모바일 지갑으로 신원·자격 VC를 제시해 비대면 계좌개설이나 대출 심사 단계를 단축한다. AML 스크리닝과 결합하면 선택적 공개로 데이터를 최소화할 수 있다.
기업 사내증·파트너 온보딩에서는 사내 DID 기반 사원증을 발급해 물리·논리 출입을 통합하고, 협력사 엔지니어의 임시 권한을 관리한다. 만료·폐기로 즉시 접근을 차단하고 감사 추적성을 확보한다.
공급망·문서 신뢰 영역에서는 원산지 증명이나 환경·품질 인증 VC로 위변조를 막고, IoT 디바이스 DID와 연계해 세관·물류 단계의 자동 검증으로 리드타임을 단축한다.
웹 로그인·연령 확인에서는 SIOP2/OID4VP 기반의 패스워드리스 로그인으로 연령·거주지를 최소한만 증명하고, 계정 연계 없는 비식별 로그인으로 추적을 최소화한다.
도입 효과
- 비용/시간: KYC 재사용으로 온보딩 시간을 평균 30
60% 단축하고, 수작업 검증 TCO를 2040% 절감한다(프로세스 자동화 기준). 문서 위변조 검출로 분쟁 처리 시간이 50% 이상 축소되는 사례가 보고된다. - 보안/프라이버시: 탈중앙 서명 검증으로 피싱·계정 탈취 공격면이 줄고, 데이터 최소 수집으로 규제 리스크가 완화된다. 키를 하드웨어로 보호하고 다중 디바이스 복구를 지원하면 계정 영속성이 강화된다.
- 확장성/상호운용: 메서드·프로토콜 표준화로 벤더 종속을 줄이고, 도메인을 넘나드는 신원 재사용을 촉진한다. 트러스트 프레임워크를 도입하면 파트너 온보딩 기간이 단축된다.
DID 메서드 고르기
| 항목 | did:key | did:web | did:ion |
|---|---|---|---|
| 성능 | 매우 높음(로컬 생성/검증) | 높음(HTTP 캐시 활용) | 중간(앵커링/확산 지연) |
| 확장성 | 개인/디바이스 단건 최적 | 조직 단위 대규모 호스팅 용이 | 대규모 글로벌 분산 적합 |
| 일관성 | 로컬 일관성 우수, 회수 곤란 | 도메인 통제 기반, DNS 의존 | 탈중앙 일관성, 반영 지연 가능 |
| 안정성 | 중앙 의존 없음, 복구 어려움 | 도메인/HTTPS 가용성 의존 | 체인/프로토콜 안정성 의존 |
| 운영 편의 | 간편 생성, 회전/발견 어려움 | DevOps 친숙, CI/CD로 관리 | 초기 학습/운영 복잡도 높음 |
메서드 선택은 신뢰·거버넌스 요구와 운영팀 역량에 따라 결정한다.
도입 절차
- 요구분석/거버넌스: 규제 준수 범위(신원 보증 수준, 데이터 보존)를 정의하고, 발급자 적격성·폐기 정책과 신뢰 레지스트리·루트 정책을 수립한다.
- 메서드/프로토콜 설계: did:web vs did:ion 같은 메서드를 선택하고, 지갑 UX·복구 전략과 OIDC4VCI/OID4VP 프로파일, 상태 리스트·정책 엔진 통합을 결정한다.
- 키·지갑 보안: HSM/TEE/플랫폼 Keystore로 키를 생성·보관하고 회전·폐기를 자동화한다. 디바이스 분실에 대비해 소셜 리커버리, 다중 디바이스 같은 복구 채널을 설계한다.
- 운영/관찰성: 리졸버 캐시, SLA 모니터링, 상태 리스트 갱신 파이프라인을 갖추고, 규정 변경에 대응할 정책 버전 관리를 유지한다.
보안 모범사례와 트레이드오프
모범사례로는 Nonce·Audience 바인딩, 재사용 방지, 시계 동기화, 프레젠테이션 최소 공개를 꼽을 수 있다. 키 수명주기 자동화(회전/폐기)와 다중 서명·계약 지갑도 고려 대상이다. StatusList나 만료, 발급자 트러스트 체크 같은 상태 검증은 필수다.
트레이드오프는 메서드마다 다르다. did:web은 운영이 단순한 대신 도메인·DNS 의존성이 늘고 상호상관 위험이 있다. did:ion은 탈중앙성이 강한 대신 확산 지연과 운영 복잡도가 따른다. LD-Proof(BBS+)의 선택적 공개는 프라이버시에 유리하지만 구현이 복잡하고 성능 부담이 있으며, JWT VC는 운영은 단순하지만 프라이버시 제약이 미세하게 남는다.
로컬 환경에서 VC 서명·검증해보기
전제조건: Python 3.11+, pip install pyjwt cryptography. 아래 코드는 데모 목적의 자기완결 예제다. 실제 환경에서는 DID 해석기(Universal Resolver/메서드별)에서 DID Document를 조회하고, 상태 리스트·정책 검증을 추가해야 한다.
# env: python 3.11+, pip install pyjwt cryptography
import base64, json, time
import jwt
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
def b64url(data: bytes) -> str:
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
def b64url_dec(s: str) -> bytes:
pad = "=" * (-len(s) % 4)
return base64.urlsafe_b64decode(s + pad)
# 1) 키 생성(실운영: HSM/OS 키스토어 사용 권장)
sk = ed25519.Ed25519PrivateKey.generate()
pk = sk.public_key()
# 2) DID 및 DID Document 구성(데모: did:web:example.com)
did = "did:web:example.com"
kid = f"{did}#key-1"
pub_bytes = pk.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
jwk = {"kty":"OKP","crv":"Ed25519","x": b64url(pub_bytes)}
did_doc = {
"id": did,
"verificationMethod": [
{"id": kid, "type":"JsonWebKey2020", "controller": did, "publicKeyJwk": jwk}
],
"assertionMethod": [kid]
}
# 3) VC(JWT 형태) 발급: 발급자=did:web:example.com
now = int(time.time())
vc_payload = {
"iss": did,
"sub": "did:example:holder123",
"nbf": now - 10,
"iat": now,
"exp": now + 600,
"jti": "urn:uuid:123e4567-e89b-12d3-a456-426614174000",
"vc": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential","EmailCredential"],
"credentialSubject": {"email": "user@example.com", "email_verified": True}
}
}
private_key_pem = sk.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
token = jwt.encode(vc_payload, private_key_pem, algorithm="EdDSA", headers={"kid": kid, "typ":"JWT"})
print("VC(JWT):", token)
# 4) 검증: 헤더의 kid로 DID Document에서 공개키 조회 → 검증
hdr = jwt.get_unverified_header(token)
assert hdr["kid"] == kid
# 리졸버 대체(데모): 로컬 did_doc에서 공개 JWK 획득
pub_jwk = next(vm["publicKeyJwk"] for vm in did_doc["verificationMethod"] if vm["id"] == hdr["kid"])
pub_key = ed25519.Ed25519PublicKey.from_public_bytes(b64url_dec(pub_jwk["x"]))
public_key_pem = pub_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
decoded = jwt.decode(token, public_key_pem, algorithms=["EdDSA"], options={"require": ["iss","exp","nbf"]})
print("검증 성공, iss:", decoded["iss"], "type:", decoded["vc"]["type"])
확장 포인트로는 세 가지를 염두에 두면 된다. did:web은 https://도메인/.well-known/did.json에서 DID Document를 조회하는 방식으로 해석하고, 상태 검증은 StatusList2021 엔드포인트를 조회해 만료·폐기를 체크하며, 프라이버시가 중요하다면 BBS+ 같은 ZKP로 속성을 최소 공개하고 nonce 바인딩을 적용한다.
메서드 선택, 프로토콜 프로파일, 키·지갑 보안, 거버넌스·상태 관리, 운영 관찰성까지 전 주기를 함께 설계해야 DID가 실제로 신뢰 가능한 신원 인프라로 작동한다. 규제·표준의 최신 동향을 점검하며 파일럿 → 거버넌스 정립 → 확산 단계로 점진적으로 도입하는 편이 안전하다.