1

I got the following problem: In an Java Application I want to store some configuration data in an encrypted, local file. This file might be used for confidential data, like user credentials.

This file should be accessible by using a password (and only a password). Now most trustworthy people and reference implementations use random salts. I completely understand that this is a reasonable choice. But if my application terminates and will be started later, the random salt is not available anymore. This application is stand-alone so no additional database could be used as a salt store.

For my software the user shall only type in the password (means: no user name, no salt, no favorite animal or colour).

Now my idea was deriving the salt from the password (e.g. by using the first 16 bytes of SHA-256).

My questions are:

  • How (in)secure would this implementation be?
  • What is a common way of encrypting stuff with only a password and would be a better alternative?

What is not the aim of this question:

  • Where to store salts
  • Secure algorithms and crypto implementation (of course, I did not implement crypto by myself)
  • Architectural improvements (nop, I do not want a global database for storing stuff)
SGEuropa
  • 13
  • 3
  • Usually a salt value is not randomly generated on each application startup anyway, mostly they are hardcoded. – f1sh Mar 10 '19 at 15:17
  • Got an article (unfortunately only in German) written by specialists that says "Developers shall randomly create the salt for every usage to avoid rainbow table attacks. In contrast to storing the salt together with the password hash in a user database, a hard coded, constant salt can be easily readout via decompilation." (https://www.heise.de/developer/artikel/CogniCrypt-Kryptografie-richtig-nutzen-4211551.html) – SGEuropa Mar 10 '19 at 15:24
  • Salts are for *hashing*, not encrypting. – jonrsharpe Mar 10 '19 at 15:26
  • 1
    Salts are used for one-way hashing, not two-way encryption. By deriving the salt from the password, you're not necessarily making things worse, but you're not making them more secure, either. It doesn't protect against rainbow table attacks, for instance. What I _do_ find a bit worrying is that you're coming up with your own encryption scheme. If I were you, I'd just find an encryption library and read its docs for how to use it effectively. – yshavit Mar 10 '19 at 15:27
  • 2
    @f1sh "*Usually a salt value is not randomly generated on each application startup anyway, mostly they are hardcoded.*" - Just don't. That defeats the entire point of salting. For each password, a separate salt should be generated. – Turing85 Mar 10 '19 at 15:30
  • Why does the Java library's implementation of PBEKeySpec (Password based encryption key specification) require a NotNull salt then? – SGEuropa Mar 10 '19 at 15:32
  • @SGEuropa What do you mean by your last statement? – Turing85 Mar 10 '19 at 15:36
  • @Turing85 My bad, forgot the word "require" :-D Thanks for the hint. – SGEuropa Mar 10 '19 at 15:37
  • Still unclear what you mean though... – Turing85 Mar 10 '19 at 15:37
  • So there are a few Java classes for password based encryption. The implementation of the key specification (PBEKeySpec) requires a salt in the constructor. And I just wondered why this is a requirement after yshavit and jonrsharpe commented that salts should not be used for that. – SGEuropa Mar 10 '19 at 15:44
  • PBEKeySpec also has a constructor that doesn't take a salt, so you could use that. I'm actually not sure why they have one that takes a salt. It could be that there are encryption algorithms that do use a salt for reasons I don't understand. :) (It looks like PBEwithSHA1AndDESede does.) But even then, I think the fact remains that if you derive the salt from the password, then it's not really a salt -- it's just the same password, with one more step in the encryption algorithm. If such a step were useful, it would have been in the algorithm already. – yshavit Mar 10 '19 at 16:53
  • To echo what @yshavit has said, a salt protects against pre-computation attacks like rainbow tables. A "salt" derived from the password, however, provides absolutely **no protection** whatsoever against precomputation attacks. None. It's just like having no salt at all. – President James K. Polk Mar 10 '19 at 18:05
  • @JamesKPolk So in consequence you would not use a salt at all or is there another good practice (under the given circumstances) improves the security? – SGEuropa Mar 10 '19 at 19:04
  • Possible duplicate of [Where do you store your salt strings?](https://stackoverflow.com/questions/1219899/where-do-you-store-your-salt-strings) – Raedwald Mar 11 '19 at 14:48
  • @Raedwald IMHO it's not a duplicate because the question is about storing salts strings and not deriving them. Nevertheless thank you for the suggestion. :-) – SGEuropa Mar 13 '19 at 14:37
  • @SGEuropa If you think this is not a duplicate of that question, edit your question to include a link to that question *and* to include an explanation of why this is not a duplicate of that question. – Raedwald Mar 13 '19 at 15:17

2 Answers2

0

To encrypt data, one needs a key not a password. There are key-derivation-functions to get a key from a user password.

A salt can be used for password hashing, but it cannot be used for encrypting data. There is a similar concept for encryption though, the random values there are called IV or Nonce and are stored together with the encrypted data.

The best thing you can do is

  1. use a key-derivation-function with a salt, to get a key from the password.
  2. With the resulting key you can encrypt the data.
  3. In this case the salt can be stored inside the encrypted data container (the IV is already there), so there is no need for a global database.

To answer your original question: Derriving a salt from the password negates the whole purpose of the salt, it just becomes a more complex hash function.

martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
0

First, I strongly recommend against devising novel encryption formats if you can help it. It is very difficult to do them correctly. If you want an encryption format that does what you're describing, see JNCryptor, which is an implementation of the RNCryptor format. The RNCryptor format is designed precisely for this problem, so the spec is a good source of information on how you could create your own if you don't want to use it directly. (I'm the author of RNCryptor.)

See also libsodium. It's a better encryption format than RNCryptor for various technical reasons, but it's a bit harder to install and use correctly. There are several Java bindings for libsodium.

When you say "of course, I did not implement crypto by myself," that's what you're doing. Crypto schemes are more than just the AES code. Deciding how to generate the salts in a novel way is implementing crypto. There are many ways to put together secure primitives (like salts) in simple ways and make them wildly insecure. That's why you want to use something well established.

The key take-away is that you store the salt with the data. I know you said this isn't about storing the salt, but that's how you do this. The simplest way to do this is to just glue the salt onto the start of the cipher text and store that. Then you just read the salt from the header. Similarly, you could put the whole thing in an envelope if that's more convenient. Something as simple as JSON:

{ "salt": "<base64-salt>",
  "data": "<base64-data>" }

It's not the most efficient way to store the data, but it's easy, standard, and secure.

Remember, salts are not secrets. It is fine that everyone can read the salt.


OK, enough of how to do it right. Let's get to your actual question.

Your salting proposal is not a salt. It's just a slightly different hashing function. The point of a salt is if the same password is used twice (without intending to be the same password), then they will have different hashes. Your scheme fails that. If I implement the same approach as you do, and I pick the same password as yours, then the hash will be the same. Rainbow tables win.

The way you fix that is with a static salt, not a modified hash function. You should pick a salt that represents your system. I usually like reverse DNS for this, because it leads to uniqueness. For example: "com.example.mygreatapp". Someone else would naturally pick "org.example.ourawesomedb". You also can pick a long random string, but the important thing is uniqueness, so I like reverse DNS. (Random strings tend to make people think the salt is a secret, and the salt is not a secret.)

That's the whole system; just pick some constant salt, unique to your system. (If you had a username, you'd add the username to the salt. This is a standard way to construct a deterministic salt.)

But for file storage, I'd never do it that way.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610