Featured image of post [js] Nice api 실명인증 API

[js] Nice api 실명인증 API

회사에서 Nice 본인인증 서비스를 통해 실명, 계좌 인증 기능을 추가하는 일이 있었습니다. 저희 회사에서는 Node 백엔드를 사용하는데 Nice 에서 제공한 예제에 자바스크립트 코드가 없고, 의외로 NPM에 모듈도 없어서 API 호출, 암호화 과정에서 삽질이 있었습니다.

미래의 저를 위한 기록으로 남깁니다.

기본 요소

Bearer Authorization

1
2
3
4
5
const generateAuthorization = (time) => {
  const currentTime = Math.round(time.getTime() / 1000);
  const auth = Buffer.from(`${process.env.NICE_API_ACCESS_TOKEN}:${currentTime}:${process.env.NICE_API_CLIENT_ID}`).toString('base64');
  return `bearer ${auth}`)}`;
};

계좌 인증 API

계좌 인증의 경우 암호화 과정 없이 진행 되기 때문에 스무스하게 진행 됐어야 했는데, 문서를 잘못 읽어서 삽질이 있었습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const response = await axios({
  url: "https://svc.niceapi.co.kr:22001/digital/niceid/api/v1.0/account/owner",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: generateAuthorization(currentTime),
    client_id: process.env.NICE_API_CLIENT_ID,
    productID: process.env.NICE_API_PRODUCT_CODE_ACCOUNT_CHECK,
  },
  data: {
    // Body 단에 따로 header, body 가 있음.
    // 아무 생각 없이 dataHeader 값을 헤더에 넣고 
    // dataBody를 body에 그냥 넣는 실수를 했습니다.
    dataHeader: {
      CNTY_CD: "ko",
    },
    dataBody: {
      acct_gb: accountData.account_gb,
      birthday: accountData.birthday,
      bnk_cd: accountData.bank_code,
      acct_no: accountData.account_number,
      name: accountData.name,
      request_no: randomString,
    },
  },
});

암호화 토큰 요청 API

요청

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const response = await axios({
  url: "https://svc.niceapi.co.kr:22001/digital/niceid/api/v1.0/common/crypto/token",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: generateAuthorization(date),
    client_id: process.env.NICE_API_CLIENT_ID,
    ProductID: "암호화 토큰을 사용할 API의 상품 ID",
  },
  data: {
    // 두번은 실수 안했습니다.
    dataHeader: {
      CITY_CD: "ko",
    },
    dataBody: {
      // yyyyMMddhhmmss 형태
      // 공식 문서에 yyyyMMddhhmiss 라고 써있어서 miss가 뭔지 한참 고민 했습니다...
      req_dtim: requestDatetime,
      req_no: randomString,
      enc_mode: "1",
    },
  },
});

토큰을 통해 암호화 키 생성

1
2
3
4
5
6
7
8
9
const generateKeys = (requestDatetime, requestNumber, tokenVal) => {
  const value = `${requestDatetime}${requestNumber}${tokenVal}`;
  const hashed = crypto.createHash("sha256").update(value).digest("base64");
  return {
    key: hashed.substring(0, 16),
    iv: hashed.substring(hashed.length - 16),
    hmacKey: hashed.substring(hashed.length - 32),
  };
};

실명 인증 API

실명 인증 API는 이름, 주민번호를 이용하여 유효성을 확인하는 API 입니다.
API 호출시 이름, 주민번호의 암호화, 무결성 체크 값이 필요합니다.

암호화 함수

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const encrypt = (plaintext, key, iv, inputEncoding = "utf8", outputEncoding = "base64") => {
  let algorithm = "";
  if (key.length === 32) {
    algorithm = "aes-256-cbc";
  } else if (key.length === 16) {
    algorithm = "aes-128-cbc";
  } else {
    throw new Error("Invalid key");
  }

  const cipher = crypto.createCipheriv(algorithm, key, iv);
  let encrypted = cipher.update(plaintext, inputEncoding, outputEncoding);
  encrypted += cipher.final(outputEncoding);

  return encrypted;
};

암호화

이름 암호화 시, 우선 이름을 EUC-KR 인코딩으로 변경해야합니다.
iconv-lite 모듈을 사용하여 진행했습니다.

1
2
3
const encryptedPersonalId = encrypt(personalId, key, iv).toString("base64");
const encodedName = iconv.encode(name, "euc-kr");
const encryptedName = encrypt(encodedName, key, iv).toString("base64");

무결성 체크 값 생성

1
2
const integrityValue = `${tokenVersionId.trim()}${encryptedPersonalId.trim()}${encryptedName.trim()}`;
const integrity = crypto.createHmac("sha256", key).update(integrityValue).digest("base64");

실명 인증 API 호출

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const response = await axios({
  url: "https://svc.niceapi.co.kr:22001/digital/niceid/api/v1.0/name/national/check",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: generateAuthorization(currentTime),
    ProductID: process.env.NICE_API_PRODUCT_CODE_NAME_CHECK,
  },
  data: {
    dataHeader: {
      CNTY_CD: "ko",
      TRAN_ID: tranId,
    },
    dataBody: {
      token_version_id: tokenVersionId,
      enc_jumin_id: encryptedPersonalId,
      enc_name: encryptedName,
      integrity_value: integrity,
    },
  },
});
Hugo로 만듦
JimmyStack 테마 사용 중