CURLOPT_CAINFO/CAPATH 到底是什么?

What exactly goes into CURLOPT_CAINFO/CAPATH?

尝试使 PHP 中的 CURL 使用自签名证书。我已将证书文件的副本提供给客户端代码,并在 CURLOPT_CAINFOCURLOPT_CAPATH 中指定了证书文件的路径。尽管如此,我还是收到错误 60:SSL certificate problem: unable to get local issuer certificate.


这是重现步骤。全部在 Linux(在我的例子中是 Debian Stretch)。将 example.com 替换为相关的主机名。

首先,我会生成一个私钥:

openssl genrsa -out key.pem 2048

编写配置文件:

[req]
prompt=no
distinguished_name=dn
req_extensions=ext
x509_extensions=ext

[dn]
emailAddress=seva@example.com
CN=example.com
O=Seva Alekseyev
L=Chicago
ST=IL
C=US

[ext]
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=@alt

[alt]
DNS=example.com

另存为req.txt,生成自签名证书:

openssl req -x509 -new -config req.txt -days 3650 -key key.pem -out example.cer

在主机名 example.com 下的 Apache 中安装 example.cerkey.pem。浏览以确保基本设置有效(取模可怕的安全消息)。

现在,客户。在 $path 下放置了一份 example.cer。 PHP 代码为:

$cu = curl_init("https://example.com/");
curl_setopt_array($cu, array(
    CURLOPT_HEADER => false,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CAINFO => "$path/example.cer",
    CURLOPT_CAPATH => "$path/example.cer"
    ));
$r = curl_exec($cu);
$c = curl_errno($cu);
$s = curl_error($cu);
curl_close($cu);

echo "$c $s";

然后是错误信息。

我在这里错过了什么?一些指南建议 CURLOPT_CAINFO/CAPATH 的值应该改为文件夹,基于序列的符号链接指向证书文件。也试过了,同样的错误。 https://curl.haxx.se/docs/sslcerts.html 的文档说:

Get a CA certificate that can verify the remote server and use the proper option to point out this CA cert for verification when connecting.

但是那里没有 CA,没有证书链。签名证书本身。我是否应该以某种方式转换证书,以便 CURL 将其视为 CA 证书?我应该先生成一个伪造的 CA 证书,然后用那个证书签署 SSL 证书吗?


命令行 curl,与 curl --cacert example.cer https://example.com/ 中一样,弹出相同的消息。


相关问题here,但我不想弄乱系统范围的设置。

CURLOPT_CAINFO/CAPATH 指向的证书应该是 CA 证书 - 至少在使用 OpenSSL 时是这样。这意味着您的自签名证书也需要是 CA 证书,即它不仅应该用于 serverAuth,还应该具有基本约束 CA:true.

[ext]下的keyUsage行必须包含keyCertSign,像这样:

keyUsage=digitalSignature,keyEncipherment,keyCertSign

否则,就 OpenSSL 而言,它不是 CA 证书。

OBTW,Steffen 建议的 [ext] 下的 basicConstraints=CA:true 行不是必需的,我已经检查过了。至少对于 CURL 7.52.1 和 OpenSSL 1.0.2r 它不是。

在客户端代码中,CURLOPT_CAPATH也不是必需的。 CURL 支持两种指定根 CA 证书包的替代方法。 CURLOPT_CAINFO 使 CURL 读取并解析单个文件,其中可能包含多个证书。 CURLOPT_CAPATH 使 CURL 扫描一个目录,其中包含由序列号标识的证书文件 - 或者指向这些文件的符号链接,由 c_rehash 生成。由于在我的场景中有效的根 CA 证书包只有一个证书,所以一个文件的方法就足够了。

在 Windows 下不起作用,至少在命令行 CURL 7.55.1 下是这样。 Windows 版本的 CURL 使用内置的 Schannel 库实现 SSL,并忽略 --cacert 选项,而是依赖于 Windows 的内置可信 CA 存储。 See here.

可能可以针对不同的 SSL 实现为 Windows 重建 CURL,但这样的麻烦几乎不值得。 Windows 自带 HTTP(S) 客户端。