[英]OpenSSL client not sending client certificate
我正在努力解决客户证书问题,希望这里有人可以帮助我。 我正在使用boost asio开发客户端/服务器对,但是我会尽量保持不确定。 我在Windows上并使用openssl 1.0.1e
基本上,我想使用客户端证书进行客户端身份验证。 服务器将仅接受具有由我自己的CA签名的证书的客户端。 因此,我建立了一个自签名的CA。 这又颁发了两个证书。 一台用于客户端,一台用于服务器。 两者均由CA签署。 我已经做过很多次了,我有信心做到了。
我的服务器端也可以正常工作。 它会请求客户端证书,如果我使用的是s_client并为这些证书提供一切正常的功能。 另外,如果我使用的是浏览器并以受信任的方式安装了根CA,然后导入客户端证书。
我唯一无法工作的是libssl客户端。 它总是在握手过程中失败,据我所知,它不会发送客户端证书:
$ openssl.exe s_server -servername localhost -bugs -CAfile myca.crt -cert server.crt
-cert2 server.crt -key private/server.key -key2 private/server.key -accept 8887 -www
-state -Verify 5
verify depth is 5, must return a certificate
Setting secondary ctx parameters
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
SSL3 alert read:warning:no certificate
SSL3 alert write:fatal:handshake failure
SSL_accept:error in SSLv3 read client certificate B
SSL_accept:error in SSLv3 read client certificate B
2675716:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a
certificate:s3_srvr.c:3193:
ACCEPT
我使用此s_server作为调试工具,但对我的真实服务器却发生了同样的事情。 s_client使用相同的证书可以正常工作。 另外,如果我在服务器中禁用“ -Verify”,则连接有效。 因此,实际上似乎只是客户端拒绝发送它的证书。 可能是什么原因呢?
由于我将boost asio用作SSL包装器,因此代码如下所示:
m_ssl_context.set_verify_mode( asio::ssl::context::verify_peer );
m_ssl_context.load_verify_file( "myca.crt" );
m_ssl_context.use_certificate_file( "testclient.crt", asio::ssl::context::pem );
m_ssl_context.use_private_key_file( "testclient.key", asio::ssl::context::pem );
我还尝试绕过asio并通过说直接访问SSL上下文:
SSL_CTX *ctx = m_ssl_context.impl();
SSL *ssl = m_ssl_socket.impl()->ssl;
int res = 0;
res = SSL_CTX_use_certificate_chain_file(ctx, "myca.crt");
if (res <= 0) {
// handle error
}
res = SSL_CTX_use_certificate_file(ctx, "testclient.crt", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
res = SSL_CTX_use_PrivateKey_file(ctx, "testclient.key", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
我看不出任何行为上的差异。 应该提到的是,我使用的是非常老的boost 1.43 asio,我无法更新,但我想所有相关调用还是或多或少直接进入了OpenSSL,并且服务器在该版本上运行良好,所以我认为可以排除这一点。
如果我开始将客户端和服务器强制设置为特定版本,则错误消息会更改,但它永远不会起作用,并且仍然可以与s_client测试一起使用。 当前将其设置为TLSv1
例如,如果我将其切换到TLSv1,则客户端和服务器之间会有更多的chat不休,最终我收到错误消息:
...
SSL_accept:SSLv3 read client key exchange A
<<< TLS 1.0 ChangeCipherSpec [length 0001]
01
<<< TLS 1.0 Handshake [length 0010], Finished
14 00 00 0c f4 71 28 4d ab e3 dd f2 46 e8 8b ed
>>> TLS 1.0 Alert [length 0002], fatal unexpected_message
02 0a
SSL3 alert write:fatal:unexpected_message
SSL_accept:failed in SSLv3 read certificate verify B
2675716:error:140880AE:SSL routines:SSL3_GET_CERT_VERIFY:missing verify
message:s3_srvr.c:2951:
2675716:error:140940E5:SSL routines:SSL3_READ_BYTES:ssl handshake failure:s3_pkt.c:989:
ACCEPT
我发现在opensl邮件列表上发布了一个较早的bug条目,并对此进行了引用。 显然,两年前已解决的握手CRLF错误。 还是有?
我已经调试了将近一个星期,我真的很困。 有人对尝试什么有建议吗? 我没主意...
干杯,斯蒂芬
PS:这是上面的s_server调试出来的结果,它将使用s_client和相同的证书进行:
$ openssl s_client -CAfile ca.crt -cert testclient.crt -key private/testclient.key -verify 2 -connect myhost:8887
ACCEPT
SSL_accept:before/accept initialization
SSL_accept:SSLv3 read client hello A
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write key exchange A
SSL_accept:SSLv3 write certificate request A
SSL_accept:SSLv3 flush data
depth=1 C = DE, // further info
verify return:1
depth=0 C = DE, // further info
verify return:1
SSL_accept:SSLv3 read client certificate A
SSL_accept:SSLv3 read client key exchange A
SSL_accept:SSLv3 read certificate verify A
SSL_accept:SSLv3 read finished A
SSL_accept:SSLv3 write session ticket A
SSL_accept:SSLv3 write change cipher spec A
SSL_accept:SSLv3 write finished A
SSL_accept:SSLv3 flush data
ACCEPT
...握手完成并传输了数据。
好吧,在遭受了很多苦难之后,OpenSSL的Dave Thompson找到了答案。
原因是我的ssl代码在创建套接字对象(SSL *)之后在OpenSSL上下文中调用了所有这些函数。 这意味着所有这些功能实际上什么都不做或做错了事。
我所要做的就是:
1.调用SSL_use_certificate_file
res = SSL_use_certificate_file(ssl, "testclient.crt", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
res = SSL_use_PrivateKey_file(ssl, "testclient.key", SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
}
(请注意缺少的CTX )
2.调用CTX函数
在创建套接字之前,请根据上下文调用CTX函数。 正如asio似乎鼓励在此之后立即创建上下文和套接字(就像我在初始化列表中所做的那样),这些调用几乎没有用。
SSL上下文(在lib OpenSSL或asio中均如此)封装了SSL用法,并且由此创建的每个套接字都将共享其属性。
谢谢你们的建议。
您不应该同时使用SSL_CTX_use_certificate_chain_file()和SSL_CTX_use_certificate_file(),因为SSL_CTX_use_certificate_chain_file()尝试加载包括客户端证书的链,而不仅仅是CA链。 来自SSL_CTX_use_certificate(3) :
SSL_CTX_use_certificate_chain_file()将证书链从文件加载到ctx。 证书必须为PEM格式,并且必须从主题的证书(实际的客户端或服务器证书)开始,然后是中间的CA证书(如果适用),并以最高级别(根)CA结束。
我认为您只使用SSL_CTX_use_certificate_file()和SSL_CTX_use_PrivateKey_file()应该会很好,因为客户端无论如何都不太在乎CA链。
我认为您需要在服务器端调用SSL_CTX_set_client_CA_list
。 这将设置要与客户端证书请求一起发送的证书颁发机构列表。
客户端将无法发送其证书,即使一个请求, 如果该证书不匹配由服务器发送的CA列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.