OpenSSL 客户端未发送客户端证书
我正在努力解决客户端证书问题,希望这里有人可以帮助我.我正在使用 boost asio 开发客户端/服务器对,但我会尽量不具体.我在 Windows 上并使用 openssl 1.0.1e
I am struggling with a client certificate problem and hope somebody here can help me. I'm developing a client/server pair using boost asio but I'll try to be unspecific. I'm on windows and using openssl 1.0.1e
基本上,我想使用客户端证书进行客户端身份验证.服务器应仅接受具有我自己的 CA 签署的证书的客户端.所以我设置了一个自签名 CA.这又颁发了两个证书.一个用于客户端,一个用于服务器.两者均由 CA 签署.我已经这样做了好几次了,我相信我做到了.
Basically, I want to have client authentication by using client certificates. The server shall only accept clients that have a certificate signed by my own CA. So I have setup a self signed CA. This has issued two more certificates. One for the client and one for the server. Both signed by the CA. I have done that quite a few times now and I am confident that I got it.
我的服务器端也可以正常工作.它请求客户端证书,如果我使用 s_client 并提供这些证书,一切正常.此外,如果我使用浏览器并将我的根 CA 安装为受信任的,然后导入客户端证书.
My server side also works fine. It requests client certificates and if I'm using s_client and give those certs everything works. Also if I'm using a browser and have my root CA installed as trusted and then import the client certs.
我唯一不能工作的是 libssl 客户端.它在握手期间总是失败,据我所知,它不会发送客户端证书:
The only thing that I can't get to work is the libssl client. It always fails during the handshake and as far as I can see it will not send the client certficate:
$ 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",则连接有效.所以看起来真的只是客户拒绝发送它的证书.这可能是什么原因?
I'm using this s_server as debugging tool but against my real server the same thing occurs. s_client will work fine with the same certificates. Also, if I disable "-Verify" in the server the connection works. So it really seems just the client refusing to send it's certficate. What can be the reason for that?
由于我使用 boost asio 作为 SSL 包装器,因此代码如下所示:
Since I'm using boost asio as an SSL wrapper the code looks like this:
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 上下文:
I have also tried to bypass asio and access the SSL context directly by saying:
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
}
我看不出行为上有什么不同.应该提到的是,我使用的是一个非常旧的 bo??ost 1.43 asio,我无法更新,但我想所有相关调用或多或少都直接转到 OpenSSL,并且服务器在该版本上运行良好,所以我想我可以排除这种情况.
I can't see any difference in behavior. It should be mentioned that I am using a very old boost 1.43 asio which I cannot update but I suppose all relevant calls go more or less directly to OpenSSL anyway and the server works fine with that version so I think I can rule that out.
如果我开始强制客户端和服务器使用特定版本,错误消息会发生变化,但它永远不会起作用,并且仍然始终适用于 s_client 测试.目前它设置为 TLSv1
If I start forcing client and server to specific versions, the error messages change but it never works and still always works with the s_client test. Currently it is set to TLSv1
如果我将它切换到 TLSv1,例如客户端和服务器之间会有更多的聊天,最终我会收到错误:
If I switch it to TLSv1 for example there is more chatter between client and server and eventually I get the error:
...
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
我在 openssl 邮件列表上发现了一个较旧的错误条目,该条目对此进行了引用.显然,两年前已修复的握手中的错误 CRLF.或者有吗?
I have found an older bug entry posted on the openssl mailing list that refereed to this. Apparently a wrong CRLF in the handshake that has been fixed two yrs ago. Or has it?
我已经调试了将近一个星期,我真的被困住了.有人对尝试什么有建议吗?我没有想法......
I have been debugging this for almost a week now and I'm really stuck. Does anyone have a suggestion on what to try? I'm out of ideas...
干杯,斯蒂芬
PS:这是上面的 s_server 调试结果与 s_client 和相同的证书:
PS: Here is what the above s_server debug out would be with s_client and the same certficate:
$ 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
...握手完成并传输数据.
... handshake completes and data is transferred.
推荐答案
好吧,经过深思熟虑,OpenSSL 的 Dave Thompson 找到了答案.
All right, after much suffering, the answer has been found by Dave Thompson of OpenSSL.
原因是我的 ssl 代码在从它创建套接字对象 (SSL*) 之后调用了 OpenSSL 上下文中的所有这些函数.这意味着所有这些功能实际上什么也没做或做错事.
The reason was that my ssl code called all those functions on the OpenSSL context after the socket object (SSL*) was created from it. Which means all those functions did practically nothing or the wrong thing.
我所要做的就是:
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 似乎鼓励在之后立即创建上下文和套接字(就像我在初始化列表中所做的那样),这些调用几乎毫无用处.
Call the CTX functions upon the context before the socket was created. As asio seemingly encourages to create the context and socket right afterwards (as I did in the initializer list) the calls were all but useless.
SSL 上下文(在 lib OpenSSL 或类似 asio 中)封装了 SSL 使用,并且从它创建的每个套接字都将共享它的属性.
The SSL context (in lib OpenSSL or asio alike) encapsulates the SSL usage and each socket created from it will share it's properties.
谢谢大家的建议.
相关文章