Sv: Re: Curl callback question, porting from OpenSSL 1.x to 3.x and from 32bit plain to 64bit UTF16

Anders Gustafsson via curl-library <[email protected]>
Newsgroups gmane.comp.web.curl.library
Message-ID <[email protected]>
OK. Point taken 😀

Yes. I do make assumptions based on how this app is used and yes it is C++/MFC on windows going from an old
version with no specified coding to UTF.

The certificate data in this case is "regular" ie a PEM payload that is not UTF encoded, just:

-----BEGIN CERTIFICATE-----
xxxxxxx
-----END CERTIFICATE-----

Ie one byte per character. Filenames OTOH might be a different kettle of fish. I probably should include a
testcase where the path has non-ascii characters.

And yes, what I wrote then was completely clear to me, but I realise when rereading that it was probably not,
so, let me elaborate:

The original code on Windows 32-bit no unicode used the CURLOPT_SSL_CTX_FUNCTION 

I replaced that with the function in the documentation, but that apparently did not add the key to the
context, just the certificate so I needed to add code to handle the certificate

The string passed to CURLOPT_SSL_CTX_DATA is a CStringA so it is OK to convert to char, especially assuming
that the data is just PEM

The example code in https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html adds the certificate to a store in
the context, what my code does is to take the certificate and key, again as CStringA then BIO_new_mem_buf()
the do PEM_Read_bio_x590 then SSL_CTX_use_certificate, then do the same for the key, SSL_CTX_use_PrivateKey.
Ie it sets the certificate and key directly without going through a store. I am not sure why my way works, but
not the store way. I guess the "store" concept was added to OpenSSL along the line and I guess I should read
up on that.

Yes. String handling in Windows is weird. I sort of understand the performance aspect of using UTF-16, but it
makes other things harder. I guess another option would be to use std::string all the way.


> (OTOH one could just assume that
> all [relevant] ACP encodings are ASCII subset, thus
> simply NOT do transcoding since it then ought to be
> ASCII-compliant content already anyway).

Which is pretty much what I do. All certificate data is assumed to be PEM and coded as ASCII with one byte
per character.

Thanks for your reply and your pointers.



-- 
Med vänlig hälsning

Anders Gustafsson, ingenjör
[email protected]  |  Support +358 18 12060  |  Direkt +358 9 315 45 121  |  Mobil +358 40506 7099

Pedago interaktiv ab, Nygatan 7 B , AX-22100 MARIEHAMN, Ã…LAND, FINLAND



>>> Andreas Mohr <[email protected]> 2025-08-20 17:44 >>>
Hi,

disclaimer:
quite experienced in certain areas, yet not too much in others (CURL).

TL;DR:
thus discussing potential string encoding issues only.

On Wed, Aug 20, 2025 at 03:58:14PM +0300, Anders Gustafsson via curl-library wrote:
> So, yes this is windows 😀 libcurl/8.15.0-DEV OpenSSL/3.5.2 zlib/1.3.1
> 
> I had some issues and I just want to check whether I am going about this the right way. The function calls
an
> API where the client certificate is used to authenticate the caller so in the original version I used the
> sslctx_function(). To complicate matters does my app support PEM certificates and keys in two different
ways:
> 1. As files (Say on a removable secure media) and 2. As strings in the database for ease of use.
> 
> The first way (filenames) worked right away, ie:
> 
> 			m_Certificate.Trim();
> 			if (m_Certificate.IsEmpty())
> 				curl_easy_setopt(curl, CURLOPT_SSLCERT, m_CertificateFile.GetString());

Bleeep - ATLMFC CStringT::GetString() encountered.
This might thus be
dirty "encoding shortcutting" here
(simply invoking .GetString() to
"quickly" "get at" some "char"-compatibly-typed - hah! - input, rather than
doing active transcoding to
the actually *correct* encoding spec of
some char-typed handling).

Thus, consulting this one:

> Where m_Certificate and m_Key and regular (char) strings with the PEM coded data.

What would "regular" mean?

Considering that
CString errfilename;
with
CT2A(errfilename),
one would think that this is
a CString[T] with UNICODE config setting (put differently, CStringW)
environment, however
since you said "regular (char) strings", I am assuming that
you are on !UNICODE config (i.e., CStringA).


https://manpages.ubuntu.com/manpages/kinetic/man3/CURLOPT_SSLCERT.3.html 
Yeah nice - that page does not specify at all which
encoding char *cert (a filesystem item argument!! - which could be
containing all the smileys available in this universe, with
only a bit of luck...) is expected to have.
Thus on Windows one would tend to
assume ACP crap - which of course would mean that it is
Unicode-compliance-broken (since: neither UTF16 nor UTF8 nor UTF7 nor
UTF-EBCDIC or whatever ;-)).





> 	errno_t fileerr = fopen_s(&errfile, CT2A(errfilename), "w+, ccs=UTF-8");

Unicode-compliance-broken filesystem item handling!

CT2A(errfilename) will be
wide-typed to CP_ACP transition (in UNICODE config setting), and
"nothing" *) (in non-UNICODE).

*) BTW *HORRIBLE* atlconv.h comment "// Code page doesn't matter" atlconv.h transcoding protocol breakage!!!
Yeah, as if that would be the case for
e.g. CP_ACP to CP_UTF8 transcoding, which *is* a valid transcoding use case...)
(think of
CT2CA(..., CP_UTF8)
protocol behaviour **DIFFERENCE**)


Thus, your errfilename possibly is ACP (CP_ACP, GetACP()) content,
which would be
"compatible" since
fopen_s() API is equally ACP-specced on Windows (yuck).
...but: then it would be
Unicode-compliance-broken (due to
being ACP crap, rather than
e.g. UTF8 as usually on Linux).

Since fopen_s() should have an overload for wide-typed input I'd think,
the way to go would at least be
CT2W(errfilename) - thereby
properly preserving Unicode-compliant (since wide-typed!) encoding (when
on UNICODE config setting - and ACP crap on !UNICODE).

Or, better do utf8everywhere.org (i.e., std::string[-means-utf8] -
to have ensured that
**every** string traffic anywhere is Unicode-compliant), and thus do
std::string errfilename = "myUtf8InputStuffStringFromSomewhere"; // e.g. std::filesystem **) API
fopen_s(... CA2W(errfilename, CP_UTF8) ...);


**) rather *horribly* Unicode-compliance-broken (on Windows!) - I digress...
"<filesystem>: prevent filesystem::path dangerous conversions to/from default code page encoding"
  https://github.com/microsoft/STL/issues/909 


> In the second scenario, PEM in database, I had some problems and I just wanted to check that the code I
came
> up with is sane. Ie the authentication will not happen unless I have both certificate and key, so:
> 
> 			if (!m_Certificate.IsEmpty())
> 			{
> 				curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctx_function);
> 				curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, m_Certificate.GetString());

WARNING CORRUPTION: CURLOPT_SSL_CTX_DATA has a void*-typed ptr arg, thus
both .GetString() CString[T] is accepted, *silently*).
IOW, once on UNICODE config setting it would be
*broken*.

So, questionable encoding stuff again.
According to
https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html 
  "char *mypem = /* CA cert in PEM format"
it seems to *appear* that
"PEM format" means some plain ASCII-only payload.

Now to be maximally precise one could do
const UINT nCP_PEM = 20127 /*CP_ASCII*/ /* these clearly are [to be!] all ASCII-only, right!!!? */;
std::string strCertificate = CW2A(CA2W(m_Certificate), nCP_PEM);
(this transition expects that m_Certificate has system ACP content, of course)



(OTOH one could just assume that
all [relevant] ACP encodings are ASCII subset, thus
simply NOT do transcoding since it then ought to be
ASCII-compliant content already anyway).


> Then, below, which seems to work OK. I first used the example here:
> https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html 
> but that one did not fix my key for me. Yes, this code still leaves allocated memory in case of errors.

"fix my key" - that wording might hint at
encoding issues.
But perhaps we're talking about
a plain CURL certificate config issue only after all.

I could not precisely identify (thus: discuss) particular ***) issues in
your handling, but I'd hope that
this will give you some ideas (*if* it is an encoding issue).

***) well, except for the broken fopen_s() filesystem item handling...

Greetings

Andreas Mohr
-- 
Unsubscribe: https://lists.haxx.se/mailman/listinfo/curl-library
Etiquette:   https://curl.se/mail/etiquette.html
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.