I am trying to send an email using an SMTP server that accepts only TLS 1.1 and higher, but I didn't manage to activate the good TLS version with Indy 10.6.2.0 under C++Builder 11.0. I copied libcrypto-1_1.dll and libssl-1_1.dll in the app folder, but that changed nothing.
EIdOSSLUnderlyingCryptoError is raised with following message : error:1409442E:SSL routines:SSL3_READ_BYTES:tlsv1 alert protocol version'.
Below is my code:
IdSMTP *IdSMTP1;
TIdText *IdText1;
TIdMessage *IdMessage1;
TIdAntiFreeze *IdAntiFreeze1;
TIdAttachmentFile *IdAttachment1, *IdAttachment2;
try {
Form1->AX0CPos = 109900;
UnicodeString UX0SNom = " "+ AX0SNom.SubString(0, AX0SNom.Length()-1);
if(!GAntiFreeze) {
IdAntiFreeze1 = new TIdAntiFreeze(NULL);
IdAntiFreeze1->IdleTimeOut = 250;
}
Form1->AX0CPos = 109901;
IdSMTP1 = new TIdSMTP(NULL);
TIdSSLIOHandlerSocketOpenSSL *IdSSLIOHandlerSocketOpenSSL1 = new TIdSSLIOHandlerSocketOpenSSL(IdSMTP1);
//IdSSLIOHandlerSocketOpenSSL1->SSLOptions->Method = sslvTLSv1_1;
// Either Version or Method !!!
IdSSLIOHandlerSocketOpenSSL1->SSLOptions->SSLVersions.Clear();
IdSSLIOHandlerSocketOpenSSL1->SSLOptions->SSLVersions << sslvTLSv1_1 << sslvTLSv1_2;
IdSSLIOHandlerSocketOpenSSL1->SSLOptions->Mode = sslmClient; // ?
IdSSLIOHandlerSocketOpenSSL1->PassThrough = false;
Form1->AX0CPos = 109902;
IdSMTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;
//IdSMTP1->Host = _D("ssl0.ovh.net");
IdSMTP1->Host = _D("smtp.mail.ovh.net");
IdSMTP1->Port = 465;
IdSMTP1->UseTLS = utUseImplicitTLS; //utUseRequireTLS; utUseExplicitTLS;
IdSMTP1->Username = _D("MyUserName");
IdSMTP1->Password = _D("MyPassWord");
IdSMTP1->AuthType = satDefault;
Form1->AX0CPos = 109903;
IdMessage1 = new TIdMessage(NULL);
IdMessage1->Priority = mpHigh;
IdMessage1->Recipients->EMailAddresses = "Recipients@Recipients.com";
IdMessage1->Subject = _D("User #"+ String(CODOPharmacie) + UX0SNom); // Form1->NRSOPharmacie
IdMessage1->Encoding = meMIME;
IdMessage1->ContentType = _D("multipart/mixed");
IdMessage1->CharSet = _D("UTF-8");
IdMessage1->From->Address = _D("MyUserName");
IdMessage1->From->Name = _D("MyApp");
IdMessage1->AttachmentEncoding = "MIME";
Form1->AX0CPos = 109904;
IdText1 = new TIdText(IdMessage1->MessageParts, NULL);
IdText1->Body->Text = AX0LMes;
IdText1->ContentType = _D("text/plain");
IdText1->CharSet = _D("utf-8");
Form1->AX0CPos = 109905;
if(FileExists(UX0CPDMyApp + "\\OrtErr.log"))
IdAttachment1 = new TIdAttachmentFile(IdMessage1->MessageParts, UX0CPDMyApp + "\\OrtErr.log");
Form1->AX0CPos = 109906;
if(FileExists(UX0CPDMyApp + "\\HTPErr.htm"))
IdAttachment2 = new TIdAttachmentFile(IdMessage1->MessageParts, UX0CPDMyApp + "\\HTPErr.htm");
Form1->AX0CPos = 109907;
IdSMTP1->Connect();
IdSMTP1->Send(IdMessage1);
IdSMTP1->Disconnect();
}
catch(Exception &EX0Vl){
AX0SNom = "Form1/SendEmail/";
AX0LMes = EX0Vl.Message;
ORTErr();
}
catch(...){
AX0SNom = "Form1/SendEmail/";
AX0LMes = "Exception non planifiée";
ORTErr();
}
Any clue to sort out the issue?
I didn't manage to activate the good TLS version with Indy 10.6.2.0 under C++Builder 11.0.
You are not setting the SSLVersions correctly, so it ends up using its default value of sslvTLSv1 (TLS 1.0), hence the tlsv1 alert protocol version error since the server is not expecting TLS 1.0.
In this code:
IdSSLIOHandlerSocketOpenSSL1->SSLOptions->SSLVersions.Clear(); IdSSLIOHandlerSocketOpenSSL1->SSLOptions->SSLVersions << sslvTLSv1_1 << sslvTLSv1_2;
This will not work as you are expecting. You are invoking the SSLVersions property getter and then modifying the temporary Set that it returns.
You need to assign a value to the property instead, eg:
TIdSSLVersions tlsVersions;
tlsVersions << sslvTLSv1_1 << sslvTLSv1_2;
IdSSLIOHandlerSocketOpenSSL1->SSLOptions->SSLVersions = tlsVersions;
Or simpler:
IdSSLIOHandlerSocketOpenSSL1->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1_1 << sslvTLSv1_2;
I copied
libcrypto-1_1.dllandlibssl-1_1.dllin the app folder, but that changed nothing.
You are using the wrong DLLs. Indy's TIdSSLIOHandlerSocketOpenSSL component does not support OpenSSL 1.1+, so you must use OpenSSL 1.0.2 instead, which does support TLS 1.1 and 1.2 at least, but not TLS 1.3.
You can download the OpenSSL 1.0.2 DLLs (libeay32.dll and ssleay32.dll) from Indy's GitHub repo:
https://github.com/IndySockets/OpenSSL-Binaries
If you really want to use OpenSSL 1.1+ with Indy, there are 3rd party repos which provide that ability, for example:
https://github.com/JPeterMugaas/TaurusTLS
On a side note, there are a number of minor issues with the rest of the code snippet you have shown:
UnicodeString UX0SNom = " "+ AX0SNom.SubString(0, AX0SNom.Length()-1);
UnicodeString is 1-indexed, not 0-indexed:
UnicodeString UX0SNom = _D(" ") + AX0SNom.SubString(1, AX0SNom.Length()-1);
// ^
UnicodeString::SubString() will adjust the index internally for you, but you really should not rely on that behavior. By definition, the 1st index of a UnicodeString is 1 not 0.
Alternatively, you can use LeftStr() instead, then you don't have to specify the starting index at all, eg:
#include <System.StrUtils.hpp>
UnicodeString UX0SNom = _D(" ") + LeftStr(AX0SNom, AX0SNom.Length()-1);
IdSMTP1 = new TIdSMTP(NULL); IdMessage1 = new TIdMessage(NULL);
These objects are being leaked, as you are not freeing them when you are done using them.
IdMessage1->Subject = _D("User #"+ String(CODOPharmacie) + UX0SNom); // Form1->NRSOPharmacie
You are misusing the _D() macro here. The macro works only on string literals (or character literals). The String objects need to be moved outside of the macro, eg:
IdMessage1->Subject = _D("User #") + String(CODOPharmacie) + UX0SNom;
Also, there are a number of string literals shown in your code snippet which are missing the _D() macro completely. While not strictly a problem since those strings are using only ASCII characters, you should nonetheless be more consistent with your use of _D() to avoid unnecessary data conversions at runtime.
catch(Exception &EX0Vl){
You should always catch exception objects by const reference instead, eg:
catch(const Exception &EX0Vl)