코나카드 서비스의 외부 연동 규격을 정의 합니다.
본 문서는 HTTPS 기반의 RESTful API를 JSON 포맷으로 사용하는 통신 방식에 대해 설명합니다.
| 항목 | 테스트계 | 운영계 |
|---|---|---|
| Protocol | http | https |
| HOST Address | http://118.33.122.28 | https://openapi.konaplate.com |
| Port | 15890 | 443 |
| Context Path | /open-api | /open-api |
| Encoding | UTF-8 | UTF-8 |
API 문서를 확인하여, 사용되는 엔드포인트 URL의 예시는 아래와 같습니다.
REST API 요청 시 포함되어야 하는 정보 입니다.
| Header name | Type | Length | MOC | Description |
|---|---|---|---|---|
| X-KM-User-AspId | STRING | 15 | MANDATORY | ASP ID • 제휴사에게 부여한 ID • ID는 계약 시 제공. |
| X-KM-Correlation-Id | STRING | 20 | MANDATORY | 전문 추적을 위한 거래번호 (Format : 'yyMMddHHmiSS-xxxxxxx') ※ xxxxxxx : 7자리 Hex String |
| X-KM-Access-Key | STRING | 72 | CONDITIONAL | 제휴사에 제공 하는 Access Key • Callback API인 경우는 제외. |
| X-KM-Crypto-Key-Id | STRING | 32 | OPTIONAL | 제휴사에 제공 하는 데이터 암복호화 키 ID (Crypto Key) • 데이터 암호화 요청 시 필수 입력값. (Data Encryption = Y) |
| X-KM-Tran-Token | STRING | 67 | MANDATORY | 데이터의 무결성을 확인하기 위한 데이터 KMV1:yyyyMMddhh24misssss:hmax Tran-Token : "KMV1:" + yyyyMMddhh24misssss + ":" + SHA256HMAC(secret key, request body String.) |
| X-KM-Tran-Time | STRING | 16 | MANDATORY | 클라이언트 요청 시간 • 형식 : YYYYMMDDHH24MMSS |
| X-KM-Time-Zone | STRING | 3 | MANDATORY | 시간대 정보 (UTC, KST, ...) • KST 입력 |
| Header name | Type | Length | MOC | Description |
|---|---|---|---|---|
| X-KM-Correlation-Id | STRING | 20 | MANDATORY | 전문 추적을 위한 거래번호 (Format : 'yyMMddHHmiSS-xxxxxxx') ※ xxxxxxx : 7자리 Hex String |
| X-KM-Crypto-Key-Id | STRING | 32 | OPTIONAL | 제휴사에 제공 하는 데이터 암복호화 키 ID (Crypto Key) Callback 으로 전달될 경우 서비스 사업자에게 발급된 키 중 가장 먼저 발급된 키를 사용. |
제공되는 API는 개인정보 및 민감 데이터를 포함하고 있기 때문에 TLS(Transport Layer Security)의 보안과 다양한 민감 정보를 외부로 누출해 재사용하지 못하도록 비대칭 암호화 방식을 통한 데이터 보안 서비스를 제공하고 있습니다.
ME(Message Encryption)는 아래 데이터를 포함할 때 제공하고 있습니다.
ME는 비대칭 암호화 기술(공개 키 암호화)을 사용하여 Message Payload에 대한 향상된 보안을 제공합니다.
ME는 128비트 또는 256비트의 AES(Advacnced Encrytpion Stardard), GCM(Glois Counter Mode)을 사용하는 대칭 암호화를 통해
개발되었습니다.
키 암호화는 2,048비트의 RSA-OAEP(Rivest ShamirAdleman-Optimal Asymmetric Encryptio Padding)로 지원하고 있습니다.
ME는 JWE(JSON Web Encryption)를 사용하여 SSL(Secure Sockets Layer)을 통해 Request, Response의 암/복호화를 지원하며 API의 Request/Response 암호화 여부를 확인하여 요청되어야 합니다.
API의 Request/Response 암호화 여부는 API Document에서 확인할 수 있습니다.
/*
* The Nimbus JOSE+JWT library is Used for encrypt and decrypt requests and responses.
* It requires Java 7+ and has minimal dependencies.
* Imported using maven or gradle as follows
*/
Maven:
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.10</version>
</dependency>
Gradle:
implementation 'com.nimbusds:nimbus-jose-jwt:9.10'
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSAEncrypter;
import com.nimbusds.jose.util.Base64;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
public class CryptoTest {
String requestBody = "{ \"userId\": \"50000123456\" }";
String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkw .. (중략) .. fdgfdfdm98DfM32Fgf9VyuwguM";
String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBC .. (중략) .. uV+EqHocNSPRHM44pgwIDAQAB";
@Test
public void rsaTest() throws JOSEException, NoSuchAlgorithmException, InvalidKeySpecException, ParseException {
String cipherText = rsaEncrypt(requestBody, getRsaPublicKey(publicKey));
String plainText = rsaDecrypt(cipherText, getRsaPrivateKey(privateKey));
assertThat(plainText).isEqualTo(requestBody);
}
public// Encrypt : make cipher text by using RSA Public Key
public String rsaEncrypt(String plainText, RSAPublicKey key) throws JOSEException {
JWEHeader jweHeader = new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128GCM).build();
Payload payload = new Payload(plainText);
JWEObject jweObject = new JWEObject(jweHeader, payload);
RSAEncrypter rsaEncrypter = new RSAEncrypter(key);
jweObject.encrypt(rsaEncrypter);
return jweObject.serialize();
}
// Encrypt : make RSA Public Key
public RSAPublicKey getRsaPublicKey(String encodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
Base64 base64 = new Base64(encodedKey);
byte[] decodedKey = base64.decode();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
// Decrypt : make plain text by using RSA Private Key
public String rsaDecrypt(String cipherText, RSAPrivateKey key) throws ParseException, JOSEException {
JWEObject jweObject = JWEObject.parse(cipherText);
RSADecrypter rsaDecrypter = new RSADecrypter(key);
jweObject.decrypt(rsaDecrypter);
Payload payload = jweObject.getPayload();
return payload.toString();
}
// Decrypt : make RSA Private Key
public RSAPrivateKey getRsaPrivateKey(String encodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
Base64 base64 = new Base64(encodedKey);
byte[] decodedKey = base64.decode();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
}
Open API는 X-KM-Tran-Token의 값으로 데이터 무결성을 확인합니다.
API 요청 시 X-KM-Tran-Token 값을 생성 후 요청 Header에 X-KM-Tran-Token으로 키와 값을 세팅하고 요청합니다.
서버는 요청된 Message의 내용을 Secret-Key로 X-KM-Tran-Token을 생성하여 요청된 X-KM-Tran-Token과 비교하여 데이터 무결성을 검증합니다.
X-KM-Tran-Token 요청 방법
X-KM-Tran-Token 생성
// https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient/4.0
implementation 'org.apache.httpcomponents:httpclient'
// tranToken make value
String tranToken = "KMV1" + ':' + (new SimpleDateFormat("yyyyMMddHHmmssSSS")).format(new Date()) + ':' + hMac(secretKey, message);
// hMac Method
import org.apache.commons.codec.digest.HmacAlgorithms
import org.apache.commons.codec.digest.HmacUtils
import org.apache.commons.codec.binary.*
public String hMac(String secretKey, String message) {
Charset charset = Charsets.UTF_8;
HmacAlgorithms algorithm = HmacAlgorithms.HMAC_SHA_256;
byte[] secretKeyByte = secretKey.getBytes(charset);
byte[] bodyByte = body.getBytes(charset);
byte[] mac = HmacUtils.getInitializedMac(algorithm, secretKeyByte).doFinal(bodyByte);
byte[] encodeBase64 = Base64.encodeBase64(mac);
return new String(encodeBase64, charset);
}
| HTTP Status | Reason (Error code) | Message | Description |
|---|---|---|---|
| 200 | 000_000 | Success | 성공 |
| 500 | 000_001 | Unknown reason | 정의되지 않은 내부 시스템 오류 |
| 400 | 000_002 | Invalid Input Parameter | 요청 파라미터 검증 오류 |
| 400 | 000_003 | Missing required values. | 필수값 누락되었습니다. |
| 400 | 000_004 | Method not allowed | 요청 Method 오류 |
| 400 | 000_008 | Internal service error. | 내부 서비스 오류 |
| 400 | 000_016 | Invalid Input Header | 요청 헤더 정보 검증 오류 |