본문 바로가기

NICEAPI 본인확인 PHP 샘플

by 식 2022. 5. 12.

본인확인 방식이 새로 바뀌었다고 한다

 

기존 CPClient파일로 하는게 아니라 개발자가 직접 암호화, 복호화를 해야함

 

다 좋은데 인간적으로 한두명 쓰는 서비스도 아니고 동네 구멍가게도 아닌데

 

php, node 까지는 샘플 코드는 넣어줘야 하는게 맞지 않나 싶다

 

웹사이트의 개발가이드랑 거기서 받은 문서랑도 다른게 있어서 개뼉다구같은걸로 한나절 이상 잡아먹어서 코드를 공유한다

 

인증이나 결제 모듈 연동시 주의점

1. 세션 사용 여부 체크 및 유지
크롬 80이후로 다른 사이트 갔다 넘어오면 세션값을 상실한다
session_start -> session_start_samesite(없으면 생성)

2. 리턴 URL 설정시 http, www 여부 확인, 상대 경로가 아닌 절대경로로
이런걸로 삽질하면 답도 안나온다

3. 인코딩, 디코딩 주의하기
응답정보 불러올때 UTF-8인코딩 때문에 4시간 잡아먹음 ㅓㅁㄴ야럼ㅇㄴ

 

 

GitHub - silversik/niceid-self-auth-php-sample: 나이스아이디 본인 확인(통합형)

나이스아이디 본인 확인(통합형). Contribute to silversik/niceid-self-auth-php-sample development by creating an account on GitHub.

github.com

https://github.com/silversik/niceid-self-auth-php-sample.git

 

// 기관 토큰 발급(최초 1회만 호출)
function getClientToken($clientId, $clientSecret)
{
    $url = "https://svc.niceapi.co.kr:22001/digital/niceid/oauth/oauth/token";

    $authorization = "Basic " . base64_encode($clientId . ':' . $clientSecret);

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, array(
        "Content-Type: application/x-www-form-urlencoded",
        "Authorization: $authorization"
    ));

    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, "grant_type=client_credentials&scope&default");

    $response = curl_exec($curl);
    curl_close($curl);

    $data = json_decode($response, true);

    if (!$data['dataBody']) {
        return null;
    }

    return $data['dataBody']['access_token'];
}

// 암호화 토큰 발급
function getCryptoTokenData($clientId, $productId, $accessToken, $dtim, $reqNo)
{
    $url = "https://svc.niceapi.co.kr:22001/digital/niceid/api/v1.0/common/crypto/token";

    $datetime = new DateTime();
    $current_timestamp = $datetime->getTimestamp();
    $authorization = "bearer " . base64_encode($accessToken . ":" . $current_timestamp . ":" . $clientId);

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, array(
        "Content-Type: application/json",
        "Authorization: $authorization",
        "productID: $productId",
    ));

    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode(array(
        "dataHeader" => array("CNTY_CD" => "ko"),
        "dataBody" => array(
            'req_dtim' => $dtim,
            'req_no' => $reqNo,
            'enc_mode' => '1'
        ))));

    $response = curl_exec($curl);
    curl_close($curl);

    $data = json_decode($response, true);

    if (!$data['dataBody']) {
        return null;
    }

    return array(
        'token' => $data['dataBody']['token_val'],
        'siteCode' => $data['dataBody']['site_code'],
        'tokenVersionId' => $data['dataBody']['token_version_id']
    );
}

// 대칭키 생성
function generateSymmetricKey($dtim, $reqNo, $cryptoToken)
{
    $hashed_value = hash("sha256", $dtim . $reqNo . $cryptoToken, true);
    return base64_encode($hashed_value);
}

// 요청 정보 가져오기
function getRequestData()
{
    global $CLIENT_ID, $CLIENT_SECRET, $CLIENT_TOKEN, $PRODUCT_ID, $RETURN_URL;

    // 변수 선언
    $dtim = date('YmdHis');

    // Todo: 리퀘스트 번호 중복 방지해야 함
    $reqNo = 'REQ' . $dtim . rand(1000, 9999);
    
    // 기관 토큰 발급
    $clientToken = $CLIENT_TOKEN || getClientToken($CLIENT_ID, $CLIENT_SECRET);

    // 암호화 토큰 발급
    $cryptoTokenData = getCryptoTokenData($CLIENT_ID, $PRODUCT_ID, $clientToken, $dtim, $reqNo);
    $cryptoToken = $cryptoTokenData['token'];
    $siteCode = $cryptoTokenData['siteCode'];
    $tokenVersionId = $cryptoTokenData['tokenVersionId'];

    // 대칭키 생성
    $symmetricKey = generateSymmetricKey($dtim, $reqNo, $cryptoToken);

    $key = substr($symmetricKey, 0, 16);
    $iv = substr($symmetricKey, strlen($symmetricKey) - 16, 16);
    $hmacKey = substr($symmetricKey, 0, 32);

    $reqData = json_encode(array(
        'requestno' => $reqNo,
        'returnurl' => $RETURN_URL,
        'sitecode' => $siteCode,
        'methodtype' => 'get',
        'authtype' => 'M',
        'popupyn' => 'Y',
    ));

    $output = openssl_encrypt($reqData, "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv);
    $encData = base64_encode($output);

    $integrityValue = base64_encode(hash_hmac("sha256", $encData, $hmacKey, true));

    return array(
        'tokenVersionId' => $tokenVersionId,
        'encData' => $encData,
        'integrityValue' => $integrityValue,
        'symmetricKey' => $symmetricKey
    );
}

// 응답 정보 가져오기
function getResponsData($encData, $integrityValue, $symmetricKey)
{
    $key = substr($symmetricKey, 0, 16);
    $iv = substr($symmetricKey, strlen($symmetricKey) - 16, 16);
    $hmacKey = substr($symmetricKey, 0, 32);
    $resData = openssl_decrypt(base64_decode($encData), 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $iv);

    if ($integrityValue != base64_encode(hash_hmac("sha256", $encData, $hmacKey, true))) {
        return null;
    }

    // UTF8 인코딩
    $resData = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $resData);
    return json_decode($resData, true);
}

반응형