gsoap Reading CA file, Windows - c++

I am trying to consume a webservice that I created and deployed in tomcat, enabling ssl in this latter.
For creating the client code, I use gsoap. I generated nedded files, and below my code in C++ :
soap_ssl_init();
soap_init(&soap);
if (soap_ssl_client_context(&soap,
SOAP_SSL_DEFAULT, /* use SOAP_SSL_DEFAULT in production code */
NULL, /* keyfile: required only when client must authenticate to server (see SSL docs on how to obtain this file) */
NULL, /* password to read the keyfile */
"cacert.pem", /* optional cacert file to store trusted certificates */
NULL, /* optional capath to directory with trusted certificates */
NULL /* if randfile!=NULL: use a file with random data to seed randomness */
))
{
soap_print_fault(&soap, stderr);
exit(1);
}
if(soap_call___ns2__Add(&soap,"https://localhost:8443/TestWebService/services/AddService.AddServiceHttpsSoap11Endpoint", NULL,&add, &resp)!= 0)
soap_print_fault(&soap, stderr);
When I execute the program, I got this meesage :
Error 30 fault: SOAP-ENV:Server [no subcode] "SSL error" Detail: Can't
read CA file and directory
As I read, I thinh that I have to generate some files (.pem, certs...).
It's the first time that I use goap and ssl, How could I solve this issue?
I use Windows (coz all examples that I found are for linux)

The problem was not related to my C++ Client code. Infact, I had to add the server certification file to the Bin\security\cacerts file located into my jre.
I had to try to implement a Java client to understand the problem.

Related

Boost ASIO SSL handshake failure

When attempting to securely connect to a remote IMAP server using Boost ASIO, the server handshake fails on every connection. The exception message reads:
handshake: unregistered scheme (STORE routines) [asio.ssl:369098857]
My code is below (url is a std::string_view containing the host URL):
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;
boost::asio::io_context context;
ssl::context ssl_context(ssl::context::tls);
SSLSocket socket(context, ssl_context);
ssl_context.set_default_verify_paths();
tcp::resolver resolver(context);
auto endpoints = resolver.resolve(url, "993");
boost::asio::connect(socket.next_layer(), endpoints);
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::host_name_verification(url.data()));
socket.handshake(SSLSocket::client);
The code immediately throws an exception on the final line, which is a blocking synchronous handshake.
The prior two lines set up host name verification, similar to how it's done in the official ASIO tutorial. These checks seem to be causing an issue, however, because when they are removed the handshake succeeds. Obviously, this is not a good solution.
After stepping through some of ASIO's internals, I found that the last three lines of the above snippet could be replaced by:
SSL_set_verify(socket.native_handle(), SSL_VERIFY_PEER, nullptr);
socket.handshake(SSLSocket::client);
and the same error occurs. SSL_set_verify is an OpenSSL function, and the fact that setting a null callback directly causes the same issue makes me think that the issue is with my system's OpenSSL environment and not ASIO or the host name verification callback. However, I have not been able to determine what exactly the error means and what could be causing it.
Here is a list of things I have tried while troubleshooting:
Load the system's certificate (.pem) file explicitly Thinking maybe ASIO and/or OpenSSL's were not able to load the right certificates to do the validation, I found my system's (a Mac) certificate file at /private/etc/ssl/cert.pem. I then inserted the following line:
ssl_context.load_verify_file("/private/etc/ssl/cert.pem");
directly after set_default_verify_paths() is called. My program loads this certificate file without complaining, but it doesn't fix the handshake error.
Use a different version of OpenSSL At first I was using Apple's system version of OpenSSL (which is really LibreSSL 2.8.3). I then rebuilt my code using the Homebrew package manager's version of OpenSSL (OpenSSL 3.0.4). This also did not fix the issue, even when I tried calling load_verify_file as in point 1.
Sanity check using the OpenSSL command-line tool To make sure my network connection and URL/port number were correct, I tried connecting to the IMAP server over SSL using the following command:
openssl s_client -connect my.url.com:993 -crlf -verify 1
and it worked fine, connecting to the IMAP server and enabling me to send/receive IMAP responses.
Has anyone encountered similar issues when using OpenSSL and ASIO? I'm not very familiar with setting up an SSL/TLS connection, but I don't see what could be causing the problem.
Thanks for your help!
Given that openssl s_client -connect my.url.com:993 -crlf -verify 1 succeeds there is not a lot that seems wrong. One thing catches my eye: I'd configure the context before constructing an SSL stream from it:
ssl::context ssl_context(ssl::context::tls);
ssl_context.set_default_verify_paths();
SSLSocket socket(context, ssl_context);
Also, openssl likely uses SNI extensions:
// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
{
throw boost::system::system_error(
::ERR_get_error(), boost::asio::error::get_ssl_category());
}
Finally, make sure the url string view contains correct data, notably that it's a valid hostname and null-terminated string. In this case I'd prefer to use a string representation that guarantees null-termination:
Summary
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;
int main() {
boost::asio::io_context context;
ssl::context ssl_context(ssl::context::tls);
ssl_context.set_default_verify_paths();
SSLSocket socket(context, ssl_context);
tcp::resolver r(context);
std::string hostname = "www.example.com";
auto endpoints = r.resolve(hostname, "443");
boost::asio::connect(socket.next_layer(), endpoints);
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::host_name_verification(hostname));
// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
{
throw boost::system::system_error(
::ERR_get_error(), boost::asio::error::get_ssl_category());
}
socket.handshake(SSLSocket::client);
}
In my case I manged to make it work with this:
ssl_context.load_verify_file("/etc/ssl/certs/ca-bundle.crt");
so pointing to the ca-bundle file instead of the cert file.

FTP NLST results in '425: Can't open data connection for transfer' only on some client machines

I'm currently running a FileZilla FTP server on a network. My issue is that on seemingly random machines, when the user navigates to a directory (which they are able to do) and attempts to ls (i.e. data transfer) their end hangs waiting for a response, while the server reports this 425: Can't open data connection for transfer mentioned above. This result varies depending on the client machine used, where some (either local or remote) are able to proceed and others stuck here. I understand that this is because simple FTP commands like CWDing operate on the 20/21 ports, whereas FTP data transfer operate on some other port number, which in turn may be blocked by a firewall somewhere along the chain. My question is, how do I account for these varying ports (if this truly is the issue), as as best I know they could be anything above 1024?
My end goal with this project is to implement a very simple FTP solution, ideally using WinINet, however, so far I've run into the same problem:
BOOL CWebFileFinder::FindFile(const CString& URL)
{
CString ServerName;
CString strObject;
INTERNET_PORT nPort;
DWORD dwServiceType = AFX_INET_SERVICE_FTP;
if (AfxParseURL(URL, dwServiceType, ServerName, strObject, nPort))
{
m_Connection = m_Session.GetFtpConnection(ServerName, m_Username, m_Password, nPort/*, true*/); // results in findfile still failing
if (m_Connection)
{
m_Connection->SetCurrentDirectory("sms"); // CDs into this dir
m_Finder = new CFtpFileFind(m_Connection);
if (m_Finder)
{
More = m_Finder->FindFile(_T("*.*")); // hangs here
}
}
}
catch (CException* pEx)
{
CString str;
LPTSTR error = str.GetBuffer(255);
pEx->GetErrorMessage(error, 255);
pEx->Delete();
str.ReleaseBuffer();
}
return More;
}
As far as I can see, either I need to call to open this data port prior to the LIST, or find the firewalls blocking these ports and create a rule to prevent that (What ports does Wininet listen on for Active FTP data connection?). Of course I could also be just completely off-base – Any insights at all would be greatly appreciated!
Your FTP server seems to require an encrypted connection (TLS/SSL).
WinInet does not support encrypted FTP.
See C++/Win32 The basics of FTP security and using SSL.

Reliable way to get root CA certificates on Windows

I'm using boost asio to connect to my valid certificate (signed by root CA). The code I'm using is the ssl client example available from boost docs.
The only line I added is:
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23_client);
ctx.set_default_verify_paths(); <------------- Add default verification paths
ctx.set_password_callback(&password_callback);
client c(io_service, ctx, iterator);
io_service.run();
the problem is: when using this code with a locally installed copy of openSSH (installed from a msi installer) the paths are found correctly and my certificate validated. When I download my own copy of the openSSH repository and compile it this line no longer works and I don't have root CA certificates to validate my own one (therefore it fails).
Since I'd like to eventually distribute these clients on customer machines I'd like to avoid setting environment variables like SSL_CERT_DIR and the like. How can I find root CA certificates with boost asio reliably or alternatively configure my openSSH from source compilation to find them?
You can load the root CAs from the windows CA store. It already contains the "default" trusted root CA certificates and can be managed through certmgr. Use the following function to replace set_default_verify_paths under windows:
#include <boost/asio/ssl/context.hpp>
#include <wincrypt.h>
void add_windows_root_certs(boost::asio::ssl::context &ctx)
{
HCERTSTORE hStore = CertOpenSystemStore(0, "ROOT");
if (hStore == NULL) {
return;
}
X509_STORE *store = X509_STORE_new();
PCCERT_CONTEXT pContext = NULL;
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) {
X509 *x509 = d2i_X509(NULL,
(const unsigned char **)&pContext->pbCertEncoded,
pContext->cbCertEncoded);
if(x509 != NULL) {
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
}
CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);
SSL_CTX_set_cert_store(ctx.native_handle(), store);
}
This will load the certificates from the windows ca store. It uses d2i_X509 to convert them to the internal OpenSSL format and adds them to an OpenSSL X509_STORE. Then SSL_CTX_set_cert_store attaches that store to the boost ssl context. You can use that to set up your ssl context:
namespace ssl = boost::asio::ssl;
ssl::context ctx(ssl::context::tlsv12_client);
ctx.set_options(ssl::context::default_workarounds
| ssl::context::no_sslv2
| ssl::context::no_sslv3
| ssl::context::tlsv12_client);
#if BOOST_OS_WINDOWS
add_windows_root_certs(ctx);
#else
ctx.set_default_verify_paths();
#endif
ctx.set_password_callback(&password_callback);
client c(io_service, ctx, iterator);
io_service.run();
Note: You will probably need to add crypt32 to your linked libraries.
Note 2: BOOST_OS_WINDOWS needs Boost Predef

Loading CA certificate from memory

I am trying to load CA certificate from memory instead of file. But I keep getting handshake error while connecting. The file loading works perfectly, memory loading fails. What am I missing?
std::ifstream file("message_server_ca.crt");
std::vector<char> fileContents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
boost::asio::const_buffer buffer(&fileContents.at(0),fileContents.size());
bool useFile = false; // switch between file and memory loading.
boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::context::verify_peer);
if(useFile)
{
// This works perfectly!
ctx.load_verify_file("message_server_ca.crt");
}
else
{
// This fails the handshake (asio.ssl:336134278)
ctx.use_certificate(buffer,boost::asio::ssl::context_base::pem);
}
client c(io_service, ctx, iterator);
io_service.run();
It appears that you want add_certificate_authority():
This function is used to add one trusted certification authority from
a memory buffer.
use_certificate() and use_certificate_file() are for the server or client certificate presented in the handshake, i.e. not the CA used to test those certificates.
These functions (load_verify_file() and add_certificate_authority()) are not consistently named. I guess it is because the memory buffer versions were added relatively recently.

MS OPM (Output Protection Manager) Initializing X509 chain

I trying to OPM (Output Protection Manager) with Visual C++ (native) using
OPMAPI. My goal is enable HDCP session between HDMI device driver and display to protect contents through out HDMI cable.
I got an example from MSDN
In example code there's some undocumented function about certifications.
(ValidateX509Certificate, GetPublicKeyFromCertificate)
Here follows example code.
OPM_RANDOM_NUMBER random; // Random number from driver.
ZeroMemory(&random, sizeof(random));
BYTE *pbCertificate = NULL; // Pointer to a buffer to hold the certificate.
ULONG cbCertificate = 0; // Size of the certificate in bytes.
PUBLIC_KEY_VALUES *pKey = NULL; // The driver's public key.
// Get the driver's certificate chain + random number
HRESULT hr = pVideoOutput->StartInitialization(
&random,
&pbCertificate,
&cbCertificate );
// Validate the X.509 certificate. (Not shown.)
hr = ValidateX509Certificate(pbCertificate, cbCertificate);
if (FAILED(hr))
{
goto done;
}
// Get the public key from the certificate. (Not shown.)
hr = GetPublicKeyFromCertificate(
pbCertificate,
cbCertificate,
&pKey );
According to MSDN, IOPMVideoOutput::StartInitialization() retrieves random number and X509 certificate chain (pbCertificate).
ValidateX509Certificate() and GetPublicKeyFromCertificate() function are not shown.
Could anyone explain to me the way to write above two function please?
Or I want to know how decode X.509 cert chain data (DER) into CERT_CHAIN_CONTEXT.
The StartInitialization function returns the complete certificate chain of the driver as signed PKCS7 data with DER encoding. This is not the usual X509 certificate with DER encoding we commonly deal with. Unfortunately this is not mentioned in the MSDN pages.
The certificates/certificate chain can be extracted from signed PKCS7 DER using any crypto library/framework. We can verify and get the public key using Microsoft crypto framework with the folloing steps.
Get certificate chain with CryptGetMessageCertificates function
Optionally you can verify the certificate chain as explained "Performing X.509 Certificate Verification with CryptoAPI"
Get the subject certificate CERT_CONTEXT as explained in Step 2. The subject public key can be retrieved from pCertInfo of CERT_CONTEXT.
It is much easier (at least I feel like) to do with OpenSSL which is explained "how to Read the certificates file from the PKCS7.p7b certificate file usind openssl". It shows how to retrieve the certificates and you can easily retrieve public key using X509_get_pubkey function.
Here is a sample code to implement the missed function mentioned MSDN, and show how to use OPM and the related crypto implementation based on the standard window API, https://github.com/wangf1978/D3DTest, CryptoUtil.cpp/OPMSession.cpp