тл; др:
Есть ли способ для TIdSSLIOHandlerSocketOpenSSL использовать один файл .pem, который включает в себя сертификат, root и ключ для сертификата клиента?
подробности:
Партнер поставщика требует от нас использования аутентификации mTLS. Они предоставили сертификат, который я экспортировал (включая мой сертификат, корневой сертификат и закрытый ключ) в формате PEM.
Затем я провел несколько простых тестов подключения в PHP, используя cURL.
Обратите внимание, что я смог использовать свой полный файл .pem.
$the_url = 'https://example.com/post-to-me';
$xml_payload = '<foo>bar</foo>';
$headers = array( "Content-type: text/xml",
"Content-length: ".strlen($xml_payload),
"Connection: close" );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $the_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Force curl_exec() to return server response string rather than true/false
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 10-second timeout
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSLCERT, 'all-in-one-cert.pem');
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, 'super-secret-password');
$result = curl_exec($ch);
Это дало ожидаемый результат от сервера, поэтому я перешел к написанию своего кода C ++ Builder:
FHTTP = new TIdHTTP( 0 );
FSSLIOHandlerSocket = new TIdSSLIOHandlerSocketOpenSSL( 0 );
FLogFile = new TIdLogFile( 0 );
FLogFile->LogTime = true;
FSSLIOHandlerSocket->Intercept = FLogFile;
FSSLIOHandlerSocket->IPVersion = Id_IPv4;
FSSLIOHandlerSocket->MaxLineAction = maException;
FSSLIOHandlerSocket->SSLOptions->Mode = sslmClient;
FSSLIOHandlerSocket->SSLOptions->CertFile = "all-in-one-cert.pem";
FSSLIOHandlerSocket->SSLOptions->RootCertFile = "all-in-one-cert.pem";
FSSLIOHandlerSocket->SSLOptions->KeyFile = "all-in-one-cert.pem";
FSSLIOHandlerSocket->OnGetPassword = GetPassWord;
FSSLIOHandlerSocket->SSLOptions->Method = sslvTLSv1_2;
FHTTP->IOHandler = FSSLIOHandlerSocket;
FHTTP->ConnectTimeout = 10000;
FHTTP->ReadTimeout = 60000;
FHTTP->MaxAuthRetries = 0;
FHTTP->Request->ContentType = "text/xml";
TStringStream * MsgStream = new TStringStream();
TStringStream * RspStream = new TStringStream();
MsgStream->WriteString( "<foo>bar</foo>" );
FHTTP->Post( "https://example.com/post-to-me", MsgStream, RspStream );
long StreamSize = RspStream->Size;
RspStream->Position = 0;
FResponse = RspStream->ReadString( StreamSize ).Trim();
Это привело к следующей ошибке OpenSSL:
Не удалось загрузить сертификат.
ошибка: 00000000: Lib (0): функ (0): причина (0)
Я тоже пробовал:
FSSLIOHandlerSocket->SSLOptions->CertFile = "all-in-one-cert.pem";
FSSLIOHandlerSocket->OnGetPassword = GetPassWord;
Это привело к той же ошибке.
Я также пытался использовать файл сертификата .pfx:
FSSLIOHandlerSocket->SSLOptions->CertFile = "all-in-one-cert.pfx";
FSSLIOHandlerSocket->OnGetPassword = GetPassWord;
Это позволило загрузить сертификат, но затем привело к сбою рукопожатия при подключении:
Ошибка подключения по SSL.
ошибка: 14094410: подпрограммы SSL: SSL3_READ_BYTES: сбой квитирования оповещения sslv3
Я использовал Wireshark, чтобы убедиться, что произошла ошибка рукопожатия, потому что в рукопожатие передавалось 0 сертификатов.
Я решил проблему, разделив клиентский сертификат, root и ключ на три отдельных файла, например:
FSSLIOHandlerSocket->SSLOptions->CertFile = "cert-only.pem";
FSSLIOHandlerSocket->SSLOptions->RootCertFile = "root-only.pem";
FSSLIOHandlerSocket->SSLOptions->KeyFile = "key-only.pem";
Тем не менее, мне любопытно: есть ли способ сделать это со всеми тремя компонентами в одном файле .pem? Или я что-то упустил? Я новичок в этом бизнесе mTLS, поэтому прошу прощения за мое невежество.