본문 바로가기
개발 업무(코딩)-개발자였을때 썼던..

C언어 HMAC SHA256 샘플코드(openssl)

by 주용사 2023. 1. 9.
728x90

참고한 사이트 를 보면 1번에선 여러가지 언어로 샘플코드들이 존재했다.

그러나 C로 짜여진 소스는 없었다.

shell로 openssl을 불러서 하는 것일 이용해 popen을 사용해서 만들었다.

동시에 popen을 했을 때 괜찮을까에 대한 문제로 소스를 짜기 시작했다.

echo -n $MESSAGE | openssl dgst -sha256 -hmac $SECRET -binary | base64

에서 -binary와 base64를 지웠을 때 데이터가 일정부분 일치한 것을 확인하고 구현할 수 있겠다는 생각이 들었다.

순서는 hmac-sha256 -> hex -> binary -> base64 이다.(ver1)

ver2는 hmac-sha256 -> base64 이다.

for구문의 행위가 bin -> hex였고 그걸 다시 hex -> bin으로 바꾸는 행위를 했던 거였다. 그래서 수정되었다.

(feat. 이사님)

 

첨부파일은 javascript으로 되어있다. 밑에 소스와 data와 key 값을 일치시켜서 데이터를 확인하면 된다.

gcc hmacsha256.c -lcrypto

#if 1 // openssl hmac sha256
#include    <openssl/hmac.h>
#include    <openssl/bio.h>
#include    <openssl/evp.h>
#include    <openssl/buffer.h>
#include    <stdint.h>
#include    <openssl/sha.h> // ver2
#endif
#include        <stdio.h>
#include        <string.h>
int             HMAC_SHA256(char* phskey, char* pdata, char* pkey);
int             HEXCHR2BIN(const char hex, char *out);
int             HEXS2BIN(const char *hex, unsigned char *out);
int             BASE64_ENCODE(char* output, const unsigned char *input, int length);

int main()
{
        char hskey[1024];
        memset(hskey, 0x00, sizeof(hskey));

        HMAC_SHA256(hskey, "data", "key");

        printf("hskey [%s]\n", hskey);

        return 0;
}
/*******************************************************************************
 * HMAC_SHA256 (made by.. Han Chang Jo)
 * phskey : 해쉬키(output) pdata : 암호화할 데이터 pkey : 암호화키
 * *****************************************************************************/
int HMAC_SHA256(char* phskey, char* pdata, char* pkey)
{
        unsigned char zresult[64];
        int nresult_len = 32;
        int ii;
        char zresult_hex[64];

        unsigned char zbin[1024];
        size_t         zbinlen;
        char ztemp[5];

        memset(zresult, 0x00, sizeof(zresult));
        memset(zresult_hex, 0x00, sizeof(zresult_hex));
        memset(zbin, 0x00, sizeof(zbin));
        memcpy(zresult, HMAC(EVP_sha256(), pkey, strlen((char *)pkey), pdata, strlen((char *)pdata), NULL, NULL), sizeof(zresult));
/* zresult가 바이너리자체로 들어와서 밑에 hex2binary는 필요없는 행위다. */
/* for구문지우고 base64인코딩으로 바로 타도 잘된다 -> ver2로 구분하겠다. */
/* ver1은 주석처리 */
/* ver1
        for (ii = 0; ii < 32; ii++) {
                if(ii == 0)
                        sprintf(zresult_hex, "%02x", zresult[ii]);
                else
                {
                        memset(ztemp, 0x00, sizeof(ztemp));
                        sprintf(ztemp, "%02x", zresult[ii]);
                        strncat(zresult_hex, ztemp, strlen(ztemp));
                }
        }


        HEXS2BIN(zresult_hex, zbin);

        BASE64_ENCODE(phskey, zbin, strlen(zbin));
*/
/* ver2 */
BASE64_ENCODE(phskey, zresult, SHA256_DIGEST_LENGTH);
/* ver2 end */
        return 0;
}
/*******************************************************************************
 * HEX -> BINARY
 * *****************************************************************************/
int HEXCHR2BIN(const char hex, char *out)
{
        if (out == NULL)
                return 1;

        if (hex >= '0' && hex <= '9') {
                *out = hex - '0';
        } else if (hex >= 'A' && hex <= 'F') {
                *out = hex - 'A' + 10;
        } else if (hex >= 'a' && hex <= 'f') {
                *out = hex - 'a' + 10;
        } else {
                return 1;
        }

        return 0;
}

int HEXS2BIN(const char *hex, unsigned char *out)
{
        size_t len;
        char   b1;
        char   b2;
        size_t i;

        if (hex == NULL || *hex == '\0' || out == NULL)
                return 1;

        len = strlen(hex);
        if (len % 2 != 0)
                return 1;
        len /= 2;

        for (i=0; i<len; i++) {
                if (HEXCHR2BIN(hex[i*2], &b1) || HEXCHR2BIN(hex[i*2+1], &b2)) {
                        return 1;
                }
                *(out+i)= (b1 << 4) | b2;
        }
        return 0;
}
/*******************************************************************************
 * (BINARY) -> BASE64 - 바이너리 아니여도 괜찮습니다.
 * *****************************************************************************/
int BASE64_ENCODE(char* output, const unsigned char *input, int length)
{
        BIO *bmem, *b64;
        BUF_MEM *bptr;

        b64 = BIO_new(BIO_f_base64());
        bmem = BIO_new(BIO_s_mem());
        b64 = BIO_push(b64, bmem);
        BIO_write(b64, input, length);
        BIO_flush(b64);
        BIO_get_mem_ptr(b64, &bptr);

        memcpy(output, bptr->data, bptr->length-1);

        BIO_free_all(b64);

        return 0;
}

참고한사이트

1. shell

https://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/#java

2. hmac sha256

https://gist.github.com/yoshiki/812d35a32bcf175f11cb952ed9d1582a

3. hex to binary

https://nachtimwald.com/2017/09/24/hex-encode-and-decode-in-c/

4. base64

https://gist.github.com/barrysteyn/7308212

728x90