2013年7月2日火曜日

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"を見ながら
トライしてみてください。

0 件のコメント:

コメントを投稿