1
$\begingroup$

I have to protect a key pair with a passphrase and to store these information someway in a table. I read that I can protect the key pair (specifically, the private one) using a digest of the passphrase as a symmetric key for a symmetric algorithm.

I also found that a good way to store a password (and therefore, I think also a passphrase) is hashing it with a salt with a password hashing algorithm (e.g. PBKDF2, bcrypt, etc.) and save the digest and the salt.

The problem is that I'm pretty sure that storing together, the salt, the digest of the passphrase - which is at the end the symmetric key used to encrypt the private key - and the private key itself, encrypted with the digest of the passphrase, is a naive solution.

But if I don't save the digest of passphrase together with the encrypted private key, how can I be sure, when decrypting the private key with an inserted passphrase, that the passphrase and the obtained private key are the correct ones?

I tried to look for an answer on the forum but I didn't find any good solution to my problem. Can you help me?

$\endgroup$
  • $\begingroup$ Your questions have been covered extensively in this forum, so you're unlikely to get an answer. However, you're on the right track, but you're confusing two distinct concepts; hashing and encryption. Encryption is reversible, hashing is not. Read this thread [stackoverflow.com/questions/326699/… to understand the use-cases. Regarding verification of decrypted data, read up on authenticated encryption [en.wikipedia.org/wiki/Authenticated_encryption] $\endgroup$ – hunter Nov 25 '16 at 11:12
  • $\begingroup$ I can distinguish the concepts of hashing and encryption: the point is that I don't need only to store a password (passphrase, for my purpose), which sure I could hash with a salt before storing it; I have also to store a secret (the private key) in an encrypted way, with the key dependant on the passphrase (and so I also need to maintain the corrispondance with the passphrase itself). If I use - as suggested - the digest of the passhprase as a symmetric key to encrypt the PK, obviously it's a silly risk to store the encrypted PK along with the encryption key (i.e. hashed passphrase). $\endgroup$ – M-elman Nov 25 '16 at 11:43
2
$\begingroup$

The problem is that I'm pretty sure that storing together, the salt, the digest of the passphrase - which is at the end the symmetric key used to encrypt the private key - and the private key itself, encrypted with the digest of the passphrase, is a naive solution.

Storing the digest of course negates both the PKBDF function and the encryption. Storing the salt and wrapped key is of course not a good idea.

But if I don't save the digest of passphrase together with the encrypted private key, how can I be sure, when decrypting the private key with an inserted passphrase, that the passphrase and the obtained private key are the correct ones?

The easiest way to do this is to use the key for authenticated encryption such as GCM or SIV. If the authentication succeeds then you know that the key was encrypted with the key derived from the passphrase.

So you'd store:

salt | authenticated_ciphertext

where the tag is the authentication tag calculated by the authenticated encryption.

You'd calculate the salt by using a secure random number generator, the ciphertext as:

key = PKBDF2(password, salt, iteration_count, key_size)
authenticated_ciphertext = SIV_AES(key, encoded_rsa_key, [])

Now decryption will fail as SIV_AES includes an IV that doubles as an authentication tag. [] is the additional authenticated data (AAD) which is the empty array, i.e. it is not present.


A disadvantage of this scheme is that you don't know on failure if you have the wrong passphrase or the wrong ciphertext. The simplest way to deal with this is to include a specific ID with the ciphertext and treat it as Additional Authenticated Data in your authenticated encryption. Furthermore, you might want to indicate the protocol version:

protocol | id | salt | authenticated_ciphertext

and the calculations:

key = PKBDF2(password, salt, iteration_count, key_size)
authenticated_ciphertext = SIV_AES(key, encoded_rsa_key, protocol | id)

so now you've got a protocol and id which are also protected by the authentication tag.


Notes:

  • in above scheme the iteration_count and key_size are considered configuration parameters (i.e. stored separately from the ciphertext), possibly derived from the protocol in the later version;
  • the above scheme has the disadvantage that the authentication tag can only be verified after decrypting; this is both a disadvantage with regards to security as well as the legit user having to wait a long time for large plaintext - I won't go into the details of these weaknesses here;
  • note that if active attacks are possible that the stored data may be altered; in that case you should consider that any part of the data (including the protocol, id or just the authentication tag) may have been altered - relying on any specific error condition is in that case not possible anymore.
  • don't forget to zero your sensitive data in memory after you've concluded your private key calculations or if you've encountered an error condition;

If you're starting to see the full scale of what you're attempting, you might want to use an existing scheme instead.

$\endgroup$
  • $\begingroup$ Come to think of it, the fact that you first need to descrypt to validate the synthetic IV is pretty damning for using SIV to encrypt/decrypt keys. If I'd create a wrapping function I'd make sure that (1) the key can be encrypted in place and (2) that I'd only decrypt after verifying the authentication tag. These things may not pop up when verifying the security proof, but they are definitely an issue in practice. $\endgroup$ – Maarten - reinstate Monica Nov 25 '16 at 13:19
  • $\begingroup$ I'm not sure if I understood the entire procedure; correct me if I'm wrong. You are suggesting to generate a symmetric key (1) through PBKDF2 using the passphrase and salt in input (along with the others parameters). Then to use AES in SIV mode to generate a ciphertext of the id value (am I right?) and the private RSA key I want to keep secret using the symmetric key (1) produced before. In the end I store together the id, the salt and the result of the previous step. $\endgroup$ – M-elman Nov 27 '16 at 19:31
  • $\begingroup$ In decryption I use the inserted passphrase to generate a symmetric key (1) in the same way (through PBKDF2 and using the corresponding salt) and then I decrypt what you called "authenticated_ciphertext" with AES SIV using (1). Now I should obtain - I think - a concatenation of the ID and the private key, but I still can't uderstand how can I be sure that I typed in the correct passphrase and so that I obtained the correct private key: maybe is it enough to check if the obtained ID is equal to the one stored in the same tuple of the table? $\endgroup$ – M-elman Nov 27 '16 at 19:37
  • 1
    $\begingroup$ You use the authentication tag (the which doubles as IV in SIV) to validate that you've got the right password / wrapped key combination. You could also use GCM (as long as your derived wrapping key is unique); SIV however was made for this kind of purpose. Since CBC does not have an authentication tag this scheme would not work (directly) with CBC mode of operation. $\endgroup$ – Maarten - reinstate Monica Nov 27 '16 at 21:52
  • 1
    $\begingroup$ Probably best to use GCM. Make sure your key / IV combination is unique and that you use the full tag and you should be golden. 12 bytes for a possibly random IV is not too much to spare. Alternatively, if the salt is always random then the resulting key is always random and you could use a zero IV. $\endgroup$ – Maarten - reinstate Monica Nov 29 '16 at 18:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.