以 PHP SSL 上下文选项可接受的格式导出 HTTPS 证书"cafile"



如何以 PHP SSL 上下文选项可接受的格式导出证书cafile

我下面的代码使用openssl_x509_exportstackoverflow.com的证书链导出到文件中。该代码基于如何在PHP中使用CURL获取SSL证书信息?

$context = stream_context_create(["ssl" => ["capture_peer_cert_chain" => true]]);
$h = stream_socket_client(
"ssl://stackoverflow.com:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT,
$context);
$params = stream_context_get_params($h);
$ca = null;
foreach ($params["options"]["ssl"]["peer_certificate_chain"] as $cert)
{
openssl_x509_export($cert, $output);
$ca .= $output;
}
file_put_contents("cafile", $ca);

(我知道没有必要导出整个链。


然后,我尝试使用以下代码片段使用导出的文件连接到同一主机以验证证书。但代码失败并显示:

PHP 警告:file_get_contents((:SSL 操作失败,代码为 1。OpenSSL 错误消息:

错误:14090086:SSL 例程:SSL3_GET_SERVER_CERTIFICATE:证书验证失败/path/test.php 第 22 行

$context = stream_context_create(["ssl" => ["cafile" => "cafile"]]);
file_get_contents("https://stackoverflow.com/", false, $context);

文件格式有什么问题?

有趣的是,wget对导出的证书感到满意,则以下成功:

wget --ca-certificate=cafile https://sourceforge.net/

导出为:

-----BEGIN CERTIFICATE-----
MIIHJDCCBgygAwIBAgISA76ZNTywMU3d6zogRjuFkuFnMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA2MDgxNjI0MDBaFw0y
MDA5MDYxNjI0MDBaMB4xHDAaBgNVBAMMEyouc3RhY2tleGNoYW5nZS5jb20wggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp1azQ2CMWUtXrIZDFim7tu2xF
NFYSLt+lIjyZJ1MeCJkljjaiNB02guKeFRSuPsMk2J3Xz8OmOoEfbvVQejMcaw6i
BVP1V+QhF+JhgyOORQkZQV8JJ58IcOJ1PwokyMCAMOWvdxHzaj73jXsxC+n/+MU7
yuq+Y2LuZE1hQYtftYia/e0QtnPDUlpnjUSd7YEDvAzNnKY3+r1smFOHChDjAxMs
W+lJfKLscNKspncihsx16JhZcGZbXiPPOd90B/zA5xNFOCUolldtMAdxFHKoKYpN
mgWrQwVvDB7VCGAQ3CiNGRvdcMa/TpB4SVsZc07C6TvD35dr0wLoZN4WqKfrAgMB
AAGjggQuMIIEKjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG
CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFM73q8X//0OoBswZWBlG
kTPmW5HPMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUF
BwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNy
eXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNy
eXB0Lm9yZy8wggHkBgNVHREEggHbMIIB14IPKi5hc2t1YnVudHUuY29tghIqLmJs
b2dvdmVyZmxvdy5jb22CEioubWF0aG92ZXJmbG93Lm5ldIIYKi5tZXRhLnN0YWNr
ZXhjaGFuZ2UuY29tghgqLm1ldGEuc3RhY2tvdmVyZmxvdy5jb22CESouc2VydmVy
ZmF1bHQuY29tgg0qLnNzdGF0aWMubmV0ghMqLnN0YWNrZXhjaGFuZ2UuY29tghMq
LnN0YWNrb3ZlcmZsb3cuY29tghUqLnN0YWNrb3ZlcmZsb3cuZW1haWyCDyouc3Vw
ZXJ1c2VyLmNvbYINYXNrdWJ1bnR1LmNvbYIQYmxvZ292ZXJmbG93LmNvbYIQbWF0
aG92ZXJmbG93Lm5ldIIUb3BlbmlkLnN0YWNrYXV0aC5jb22CD3NlcnZlcmZhdWx0
LmNvbYILc3N0YXRpYy5uZXSCDXN0YWNrYXBwcy5jb22CDXN0YWNrYXV0aC5jb22C
EXN0YWNrZXhjaGFuZ2UuY29tghJzdGFja292ZXJmbG93LmJsb2eCEXN0YWNrb3Zl
cmZsb3cuY29tghNzdGFja292ZXJmbG93LmVtYWlsghFzdGFja3NuaXBwZXRzLm5l
dIINc3VwZXJ1c2VyLmNvbTBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLf
EwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCC
AQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1AF6nc/nfVsDntTZIfdBJ4DJ6kZoMhKES
EoQYdZaBcUVYAAABcpT136gAAAQDAEYwRAIhAP68FqsHN7WAMY3gio0PiAX1o0ID
d9G35wA7pgn/OetjAh9f9JkgTdN4U58fsIVcZAPstOIoKOblbzAof32fixlAAHUA
B7dcG+V9aP/xsMYdIxXHuuZXfFeUt2ruvGE6GmnTohwAAAFylPXfyQAABAMARjBE
AiArXFv1bOQ2wDYjJvVjSm13AnPCQmbkRRgw6YEaSDhP7gIgJQPAbeBEhu44aJQe
Ih4j33U59OY4Ux2f02dWw3oqhNIwDQYJKoZIhvcNAQELBQADggEBAHyW5+Ss+0vt
jKPFOL9atcI3WZYlwp7Djg3dXByWFBgVEzhrRGGJKEZyK9N178cG9zGXXXbxrxGY
L81pxfZNGiPBYueQ191bTKRS0/9NyCfQhSjFG9SUy7BHDC8OTX5OxEZfnG4wglCf
vPcBkdZ49b0TsTiDrRfCltQvqkeY+/6CfJnayFV/RuZcfRV2bcnu2lLE3da57mA0
TEc9hSW/AefP5ad4noqhSsPISPPbo8S1U4JAFnn1M0KnoEKr5/awHenQeqKgPSKr
DmtDcoy8Tf0RpqSMUwIKqBZEERthO6FC506E64wcuYww3EqA7Um7MM1MkQ8hosFN
WK9ZLI8ylk4=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----

一些背景:我正在使用 https://curl.se/docs/caextract.html 的证书包。

由于某种原因,PHP 无法验证"USERTrust RSA 证书颁发机构"颁发的证书,尽管根证书似乎存在于捆绑包中。例如,证书本身sectigo.com。这是我尝试找到根本原因或解决方法的一部分。

stackoverflow 仅提供服务器证书和中间 CA 证书,而不是根 CA 证书。

OpenSSL CAFILE 仅在完整链(到自签名根 CA(可以通过验证的情况下工作,

因此,您要么必须通过下载来信任自己的根 CA,要么使用某种 PKI 以便拥有所有全局受信任的根 CA。

在基于 Debian GNU/Linux 的机器上,您可以安装 ca-证书包,sudo apt-get install ca-certificates

或者你可以使用Mozilla https://curl.haxx.se/docs/caextract.html 的cacert

  1. 从这里下载 CA 证书 https://curl.haxx.se/ca/cacert.pem
  2. 执行以下代码:
<?php
$context = stream_context_create([
"ssl" => [
"cafile" => "cacert.pem"
]
]);
file_get_contents("https://stackoverflow.com/", false, $context);
  1. 您不会看到任何错误

解释: 关于文档cafile接受Certificate Authority file但您尝试放置链证书

可能有误解。stream_context_create()是创建一个TCP服务器。它可以处理握手,但要做到这一点,必须有一个完整的密钥环,格式如下:

这是一个由openssl生成的.crt和私有.key文件,连接在一个文件中。

-----BEGIN CERTIFICATE-----
MIIDaDCCAlCgAwIB(...)
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAK(...)
-----END RSA PRIVATE KEY-----

有了这样的密钥环,我能够实现 ssl/https 并保护 websockets 服务器 wss://。

不确定您要做什么,为什么 file_get_contents(( 在没有上下文的情况下不起作用。通常您不必导入任何 CAcert,如果是这样,它属于浏览器。

这在企业环境中非常有用,或者可以避免self_signed密钥环的错误。

因此,只是:

echo file_get_contents("https://stackoverflow.com/");

wget https://sourceforge.net/

它只是工作没有麻烦。

请准确地解释您要实现的目标,从有关 ssl 和 php 的实验中获得了许多笔记。

要提取给定服务器的公钥,您必须在端口 443 上进行查询:

<?php
$opt = [
"capture_peer_cert" => true,
"capture_peer_cert_chain" => true
];
$a = stream_context_create(["ssl"=>$opt]);
$b = stream_socket_client("tls://stackoverflow.com:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $a);
$cont = stream_context_get_params($b);
$key = openssl_pkey_get_public($cont["options"]["ssl"]["peer_certificate"]);
$c = openssl_pkey_get_details($key);
var_dump($c["key"]);

输出

string(451) "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqdWs0NgjFlLV6yGQxYpu
7btsRTRWEi7fpSI8mSdTHgiZJY42ojQdNoLinhUUrj7DJNid18/DpjqBH271UHoz
HGsOogVT9VfkIRfiYYMjjkUJGUFfCSefCHDidT8KJMjAgDDlr3cR82o+9417MQvp
//jFO8rqvmNi7mRNYUGLX7WImv3tELZzw1JaZ41Ene2BA7wMzZymN/q9bJhThwoQ
4wMTLFvpSXyi7HDSrKZ3IobMdeiYWXBmW14jzznfdAf8wOcTRTglKJZXbTAHcRRy
qCmKTZoFq0MFbwwe1QhgENwojRkb3XDGv06QeElbGXNOwuk7w9+Xa9MC6GTeFqin
6wIDAQAB
-----END PUBLIC KEY-----
"

也许这个答案会有所帮助

最新更新