Development Tip

NCP(Naver Cloud Platform)으로 ECDSA 방식의 전자서명하는 C# 예제

MoonLight314 2025. 1. 24. 16:41
728x90

안녕하세요, MoonLight입니다.

지난 번 Post에서는 NCP(Naver Cloud Platform)의 소개와 API를 사용하기 위한 API 인증키와 Secret Key를 발급받는 방법에 대해서 알아보았습니다.

 

https://moonlight314.tistory.com/entry/Naver-Cloud-Platform%EC%9D%98-API-%EC%9D%B8%EC%A6%9D%ED%82%A4-%EB%B0%9C%ED%96%89%EB%B0%A9%EB%B2%95-1

 

Naver Cloud Platform의 API 인증키 발행방법

안녕하세요, MoonLight입니다.​이번 Post에서는 Naver Cloud Platform에서 제공하는 API를 사용하기 위한 API 인증키 발행방법에 대해서 알아보고자 합니다.​​   0. 네이버 클라우드 서비스? ​네이버

moonlight314.tistory.com

 

이번 Post에서는 실제로 API를 사용하기 위한 방법을 코드로 알아보도록 하겠습니다.

API 소개 문서에서도 예제가 나와있기는 하지만, Java 예제만 나와있고, 다른 언어로된 코드는 찾을 수 없습니다.

그래서 C언어 계열 중에 가장 최신 언어인 C#으로 된 예제를 하나 소개해 드리고자 합니다.

예제로 다루어볼 API는 Digital Sign 기법중에 하나인, ECDSA 방식으로 Sign / Verify를 하는 API를 사용하는 Code를 보여드리도록 하겠습니다.

1. NCP API 개요

모든 NCP(Naver Cloud Platform) API를 사용하기 위해서 공통적으로 작성해야 하는 Signature / Request Header의 생성 방법에 대한 내용과 응답 Format에 대한 내용은 아래의 Link에서 자세하게 설명하고 있습니다.

 

 

Ncloud API

 

api-fin.ncloud-docs.com

 

그런데, 설명은 아주 자세하게 잘 나와 있지만 막상 실제로 API를 사용해 보려고 하면 막막하다는 것이죠.

이제부터 하나씩 천천히 알아보도록 하겠습니다.

2. Sign

 

2.0. Introduction

 

우선 NCP의 Key Management Service의 전체적인 개요는 아래를 참고하시면 됩니다.

 

https://api.ncloud-docs.com/docs/security-kms#%EC%9A%94%EC%B2%AD%ED%97%A4%EB%8D%941

 

Key Management Service 개요

 

api.ncloud-docs.com

 

 

중요한 내용들을 먼저 한 번 살펴보도록 하겠습니다.

Key Management Service API는 2.0을 사용하시면 됩니다.

그에 맞춰서, API Gateway request signature도 v2 방식으로 요청해야 합니다.

Access Key와 Secret Key를 발급받는 방법은 아래 글을 참고하시면 됩니다.

 

https://moonlight314.tistory.com/entry/Naver-Cloud-Platform%EC%9D%98-API-%EC%9D%B8%EC%A6%9D%ED%82%A4-%EB%B0%9C%ED%96%89%EB%B0%A9%EB%B2%95-1

 

 

Naver Cloud Platform의 API 인증키 발행방법

안녕하세요, MoonLight입니다.​이번 Post에서는 Naver Cloud Platform에서 제공하는 API를 사용하기 위한 API 인증키 발행방법에 대해서 알아보고자 합니다.​​   0. 네이버 클라우드 서비스? ​네이버

moonlight314.tistory.com

 

 

NCP(Naver Cloud Platform) ECDSA Sign API의 구체적인 Spec.인 아래 Page를 참고하시면 됩니다.

 

https://api.ncloud-docs.com/docs/security-kms2-token-sign

 

Sign

 

api.ncloud-docs.com

 

 

 

2.1. Hash Value 구하기

NCP(Naver Cloud Platform) ECDSA Sign API는 최대 8KB(8192 Byte)까지 가능합니다.

그래서, Sign값을 구하려는 Data를 우선 Hash를 돌려서 Hash값의 Sign을 구하는 방법을 사용합니다.

특정 File에 ECDSA Sign값을 구하기 위해서는 우선 아래 Code와 같이 File을 Open해서 해당 File 전체에 대한 Hash값을 구합니다.

 

string Signature;
byte[] Sign_Data = { };

/*  */
string OrgBinFilePath = Path.Combine(Directory.GetCurrentDirectory(), FileName);

/* SHA256 */
try
{
    Console.WriteLine("Compute Hash Value.\n");

    using (SHA256 sha256Hash = SHA256.Create())
    using (FileStream fileStream = File.OpenRead(OrgBinFilePath))
    {
        Sign_Data = sha256Hash.ComputeHash(fileStream);
    }
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
    return;
}
finally
{
    Console.WriteLine("Compute Hash Value Completed.\n");
}

 

 

'FileName'은 ECDSA Sign하고자 하는 Filename입니다.

 

2.2. Base64 Encoding

다음으로, Sign할 Data, 즉, 앞에서 구한 Hash Value를 아래와 같이 Base64 Encoding 합니다.

 

string data = Convert.ToBase64String(Sign_Data);
 
 
 

2.3. Request Header 작성

Request Header에는 3가지 값을 넣어주면 됩니다.

1) x-ncp-apigw-timestamp

1970년 1월 1일 00:00:00 협정 세계시(UTC)부터의 경과 시간(밀리초)을 넣어주면 되는데, 이 값을 구하는 C# Code는 아래와 같습니다.

string timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();

2) x-ncp-iam-access-key

Access Key 값을 넣어주면 되는데, 앞서 설명했듯이 Access Key와 Secret Key를 발급받는 방법에서 설명드렸습니다.

그 값을 string 형식으로 넣어주면 됩니다.

 

 

3) x-ncp-apigw-signature-v2

Signature값입니다. 이 값은 네이버 클라우드 플랫폼에서 발급받은 Access Key에 맵핑되는 Secret Key 및 HMAC 암호화 알고리즘

(HmacSHA256)으로 요청 정보를 암호화한 후 Base64로 인코딩한 값이어야 합니다.

뭔가 복잡한 것 같은데, 차근차근 알아봅시다.

 

/* 추후에 사용할 요청 방식입니다.
POST 방식 고정입니다.
*/
string method = "POST";

/* URL에 해당하는 값은 아래 형식으로 값을 채워주면 됩니다. */
url = $"/kms/v1/keys/{keyTag}/sign";

 

 

 

keyTag는 계정 고유 값으로, 확인하는 방법은 아래 KMS Console에서 확인 가능합니다.

 

 

https://console.ncloud.com/security-kms/management

 

Console화면에 아래 위치에 있는 값이 keyTag입니다.

 

다음으로 지금까지 구한 값들을 아래 형식으로 하나의 String으로 만듭니다.

 

string toSign = $"{method} {url}\n{timestamp}\n{accessKey}";

 

 

method는 'POST', timestamp는 x-ncp-apigw-timestamp값을 사용하고, url값은 방금 만든 url 변수값을 사용하면 됩니다.

accessKey는 Access Key값을 적용합니다.

 

마지막으로 앞서 만든 변수(toSign)를 Secret Key값으로 HMACSHA256 돌립니다.

 

using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)))
{
    byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(toSign));
    return Convert.ToBase64String(hash);
}

 

 

이렇게 해서 나온 결과가 바로 Request Header의 x-ncp-apigw-signature-v2 값이 됩니다.

2.4. POST

이제 Request Header 작성은 마쳤고, 이제 POST를 이용해서 NCP에 보내야 합니다. 하나씩 살펴보겠습니다.

1) POST를 사용하기 위해서 아래와 같이 C#의 HttpClient() Class를 사용합니다.

 

using (HttpClient client = new HttpClient())

 

 

2) client 객체에 아래와 같이 이전까지 작성한 값들을 설정합니다.

 

client.DefaultRequestHeaders.Add("x-ncp-apigw-timestamp", timestamp);
client.DefaultRequestHeaders.Add("x-ncp-iam-access-key", accessKey);
client.DefaultRequestHeaders.Add("x-ncp-apigw-signature-v2", signature);

 

 

signature값은 2.3.의 3)에서 HMACSHA256()으로 값을 출력한 그 값이 됩니다.

 

 

3) 아래와 같이 POST를 보낼 URL 작성합니다.

 

string url = $"{baseUrl}/{keyTag}/sign";

 

이때, baseUrl은 상수로써 'https://ocapi.ncloud.com/kms/v1/keys'라는 값을 사용하고,

keyTag값은 KMS Console에서 확인할 수 있는 아래 값을 사용합니다.

4) 요청 본문(Request Body)

실제로 Sign을 할 Data를 아래와 같이 작성하고, JsonSerializer Class를 이용해 Serialize합니다.

 

var requestBody = new
{
    data = data
};

string jsonBody = JsonSerializer.Serialize(requestBody);

 

 

JSON 형태의 Data를 Server에 전송하기 위해서 StringContent() Class를 사용해서 최종적으로 POST에 사용할 Data를 만듭니다.

 

5) POST

이제 준비를 다 마쳤으니, Server에 보내도록 합니다. POST를 이용해서 보낼때는 아래와 같이, HttpClient() Class의 PostAsync() Method를 사용합니다.

 

HttpResponseMessage response = client.PostAsync(url, content).Result;

 

.Result를 사용하면 결과까지 같이 받아옵니다.

 

 

6) 응답 확인

POST 할때 사용한 HttpResponseMessage Class의 response 객체로 성공여부를 확인할 수 있습니다.

 

if (response.IsSuccessStatusCode)
{
    /* 응답성공 */
    responseBody = response.Content.ReadAsStringAsync().Result;
}
else
{
      /* 실패 */
}

 

 

7) Sign 값 확인

ReadAsStringAsync().Result 값을 분석해서 Sign 값을 알아낼 수 있습니다.

Sign의 응답은 아래와 같은 JSON 형태로 옵니다. 여기에서 실제 Sign값은 {SIGNATURE} 입니다. 아래 Nested JSON 구조를

참고해서 {SIGNATURE}값을 뜯어내면 됩니다.

 

{
    "code": "SUCCESS",
    "data": {
        "signature": "{SIGNATURE}"
    }
}

 

이를 구현한 C# Code는 아래와 같습니다.

 

 string Signature="";
  byte[] decodedBytes = { };

  var jsonData = JsonSerializer.Deserialize<Dictionary<string, object>>(responseBody);           

  var nestedData = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonData["data"].ToString());

  Signature = nestedData["signature"];

 


 

3. Verify

ECDSA Sign한 Data를 받은 사람은 Digital Sign 목적에 맞게 받은 값이 변경이 없었는지 확인(Verify)하는 과정이 반드시 필요합니다.

Verify하는 과정은 Sign 과정과 매우 유사합니다.

 

3.1. Hash Value 구하기

Digital Sign을 해서 보낸 상대는 반드시 원본 Data와 그 Data에 해당하는 Sign을 같이 보냈을 것입니다.

Verify 하는 과정은 Sign이 해당 Data의 Sign이 맞는지 확인하는 것입니다.

우선, Data의 Hash Value부터 구합니다. 왜냐하면 Sign값은 해당 Data의 Hash Value값에 Sign한 값이기 때문입니다.

Hash Value 구하는 것은 Sign 동작과 동일합니다. File을 읽어서 그 파일 전체에 대해서 Hash Value를 구합니다.

 

using (FileStream sourceStream = File.OpenRead(SignedBinFilePath))
{
  sourceData = new byte[FileSize];
  sourceStream.Read(sourceData, 0, sourceData.Length);

  using (SHA256 sha256Hash = SHA256.Create())
  {
      Sign_Data = sha256Hash.ComputeHash(sourceData);
  }
}
 
 
 

3.2. Base64로 Encoding

다음으로, Verify할 Data, 즉, 앞에서 구한 Hash Value를 아래와 같이 Base64 Encoding 합니다.

 

string data = Convert.ToBase64String(Sign_Data);
 
 
 

3.3. Request Header 작성

Request Header에 들어가는 값들도 Sign때와 거의 동일합니다.

 

 

1) x-ncp-apigw-timestamp

timestamp값은 Sign때와 동일합니다. 아래의 Code로 작성할 수 있습니다.

string timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();

 

 

2) x-ncp-iam-access-key

Access Key 값도 Sign때와 동일한 값을 넣어주면 됩니다.

 

 

3) x-ncp-apigw-signature-v2

signature값도 Sign때와 동일한데, 다른 점이 딱 하나 있습니다.

아래와 같이 URL 마지막에 verify를 넣어줘야 합니다.

 

url = $"/kms/v1/keys/{keyTag}/verify"

 

나머지 Sign 과정은 모두 동일합니다.

 

3.4. POST

이제 모든 준비를 마쳤고, 이제 POST를 이용해서 Server에 보내야 합니다.

 

이 과정은 Sign과 모두 동일하며, 다만 한가지 차이점은 URL 값에 'sign' 대신 'verify'를 넣어준다는 것입니다.

 

string url = $"{baseUrl}/{keyTag}/verify";

 

다만, 결과를 확인하는 과정이 살짝 다른데요, verify의 응답 JSON 형식은 아래와 같습니다.

 

{
  "code": "SUCCESS",
  "data": {
      "valid": true
  }
}

 

 

우리는 이 값중에 "valid" 결과를 참조하면 됩니다. 아래 Code를 참고해 주세요.

 

var jsonData = JsonSerializer.Deserialize<Dictionary<string, object>>(Res);
var nestedData = JsonSerializer.Deserialize<Dictionary<string, bool>>(jsonData["data"].ToString());

Result = nestedData["valid"];

 

이번 Post에서는 NCP(Naver Cloud Platform) API를 사용하는 방법을 Java가 아닌 C# Code로 만들어본 예제를 소개해 드렸습니다.

저처럼 Java를 안 쓰고, C/C# 등을 사용하는 분들에게 도움이 되었으면 좋겠습니다.

그럼, 다음에 또 유익한 내용으로 찾아오겠습니다.

 

728x90