전자서명 로그인 원리 완벽 정리: tokenId는 어디에 저장되고 공개키는 어떻게 사용될까?

전자서명 기반 로그인 시스템을 처음 접하면 가장 헷갈리는 부분이 바로 tokenId, tokenRef, PublicKey, PrivateKey의 역할입니다.

특히 다음과 같은 질문을 많이 하게 됩니다.

  • tokenId는 서버에서 가져오는 것일까?
  • tokenId는 어디에 저장되는 걸까?
  • 서버는 왜 tokenId를 가지고 있을까?
  • 랜덤(Random)은 공개키로 만드는 걸까?
  • tokenRef는 무엇이며 왜 서버로 보내지 않을까?

이번 글에서는 인증서 발급부터 로그인까지의 전체 흐름을 따라가며 각각의 역할을 쉽게 정리해보겠습니다.


전체 구조

전자서명 로그인은 아래와 같은 구조로 동작합니다.

사용자
    │
브라우저
    │
로컬 SDK
    │
인증 서버

가장 중요한 점은 개인키(PrivateKey)는 절대로 서버로 전송되지 않는다는 것입니다.

서버는 공개키(PublicKey)만 가지고 전자서명을 검증합니다.


인증서 발급 과정(T001)

인증서는 최초 발급 시 SDK에서 생성됩니다.

사용자
    │
휴대폰 인증
    │
PIN 설정
    │
SDK

SDK 내부에서는 다음 정보를 생성합니다.

privateKey 생성
publicKey 생성
tokenId 생성

예를 들어 다음과 같은 값이 생성될 수 있습니다.

privateKey

ABCD123456

publicKey

XYZ999999

tokenId

A000123456789

이후 SDK는 서버로 아래 정보를 전송합니다.

POST /T001

{
    "tokenId": "A000123456789",
    "publicKey": "XYZ999999"
}

서버는 이를 데이터베이스에 저장합니다.

USER_TOKEN

-----------------------------------------
tokenId          publicKey
-----------------------------------------
A000123456789    XYZ999999

tokenId는 어디에 저장될까?

많은 사람들이 tokenId를 서버에서 조회한다고 생각하지만 실제로는 그렇지 않습니다.

tokenId는 SDK와 서버 양쪽 모두 저장됩니다.

            인증서 발급

        ┌──────────────┐
        │    SDK       │
        │--------------│
        │ privateKey   │
        │ tokenId      │
        └──────┬───────┘
               │
               │ tokenId
               │ publicKey
               ▼
      ┌────────────────────┐
      │     인증 서버      │
      │--------------------│
      │ tokenId            │
      │ publicKey          │
      └────────────────────┘

즉,

SDK에는

  • PrivateKey
  • tokenId

가 저장되고,

서버에는

  • tokenId
  • PublicKey

가 저장됩니다.


로그인 시 tokenId는 어디서 가져올까?

로그인 시 SDK는 이미 저장되어 있는 tokenId를 가져옵니다.

예를 들어 다음과 같은 코드가 있습니다.

const arrList = TokenSdkHelper.getUidList(this._config.organization);
const uid = arrList[0];

const { tokenRef, tokenId } =
    TokenSdkHelper.getTokenId(uid, this._config.organization);

여기에는 서버 통신이 없습니다.

즉,

브라우저

↓

SDK

↓

저장되어 있는 tokenId 조회

일 뿐입니다.

따라서 로그인할 때 tokenId는 서버에서 내려받는 값이 아니라 SDK 내부 저장소에서 가져오는 값입니다.


왜 서버도 tokenId를 가지고 있을까?

로그인 시 클라이언트는 서버에게 tokenId를 전달합니다.

POST /SIG-001

{
    "tokenId": "A000123456789"
}

서버는 tokenId를 이용해 데이터베이스를 조회합니다.

USER_TOKEN

-----------------------------------------
tokenId          publicKey
-----------------------------------------
A000123456789    XYZ999999

이를 통해 해당 인증서의 공개키를 찾을 수 있습니다.

만약 서버가 tokenId를 가지고 있지 않다면

클라이언트

tokenId=A0001

↓

서버

"이 tokenId가 누구인지 모르겠습니다."

라는 상황이 됩니다.

반대로 SDK가 tokenId를 가지고 있지 않다면

서버

"어떤 인증서를 사용할 건가요?"

↓

클라이언트

"모르겠습니다."

가 됩니다.

즉, 동일한 인증서를 식별하기 위해 SDK와 서버가 같은 tokenId를 공유하는 것입니다.


서버는 랜덤(Random)을 어떻게 만들까?

여기서 가장 많이 오해하는 부분이 있습니다.

많은 사람들이

공개키로 랜덤을 생성한다.

고 생각하지만 실제로는 그렇지 않습니다.

랜덤은 서버가 새롭게 생성하는 난수(Random Challenge) 입니다.

예를 들어 Java에서는 다음과 같이 생성합니다.

String random = UUID.randomUUID().toString();

또는

SecureRandom secureRandom = new SecureRandom();

처럼 생성할 수도 있습니다.

예시

8A7F2D9B18CC4E91A5D3

이 값은

  • tokenId와도 관계없고
  • PublicKey와도 관계없으며
  • 매 로그인마다 새롭게 생성됩니다.

서버가 Random을 저장하는 이유

생성한 Random은 서버가 잠시 저장합니다.

예를 들어

LOGIN_CHALLENGE

-----------------------------------
tokenId        random
-----------------------------------
A000123456     8A7F2D9B18CC...

또는 Redis를 사용할 수도 있습니다.

LOGIN:A000123456

↓

8A7F2D9B18CC...

왜 저장할까요?

나중에 클라이언트가 만든 전자서명이 올바른지 검증해야 하기 때문입니다.


클라이언트는 무엇을 할까?

SDK는

  • 저장되어 있는 PrivateKey
  • 서버에서 받은 Random

을 이용하여 전자서명을 생성합니다.

전자서명 = Sign(
    privateKey,
    random
)

그리고 서버로 전달합니다.

POST /SIG-002

{
    tokenId,
    random,
    signData
}

서버는 어떻게 검증할까?

서버는 다음 순서대로 검증합니다.

  1. tokenId로 공개키 조회
  2. 저장해둔 Random 조회
  3. 전자서명 검증 수행

즉,

verify(
    publicKey,
    random,
    signData
)

를 수행합니다.

검증이 성공하면

로그인 성공

이 됩니다.


전체 로그인 흐름

① SDK

tokenId 전송

        │
        ▼

② 서버

tokenId로 공개키 조회

        │
        ▼

③ 서버

새로운 Random 생성

        │
        ▼

④ SDK

PrivateKey로 전자서명 생성

        │
        ▼

⑤ 서버

PublicKey로 전자서명 검증

        │
        ▼

로그인 성공

이 방식을 Challenge-Response 인증 방식이라고 합니다.


tokenRef는 무엇일까?

tokenRef도 자주 헷갈리는 개념입니다.

tokenId는

  • 서버와 SDK가 모두 알고 있는 인증서 식별자

입니다.

반면 tokenRef는

  • SDK 내부에서만 사용하는 참조값

입니다.

예를 들어 SDK 안에 여러 개의 인증서가 있다고 가정해보겠습니다.

SDK

토큰1
토큰2
토큰3

SDK는 내부적으로

tokenRef = 7

처럼 번호를 관리합니다.

전자서명을 만들 때는

sign(
    tokenRef,
    random,
    pin
)

처럼 사용됩니다.

SDK는 tokenRef를 이용하여

  • 해당 인증서의 PrivateKey
  • PIN

을 찾아 전자서명을 수행합니다.

tokenRef는 절대로 서버로 전송되지 않습니다.


PublicKey와 PrivateKey의 역할

많은 분들이 두 키의 역할을 혼동합니다.

항목역할

PrivateKey 전자서명 생성
PublicKey 전자서명 검증

PrivateKey는 사용자의 디바이스 밖으로 절대로 나가지 않습니다.

PublicKey는 서버가 보관하며 전자서명 검증에 사용합니다.


저장 위치 정리

항목SDK(클라이언트)인증 서버

tokenId O O
tokenRef O X
PrivateKey O X
PublicKey 선택적으로 보관 가능 O
PIN O X
Random X 로그인 중 임시 저장

핵심 정리

전자서명 로그인에서 가장 중요한 점은 다음과 같습니다.

  • tokenId는 SDK와 서버가 함께 저장하는 인증서 식별자이다.
  • PrivateKey는 SDK 내부에만 저장되며 절대로 서버로 전송되지 않는다.
  • PublicKey는 서버가 저장하고 전자서명을 검증하는 데 사용한다.
  • Random은 서버가 매 로그인마다 새롭게 생성하는 난수이다.
  • 서버는 tokenId로 PublicKey를 조회한 뒤 Random과 전자서명을 검증한다.
  • tokenRef는 SDK 내부에서만 사용하는 참조값이며 서버에는 전달되지 않는다.

결국 전자서명 로그인은 tokenId로 인증서를 식별하고, 서버가 생성한 Random에 대해 PrivateKey로 서명한 뒤, 서버가 PublicKey로 이를 검증하는 Challenge-Response 방식으로 동작합니다. 이러한 구조 덕분에 PrivateKey를 외부로 노출하지 않고도 안전하게 사용자를 인증할 수 있습니다.

FAQ

Q. tokenId는 민감한 정보인가요?

아닙니다. tokenId는 인증서를 식별하기 위한 ID이며, 민감한 비밀 정보는 아닙니다. 실제 보안의 핵심은 PrivateKey입니다.

Q. PrivateKey는 서버에 저장되나요?

아닙니다. PrivateKey는 SDK 또는 보안 저장소에만 존재하며 서버에는 저장되거나 전송되지 않습니다.

Q. PublicKey는 왜 서버에 저장되나요?

서버가 클라이언트가 생성한 전자서명이 올바른지 검증하기 위해서입니다.

Q. Random을 매번 새로 만드는 이유는 무엇인가요?

같은 Random을 반복해서 사용하면 이전 전자서명을 재사용하는 재전송(Replay) 공격이 가능해질 수 있습니다. 따라서 로그인 시마다 새로운 Random을 생성하여 이러한 공격을 방지합니다.

관련글