이 문서는 카카오톡 인증 서비스의 K3220 축약서명 API 사용법을 안내합니다.
카카오톡 축약서명은 카카오톡 애플리케이션(이하 앱)을 통한 전자서명 후, 세션 유효시간 동안 반복적인 축약서명 기능을 제공하는 서비스입니다.
세션은 사용자가 카카오톡으로 사용자 서명 완료 후 세션 정보 가져오기의 호출 결과로 Kakao SDK 내부에 생성됩니다. 세션의 유효시간은 전자서명 요청하기 호출 시 최소 6시간, 최대 48시간으로 설정할 수 있습니다.
임시 키 쌍은 카카오톡 축약서명 시 필요한 암호화 키로, Kakao SDK를 통해 생성 및 삭제할 수 있습니다. 임시 키 쌍은 공개 키와 개인 키로 이뤄져 있으며, 세션의 유효시간 동안만 사용할 수 있습니다. 각 키의 용도는 아래와 같습니다.
이용기관은 사용자 비교 및 검증 용도의 서명자 정보를 서명자 정보 가져오기로 제공받을 수 있습니다.
카카오톡 인증 서비스 API는 서버간 연동(Server to Server)을 원칙으로 합니다.
카카오톡 축약서명 서비스는 Kakao SDK를 통해 이용 가능하며, Android와 iOS 네이티브 앱만 지원합니다. Kakao SDK for Android(이하 Android SDK), Kakao SDK for iOS(이하 iOS SDK)의 Cert
모듈을 설치해 카카오톡 축약서명 서비스에 필요한 API를 사용할 수 있습니다. Kakao SDK 설치 및 설정 안내는 아래를 참고합니다.
아래 순서로 Android SDK 설치 및 설정을 완료합니다.
이용기관 앱에서 사용자를 카카오톡으로 이동시켜 필요한 절차를 수행한 후 복귀 시, 서비스 기능의 정상 동작을 위해 앱 프로젝트의 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>
요소를 추가하고, host
와 scheme
요소 값으로 Redirect URI를 설정합니다. scheme
속성의 값은 "kakao${NATIVE_APP_KEY}" 형식으로 입력합니다. 예를 들어 네이티브 앱 키가 "123456789"라면 "kakao123456789"를 입력합니다.
아래 순서로 iOS SDK 설치 및 설정을 완료합니다.
iOS SDK 설치 및 설정 완료 후, 아래와 같이 이용기관 앱에서 Cert
를 포함해 필요한 모듈을 사용하도록 설정해야 합니다.
import KakaoSDKCommon
import KakaoSDKAuth
import KakaoSDKUser
import KakaoSDKCert
iOS 9.0 이상에서 iOS SDK로 카카오톡 애플리케이션(이하 앱)을 실행하려면 Info.plist 파일에 앱 실행 허용 목록(Allowlist)을 설정해야 합니다. 이 설정은 사용자 정보 보호를 위한 OS 정책에 따라 필요한 것으로, 자세한 내용은 Privacy and Your App을 참고합니다.
Queried URL Schemes
을 추가kakaotalk
추가위 설정은 Info.plist 파일을 직접 수정해 적용할 수도 있습니다. 상세한 설정 값은 아래 예제를 참고합니다.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>kakaotalk</string>
</array>
Info.plist 파일 내부에서 앱 실행 허용 목록 설정을 위한 키는 Queried URL Schemes
대신 LSApplicationQueriesSchemes
를 사용합니다. Xcode 14 미만인 경우, 메뉴 설정에도 Queried URL Schemes
가 아닌 LSApplicationQueriesSchemes
로 표시될 수 있습니다.
이용기관 앱에서 사용자를 카카오톡으로 이동시켜 필요한 절차를 수행한 후 복귀 시, 처리 결과를 전달받아 서비스 기능이 정상 동작할 수 있도록 앱 프로젝트의 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()
을 추가합니다.
아래 예제를 참고합니다.
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. 전자서명publicKey()
를 호출해 공개 키 가져오기를 합니다.tx_id
를 발급받습니다.tx_id
에 대한 세션 유효시간을 설정합니다. 필요한 서명자 정보를 identify_items
파라미터로 지정할 수 있습니다.tx_id
를 전달합니다.txId
로 Kakao SDK의 signWithKakaoTalk()
를 호출해 카카오톡으로 사용자 서명을 요청합니다.txId
와 서명 관련 시간 정보를 반환합니다.카카오톡 축약서명은 익명서명 방식으로, 전자서명 준비하기 호출 단계에서 이용기관이 카카오톡 인증 서비스에 사용자 정보를 전달하지 않습니다.
txId
를 전달하고 전자서명 검증을 요청합니다.txId
로 Kakao SDK의 sessionInfoByAppKey()
를 호출해 세션 정보를 받습니다.sessionInfoByAppKey()
에서 반환된 값으로 세션 정보를 업데이트합니다.reducedSign()
을 호출해 축약서명을 요청합니다.카카오톡 축약서명의 전자서명은 아래 두 종류로, 서명 원문의 구성과 검증 방식에 차이가 있습니다.
서비스의 사용자 서명 요청하기 후 사용자가 카카오톡에서 수행하는 서명입니다.
사용자 서명 완료 후 사용자의 축약서명 필요 서비스 요청 시 이용기관에서 처리하는 서명입니다.
권한 | 사전 설정 | 카카오 로그인 | 동의항목 | 레퍼런스 |
---|---|---|---|---|
필요 | 플랫폼 등록 | - | - | Android SDKpublicKey() ReactiveX Android SDK publicKey() iOS SDK publicKey() |
Kakao SDK를 사용해 구현해야 하는 기능입니다.
임시 키 쌍을 생성하고, 공개 키 값을 조회합니다. publicKey()
를 호출합니다.
publicKey()
호출 시, 기존에 생성된 임시 키 쌍이 없다면 새로 생성합니다. 기존에 생성된 임시 키 쌍이 있다면 공개 키 값을 반환합니다. null
또는 nil
이 반환된 경우, 임시 키 쌍 삭제하기 호출 후 다시 공개 키 가져오기를 요청합니다.
공개 키는 이용기관 서버로 전송해 관리해야 합니다. 이용기관 서버는 공개 키를 전자서명 요청하기의 서명 원문, 전자서명 검증하기의 서명 값 검증에 사용합니다.
요청 실패 시 문제 해결을 참고합니다.
// 공개 키 가져오기
val publicKey = CertApiClient.instance.publicKey(CertType.K3220)
// 가져온 공개 키를 이용기관 서버로 전송
...
// 공개 키 가져오기
val publicKey = CertApiClient.rx.publicKey(CertType.K3220)
// 가져온 공개 키를 이용기관 서버로 전송
...
// 공개 키 가져오기
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 |
이름 | 타입 | 설명 | 필수 |
---|---|---|---|
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 |
이름 | 타입 | 설명 | 필수 |
---|---|---|---|
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
를 이용기관 서버에 전달한 후 전자서명 검증하기를 요청해야 합니다.
요청 실패 시 문제 해결을 참고합니다.
// 사용자 서명 요청하기
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)
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: 전자서명 검증 결과 참고 |
이름 | 타입 | 설명 |
---|---|---|
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}"
}
아래는 실제 서비스에서 고려해야 할 여러 요소(성능, 예외 처리, 동시성 등)가 반영되지 않은 참고용 예제입니다.
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 SDKsessionInfoByAppKey() 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에 저장되어 있는 세션 정보를 조회하는 용도로 사용할 수 있습니다.
요청 실패 시 문제 해결을 참고합니다.
// 세션 정보 가져오기
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)
...
// 세션 정보 가져오기
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 SDKreducedSign() ReactiveX Android SDK reducedSign() iOS SDK reducedSign() ReactiveX iOS SDK reducedSign() |
Kakao SDK를 사용해 구현해야 하는 기능입니다.
세션이 유효한지 확인 후, 세션이 유효하다면 서명 원문에 개인 키로 축약서명합니다. reducedSign()
을 호출합니다. 호출 시 서명 원문에 포함할 데이터를 전달해야 합니다. 축약서명의 서명 원문은 축약서명 요청 시 전달된 데이터, 공개 키, 세션 유효시간으로 구성됩니다.
축약서명 성공 시, 이용기관 서버에 서명 값을 검증하도록 요청해야 합니다. 이용기관 서버에서 공개 키로 서명 값을 검증할 수 있습니다.
축약서명 시 세션이 유효하지 않은 경우, Kakao SDK에 저장된 임시 키 쌍과 세션 정보가 삭제됩니다.
요청 실패 시 문제 해결을 참고합니다.
// 축약서명하기
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)
// 축약서명하기
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 SDKisValidSession() ReactiveX Android SDK isValidSession() iOS SDK isValidSession() |
Kakao SDK를 사용해 구현해야 하는 기능입니다.
세션이 유효한지 확인합니다. isValidSession()
를 호출합니다.
축약서명하기 요청 시에도 Kakao SDK에 저장된 세션이 유효한지 확인 후 축약서명이 진행되지만, 이용기관의 필요에 따라 별도로 세션 유효성을 확인할 수 있습니다.
세션 유효성 확인하기 요청 시, 서버로부터 세션 정보를 응답받지 못헸거나 세션이 유효하지 않으면 false
가 반환되고 Kakao SDK에 저장된 임시 키 쌍과 세션 정보가 삭제됩니다. 이 경우, 임시 키 쌍 삭제하기 후 다시 처음부터 전자서명 과정을 진행합니다.
요청 실패 시 문제 해결을 참고합니다.
// 세션 유효성 확인하기
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, "세션 정보가 없거나 만료됨")
}
// 세션 유효성 확인하기
if (CertApi.shared.isValidSession(certType: .K3220)) {
print("세션 유효함")
} else {
print("세션 정보가 없거나 만료됨")
}
권한 | 사전 설정 | 카카오 로그인 | 동의항목 | 레퍼런스 |
---|---|---|---|---|
필요 | 플랫폼 등록 | 필요 | - | Android SDKdeleteKeyPair() ReactiveX Android SDK deleteKeyPair() iOS SDK deleteKeyPair() |
Kakao SDK를 사용해 구현해야 하는 기능입니다.
Kakao SDK에 저장된 임시 키 쌍과 세션 정보를 삭제합니다.
카카오톡 축약서명 과정 중 Kakao SDK에서 에러가 발생한 경우, 임시 키 쌍과 txId
를 삭제하고 공개 키 가져오기부터 카카오톡 축약서명 과정을 다시 시작해야 합니다.
요청 실패 시 문제 해결을 참고합니다.
// 임시 키 쌍 삭제하기
CertApiClient.instance.deleteKeyPair(CertType.K3220)
// 임시 키 쌍 삭제하기
CertApiClient.rx.deleteKeyPair(CertType.K3220)
// 임시 키 쌍 삭제하기
CertApi.shared.deleteKeyPair(certType: .K3220)
축약서명 과정의 각 단계를 Kakao SDK의 샘플 앱에서 직접 실행해 볼 수 있습니다.
아래는 샘플 앱의 단계별 구현 예제로, 실제 이용기관 서비스 개발 시 참고합니다.
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)
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
}
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)
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)
// 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)
// 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)