TCP/IP の Socket通信は、それほど難しいものではなくサイト検索や一般書籍、
あるいは iSeries の TCP/IP解説書などにサンプル・ソースの例はいくらでもある。
しかし SSL となるとサイトでも SSL の原理の説明ばかりで肝心のソースの例は少ない。
そこでここでは SSL 通信を行うサーバーとクライアントの例をシンプルでかつ、
わかりやすい解説を加えてみよう。
Socket 通信では Socket識別子というハンドルによって通信を識別して通信を行っていたが
SSL では Socket識別子の代わりに アプリケーション識別とSocket識別子から得られる
SSLハンドルによって通信するのである。
つまり Socket の代わりに SSLハンドルを使うものと理解するだけでよい。
たったこれだけのことである。
SSLサーバーの手順としては、最初に SSL環境をセットしておいてから通常の TCP/IPサーバーの
処理に加えて、「CREATE AND HANDSHAKE」 という処理によって
クライアントとの認証作業を行う。
認証が成功すると 正しい SSLハンドルを取得することができるので、後はこのSSLハンドルによって
通信するだけである。
そのような理解のもとに下記のソースを参照されたい。
/**********************************************************************
/*
/* SSLSVR : TEST SSL SERVER
/*
/**********************************************************************
#define _MULTI_THREADED
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <ssl.h>
#include <errno.h>
#define TRUE 0
#define FALSE -1
SSLHandle* sslh;
SSLInitApp sslinit;
void * malloc_ptr = (void*)NULL;
unsigned int malloc_size = 8192;
unsigned short int cipher = SSL_RSA_WITH_RC4_128_SHA;
char ssl_msg[256];
typedef struct {
int BYTESPRO;
int BYTESAVL;
char MSGID[7];
char RESRVD;
char EXCPDATA[100];
} ERRSTRUCTURE; /* Define the error return structure *
ERRSTRUCTURE errcode;/* Error Code Structure for RCVMSG */
int PORT = 443;
pthread_t sig_thread;
void INZSR(void);
int SSL_INZSR(void);
int SSL_CREATE_AND_HANDSHAKE(int* sockfd);
int SSL_READ(char* buff, int len);
int SSL_WRITE(char* buff, int len);
void SSL_CLOSE(void);
char* SSL_error(int rc);
void err_log(char* msg, int opt, char* function, int line);
void closesocket(int sockfd);
void main(void){
int sockfd, len, rc, newfd, on = 1;
struct sockaddr_in iaddr;
char buff[48];
char errmsg[128];
printf("** SSLSVR **n");
getchar();
INZSR();
if(SSL_INZSR() == FALSE){/*SSL_INZSR*/
exit(0);
}/*SSL_INZSR*/
/*-------------------------------------------------------*/
/* socket : Get a socket descriptor */
/*-------------------------------------------------------*/
if((sockfd = socket(AF_INET, SOCK_STREAM, 0 )) < 0){
perror("SOCKET"); getchar(); exit(0);
}
if((rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(char*)&on, sizeof(on))) < 0){
perror("SOCKOPT"); getchar(); exit(0);
}
/*-------------------------------------------------------*/
/* bind : bind address to the socket */
/*-------------------------------------------------------*/
memset(&iaddr, 0x00, sizeof(struct sockaddr_in));
iaddr.sin_family = AF_INET;
iaddr.sin_port = htons(PORT); /* PORT no set*/
iaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* any address OK */
if(bind(sockfd, (struct sockaddr *)&iaddr, sizeof(iaddr)) < 0){
perror("BIND"); getchar(); exit(0);
}
/*---------------------------------------------------------*/
/* listen : wait for new client's call */
/* Up to 5 clients can be queued */
/*---------------------------------------------------------*/
rc = listen(sockfd, 10);
if(rc < 0){/* LISTEN */
perror("LISTEN"); getchar(); exit(0);
}/* LISTEN */
/*---------------------------------------------------------*/
/* accept : accept new client's request */
/* from parent, should block */
/*---------------------------------------------------------*/
newfd = accept(sockfd, (struct sockaddr *)NULL, NULL);
if(newfd < 0){/*failed*/
perror("ACCEPT"); getchar(); exit(0);
}/*failed*/
/*---------------------------------------------------------*/
/* SSL_Create : enable SSL support */
/*---------------------------------------------------------*/
if(SSL_CREATE_AND_HANDSHAKE(&newfd) == FALSE){
perror("SSL_CREATE");
close(sockfd); close(newfd);
getchar();
exit(0);
}
/*---------------------------------------------------------*/
/* SSL_READ : receive message from the client */
/*---------------------------------------------------------*/
len = 48;
memset(buff, 0, sizeof(buff));
if(SSL_READ(buff, len) == FALSE){
perror("SSL_READ"); getchar(); exit(0);
}
/*---------------------------------------------------------*/
/* SSL_WRITE : send the message to the client */
/*---------------------------------------------------------*/
memset(buff, 0, sizeof(buff));
strcpy(buff, "* SSL SERVER *");
if(SSL_WRITE(buff, len) == FALSE){
perror("SSL_WRITE"); getchar(); exit(0);
}
SSL_CLOSE();
close(sockfd);
close(newfd);
printf("**************************************・n");
printf("* SSLSVR SUCCESSFULLY COMPLETE ! *・n");
printf("**************************************・n");
getchar();
return;
}
/****************/
void INZSR(void)
/****************/
{
errcode.BYTESPRO = errcode.BYTESAVL = 0;
}
/********************/
int SSL_INZSR(void)
/********************/
{
int rc = 0;
char errmsg[128];
memset((char*)&sslinit, 0, sizeof(sslinit));
sslinit.applicationID = "MY_SERVER_APP";
sslinit.applicationIDLen = 13;
sslinit.localCertificate = NULL;
sslinit.localCertificateLen = 0;
sslinit.cipherSuiteList = NULL;
sslinit.cipherSuiteListLen = 0;
malloc_ptr = (void*)malloc(malloc_size);
sslinit.localCertificate = (unsigned char*)malloc_ptr;
sslinit.localCertificateLen = malloc_size;
rc = SSL_Init_Application(&sslinit);
if(rc != 0){/* init App err */
memset(errmsg, 0, sizeof(errmsg));
sprintf(errmsg,
"SSL_INZSR:SSL_Init_App ERROR = %s・n", SSL_error(rc))
err_log(errmsg, FALSE, "SSL_INZSR", __LINE__);
return FALSE;
}/* init App err */
return TRUE;
}
/******************************************/
int SSL_CREATE_AND_HANDSHAKE(int* sockfd)
/******************************************/
{
int rc;
char errmsg[128];
sslh = SSL_Create(*sockfd, SSL_ENCRYPT);
if(sslh == NULL){/* failed in create */
memset(errmsg, 0, sizeof(errmsg));
sprintf(errmsg, "SSL_Create failed = %s・n", SSL_error(rc));
err_log(errmsg, FALSE, "SSL_CRETAE_AND_HANDSHAKE", __LINE__);
return FALSE;
}/* failed in create */
/* set parameter for handshake */
sslh->protocol = 0;
sslh->timeout = 0;
sslh->cipherSuiteList = &cipher;
sslh->cipherSuiteListLen = 1;
/* initiate the SSL handshake */
rc = SSL_Handshake(sslh,
SSL_HANDSHAKE_AS_SERVER);
if(rc != 0){/* handshake failed */
memset(errmsg, 0, sizeof(errmsg));
sprintf(errmsg, "handshake failed = %s・n", SSL_error(rc));
err_log(errmsg, FALSE, "SSL_CREATE_AND_HANDSHAKE", __LINE__);
SSL_Destroy(sslh);
closesocket(*sockfd);
return FALSE;
}/* handshake failed */
return TRUE;
}
/**********************************/
int SSL_READ(char* buff, int len)
/**********************************/
{
int rc;
char errmsg[128];
rc = SSL_Read(sslh, buff, len);
if(rc < 0){/* failed in read */
memset(errmsg, 0, sizeof(errmsg));
sprintf(errmsg, "failed in SSL_Read = %s・n", SSL_error(rc));
err_log(errmsg, FALSE, "SSL_READ", __LINE__);
SSL_Destroy(sslh);
return FALSE;
}/* failed in read */
return rc;
}
/***********************************/
int SSL_WRITE(char* buff, int len)
/***********************************/
{
int rc;
char errmsg[128];
rc = SSL_Write(sslh, buff, len);
if(rc < 0){/* failed in write */
memset(errmsg, 0, sizeof(errmsg));
sprintf(errmsg, "failed in SSL_Write = %s・n", SSL_error(rc));
err_log(errmsg, FALSE, "SSL_WRITE", __LINE__);
SSL_Destroy(sslh);
return FALSE;
}/* failed in write */
return rc;
}
/********************/
void SSL_CLOSE(void)
/********************/
{
SSL_Destroy(sslh);
}
/**********************/
char* SSL_error(int rc)
/**********************/
{
memset(ssl_msg, 0, sizeof(ssl_msg));
printf("rc = %d・n", rc);
switch(rc){/*switch*/
case SSL_ERROR_NO_CIPHERS:
strcpy(ssl_msg,
" 適切でない CIPHER が指定されました。 ");
break;
case SSL_ERROR_CERT_EXPIRED:
strcpy(ssl_msg,
"the validity time period of certificate is expired.");
break;
case SSL_ERROR_KEYPASSWORD_EXPIRED:
strcpy(ssl_msg,
"the specified key ring pasword has expied.");
break;
case SSL_ERROR_NO_KEYRING:
strcpy(ssl_msg,
"No key ring file was found.");
break;
case SSL_ERROR_NOT_REGISTERED:
strcpy(ssl_msg,
"The applicatin identifier is not registored width certifi・
cate registry facility.");
break;
case SSL_ERROR_NOT_TRUSTED_ROOT:
strcpy(ssl_msg,
"The certificate is not signed by a tructed certificate ・
authority.");
break;
case SSL_ERROR_NO_CERTIFICATE:
strcpy(ssl_msg,
"No certificate is available for SSL processing.");
break;
case SSL_ERROR_IO:
strcpy(ssl_msg,
"An error occurd in SSL processing; check the error value")
break;
case SSL_ERROR_SSL_NOT_AVAILABLE:
strcpy(ssl_msg,
"SSL が使用可能ではありません。 ");
break;
case SSL_ERROR_BAD_CERTIFICATE:
strcpy(ssl_msg,
"SSL 認証が正しくない。 ");
break;
case SSL_ERROR_BAD_CERT_SIG:
strcpy(ssl_msg,
"SSL 認証のサインが正しくない。 ");
break;
case SSL_ERROR_BAD_CIPHER_SUITE:
strcpy(ssl_msg, " 指定された CIPHER が正しくありません。 ");
break;
case SSL_ERROR_BAD_MAC:
strcpy(ssl_msg, "A bad message autherntication code was recieved.");
break;
case SSL_ERROR_BAD_MALLOC:
strcpy(ssl_msg, "unable to allocate storage for SSL");
break;
case SSL_ERROR_BAD_MESSAGE:
strcpy(ssl_msg, "SSL received a badlly formated msg");
break;
case SSL_ERROR_BAD_PEER:
strcpy(ssl_msg, "the peer system is not recognized");
break;
case SSL_ERROR_BAD_STATE:
strcpy(ssl_msg, "SSL detected a bad state in SSL session");
break;
case SSL_ERROR_CERTIFICATE_REJECTED:
strcpy(ssl_msg,"certificate is not valid or rejected by ext pgm");
break;
case SSL_ERROR_CLOSED:
strcpy(ssl_msg, "SSL session ended");
break;
case SSL_ERROR_NO_INIT:
strcpy(ssl_msg,"SSL_Init was not previously called for this job");
break;
case SSL_ERROR_PERMISSION_DENIED:
strcpy(ssl_msg, "PERMISSIO_DENIED");
break;
case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:
strcpy(ssl_msg, "UNSUPPORTED_CERTIFICATE_TYPE");
break;
case SSL_ERROR_UNKNOWN:
strcpy(ssl_msg,
"An unknown oe unexpected error occued durering SSL ・
processing");
break;
default:
sprintf(ssl_msg, "rc = %d", rc);
}/*switch*/
return ssl_msg;
}
/********************************************************/
void err_log(char* msg, int opt, char* err_at, int line)
/********************************************************/
{
fprintf(stderr, "%s・n", msg);
fprintf(stderr, " ->ERR AT = %s, LINE = %d・n", err_at, line);
if(opt == TRUE)
fprintf(stderr, "%s・n", strerror(errno));
}
/****************************/
void closesocket(int sockfd)
/****************************/
{
close(sockfd);
}
コンパイルは
CRTBNDC MYOBJLIB/SSLSVR SRCFILE(MYSRCLIB/QCSRC) AUT(*ALL)
キー・ポイントとなるのは
sslinit.applicationID = "MY_SERVER_APP";
sslinit.applicationIDLen = 13;
という 13桁の 「MY_SERVER_APP」 というアプリケーション識別子を宣言している部分である。
このプログラムが SSLの証明書つきで通信を認証するにはプログラムの名前ではなく、
「アプリケーション識別子」という ID をディジタル証明書マネージャー(DCM) によって
証明書を割り当てておく必要がある。
このように SSL通信プログラム自体のコーディングは、それほど難しいものではないが、むしろ
ディジタル証明書マネージャー(DCM) の操作に骨が折れる。
また #include <ssl.h> によって SSLのサービス・プログラムである QSOSSLSR が
自動的に組み込まれるが、これが SSL を使用可能とする *SRVPGM でありOS400 V4R3M0 から
提供されている。
IBM は OS400 から GSKIT という SSL のための新しい別の API を公示したが、
機能としての *SRVPGM は、同じ QSOSSLSR に組み込まれている。
IBM は GSKIT を推奨しているようだが 上記の例にある SSL API のほうが
やさしく使用することができる。
なお、上記の例では SSL_error というエラーメッセージ処理関数を用意しているが
OS400 V5R1M0 からは QSOSSLSR の中に SSL_Error という API が用意されている。