Export a PKCS#12 file without an export password?

OpensslCommandPkcs#12

Openssl Problem Overview


I am generating exporting some pkcs#12 files for testing purposes. These files are not being used in production and only exist temporary during automated testing.

I am using the following command:

openssl pkcs12 -export -nodes -out bundle.pfx -inkey mykey.key -in certificate.crt -certfile ca-cert.crt

Why is it insisting on an export password when I have included -nodes?

My OpenSSL version is OpenSSL 1.0.1f 6 Jan 2014 on Ubuntu Server 14.10 64-bit.

Openssl Solutions


Solution 1 - Openssl

In interactive mode, when it prompts for a password, just press enter and there will be no password set.

If you are want to automate that (for example as an ansible command), use the -passout argument. It expects the parameter to be in the form pass:mypassword. Since we want no password:

openssl pkcs12 -export -nodes -out bundle.pfx -inkey mykey.key \
    -in certificate.crt -certfile ca-cert.crt \
    -passout pass:

Solution 2 - Openssl

tl;dr If you explicitly set the encryption algorithms both to NONE (the one for the key and the one for the cert), you will still have to provide a password but as no encryption is performed, it won't matter which password you provide as the password is simply ignored and the resulting file is not encrypted.

For a full command line sample, check out this reply:
https://stackoverflow.com/a/62863490/7878845

Very detailed answer:

-nodes means "don't encrypt private key" but in a PKCS#12 file, the certificates are encrypted as well, so even with -nodes you'd need an export password.

See documentation of -descert which says:

> Encrypt the certificate using triple DES; this may render the PKCS#12 file > unreadable by some "export grade" software. By default, the private key is > encrypted using triple DES and the certificate using 40-bit RC2.

So unless you use this option, the certificates are encrypted using RC2. You can change the algorithms for either key or certificate using the options -keypbe and -certpbe.

Also for openssl pkcs12 the -nodes option is only listed in the section:

> The options for parsing a PKCS12 file are as follows:

But you are not parsing such a file, you are creating it and if you look at

> The options for PKCS12 file creation are as follows:

the option -nodes is not even listed.

Just hitting return when prompted for a password also won't mean "no password" but it means "empty password" (your password is an empty string), which is legal. The reason why this works like no password in some cases is that some software will try to read PKCS#12 files with an empty string password first and only if that fails, prompt the user for an actual password, so if the password is empty, the user won't ever be prompted in these cases making it look like there is "no password" set.

This can cause issues in macOS and iOS, as Apple assumes that PKCS#12 always has a password set and it won't allow you to enter an "empty password", so if a file has an empty password set, it's impossible to import it on these systems. Firefox also had this issue in the very beginning but it was fixed 13 years ago.

When reading a PKCS#12 file, OpenSSL itself tries to distinguish "no password" and "empty password" only by guessing. Here is original code from the project:

 /* If we enter empty password try no password first */
 if(!mpass[0] && PKCS12_verify_mac(p12, NULL, 0)) {
     /* If mac and crypto pass the same set it to NULL too */
     if(!twopass) cpass = NULL;
 } else if (!PKCS12_verify_mac(p12, mpass, -1)) {
     BIO_printf (bio_err, "Mac verify error: invalid password?\n");
     ERR_print_errors (bio_err);
     goto end;
 }

The first time NULL is passed for password, the second time the empty string is parsed for password. Now let's look at the code when creating a P12 file:

 p12 = PKCS12_create(cpass, name, key, ucert, certs,
             key_pbe, cert_pbe, iter, -1, keytype);

Theoretically this call would create a PKCS#12 file without a password if cpass is NULL, however, when this call is being made, it cannot be NULL because if you follow the code path from the beginning of the function to the call above, there is no code path that would lead to cpass being NULL in the end.

 if(!cpass) {
     if(export_cert) cpass = passout;
     else cpass = passin;
 }

 if(cpass) {
   mpass = cpass;
   noprompt = 1;
 } else {
   cpass = pass;
   mpass = macpass;
 }

In case cpass was still NULL at the last if, it will be set to pass and pass is:

char pass[50], macpass[50];

This is a static variable and when stored to a pointer, this pointer cannot be NULL. There is no other code that ever assigns a different value to cpass, so cpass can be an empty string but it can certainly never be NULL.

The actual encryption happens at a function named PKCS12_add_safe_ex() and if look at this function, you see the following code:

    if (nid_safe == 0)
#ifdef OPENSSL_NO_RC2
        nid_safe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
#else
        nid_safe = NID_pbe_WithSHA1And40BitRC2_CBC;
#endif

    if (nid_safe == -1)
        p7 = PKCS12_pack_p7data(bags);
    else
        p7 = PKCS12_pack_p7encdata_ex(nid_safe, pass, -1, NULL, 0, iter, bags, ctx, propq);
    if (p7 == NULL)
        goto err;

nid_safe is a number that tells the function which encryption method to use. As you can see, if it isn't set (value of 0) a default is used which is either RC2, or in case not available, 3DES.

However, if nid_safe is -1, which means NONE, an alternative function is being used and pass is not even passed to this function, so the value of pass is entirely irrelevant in that case. So you can provide any password you like, if you set the encryption algorithm for the key and the certificate to NONE, nothing is encrypted.

Solution 3 - Openssl

To generate unencrypted PKCS12 file with just OpenSSL command line utility, call following command:

$ openssl pkcs12 -export -keypbe NONE -certpbe NONE -nomaciter -passout pass: -out bundle.pfx -inkey mykey.key -in certificate.crt -certfile ca-cert.crt

When encryption algorithm for private key (-keypbe) and certificate (-certpbe) is set to NONE then openssl's pkcs12 library ignores password argument and does not encrypt private key and certificate.

This can be verified by openssl pkcs12 -info command:

$ openssl pkcs12 -info -in bundle.pfx -noout -passin pass:
MAC: sha1, Iteration 1
MAC length: 20, salt length: 8
PKCS7 Data
Certificate bag
Certificate bag
PKCS7 Data
Key bag

Please note that when reading existing PKCS12 file with openssl command line tool, it is needed to specify -passin pass: argument even when data are not encrypted. This is because openssl command line tools cannot detect if PKCS12 file is encrypted or not. When empty password is specified then openssl first tries to read file as unencrypted. And if it fails then openssl tries to read that file as encrypted with empty password.

When I generate bundle.pfx without specifying -keypbe NONE -certpbe NONE -nomaciter arguments then openssl pkcs12 -info shows following:

$ openssl pkcs12 -info -in bundle.pfx -noout -passin pass:
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 8
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048

So data are in this case encrypted with empty password.

Solution 4 - Openssl

Faced same issue when needed to convert certificate for openconnect

Needed additional step to make it without password

openssl rsa -in private.key -out private.nopwd.key

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionF21View Question on Stackoverflow
Solution 1 - OpensslF21View Answer on Stackoverflow
Solution 2 - OpensslMeckiView Answer on Stackoverflow
Solution 3 - OpensslPaliView Answer on Stackoverflow
Solution 4 - OpensslUnknown_GuyView Answer on Stackoverflow