使用openssl的认证机制完成版本控制

晨曦之光 发布于 2012/04/10 15:03
阅读 198
收藏 0

openssl的功能十分强大,比如可以实现无缝的io过滤,可是实现安全套接字,可以实现数据加密解 密等等,其实openssl的证书认证功能可以实现C/S模式程序的版本控制功能,最重要的就是使用在ssl握手的时候会传递证书,而证书中很多字段是可 以自定义的,服务器可以通过查看客户端的证书得到客户端的信息,反之客户端也可以通过服务器的证书得到服务器的信息,很多人会认为这有点复杂了,实现信息 互通大可不必使用ssl握手,标准套接字就可以,然而使用ssl的目的就是使得通信更加保密,或者就是使用ssl的清晰的过程,openssl的握手过程 十分清晰,为我们考虑了很多我们可能考虑不到的事情,而且openssl的api非常丰富,我们可以在很安全很稳定的环境下轻松得到很充足的信息,何乐而 不为!

最简单的版本控制就是在控制端使用一个全局的变量,这个变量可以是int类型,它的每一个位代表一个功能,在程序初始化的时候服务器和作为控制端的客户端 进行ssl握手,期间服务器会将证书传递给控制端,控制端可以根据服务器证书的内容得到这个全局的版本控制变量,然后解析这个变量得到该程序可以使用的功 能,或者过程反过来也可以,就是客户端将证书传递给服务器,服务器通过验证客户端的证书来确定提供哪些服务,亦或者服务器和客户端进行双向验证。以下就是 一个实际的例子,首先看一下服务器的粗略代码:

#include "StdAfx.h"

#include "Server_Verify.h"

DWORD WINAPI VerifyProc( LPVOID lpParam ) ;

int IsValidCert( char * strCertPath )  //这个函数作为回调函数出现,实现内部认证策略,这里仅仅给出一个简单的实例

{

X509 * server_cert = NULL;

char*    str;

BIO *pbio = BIO_new_file((const char*)strCertPath, "r");

if( pbio == NULL )

{

        return 0;
    }
    server_cert = PEM_read_bio_X509(pbio,NULL,NULL,NULL);
    if( server_cert == NULL )
    {
        return 0;
    }
    str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
    if ( str == NULL )
    {
        X509_free (server_cert);
        return 0;
    }
    char *sub = strstr(str,"CN=");
    int len = 0;
    for ( len = 0;(char)*(sub+len) != '/';len++);
    sub[len] = 0;
    sub = sub+3;
    int sublen = strlen(sub);
    for ( ;(char)*(sub+sublen) != ')';sublen--);
    if(sublen <= 0||')' != *(sub+sublen))
    {
        X509_free (server_cert);
        return 0;
    }
    for ( len = sublen - 1;'(' != *(sub+len)&≤n != 0;len--);
    if ( len <= 0 )
    {

        X509_free (server_cert);
        return 0;

}

sub[sublen-1] = 0;

sub = sub+len+1;

    //获取SSL证书中的版本信息

    int nVersion = atoi(sub);

    OPENSSL_free (str);
    X509_free (server_cert);
    return 1;
}
CServerVerify::CServerVerify(void)
{
    g_hListenThread = NULL;
    m_soServer = INVALID_SOCKET;
    m_serverip=_T("") ;
    m_bExit = false;
}
int CServerVerify::StartListen(const char* lpszServerIP, int nPort, HWND hMsgRevWnd)
{
    m_serverip = (CString)lpszServerIP;
    m_port = nPort;
    m_meth = SSLv23_server_method();
    m_ctx = SSL_CTX_new (m_meth);
    char szFilePath[_MAX_PATH];
    ::GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath));
...//在szFilePath中得到证书文件的全路径
    if (SSL_CTX_use_certificate_file(m_ctx, szFilePath, SSL_FILETYPE_PEM) != 1) {
...//版本信息的出错处理
    }
    int server_ID;  //这个server_ID代表了服务器端自己验证的逻辑,要验证客户端证书,首先自己的证书要通过验证
    if( !IsValidCert_server[ID]( szFilePath ) )  //验证是否是合法证书,合法证书必须符合本公司的认证策略规定
    {
...//证书不合法的出错处理
    }
...//在szFilePath中的到key文件的全路径
    if (SSL_CTX_use_PrivateKey_file(m_ctx, szFilePath, SSL_FILETYPE_PEM) != 1) {
...//key文件和证书不匹配的出错处理
    }
    m_soServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
...//套接字创建的出错处理
...//套接字参数设置,使用ioctl
...//套接字绑定,使用bind
...//套接字侦听,使用listen
    DWORD dwThreadID = 0;
    g_hListenThread = CreateThread(NULL, 0, VerifyProc, (LPVOID)this, 0,&dwThreadID);  
    return (int)m_soServer;
}
int CServerVerify::StopListen()
{
...//停止认证服务线程,重置套接字
    return 0;
}
SSL* InitNewSslSession( SOCKET sd, CServerVerify * pServer )
{
    SSL* ssl = NULL;
    int err;
    ssl = SSL_new ( pServer->m_ctx );
...//出错后将ssl设置为NULL并返回ssl
    SSL_set_fd (ssl, sd);
    err = SSL_accept (ssl);
...//出错后将ssl设置为NULL
    return ssl;
}
DWORD WINAPI VerifyProc( LPVOID lpParam )
{
    CServerVerify * pServer = (CServerVerify *)lpParam ;
    SOCKET soServer = pServer->m_soServer ;
    FD_SET fsRead;
    FD_ZERO (&fsRead);
    FD_SET (soServer, &fsRead);
    while ( !pServer->m_bExit )
    {
        int nSelectRes = select (0, &fsRead, NULL, NULL, NULL);
...//select出错处理
        if (FD_ISSET(soServer, &fsRead))
        {
            struct sockaddr_in saddrClient;
            size_t szAddress = sizeof(saddrClient);
            SOCKET clientsd = accept( soServer, (sockaddr*)&saddrClient, (int*)&szAddress );
...//accept出错处理
            int mode = 0;
            ioctlsocket(clientsd, FIONBIO, (u_long FAR*) &mode);
            SSL *ssl = InitNewSslSession( clientsd, pServer );
            int client_ID; //这个ID代表了认证的策略ID。通过标准套接字recv得到,马上要进行客户端验证
            //以下实际上应该调用实现认证策略的回调函数,每一个客户端在连接进来的时候会告诉服务器使用哪一个认证策略,所有的认证策略在服务器端被注册
            if( !IsValidCert_server[client_ID](...) )
            {
...//证书不通过时的处理,比如发送消息给客户端,告知客户端即将不可用
            }
            if( ssl ) //由于只需要这一个ssl进行认识服务,因此认证完成后就没有必要存在了,于是释放
            {
...//ssl释放,客户端套接字的关闭
            }
        }
    }
    return 2009;
}
以下为客户端代码:
#include "StdAfx.h"
#include "Client_Verify.h"
#pragma comment(lib, "libeay32")
#pragma comment(lib, "ssleay32")
unsigned int g_iCertcode = 0;    //全局的认证信息,版本控制参考字段
int GetCertCode(SSL *ssl)
{
    X509 * server_cert = NULL;
    char*    str;
    server_cert = SSL_get_peer_certificate (ssl);
    if ( server_cert == NULL )
    {
        return 0;
    }
    str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
...//空指针出错处理
    char *sub = strstr(str,"CN=");
    int len = 0;
    for ( len = 0;(char)*(sub+len) != '/';len++);
    sub[len] = 0;
    sub = sub+3;
    int sublen = strlen(sub);
    for ( ;(char)*(sub+sublen) != ')';sublen--);
    if(sublen <= 0||')' != *(sub+sublen))
    {
        X509_free (server_cert);
        return 0;
    }
    for ( len = sublen - 1;'(' != *(sub+len)&≤n != 0;len--);
    if ( len <= 0 )
    {
        X509_free (server_cert);
        return 0;
    }
    sub[sublen-1] = 0;
    sub = sub+len+1;
    g_iCertcode = atoi(sub);  //这里得到全局的版本控制信息,这也是最终目的
    OPENSSL_free (str);
    X509_free (server_cert);
    return 1;
}
CClientVerify::CClientVerify(void)     
{
    SSLeay_add_ssl_algorithms();
    SSL_load_error_strings();
    m_soClient = INVALID_SOCKET ;
    m_clientssl = NULL;
    m_clientmeth = SSLv23_client_method();
}
SOCKET CClientVerify::ConnectToServer(sockaddr_in sdServer)
{
    SOCKET soClient = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
...//套接字创建出错处理
    if (connect(soClient, (SOCKADDR*)&sdServer, sizeof(sdServer)) == SOCKET_ERROR)
...//标准套接字连接出错处理
    m_clientmeth = SSLv23_client_method();
    m_clientctx = SSL_CTX_new (m_clientmeth);
...//得到根证书的完全路径
    int re = SSL_CTX_load_verify_locations(m_clientctx, szFilePath , ".");
...//得到根证书的出错处理,比如根证书不存在
    SSL * ssl = SSL_new(m_clientctx);
    SSL_set_fd(ssl, soClient);
    int err = SSL_connect(ssl);
...//ssl连接的出错处理
    int vres = SSL_get_verify_result(ssl);
    if ( vres != X509_V_OK )
    {
...//ssl验证不通过的出错处理,比如退出进程
    }
    if(!GetCertCode(ssl))//这个函数本来也应该是一个回调函数,通过服务器和客户端协商确定
    {
...//自定义验证不通过的出错处理,比如退出进程
    }
...//保存ssl等等用户自定义操作
    return soClient;
}


原文链接:http://blog.csdn.net/dog250/article/details/5302962
加载中
返回顶部
顶部