RSA 署名と検証

私密鍵(private key)で何らかのデータを署名する.この署名を証明書(certification)に含まれている公開鍵(public key)で検証する.

準備するもの

openssl.exe で鍵を作成する

鍵を3種1個ずつ作る.private key(pKey: certpKey.pem),certification(cert: cert.pem),public key(pubKey: pubKey.pem).pubKey を作るのは,私がテストしている中で問題があって,原因の切り分けのために作った.だから,署名検証の成立だけを見届ける場合はいらない.

openssl rsa -inform pem -outform pem -in pKey.pem -out pubKey.pem -pubout
openssl req -outform pem -out cert.pem -nodes -key certpkey.pem -keyform pem -subj /C=JP/ST=Sendai/L=Morio/CN=ssl.example.com -new -x509
openssl rsa -inform pem -outform pem -in certpKey.pem -out pubKey.pem -pubout

プログラム

「The quick brown fox jumps over the lazy dog」というテキストに署名する.実際にはこのテキストから生成されるハッシュ値に署名する.ハッシュ関数は SHA-256 を使用した.

署名(検証)の関数は RSA_sign(),RSA_verify()を使用する.署名(検証)に問題ない場合,関数は 1 を返す.

ここで示しているソースコードでは,署名検証について,公開鍵 PEM ファイルから公開鍵を取り出す A 方式と,自己署名証明書 PEM ファイルから公開鍵を取り出す B 方式の2とおりある.

#ifdef _DEBUG
#pragma comment(lib, "libeay32MDd.lib")
#pragma comment(lib, "ssleay32MDd.lib")
#else
#pragma comment(lib, "libeay32MD.lib")
#pragma comment(lib, "ssleay32MD.lib")
#endif

#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
// openssl/applink.c 内の fopen() への warning C4996 の抑制
#ifdef _WIN32
#ifdef WIN32
#define WIN32_PREDEFINED
#else
#define WIN32
#endif
#pragma warning( disable: 4996 )
#endif
#include <openssl/applink.c>
#ifdef WIN32
#ifdef WIN32_PREDEFINED
#undef WIN32_PREDEFINED
#else
#undef WIN32
#endif
#pragma warning( default: 4996 )
#endif
// warning C4996 の抑制はここまで

const char *pKeyFilename = "C:\\data\\PEM\\Private\\certpKey.pem"; // 署名鍵
const char *pubKeyFilename = "C:\\data\\PEM\\Public\\pubKey.pem"; // 署名鍵から生成した公開鍵
const char *certFilename = "C:\\data\\PEM\\Signature\\cert.pem"; // 署名鍵から生成した自己署名証明書

int main ( int argc, char *argv[] )
{
	FILE *fp; // PEMファイル読み込みしに使用するファイルポインタ
	RSA *pKey; // 私密鍵
	RSA *pubKey; // 公開鍵
	X509 *cert; // 証明書
	char *text = "The quick brown fox jumps over the lazy dog"; // 平文.(unsigned char *)にキャストする
	unsigned char hash[SHA256_DIGEST_LENGTH]; // 署名対象.平文のSHA-1
	unsigned char signature[2048/8]; // 署名情報
	unsigned int signatureLength; // 署名情報のサイズ
	int sigret; // sign,verify の返り値を格納する

	// 署名対象を作る
	SHA256( (unsigned char *)text, strlen( text ), hash );

	// 私密鍵を読み込む.署名に使用する
	fopen_s( &fp, pKeyFilename, "r");
	pKey = PEM_read_RSAPrivateKey( fp, 0, 0, 0 );
	fclose( fp );

	// 署名する
	sigret = RSA_sign( NID_sha256, hash, SHA256_DIGEST_LENGTH, signature, &signatureLength, pKey );
	printf("%d\n", sigret);

	// A. 公開鍵を読み込む
	//fopen_s( &fp, pubKeyFilename, "r" );
	//pubKey = PEM_read_RSA_PUBKEY( fp, 0, 0, 0 );
	//fclose( fp );
	//RSA_print_fp( stdout, pubKey, 0 );

	// B. 証明書から公開鍵を取り出す
	fopen_s( &fp, certFilename, "r" );
	cert = PEM_read_X509( fp, 0, 0, 0 );
	fclose( fp );
	EVP_PKEY *evp = X509_get_pubkey( cert );
	evp = X509_get_pubkey( cert );
	pubKey = EVP_PKEY_get1_RSA( evp );
//	RSA_print_fp( stdout, pubKey, 0 );
	

	// 検証する
	sigret = RSA_verify( NID_sha256, hash, SHA256_DIGEST_LENGTH, signature, signatureLength, pubKey );
	printf("%d\n", sigret);

	return 0;
}