4

Assuming I'm working on an environment that currently uses MD5 to hash passwords, and I want to switch to something more secure.

I already heard of bcrypt, but I had another idea.

Would it be possible to use RSA2048 to hash passwords if I just threw away the private key of the key pair?

If so, is that less secure then using a dedicated hashing algorhitm like for instance bcrypt? (Assuming you use salt/pepper in both cases in addition):

I have following process in mind:

  • Every password gets its own public RSA2048 key, the private keys are discarded.

  • MD5 Hash the password

  • Encrypt the MD5 Hash using RSA2048 with that password's specific RSA2048 public key
  • Pepper the result with pepper of fixed lenght

Is this a secure password hashing scheme? On the same level as running a password through bcrypt or scrypt?

Magisch
  • 293
  • 2
  • 9
  • Are you going to generate a new key pair for each password (very expensive) or use the same key for all? – 5gon12eder Feb 08 '17 at 08:37
  • @5gon12eder My first idea was to use one key for all, but I suppose generating a new key for each password wouldn't be too bad (You'd only have to do it during user creation and password changes, right?). Wouldn't that in essence work like a super-salt? – Magisch Feb 08 '17 at 08:39
  • 4
    See also [Using RSA for hash](http://stackoverflow.com/questions/7561989/using-rsa-for-hash) – Sjoerd Feb 08 '17 at 08:39
  • 1
    The problem with using a single key for all passwords is that if an attacker breaks (or otherwise obtains) that key, they have a universal way to decrypt an arbitrary number (all) of your passwords. While you could argue that this is not feasible, I still see it as a disadvantage compared to using a non-bijective hash function which an attacker would have to break for each password individually. – 5gon12eder Feb 08 '17 at 08:46
  • @5gon12eder So suppose I use a different key for each password, can I forego the salt in favor of that key? Wouldn't the key itself act as a super-salt then? – Magisch Feb 08 '17 at 08:48
  • Since the point of a salt is to make potentially equal passwords hash to different values, using different keys would provide that property already. But I'm still not convinced that using this scheme would necessarily be a good idea. Another question of course is: how do you deal with passwords of different lengths? You could first hash them using a standard cryptographic hash function and then encrypt that hash. That would probably be a very secure scheme. – 5gon12eder Feb 08 '17 at 08:53
  • @5gon12eder Was thinking something of that sort - hasing it and then encrypting the hash (having the encryption key stored on the server). However, I'm not a cryptography guy and every cryptography guy I've ever met told me to not meddle with my own encryption schemes, so I'd want to know if this is a secure approach first. – Magisch Feb 08 '17 at 08:56
  • @5gon12eder I edited my question with an approach I have in mind. – Magisch Feb 08 '17 at 09:21
  • The approach you proposed is just as weak as plain MD5 as it involves the MD5 anyway – Lie Ryan Feb 08 '17 at 10:12
  • @LieRyan How come? The actual issue if someone wanted to break this wouldn't be in breaking the MD5, but in breaking the RSA2048 for which no private key is anywhere. How'd using MD5 to cat the passwords to the same lenght be any worse then XORing them with a block of blanks or concating them to a fixed size with 0's? – Magisch Feb 08 '17 at 10:15
  • The main problem with plain MD5 that makes it unsuitable for modern password hashing is that it is fast, extremely fast. Though there are better choices, when properly salted (to eliminate rainbow attack) and with high iteration count (to slow down the algorithm), MD5 is actually not a very bad password hashing method. The reason MD5 is considered broken is because it had a very weak collision resistance, but that doesn't really apply to password hashing. – Lie Ryan Feb 08 '17 at 10:36
  • @LieRyan In my scheme, I'm using MD5 only to concatenate the passwords to a fixed lenght before inputting them into the "real" hashing algorhitm, which is RSA2048 where I throw away the private key. I thought the fact that I use a different public key for every password acts as salt, and I'd append pepper later as necessary. – Magisch Feb 08 '17 at 10:37
  • 2
    @Magisch: If someone make a collision on the MD5, they'll have a collision on the RSA as well. Also, adding a single step of RSA wouldn't really add sufficient work factor against brute force attack. Your proposed method doesn't really address these issues. – Lie Ryan Feb 08 '17 at 10:56
  • There is a hashing scheme which is vaguely similar to RSA which was actually proposed in the Password Hashing Competition (but eventually lost to Argon2), called Makwa, which is based on repeated squarings. – forest May 24 '19 at 00:05

2 Answers2

4

Assuming I'm working on an environment that currently uses MD5 to hash passwords, and I want to switch to something more secure.

If this is a practical question, then it deserves a practical answer:

Don't invent your own password storage schemes. Do it according to best practices.

There is no benefit to your scheme over using the standard options (pbkdf/bcrypt/scrypt/argon2). There are many potential security downsides, plus general bugs in the implementation (since you're constructing this yourself instead of using a commonly-used library). Implementing this in a production environment borders on professional negligence.

On the subject of its security: at the very least you suffer from it being easy to parallelize, so an attacker who has obtained a dump of the database can do the old "spin up a bunch of ec2 instances" attack to crack your hashes. This is the point behind bcypt, but you don't seem to have addressed that at all.

Xiong Chiamiov
  • 9,384
  • 2
  • 34
  • 76
4

Your proposed hashing scheme has no real advantage over existing schemes and some potential downsides.

A password hashing scheme must have three key properties:

  • it must be one-way: no way to recover the password from the hash, the only way to find the password is to guess it;
  • it must be salted: each hash incorporates a unique value so that an attacker can't reuse the same computations to attack many hashes;
  • it must be expensive (at the very least, slow): calculating a hash must require significant resources, so that mass attacks require a huge investment in hardware.

Your proposed scheme is one-way — but the first hashing step with MD5 is sufficient for that. (Incidentally, although MD5 is has no known weaknesses for this use case, it's sufficiently broken that it's strongly recommended not to use it at all. But you could just substitute SHA-256 and your question would remain valid, as would my answer.) Performing an RSA operation on top doesn't improve this aspect.

Your proposed scheme is salted, by using a different key for each hash. That's fine. But there's an easier way to do the salting: you essentially take hash(password + salt) instead of hash(password). (The devil is in the details but I'll stick to the high-level summary here.) So we already know how to do this.

Your proposed scheme is not expensive. Expensiveness needs to be tunable, to go to the maximum resource usage that is bareable on the legitimate server that verifies the password, and keep up with technological evolution (e.g. increase the number of iterations from 105 to 106 every 5 years as computers are ~10 times faster). A simple way to do this is to take a fast hash and iterate it, which requires a long sequential computation; this is what PBKDF2 does. More complex schemes try to be optimal on PC hardware and less good on e.g. GPU or FPGA; see Do any security experts recommend bcrypt for password storage?. It is not clear how your proposal could be tuned for expensiveness: use larger RSA keys?

A major potential flaw in your proposal is that the core RSA operation (modular exponentiation) does not have good security properties when used directly, because it is subject to algebraic relations such as xe·ye = (x·y)e. This is why “RSA encryption” is not a simple exponentiation, but instead adds some padding to the data; OAEP is the modern standard. You cannot use OAEP as it is because it's non-deterministic: running it twice on the same input yields different ciphertexts. It is probable that a derandomized version of OAEP would work, or even some simpler scheme given that you're in a very special case where no ciphertext is ever going to be decrypted, which means that many attacks on encryption schemes are not applicable.

Note that you cannot go as far as not adding any padding, not without additional constraints: y = xe mod n absolutely must wrap modulo n, otherwise x can be computed from y simply by taking the eth root in the reals. As specified in your question, e is not constrained, allowing the common choice of e = 3. Since x is a 128-bit number (256-bit if you use SHA-256 instead of MD5) and n is a 2048-bit number, xe would not wrap, so x can be recovered trivially from y. There's still the one-wayness of the hash, but you lose the salting which was only provided by the RSA step. There are easy solutions to this problem, of course: require a sufficiently large e, and include a salt in the hashed data. But if you have to rely on the rest of the design, why would you put RSA into the mix at all? This illustrates the risk of adding more stuff to a cryptographic algorithm: you can make it weaker even if you thought you were adding a security feature.

To summarize:

  • Your proposal lacks a key property of password hashing functions: expensiveness.
  • It may be possible to modify this proposal to have this property, but it is not clear how.
  • You add a potential attack vector (algebraic relations); defenses exist but it is not clear what you are gaining.
Gilles 'SO- stop being evil'
  • 50,912
  • 13
  • 120
  • 179
  • So what you are saying (in condensed form) is that I'd be better off using a garden variety bcrypt implementation? – Magisch Feb 08 '17 at 18:10
  • @Magisch Sure. If you aren't a professional cryptographer, it's always a safe bet that you'd be better off with an algorithm that has been vetted by professional cryptographers rather than rolling your own. Even if you are a professional cryptographer, you are better off with an algorithm that has been vetted by *many* professional cryptographers than doing your own thing. [As Schneier puts it](https://www.schneier.com/blog/archives/2011/04/schneiers_law.html) (and Babbage before him…), anybody can invent a cryptosystem they can't break, the trick is to invent a system that others can't break. – Gilles 'SO- stop being evil' Feb 08 '17 at 18:17
  • I'm a apprentice software developer and standard modus operandi in my company is using md5 to hash passwords. I read about it a bit and it says that MD5 (without salt or pepper) is ridiculously vulnerable to rainbow table attacks and collision. That prompted me to read up on salt and pepper and at least look into doing something less vulnerable for my applications. – Magisch Feb 08 '17 at 18:21
  • @Magisch Ok, then as a practical matter, go read [our reference question on the topic](http://security.stackexchange.com/questions/211/how-to-securely-hash-passwords). Thomas's answer is interesting, but if you just want the TL&DR, it's “use PBKDF2, bcrypt or scrypt”. BTW, collisions are not the problem with the use of MD5 for password hashing (they're a problem for most other uses of MD5); even rainbow tables aren't the #1 problem: most people's passwords' MD5 can be found in a simple Google search! Salting is absolutely indispensible to defend against this attack method. – Gilles 'SO- stop being evil' Feb 08 '17 at 18:28