Trusted Design

OpenSSLでECDSA署名処理

概要

OpenSSLが提供しているC言語のAPIを使って公開鍵暗号方式であるECDSAで署名を実行します。

利用するAPI

ECDSA署名を実行するためのAPI

  • EVP_SignInit_ex()
    署名処理を初期化します。
  • EVP_SignUpdate()
    署名処理用のオブジェクトに平文データを入力します。
  • EVP_SignFinal_ex()
    署名処理を実行します。
  • EVP_VerifyInit()
    署名検定処理を初期化します。
  • EVP_VerifyUpdate()
    署名検定用のオブジェクトに署名検定をしたい平文を入力します。
  • EVP_VerifyFinal()
    署名データを入力して,署名を検定します。
RSA署名処理と同じ関数を利用してECDSAの署名/署名検定処理が実現できます。アルゴリズムによらず,処理を共通化できます。

サンプルプログラム

署名処理の関数

上で書いた署名処理の関数を順番にコールしていきます。複数のデータを連結して署名したい場合には,EVP_SignUpdate()を複数回呼び出します。


int ECDSASign(const EVP_MD* md, EVP_MD_CTX* ctx, EVP_PKEY* pkey, const unsigned char* msg, int msgLen, unsigned char* sign, unsigned int* signLen)
{
	if (!EVP_SignInit_ex(ctx, md, NULL))
	{
		/* Error */
		printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		return 0;
	}

	if (!EVP_SignUpdate(ctx, msg, msgLen))
	{
		/* Error */
		printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		return 0;
	}

	if (!EVP_SignFinal(ctx, sign, signLen, pkey))
	{
		/* Error */
		printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		return 0;
	}
	return 1;
}

署名検定処理の関数

署名検定も呼び出し方は署名処理と同じです。


int ECDSAVerify(const EVP_MD* md, EVP_MD_CTX* ctx, EVP_PKEY* pkey, const unsigned char* msg, int msgLen, unsigned char* sign, unsigned int sigLen)
{
	if (!EVP_VerifyInit(ctx, md))
	{
		/* Error */
		printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		return 0;
	}

	if(!EVP_VerifyUpdate(ctx, msg, msgLen))
	{
		/* Error */
		printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		return 0;
	}

	if(!EVP_VerifyFinal(ctx, sign, sigLen, pkey))
	{
		/* Error */
		printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		printf("Invalid Signature\n");
		return 0;
	}

	return 1;
}

呼び出し側

入力する平文は「Trusted Design sign message.」です。署名処理で使う鍵ペアは通常は既存のものを利用します。今回はサンプルなので鍵ペアを生成して,それを使って署名を行います。


#include "openssl/ec.h"
#include "openssl/ecdsa.h"
#include "openssl/err.h"
#include "openssl/obj_mac.h"
#include "openssl/evp.h"

/*
 バイト列を出力する関数
*/
int PrintBytes(unsigned char* bytes, unsigned int bytesLen)
{
	for (int i = 1; i <= bytesLen; i++) {
		printf("%02x ", *bytes++);
		if (i % 16 == 0) printf("\n");
	}
	printf("\n");
	return 1;
}

/*
 ECDSA鍵を生成しEVP_PKEYに鍵を設定する関数
*/
int GetECDSAKey(EC_KEY* ecKey, EC_GROUP* ecGroup, EVP_PKEY* pkey)
{
    int nid = NID_secp192k1;        // 実際はもっと長い鍵長での利用を推奨

    unsigned char* buff;
    unsigned char* work;
    int bufLen = 0;
    /* EC_KEYオブジェクトを生成する */
    ecKey = EC_KEY_new();
    if (NULL == ecKey)
    {
        printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
        return 0;
    }

    /* EC_GROUPオブジェクトを生成する */
    ecGroup = EC_GROUP_new_by_curve_name(nid);
    if (NULL == ecGroup)
    {
        printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		EC_KEY_free(ecKey);
		return 0;
    }

    /* EC_KEYをEC_GROUPと関連付ける */
    if (!EC_KEY_set_group(ecKey, ecGroup))
    {
        printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		EC_KEY_free(ecKey);
		EC_GROUP_free(ecGroup);
        return 0;
    }

    /* 鍵を生成する */
    if (!EC_KEY_generate_key(ecKey))
    {
        printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
		EC_KEY_free(ecKey);
		EC_GROUP_free(ecGroup);
		return 0;
    }

    /* PKEYオブジェクトに鍵を設定する */
    EVP_PKEY_set1_EC_KEY(pkey, ecKey);
    return 1;
}

/*
 ECDSA鍵オブジェクトを解放する関数
*/
int DestroyECDSAKey(EC_KEY* ecKey, EC_GROUP* ecGroup)
{
    EC_KEY_free(ecKey);
    EC_GROUP_free(ecGroup);
	return 1;
}

/*
 メイン関数
*/

int main()
{
	/* ハッシュアルゴリズム SHA-256を利用する */
	const EVP_MD* md = EVP_sha256();

	EVP_MD_CTX* ctx;
	ctx = EVP_MD_CTX_new();
	EVP_PKEY* pkey = EVP_PKEY_new();
	EC_KEY* ecKey = NULL;
	EC_GROUP* ecGroup = NULL;

	const unsigned char msg[] = { "Trusted Design sign message." };
	int msgLen = sizeof(msg);
	unsigned char sign[128];	
	unsigned int signLen = 128;

	if (!GetECDSAKey(ecKey, ecGroup, pkey))
	{
		/* Error */
		printf("GetECDSAKey() Error\n");
		EVP_MD_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		return 0;
	}

	if (!ECDSASign(md, ctx, pkey, msg, msgLen, sign, &signLen))
	{
		/* Error */
		printf("ECDSASign() Error\n");
		EVP_MD_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		return 0;
	}
	PrintBytes(sign, signLen);

	if (!ECDSAVerify(md, ctx, pkey, msg, msgLen, sign, signLen))
	{
		/* Error */
		printf("ECDSAVerify() Error\n");
		EVP_MD_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		return 0;
	}
	printf("Valid Signature!\n");

	/* 解放処理 */
	DestroyECDSAKey(ecKey, ecGroup);
	EVP_MD_CTX_free(ctx);
	EVP_PKEY_free(pkey);
}

実行結果


30 35 02 19 00 bb 0b 46 72 35 e5 82 23 98 62 3d
27 d6 79 b1 fb d4 bc 2a fa 06 2c 89 f2 02 18 3b
20 8a a1 24 43 dd 08 ed 67 49 f0 1c 54 77 6f f9
80 43 32 1e b3 d5 a9
Valid Signature!