具有ECDHE密钥和证书的服务器无法正常工作

我使用下面的server.c源码,我生成了

 有罪的宿主cert.pem
罪孽深重,host.key 

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

运行程序时会出现以下错误:

140722397161136:错误:10071065:椭圆曲线例程:func(113):reason(101):ec_lib.c:995:140722397161136:错误:0B080075:x509证书例程:func(128):reason(117):x509_cmp.c: 346:

我编译使用:

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

我认为错误发生在这一行

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

server.c

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #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="
%s

\n\n"; printf("huhupre\n"); err = SSL_accept(ssl); if ( err 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 */ }

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

这个页面有很多错误和遗漏我会丢弃它。 第一个红旗是白色文字和黑色背景。 这告诉我没有经验的人提供页面……

从页面:

 openssl ecparam -list-curves 

这应该是-list_curves ,而不是-list-curves

从页面:

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

这应该是:

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

如果您使用命名曲线,那么稍后您将遇到很多问题,例如客户端尝试连接到服务器时。 这里,命名曲线是像secp256k1这样的曲线的OID,而不是pabG等域参数。

“OpenSSL wiki Elliptic Curve Cryptography,Named Curves ”中记录了“以后的许多问题”。 以下是您将遇到的一些问题:

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

此外,为了获得最大的互操作性,您应该使用secp256k1secp521r1secp521r1

此外,在openssl ecparamopenssl req命令中使用/缺少-*form在下面讨论。


 SSL_CTX* InitServerCTX(void) { ... } 

这个代码块有很多问题。 最值得注意的是缺少ECDH回调。 您在哪里设置SSL_CTX_set_tmp_ecdh回调(OpenSSL 1.0.1及更低版本),或者调用SSL_CTX_set_ecdh_auto (OpenSSL 1.0.2及更高版本)的位置?

其他包括默认协议 ,默认密码列表,弱密码和受损密码,包含匿名协议和压缩。 有关提供服务器上下文的代码的部分示例,请参阅EDH-RSA-DES-CBC3-SHA的“无共享密码”错误 。


我认为错误发生在这一行

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

我认为这会追溯到您引用的有缺陷的页面。 这个:

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

应该是(注意添加-keyform

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

要么

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

通常,总是对命令使用*form选项,无论是-keyform-certform-inform-outform-outform并不总是正确(即使它默认使用PEM)。


我认为错误发生在这一行

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

如果私钥有密码,则需要提供密码回调或从文件中删除密码。

可以剥离密码,因为存储明文私钥没有区别; 或密钥旁边的配置文件中带密码的加密私钥。 在这两种情况下,唯一有效的安全性是文件系统ACL。

相关的,这称为无人值守密钥存储问题。 古特曼在他的“ 工程安全”一书中对此进行了讨论 这是一个没有解决方案的问题。


这里有一些更完整的错误信息......看起来你使用的是旧版本的OpenSSL,并没有提供更新的错误代码。

运行程序时会出现以下错误:

  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 

首先, 0x10071065错误:

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

0x10071065通常表示客户端和服务器使用不兼容的EC字段。 在这种情况下,您应该使用secp256k1secp521r1

二, 0x0B080075错误:

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

我猜测证书和私钥不匹配。 但它只是一个猜测。 我会(1)清除命名曲线问题,(2)清除sect283k1问题,(3)清除下级库问题(见下文)。 清除这些问题后,请查看此问题是否仍然存在。


看起来您使用的是旧版本的OpenSSL,并且没有提供更新的错误代码......

确保您运行的是OpenSSL 1.0.0或更高版本。 0.9.8的EC支持有限,但直到1.0.0才真正切入。 更好,使用OpenSSL 1.0.2。


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

另请参阅OpenSSL wiki上的库初始化 。


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

由于SecureTransport库中的错误,这将使您在某些版本的OS X和iOS上遇到麻烦。 Apple仅在某些版本的操作系统上修复了它。

如果您计划维修Apple hardwarez,那么您将需要一个额外的non-ECDHE-ECDSA密码。 您需要使用服务器端上下文选项SSL_OP_SAFARI_ECDHE_ECDSA_BUG

相关的 ,Apple非常大胆地不修复他们的安全漏洞。 你有破碎的ECDHE-ECDSA密码套件; 和gem如CVE-2015-1130(隐藏后门与根) 。


这是我的ECDH回调在OpenSSL 1.0.1及更低版本中的样子。 OpenSSL 1.0.2应该使用SSL_CTX_set_ecdh_auto 。 它的C ++代码,但它很容易转换回C代码。 另请参阅OpenSSL邮件列表中1.0.1中的SL_CTX_set_tmp_ecdh_callback语义 。

下面的代码可能更强大。 回调应该使用SSL_get_certificate而不是 SSL_get_peer_certificate )获取证书,查询EC字段的证书,然后在相应的字段中提供临时密钥,如secp256k1secp571k1 。 (它的工作原理是因为我的证书使用secp256 ,而EcdhCallback使用secp256作为默认值)。

SSL_get_certificate没有记录。 但它在/apps/s_cb.c 。 这就是OpenSSL着名的“自我记录”代码。

 using SSL_ptr = std::shared_ptr; using SSL_CTX_ptr = std::shared_ptr; using EC_KEY_ptr = std::unique_ptr; using EC_GROUP_ptr = std::unique_ptr; using EC_POINT_ptr = std::unique_ptr; using EVP_PKEY_ptr = std::unique_ptr; using BIO_MEM_ptr = std::unique_ptr; using BIO_FILE_ptr = std::unique_ptr; ... 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; }