ここのところSSLを使った暗号化通信の方法を勉強しているのですが、とりあえず出来るようになったのでまとめておきます。
基本的に普段のソケットプログラミングに1枚皮を被せるだけで素直な作りになっているようですが、ドキュメントが一部の関数の分しか存在しないのが困りものですが。
サーバもクライアントもいつものヘッダに加えて
#include
#include
を指定しておき、リンク時には-lsslオプションを付けます。パスは各自の環境に合わせて下さい。
クライアント編
SSL_CTX *ctx; //SSL全体の設定や作業領域のようです(プログラム全体で1つ)
SSL *ssl; //実際のSSL通信に対応する構造体のようです(接続毎に1つ)
int sock; //いつものソケットディスクリプタ
//SSL初期化 error_stringsはエラーメッセージ出力用のようですが使ってないのでよく分かりません
// SSL_load_error_strings()したときは終了前にERR_free_strings()で開放
SSL_load_error_strings();
SSL_library_init();
// SSL_CTX作成
// 今回はTLSv1を使ったのでTLSv1_client_method()を引数に。その他
// SSLv2 SSLv2_client_method()
// SSLv3 SSLv3_client_method()
// が指定できるようです
// SSL_CTXを作ったら終了前にSSL_CTX_free(ctx)で開放
if(!(ctx=SSL_CTX_new(TLSv1_client_method())))
{
fprintf(stderr,"SSL init error.\n");
ERR_free_strings();
return -1;
};
// ルート証明書読み込み
// 今回は似非証明書CRTFILEを読み込む(自分で作ったサーバとクライアント同士の接続なので)だけですが、httpsクライアントとか作る時は必要なルート証明書を同じディレクトリに集めておき、SSL_CTX_load_verify_locationsの第3引数にディレクトリを指定するとそれを全部読み込みます
if(!SSL_CTX_load_verify_locations(ctx,CRTFILE,NULL))
{
fprintf(stderr,"Can't load CA file.\n");
SSL_CTX_free(ctx);
ERR_free_strings();
return -1;
};
//とりあえず普段通りソケット作って繋ぎます
struct addrinfo hints,*ai,*ai_start;
memset(&hints,0,sizeof(hints));
hints.ai_family=PF_UNSPEC; //IPv4のみのときはPF_INET, v6のみのときはPF_INET6
hints.ai_socktype=SOCK_STREAM;
char addr_string[128];
char port_string[64];
strncpy(addr_string,ADDRESS,128);
snprintf(port_string,64,"%d",PORT);
if(getaddrinfo(addr_string,port_string,&hints,&ai_start))
{
fprintf(stderr,"Host lookup error\n");
SSL_CTX_free(ctx);
ERR_free_strings();
return -1;
};
sock=-1;
for(ai=ai_start;ai!=NULL;ai=ai->ai_next)
{
sock=socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(sock<0){continue;};
if(connect(sock,ai->ai_addr,(int)ai->ai_addrlen)<0)
{
close(sock);
sock=-1;
continue;
};
// connectまで成功したらSSL_nex(ctx)でSSL構造体を作成し(開放はSSL_free(ssl))、
// SSL_set_fd(ssl,sock)でソケットディスクリプタをSSL構造体にセットし、
// SSL_connect(ssl)でSSL通信を開始します
ssl=SSL_new(ctx);
SSL_set_fd(ssl,sock);
if(SSL_connect(ssl)<0)
{
SSL_free(ssl);
ssl=NULL;
close(sock);
sock=-1;
continue;
};
break;
};
if(sock<0)
{
fprintf(stderr,"Can't connect server\n");
SSL_CTX_free(ctx);
ERR_free_strings();
return -1;
};
freeaddrinfo(ai_start);
あとはread(sock,buf,buflen), write(sock,buf,buflen)のかわりに
SSL_read(ssl,buf,buflen), SSL_write(ssl,buf,buflen)を使えばOKです。
selectする際などSSL構造体からソケットディスクリプタを得る必要があるときは
SSL_get_fd(ssl)で取得できます。
// 後始末
// closeする前にSSL_shutdownでSSL通信を終了します。
// 強制切断された時などはSSL_shutdownしようとするとSIGPIPEで落ちるのでトラップしておきましょう(^^;;)
SSL_shutdown(ssl);
SSL_free(ssl);
close(sock);
SSL_CTX_free(ctx);
ERR_free_strings();
return 0;
2008-07-12 12:55:54[プログラミング]
この記事へのトラックバックURL: https://www.asmusic.jp/ASHARD/score/tb.cgi/yu-oishi/20080710_1
手動トラックバック