简体   繁体   English

OpenSSL客户端未发送客户端证书

[英]OpenSSL client not sending client certificate

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. 我正在使用boost asio开发客户端/服务器对,但是我会尽量保持不确定。 I'm on windows and using openssl 1.0.1e 我在Windows上并使用openssl 1.0.1e

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. 服务器将仅接受具有由我自己的CA签名的证书的客户端。 So I have setup a self signed CA. 因此,我建立了一个自签名的CA。 This has issued two more certificates. 这又颁发了两个证书。 One for the client and one for the server. 一台用于客户端,一台用于服务器。 Both signed by the CA. 两者均由CA签署。 I have done that quite a few times now and I am confident that I got it. 我已经做过很多次了,我有信心做到了。

My server side also works fine. 我的服务器端也可以正常工作。 It requests client certificates and if I'm using s_client and give those certs everything works. 它会请求客户端证书,如果我使用的是s_client并为这些证书提供一切正常的功能。 Also if I'm using a browser and have my root CA installed as trusted and then import the client certs. 另外,如果我使用的是浏览器并以受信任的方式安装了根CA,然后导入客户端证书。

The only thing that I can't get to work is the libssl client. 我唯一无法工作的是libssl客户端。 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

I'm using this s_server as debugging tool but against my real server the same thing occurs. 我使用此s_server作为调试工具,但对我的真实服务器却发生了同样的事情。 s_client will work fine with the same certificates. s_client使用相同的证书可以正常工作。 Also, if I disable "-Verify" in the server the connection works. 另外,如果我在服务器中禁用“ -Verify”,则连接有效。 So it really seems just the client refusing to send it's certficate. 因此,实际上似乎只是客户端拒绝发送它的证书。 What can be the reason for that? 可能是什么原因呢?

Since I'm using boost asio as an SSL wrapper the code looks like this: 由于我将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 );

I have also tried to bypass asio and access the SSL context directly by saying: 我还尝试绕过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
}

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. 应该提到的是,我使用的是非常老的boost 1.43 asio,我无法更新,但我想所有相关调用还是或多或少直接进入了OpenSSL,并且服务器在该版本上运行良好,所以我认为可以排除这一点。

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. 如果我开始将客户端和服务器强制设置为特定版本,则错误消息会更改,但它永远不会起作用,并且仍然可以与s_client测试一起使用。 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: 例如,如果我将其切换到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

I have found an older bug entry posted on the openssl mailing list that refereed to this. 我发现在opensl邮件列表上发布了一个较早的bug条目,并对此进行了引用。 Apparently a wrong CRLF in the handshake that has been fixed two yrs ago. 显然,两年前已解决的握手CRLF错误。 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... 我没主意...

Cheers, Stephan 干杯,斯蒂芬

PS: Here is what the above s_server debug out would be with s_client and the same certficate: 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

... handshake completes and data is transferred. ...握手完成并传输了数据。

All right, after much suffering, the answer has been found by Dave Thompson of OpenSSL. 好吧,在遭受了很多苦难之后,OpenSSL的Dave Thompson找到了答案。

The reason was that my ssl code called all those functions on the OpenSSL context after the socket object (SSL*) was created from it. 原因是我的ssl代码在创建套接字对象(SSL *)之后在OpenSSL上下文中调用了所有这些函数。 Which means all those functions did practically nothing or the wrong thing. 这意味着所有这些功能实际上什么都不做或做错了事。

All I had to do was either: 我所要做的就是:

1. Call SSL_use_certificate_file 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
}

(notice the missing CTX ) (请注意缺少的CTX

2. Call the CTX functions 2.调用CTX函数

Call the CTX functions upon the context before the socket was created. 在创建套接字之前,请根据上下文调用CTX函数。 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. 正如asio似乎鼓励在此之后立即创建上下文和套接字(就像我在初始化列表中所做的那样),这些调用几乎没有用。

The SSL context (in lib OpenSSL or asio alike) encapsulates the SSL usage and each socket created from it will share it's properties. SSL上下文(在lib OpenSSL或asio中均如此)封装了SSL用法,并且由此创建的每个套接字都将共享其属性。

Thank you guys for your suggestions. 谢谢你们的建议。

You should not use both SSL_CTX_use_certificate_chain_file() and SSL_CTX_use_certificate_file(), as SSL_CTX_use_certificate_chain_file() tries to load a chain including the client certificate, not just the CA chain. 您不应该同时使用SSL_CTX_use_certificate_chain_file()和SSL_CTX_use_certificate_file(),因为SSL_CTX_use_certificate_chain_file()尝试加载包括客户端证书的链,而不仅仅是CA链。 From SSL_CTX_use_certificate(3) : 来自SSL_CTX_use_certificate(3)

SSL_CTX_use_certificate_chain_file() loads a certificate chain from file into ctx. SSL_CTX_use_certificate_chain_file()将证书链从文件加载到ctx。 The certificates must be in PEM format and must be sorted starting with the subject's certificate (actual client or server certificate), followed by intermediate CA certificates if applicable, and ending at the highest level (root) CA. 证书必须为PEM格式,并且必须从主题的证书(实际的客户端或服务器证书)开始,然后是中间的CA证书(如果适用),并以最高级别(根)CA结束。

I think you should be fine using only SSL_CTX_use_certificate_file() and SSL_CTX_use_PrivateKey_file(), as the client does not care much for the CA chain anyway. 我认为您只使用SSL_CTX_use_certificate_file()和SSL_CTX_use_PrivateKey_file()应该会很好,因为客户端无论如何都不太在乎CA链。

I think you need to call SSL_CTX_set_client_CA_list on the server side. 我认为您需要在服务器端调用SSL_CTX_set_client_CA_list This sets a list of certificate authorities to be sent together with the client certificate request. 这将设置要与客户端证书请求一起发送的证书颁发机构列表。

The client will not send its certificate, even if one was requested, if the certificate does not match that CA list sent by the server. 客户端将无法发送其证书,即使一个请求, 如果该证书不匹配由服务器发送的CA列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM