3

Background

I have complex application which uses some OpenSSL features. One of the features is possibility to read PKCS12 files. OpenSSL is statically linked and my project builds it from sources.

Problem

Currently I'm upgrading my application dependency OpenSSL form 1.1.x to 3.0.8. Happily build process was not broken, but some test detected issues. Turns out that some PKCS12 files used for testing are now not readable.

bool pfxToDer(const unsigned char* in, long size, const char* pass, .....)
{
    utils::UniquePtr<PKCS12> pPkcs12; // this are my smart pointers to handle OpenSSL using C++ RAII pattern
    utils::UniquePtr<EVP_PKEY> pKey;
    utils::UniquePtr<X509> pCert;

    d2i_PKCS12(std::out_ptr(pPkcs12), &in, size);
    if (!pPkcs12)
    {
        debugOpenSslError();
        return false;
    }

    const auto parsingResult = PKCS12_parse(pPkcs12.get(), pass, std::out_ptr(pKey), std::out_ptr(pCert), nullptr);
    if (!parsingResult)
    {
        // here fails
        debugOpenSslError();
        return false;
    }
    ....

PKCS12_parse with error which is log by debugOpenSslError(); as:

errorCode = 0x0308010c   description = "error:0308010C:digital envelope routines::unsupported"

With quick googling I've found this SO answer.

So looks like my test data are using some old encryption which is not considered safe anymore. I need to read this data anyway. If customer has some old data he like to import it should be possible.

I've found it should be possible to enable legacy mode and make this work. So I've added somewhere in my code:

    [[maybe_unused]] auto provider = OSSL_PROVIDER_load(NULL, "legacy");

This call succeeded (it returns provider), but PKCS12_parse keeps failing.

When I try use opessl tool built with library I'm using I've got this:

F:\repos\project\third_party\openssl\install_dir\x64\Release\bin>openssl.exe pkcs12 -in test_certs.pfx -nodes
Enter Import Password:
Error outputting keys and certificates
88940000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto\evp\evp_fetch.c:373:Global default library context, Algorithm (RC2-40-CBC : 0), Properties ()

F:\repos\project\third_party\openssl\install_dir\x64\Release\bin>openssl.exe pkcs12 -in test_certs.pfx -nodes -provider legacy
-nomacver
Enter Import Password:
Error outputting keys and certificates
D83F0000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto\evp\evp_fetch.c:373:Global default library context, Algorithm (SHA1 : 96), Properties (<null>)
D83F0000:error:030000A1:digital envelope routines:EVP_PBE_CipherInit_ex:unknown digest:crypto\evp\evp_pbe.c:143:

Questions

  • Is there some way to force PKCS12_parse to work anyway?
  • is my attempt to load legacy provider a good solution? If yes why it didn't work?
  • do I have to change configuration of OpenSSL build to make it work? How?
  • can I get more details why exactly parsing fails? What exactly is missing?

Tech notes

This must work on Windows/Linux/MacOS

I build OpenSSL on Windows (VC-WIN32 and VC-WIN64A) this way:

perl Configure %PLATFORM% no-asm enable-static-engine no-shared no-tests no-nod-module --prefix="%cd%\%INSTALL_PREFIX%" --openssldir="%cd%\%INSTALL_PREFIX%" -FS || exit /b 1
perl -i.bak -pe "s/^\s*@\s*$/    @\$(ECHO\)\n/g" makefile || exit /b 1
nmake -k all  || exit /b 1
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Marek R
  • 32,568
  • 6
  • 55
  • 140
  • Why did you tag this C when your code is obviously C++? – Jabberwocky May 19 '23 at 15:27
  • 1
    OpenSSL is C API. Fact that I'm calling it from C++ is not important. – Marek R May 19 '23 at 15:39
  • *So looks like my test data are using some old encryption which is not considered safe anymore. I need to read this data anyway* Which specific "old encryption" algorithm(s)? If those algorithm(s) have been entirely removed from the OpenSSL code base, you won't be able to use OpenSSL 3.x to decrypt them. – Andrew Henle May 19 '23 at 16:36
  • 1
    This is also likely relevant: https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers – Andrew Henle May 19 '23 at 17:26
  • `PKCS#12 and OSSL_STORE currently have no library context support` does it mean I can't address this issue now, until this is supported? – Marek R May 19 '23 at 21:01
  • 1
    @MarekR See updated information at https://www.openssl.org/docs/man3.0/man7/migration_guide.html#PKCS-12-API-updates – Andrew Henle May 20 '23 at 00:46
  • Re: `RC2-40-CBC` - RC2 was removed - source and all - from OpenSSL 1.1.1 in 2016. [Changelog](https://www.openssl.org/news/cl111.txt) Although [this answer](https://stackoverflow.com/a/72859712/4756299) claims it still works, but it's not clear which version of OpenSSL the answer is referring to. – Andrew Henle May 20 '23 at 00:50
  • I tested this with `OpenSSL 3.0.8 7 Feb 2023`. The pkcs12 app cmdline should use `-legacy` if you're trying to use the legacy provider. ex: i have a legacy PFX from `makecert` (it's a Windows SDK thing). Without `-legacy` cracking it open will exhibit your same error. It cracks open fine with that option (assuming you provide the correct passwords). If you're interested, the source in [`apps/pkcs12.c`](https://github.com/openssl/openssl/blob/master/apps/pkcs12.c#L440) will demonstrate what that configuration switch actually does if you want to mimic a similar operation in your code. – WhozCraig May 20 '23 at 12:26
  • @AndrewHenle: the RC2 _ciphersuites_ were deleted (and as noted had already been default-disabled because they were 'export' strength i.e. broken) but the [RC2 _cipher_ remains present as of latest=3.1.2](https://github.com/openssl/openssl/tree/openssl-3.1.2/crypto/rc2) -- in the legacy provider, as OP correctly diagnosed. – dave_thompson_085 Aug 20 '23 at 21:32

1 Answers1

1

is my attempt to load legacy provider a good solution? If yes why it didn't work?

Partly (assuming 'somewhere in my code' is executed prior to the PKCS12_parse call). If you don't load any providers OpenSSL uses "default" by default (!), but if you do it uses ONLY the provider(s) you explicitly loaded; it does not add "default" to the call(s) you make. Since you need BOTH "default" and "legacy" providers, you need to call OSSL_PROVIDER_load on BOTH of them -- and then it works.

can I get more details why exactly parsing fails? What exactly is missing?

It looks like your debug routine is printing only the 32-bit error code and the result of decoding it with ERR_error_string. The OpenSSL error queue can also contain a sourcefile location (filename and linenumber), which usually doesn't help much more than the errorcode does, and optional 'data' (text), which is useful for SOME errors -- including this one. Plus the error queue can contain more than one entry, and in that case it is often helpful to display ALL of them; while you can code this yourself, it is easier to just call ERR_print_errors[_fp] -- see the man page, installed automatically on your system but not easily readable on Windows, or on the web.

When I try use [commandline] I've got this: ...

Similarly, for commandline you need to specify BOTH providers -provider legacy -provider default (order doesn't matter unless they conflict and these don't) or alternatively use the shorthand -legacy which sets the two providers and also sets the algorithms for -export -- which doesn't matter in your case because you aren't doing -export. (My answer that you linked to was wrong on this point, and one other, and I am fixing.)

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70