Skip to content
마지막 작성자: hyejung-song
최근 업데이트: 25. 7. 7. 오후 6:50

연동 개요 및 용어 정의 ​

모바일 신분증 연동을 위한 개념, 용어, 기본 Handler 경로, 인코딩 방식 등을 설명합니다.

Default handler 경로 ​

종류Handler 경로예시 URL
프로필 조회/mip/profilehttp://example.com/mip/profile
이미지 조회/mip/imagehttp://example.com/mip/image
VP 제출/mip/vphttp://example.com/mip/vp
오류 제출/mip/errorhttp://example.com/mip/error

1. 개요 ​

서비스 제공자(SP, Service Provider) 연계를 위해 필요한 용어와 인터페이스, 데이터 전송 방식에 대해 설명해요.

INFO

  • mDL (모바일 운전면허증)
    모바일로 발급한 운전면허증이에요.
  • 모바일 신분증
    국가에서 발급한 신분증을 스마트폰 앱 형태로 제공한 것이에요.
  • DID (탈중앙화 신원, Decentralized ID)
    중앙기관 없이 신원을 관리할 수 있는 방식이에요.
  • DID 문서
    DID와 연결된 정보(ID, 공개키 등)를 담고 있으며, 블록체인에 등록돼 누구나 조회할 수 있어요.
    W3C의 Decentralized Identifier v1.0과 Verifiable Credential Data Model v1.0을 따라요.
  • VC (검증 가능한 증명서, Verifiable Credential)
    발급자(Issuer)가 소유자(Holder)에게 발급한 디지털 증명서예요.
  • VP (검증 가능한 제시 자료, Verifiable Presentation)
    Holder가 하나 이상의 VC를 모아 서명한 문서로, 서비스 이용을 위해 검증자에게 제출해요.
  • Holder (소유자)
    VC를 소유한 사람 또는 앱이에요.
  • Issuer (발급자)
    VC를 발급하는 기관이나 앱이에요.
  • Verifier (검증자)
    VP를 받아서 진위 여부를 확인하는 주체로, 보통 서비스 제공자(SP)가 맡아요.

인터페이스와 전송 방식 ​

모바일 신분증을 사용하는 다양한 상황에 따라 인터페이스와 전송 방식을 선택할 수 있어요. 아래 표는 사용 시나리오에 따라 어떤 방식이 쓰이는지 요약한 거예요.

인터페이스 종류 ​

방식설명참고 항목
QR-MPM단말기(상점 등)에서 QR코드를 보여주고 신분증 앱이 스캔해요.Merchant Presented Mode
QR-CPM신분증 앱에서 QR코드를 생성해 단말기가 스캔해요.Customer Presented Mode
App2App신분증 앱과 서비스 앱이 URL 스킴으로 직접 통신해요.
PUSH조폐공사 서버가 신분증 앱에 VP 제출을 요청해요.
BLE + QRQR코드로 BLE(Bluetooth Low Energy) 연결한 뒤 통신해요.CPM, MPM 모두 사용 가능
NFC신분증 앱이 단말기와 NFC 방식으로 직접 통신해요.

전송 방식 ​

모드설명특징
directVP를 검증 서버에 직접 전송해요.
indirectApp2App이나 BLE 방식으로 VP를 단말기나 서비스 앱에 전달해요.
proxy검증 서버와 통신할 수 없을 때, 중계 서버를 통해 VP를 전송해요.웹소켓 사용
nfc단말기와 NFC 방식으로 직접 VP를 전송해요.

인코딩과 디코딩 ​

데이터 전송 시 문자열을 URL-safe 형식으로 변환하려면 Base64 인코딩을 사용해요. 이때 RFC 4648에서 정의한 URL 및 파일 이름 안전 알파벳을 따르는 방식이에요.

Base64 (URL Safe) ​

Node.js 예시

js
const urlsafe_base64 = require('urlsafe-base64');
const { Buffer } = require('buffer');

function base64Encode(str) {
    return urlsafe_base64.encode(Buffer.from(str));
}

function base64Decode(base64) {
    return urlsafe_base64.decode(base64).toString();
}

const base64 = base64Encode('Test string');

console.log(base64);               // 인코딩 결과
console.log(base64Decode(base64)); // 디코딩 결과

Java 8 예시

java
import java.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;

String rawCookieValue = "(DMPD)b80f9ed8-4e66-41a4-ac60-b46ea5586cf0";

// URL-safe Base64 인코딩 (Padding 제거)
byte[] encodedBytes = Base64.getUrlEncoder()
                            .withoutPadding()
                            .encode(rawCookieValue.getBytes(UTF_8));
String encodedValue = new String(encodedBytes, UTF_8);

System.out.println(encodedValue);

// Node.js에서 인코딩한 결과와 비교
String encodedFromNodeJs = "KERNUEQpYjgwZjllZDgtNGU2Ni00MWE0LWFjNjAtYjQ2ZWE1NTg2Y2Yw";
System.out.println(encodedFromNodeJs);
System.out.println("Node.js와 Java 인코딩 결과 일치 여부: " + encodedValue.equals(encodedFromNodeJs));

// 디코딩
byte[] decodedBytes = Base64.getUrlDecoder().decode(encodedValue.getBytes(UTF_8));
String decodedValue = new String(decodedBytes, UTF_8);

// 검증
System.out.println("++++++++++++");
System.out.println(rawCookieValue);
System.out.println(decodedValue);
System.out.println("원본 문자열과 디코딩 결과 일치 여부: " + rawCookieValue.equals(decodedValue));