当前位置: 首页 > 知识库问答 >
问题:

如何使用OpenSSL在C代码中进行互tls认证?

邵毅
2023-03-14

我的工作细节如下。

  1. 服务器和客户端证书生成(不通过CA进行证书签名,只需自签名)

(1)生成服务器密钥和证书。

(2)生成客户端密钥和证书。$openssl genrsa-des3-退出客户端.key 2048

$openssl请求-新建-密钥客户端.密钥-退出客户端.CSR

$cp Client.Key Client.Key.Origin

//server.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define HOME "./"
#define CERTF HOME "server.crt"
#define KEYF HOME "server.key"

#define CHK_NULL(x) if((x) == NULL) exit(1);
#define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }

int main(void) {
int err;
    int listen_sd;
    int sd;
    struct sockaddr_in sa_serv;
    struct sockaddr_in sa_cli;
    size_t client_len;

    SSL_CTX  *ctx;
    SSL    *ssl;
    X509                *client_cert;
    char                *str;
    char                buf[4096];
    SSL_METHOD  *meth;

    SSL_load_error_strings();
    SSLeay_add_ssl_algorithms();
    meth = TLSv1_2_server_method();
    ctx = SSL_CTX_new(meth);

    if(!ctx) {
        ERR_print_errors_fp(stderr);
        exit(2);
    }

    if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }

    if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(4);
    }

    if(!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr, "Private key does not match the certificate public keyn");
        exit(5);
    }

    listen_sd = socket(AF_INET, SOCK_STREAM, 0);
    CHK_ERR(listen_sd, "socket");

    memset(&sa_serv, 0x00, sizeof(sa_serv));
    sa_serv.sin_family = AF_INET;
    sa_serv.sin_addr.s_addr = INADDR_ANY;
    sa_serv.sin_port = htons(1111);

    err = bind(listen_sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv));
    CHK_ERR(err, "bind");

    err = listen(listen_sd, 5);
    CHK_ERR(err, "listen");

    client_len = sizeof(sa_cli);
    sd = accept(listen_sd, (struct sockaddr*)&sa_cli, &client_len);
    CHK_ERR(sd, "accept");
    close(listen_sd);

    ssl = SSL_new(ctx);
    CHK_NULL(ssl);
    SSL_set_fd(ssl, sd);

// to request client's certificate
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    err = SSL_accept(ssl);
    CHK_SSL(err);

    printf("SSL connection using %s \n", SSL_get_cipher(ssl));

    client_cert = SSL_get_peer_certificate(ssl);

    if(client_cert != NULL) {
        printf("Client certificate: \n");

        str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
        CHK_NULL(str);
        printf("\t subject: %s\n", str);
        OPENSSL_free(str);

        str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
        CHK_NULL(str);
        printf("\t issuer: %s\n", str);
        OPENSSL_free(str);

        X509_free(client_cert);
    } else {
        printf("Client does not have certificate. \n");
    }

    err = SSL_read(ssl, buf, sizeof(buf)-1);
    CHK_SSL(err);
    buf[err] = 0x00;
    printf("Got %d chars: %s \n", err, buf);

    err = SSL_write(ssl, "I hear you/", strlen("I hear you."));
    CHK_SSL(err);

    close(sd);
    SSL_free(ssl);
    SSL_CTX_free(ctx);

    return(0);
}

//客户端.c

#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define CHK_NULL(x) if((x) == NULL) exit(1);
#define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }

int main(void) {
    int err;
    int sd;
    struct sockaddr_in sa;

    SSL_CTX   *ctx;
    SSL     *ssl;
    X509                    *server_cert;
    char                    *str;
    char                    buf[4096];
    SSL_METHOD    *meth;

    SSL_load_error_strings();
    SSLeay_add_ssl_algorithms();
    meth = TLSv1_2_client_method();
    ctx = SSL_CTX_new(meth);
    CHK_NULL(ctx);

    if(SSL_CTX_use_certificate_file(ctx, "./client.crt", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }

    if(SSL_CTX_use_PrivateKey_file(ctx, "./client.key", SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    exit(4);
    }

    if(!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr, "Private key does not match the certificate public keyn");
        exit(5);
    }

    CHK_SSL(err);

    sd = socket(AF_INET, SOCK_STREAM, 0);
    CHK_ERR(sd, "socket");

    memset(&sa, 0x00, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    sa.sin_port = htons(1111);

    err = connect(sd, (struct sockaddr*)&sa, sizeof(sa));
    CHK_ERR(err, "connect");

    ssl = SSL_new(ctx);
    CHK_NULL(ssl);

    SSL_set_fd(ssl, sd);
    err = SSL_connect(ssl);
    CHK_NULL(err);

    printf("SSL connection using %s \n", SSL_get_cipher(ssl));

    server_cert = SSL_get_peer_certificate(ssl);
    CHK_NULL(server_cert);
    printf("Server certificate: \n");

    str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
    CHK_NULL(str);
    printf("\t subject: %s \n", str);
    OPENSSL_free(str);

    str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
    CHK_NULL(str);
    printf("\t issuer: %s \n", str);
    OPENSSL_free(str);

    X509_free(server_cert);

    err = SSL_write(ssl, "Hello World!", strlen("Hello World!"));
    CHK_SSL(err);

    err = SSL_read(ssl, buf, sizeof(buf)-1);
    CHK_SSL(err);
    buf[err] = 0x0;
    printf("Got %d chars: %s \n", err, buf);
    SSL_shutdown(ssl);

    close(sd);
    SSL_free(ssl);
    SSL_CTX_free(ctx);

    return 0;
}

下面是输出结果。

(1)服务器

得到12个字符:你好世界!

(2)客户

$./client

得到11个字符:我听到了/

======================================================================================

我不知道为什么服务器的输出说:“客户端没有证书。”

虽然我在server.c中添加了“SSL_CTX_SET_VERIFY(ctx,SSL_VERIFY_PEER,NULL)”,但服务器不会收到客户端的证书。

如果有人知道这件事,请回复。

共有1个答案

鲜于意
2023-03-14

如何避免证书验证失败错误?

如果您正在创建“根CA”,然后使用相同的“根CA”颁发服务器证书和客户端证书,则需要确保通过API SSL_CTX_LOAD_VERIFY_Locations将“根CA”证书文件作为CA添加到OpenSSL。即使只使用自签名证书,我们仍然需要使用API SSL_CTX_LOAD_VERIFY_LOCITIONS添加它们。此操作必须在SSL_NEW之前完成。

有没有一种方法可以将证书的验证与客户端分离?现在的代码是SSL_accept(ssl);似乎证书验证是在函数中完成的。也就是说,我想要的是服务器收到客户端证书后,首先打印出证书信息,然后进行验证。

 类似资料:
  • 我有基于C的应用程序,它们是用openssl smime签名的。 我希望使用C语言通过OpenSSL编程验证这些s/mime签名。我花了很多时间搜索类似的场景,但没有得到相关页面。 我已经下载了(openssl-1.0.2a)并在LinuxEnv上编译。我已经将编译的静态库集成到我的项目中。 请让我知道如何进行签名验证,或者请指导我需要调用哪个API来验证s/mime签名。

  • 如何使用Visual Studio中的集成终端启动C/C控制台应用程序,而不是启动单独的终端窗口? 当我现在按下跑步按钮时,会发生以下情况:

  • 问题内容: 我是Visual Studio用户,习惯于调试断点。我现在在Linux环境中工作,并且正在使用Eclipse作为IDE。我是linux和eclipse的新手。我不知道如何在Eclipse中使用gdb。我尝试在命令行中使用gdb,但它并不像拥有UI那样容易。 如何在Eclipse中使用gdb? 问题答案: 以下说明适用于Eclipse 3.5(Galileo)。对于3.6(Helios)

  • 如果没有,我如何在使用Apache服务器的应用程序中配置SSL/TLS相互身份验证,以便它可以要求一个客户端证书,并从这个证书中提取公钥?

  • 我想通过将spark Java/Scala api转换为dll文件来运行C#中的apache spark源代码。我引用了IKVM/IKVMC将spark jar文件转换为dll文件,但无法得到正确的结果。有没有办法在C#中运行spark源?

  • 我正在使用spring MVC创建Rest Web服务。并希望发送213代码状态作为响应。但是在类org.springframework.http.HttpStatus和类ResponseEntity(T body,HttpStatus statusCode)accept HttpStatus类型中不存在此代码。我们如何处理发送此httpStatus代码?下面是伪代码: