RSAで公開鍵暗号

ちょっと必要があってデータを暗号化するツールをCで実装しようとRSAを使ってみました。
と言っても独自に実装したわけではなくOpenSSLを利用しました。
RSAはすでに特許が切れているので自由に使えはしますが、
各実装には著作権等が絡んできますのでその辺はご注意を。

さて、Ubuntu 12.04であれば

# apt-get install libssl-dev

を実行した後に、

#include <stdio.h>
#include <openssl/rsa.h>

int main(void)
{
	int i;
	FILE *fp;
	RSA *rsa;

	SSL_library_init();
	rsa = RSA_generate_key(1024,65537,NULL,NULL);
	RSA_print_fp(stdout,rsa,0);
	fp=fopen("public.pem","w");
	PEM_write_RSAPublicKey(fp,rsa);
	fclose(fp);
	fp=fopen("private.pem","w");
	PEM_write_RSAPrivateKey(fp,rsa,NULL,NULL,0,NULL,NULL);
	fclose(fp);
	RSA_free(rsa);
	return EXIT_SUCCESS;
}

を中身とするファイル”gen.c”を作成して

$ gcc gen.c -lcrypto -lssl
$ ./gen

とすれば、秘密鍵ファイル”private.pem”と公開鍵ファイル”public.pem”が生成されます。
生成される鍵データは毎回異なります。
なお、エラーハンドリングは一切してませんがご容赦を。

続いて

#include <stdio.h>
#include <openssl/rsa.h>

int main(void)
{
	int i,j,size;
	FILE *fp;
	RSA *rsa=NULL;
	unsigned char *rb=NULL,*wb=NULL;

	SSL_library_init();
	fp=fopen("private.pem","r");
	PEM_read_RSAPrivateKey(fp,&rsa,NULL,NULL);
	fclose(fp);
	size=RSA_size(rsa);
	rb=malloc(size);
	wb=malloc(size);
	size-=11;
	while((i=fread(rb,1,size,stdin))>0){
		j=RSA_private_encrypt(i,rb,wb,rsa,RSA_PKCS1_PADDING);
		if(j<=0)
			return EXIT_FAILURE;
		fwrite(wb,1,j,stdout);
	}
	free(rb);
	free(wb);
	RSA_free(rsa);
	return EXIT_SUCCESS;
}

を中身とするファイル”enc.c”を作成して

$ gcc enc.c -lcrypto -lssl
$ ./enc < 1 > 2

とするとファイル”1″を秘密鍵ファイル”private.pem”で暗号化したファイル”2″が得られます。

さらに

#include <stdio.h>
#include <openssl/rsa.h>

int main(void)
{
	int i,j,size;
	FILE *fp;
	RSA *rsa=NULL;
	unsigned char *rb=NULL,*wb=NULL;

	SSL_library_init();
	fp=fopen("public.pem","r");
	PEM_read_RSAPublicKey(fp,&rsa,NULL,NULL);
	fclose(fp);
	size=RSA_size(rsa);
	rb=malloc(size);
	wb=malloc(size);
	while((i=fread(rb,1,size,stdin))>0){
		j=RSA_public_decrypt(i,rb,wb,rsa,RSA_PKCS1_PADDING);
		if(j<=0)
			return EXIT_FAILURE;
		fwrite(wb,1,j,stdout);
	}
	free(rb);
	free(wb);
	RSA_free(rsa);
	return EXIT_SUCCESS;
}

を中身とするファイル”dec.c”を作成して

$ gcc dec.c -lcrypto -lssl
$ ./dec < 2 > 3

とするとファイル”2″を公開鍵ファイル”public.pem”で暗号化したファイル”3″が得られ、
“1”と”3″の内容は同一となります。

もし公開鍵をCのソースファイルに埋め込みたいなら、
先の”dec.c”で鍵をファイルから読み込んだ後のrsa->n->dの指し示すメモリから256バイトを
読み取ってメモしておき、

	fp=fopen("public.pem","r");
	PEM_read_RSAPublicKey(fp,&rsa,NULL,NULL);
	fclose(fp);

の代わりに

	unsigned char pubkey[]={
		<256バイトの鍵データ>
	};      
	rsa = RSA_generate_key(1024,65537,NULL,NULL);
	memcpy(rsa->n->d,pubkey,sizeof(pubkey));

を実行してやればうまくいくっぽいです。結構強引ですが動いています。
ちなみにこれの代わりに

	unsigned char pubkey[]={
		<256バイトの鍵データ>,
		<16バイトのダミーデータ(0x00)>
	};      
        RSA rsa1;
        BIGNUM e,n;
        RSA_METHOD method;
        unsigned long long ed=65537;

        rsa = RSA_generate_key(1024,65537,NULL,NULL);
        if(!rsa){
                fprintf(stderr,"failed to generate\n");
                return EXIT_FAILURE;
        }
        memset(&rsa1,0,sizeof(RSA));
        memcpy(&method,rsa->meth,sizeof(RSA_METHOD));
        rsa1.meth=&method;
        RSA_free(rsa);
        rsa=&rsa1;
        memset(&e,0,sizeof(BIGNUM));
        memset(&n,0,sizeof(BIGNUM));
        rsa->references=1;
        rsa->flags=6;
        rsa->e=&e;
        rsa->e->d=&ed;
        rsa->e->top=1;
        rsa->e->dmax=1;
        rsa->e->flags=1;
        rsa->n=&n;
        rsa->n->d=pubkey;
        rsa->n->top=16;
        rsa->n->dmax=17;
        rsa->n->flags=1;

でもいけました。ただし64ビット処理系です。
なおRSA_generate_key()は処理関数へのポインタを得るためだけに実行しています。
実体はライブラリ上でstatic宣言されているので直接関数名を指定することができず、
このような形になっています。

また、試していませんが秘密鍵を埋め込みたい場合は、
rsaポインタのe,d,p,q,dmp1,dmq1,iqmpの各メンバ変数のdプロパティについて
同様に処理するといけるような気がします。
もしやるなら”bn.h”の”struct bignum_st”や”rsa.h”の”struct rsa_st”を見ながら
トライしてみてください。

広告