尝试使用客户端证书时解决sslv3警报握手失败问题

36

我正在尝试连接一个需要证书授权的服务。流程是我发送一个CSR文件给服务,服务签署CSR并返回一个证书供我用于连接。

  1. 我使用以下命令行生成了CSR:

    openssl req -new -nodes -newkey rsa:2048 -keyout cert.key -out cert.csr
    
  2. 我将cert.csr的内容发送给他们。他们生成了客户端证书,我得到了一个PEM文件。

  3. 现在我尝试使用他们提供的SSL证书文件连接curl(),并将cert.key中的私钥作为CURLOPT_SSLKEY提供-(这是我在步骤1中获得的)。

  4. 出现错误:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure

在这个过程中我做错了什么?

当我使用一个从该服务生成的证书并使用我的私钥作为密钥时,它会出现握手失败的错误。但是,当我尝试使用一个包含来自该服务的测试证书和私钥的证书(自签名证书)时,它可以工作。

所以我知道这与openssl/curl不支持v3/TLS等其他人在寻找解决方案时发现的问题无关。

这是我运行的命令:

  curl -i -v --request POST https://service.com/ --cert clientcert.pem --key private_key.pem --cert-type pem --tlsv1.1 --insecure
* Connected to service.com (1xx.xxx.xxx.xx) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Request CERT (13):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS handshake, CERT verify (15):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS alert, Server hello (2):
* error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
* Closing connection 0

运行以下版本:curl 7.35.0(x86_64-pc-linux-gnu)libcurl/7.35.0 OpenSSL/1.0.1f zlib/1.2.8 libidn/1.28 librtmp/2.3


4
任何私钥都不会被发送到任何地方,更不用说通过SSL发送了。这就是它们被称为私钥的原因。很难理解你在这里具体要求什么。 - user207421
@EJP 我必须发送一个私钥,否则会出现错误“无法设置私钥文件:”。 - Karem
3
在您的端点的SSL配置中,您需要使用您的私钥。您不会在任何时候发送它。SSL也不会发送它。它只是用来签署发送给对等方的证书消息。 - user207421
1
@EJP,我实际上是指“设置”而不是“发送”,谢谢您指出。当我配置CURL使用从他们那里获得的测试证书+测试私钥时,它可以工作。但是当我配置CURL使用从我的CSR + 私钥生成的证书时,它就无法工作了。所以我是否以错误的方式生成了签名的CSR,因为我认为我的错误不匹配? - Karem
根据您用于创建CSR的OpenSSL命令,您的密钥应该叫做***cert.key,而不是private_key.pem***。请修改您的命令:curl ... --key cert.key... - jww
显示剩余5条评论
4个回答

10
这并不是一个明确的答案,但是评论框里放不下这么多东西:
我的假设是他们给了你一个证书,但它可能有错误的颁发者(虽然他们的服务器可能会使用更具体的提示代码)或错误的主题。我们知道该证书与您的私钥匹配——因为两者都没有出现不匹配的情况,但我们实际上并不知道它是否与他们所需的 CA 匹配——因为您的 curl 使用 openssl SSL 客户端时不强制配置的客户端证书与 certreq.CAs 匹配。
运行以下命令: openssl x509 <clientcert.pem -noout -subject -issuer 然后在测试 P12 证书上运行同样的命令。运行以下命令: openssl s_client (或检查您已经运行的命令),并查看“可接受的客户端证书 CA 名称”。那里的名称应该与您证书的颁发者完全匹配(精确匹配!)。如果没有,那很可能是您的问题,并且您需要确认是否以正确的方式将 CSR 提交给了他们。也许他们在不同地区、业务线、测试和生产、激活和待处理等方面有不同的规则。
如果您的证书颁发者与所需的 CA 匹配,请将其主题与工作(测试 P12)的主题进行比较:它们的格式相似吗?工作的证书中是否有任何您的证书中没有的组件?如果允许,尝试生成并提交一个新的 CSR,其主题名称与测试 P12 的完全相同或尽可能接近,并查看是否能够生成一个更好的证书。(您不必为此生成新的密钥,但如果选择这样做,请记住哪些证书与哪些密钥匹配,以免混淆。)如果这样仍然无法解决问题,请使用 openssl x509 <cert -noout -text 查看证书扩展名是否存在与主题授权相关的差异,例如 KeyUsage、ExtendedKeyUsage、Policy、Constraints,甚至是一些非标准内容。
如果其他方法都失败了,请向服务器操作员询问有关该问题的日志记录,或者如果您可以访问,自己查看日志记录。

1
谢谢你的回答。我刚刚检查了一下。发证人和主题在p12格式的测试证书和我正在尝试的“现场”客户端证书中是相同的格式。虽然发证人和主题存在差异,但这仅涉及数据而不是格式/结构。你建议的剩下的就是使用openssl s_client并查看可接受的客户端证书。我将尝试连接并立即进行检查。 - Karem
1
完全就是这样!使用不同的CA名称(测试与生产)! - Karem
@Karem,你是如何解决这个问题的?你所说的是使用了不同域名的 SSL 证书,例如 sub.site.com 和 bus.site.com 吗? - Nono

7

客户端证书应该发送哪个SSL私钥?

不需要发送任何一个 :)

关于客户端证书的吸引人之处之一是它不会做愚蠢的事情,比如将密码等秘密信息明文传输到服务器(HTTP basic_auth)。密码仍然用于解锁客户端证书的密钥,但在交换期间或用于验证客户端时不直接使用密码。

相反,客户端为该会话选择一个临时的随机密钥。然后,客户端使用自己的证书对临时的随机密钥进行签名,并将其发送到服务器(有些手势)。如果坏人拦截了任何内容,由于它是随机的,因此无法在未来使用。它甚至不能用于与服务器的协议第二次运行,因为服务器也会选择一个新的随机值。


失败原因:错误:14094410:SSL例程:SSL3_READ_BYTES:sslv3警报握手失败

使用TLS 1.0及以上版本,并使用服务器名称指示

您没有提供任何代码,因此我不清楚该如何告诉您该怎么做。取而代之的是,这里是OpenSSL命令行测试:

openssl s_client -connect www.example.com:443 -tls1 -servername www.example.com \
    -cert mycert.pem -key mykey.pem -CAfile <certificate-authority-for-service>.pem

您还可以使用-CAfile来避免“verify error:num=20”。例如,参见连接到gateway.sandbox.push.apple.com时出现的“verify error:num=20”


1
感谢您的解释。我还是有些困惑。为了进行测试,我收到了一个 .p12 文件,我提取了其中的证书和私钥文件。当在 curl 中配置 CURLOPT_SSLCERT + CURLOPT_SSLKEY 时,这个方法能够完美地工作(!)。现在对于由他们端生成的真实客户端证书(使用我在我的服务器上生成的 CSR),他们并没有向我提供私钥。那么我想,也许我应该使用生成 CSR 时得到的私钥,并将其放置在 CURLOPT_SSLKEY 中。但是这并没有起作用(这也是我提出这个问题的原因)。 - Karem
现在你和其他人都说私钥根本不应该设置。如果我没有设置私钥(使用CURLOPT_SSLKEY),我会收到错误消息“无法设置私钥文件:”(客户端证书中找不到私钥)。这让我感到非常沮丧,我就是想不出问题出在哪里。 - Karem
我正在使用TLS 1.2(并且如所说,一切都与测试客户端证书+测试私钥配合正常工作)。我觉得我可能漏掉了什么/我理解错了什么。 - Karem
@Karem - 您有从创建CSR时拥有的私钥。它不在客户端证书中,因为证书只有公钥。根据您展示的OpenSSL命令:***-keyout cert.key***。请检查文件系统以获取 cert.key - jww
OpenSSL 1.0.1及更高版本通过TLS1.2自动协商SSL3;错误消息中的SSL3_READ_BYTES是一个共享例程名称,并不意味着正在使用SSL3。-tls1实际上限制了您只能使用1.0版本;如果您需要任何选项,应该使用-no_ssl3,它允许1.0到1.2版本。curl会自动执行SNI,尽管openssl s_client需要使用-servername(此外,我们现在看到警报是在第二次通信之后,在服务器证书被正确选择和验证之后)。 - dave_thompson_085
显示剩余2条评论

3
在我的CentOS 8系统中解决问题的方法是通过验证/etc/crypto-policies/config读取默认值DEFAULT而不是其他任何值来检查系统加密策略。 一旦将此值更改为DEFAULT,运行以下命令:
/usr/bin/update-crypto-policies --set DEFAULT

重新运行curl命令,应该就可以正常工作了。

0
在我的情况下,错误的原因是我只导入了证书(例如cert1.pem)和私钥(例如privkey1.pem)到密钥库中,为了解决这个问题,我还需要导入完整的证书链。
我从“Let's Encrypt”认证机构获得了以下文件:
privkey1.pem
fullchain1.pem
chain1.pem
cert1.pem

所以我做了以下操作:

  1. 将三个证书文件的内容合并到一个名为 all.pem 的文件中:
cat cert1.pem chain1.pem fullchain1.pem > all.pem

2. 创建包含完整证书链的密钥库:
openssl pkcs12 -export -in all.pem -inkey privkey1.pem -out my_keystore.p12 -name mycertalias -CAfile chain1.pem -caname root

你是指服务器机器的密钥库吗?还是客户端机器上的? - Elouan Keryell-Even
服务器的密钥库。继续使用我在答案中提供的示例命令,“my_keystore.p12”是服务器使用的密钥库文件,以向客户端提供HTTPS功能。在我的情况下,它是一个Java servlet容器(Jetty)。 - Víctor Gil
顺便说一下,我知道这个问题是关于使用客户端证书来访问服务器的,但我遇到的错误是相同的,因此即使认证的“方向”相反,这可能对某人有所帮助。 - Víctor Gil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接