When using public key cryptography, it is often customary to store private keys in an encrypted format, since they are of course supposed to be a secret. This is reflected in the OpenSSL C API, which provides functions like PEM_write_PrivateKey
, which takes as function arguments an optional cipher to use to encrypt the key (like AES). Then, when reading the encrypted key back in from disk, the OpenSSL API provides functions like PEM_read_PrivateKey
, which allows the user to provide a function pointer used as a callback so the application can provide OpenSSL with the password for the encrypted key.
But what confuses me is that the OpenSSL API also seems to let the user provide a password callback when reading in a Public key. For example, one API function signature for reading in a public key is:
EVP_PKEY *PEM_read_PUBKEY(FILE *fp, EVP_PKEY **x,
pem_password_cb *cb, void *u);
So what is the purpose of providing a password callback when reading in a public key? AFAIK it makes no sense to encrypt a public key, since a public key is by definition supposed to be available to the general public. So why does the OpenSSL API have a function parameter here that takes a password callback?
As mentioned in this comment, any PEM-encoded data can be encrypted. Message encryption for privacy-enhanced mail (PEM) is defined in RFC 1421 and in the context of your question, it is interesting to look at the example message in section 4.6 Summary of Encapsulated Header Fields
Looking at the 1.1 branch of OpenSSL it has a function
PEM_read_bio()
that supports reading such a message and splitting it into its name (as given in the top line), header (the name-value pairs below that) and data (the base64-encoded stuff):All OpenSSL
PEM_read_XYZ()
functions at some point invoke it, fromPEM_bytes_read_bio()
, because they are all implemented in the same way by means of macro expansions. That function contains the following calls:to split the message, then
to figure out which type of encryption information is found in the header of that message and fill an
EVP_CIPHER_INFO
object with it, and thento do the decryption of the data based on the cipher information found -- again if needed. Note the
cb
parameter which stands for callback, a mechanism to get input for any passphrase if needed.Now what might be confusing, is that certain private key formats, like for example PKCS#8, also have their own mechanism of storing encryption information independent of the PEM encoding. Technically speaking, it should be possible to apply encryption to such keys twice: once at the PEM level and once at the PKCS#8 level. The OpenSSL tools for generating or converting to PKCS#8 formatted keys do not seem to offer that option though. Also, none of the tools seem to expose the option of encrypting any generated public key PEM files, unless a private key is included as well.
You can check some of the outputs to see if they match my story. First, generating an RSA key pair to a PKCS#1 format, nothing encrypted:
Then the same command, but using encryption, which happens at the PEM level, as you can see in the headers:
Finally generating a similar key but now to PKCS#8, which has its own encryption and therefore does not get encrypted at the PEM level. You can see that the PEM headers are not there.
If all my reasoning is correct, then the prompt "Enter PEM pass phrase" is inaccurate since this is not a PEM-level encryption but a PKCS#8-level encryption.