我使用下面的server.c源码,我生成了
sinful-host-cert.pem sinful-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)
#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 */
}
Run Code Online (Sandbox Code Playgroud)
jww*_*jww 11
如下所述:椭圆曲线CA指南...
这个页面有很多错误和遗漏我会丢弃它.第一个红旗是白色文字和黑色背景.这告诉我一个经验不足的人正在提供这个页面......
从页面:
Run Code Online (Sandbox Code Playgroud)openssl ecparam -list-curves
这应该是-list_curves,而不是-list-curves.
从页面:
Run Code Online (Sandbox Code Playgroud)openssl ecparam -out sinful.key -name sect283k1 -genkey
这应该是:
openssl ecparam -param_enc named_curve -out sinful.key -name sect283k1 -genkey
Run Code Online (Sandbox Code Playgroud)
如果您不使用命名曲线,那么稍后您将遇到很多问题,例如客户端尝试连接到服务器时.在这里,命名曲线是一条曲线,如OID secp256k1,而不是域的参数,如p,a,b,G,等.
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 40139925962778272:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:596140339533272744:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1353此外,为了实现最大的互操作性,您应该使用secp256k1.紧随其后的是secp521r1.
此外,下面讨论-*form在openssl ecparam和openssl req命令中使用/缺少.
Run Code Online (Sandbox Code Playgroud)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的"无共享密码"错误.
我认为错误发生在这一行
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
应该是(注意增加-keyform)
openssl req -x509 -new -key sinful.key -keyform PEM -out sinful-ca.pem -outform PEM -days 3650
Run Code Online (Sandbox Code Playgroud)
要么
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_ASN1) <= 0)
Run Code Online (Sandbox Code Playgroud)
在一般情况下,始终使用*form选择的命令,无论是它的-keyform,-certform,-inform,-outform,等OpenSSL的并不总是正确(即使它应该默认使用PEM).
我认为错误发生在这一行
Run Code Online (Sandbox Code Playgroud)if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)
如果私钥有密码,则需要提供密码回调或从文件中删除密码.
可以剥离密码,因为存储明文私钥没有区别; 或密钥旁边的配置文件中带密码的加密私钥.在这两种情况下,唯一有效的安全性是文件系统ACL.
相关的,这称为无人值守密钥存储问题.格特曼讨论它是在他的书中工程安全.这是一个没有解决方案的问题.
这里有一些更完整的错误信息......看起来你使用的是旧版本的OpenSSL,并没有提供更新的错误代码.
运行程序时会出现以下错误:
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
在0x10071065通常意味着客户端和服务器使用的是不兼容的EC领域.在这种情况下,您应该使用secp256k1或secp521r1.
二,0x0B080075错误:
$ /usr/local/ssl/macosx-x64/bin/openssl errstr 0x0B080075
error:0B080075:x509 certificate routines:X509_check_private_key:unknown key type
Run Code Online (Sandbox Code Playgroud)
我猜测证书和私钥不匹配.但它只是一个猜测.我会(1)清除指定的曲线问题,(2)清除sect283k1问题,(3)清除下级库问题(见下文).清除这些问题后,请查看此问题是否仍然存在.
看起来您使用的是旧版本的OpenSSL,并且没有提供更新的错误代码......
确保您运行的是OpenSSL 1.0.0或更高版本.0.9.8的EC支持有限,但直到1.0.0才真正切入.更好,使用OpenSSL 1.0.2.
Run Code Online (Sandbox Code Playgroud)OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */ SSL_load_error_strings(); /* load all error messages */
另请参阅OpenSSL wiki上的库初始化.
Run Code Online (Sandbox Code Playgroud)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密码套件; 和宝石如CVE-2015-1130(带有Root的隐藏后门).
这是我的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字段的证书,然后在相应的字段中提供临时密钥,例如secp256k1或secp571k1.(它的工作原理,因为我的证书使用secp256,以及EcdhCallback使用secp256它的默认值).
SSL_get_certificate没有记录.但它被用于<openssl src>/apps/s_cb.c.这就是OpenSSL着名的"自我记录"代码.
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;
}
Run Code Online (Sandbox Code Playgroud)