与ECDHE密钥和证书服务器无法正常工作 [英] Server with ECDHE key and cert not working

查看:710
本文介绍了与ECDHE密钥和证书服务器无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用下面的server.c源,我产生

罪恶宿主cert.pem
罪孽深重-host.key

如下所述:椭圆曲线CA指南

在运行程序得到以下错误:


  

140722397161136:错误:10071065:椭圆曲线函数:FUNC(113):原因(101):ec_lib.c:995:
  140722397161136:错误:0B080075:X509证书程序:FUNC(128):原因(117):x509_cmp.c:346:


我使用编译:


  

GCC server.c -ldl -lcrypto -lssl -o服务器


在该行出现的错误,我认为


  

如果(SSL_CTX_use_PrivateKey_file(CTX,密钥文件,SSL_FILETYPE_PEM)LT; = 0)


server.c

 的#include<&errno.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&;&malloc.h所GT;
#包括LT&;&string.h中GT;
#包括LT&; ARPA / inet.h>
#包括LT&; SYS / socket.h中>
#包括LT&; SYS / types.h中>
#包括LT&; netinet / in.h中>
#包括LT&;&resolv.h GT;
#包括为OpenSSL / ssl.h
#包括为OpenSSL / err.h#定义FAIL -1INT OpenListener(INT端口)
{诠释SD;
    结构sockaddr_in的地址;
    SD =插座(PF_INET,SOCK_STREAM,0);
    bzero(安培;地址,的sizeof(地址));    INET_ATON(10.8​​.0.26,&放大器; addr.sin_addr);    addr.sin_family = AF_INET;
    addr.sin_port = htons(端口);
    //addr.sin_addr.s_addr = INADDR_ANY;    如果(绑定(SD,(结构sockaddr *)及!地址,的sizeof(地址))= 0)
    {
        PERROR(不能绑定端口);
        中止();
    }
    如果(听(SD,10)!= 0)
    {
        PERROR(无法配置监听端口);
        中止();
    }
    返回SD;
}SSL_CTX * InitServerCTX(无效)
{常量SSL_METHOD *方法;
    SSL_CTX * CTX;    OpenSSL_add_all_algorithms(); / *负载和放大器;注册所有cryptos等。* /
    SSL_load_error_strings(); / *加载所有的错误信息* /    //方法= SSLv23_server_method();
    方法= TLSv1_2_server_method(); / *创建新的服务器实例的方法* /
    CTX = SSL_CTX_new(法); / *从方法来创建新的上下文* /
    如果(CTX == NULL)
    {
        ERR_print_errors_fp(标准错误);
        中止();
    }
    返回CTX;
}无效LoadCertificates(SSL_CTX * CTX,字符* CERTFILE,字符* KeyFile时)
{
    //新线
    如果(SSL_CTX_set_cipher_list(CTX,ECDHE-ECDSA-AES128-GCM-SHA256)!= 1)
         ERR_print_errors_fp(标准错误);    如果(SSL_CTX_load_verify_locations(CTX,CERTFILE,密钥文件)!= 1)
        ERR_print_errors_fp(标准错误);    如果(SSL_CTX_set_default_verify_paths(CTX)!= 1)
        ERR_print_errors_fp(标准错误);
    //结尾的新行    / *从CERTFILE设置本地证书* /
    如果(SSL_CTX_use_certificate_file(CTX,CERTFILE,SSL_FILETYPE_PEM)下; = 0)
    {
        ERR_print_errors_fp(标准错误);
        中止();
    }    的printf(FFFF \\ n);
    / *从KeyFile中设置专用密钥(可以是相同CERTFILE)* /
    如果(SSL_CTX_use_PrivateKey_file(CTX,密钥文件,SSL_FILETYPE_PEM)LT; = 0)
    {
        ERR_print_errors_fp(标准错误);
        中止();
    }
    的printf(GGGG \\ n);
    / *验证私钥* /
    如果(!SSL_CTX_check_private_key(CTX))
    {
        fprintf中(标准错误,私人密钥不匹配的公证书\\ n);
        中止();
    }    //新行 - 强制客户端有一个证书
    // SSL_CTX_set_verify(CTX,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL);
    // SSL_CTX_set_verify_depth(CTX,4);
    //结尾的新行
}无效ShowCerts(SSL * SSL)
{X509 *证书;
    字符*线;    证书= SSL_get_peer_certificate(SSL); / *获取证书(如果可用)* /
    如果(CERT!= NULL)
    {
        的printf(服务器证书:\\ n);
        行= X509_NAME_oneline(X509_get_subject_name(证书),0,0);
        的printf(主题:%S \\ n,行);
        免费(线);
        行= X509_NAME_oneline(X509_get_issuer_name(证书),0,0);
        的printf(发行人:%S \\ n,行);
        免费(线);
        X509_free(CERT);
    }
    其他
        的printf(没有证书\\ n);
}无效的Servlet(SSL * SSL)/ *即成连接 - threadable * /
{字符BUF [1024];
    焦炭回复[1024];
    INT SD,字节,走错了路。
    为const char * HTMLecho =< HTML><身体GT;< pre>%S< / pre>< /身体GT;< / HTML> \\ n \\ n;    的printf(忽忽pre \\ n);    ERR = SSL_accept(SSL);    如果(ERR< = 0){/ *做SSL协议接受* /
        的printf(%d个\\ N,ERR);
        ERR_print_errors_fp(标准错误);
    }
    其他
    {
        的printf(XXXXXX \\ n);
        // SSL_write(SSL,忽忽\\ n \\ r,即8);
        ShowCerts(SSL); / *得到任何证书* /
        字节= SSL_read(SSL,BUF,sizeof的(BUF)); / * get请求* /
        如果(字节大于0)
        {
            BUF [字节] = 0;
            的printf(客户信息:\\%s \\的\\ n,BUF);
            sprintf的(回复,HTMLecho,BUF); / *结构答复* /
            SSL_write(SSL,回复,strlen的(回复)); / *发送应答* /
        }
        其他
            ERR_print_errors_fp(标准错误);
    }
    SD = SSL_get_fd(SSL); / *获取套接字连接* /
    SSL_free(SSL); / *释放SSL状态* /
    关闭(SD); / *关闭连接* /
}诠释的main()
{SSL_CTX * CTX;
    int服务器;
    CHAR端口编号[] =5000;        CHAR CERTFILE [] =罪恶的宿主cert.pem
        CHAR KeyFile是[] =罪恶-host.key    SSL_library_init();    CTX = InitServerCTX(); / *初始化SSL * /
    LoadCertificates(CTX,CERTFILE,密钥文件); / *加载证书* /
    服务器= OpenListener(与atoi号(端口)); / *创建服务器套接字* /    的printf(%d个,而\\ n,服务器);
    而(1)
    {结构sockaddr_in的地址;
        socklen_t的LEN = sizeof的(地址);
        SSL * SSL;        INT客户端=接受(服务器,(结构sockaddr *)及地址,和放大器; LEN); / *接受连接一样* /
        的printf(连接:%s的数:%d \\ n,INET_NTOA(addr.sin_addr),ntohs和(addr.sin_port));
        SSL = SSL_new(CTX); / *获得新的SSL状态,中* /
        如果(SSL == NULL){            ERR_print_errors_fp(标准错误);
            返回0;        }
        SSL_set_fd(SSL,客户端); / *设置的连接插座SSL状态* /
        的servlet(SSL); / *服务连接* /
    }
    关闭(服务器); / *关闭服务器套接字* /
    SSL_CTX_free(CTX); / *版本中* /
}


解决方案

  

如下所述:椭圆曲线CA指南...


本页面有这么多的错误和遗漏我会丢弃它。第一面红旗是白色​​文本和黑色的背景。这告诉我这样的人经验不足的是提供网页...


  

从页面:

  OpenSSL的ecparam -list-曲线


这应该是 -list_curves ,而不是 -list-曲线


  

从页面:

  OpenSSL的ecparam退房手续sinful.key -name sect283k1 -genkey


这应该是:

OpenSSL的ecparam -param_enc named_curve退房手续sinful.key -name sect283k1 -genkey

如果您的的使用命名的曲线,那么你将有很多问题后,当客户端试图连接到服务器等。在这里,命名为曲线像 secp256k1 曲线的OID,而不是域的参数,如 P A b

在很多问题后来的在OpenSSL的维基椭圆曲线加密文件,命名为曲线 。以下是一些您将遇到问题:


  • 客户端: 139925962778272:错误:14094410:SSL例程:SSL3_READ_BYTES:SSLv3的警报握手失败:s3_pkt.c:1256:SSL警告40号

  • 客户端: 139925962778272:错误:1409E0E5:SSL例程:SSL3_WRITE_BYTES:SSL握手失败:s3_pkt.c:596

  • 服务器: 140339533272744:错误:1408A0C1:SSL例程:SSL3_GET_CLIENT_HELLO:没有共享密码:s3_srvr.c:1353

此外,为的最大的互操作性,你应该使用 secp256k1 。紧随其后是 secp521r1

此外,使用/缺少 - *形式 OpenSSL的ecparam OpenSSL的REQ 命令将在下面讨论。



  SSL_CTX * InitServerCTX(无效){...}


这code座有不少问题。最值得注意的是缺乏ECDH回调。你在哪里设置 SSL_CTX_set_tmp_ecdh 回调(OpenSSL的1.0.1及以下),或者是调用 SSL_CTX_set_ecdh_auto ( OpenSSL的1.0.2及以上)?

其他包括<击>默认协议,默认的密码列表,虚弱和受伤的密码,匿名协议纳入,和COM pression。对于code的部分例如提供的服务器内,参见没有共享密码错误与EDH-RSA-DES-CBC3 -SHA



  

在该行出现的错误,我认为

 如果(SSL_CTX_use_PrivateKey_file(CTX,密钥文件,SSL_FILETYPE_PEM)LT; = 0)


我认为这可以追溯到有缺陷的页面引用你。这样的:

OpenSSL的REQ -x509 -new -key sinful.key退房手续有罪,ca.pem -outform PEM -days 3650

也许应该是(注意添加 -keyform

OpenSSL的REQ -x509 -new -key sinful.key -keyform PEM退房手续有罪,ca.pem -outform PEM - 3650天

或者

 如果(SSL_CTX_use_PrivateKey_file(CTX,密钥文件,SSL_FILETYPE_ASN1)LT; = 0)

在一般情况下,始终使用 *形式选项命令,不管是它的 -keyform -certform -inform -outform 等OpenSSL的不总是正确(即使它应该默认使用PEM)。



  

在该行出现的错误,我认为

 如果(SSL_CTX_use_PrivateKey_file(CTX,密钥文件,SSL_FILETYPE_PEM)LT; = 0)


如果私钥具有密码,那么你就需要提供一个密码回调或剥去文件的密码。

其确定剥离密码,因为有一个在存储明文私钥没有什么区别;或在旁边的密钥的配置文件的密码一个加密的私有密钥。在这两种情况下,你拥有的唯一有效的安全性是文件系统访问控制列表。

相关,这就是所谓的的无人值守的密钥存储的问题。格特曼讨论它是在他的书工程安全。它没有解决的问题。


下面是一些更完整的错误信息......它看起来像您使用的是旧版本的OpenSSL,并且不提供新的错误codeS。


  

在运行程序得到以下错误:

  140722397161136:错误:10071065:椭圆曲线函数:FUNC(113):原因(101):ec_lib.c:995
 140722397161136:错误:0B080075:X509证书程序:FUNC(128):原因(117):x509_cmp.c:346


首先, 0x10071065 错误:

$在/ usr /本地/ SSL / MacOSX的-64 /斌/ OpenSSL的errstr 0x10071065
错误:10071065:椭圆曲线套路:EC_POINT_cmp:不兼容对象

0x10071065 通常意味着客户端和服务器使用的是不兼容的EC领域。在这种情况下,你应该使用 secp256k1 secp521r1

二, 0x0B080075 错误:

$在/ usr /本地/ SSL / MacOSX的-64 /斌/ OpenSSL的errstr 0x0B080075
错误:0B080075:X509证书套路:X509_check_private_key:未知的密钥类型

我猜有证书和私钥不匹配。但它只是一种猜测。我将(1)清除指定的曲线问题,(2)清除 sect283k1 问题;(3)清除下级库的问题(见下文)。清除这些问题之后,再看看这个问题仍然存在。



  

看起来您使用的是旧版本的OpenSSL,并且不提供新的错误$ C $ ... CS


请确保您正在运行的OpenSSL 1.0.0或更高版本。 0.9.8有有限的EC支持,但它并没有真正切入有效,直至1.0.0。更好的,使用OpenSSL 1.0.2。



  OpenSSL_add_all_algorithms(); / *负载和放大器;注册所有cryptos等。* /
 SSL_load_error_strings(); / *加载所有的错误信息* /


也可参阅OpenSSL的维基库初始化



 如果(SSL_CTX_set_cipher_list(CTX,ECDHE-ECDSA-AES128-GCM-SHA256)


这将让你进入在OS X和iOS的某些版本的麻烦,由于在 SecureTransport 的库中的缺陷。苹果只固定在他们的操作系统的某些版本。

如果你打算维修苹果hardwarez,那么您将需要一个额外的非ECDHE-ECDSA 密码。而你需要使用服务器端上下文选项<一个href=\"https://wiki.openssl.org/index.php/SSL_OP_SAFARI_ECDHE_ECDSA_BUG\"><$c$c>SSL_OP_SAFARI_ECDHE_ECDSA_BUG.

相关的,苹果是pretty大胆关于未固着他们的安全漏洞。你有破ECDHE-ECDSA密码套件;和宝石如 CVE-1115至30年(隐藏后门带根)


下面就是我的ECDH的回调看起来像OpenSSL的1.0.1及以下。 OpenSSL的1.0.2应使用 SSL_CTX_set_ecdh_auto 。它的C ++ code,但它很容易转换回至C code。另请参阅<一个href=\"http://openssl.6102.n7.nabble.com/SSL-CTX-set-tmp-ecdh-callback-semantics-in-1-0-1-td51401.html\">SL_CTX_set_tmp_ecdh_callback语义1.0.1 OpenSSL的邮件列表上。

下code可能会更加强劲。回调函数应该获取与 SSL_get_certificate 证书(的 SSL_get_peer_certificate ),查询证书EC字段,然后提供相应的字段临时密钥,如 secp256k1 secp571k1 。 (它的工作原理,因为我的证书使用 secp256 EcdhCallback 使用 secp256 作为其默认值)。

SSL_get_certificate 未记录。但它在中所使用; OpenSSL的SRC&GT; /apps/s_cb.c 。这就是自我记录code OpenSSL是著名的。

 使用SSL_ptr =的std :: shared_ptr的&LT; SSL取代;
使用SSL_CTX_ptr =的std :: shared_ptr的&LT; SSL_CTX取代;使用EC_KEY_ptr =的std ::的unique_ptr&LT; EC_KEY,decltype(安培; :: EC_KEY_free)取代;
使用EC_GROUP_ptr =的std ::的unique_ptr&LT; EC_GROUP,decltype(安培; :: EC_GROUP_free)取代;
使用EC_POINT_ptr =的std ::的unique_ptr&LT; EC_POINT,decltype(安培; :: EC_POINT_free)取代;
使用EVP_PKEY_ptr =的std ::的unique_ptr&LT; EVP_PKEY,decltype(安培; :: EVP_PKEY_free)取代;
使用BIO_MEM_ptr =的std ::的unique_ptr&LT; BIO,decltype(安培; :: BIO_free)取代;
使用BIO_FILE_ptr =的std ::的unique_ptr&LT; BIO,decltype(安培; :: BIO_free)取代;
...SSL_CTX * CreateServerContext(常量字符串和放大器;域)
{
    常量SSL_METHOD *方法= SSLv23_server_method();
    ASSERT(方法!= NULL);    SSL_CTX_ptr吨(SSL_CTX_new(方法):: SSL_CTX_free);
    ASSERT(t.get()!= NULL);    长标志= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
            标志| = SSL_OP_NO_COM preSSION;
            标志| = SSL_OP_SAFARI_ECDHE_ECDSA_BUG;
            标志| = SSL_OP_CIPHER_SERVER_ preFERENCE;    的SSL_CTX_set_options(t.get(),旗);    字符串密码=!!!HIGH:A零位:RC4:MD5
    RC = SSL_CTX_set_cipher_list(t.get(),ciphers.c_str());
    ...    LogDebug(GetServerContext:设置ECDH回调);
    SSL_CTX_set_tmp_ecdh_callback(t.get(),EcdhCallback);
    ...    返回T.RELEASE();
}EC_KEY * EcdhCallback(SSL * SSL,诠释is_export,诠释keylength)
{
    未使用(SSL);
    未使用(is_export);
    未使用(keylength);    / *这个回调OK,但OpenSSL的调用它在一个破碎的方式。 * /
    / *使用1.0.1e和1.0.1f,该值为1024位。这是比较* /
    / *适用于RSA ....我们会尽力在这里重写。 * /
    如果(keylength&GT; = 1024)
    {
        keylength = 256;
        LogRelevant(EcdhCallback:字段大小是错误的,使用256位组);
    }#如果定义(ALLOW_ECDH_192_PARAMS)
    如果(keylength&下; = 192 + 4)
        返回ECDH192();
#万一    如果(keylength&下; = 224 + 4)
        返回ECDH224();
    否则如果(keylength&下; = 256 + 4)
        返回ECDH256();
    否则如果(keylength&下; = 384 + 4)
        返回ECDH384();
    否则如果(keylength&下; = 521 + 4)
        返回ECDH521();    返回ECDH521();
}#如果定义(ALLOW_ECDH_192_PARAMS)
静态EC_KEY * ECDH192()
{
    静态EC_KEY_ptr键(NULL,NULL);
    静态once_flag标志;    call_once的(标记,[]()
    {
        键= EC_KEY_ptr(InitEcdhkey(192):: EC_KEY_free);
        ASSERT(key.get());        如果(!key.get())
            LOGERROR(ECDH192:InitEcdhkey失败);
    });    返回key.get();
}
#万一静态EC_KEY * ECDH224()
{
    静态EC_KEY_ptr键(NULL,NULL);
    静态once_flag标志;    call_once的(标记,[]()
    {
        键= EC_KEY_ptr(InitEcdhkey(224):: EC_KEY_free);
        ASSERT(key.get());        如果(!key.get())
            LOGERROR(ECDH224:InitEcdhkey失败);
    });    返回key.get();
}静态EC_KEY * ECDH256()
{
    静态EC_KEY_ptr键(NULL,NULL);
    静态once_flag标志;    call_once的(标记,[]()
    {
        键= EC_KEY_ptr(InitEcdhkey(256):: EC_KEY_free);
        ASSERT(key.get());        如果(!key.get())
            LOGERROR(ECDH256:InitEcdhkey失败);
    });    返回key.get();
}静态EC_KEY * ECDH384()
{
    静态EC_KEY_ptr键(NULL,NULL);
    静态once_flag标志;    call_once的(标记,[]()
    {
        键= EC_KEY_ptr(InitEcdhkey(384):: EC_KEY_free);
        ASSERT(key.get());        如果(!key.get())
            LOGERROR(ECDH384:InitEcdhkey失败);
    });    返回key.get();
}静态EC_KEY * ECDH521()
{
    静态EC_KEY_ptr键(NULL,NULL);
    静态once_flag标志;    call_once的(标记,[]()
    {
        键= EC_KEY_ptr(InitEcdhkey(521):: EC_KEY_free);
        ASSERT(key.get());        如果(!key.get())
            LOGERROR(ECDH521:InitEcdhkey失败);
    });    返回key.get();
}静态EC_KEY * InitEcdhkey(INT位)
{
    如果(比特下; = 160 + 4)
        位= 160;
    否则如果(比特下; = 192 + 4)
        位= 192;
    否则如果(比特下; = 224 + 4)
        位= 224;
    否则如果(比特下; = 256 + 4)
        位= 256;
    否则如果(比特下; = 384 + 4)
        位= 384;
    否则如果(比特下; = 521 + 4)
        位= 521;
    其他
        位= 521;    EC_KEY *键= EC_KEY_new_by_curve_name(CurveToNidByBits(位));
    无符号长ERR = ERR_get_error();    ASSERT(关键!= NULL);
    如果(键== NULL)
    {
        ostringstream OSS;
        OSS&LT;&LT; InitEcdhkey:EC_KEY_new_by_curve_name失败;
        OSS&LT;&LT;比特&LT;&LT; 位密钥,错误&LT;&LT;犯错&LT;&LT; 0X&LT;&LT;呃;
        LOGERROR(OSS);
    }    返回键;
}

I use the below server.c source, i generated

sinful-host-cert.pem
sinful-host.key

as described here: Elliptic Curve CA Guide

When running the program get the following errors:

140722397161136:error:10071065:elliptic curve routines:func(113):reason(101):ec_lib.c:995: 140722397161136:error:0B080075:x509 certificate routines:func(128):reason(117):x509_cmp.c:346:

I compiled using:

gcc server.c -ldl -lcrypto -lssl -o Server

The error occurs at this line I think

if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)

server.c

#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "openssl/ssl.h"
#include "openssl/err.h"

#define FAIL    -1

int OpenListener(int port)
{   int sd;
    struct sockaddr_in addr;


    sd = socket(PF_INET, SOCK_STREAM, 0);
    bzero(&addr, sizeof(addr));

    inet_aton("10.8.0.26", &addr.sin_addr);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    //addr.sin_addr.s_addr = INADDR_ANY;

    if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
    {
        perror("can't bind port");
        abort();
    }
    if ( listen(sd, 10) != 0 )
    {
        perror("Can't configure listening port");
        abort();
    }
    return sd;
}

SSL_CTX* InitServerCTX(void)
{   const SSL_METHOD *method;
    SSL_CTX *ctx;

    OpenSSL_add_all_algorithms();  /* load & register all cryptos, etc. */
    SSL_load_error_strings();   /* load all error messages */

    //method = SSLv23_server_method();
    method = TLSv1_2_server_method();  /* create new server-method instance */
    ctx = SSL_CTX_new(method);   /* create new context from method */
    if ( ctx == NULL )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    return ctx;
}

void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
    //New lines 
    if (SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-AES128-GCM-SHA256") != 1)
         ERR_print_errors_fp(stderr);

    if (SSL_CTX_load_verify_locations(ctx, CertFile, KeyFile) != 1)
        ERR_print_errors_fp(stderr);

    if (SSL_CTX_set_default_verify_paths(ctx) != 1)
        ERR_print_errors_fp(stderr);
    //End new lines

    /* set the local certificate from CertFile */
    if (SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    printf("FFFF\n");
    /* set the private key from KeyFile (may be the same as CertFile) */
    if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    printf("GGGG\n");
    /* verify private key */
    if (!SSL_CTX_check_private_key(ctx))
    {
        fprintf(stderr, "Private key does not match the public certificate\n");
        abort();
    }

    //New lines - Force the client-side have a certificate
    //SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
    //SSL_CTX_set_verify_depth(ctx, 4);
    //End new lines
}

void ShowCerts(SSL* ssl)
{   X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
    if ( cert != NULL )
    {
        printf("Server certificates:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("Subject: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("Issuer: %s\n", line);
        free(line);
        X509_free(cert);
    }
    else
        printf("No certificates.\n");
}

void Servlet(SSL* ssl) /* Serve the connection -- threadable */
{   char buf[1024];
    char reply[1024];
    int sd, bytes, err;
    const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";

    printf("huhupre\n");

    err = SSL_accept(ssl);

    if ( err <= 0 ) {    /* do SSL-protocol accept */
        printf("%d\n",err);
        ERR_print_errors_fp(stderr);
    }
    else
    {
        printf("XXXXXX\n");
        //SSL_write(ssl, "huhu\n\r", 8);
        ShowCerts(ssl);        /* get any certificates */
        bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
        if ( bytes > 0 )
        {
            buf[bytes] = 0;
            printf("Client msg: \"%s\"\n", buf);
            sprintf(reply, HTMLecho, buf);   /* construct reply */
            SSL_write(ssl, reply, strlen(reply)); /* send reply */
        }
        else
            ERR_print_errors_fp(stderr);
    }
    sd = SSL_get_fd(ssl);       /* get socket connection */
    SSL_free(ssl);         /* release SSL state */
    close(sd);          /* close connection */
}

int main()
{   SSL_CTX *ctx;
    int server;
    char portnum[]="5000";

        char CertFile[] = "sinful-host-cert.pem";
        char KeyFile[] = "sinful-host.key";

    SSL_library_init();

    ctx = InitServerCTX();        /* initialize SSL */
    LoadCertificates(ctx, CertFile, KeyFile); /* load certs */
    server = OpenListener(atoi(portnum));    /* create server socket */

    printf("%d while\n", server);
    while (1)
    {   struct sockaddr_in addr;
        socklen_t len = sizeof(addr);
        SSL *ssl;

        int client = accept(server, (struct sockaddr*)&addr, &len);  /* accept connection as usual */
        printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
        ssl = SSL_new(ctx);              /* get new SSL state with context */
        if (ssl == NULL) {

            ERR_print_errors_fp(stderr);
            return 0;

        }
        SSL_set_fd(ssl, client);      /* set connection socket to SSL state */
        Servlet(ssl);         /* service connection */
    }
    close(server);          /* close server socket */
    SSL_CTX_free(ctx);         /* release context */
}

解决方案

as described here: Elliptic Curve CA Guide...

This page has so many errors and omissions I would discard it. The first red flag is the white text and black background. That tells me someone less experienced is providing the page...

From the page:

openssl ecparam -list-curves

This should be -list_curves, not -list-curves.

From the page:

openssl ecparam -out sinful.key -name sect283k1 -genkey

This should be:

openssl ecparam -param_enc named_curve -out sinful.key -name sect283k1 -genkey

If you don't use a named curve, then you will have lots of problems later, like when a client attempts to connect to the server. Here, named curve is the OID for a curve like secp256k1, and not the domain parameters like p, a, b, G, etc.

The "lots of problems later" is documented at the OpenSSL wiki Elliptic Curve Cryptography, Named Curves. Here are some of the problems you will experience:

  • Client: 139925962778272:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1256:SSL alert number 40
  • Client: 139925962778272:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:596
  • Server: 140339533272744:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1353

Also, for maximum interoperability, you should use secp256k1. A close second is secp521r1.

Also, use of/lack of -*form in the openssl ecparam and openssl req commands are discussed below.


SSL_CTX* InitServerCTX(void) { ... }

This code block has quite a few problems. The most notable is lack of the ECDH callback. Where are you setting the SSL_CTX_set_tmp_ecdh callback (OpenSSL 1.0.1 and below), or where is the call to SSL_CTX_set_ecdh_auto (OpenSSL 1.0.2 and above)?

Others include the default protocol, the default cipher list, weak and wounded ciphers, the inclusion of anonymous protocols, and compression. For a partial example of code to provide a server context, see 'No Shared Cipher' Error with EDH-RSA-DES-CBC3-SHA.


The error occurs at this line I think

if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)

I think that traces back to that defective page you referenced. This:

openssl req -x509 -new -key sinful.key -out sinful-ca.pem -outform PEM -days 3650

Should probably be (note the addition of -keyform)

openssl req -x509 -new -key sinful.key -keyform PEM -out sinful-ca.pem -outform PEM -days 3650

Or

if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_ASN1) <= 0)

In general, always use the *form option for a command, whether its -keyform, -certform, -inform, -outform, etc. OpenSSL does not always get it right (even though its supposed to use PEM by default).


The error occurs at this line I think

if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)

If the private key has a password, then you will need to provide a Password Callback or strip the password from the file.

Its OK to strip the password because there's no difference in storing a plaintext private key; or a encrypted private key with the passphrase in a configuration file next to the key. In both cases, the only effective security you have is the filesystem ACLs.

Related, this is known as the Unattended Key Storage problem. Guttman discusses it in his book Engineering Security. Its a problem without a solution.


Here's some more complete error information... It looks like you are using an old version of OpenSSL, and that does not provide the newer error codes.

When running the program get the following errors:

 140722397161136:error:10071065:elliptic curve routines:func(113):reason(101):ec_lib.c:995
 140722397161136:error:0B080075:x509 certificate routines:func(128):reason(117):x509_cmp.c:346

First, the 0x10071065 error:

$ /usr/local/ssl/macosx-x64/bin/openssl errstr 0x10071065
error:10071065:elliptic curve routines:EC_POINT_cmp:incompatible objects

The 0x10071065 usually means the client and the server are using incompatible EC fields. In this case, you should use either secp256k1 or secp521r1.

Second, the 0x0B080075 error:

$ /usr/local/ssl/macosx-x64/bin/openssl errstr 0x0B080075
error:0B080075:x509 certificate routines:X509_check_private_key:unknown key type

I'm guessing that there's a mismatch in the certificate and private key. But its only a guess. I would (1) clear the named curve issue, (2) clear the sect283k1 issue, and (3) clear the down level library issue (see below). After clearing those issues, then see if this issue remains.


It looks like you are using an old version of OpenSSL, and that does not provide the newer error codes...

Be sure you are running OpenSSL 1.0.0 or above. 0.9.8 had limited EC support, but it was not really cut-in in force until 1.0.0. Better, use OpenSSL 1.0.2.


 OpenSSL_add_all_algorithms();  /* load & register all cryptos, etc. */
 SSL_load_error_strings();   /* load all error messages */

Also see Library Initialization on the OpenSSL wiki.


 if (SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-AES128-GCM-SHA256")

This will get you into trouble on some versions of OS X and iOS due to a bug in the SecureTransport library. Apple only fixed it on some versions of their operating systems.

If you plan on servicing Apple hardwarez, then you will need one additional non-ECDHE-ECDSA cipher. And you need to use the server side context option SSL_OP_SAFARI_ECDHE_ECDSA_BUG.

Related, Apple is pretty bold about not fixing their security bugs. You have the broken ECDHE-ECDSA cipher suites; and gems like CVE-2015-1130 (Hidden Backdoor with Root).


Here's what my ECDH callback looks like in OpenSSL 1.0.1 and below. OpenSSL 1.0.2 should use SSL_CTX_set_ecdh_auto. Its C++ code, but its easy enough to convert back to C code. Also see SL_CTX_set_tmp_ecdh_callback semantics in 1.0.1 on the OpenSSL mailing list.

The code below could be more robust. The callback should fetch the certificate with SSL_get_certificate (not SSL_get_peer_certificate), query the certificate for the EC field, and then provide a temporary key in the appropriate field, like secp256k1 or secp571k1. (It works because my certificates use secp256, and EcdhCallback uses secp256 as its default).

SSL_get_certificate is not documented. But it is used in <openssl src>/apps/s_cb.c. That's the "self documenting" code OpenSSL is famous for.

using SSL_ptr = std::shared_ptr<SSL>;
using SSL_CTX_ptr = std::shared_ptr<SSL_CTX>;

using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>;
using EC_GROUP_ptr = std::unique_ptr<EC_GROUP, decltype(&::EC_GROUP_free)>;
using EC_POINT_ptr = std::unique_ptr<EC_POINT, decltype(&::EC_POINT_free)>;
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
using BIO_MEM_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
using BIO_FILE_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
...

SSL_CTX* CreateServerContext(const string & domain)
{
    const SSL_METHOD* method = SSLv23_server_method();
    ASSERT(method != NULL);

    SSL_CTX_ptr t(SSL_CTX_new(method), ::SSL_CTX_free);
    ASSERT(t.get() != NULL);

    long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
            flags |= SSL_OP_NO_COMPRESSION;
            flags |= SSL_OP_SAFARI_ECDHE_ECDSA_BUG;
            flags |= SSL_OP_CIPHER_SERVER_PREFERENCE;

    SSL_CTX_set_options(t.get(), flags);

    string ciphers = "HIGH:!aNULL:!RC4:!MD5";
    rc = SSL_CTX_set_cipher_list(t.get(), ciphers.c_str());    
    ...

    LogDebug("GetServerContext: setting ECDH callback");
    SSL_CTX_set_tmp_ecdh_callback(t.get(), EcdhCallback);
    ...

    return t.release();
}

EC_KEY* EcdhCallback(SSL *ssl, int is_export, int keylength)
{
    UNUSED(ssl);
    UNUSED(is_export);
    UNUSED(keylength);

    /* This callback is OK, but OpenSSL calls it in a broken fashion. */
    /* With 1.0.1e and 1.0.1f, the value is 1024-bits. That is more   */
    /* appropriate for RSA.... We'll try and rewrite it here.         */
    if (keylength >= 1024)
    {
        keylength = 256;
        LogRelevant("EcdhCallback: field size is wrong, using 256-bit group");
    }

#if defined(ALLOW_ECDH_192_PARAMS)
    if (keylength <= 192 + 4)
        return ECDH192();
#endif

    if (keylength <= 224 + 4)
        return ECDH224();
    else if (keylength <= 256 + 4)
        return ECDH256();
    else if (keylength <= 384 + 4)
        return ECDH384();
    else if (keylength <= 521 + 4)
        return ECDH521();

    return ECDH521();
}

#if defined(ALLOW_ECDH_192_PARAMS)
static EC_KEY* ECDH192()
{
    static EC_KEY_ptr key(NULL, NULL);
    static once_flag flag;

    call_once(flag, []()
    {
        key = EC_KEY_ptr(InitEcdhkey(192), ::EC_KEY_free);
        ASSERT(key.get());

        if(!key.get())
            LogError("ECDH192: InitEcdhkey failed");
    });

    return key.get();
}
#endif

static EC_KEY* ECDH224()
{
    static EC_KEY_ptr key(NULL, NULL);
    static once_flag flag;

    call_once(flag, []()
    {
        key = EC_KEY_ptr(InitEcdhkey(224), ::EC_KEY_free);
        ASSERT(key.get());

        if(!key.get())
            LogError("ECDH224: InitEcdhkey failed");
    });

    return key.get();
}

static EC_KEY* ECDH256()
{
    static EC_KEY_ptr key(NULL, NULL);
    static once_flag flag;

    call_once(flag, []()
    {
        key = EC_KEY_ptr(InitEcdhkey(256), ::EC_KEY_free);
        ASSERT(key.get());

        if(!key.get())
            LogError("ECDH256: InitEcdhkey failed");
    });

    return key.get();
}

static EC_KEY* ECDH384()
{
    static EC_KEY_ptr key(NULL, NULL);
    static once_flag flag;

    call_once(flag, []()
    {
        key = EC_KEY_ptr(InitEcdhkey(384), ::EC_KEY_free);
        ASSERT(key.get());

        if(!key.get())
            LogError("ECDH384: InitEcdhkey failed");
    });

    return key.get();
}

static EC_KEY* ECDH521()
{
    static EC_KEY_ptr key(NULL, NULL);
    static once_flag flag;

    call_once(flag, []()
    {
        key = EC_KEY_ptr(InitEcdhkey(521), ::EC_KEY_free);
        ASSERT(key.get());

        if(!key.get())
            LogError("ECDH521: InitEcdhkey failed");
    });

    return key.get();
}

static EC_KEY* InitEcdhkey(int bits)
{
    if (bits <= 160 + 4)
        bits = 160;
    else if (bits <= 192 + 4)
        bits = 192;
    else if (bits <= 224 + 4)
        bits = 224;
    else if (bits <= 256 + 4)
        bits = 256;
    else if (bits <= 384 + 4)
        bits = 384;
    else if (bits <= 521 + 4)
        bits = 521;
    else
        bits = 521;

    EC_KEY* key = EC_KEY_new_by_curve_name(CurveToNidByBits(bits));
    unsigned long err = ERR_get_error();

    ASSERT(key != NULL);
    if (key == NULL)
    {
        ostringstream oss;
        oss << "InitEcdhkey: EC_KEY_new_by_curve_name failed for ";
        oss << bits << "-bit key, error " << err << ", 0x" << err;
        LogError(oss);
    }

    return key;
}

这篇关于与ECDHE密钥和证书服务器无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆