페이지 이동경로
  • 문서>
  • 카카오톡 인증 서비스>
  • K3220: 축약서명

카카오톡 인증 서비스

K3220: 축약서명

이 문서는 카카오톡 인증 서비스의 K3220 축약서명 API 사용법을 안내합니다.

시작하기 전에

기능 소개

카카오톡 축약서명은 카카오톡 애플리케이션(이하 앱)을 통한 전자서명 후, 세션 유효시간 동안 반복적인 축약서명 기능을 제공하는 서비스입니다.

세션은 사용자가 카카오톡으로 사용자 서명 완료 후 세션 정보 가져오기의 호출 결과로 Kakao SDK 내부에 생성됩니다. 세션의 유효시간은 전자서명 요청하기 호출 시 최소 6시간, 최대 48시간으로 설정할 수 있습니다.

임시 키 쌍은 카카오톡 축약서명 시 필요한 암호화 키로, Kakao SDK를 통해 생성 및 삭제할 수 있습니다. 임시 키 쌍은 공개 키와 개인 키로 이뤄져 있으며, 세션의 유효시간 동안만 사용할 수 있습니다. 각 키의 용도는 아래와 같습니다.

  • 공개 키: 카카오톡 축약서명 서비스의 서명 원문을 구성하며, 이용기관 서버에서 축약서명 값 검증 시에도 사용
  • 개인 키: 축약서명 요청하기 호출 시 Kakao SDK가 축약서명을 수행하는 데 사용

이용기관은 사용자 비교 및 검증 용도의 서명자 정보서명자 정보 가져오기로 제공받을 수 있습니다.

카카오톡 인증 서비스 API는 서버간 연동(Server to Server)을 원칙으로 합니다.

Kakao SDK 설치

카카오톡 축약서명 서비스는 Kakao SDK를 통해 이용 가능하며, Android와 iOS 네이티브 앱만 지원합니다. Kakao SDK for Android(이하 Android SDK), Kakao SDK for iOS(이하 iOS SDK)의 Cert 모듈을 설치해 카카오톡 축약서명 서비스에 필요한 API를 사용할 수 있습니다. Kakao SDK 설치 및 설정 안내는 아래를 참고합니다.

Android SDK

아래 순서로 Android SDK 설치 및 설정을 완료합니다.

  1. 설치
  2. 플랫폼 등록
  3. 인터넷 사용 권한 설정
  4. Java 8 사용 설정
  5. 키 해시 등록
  6. Redirect URI 설정
Redirect URI 설정

이용기관 앱에서 사용자를 카카오톡으로 이동시켜 필요한 절차를 수행한 후 복귀 시, 서비스 기능의 정상 동작을 위해 앱 프로젝트의 AndroidManifest.xml 파일에 액티비티(Activity)를 설정해야 합니다. 아래 예제를 참고합니다.

<activity 
    android:name="com.kakao.sdk.cert.CertServiceHandlerActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        
        <!-- Redirect URI: "kakao${NATIVE_APP_KEY}://cert" -->
        <data android:host="cert"
                android:scheme="kakao${NATIVE_APP_KEY}" />
    </intent-filter>
</activity>

새로운 액티비티를 추가하고, name 요소의 값은 "com.kakao.sdk.cert.CertServiceHandlerActivity"로 입력합니다. Android 12(API 31) 이상을 타깃으로 하는 앱인 경우, exported 요소를 반드시 "true"로 선언해야 합니다.

해당 액티비티 하위에 <intent-filter> 요소를 추가하고, hostscheme 요소 값으로 Redirect URI를 설정합니다. scheme 속성의 값은 "kakao${NATIVE_APP_KEY}" 형식으로 입력합니다. 예를 들어 네이티브 앱 키가 "123456789"라면 "kakao123456789"를 입력합니다.

iOS SDK

아래 순서로 iOS SDK 설치 및 설정을 완료합니다.

  1. 설치
  2. 플랫폼 등록
  3. 앱 실행 허용 목록 설정
  4. URL Schemes 설정
  5. URL Handler 설정

iOS SDK 설치 및 설정 완료 후, 아래와 같이 이용기관 앱에서 Cert를 포함해 필요한 모듈을 사용하도록 설정해야 합니다.

import KakaoSDKCommon
import KakaoSDKAuth
import KakaoSDKUser
import KakaoSDKCert
앱 실행 허용 목록

iOS 9.0 이상에서 iOS SDK로 카카오톡 애플리케이션(이하 앱)을 실행하려면 Info.plist 파일에 앱 실행 허용 목록(Allowlist)을 설정해야 합니다. 이 설정은 사용자 정보 보호를 위한 OS 정책에 따라 필요한 것으로, 자세한 내용은 Privacy and Your App을 참고합니다.

  1. [Info] > [Custom iOS Target Properties]에 Array 타입 키(Key)인 Queried URL Schemes을 추가
  2. 해당 키의 [Item]의 값으로 kakaotalk 추가
Info.plist 설정 화면

위 설정은 Info.plist 파일을 직접 수정해 적용할 수도 있습니다. 상세한 설정 값은 아래 예제를 참고합니다.

<key>LSApplicationQueriesSchemes</key>
  <array>
      <string>kakaotalk</string>
  </array>
주의: 앱 실행 허용 목록 설정을 위한 키 변경

Info.plist 파일 내부에서 앱 실행 허용 목록 설정을 위한 키는 Queried URL Schemes 대신 LSApplicationQueriesSchemes를 사용합니다. Xcode 14 미만인 경우, 메뉴 설정에도 Queried URL Schemes가 아닌 LSApplicationQueriesSchemes로 표시될 수 있습니다.

URL Handler 설정

이용기관 앱에서 사용자를 카카오톡으로 이동시켜 필요한 절차를 수행한 후 복귀 시, 처리 결과를 전달받아 서비스 기능이 정상 동작할 수 있도록 앱 프로젝트의 AppDelegate.swift 파일에 handleOpenUrl()을 추가해야 합니다. 아래 예제를 참고합니다.

import KakaoSDKCert
...

class AppDelegate: UIResponder, UIApplicationDelegate {
    ...
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        if (CertApi.isKakaoTalkSignReturnUrl(url)) {
            return CertApi.handleOpenUrl(url: url)
        }

        return false
    }
    ...
}

Deployment target이 iOS 13 이상으로 생성된 프로젝트라면 Info.plist 파일에 UIApplicationSceneManifest 설정이 추가되며, UISceneDelegate.swift를 기본으로 사용하도록 설정됩니다. UISceneDelegate.swift를 기본으로 사용하는 경우, AppDelegate.swift 파일 대신 SceneDelegate.swift 파일에 handleOpenUrl()을 추가합니다.

SwiftUI App Life Cycle 사용 시에는 SDK 초기화와 마찬가지로 ${PROJECT_NAME}App 클래스 내부에 onOpenURL()을 사용하여 handleOpenUrl()을 추가합니다.

아래 예제를 참고합니다.

Swift
SwiftUI App
import KakaoSDKCert
...

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    ...
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        if let url = URLContexts.first?.url {
            if (CertApi.isKakaoTalkSignReturnUrl(url)) {
                _ = CertApi.handleOpenUrl(url: url)
            }
        }
    }
    ...
}
import SwiftUI
import KakaoSDKCommon
import KakaoSDKCert
...

@main
struct SwiftUI_testApp: App {

    ...
    init() {
        // Kakao SDK 초기화
        KakaoSDK.initSDK(appKey: "NATIVE_APP_KEY")
    }

    var body: some Scene {
        WindowGroup {
            // onOpenURL()을 사용해 커스텀 URL 스킴 처리
            ContentView().onOpenURL(perform: { url in
                if (CertApi.isKakaoTalkSignReturnUrl(url)) {
                    CertApi.handleOpenUrl(url: url)
                }
            })
        }
    }
    ...

}

축약서명 과정

카카오톡 축약서명의 진행 과정을 크게 세 단계로 나눠보면 아래와 같습니다.

  1. 전자서명
  2. 전자서명 검증
  3. 축약서명 및 검증

1. 전자서명
  1. 사용자가 이용기관에 로그인을 요청하면, 이용기관이 클라이언트에서 Kakao SDK의 publicKey()를 호출해 공개 키 가져오기를 합니다.
  2. 이용기관이 생성된 공개 키를 클라이언트에서 서버로 전달합니다.
  3. 이용기관이 서버에서 전자서명 준비하기를 호출해 tx_id를 발급받습니다.
  4. 이용기관이 서버에서 전자서명 요청하기를 호출해 tx_id에 대한 세션 유효시간을 설정합니다. 필요한 서명자 정보identify_items 파라미터로 지정할 수 있습니다.
  5. 이용기관이 서버에서 클라이언트로 tx_id를 전달합니다.
  6. 이용기관이 클라이언트에서 txId로 Kakao SDK의 signWithKakaoTalk()를 호출해 카카오톡으로 사용자 서명을 요청합니다.
    1. Kakao SDK가 카카오톡을 실행해 카카오 인증서로 전자서명을 요청합니다.
    2. 사용자가 카카오톡에서 카카오 인증서로 전자서명을 완료하면, 카카오 인증 서버가 txId와 서명 관련 시간 정보를 반환합니다.
참고: 익명서명

카카오톡 축약서명은 익명서명 방식으로, 전자서명 준비하기 호출 단계에서 이용기관이 카카오톡 인증 서비스에 사용자 정보를 전달하지 않습니다.

2. 전자서명 검증
  1. 이용기관이 클라이언트에서 서버로 txId를 전달하고 전자서명 검증을 요청합니다.
  2. 이용기관이 서버에서 전자서명 검증하기를 호출해 전자서명 데이터 전문을 받습니다.
  3. 이용기관이 서버에서 서명자 정보 가져오기를 호출해 서명자 정보를 받습니다.
  4. 이용기관이 서버에서 클라이언트로 검증 결과를 전달하고 로그인을 완료 처리합니다.

3. 축약서명 및 검증
  1. 사용자가 축약서명 필요 서비스 사용을 요청하면, 이용기관이 클라이언트에서 txId로 Kakao SDK의 sessionInfoByAppKey()를 호출해 세션 정보를 받습니다.
    1. Kakao SDK는 sessionInfoByAppKey()에서 반환된 값으로 세션 정보를 업데이트합니다.
    2. 세션이 유효하지 않다면 Kakao SDK는 임시 키 쌍을 삭제하고 에러를 반환합니다. 이 경우, 처음부터 다시 축약서명 과정을 진행해야 합니다.
  2. 세션이 유효할 경우, 이용기관이 축약서명 필요 시 reducedSign()을 호출해 축약서명을 요청합니다.
    1. Kakao SDK가 내부적으로 개인 키를 사용해 축약서명을 수행합니다.
  3. 이용기관이 축약서명 값을 클라이언트에서 서버로 전달합니다.
  4. 이용기관이 서버에서 공개 키로 축약서명 값을 검증합니다.
  5. 축약서명 값이 유효할 경우, 이용기관이 서버에서 클라이언트로 검증 결과를 전달하고 축약서명 필요 서비스를 제공합니다.

전자서명 검증

카카오톡 축약서명의 전자서명은 아래 두 종류로, 서명 원문의 구성과 검증 방식에 차이가 있습니다.

사용자 서명

서비스의 사용자 서명 요청하기 후 사용자가 카카오톡에서 수행하는 서명입니다.

  • 서명 원문: 공개 키, 세션 유효시간으로 구성
  • 검증: 전자서명 검증하기 시 반환받은 전자서명 데이터 전문이 전자서명 요청하기 시 전달한 서명 원문과 일치하는지 비교해 검증, 서명 값 검증은 이용기관에서 직접 구현해야 함

축약서명

사용자 서명 완료 후 사용자의 축약서명 필요 서비스 요청 시 이용기관에서 처리하는 서명입니다.

  • 서명 원문: 공개 키, 세션 유효시간, 요청 시 전달된 데이터로 구성
  • 검증: 축약서명 후 반환받은 서명 값을 이용기관 서버에서 공개 키로 검증, 서명 값 검증은 이용기관에서 직접 구현해야 함

공개 키 가져오기

기본 정보
권한 사전 설정 카카오 로그인 동의항목 레퍼런스
필요 플랫폼 등록 - - Android SDK
publicKey()
ReactiveX Android SDK
publicKey()
iOS SDK
publicKey()

Kakao SDK를 사용해 구현해야 하는 기능입니다.

임시 키 쌍을 생성하고, 공개 키 값을 조회합니다. publicKey()를 호출합니다.

publicKey() 호출 시, 기존에 생성된 임시 키 쌍이 없다면 새로 생성합니다. 기존에 생성된 임시 키 쌍이 있다면 공개 키 값을 반환합니다. null 또는 nil이 반환된 경우, 임시 키 쌍 삭제하기 호출 후 다시 공개 키 가져오기를 요청합니다.

공개 키는 이용기관 서버로 전송해 관리해야 합니다. 이용기관 서버는 공개 키를 전자서명 요청하기의 서명 원문, 전자서명 검증하기의 서명 값 검증에 사용합니다.

요청 실패 시 문제 해결을 참고합니다.

예제

Android
Kotlin
RxKotlin
// 공개 키 가져오기
val publicKey = CertApiClient.instance.publicKey(CertType.K3220)

// 가져온 공개 키를 이용기관 서버로 전송 
...
// 공개 키 가져오기
val publicKey = CertApiClient.rx.publicKey(CertType.K3220)

// 가져온 공개 키를 이용기관 서버로 전송 
...
iOS
// 공개 키 가져오기
guard let publicKey = CertApi.shared.publicKey(certType: .K3220) else {
    // 에러 처리
    print("public key is nil")
    return
}

// 가져온 공개 키를 이용기관 서버로 전송 
...

전자서명 준비하기

기본 정보
메서드 URL 인증 방식
POST https://cert-sign.kakao.com/sign/v2/prepare/${PRODUCT_CODE} REST API 키
권한 사전 설정 카카오 로그인 동의항목
필요 플랫폼 등록
보안: 허용 IP 주소
- -

이용기관 서버에 구현해야 하는 기능입니다.

정산 ID인 settle_id를 카카오톡 인증 서비스에 전달하고, 접수번호인 tx_id를 발급받습니다. tx_id를 사용해 다음 단계의 전자서명 요청하기, 사용자 서명 요청하기, 전자서명 검증하기를 요청할 수 있습니다.

헤더(Header)에 이용기관 앱 REST API 키와 딜러사 앱 REST API 키를 담아 POST로 요청합니다. 요청 URL에 경로 변수(Path variable)로 상품 코드인 K3220을 포함합니다. 쿼리 파라미터에 정산 ID를 포함해야 합니다. 카카오톡 축약서명은 익명서명 방식이므로 사용자 정보는 전달하지 않습니다.

요청 성공 시 응답은 tx_id를 포함합니다. 요청 실패 시 문제 해결을 참고합니다.

요청

헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${DEALER_REST_API_KEY}
인증 방식, 딜러사 앱의 REST API 키로 인증 요청
O
Target-Authorization Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}
인증 방식, 이용기관 앱 REST API 키로 인증 요청
O
경로 변수
이름 타입 설명 필수
PRODUCT_CODE String 상품 코드, K3220 전달 O
쿼리 파라미터
이름 타입 설명 필수
settle_id String 정산 ID O

응답

본문
이름 타입 설명
tx_id String 전자서명 원문 접수번호

예제

요청
curl -X POST "https://cert-sign.kakao.com/sign/v2/prepare/${PRODUCT_CODE}?settle_id=${SETTLE_ID}"
    -H "Authorization: KakaoAK ${DEALER_REST_API_KEY}" \
    -H "Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}" \
    -H "Content-Type: application/json;charset=UTF-8"
응답
{
   "tx_id": "ancb290af0-f72a-44dd-8f41-f73ed11879e3"
}

전자서명 요청하기

기본 정보
메서드 URL 인증 방식
POST https://cert-sign.kakao.com/sign/v2/request REST API 키
권한 사전 설정 카카오 로그인 동의항목
필요 플랫폼 등록
보안: 허용 IP 주소
- -

이용기관 서버에 구현해야 하는 기능입니다.

사용자가 전자서명할 서명 원문을 전달하고, 세션 유효시간을 설정합니다. 요청 성공 이후 사용자가 카카오톡으로 전자서명할 수 있습니다.

헤더(Header)에 이용기관 앱 REST API 키와 딜러사 앱 REST API 키를 담아 POST로 요청합니다. 쿼리 파라미터에 정산 ID를 포함해야 합니다. 본문에 서명할 데이터와 전자서명 요청 정보를 JSON 형식으로 전달해야 합니다. 서명할 데이터는 공개 키와 세션 유효시간으로 구성합니다.

서명자 정보가 필요한 경우, identify_items 파라미터로 필요한 항목을 지정합니다. 사용자 서명 요청하기 완료 후, 서명자 정보 가져오기로 서명자 정보를 제공받을 수 있습니다.

request_type은 사용자가 카카오 인증서로 전자서명할 때 노출되는 [요청구분]의 문구입니다. 아래 이미지를 참고합니다.

전자서명 과정

요청 성공 시 응답은 tx_id와 처리 결과인 result를 포함합니다. tx_id는 이용기관 클라이언트로 전달해 다음 단계인 카카오톡으로 사용자 서명 요청하기에 사용합니다.

요청 실패 시 문제 해결을 참고합니다.

요청

헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${DEALER_REST_API_KEY}
인증 방식, 딜러사 앱의 REST API 키로 인증 요청
O
Target-Authorization Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}
인증 방식, 이용기관 앱 REST API 키로 인증 요청
O
쿼리 파라미터
이름 타입 설명 필수
settle_id String 정산 ID O
본문
이름 타입 설명 필수
tx_id String 전자서명 원문 접수번호 O
encrypted Boolean 서명할 데이터 암호화 여부, 암호화 예제 참고
(기본값: false)
X
data String 서명할 데이터, 아래 항목 포함
txId: 전자서명 요청 원문 접수번호, tx_id와 같은 값
data: data: 서명할 데이터, 공개 키와 공개 키의 유효시간을 포함한 전자서명 원문 JSON 문자열
예제 참고
(최대: 300자)
O
delegate_info JSON 카카오 인증서로 전자서명 시 인증 정보 화면에 노출할 부가 정보, delegate_info: 사용자 인증 요청 안내문, 예제 참고
identify_items String[] 서명자 정보 가져오기 응답으로 확인할 서명자 정보 목록
다음 중 하나 이상의 값 사용 가능
CI: 연계정보
NAME: 이름
BIRTHDAY: 생일
PHONE_NUMBER: 전화번호
GENDER: 성별
X
data: 서명할 데이터
이름 타입 설명 필수
public_key String 공개 키 가져오기 시 반환받은 공개 키 값 O
start_at String 세션 유효시간의 시작 시각
yyyy-mm-dd hh:MM:ss 형식
O
end_at String 세션 유효시간의 종료 시각
yyyy-mm-dd hh:MM:ss 형식
start_at으로부터 최소 6시간, 최대 48시간 이후의 시각이어야 함
O
delegate_info: 사용자 인증 요청 안내문
이름 타입 설명 필수
request_type String 인증 정보 화면의 [요청구분] 문구
(최대: 40자, 공백 포함)
O

응답

본문
이름 타입 설명
tx_id String 전자서명 원문 접수번호
result String 처리 결과
Y: 접수 성공
N: 접수 실패

예제

요청
curl -i -X POST "https://cert-sign.kakao.com/sign/v2/request?settle_id=${SETTLE_ID}" \
    -H "Authorization: KakaoAK ${DEALER_REST_API_KEY}" \
    -H "Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}" \
    -H "Content-Type: application/json;charset=UTF-8" \
    -d '{
        "data": "{\"txId\": \"ancb290af0-f72a-44dd-8f41-f73ed11879e3\",\"data\": \"{\\\"public_key\\\":\\\"${PUBLIC_KEY}\\\",\\\"start_at\\\":\\\"2023-06-19 00:00:00\\\",\\\"end_at\\\":\\\"2023-06-19 06:00:00\\\"}\"}",
        "delegate_info": {
            "request_type": "증권 로그인"
        },
        "identify_items": ["CI", "NAME", "BIRTHDAY", "PHONE_NUMBER", "GENDER"]
    }'
응답
{
   "tx_id": "ancb290af0-f72a-44dd-8f41-f73ed11879e3",
   "result": "Y"
}

사용자 서명 요청하기

기본 정보
권한 사전 설정 카카오 로그인 동의항목 레퍼런스
필요 플랫폼 등록
보안: 허용 IP 주소
필요 필요:
필수 동의항목
Android SDK 공통
SignStatusInfo
Android SDK
signWithKakaoTalk()
ReactiveX Android SDK
signWithKakaoTalk()
iOS SDK 공통
SignStatusInfo
iOS SDK
signWithKakaoTalk()
ReactiveX iOS SDK
signWithKakaoTalk()

Kakao SDK를 사용해 구현해야 하는 기능입니다.

사용자 기기의 카카오톡을 실행하고 사용자에게 전자서명을 요청합니다. signWithKakaoTalk()를 호출합니다. 호출 시 이용기관 서버로부터 전달받은 txId, K3220으로 지정한 certType을 전달해야 합니다.

targetAppKey는 딜러사에서 호출하는 경우에 한 해, 이용기관 앱의 네이티브 앱 키를 지정해 전달합니다.

참고: 전자서명 가능 여부 확인

카카오톡 축약서명 서비스는 카카오톡 앱을 실행하고 전자서명 요청하는 앱투앱(App to app) 방식만 지원합니다. isKakaoTalkSignAvailable()를 호출해 전자서명 가능 여부를 먼저 확인할 수 있습니다. 예제를 참고합니다.

사용자 서명 요청하기 성공 시, 사용자는 카카오톡 앱에서 카카오 인증서로 전자서명을 수행합니다. 사용자가 전자서명을 완료하면 이용기관 클라이언트의 Kakao SDK에 txId와 서명 관련 시간 정보를 반환합니다.

반환받은 txId는 이용기관 클라이언트에 별도 저장해 관리해야 합니다.txId를 이용기관 서버에 전달한 후 전자서명 검증하기를 요청해야 합니다.

요청 실패 시 문제 해결을 참고합니다.

예제

Android
Kotlin
RxKotlin
// 사용자 서명 요청하기
CertApiClient.instance.signWithKakaoTalk(
    context = context,
    certType = CertType.K3220,
    txId = "${TX_ID}",
    targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
) { signStatusInfo, error ->
    if (error != null) {
        // 사용자 서명 요청에 실패한 경우
        if (error is KakaoSdkError) {
            // 에러 유형이 SdkError인 경우에는 기존 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
            CertApiClient.instance.deleteKeyPair(certType = CertType.K3220)

            // txId 삭제 처리
            ...
        }
    } else if (signStatusInfo != null) {
        // 사용자 서명 완료, txId 저장
        val txId = signStatusInfo.txId
        ...
    }
}
val disposables = CompositeDisposable()

CertApiClient.rx.signWithKakaoTalk(
    context = context,
    certType = CertType.K3220,
    txId = "${TX_ID}",
    targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { signStatus ->
            // 사용자 서명 완료, txId 저장
            val txId = signStatus.txId
            ...
        },
        { error ->
            if (error is KakaoSdkError) {
                CertApiClient.rx.deleteKeyPair(certType = CertType.K3220)
                
                // txId 삭제 처리
                ...
            }
        },
    )
    .addTo(disposables)
iOS
Swift
RxSwift
CertApi.shared.signWithKakaoTalk(
    certType: .K3220,
    txId: "${TX_ID}",
    targetAppKey: "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
) { signStatusInfo, error in
    if let error = error {
        // 사용자 서명 요청에 실패한 경우
        if let sdkError = error as? SdkError {
            // 에러 유형이 SdkError인 경우에는 기존 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
            CertApi.shared.deleteKeyPair(certType: .K3220)
            // txId 삭제 처리
            ...
        } else {
            // 네트워크 에러 등 기타 에러 처리
            ...
        }
    } else {
        // 사용자 서명 완료, txId 저장
        if let txId = certTokenInfo?.txId {
            ...
        }
    }
}
CertApi.shared.rx.signWithKakaoTalk(
    certType: .K3220,
    txId: "${TX_ID}",
    targetAppKey: "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
)
.subscribe(
    onNext: { signStatusInfo in
        let txId = signStatusInfo.txId
        // 사용자 서명 완료, txId 저장
        ...

    },
    onError: { error in
        if let sdkError = error as? SdkError {
            // 에러 유형이 SdkError인 경우에는 기존 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
            CertApi.shared.deleteKeyPair(certType: .K3220)
            // txId 삭제 처리
            ...
        } else {
            // 네트워크 에러 등 기타 에러 처리
            ...
        }
    }
)
.disposed(by: disposeBag)

전자서명 검증하기

기본 정보
메서드 URL 인증 방식
GET https://cert-sign.kakao.com/sign/v2/verify REST API 키
권한 사전 설정 카카오 로그인 동의항목
필요 플랫폼 등록
보안: 허용 IP 주소
필요 -

이용기관 서버에 구현해야 하는 기능입니다.

완료된 전자서명을 검증하고 전자서명 데이터 전문을 가져옵니다. 사용자가 사용자 서명 시 수행한 전자서명만을 검증하며, 축약서명에 대한 검증을 지원하지 않습니다.

헤더(Header)에 이용기관 앱 REST API 키와 딜러사 앱 REST API 키를 담아 GET으로 요청합니다. 쿼리 파라미터에 tx_id와 정산 ID를 포함해야 합니다.

요청 성공 시 응답은 전자서명 검증 결과와 전자서명 데이터 전문을 포함합니다. 전자서명 데이터 전문은 사용자 서명 시 전자서명한 서명 값입니다. 전자서명 데이터 전문이 전자서명 요청하기 시 전달한 서명 원문과 일치하는지 비교해 검증해야 합니다. 서명 값 검증 결과 이상이 없는 경우, 이용기관 서비스에서 사용자를 로그인 완료 처리합니다.

요청 실패 시 문제 해결을 참고합니다.

요청

헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${DEALER_REST_API_KEY}
인증 방식, 딜러사 앱의 REST API 키로 인증 요청
O
Target-Authorization Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}
인증 방식, 이용기관 앱 REST API 키로 인증 요청
O
쿼리 파라미터
이름 타입 설명 필수
tx_id String 전자서명 원문 접수번호 O
settle_id String 정산 ID O

응답

본문
이름 타입 설명
status String 전자서명 상태
REQUESTED: 사용자에게 전자서명을 요청함
COMPLETED: 사용자가 전자서명을 완료함
EXPIRED: 유효시간 이내에 사용자가 전자서명을 완료하지 않아 요청이 만료됨
data JSON 전자서명 검증 결과
data: 전자서명 검증 결과 참고
data: 전자서명 검증 결과
이름 타입 설명
result String 전자서명 검증 결과
Y: 검증 성공
N: 실패
signed_data String 전자서명 데이터 전문
(참고: CMSSignedData)

예제

요청
curl -i -X GET "https://cert-sign.kakao.com/sign/v2/verify?settle_id=${SETTLE_ID}&tx_id=${TX_ID}" \
    -H "Authorization: KakaoAK ${DEALER_REST_API_KEY}" \
    -H "Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}" \
    -H "Content-Type: application/json;charset=UTF-8" 
응답
{
    "status": "COMPLETED",
    "data": {
        "result": "Y",
        "signed_data": "${CMSSignedData}"    // 상단 설명 및 표 참고
    }
}

서명자 정보 가져오기

기본 정보
메서드 URL 인증 방식
GET https://cert-sign.kakao.com/sign/v2/identify REST API 키
권한 사전 설정 카카오 로그인 동의항목
필요 - - -

이용기관 서버에 구현해야 하는 기능입니다.

서명자 정보받기 API는 서명을 완료한 서명자 정보를 제공합니다.

이 API는 완료된 전자서명 요청당 1회만 요청 가능하며, 사용자가 서명을 완료한 후, 일정시간(10분) 동안만 정상 응답합니다. 이후 요청에 대해서는 E2017 에러가 발생합니다. 서명자의 개인정보 제공 동의 및 서명 완료 후, 개발 설계 오류, 네트워크 오류 등으로 인한 이용기관의 정보 수신 실패에 대해 카카오는 책임을 지지 않습니다.

헤더(Header)에 이용기관 앱 REST API 키와 딜러사 앱 REST API 키를 담아 GET으로 요청합니다. 본문에 완료된 전자서명 요청의 tx_id와 정산 ID를 포함해야 합니다.

요청 성공 시 응답은 전자서명 요청하기identify_items 파라미터로 지정한 서명자 정보와 서명자 회원번호를 포함합니다. 요청 실패 시 문제 해결에서 에러 코드 정보를 확인합니다.

주의

카카오톡 인증 서비스에서 제공하는 연계정보(CI)와 이름, 생년월일, 전화번호는 사용자 비교 및 검증 용도로만 사용할 수 있습니다. 서명자 정보 가져오기 API를 통해 제공받은 CI, 이름, 생년월일, 전화번호를 이용기관의 정보와 비교해 올바른 사용자인지 검증해야 합니다. 비교 및 검증은 반드시 이용기관 서버에서 수행해야 합니다.

요청

헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${DEALER_REST_API_KEY}
인증 방식, 딜러사 앱의 REST API 키로 인증 요청
O
Target-Authorization Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}
인증 방식, 이용기관 앱 REST API 키로 인증 요청
O
쿼리 파라미터
이름 타입 설명 필수
settle_id String 정산 ID O
tx_id String 전자서명 원문 접수번호 O

응답

본문
이름 타입 설명
name String 암호화된 서명자의 이름
phone_no String 암호화된 서명자의 카카오톡 전화번호
birthday String 암호화된 서명자의 생년월일
gender String 암호화된 서명자의 성별
ci String 암호화된 서명자의 CI(연계정보)

비고: 전자서명 검증하기CI 수신 시 응답에 미포함
vid String 암호화된 서명자의 CI 검증값

비고: 전자서명 검증하기CI 수신 시 응답에 미포함
sign_user_id Long 서명자 회원번호

예제

요청
curl -i -X GET "https://cert-sign.kakao.com/sign/v2/identify?settle_id=${SETTLE_ID}&tx_id=${TX_ID}" \
    -H "Authorization: KakaoAK ${DEALER_REST_API_KEY}" \
    -H "Target-Authorization: KakaoAK ${PARTNER_REST_API_KEY}" \
    -H "Content-Type: application/json;charset=UTF-8"
응답
{
    "name": "${NAME}",
    "phone_no": "${PHONE_NO}",
    "birthday": "${BIRTHDAY}",
    "gender": "${GENDER}",
    "ci": "${CI}",
    "vid": "${VID}",
    "sign_user_id": "${SIGN_USER_ID}"
}
언어별 예제

아래는 실제 서비스에서 고려해야 할 여러 요소(성능, 예외 처리, 동시성 등)가 반영되지 않은 참고용 예제입니다.

Kotlin
Java
C++
class IdentifySample {
 
    val kakaoClient = HttpClient.newBuilder().build()
    val mapper = ObjectMapper()
    val settleId = "PARTNER_SETTLE_ID"
    private val transformation = "AES/CTR/NoPadding"
    private val secretKeySpec = SecretKeySpec("PARTNER_SECRET_KEY".decodeBase64(), "AES")
    private val ivSpec = IvParameterSpec("PARTNER_IV".decodeBase64())
 
    private fun decrypt(content: ByteArray): String =
        Cipher.getInstance(transformation).run {
            init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec)
            String(doFinal(content))
        }
 
    private fun createHttpRequest(uri: URI): HttpRequest {
        return HttpRequest.newBuilder()
            .uri(uri)
            .setHeader(AUTHORIZATION, DEALER_REST_API_KEY)
            .setHeader(TARGET_AUTHORIZATION, PARTNER_REST_API_KEY)
            .GET()
            .build()
    }
 
    // 서명자 정보 가져오기 API
    private fun callIdentify(txId: String): IdentifyResponse {
        val uri = URI.create(createURI(txId))
        val response: HttpResponse<String> = kakaoClient.send(createHttpRequest(uri), HttpResponse.BodyHandlers.ofString())
        return mapper.readValue<IdentifyResponse>(response.body())
    }
 
    private fun createURI(txId: String): String {
        val sb = StringBuilder()
        sb.append("https://cert-sign.kakao.com/sign/v2/identify?settle_id=")
            .append(settleId)
            .append("&tx_id=")
            .append(txId)
        return sb.toString()
    }

    /**
     * 실명인증된 CI가 있는 경우
     * 서명자 정보 가져오기 API의 결과와 실명인증된 CI를 비교
     * !!중요!! 서명자 정보 가져오기 API 호출 및 CI 비교는 반드시 서버에서 수행
     */
    // 서명자 정보 가져오기 API 호출
    fun compareCI(txId: String, ci: String): Boolean {
        // 응답에 포함된 CI를 복호화
        val encryptedCi = callIdentify(txId).ci
        // 실명인증된 CI와 비교
        val decryptedCI = decrypt(encryptedCi.decodeBase64())
        return ci == decryptedCI
    }
}
class IdentifySample {
 
    private final HttpClient kakaoClient = HttpClient.newBuilder().build();
    private final SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode("PARTNER_SECRET_KEY"), "AES");
    private final IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode("PARTNER_IV"));
    private final String settleId = "PARTNER_SETTLE_ID";
 
    private String decrypt(byte[] content) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
        String transformation = "AES/CTR/NoPadding";
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
        return new String(cipher.doFinal(content));
    }
 
    private HttpRequest createHttpRequest(URI uri)  {
        return HttpRequest.newBuilder()
                .uri(uri)
                .setHeader(AUTHORIZATION, DEALER_REST_API_KEY)
                .setHeader(TARGET_AUTHORIZATION, PARTNER_REST_API_KEY)
                .GET()
                .build();
    }
 
    private IdentifyResponse callIdentify(String txId) throws IOException, InterruptedException {
        URI uri = URI.create(createURI(txId));
        // 서명자 정보 가져오기 API
        HttpResponse<String> response = kakaoClient.send(createHttpRequest(uri), HttpResponse.BodyHandlers.ofString());
        return Mapper.getInstance().readValue(response.body(), IdentifyResponse.class);
    }
 
    private String createURI(String txId) {
        StringBuilder sb = new StringBuilder();
        sb.append("https://cert-sign.kakao.com/sign/v2/identify?settle_id=")
                .append(settleId)
                .append("&tx_id=")
                .append(txId);
        return sb.toString();
    }
 
    /**
     * 실명인증된 CI가 있는 경우
     * 서명자 정보 가져오기 API의 결과와 실명인증된 CI를 비교
     * !!중요!! 서명자 정보 가져오기 API 호출 및 CI 비교는 반드시 서버에서 수행
     */
    // 서명자 정보 가져오기 API 호출
    boolean compareCI(String txId, String ci) throws IOException, InterruptedException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        // 응답에 포함된 CI를 복호화
        String encryptedCi = callIdentify(txId).getCi();
        // 실명인증된 CI와 비교
        String decryptedCI = decrypt(Base64.getDecoder().decode(encryptedCi));
        return ci.equals(decryptedCI);
    }
}
class IdentifySample {
private:
    //카카오의 서명자 정보 가져오기 API를 호출.
    json callIdentify(const string& txId) {
        string uri = createURI(txId);
        auto response = kakaoClient.Get(uri.c_str());
        return json::parse(response->body);
    }
 
    string createURI(const string& txId) {
        return "https://cert-sign.kakao.com/sign/v2/identify?settle_id=" + settleId + "&tx_id=" + txId;
    }
 
public:
    /**
     * 실명인증된 CI가 있는 경우
     * 서명자 정보 가져오기 API의 결과와 실명인증된 CI를 비교
     * !!중요!! 서명자 정보 가져오기 API 호출 및 CI 비교는 반드시 서버에서 수행
     */
    // 서명자 정보 가져오기 API 호출
    bool compareCI(const string& txId, const string& ci) {
        // 응답에 포함된 CI를 복호화
        string encryptedCi = callIdentify(txId)["ci"];
        // 실명인증된 CI와 비교
        string decryptedCI = decrypt(base64_decode(encryptedCi));
        return ci == decryptedCI;
    }
};

세션 정보 가져오기

기본 정보
권한 사전 설정 카카오 로그인 동의항목 레퍼런스
필요 플랫폼 등록
보안: 허용 IP 주소
필요 - Android SDK
sessionInfoByAppKey()
ReactiveX Android SDK
sessionInfoByAppKey()
iOS SDK
sessionInfoByAppKey()
ReactiveX iOS SDK
sessionInfoByAppKey()

Kakao SDK를 사용해 구현해야 하는 기능입니다.

전자서명 완료 후 생성된 세션 정보를 가져옵니다. sessionInfoByAppKey()를 호출합니다. 호출 시 txId를 전달해야 합니다.

targetAppKey는 딜러사에서 호출하는 경우에 한 해, 이용기관 앱의 네이티브 앱 키를 지정해 전달합니다.

세션 정보 가져오기 요청이 성공하면 Kakao SDK가 카카오 인증 서버로부터 조회한 세션 정보를 내부에 저장합니다. Kakao SDK에 저장된 세션의 유효시간 동안 축약서명하기를 요청할 수 있습니다.

서버로부터 세션 정보를 응답받지 못한 경우, null 또는 nil이 반환됩니다. 세션 정보 가져오기를 재요청하거나, 임시 키 쌍 삭제하기로 임시 키 쌍과 세션 정보를 삭제하고, 다시 처음부터 전자서명 과정을 진행합니다.

요청 시 txId를 전달하지 않으면 Kakao SDK에 저장되어 있는 세션 정보를 조회하는 용도로 사용할 수 있습니다.

요청 실패 시 문제 해결을 참고합니다.

예제

Android
Kotlin
RxKotlin
// 세션 정보 가져오기
CertApiClient.instance.sessionInfoByAppKey(
    certType = CertType.K3220,
    txId = txId,
    targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
) { sessionInfo, error ->
    if (error != null) {
        // 에러 처리
        Log.e(TAG, error)
        ...
        return@sessionInfoByAppKey
    }

    // 세션 정보 가져오기 성공, Kakao SDK에 세션 정보 저장됨
    ...
}

// txId 없이 sessionInfo 호출 시 세션 정보 조회 가능
val sessionInfo = CertApiClient.instance.sessionInfo(CertType.K3220)
...
val disposables = CompositeDisposable()

// 세션 정보 가져오기
CertApiClient.rx.sessionInfoByAppKey(
    certType = CertType.K3220,
    txId = txId,
    targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ sessionInfo -> 
        // 세션 정보 가져오기 성공, Kakao SDK에 세션 정보 저장됨
        ...
    }, { error -> 
        // 에러 처리
        Log.e(TAG, error)
    })
    .addTo(disposables)

// txId 없이 sessionInfo 호출 시 세션 정보 조회 가능
val sessionInfo = CertApiClient.rx.sessionInfo(CertType.K3220)
...
iOS
Swift
RxSwift
// 세션 정보 가져오기
CertApi.shared.sessionInfoByAppKey(
  certType: .K3220,
  txId: txId,
  targetAppKey: "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
) { sessionInfo, error in
    if let error = error  {
        // 에러 처리
        print(error)
        ...
    }
    else {
        // 세션 정보 가져오기 성공, Kakao SDK에 세션 정보 저장됨
        ...
    }
}
let disposeBag = DisposeBag()

// 세션 정보 가져오기
CertApi.shared.rx.sessionInfoByAppKey(
    certType: .K3220,
    txId: txId,
    targetAppKey: "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
)
.subscribe(
    onSuccess: { (sessionInfo) in
        // 세션 정보 가져오기 성공, Kakao SDK에 세션 정보 저장됨
        ...

    },
    onFailure: { error in
        // 에러 처리
        print(error)
        ...
    }
)
.disposed(by: disposeBag)

// txId 없이 sessionInfo 호출 시 세션 정보 조회 가능
if let sessionInfo = CertApi.shared.sessionInfo(certType: .K3220) {
    // 세션 정보 처리
    ...
}

축약서명하기

기본 정보
권한 사전 설정 카카오 로그인 동의항목 레퍼런스
필요 플랫폼 등록 - - Android SDK
reducedSign()
ReactiveX Android SDK
reducedSign()
iOS SDK
reducedSign()
ReactiveX iOS SDK
reducedSign()

Kakao SDK를 사용해 구현해야 하는 기능입니다.

세션이 유효한지 확인 후, 세션이 유효하다면 서명 원문에 개인 키로 축약서명합니다. reducedSign()을 호출합니다. 호출 시 서명 원문에 포함할 데이터를 전달해야 합니다. 축약서명의 서명 원문은 축약서명 요청 시 전달된 데이터, 공개 키, 세션 유효시간으로 구성됩니다.

축약서명 성공 시, 이용기관 서버에 서명 값을 검증하도록 요청해야 합니다. 이용기관 서버에서 공개 키로 서명 값을 검증할 수 있습니다.

축약서명 시 세션이 유효하지 않은 경우, Kakao SDK에 저장된 임시 키 쌍과 세션 정보가 삭제됩니다.

요청 실패 시 문제 해결을 참고합니다.

예제

Android
Kotlin
RxKotlin
// 축약서명하기
CertApiClient.instance.reducedSign(
    certType = CertType.K3220,
    data = signData,
) { signature, error ->
    if (error != null) {
        // 에러 처리
        ...
        return@reducedSign
    }

    // 축약서명 성공
    // 서버에 서명 값 검증 요청 필요
    ...
}
val disposables = CompositeDisposable()

// 축약서명하기
CertApiClient.rx.reducedSign(
    certType = CertType.K3220,
    data = signData,
)
    .flatMap { signature ->
        // 축약서명 성공
        // 서버에 서명 값 검증 요청 필요
    }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ response ->
        // 서명 값 검증 성공
    }, { error -> 
        // 에러 처리
    })
    .addTo(disposables)
iOS
Swift
RxSwift
// 축약서명하기
CertApi.shared.reducedSign(certType: .K3220, data: "${SIGN_DATA}") { signature, error in
    if let error = error {
        // 축약서명 실패 에러 처리
        print(error)
        ...
    }
    else  {
        if let signature = signature {
            // 축약서명 성공
            ...
        }
        else {
            // 에러 처리
            print("signature is nil")
            ...
        }
    }
}
let disposeBag = DisposeBag()

// 축약서명하기
CertApi.shared.rx.reducedSign(certType: .K3220, data: "${SIGN_DATA}")  
    .subscribe (onSuccess:{ signature in
        if let signature = signature {
            // 축약서명 성공
            ...
        }
        else {
            // 에러 처리
            print("signature is nil")
            ...
        }        
    }, onError: { error in
        // 축약서명 실패 에러 처리
        print(error)
        ...
    })
    .disposed(by: disposeBag)

추가 기능

세션 유효성 확인하기

기본 정보
권한 사전 설정 카카오 로그인 동의항목 레퍼런스
필요 플랫폼 등록 필요 - Android SDK
isValidSession()
ReactiveX Android SDK
isValidSession()
iOS SDK
isValidSession()

Kakao SDK를 사용해 구현해야 하는 기능입니다.

세션이 유효한지 확인합니다. isValidSession()를 호출합니다.

축약서명하기 요청 시에도 Kakao SDK에 저장된 세션이 유효한지 확인 후 축약서명이 진행되지만, 이용기관의 필요에 따라 별도로 세션 유효성을 확인할 수 있습니다.

세션 유효성 확인하기 요청 시, 서버로부터 세션 정보를 응답받지 못헸거나 세션이 유효하지 않으면 false가 반환되고 Kakao SDK에 저장된 임시 키 쌍과 세션 정보가 삭제됩니다. 이 경우, 임시 키 쌍 삭제하기 후 다시 처음부터 전자서명 과정을 진행합니다.

요청 실패 시 문제 해결을 참고합니다.

예제

Android
Kotlin
RxKotlin
// 세션 유효성 확인하기
if (CertApiClient.instance.isValidSession(CertType.K3220)) {
    Log.i(TAG, "세션 유효함")
} else {
    Log.i(TAG, "세션 정보가 없거나 만료됨")
}
// 세션 유효성 확인하기
if (CertApiClient.rx.isValidSession(CertType.K3220)) {
    Log.i(TAG, "세션 유효함")
} else {
    Log.i(TAG, "세션 정보가 없거나 만료됨")
}
iOS
// 세션 유효성 확인하기
if (CertApi.shared.isValidSession(certType: .K3220)) {
    print("세션 유효함")
} else {
    print("세션 정보가 없거나 만료됨")
}

임시 키 쌍 삭제하기

기본 정보
권한 사전 설정 카카오 로그인 동의항목 레퍼런스
필요 플랫폼 등록 필요 - Android SDK
deleteKeyPair()
ReactiveX Android SDK
deleteKeyPair()
iOS SDK
deleteKeyPair()

Kakao SDK를 사용해 구현해야 하는 기능입니다.

Kakao SDK에 저장된 임시 키 쌍과 세션 정보를 삭제합니다.

카카오톡 축약서명 과정 중 Kakao SDK에서 에러가 발생한 경우, 임시 키 쌍과 txId를 삭제하고 공개 키 가져오기부터 카카오톡 축약서명 과정을 다시 시작해야 합니다.

요청 실패 시 문제 해결을 참고합니다.

예제

Android
Kotlin
RxKotlin
// 임시 키 쌍 삭제하기
CertApiClient.instance.deleteKeyPair(CertType.K3220)
// 임시 키 쌍 삭제하기
CertApiClient.rx.deleteKeyPair(CertType.K3220)
iOS
// 임시 키 쌍 삭제하기
CertApi.shared.deleteKeyPair(certType: .K3220)

예제

축약서명 과정의 각 단계를 Kakao SDK의 샘플 앱에서 직접 실행해 볼 수 있습니다.

아래는 샘플 앱의 단계별 구현 예제로, 실제 이용기관 서비스 개발 시 참고합니다.

전자서명

Android
Kotlin
RxKotlin
val targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요

// 카카오톡으로 전자서명 할 수 없는 경우 에러 처리
if (!CertApiClient.instance.isKakaoTalkSignAvailable(context)) {
    Log.e(TAG, "KakaoTalk Sign is not available")

    // 카카오톡 설치 호출 예시
    // val uri = Uri.parse("market://launch?id=com.kakao.talk")
    // startActivity(Intent(Intent.ACTION_VIEW, uri))
    return
}

val publicKey = CertApiClient.instance.publicKey(CertType.K3220) ?: run {
    Log.e(TAG, "public key is null")
    return
}

// 이용기관 서버에서 전자서명 준비하기, 전자서명 요청하기를 통해 txId 발급 후 클라이언트에 전달
// 아래 샘플 앱 코드를 참고해 이용기관에서 직접 구현
CertDemoClient.instance(context).demoSign(
    publicKey = publicKey,
    validateAppKey = KakaoSdk.appKey,
    targetAppKey = targetAppKey,
    returnUrl = "${KakaoSdk.applicationContextInfo.customScheme}://cert",
) { txId, error ->
    if (error != null) {
        Log.e(TAG, error)
        return@demoSign
    }

    // 전자서명 요청
    CertApiClient.instance.signWithKakaoTalk(
        context,
        CertType.K3220,
        txId = txId!!,
        targetAppKey = targetAppKey,
    ) { signStatusInfo, signError ->
        if (signError != null) {
            // 에러 유형이 KakaoSdkError인 경우에는 기존 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
            if (loginError is KakaoSdkError) {
                CertApiClient.instance.deleteKeyPair(CertType.K3220)
                // txId 삭제 처리
                ...
            } else {
                // 네트워크 에러 등 기타 에러 처리
            }
            return@signWithKakaoTalk
        }

        // 전자서명 완료, txId 저장
        val txId = signStatusInfo!!.txId
        // txId 저장
        ...
    }
}
val disposables = CompositeDisposable()
val targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요

if (!CertApiClient.rx.isKakaoTalkSignAvailable(context)) {
    Log.e(TAG, "KakaoTalk Sign is not available")

    // 카카오톡 설치 호출 예시
    // val uri = Uri.parse("market://launch?id=com.kakao.talk")
    // startActivity(Intent(Intent.ACTION_VIEW, uri))
    return
}

val publicKey = CertApiClient.rx.publicKey(CertType.K3220) ?: run {
    CertDemoClient.instance(context).deleteTxId(CertType.K3220)
    Log.e(TAG, "public key is null")
    return
}

RxCertDemoClient.instance(context).demoSign(
    publicKey = publicKey,
    validateAppKey = KakaoSdk.appKey,
    targetAppKey = targetAppKey,
    returnUrl = "${KakaoSdk.applicationContextInfo.customScheme}://cert",
)
    .flatMap { txId ->
        // 전자서명 요청
        CertApiClient.rx.signWithKakaoTalk(
            context = context,
            certType = CertType.K3220,
            txId = txId,
            targetAppKey = targetAppKey
        )
    }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ signStatus ->
        // 전자서명 완료, txId 저장
        val txId = signStatusInfo!!.txId
        // txId 저장
        ...
    }, { error ->
        // 에러 유형이 KakaoSdkError인 경우에는 기존 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
        if (error is KakaoSdkError) {
            CertApiClient.rx.deleteKeyPair(CertType.K3220)
            // txId 삭제 처리
            ...
        } else {
            // 네트워크 에러 등 기타 에러 처리
        }
    })
    .addTo(disposables)
iOS
Swift
RxSwift
let returnUrl = CertApi.certReturnUrl()
let targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요

// 사용자 서명 가능 여부 확인
if (CertApi.isKakaoTalkSignAvailable()) {
    guard let publicKey = CertApi.shared.publicKey(certType: .K3220) else {
        print("public key is nil")
        return
    }
    
    // 이용기관 서버에서 전자서명 준비하기, 전자서명 요청하기로 txId 발급 후 클라이언트에 전달
    // 아래 샘플 앱 코드를 참고해 이용기관에서 직접 구현
    CertDemoApi.shared.demoSign(certType: .K3220,
                                base64encodedDerPublicKey:publicKey,
                                returnUrl: returnUrl,
                                validateAppKey: try! KakaoSDK.shared.appKey(),
                                targetAppKey: targetAppKey,
                                completion: { (txId, error) in
        if let error = error  {
            // 서버 에러 처리
            print(error)
        }
        else  {
            if let txId = txId {
                CertApi.shared.signWithKakaoTalk(certType: .K3220, txId: txId, targetAppKey: targetAppKey) { signStatusInfo, error in
                    if let error = error {
                        if let sdkError = error as? SdkError {
                            // 에러 유형이 SdkError인 경우에는 기존 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
                            CertApi.shared.deleteKeyPair(certType: .K3220)
                            // txId 삭제 처리
                            ...
                        }
                        else {
                            // 네트워크 에러 등 기타 에러 처리
                        }
                    }
                    else {
                        // 사용자 서명 완료, txId 저장
                        if let txId = signStatusInfo?.txId {
                            // txId 저장
                            ...
                        }
                    }
                }
            }
            else {
                // 사용자 서명 실패, 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
                CertApi.shared.deleteKeyPair(certType: .K3220)
                // txId 삭제
                ...
            }
        }
    })
}
else {
    // 카카오톡으로 사용자 서명할 수 없는 경우 에러 처리
    print("kakaotalk is not available")
    ...
    return
}
let disposeBag = DisposeBag()

let returnUrl = CertApi.certReturnUrl()
let targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요

// 카카오톡을 통한 서명 가능 여부 확인
if (UserApi.isKakaoTalkSignAvailable()) {
    guard let publicKey = CertApi.shared.publicKey(certType: .K3220) else {
        print("public key is nil")
        return
    }
    CertDemoApi.shared.rx.demoSign(certType: .K3220,
                                   base64encodedDerPublicKey: publicKey,
                                   returnUrl: returnUrl,
                                   validateAppKey: try! KakaoSDK.shared.appKey(),
                                   targetAppKey: targetAppKey)
        .flatMap { txId -> Observable<SignStatusInfo> in
            CertApi.shared.rx.signWithKakaoTalk(certType: .K3220, txId: txId, targetAppKey:targetAppKey)
        }
        .subscribe(onNext: { certTokenInfo in
            // 서명 완료, txId 저장
            ...
            
        }, onError: { error in
            if let sdkError = error as? SdkError {
                // 에러 유형이 SdkError인 경우에는 기존 임시 키 쌍과 txId 삭제 후, 임시 키 쌍 생성하기부터 다시 요청해야 함
                CertApi.shared.deleteKeyPair(certType: .K3220)
                // txId 삭제 처리
                ...
            }
            else {
                // 네트워크 에러 등 기타 에러 처리
            }
        })
        .disposed(by: disposeBag)
}
else {
    // 카카오톡으로 사용자 서명할 수 없는 경우 에러 처리
    print("kakaotalk is not available")
    ...

    return
}

전자서명 검증

Android
Kotlin
RxKotlin
val targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요

// txId 발급 상태 확인
val txId = CertDemoClient.instance(context).getTxId(CertType.K3220) ?: run {
    Log.e(TAG, "txId is null.")
    return 
}

// 이용기관 서버에 txId를 전달하고 전자서명 검증 요청
CertDemoClient.instance(context).demoVerify(
    txId = txId,
    targetAppKey = targetAppKey,
) { response, verifyError ->
    if (verifyError != null) {
        // 전자서명 검증하기 실패, 에러 처리
        Log.e(TAG, verifyError)
        return@demoVerify
    }

    // 전자서명 검증하기 성공
    ...
}
var disposables = CompositeDisposable()
val targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요
val txId = CertDemoClient.instance(context).getTxId(CertType.K3220) ?: run {
    Log.e(TAG, "txId is null.")
    return 
}

RxCertDemoClient.instance(context)
    .demoVerify(txId = txId, targetAppKey = targetAppKey)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ response -> 
        // 전자서명 검증하기 성공
    }, { error -> 
        // 전자서명 검증하기 실패, 에러 처리
        Log.e(TAG, verifyError)
    })
    .addTo(disposables)
iOS
Swift
RxSwift
let targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요

// txId 발급 상태 확인
guard let txId = CertDemoApi.shared.txId(certType: .K3220) else {
    print("txId is nil. sign first.")
    return
}

CertDemoApi.shared.demoVerify(certType: .K3220, txId: txId, targetAppKey: targetAppKey) { [weak self] response, error in
    if let error = error {
        // 전자서명 검증하기 실패, 에러 처리
        print(error)
    }
    else {
        // 전자서명 검증하기 성공, 이용기관 서비스 서명 완료 처리
    }
}
let disposeBag = DisposeBag()
let targetAppKey = "${PARTNER_NATIVE_APP_KEY}" // 이용기관 앱 네이티브 앱 키, 딜러사에서 호출 시에만 지정 필요

// txId 발급 상태 확인
guard let txId = CertDemoApi.shared.txId(certType: .K3220) else {
    print("txId is nil. sign first.")
    return
}

CertDemoApi.shared.rx.demoVerify(certType: .K3220, txId: txId, targetAppKey: targetAppKey)
    .subscribe (onNext:{ response in
        // 전자서명 검증하기 성공, 이용기관 서비스 로그인 완료 처리
    }, onError: { error in
        // 전자서명 검증하기 실패, 에러 처리
        print(error)
    })
    .disposed(by: disposeBag)

축약서명 및 검증

Android
Kotlin
RxKotlin
// txId 발급 상태 확인
val txId = CertDemoClient.instance(context).getTxId(CertType.K3220) ?: run {
    Log.e(TAG, "txId is null.")
    return
}

// 서명 원문에 포함할 텍스트
val signData = "${SIGN_DATA}"

// 축약서명 요청하기
CertApiClient.instance.reducedSign(
    certType = CertType.K3220,
    data = signData,
) { signature, error ->
    if (error != null) {
        Log.e(TAG, "축약서명 실패 $error")
        return@reducedSign
    }

    Log.i(TAG, "축약서명 성공 $signature")

    // 이용기관 서버에 축약서명 검증 요청하기
    CertDemoClient.instance(context).demoReducedSign(
        txId = txId,
        data = signData,
        signature = signature!!,
        certType = CertType.K3220,
    ) { response, signError ->
        if (signError != null) {
            // 축약서명 검증 실패, 에러 처리
            Log.e(TAG, signError)
            return@demoReducedSign
        }

        // 축약서명 검증 성공
        ...
    }
}
val disposables = CompositeDisposable()

// txId 발급 상태 확인
val txId = CertDemoClient.instance(context).getTxId(CertType.K3220) ?: run {
    Log.e(TAG, "txId is null.")
    return
}

val signData = "hello"

CertApiClient.rx.reducedSign(
    certType = CertType.K3220,
    data = signData, 
)
    .flatMap { signature ->
        // 이용기관 서버에 축약서명 검증 요청하기
        RxCertDemoClient.instance(context).demoReducedSign(
            txId = txId,
            data = signData,
            signature = signature,
            certType = CertType.K3220,
        )
    }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ response ->
        // 축약서명 검증 성공
    }, { error -> 
        // 축약서명 검증 실패, 에러 처리
    })
    .addTo(disposables)
iOS
Swift
RxSwift
// txId 발급 상태 확인
guard let txId = CertDemoApi.shared.txId(certType: .K3220) else {
    // txId가 없는 경우 에러 처리
    print("txId is nil. login first.")
    return
}

// 서명 원문에 포함할 텍스트
let signData = "${SIGN_DATA}"

// 축약서명 요청하기
CertApi.shared.reducedSign(certType: .K3220, data: signData, completion: { signature, error in
    if let error = error {
        // 축약서명 실패
        print(error)
    }
    else  {
        guard let signature = signature else {
            // 에러 처리
            print("signature is nil")
            return
        }
        
        // 이용기관 서버에 축약서명 검증 요청하기
        CertDemoApi.shared.demoReducedSign(certType: .K3220, txId: txId, data: signData, signature: signature, completion:{ response, error in
            if let error = error  {
                // 축약서명 검증 실패, 에러 처리
                print(error)
            }
            else  {
                // 축약서명 검증 성공
            }
        })
    }
})
let disposeBag = DisposeBag()

// txId 발급 상태 확인
guard let txId = CertDemoApi.shared.txId(certType: .K3220) else {
    // txId가 없는 경우 에러 처리
    print("txId is nil. login first.")
    return
}

// 서명 원문에 포함할 텍스트
let signData = "${SIGN_DATA}"

// 축약서명 요청하기
CertApi.shared.rx.reducedSign(certType: .K3220, data: signData)
    .asObservable()
    .flatMap { signature -> Observable<CertDemoResponse> in    
        CertDemoApi.shared.rx.demoReducedSign(certType: .K3220, txId: txId, data: signData, signature: signature)
    }
    .subscribe (onNext:{ response in
        // 축약서명 검증 성공
    }, onError: { error in
        // 축약서명 실패
        print(error)
    })
    .disposed(by: disposeBag)

더 보기