欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ssl双向验证

程序员文章站 2022-04-23 11:55:11
...

        一般情况下ssl加密连接都只是单向认证,是客户端验证服务端的证书。例如常见的web服务,是客户端验证服务端的证书,防止遇到假冒的网站,正规的网站都有权威机构签名的证书,浏览器会帮助用户校验服务端的证书,如果证书是可信的,说明网站是可信的可如果证书不可信,浏览器会有提示,提示一般在地址栏前面有个锁形符号。
        服务端在发送完server hello后将自己的证书发送给客户端,客户端此时可选的验证服务端的证书。客户端虽然是可选验证,但是连接仍然是加密连接,只是没有验证连接对端的身份。客户端用服务端证书的公钥加密一个预主**发送给服务端,以供双方后续产生加***。
        双向认证是在建立一个加密连接时,双方互相验证对方的身份,此时在上述一般情况下ssl加密连接的基础上,客户端也要提供自己的证书以供服务端验证,如果证书不正确,服务端会断开连接。双向认证时双方可以使用**协商算法协商出一个预主**,供后续产生加***。

服务端的代码如下:

int loadCertFile(SSL_CTX* ctx, const char* CertFile, const char* KeyFile, const char* caFile)
{
    if(!ctx)
    {
        return errcode;
    }
    // load cert
    if(CertFile && SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0)
    {
        return errcode;
    }

    // load private key
    if(KeyFile && SSL_CTX_use_PrivateKey_file(ctx,KeyFile, SSL_FILETYPE_PEM) <= 0)
    {
        return errcode;

    }

    // check private key
    if(CertFile && KeyFile && !SSL_CTX_check_private_key(ctx))
    {
        return errcode;
    }

    // load ca file
    if(caFile && !SSL_CTX_load_verify_locations(ctx, caFile, 0))
    {
        return errcode;
    }
    return SEC_OK;
}


// client
int openConnection(const char* ipAddr, int port)
{
    int sd;
    struct sockaddr_in addr;
    LOG_INFO("openConnection -->> ipAddr:%s; port:%d\n", ipAddr, port);
    sd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if(inet_pton(AF_INET, ipAddr, &addr.sin_addr) <= 0)
    {
        return errcode;
    }   


    if(connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
    {
        close(sd);
        return errcode;
    }
    return sd;
}
int rv = 0;
SSL_CTX* ctx;
SSL *ssl;
int listenfd, connfd;
struct sockaddr_in  clientaddr;

SSL_library_init();
ctx = initServerSslCtx();
rv  = loadCertFile(ctx, pszServerCert, pszServerKey, pszServerCA);
if(rv)
{
    return rv;
}
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); // 设置验证对方的证书
listenfd = openListener(atoi(nPort));         // 打开监听端口
connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len); //接受连接

ssl = SSL_new(ctx);
SSL_set_accept_state(ssl);
SSL_set_fd(ssl, connfd);
rv = SSL_accept(ssl);
if(rv != 1)
{
    return ERRCODE;
}

建立ssl连接后就可以使用SSL_write和SSL_read进行读写操作了。

客户端的代码如下:

nt openConnection(const char* ipAddr, int port)
{
    int sd;
    struct sockaddr_in addr;
    LOG_INFO("openConnection -->> ipAddr:%s; port:%d\n", ipAddr, port);
    sd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    if(inet_pton(AF_INET, ipAddr, &addr.sin_addr) <= 0)
    {
        return errcode;
    }   


    if(connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
    {
        close(sd);
        return errcode;
    }
    return sd;
}

SSL_CTX* initCliSslCtx(void)
{
    const   SSL_METHOD *method;
    SSL_CTX *ctx;
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    method = SSLv23_client_method();
    ctx = SSL_CTX_new(method);
    if(ctx == NULL)
    {
        return NULL;
    }
    return ctx;
}

SSL* getCliSsl(void*phSession, int sockfd)
{
    int rv = 0;
    SSL_CTX *ssl_ctx;
    SSL *ssl;
    char recvline[1];
    ssl_ctx = initCliSslCtx();
    if(!ssl_ctx)
    {
        return NULL;
    }
    SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL);
    rv  = loadCertFile(ssl_ctx, pszClientCert, pszClientKey, pszClientCA);
    if(rv)
    {
        return NULL;
    }
    ssl = SSL_new(ssl_ctx);    // create new SSL connection state
    if(!ssl)
    {
        return NULL;
    }
    SSL_set_connect_state(ssl);
    SSL_set_fd(ssl, sockfd);   // attache socet descriptor
    if(SSL_connect(ssl)  != 1)   // perform the connection
    {
        return NULL;
    }
    SSL_read(ssl, recvline, 0);
    SSL_CTX_free(ssl_ctx);
    return ssl;
}

int sockfd;
SSL* ssl;
sockfd =  openConnection(ipaddr, atoi(port));
ssl = getCliSsl(phSession, sockfd);

建立ssl连接后就可以使用SSL_write和SSL_read进行读写操作了。
具体工程代码可参考github仓库:https://github.com/liyakai/toy_server