我需要确保SMTP服务器证书由公共证书颁发机构签名。我想使用phpseclib或其他一些受信任的库。我相信我可以使用从Firefox中提取的the root certificates。
有一些home-brew approaches here来检查证书日期和其他元数据,但它看起来不像任何签名检查那样(除了确保OpenSSL这样做)。无论如何,我想使用一个库 - 我想写尽可能少的证书处理代码,因为我不是一个密码学家。
也就是说,上面链接的答案仍然非常有用,因为它帮助我获取了一些代码来从TLS对话中获取证书:
$url = "tcp://{$domain}:{$port}";
$connection_context_option = [
'ssl' => [
'capture_peer_cert' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
]
];
$connection_context = stream_context_create($connection_context_option);
$connection_client = stream_socket_client($url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $connection_context);
stream_set_timeout($connection_client, 2);
fread($connection_client, 10240);
fwrite($connection_client,"HELO alice\r\n");
fread($connection_client, 10240);
fwrite($connection_client, "STARTTLS\r\n");
fread($connection_client, 10240);
$ok = stream_socket_enable_crypto($connection_client, TRUE, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
if ($ok === false)
{
return false;
}
$connection_info = stream_context_get_params($connection_client);
openssl_x509_export($info["options"]["ssl"]["peer_certificate"], $pem_encoded);
(请注意,我已经故意关闭了证书验证。这是因为我无法控制运行它的主机,并且它们的证书可能已经过旧或配置错误。因此,无论验证如何,我都希望获取证书在我正在使用的连接上,然后使用我将提供的cacert.pem
自行验证。)
那会给我这样的证明。这个适用于smtp.live.com:587
的Microsoft Live.com电子邮件服务器:
-----BEGIN CERTIFICATE-----
MIIG3TCCBcWgAwIBAgIQAtB7LVsRCmgbyWiiw7Sf5jANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTcwOTEzMDAwMDAwWhcN
MTkwOTEzMTIwMDAwWjBqMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
aW9uMRQwEgYDVQQDEwtvdXRsb29rLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAIz2tovvgBmK4sOHgpyzCdtXrI0XOujctf6LHMj16wzUnMEatioS
tH0Pz0dKkCr/0yd9qtXbGhD1o6WhFsd7k651K9MZ98+uQ29SzTIAl6y1gkaBbp4h
MFXcE5EpRNHHmK8t2OR7hzmrvvNr6OTYv7BhVCw9pSrQqEFNno0K2TQRhAD9uzrL
OY+rBBVedCXWXH7uhZoZ6joUU7CEA5pPMzKPL1ro+Eorc8vt5FYOC+oAT587+b1M
z+jbZVQlq0qaMkBKRtUIII78MYY0n8DopGqHyzwqWoGySHJNC8256q+MwsZQvvQ3
vmy/rf61h2sg1tU0s7O88Yufxp0LSaMMzZcCAwEAAaOCA5owggOWMB8GA1UdIwQY
MBaAFA+AYRyCMWHVLyjnjUY4tCzhxtniMB0GA1UdDgQWBBT7hLoZ/03rqwcslIc2
0k0z2R+vNTCCAdwGA1UdEQSCAdMwggHPggtvdXRsb29rLmNvbYIWKi5jbG8uZm9v
dHByaW50ZG5zLmNvbYIWKi5ucmIuZm9vdHByaW50ZG5zLmNvbYIgYXR0YWNobWVu
dC5vdXRsb29rLm9mZmljZXBwZS5uZXSCG2F0dGFjaG1lbnQub3V0bG9vay5saXZl
Lm5ldIIdYXR0YWNobWVudC5vdXRsb29rLm9mZmljZS5uZXSCHWNjcy5sb2dpbi5t
aWNyb3NvZnRvbmxpbmUuY29tgiFjY3Mtc2RmLmxvZ2luLm1pY3Jvc29mdG9ubGlu
ZS5jb22CC2hvdG1haWwuY29tgg0qLmhvdG1haWwuY29tggoqLmxpdmUuY29tghZt
YWlsLnNlcnZpY2VzLmxpdmUuY29tgg1vZmZpY2UzNjUuY29tgg8qLm9mZmljZTM2
NS5jb22CFyoub3V0bG9vay5vZmZpY2UzNjUuY29tgg0qLm91dGxvb2suY29tghYq
LmludGVybmFsLm91dGxvb2suY29tggwqLm9mZmljZS5jb22CEm91dGxvb2sub2Zm
aWNlLmNvbYIUc3Vic3RyYXRlLm9mZmljZS5jb22CGHN1YnN0cmF0ZS1zZGYub2Zm
aWNlLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
AQUFBwMCMGsGA1UdHwRkMGIwL6AtoCuGKWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
bS9zc2NhLXNoYTItZzEuY3JsMC+gLaArhilodHRwOi8vY3JsNC5kaWdpY2VydC5j
b20vc3NjYS1zaGEyLWcxLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwBATAqMCgG
CCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAEC
AjB8BggrBgEFBQcBAQRwMG4wJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
ZXJ0LmNvbTBGBggrBgEFBQcwAoY6aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
L0RpZ2lDZXJ0U0hBMlNlY3VyZVNlcnZlckNBLmNydDAMBgNVHRMBAf8EAjAAMA0G
CSqGSIb3DQEBCwUAA4IBAQA3zjN7I6jTeL+08nhG5eAY0q4pLY40bCQHqONBLSI3
uRmQFUfrQOPYBqLC1QU+J2Z2HcX7YiqE3WAR3ODS9g2BAVXkKOQKNBnr2hKwueOz
qPwyvTyzcIQYUw+SrTX+bfJwYMTmZvtP9S7/pB1jPhrV7YGsD55AI9bGa9cmH7VQ
OiL1p5Qovg5KRsldoZeC04OF/UQIR1fv47VGptsHHGypvSo1JinJFQMXylqLIrUW
lV66p3Ui7pFABGc/Lv7nOyANXfLugBO8MyzydGA4NRGiS2MbGpswPCg154pWausU
M0qaEPsM2o3CSTfxSJQQIyEe+izV3UQqYSyWkNqCCFPN
-----END CERTIFICATE-----
好的,太好了。所以我想针对任何公共CA验证这一点。我相信这是一个有效的证书,并使用this checking service正确验证了链:
Array
(
[name] => /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=outlook.com
[subject] => Array
(
[C] => US
[ST] => Washington
[L] => Redmond
[O] => Microsoft Corporation
[CN] => outlook.com
)
[hash] => a3c08ece
[issuer] => Array
(
[C] => US
[O] => DigiCert Inc
[CN] => DigiCert SHA2 Secure Server CA
)
[version] => 2
[serialNumber] => 3740952067977374966703603448215281638
[serialNumberHex] => 02D07B2D5B110A681BC968A2C3B49FE6
[validFrom] => 170913000000Z
[validTo] => 190913120000Z
[validFrom_time_t] => 1505260800
[validTo_time_t] => 1568376000
[signatureTypeSN] => RSA-SHA256
[signatureTypeLN] => sha256WithRSAEncryption
[signatureTypeNID] => 668
[purposes] => Array
(
[1] => Array
(
[0] => 1
[1] =>
[2] => sslclient
)
[2] => Array
(
[0] => 1
[1] =>
[2] => sslserver
)
[3] => Array
(
[0] => 1
[1] =>
[2] => nssslserver
)
[4] => Array
(
[0] =>
[1] =>
[2] => smimesign
)
[5] => Array
(
[0] =>
[1] =>
[2] => smimeencrypt
)
[6] => Array
(
[0] =>
[1] =>
[2] => crlsign
)
[7] => Array
(
[0] => 1
[1] => 1
[2] => any
)
[8] => Array
(
[0] => 1
[1] =>
[2] => ocsphelper
)
[9] => Array
(
[0] =>
[1] =>
[2] => timestampsign
)
)
[extensions] => Array
(
[authorityKeyIdentifier] => keyid:0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2
[subjectKeyIdentifier] => FB:84:BA:19:FF:4D:EB:AB:07:2C:94:87:36:D2:4D:33:D9:1F:AF:35
[subjectAltName] => DNS:outlook.com, DNS:*.clo.footprintdns.com, DNS:*.nrb.footprintdns.com, DNS:attachment.outlook.officeppe.net, DNS:attachment.outlook.live.net, DNS:attachment.outlook.office.net, DNS:ccs.login.microsoftonline.com, DNS:ccs-sdf.login.microsoftonline.com, DNS:hotmail.com, DNS:*.hotmail.com, DNS:*.live.com, DNS:mail.services.live.com, DNS:office365.com, DNS:*.office365.com, DNS:*.outlook.office365.com, DNS:*.outlook.com, DNS:*.internal.outlook.com, DNS:*.office.com, DNS:outlook.office.com, DNS:substrate.office.com, DNS:substrate-sdf.office.com
[keyUsage] => Digital Signature, Key Encipherment
[extendedKeyUsage] => TLS Web Server Authentication, TLS Web Client Authentication
[crlDistributionPoints] =>
Full Name:
URI:http://crl3.digicert.com/ssca-sha2-g1.crl
Full Name:
URI:http://crl4.digicert.com/ssca-sha2-g1.crl
[certificatePolicies] => Policy: 2.16.840.1.114412.1.1
CPS: https://www.digicert.com/CPS
Policy: 2.23.140.1.2.2
[authorityInfoAccess] => OCSP - URI:http://ocsp.digicert.com
CA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt
[basicConstraints] => CA:FALSE
)
)
以下是我尝试验证phpseclib中的sig的方法:
$x509 = new \phpseclib\File\X509();
// From the Mozilla bundle (getPublicCaCerts splits them with a regex)
$splitCerts = getPublicCaCerts(file_get_contents('cacert.pem'));
// Load the certs separately
$caStatus = true;
foreach ($splitCerts as $caCert)
{
$caStatus = $caStatus && $x509->loadCA($caCert);
}
// $caStatus is now true, so all good here
$certData = $x509->loadX509($pem_encoded); // From the TLS server
$valid = $x509->validateSignature();
// $valid is now false
这会返回false
,这不是我所期望的。我想知道我的输入格式是否正确? CA的加载和被测试的证书似乎返回了良好的值。不幸的是,phpseclib文档对示例有点轻松,而且我在网络上的其他地方找不到太多。
除此之外:我怀疑this library可以帮助我,假设它具有验证证书的功能。但是,我认为它试图为我的情况做很多事情 - 我希望我的软件在共享主机上运行,而自动下载感觉就像另一个可能失败的移动部分。我宁愿部署自己的包,提供公共CA信息作为(大)参数,并运行验证测试 in situ 。 phpseclib可能是完美的,只要我能找出输入格式!
我已将问题缩小到phpseclib验证器中的search loop。在L2156上,我们有这段代码:
case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']:
常量确实未定义,因此测试确实是CA是否可以匹配正确的证书细节。证书有这个元数据:
id-at-countryName = US
id-at-organizationName = DigiCert Inc
id-at-organizationalUnitName = www.digicert.com
id-at-commonName = DigiCert SHA2 High Assurance Server CA
对于所有当前匹配的证书,我在最新的证书捆绑包中只有这些值(即如果没有找到公共名称DigiCert SHA2 High Assurance Server CA
,则以下所有值都匹配):
id-at-commonName = DigiCert Assured ID Root CA
id-at-commonName = DigiCert High Assurance EV Root CA
id-at-commonName = DigiCert Assured ID Root G2
id-at-commonName = DigiCert Assured ID Root G3
id-at-commonName = DigiCert Global Root G2
id-at-commonName = DigiCert Global Root G3
id-at-commonName = DigiCert Trusted Root G4
因此,系统甚至不能作为数字签名检查,因为它找不到与该证书相对应的CA.我错过了什么?这个简单的任务应该比这容易多了!
我推测邮件服务器证书不在Mozilla捆绑包中,因为Web浏览器不需要它们。我会假设我的GNU / Linux Mint安装上的证书是最新的并且适用于此目的,因为操作系统应该能够验证邮件服务器中使用的证书。
因此我尝试了这段代码,它将所有系统证书加载到phpseclib:
$certLocations = openssl_get_cert_locations();
$dir = $certLocations['default_cert_dir'];
$glob = $dir . '/*';
echo "Finding certs: " . $dir . "\n";
$x509 = new \phpseclib\File\X509();
foreach (glob($glob) as $certPath)
{
// Change this so it is recursive?
if (is_file($certPath))
{
$ok = $x509->loadCA(file_get_contents($certPath));
if (!$ok)
{
echo sprintf("CA cert `%s` is invalid\n", $certPath);
}
}
}
// The 'getCertToTest' func just gets the live.com cert as a string
$data = $x509->loadX509(getCertToTest());
if (!$data)
{
echo "Cert is invalid\n";
exit();
}
$valid = $x509->validateSignature();
echo sprintf("Validation: %s\n", $valid ? 'Yes' : 'No');
不幸的是,这也失败了。
我已经在我的系统上发出了此命令,并且验证了远程TLS证书。我不太了解phpseclib代码,但看起来它没有做任何链接,这显然是必要的。
openssl s_client -connect smtp.live.com:25 -starttls smtp
CONNECTED(00000003)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert Cloud Services CA-1
verify return:1
depth=0 C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = outlook.com
verify return:1
---
Certificate chain
0 s:/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=outlook.com
i:/C=US/O=DigiCert Inc/CN=DigiCert Cloud Services CA-1
1 s:/C=US/O=DigiCert Inc/CN=DigiCert Cloud Services CA-1
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIG/jCCBeagAwIBAgIQDs2Q7J6KkeHe1d6ecU8P9DANBgkqhkiG9w0BAQsFADBL
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSUwIwYDVQQDExxE
aWdpQ2VydCBDbG91ZCBTZXJ2aWNlcyBDQS0xMB4XDTE3MDkxMzAwMDAwMFoXDTE4
MDkxMzEyMDAwMFowajELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
(snipped, see other code block)
nGhseM2tJfwa2HMwUpuuo5029u4Dd40qvD0cMz33cOvBLRGkTPbXCFw24ZBdQrkt
SC5TAWzHFyT2tLC17LeSb7d0g+fuj41L6y4a9och8cPiv9IAP4sftzYupO99h4qg
7UXP7o3AOOGqrPS3INhO4068Z63indstanIHYM0IUHa3A2xrcz7ZbEuw1HiGH/Ba
HMz/gTSd2c0BXNiPeM7gdOK3
-----END CERTIFICATE-----
subject=/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=outlook.com
issuer=/C=US/O=DigiCert Inc/CN=DigiCert Cloud Services CA-1
---
No client certificate CA names sent
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: RSA+SHA256:RSA+SHA384:RSA+SHA1:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA1:DSA+SHA1:RSA+SHA512:ECDSA+SHA512
Shared Requested Signature Algorithms: RSA+SHA256:RSA+SHA384:RSA+SHA1:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA1:DSA+SHA1:RSA+SHA512:ECDSA+SHA512
Peer signing digest: SHA1
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3831 bytes and written 478 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: C11A0000050CD144CB5C49DD873D2C911F7CDDECFE18001F70FE0427C88B52F7
Session-ID-ctx:
Master-Key: 5F4EC0B1198CF0A16D19F758E6A0961ED227FCEBD7EF96D4D6A7470E3F9B0453A2A06AC0C1691C31A1CA4B73209B38DE
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1519322480
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
250 SMTPUTF8
我可能会删除phpseclib以支持二进制命令,但我会依赖于system
/ exec
等,这些可能无法使用。尽管如此,有时工作比不工作更好!
尽管做了大量工作,但我已经走到了尽头。我将在这里总结一下我想要做的事情。
我想使用PHP来验证针对已知公共CA的邮件服务器SSL证书。我不知道Mozilla证书是否适合用于此,或者我是否需要从其他地方获取它们。我发现我的Linux Mint开发机器有证书可以验证上面的示例邮件服务器。
这里的简单策略是使用PHP 5.6+并确保在流上下文中启用所有验证选项(尽管理想情况下,我也希望支持5.5)。但是,我想自己做证明,使用openssl_
函数或像phpseclib这样的库,所以我可以看到为什么给定的证书是有效的(或不是)。 openssl
二进制文件执行此操作(如上所示),并且它可能使用与PHP的openssl调用非常相似的内容,但我不知道它是如何实现的。例如,openssl二进制文件是否使用证书链信息来执行此操作?
另一种方法是从有效的SSL会话中读取一些信息,但我也无法在手册中找到任何信息。
答案 0 :(得分:3)
I was able to get it to verify thusly:
<?php
include('File/X509.php');
$certs = file_get_contents('cacert.pem');
$certs = preg_split('#==(?:=)+#', $certs);
foreach ($certs as &$cert) {
$cert = trim(preg_replace('#-----END CERTIFICATE-----.+#s', '-----END CERTIFICATE-----', $cert));
}
unset($cert);
array_shift($certs);
$x509 = new File_X509();
foreach ($certs as $i => $cert) {
$x509->loadCA($cert);
}
$test = file_get_contents('test.cer');
$x509->loadX509($test);
$opts = $x509->getExtension('id-pe-authorityInfoAccess');
foreach ($opts as $opt) {
if ($opt['accessMethod'] == 'id-ad-caIssuers') {
$url = $opt['accessLocation']['uniformResourceIdentifier'];
break;
}
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$intermediate = curl_exec($ch);
$x509->loadX509($intermediate);
if (!$x509->validateSignature()) {
exit('validation failed');
}
$x509->loadCA($intermediate);
$x509->loadX509($test);
echo $x509->validateSignature() ?
'good' :
'bad';
Note the $test = file_get_contents('test.cer');
bit. That's where I loaded your cert. If I commented out $x509->loadCA($intermediate);
the cert didn't validate. If I leave it in it does validate.
edit:
This branch does this automatically:
https://github.com/terrafrost/phpseclib/tree/authority-info-access-1.0
Unit tests still need to be added however it's not in the 2.0 or master branches yet either. I'll try to do work on that this weekend.
Example of how to use:
<?php
include('File/X509.php');
$certs = file_get_contents('cacert.pem');
$certs = preg_split('#==(?:=)+#', $certs);
foreach ($certs as &$cert) {
$cert = trim(preg_replace('#-----END CERTIFICATE-----.+#s', '-----END CERTIFICATE-----', $cert));
}
unset($cert);
array_shift($certs);
$x509 = new File_X509();
foreach ($certs as $i => $cert) {
$x509->loadCA($cert);
}
$test = file_get_contents('test.cer');
$x509->loadX509($test);
//$x509->setRecurLimit(0);
echo $x509->validateSignature() ?
'good' :
'bad';
答案 1 :(得分:3)
事实证明我可以从远程服务器获取整个证书链 - 我不得不经历各种错误的线索和狡猾的假设来达到这一点!感谢Joe在评论中指出,上下文选项capture_peer_cert
仅获取证书证书,而没有任何链证书可以完成公共CA的验证路径;要做到这一点,需要capture_peer_cert_chain
。
以下是一些代码:
$url = "tcp://{$domain}:{$port}";
$connection_context_option = [
'ssl' => [
'capture_peer_cert_chain' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
]
];
$connection_context = stream_context_create($connection_context_option);
$connection_client = stream_socket_client($url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $connection_context);
// timeout fread after 2s
stream_set_timeout($connection_client, 2);
fread($connection_client, 10240);
fwrite($connection_client,"HELO alice\r\n");
// let the server introduce it self before sending command
fread($connection_client, 10240);
// send STARTTLS command
fwrite($connection_client, "STARTTLS\r\n");
// wait for server to say its ready, before switching
fread($connection_client, 10240);
// Switching to SSL/TLS
$ok = stream_socket_enable_crypto($connection_client, TRUE, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
if ($ok === false)
{
return false;
}
$chainInfo = stream_context_get_params($connection_client);
然后我们可以使用OpenSSL提取所有证书:
if (isset($chainInfo["options"]["ssl"]["peer_certificate_chain"]) && is_array($chainInfo["options"]["ssl"]["peer_certificate_chain"]))
{
$verboseChainCerts = [];
foreach ($chainInfo["options"]["ssl"]["peer_certificate_chain"] as $ord => $intermediate)
{
$chainCertOk = openssl_x509_export($intermediate, $verboseChainCerts[$ord]);
if (!$chainCertOk)
{
$verboseChainCerts[$ord] = 'Cannot read chain info';
}
}
$chainValid = checkChainAutomatically($x509Chain, $verboseChainCerts);
}
最后,进行检查的功能就在这里。根据以下问题,您应该假设已经加载了一组好的公共证书:
function checkChainAutomatically(X509 $x509, array $encodedCerts)
{
// Set this to true as long as the loop will run
$verified = (bool) $encodedCerts;
// The certs should be tested in reverse order
foreach (array_reverse($encodedCerts) as $certText)
{
$cert = $x509->loadX509($certText);
$ok = $x509->validateSignature();
if ($ok)
{
$x509->loadCA($cert);
}
$verified = $verified && $ok;
}
return $verified;
}
我尝试按顺序验证它们,但第一个失败了。我因此颠倒了秩序,他们都成功了。我不知道证书是否按链式提供,因此一个非常可靠的方法是使用两个嵌套循环进行循环,将任何有效的证书添加为CA,然后继续外部循环。这可以在列表中的所有证书都被确认为具有经过验证的签名之前完成。
答案 2 :(得分:2)