-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
How to specify empty password for PKCS#12 client certificate? #1308
Comments
Try using the --key option with an empty string and see if that solves the problem, like this: --key "" If not, then one other thing you can try is importing the PKCS#12 data into your Keychain, and then using --cert with the identity name as it appears in Keychain Access. |
@nickzman Thanks. 😄 Trying with
Importing into the keychain, though it might work, probably isn't going to be feasible. Aiming for something scriptable, that can be used with bulk keys for testing. Should this be considered a bug in curl not picking up the empty string? |
Can you even import it into the Keychain? I must say, it's quite unusual for a PKCS#12 file to have an empty password. If Keychain Access can't understand it, then the problem is with Apple's Security framework, not libcurl, since on macOS, libcurl uses Apple's PKCS#12 import function. |
Recall for --cert that it expects a file with both the client certificate and private key. It is typical pfx contains both objects but check. I wonder if maybe you don't have both or it has a password even though you think it doesn't?
hm. Is a blank password the same as no password? That password error only happens on errSecPassphraseRequired. so if there is an empty password I guess it could be seen that way since parse_cert_parameter doesn't return an empty string as a password, instead it returns NULL (ie no password). OpenSSL PKCS12_parse for example seems to be ok with this. CopyIdentityFromPKCS12File in darwinssl.c relevant area: const void *cKeys[] = {kSecImportExportPassphrase};
const void *cValues[] = {password};
CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
password ? 1L : 0L, NULL, NULL);
CFArrayRef items = NULL;
/* Here we go: */
status = SecPKCS12Import(pkcs_data, options, &items); I assume password is NULL based on my read of the code but cannot confirm this at runtime since I don't use mac. So 0 is set for numValues param for CFDictionaryCreate. In other words based on my read it seems no password is being set. And so again this makes me wonder is a blank password the same as no password. |
@nickzman Thanks, that's interesting. You're right, KeyChain Access gets it wrong as well, also refusing to import it with no password. When trying the import, it first pops up a dialog asking for a password: Conversely, trying the exact same certificate file with Firefox imports it fine. Firefox also asks for a password: So it seems there's something wrong with Apples' import code then. As a data point, the way I created the PKCS#12 cert file was by converting the PEM cert and it's key:
For both of those password lines with the OpenSSL command, I just pressed enter. No other input. Hmmm, I guess it's not going to be feasible to get a blank password passed in for PKCS#12 certs at this stage. Might need to turn to curl with the OpenSSL backend instead of Apple's "Secure"Transport, and use PEM format certs. Will test it tomorrow. 😄 @jay Yeah, not sure. My present thinking is it might be useful to file a bug with Apple. I've never personally seen Apple ever take an interest, so no idea if it's worth putting time into. What do you reckon? |
If it's useful, I can provide the PKCS#12 file, cert (PEM format), PEM cert key, ca chain, etc. They're all dev-only ones I generated earlier today, for a client-server thing I'm working on. (https://dbhub.io) It's no real stress to generate new ones again tomorrow. 😉 |
You probably should but first if you are capable of compiling the source here are two things you can try. First it's possible that diff --git a/lib/vtls/darwinssl.c b/lib/vtls/darwinssl.c
index 25a8ab8..7795aa6 100644
--- a/lib/vtls/darwinssl.c
+++ b/lib/vtls/darwinssl.c
@@ -1003,6 +1003,15 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
/* Here we go: */
status = SecPKCS12Import(pkcs_data, options, &items);
+ /* If auth fails and there is no password then try again with an empty
+ password. Apparently Apple treats those two things differently? */
+ if((status == errSecAuthFailed || status == errSecPassphraseRequired) &&
+ !cValues[0]) {
+ CFRelease(options);
+ cValues[0] = "";
+ options = CFDictionaryCreate(NULL, cKeys, cValues, 1L, NULL, NULL);
+ status = SecPKCS12Import(pkcs_data, options, &items);
+ }
if(status == errSecSuccess && items && CFArrayGetCount(items)) {
CFDictionaryRef identity_and_trust = CFArrayGetValueAtIndex(items, 0L);
const void *temp_identity = CFDictionaryGetValue(identity_and_trust, |
@jay Thanks. It's been ages since I did C dev work, so wasn't sure where to attempt the With your patch though, that was simple to apply then test. But still no luck with the certificate working. 😉 The error message changed, and now is:
For reference, the commands I tried with the patched version are:
Should I still try to figure out the |
I meant pass the second parameter as that. If you go to the doc for SecPKCS12Import it doesn't say whether or not that parameter is optional. So the two things I thought we could try are passing it a blank password (which doesn't work according to your error -50, which seems to mean invalid param list) or just passing it NULL for options. Revert the earlier patch and then try this diff --git a/lib/vtls/darwinssl.c b/lib/vtls/darwinssl.c
index 25a8ab8..dd9c3c1 100644
--- a/lib/vtls/darwinssl.c
+++ b/lib/vtls/darwinssl.c
@@ -1002,7 +1002,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
CFArrayRef items = NULL;
/* Here we go: */
- status = SecPKCS12Import(pkcs_data, options, &items);
+ status = SecPKCS12Import(pkcs_data, password ? options : NULL, &items);
if(status == errSecSuccess && items && CFArrayGetCount(items)) {
CFDictionaryRef identity_and_trust = CFArrayGetValueAtIndex(items, 0L);
const void *temp_identity = CFDictionaryGetValue(identity_and_trust, |
Here's another one you could try if that one gives you an error. Other than this I'm out of ideas diff --git a/lib/vtls/darwinssl.c b/lib/vtls/darwinssl.c
index 25a8ab8..0bb0f66 100644
--- a/lib/vtls/darwinssl.c
+++ b/lib/vtls/darwinssl.c
@@ -998,7 +998,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
const void *cKeys[] = {kSecImportExportPassphrase};
const void *cValues[] = {password};
CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
- password ? 1L : 0L, NULL, NULL);
+ 1L, NULL, NULL);
CFArrayRef items = NULL;
/* Here we go: */ |
I figured out why the first one returned bad parameter list, apparently I didn't implement it properly because CFStringRef must be passed instead of char *. So here's yet a third new patch for you to try similar to the failed one. Note all of these patches are mutually exclusive. diff --git a/lib/vtls/darwinssl.c b/lib/vtls/darwinssl.c
index 25a8ab8..04f0322 100644
--- a/lib/vtls/darwinssl.c
+++ b/lib/vtls/darwinssl.c
@@ -1003,6 +1003,15 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
/* Here we go: */
status = SecPKCS12Import(pkcs_data, options, &items);
+ /* If auth fails and there is no password then try again with an empty
+ password. Apparently Apple treats those two things differently? */
+ if((status == errSecAuthFailed || status == errSecPassphraseRequired) &&
+ !cValues[0]) {
+ CFRelease(options);
+ cValues[0] = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+ options = CFDictionaryCreate(NULL, cKeys, cValues, 1L, NULL, NULL);
+ status = SecPKCS12Import(pkcs_data, options, &items);
+ }
if(status == errSecSuccess && items && CFArrayGetCount(items)) {
CFDictionaryRef identity_and_trust = CFArrayGetValueAtIndex(items, 0L);
const void *temp_identity = CFDictionaryGetValue(identity_and_trust, |
Thanks @jay, going through those now. The first of the three is a no go:
And just for the heck of it, though it doesn't help either:
Will try the 2nd patch in a bit. |
No luck with the 2nd either:
Trying it with an incorrect password, just to see what happens:
Correct failure. 😉 And also:
Trying the 3rd patch next. |
And no joy there either. 🤕
I'll just use curl with OpenSSL compiled in, instead of Apple's (at present crappy) "Secure"Transport. That's been working fine earlier today with certs in PEM format instead. 😄 @jay Thank you very much for putting in this level of effort. It's hugely helpful to have people around doing that. 😄 |
As a data point, looking through opensource.apple.com, the closest stuff I can find to being the "Secure"Transport code is this: https://opensource.apple.com/source/Security/Security-57740.31.2/ Anyone know if the "Secure"Transport code is open sourced (eg possible to peer review), or if it's relying on security by obscurity? |
No problem, thank you for testing. Apparently this is a known issue. I found this: |
Interesting, thanks. 😄 Close this issue now, as it not really a curl problem. |
I figured that had to be Apple's bug, not ours. And yes, Secure Transport is open source and part of the Security framework on all Apple operating systems. That was how the infamous "goto fail" bug was found a while back. |
Thanks @nickzman. As a data point for anyone interested, this seem to be the PKCS#12 importing code: |
+1
|
The problem situation
Attempting to use curl (for testing purposes) on OSX.
When connecting to the (internal development) HTTPS server, curl complains the PKCS#12 file needs a password. Which is bizarre, as there is no password set on the file. 😉
Is there a way to specify "empty password", so that it works? I've tried a few obvious variations, and no joy so far:
I expected the following
Successful connection to the remote server.
If I manually add a password to the PKCS file using openssl, then it works. eg adding
:password
to the end of the file argument. The trouble I'm hitting is only where there's no password set in the file.curl/libcurl version
Using curl installed through Homebrew. eg it's using "Secure"Transport
The problem also shows up when using the system curl provided by OSX:
operating system
OSX 10.11.6
The text was updated successfully, but these errors were encountered: