-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tls.createSecureContext SetKey doesn't respect OpenSSL engine set by crypto.setEngine #47008
Comments
I'm skeptical that this is actually an engine issue because that "DECODER routines::unsupported" error means openssl doesn't understand the contents of the PEM file. If you have a key that openssl doesn't understand natively but your custom engine does, you need to load it (by name) through |
Are you sure that you can pass a path to pem file as privateKeyIdentifier? This trick doesn't work with gost-engine. As far as I understand, purpose of privateKeyIdentifier is a support of hardware tokens (key is hidden inside engine but not in a pem file). |
Just to make it clear why I believe it's Node.js bug:
Call stack is:
Call stack is: As you can see, the same key.pem, the same OpenSSL function PEM_read_bio_PrivateKey, but it works in one place and fails in another. |
Yeah, node calls PEM_read_bio_PrivateKey() in both cases. It seems unlikely that it would parse the key successfully in one place but not the other. Something else is probably going on but you have a bespoke setup. There's no way for me to reproduce any of that locally and no way to confirm whether it's actually a node issue. Your bug report is basically inactionable. If you have time and inclination to investigate yourself, great. If not, we might as well close this. |
Do you have any clue why Node loads engine in one case and ignores it in another? |
I doubt that's what happens. Once an engine is loaded, it's loaded. What flags do you pass to |
I don't pass any, because ALL is default. |
The most weird thing is that tls.getCiphers() result includes ciphers added by engine even without crypto.setEngine. |
You may want to investigate if it isn't a problem with the openssl library you link to. |
Actually, setEngine is not needed in my configuration because engine is correctly described in openssl.cnf. |
Do I understand correctly that when I create new SecureContext it is different from the default Node.JS SecureContext, it's absolutely clean, doesn't load system openssl.cnf and doesn't know anything about engines etc? |
No. SecureContext maps directly to openssl's SSL_CTX. It's still affected by things like |
I found it! It turned out to be some error in your BIO loader. I've replaced in "void SecureContext::SetKey" function of src/crypto/crypto_context.cc the code:
and everything works! The correct code was taken from ParsePrivateKey function of src/crypto/crypto_keys.cc |
I looked at NodeBIO but I don't think it does anything materially different from BIO_new_mem_buf() so... 🤷 If you can identify where it diverges, it can probably be fixed. |
I've investigated further. The problem is that when you create bio like this: As an experiment, I did this:
|
NodeBIO::Ctrl() doesn't implement BIO_C_FILE_SEEK (nor BIO_C_FILE_TELL) so that does indeed seem like a likely culprit. OpenSSL doesn't normally BIO_seek() when parsing data structures so that's probably why it went unnoticed for so long. If implementing those controls helps the GOST engine, then PR welcome. |
As far as I see, NodeBIO is based on singly-linked buffers. So I can seek within current buffer and I can switch to the next buffer. But how should I jump to the first buffer? |
There's also option 4) get rid of NodeBIO in crypto_context.cc and use openssl's own memory BIOs. :-) NodeBIO makes sense for TLS traffic but less so for loading keys or certificates. Loading private keys with BIO_s_secmem() is also desirable, see #30956. |
As an example: is it OK if I write
instead of |
The first line should be: BIOPointer bio(BIO_new(BIO_s_secmem())); Because automatic resource management > manually calling BIO_free. Check that |
So I've rewritten LoadBIO this way:
Is it pretty enough? |
Looks good to me modulo the signed-vs-unsigned comparison. If you open a pull request, I'll run it through the CI. |
NodeBIO's memory buffer structure does not support BIO_C_FILE_SEEK and B IO_C_FILE_TELL. This prevents OpenSSL PEM_read_bio_PrivateKey from readi ng some private keys. So I switched to OpenSSL'w own protected memory bu ffers. Fixes: #47008 PR-URL: #47160 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
NodeBIO's memory buffer structure does not support BIO_C_FILE_SEEK and B IO_C_FILE_TELL. This prevents OpenSSL PEM_read_bio_PrivateKey from readi ng some private keys. So I switched to OpenSSL'w own protected memory bu ffers. Fixes: nodejs#47008 PR-URL: nodejs#47160 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
NodeBIO's memory buffer structure does not support BIO_C_FILE_SEEK and B IO_C_FILE_TELL. This prevents OpenSSL PEM_read_bio_PrivateKey from readi ng some private keys. So I switched to OpenSSL'w own protected memory bu ffers. Fixes: #47008 PR-URL: #47160 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
NodeBIO's memory buffer structure does not support BIO_C_FILE_SEEK and B IO_C_FILE_TELL. This prevents OpenSSL PEM_read_bio_PrivateKey from readi ng some private keys. So I switched to OpenSSL'w own protected memory bu ffers. Fixes: #47008 PR-URL: #47160 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
NodeBIO's memory buffer structure does not support BIO_C_FILE_SEEK and B IO_C_FILE_TELL. This prevents OpenSSL PEM_read_bio_PrivateKey from readi ng some private keys. So I switched to OpenSSL'w own protected memory bu ffers. Fixes: nodejs#47008 PR-URL: nodejs#47160 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
Version
19.7.0
Platform
Linux 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64 GNU/Linux
Subsystem
crypto
What steps will reproduce the bug?
I'm trying to create TLS connection using custom OpenSSL engine (gost-engine). OpenSSL version is 3.0.8 and engine are installed correctly (
openssl s_client -connect api.dom.gosuslugi.ru:443 -engine gost -verifyCAfile server.pem -partial_chain -cert public.cer -key private.pem -ignore_unexpected_eof
works perfectly). Node.JS is recompiled with shared OpenSSL support (./configure --shared-openssl --shared-openssl-libpath=/usr/local/ssl/lib --shared-openssl-includes=/usr/local/ssl/include
). Private key is in a .pem file so it should be loaded with "key" option, not "privateKeyIdentifier".So I try to tls.createSecureContext with key: fs.readFileSync('/path/to/private/key.pem').
How often does it reproduce? Is there a required condition?
Always.
What is the expected behavior?
tls.createSecureContext should finish with no errors.
What do you see instead?
I get an error:
Additional information
Setting engine by crypto.setEngine doesn't help.
As far as I see, Node.JS uses PEM_read_bio_PrivateKey both in src/crypto/crypto_keys.cc/ParsePrivateKey for Sign.sign() routine and in src/crypto/crypto_context.cc/SecureContext::SetKey for tls.createSecureContext calls. While in first place it understands which engine is set, in second place the engine is ignored.
The text was updated successfully, but these errors were encountered: