반응형

사내 업무 중 NICE에서 제공해주는 본인인증 서비스를 적용해야 하는 상황이 있었다.

API 개발가이드는 JAVA 언어 기반으로만 예시코드를 제공해주는 상황으로

C# 소스 적용 시, 암호화 처리 후 잘 되었는지 JAVA 코드라 비교해야 하는 작업까지 진행하였다.

 

아래는 API 처리 흐름도와 C# 소스이다.

 

본인인증 API 처리 흐름도

 (1) 기관 인증 토큰 API 최초 1회 호출 반영구적(50년)

 (2) 1번 ‘기관 인증 토큰’으로 NICE 본인확인 호출을 위한 암호화 토큰 API 호출

 (3) 2번에서 발급받은 암호화 토큰으로 대칭키(Symmetric Key, IV)와 무결성키(hmac Key)를 생성

 (4) 요청할 데이터(request_data)를 대칭키로 암호화 하고 암호화 데이터를 체크

 → 요청 데이터 암호화는 AES128/CBC/PKCS7 패딩 후, Base64 Encoding으로 사용

 (5) 생성한 데이터를 form에 추가하여 전송

 (6) return URL 을 통해 받은 암호화를 복호화 처리

 

‘기관 인증 토큰 API 호출’ 과 ’암호화 Token 요청 API’ 

총 2번의 API를 호출하고 암호화 키를 생성하여

데이터를 암호화 처리해서 본인인증 폼 레이어를 호출하는 방식이다.

 

각 API 전송 시 필요한 데이터는 아래 사이트에서 신청한 아이디로 로그인 한 후

MyAppList > 상세보기에서 필요한 데이터 값을 조회 할 수 있다

사이트 URL : https://www.niceapi.co.kr/#/

string __niceClientID = 사이트에서 제공해주는 값;
string __niceClientSecret = 사이트에서 제공해주는 값;

#region [ 최초 50년(반영구적) Token 생성 / 토큰 만료 시 처리 ]

public string GetNiceApiAccessToken()
{
    string hostName = "https://svc.niceapi.co.kr:22001";
    string requestUrl = "/digital/niceid/oauth/oauth/token";

    string postData = "grant_type=client_credentials&scope=default";
    byte[] sendData = UTF8Encoding.UTF8.GetBytes(postData);

    string url = hostName + requestUrl;

    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
    request.ContentType = "application/x-www-form-urlencoded";
    request.Method = "POST";
    request.ContentLength = sendData.Length;

    string text = __niceClientID + ":" + __niceClientSecret;

    byte[] byteUTF8 = Encoding.UTF8.GetBytes(text);
    string base64UTF8 = Convert.ToBase64String(byteUTF8);

    request.Headers.Add("Authorization", "Basic " + base64UTF8);

    try
    {
        Stream requestStream = request.GetRequestStream();
        requestStream.Write(sendData, 0, sendData.Length);
        requestStream.Close();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        StreamReader responseStream = new StreamReader(response.GetResponseStream());
        string rawData = responseStream.ReadToEnd();
        parser = new JsonTextParser();
        JObject obj = JObject.Parse(rawData);

        JToken jToken = obj["dataBody"];
        accessToken = jToken.SelectToken("access_token").ToString();

        message = rawData;

        return "OK";
    }
    catch (System.Exception ex)
    {
        message = ex.Message.ToString();
        return ex.Message.ToString();
    }
}

#endregion
string __accessToken = 인증 기관 토큰 (위 소스에서 부여받은 값);
string __productID = 사이트에서 제공해주는 값;

#region [ 암호화 토큰 발급 ]

private string GetNiceApiEncryptToken()
{
    string hostName = "https://svc.niceapi.co.kr:22001";
    string requestUrl = "/digital/niceid/api/v1.0/common/crypto/token";
    
    string url = hostName + requestUrl;

    req_dtim = DateTime.Now.ToString("yyyyMMddHHmmss");
    req_no = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);

    double unixTimeStamp = DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
    string text = __accessToken + ":" + (int)unixTimeStamp + ":" + __niceClientID;
    byte[] byteUTF8 = Encoding.UTF8.GetBytes(text);
    string base64UTF8 = Convert.ToBase64String(byteUTF8);

    request.Headers.Add("Authorization", "bearer " + base64UTF8);
    request.Headers.Add("client_id", client_id);
    request.Headers.Add("productID", productID);
    request.ContentType = "application/json";
    request.Method = "POST";

    var streamWriter = new StreamWriter(request.GetRequestStream());

    try
    {
        string strData = "{\"dataHeader\": {\"CNTY_CD\":\"ko\"}, \"dataBody\":{\"req_dtim\":\"" + req_dtim + "\",\"req_no\":\"" + req_no + "\",\"enc_mode\":\"1\"}}";
        JObject jsonData = JObject.Parse(strData);

        streamWriter.Write(strData);
        streamWriter.Flush();
        streamWriter.Close();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        StreamReader responseStream = new StreamReader(response.GetResponseStream());
        string rawData = responseStream.ReadToEnd();

        JsonTextParser parser = new JsonTextParser();
        JObject obj = JObject.Parse(rawData);
        
        JToken jToken = obj["databody"];
        string token_val = jToken.SelectToken("token_val").ToString();
        string token_version_id = jToken.SelectToken("token_version_id").ToString();
        string siteCode = jToken.SelectToken("site_code").ToString();

        JToken jTokenHeader = obj["dataHeader"];
        resultCode = jTokenHeader.SelectToken("GW_RSLT_CD").ToString();

        // 성공시 키 발급 처리
        if (resultCode 성공시)
        {
            // 대칭키
            string strValue = req_dtim.Trim() + req_no.Trim() + token_val.Trim();

            SHA256Managed sha256 = new SHA256Managed();
            byte[] encryptBytes = sha256.ComputeHash(Encoding.ASCII.GetBytes(strValue));

            string strKeyValue = Convert.ToBase64String(encryptBytes);

            // 대칭키(Symmetric Key, IV), 무결성 키 생성
            string sKey = strKeyValue.Substring(0, 16);
            string IV = strKeyValue.Substring(strKeyValue.Length - 16);
            string encryptedHMacKey = strKeyValue.Substring(0, 32);

            string strRequestNo = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

            string strRequestData = "{\"requestno\":\"" + strRequestNo + "\",\"returnurl\":\"" + HttpContext.Current.Request.Url.AbsoluteUri + "\",\"sitecode\":\"" + siteCode + "\",\"methodtype\":\"post\",\"popupyn\":\"Y\"}";

            try
            {
                RijndaelManaged rijndaeManaged = new RijndaelManaged();
                rijndaeManaged.Mode = CipherMode.CBC;
                rijndaeManaged.Padding = PaddingMode.PKCS7;
                rijndaeManaged.Key = Encoding.ASCII.GetBytes(sKey);
                rijndaeManaged.IV = Encoding.ASCII.GetBytes(IV);

                byte[] encDataByte = EncryptStringToBytes(strRequestData, rijndaeManaged.Key, rijndaeManaged.IV);
                enc_data = Convert.ToBase64String(encDataByte);

                byte[] hmacKeyByte = Encoding.UTF8.GetBytes(encryptedHMacKey);
                HMACSHA256 sha = new HMACSHA256(hmacKeyByte);
                byte[] integrityValueByte = sha.ComputeHash(Encoding.ASCII.GetBytes(enc_data));

                integrity_value = Convert.ToBase64String(integrityValueByte);

            }
            catch (System.Exception ex)
            {
                message = ex.Message.ToString();
                return ex.Message.ToString();
            }
        }

        return "";

    }
    catch (System.Exception ex)
    {
        message = ex.Message.ToString();
        return ex.Message.ToString();
    }
}

#endregion

#region [ Byte를 EncryptString으로 변환 ]

protected byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
    // Check arguments. 
    if (plainText == null || plainText.Length <= 0)
        throw new ArgumentNullException("plainText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");
    byte[] encrypted;

    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                {

                    //Write all data to the stream.
                    swEncrypt.Write(plainText);
                }
                encrypted = msEncrypt.ToArray();
            }
        }
    }

    // Return the encrypted bytes from the memory stream. 
    return encrypted;

}

#endregion
반응형

'프로그래밍 > TIL' 카테고리의 다른 글

DevOps란 무엇인가?  (0) 2022.07.20
반응형

1. DevOps란 무엇인가?


DevOps란, 개발(Dev) 과 운영(Ops)이 결합해 탄생한 문화 또는 개발 방법론으로 빠른 시간에 사용자의 요구를 반영 할 수 있도록 소프트웨어를 만들고자 하는 목적을 갖고 있다.


2. DevOps 이전의 문제


이전 회사에서 나는 기획으로부터 업무를 부여 받고 개발을 완료하면 테스트를 거치고 배포 전담 팀에서 배포를 진행하는 방식으로 진행하였다. 이와 같은 방식은 문제가 발생했을 때 본인이 아는 범위 밖에서 문제가 발생하게 되면 운영, 개발 팀 간 서로에게 떠넘기는 상황이 발생한다는 것이다.

이를 해결하기 위해 개발과 운영을 단일팀으로 병합하고 빠르고 쉽게 사용자의 요구 사항을 처리할 수 있도록 처리하는 것이 DevOps이다.


3. DevOps 특징


몇 가지 DevOps의 특징을 정리해보면

Cross Functional Team

각 프로세스를 담당하는 사람들을 하나의 팀으로 모으라는 뜻으로 개발, 배포, 테스트 등 하나의 팀에서 처리해야 한다는 특징이다.

Widely Shared Metrics

서비스를 이용하는 사용자의 반응, 서비스의 안정성, 서비스 운영이 잘 돌아가고 있는지 등을 나타내는 하나의 공유된 지표가 필요하다는 것

Automating repetitive tasks

반복적인 수작업을 줄이고 CI/CD를 활용해 자동화된 툴을 이용하는 것을 의미한다.

Post Mortems

시스템 장애가 발생 했을때 팀원들과 소통하며 공유해야 한다.

Teamwork Over Individual Work

팀워크가 중요하며 서로를 존중하되 아이디어에 동의하지 않아야 한다.

또한, 피드백을 주고 받으며 필요 시 도움을 청할 수 있어야 한다.


참고

반응형

'프로그래밍 > TIL' 카테고리의 다른 글

[C#] NICE API 본인인증 소스 및 정리  (2) 2022.11.08

+ Recent posts