2015년 1월 20일 화요일

EVP_DigestSign/Verify 예제

비대칭 알고리즘을 이용하여, 사인/검증 하는 API를 OpenSSL 1.0.0부터 쓸 수 있다. 문제는 아직 예쁜 예제가 없다는 것. 그래서 낑낑끙끙 하며 만들어 봤다.


#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <iostream>
using namespace std;

void testSignVerify(void)
{
 EVP_PKEY* pkey(/* 이미 만들어놓은 키 */);

 char src[] = "Hello, world!";

 unsigned char sign[1024] = {0x00};
 size_t signlen(sizeof(sign));

 do {
  EVP_MD_CTX md;
  EVP_MD_CTX_init(&md);
  if ( EVP_DigestSignInit(&md, nullptr, EVP_sha256(), nullptr, pkey) <= 0 )
  {
   cerr << "EVP_DigestSignInit" << endl;
   break;
  }

  if ( EVP_DigestSignUpdate(&md, src, strlen(src)) <= 0 )
  {
   cerr << "EVP_DigestSignUpdate" << endl;
   break;
  }

  if ( EVP_DigestSignFinal(&md, sign, &signlen) <= 0 )
  {
   cerr << "EVP_DigestSignFinal" << endl;
   break;
  }

  cerr << "Sign:   ";
  PWEnc::encodeHex(cerr, sign, signlen) << endl;

  EVP_MD_CTX_cleanup(&md);
 } while (false);

 do {
  EVP_MD_CTX md;
  EVP_MD_CTX_init(&md);
  if ( EVP_DigestVerifyInit(&md, nullptr, EVP_sha256(), nullptr, pkey) <= 0 )
  {
   cerr << "EVP_DigestVerifyInit" << endl;
   break;
  }

  if ( EVP_DigestVerifyUpdate(&md, src, strlen(src)) <= 0 )
  {
   cerr << "EVP_DigestVerifyUpdate" << endl;
   break;
  }

  if ( EVP_DigestVerifyFinal(&md, sign, signlen) <= 0 )
  {
   cerr << "EVP_DigestVerifyFinal" << endl;
   break;
  }

  cerr << "Verify: ";
  PWEnc::encodeHex(cerr, sign, signlen) << endl;

  EVP_MD_CTX_cleanup(&md);
 } while (false);

 cerr << endl;
}

패딩 규칙은 기본값은 PKCS#1이며, 변경하고자 할 때는 EVP_Digest(Sign|Verify)Init 함수 두번째 인자에 EVP_PKEY_CTX*의 주소를 넘겨 EVP_PKEY_CTX*를 받는다. 이것에 EVP_PKEY_CTX_set_rsa_padding 함수(실은 매크로 함수이다)로 세팅할 수 있다.


/* 생략 ... */
EVP_PKEY_CTX* pkey_ctx(nullptr);
EVP_DigestSignInit(&md, &pkey_ctx, EVP_sha256(), nullptr, pkey);
EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, 20);
/* 생략 ... */