2

In most cases I need to encrypt a string with a password and then send/save it somewhere. Later I want do decrypt it with the password. I am not encrypting nuclear missile codes or medical patient data! The ideal would be 2 functions:

string Encrypt(string plainText, string password);
string Decrypt(string cipherText, string password);

I had a look at the crypto documentation... Oh boy! So I try to code the above calls myself (see a proof of concept using AES Managed and Base64 encoded payload). I am no crypto expert, why do I have to code that? I probably did somethings wrong...

  1. To derive the key from the password the interface requires a salt. Can I use the password as salt? Can I re-use the IV as salt? Maybe not, but I don't want to add another parameter.
  2. Can I use a fixed IV? Same plaintext and password should result in different cipher text, so I have to supply the IV for decryption in the payload.
  3. Can I use a salt for the key and keep the IV constant instead? Feels wrong.
  4. Creating a nonce and deriving IV and key salt from it is a valid approach?
  5. If .Net would support the GCM mode would I still have this problems?
aggsol
  • 2,343
  • 1
  • 32
  • 49
  • You don't have to implement crypto methods and you should avoid that. I am no expert so I'll let experts answer to your questions. Did you take a look at the [.NET Aes implementation](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aescryptoserviceprovider?view=netframework-4.8) ? – Florian May 14 '19 at 08:34
  • @Florian Yes, that is what I using the get the two simplyfied calls I mention above. As you can see in the example there several nested `using` and stream other complex stuff. – aggsol May 14 '19 at 08:39
  • 1
    Totally valid question. These APIs would be very useful and I think they should exist. The crypto APIs feel a bit nasty. Different .NET APIs in 1.0 were done by different teams. Most are very nice but some areas of the framework are not. You can propose this on GitHub in the corefx repository. – usr May 14 '19 at 08:55
  • Because crypto is not easy and require some math to make it difficult to break. Look at: https://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp – Piotr Stapp May 14 '19 at 09:14
  • crypto is *inherently* complicated; now, you could say that it is the job of a library to encapsulate that complexity, but in the case of crypto, the complexity (and the configuration that goes with it) is kinda *necessary* - there are no easy options to **secure** crypto – Marc Gravell May 14 '19 at 09:16
  • 3
    @MarcGravell the inherent complexity is rather limited. For example, you can make a very safe and general purpose authenticated encryption primitive out of AES-GCM. A single call to `byte[] Encrypt(byte[] data, byte[] key, byte[] authTag)` could be enough. The user still must understand a few things but the potential for misuse is rather limited. There is no reason the .NET Framework must make things so hard. – usr May 14 '19 at 09:22
  • 3
    @MarcGravell and there are libraries like that which aim to create primitives that are hard to misuse and have a *very* simple API. Crypto in .NET has a lot of artificial complexity. It's like ADO.NET where really you want EF or Dapper most of the time. Want to execute a query? Call Query. Not open and configure lots of objects and run through readers. – usr May 14 '19 at 09:23
  • 2
    @usr agreed, I hear you; perhaps your ADO.NET comparison is a very good one here re necessary complexity vs unnecessary complexity; point conceded; but (aside)... I don't think you can trust those Dapper folks... – Marc Gravell May 14 '19 at 09:29
  • @usr I just had a look [how libsodium-net does it](https://bitbeans.gitbooks.io/libsodium-net/content/secret-key_cryptography/authenticated_encryption.html). They are really close to what I want and way less complex what .Net provides. – aggsol May 14 '19 at 09:31
  • It's not clear whether this is a question about how to proceed or a rant about library design. – Eric Lippert May 14 '19 at 17:05
  • 1
    You might be interested in this (disclaimer, written by me) [repository](https://github.com/luke-park/SecureCompatibleEncryptionExamples), it contains the methods exactly as you outlined them in 14 different languages, including C#. – Luke Joshua Park May 15 '19 at 00:24
  • 1
    To answer some of your questions: No need for a salt. This is not a password database. Any way to turn the password string into bytes is OK. Generate a securely random IV and store it with the ciphertext. That takes care of all IV related issues. – usr May 15 '19 at 07:53

1 Answers1

3

The .NET crypto API exposes a general purpose encryption library, containing object oriented approaches to implement cryptographic algorithms. Of course, to use these algorithms and algorithm implementations you need to have a good grasp on cryptography, which you currently lack.

This general purpose library is required to implement the various protocols that exist out there. Usually a single algorithm doesn't fulfill a specific use case (encrypt a string using a password, returning a different string, in your case). So a protocol needs to be chosen or devised that does fulfill that use case. This protocol may e.g. define a container format such as CMS or PGP, which can for instance be used to encrypt emails (the use case).

You're directly trying to apply cryptographic algorithms to solve your use case. That's not going to work. You need a pre-made protocol, preferably with a pre-made API.


Note that there are many different use cases, many different protocols and even more opinions on how to create and implement those correctly. Libsodium / NaCl for instance defines a small container format called SecretBox that does take some of the work from you.

However, it would of course be rather impossible to implement TLS on top of NaCl, as the functionality / algorithms are just not there. Again, .NET needs a generic crypto library like the .NET API for others to implement their protocols.


So either you'll have to byte the bullet and try to create your own protocol or you take an existing one and take an educated guess if it is secure (hopefully the protocol has been reviewed / updated a few times). Stay away from single person projects without additional contributors (like the many sample codes out there without review).

For your own protocol, yes, there are mistakes such as not storing the salt with the ciphertext. You need a random - or at least unique - salt to be secure, reusing the password for that is certainly not secure. Don't let it become a single person project itself and either borrow a protocol or have it reviewed.


OK, quickly then:

  1. To derive the key from the password the interface requires a salt. Can I use the password as salt? Can I re-use the IV as salt? Maybe not, but I don't want to add another parameter.

No, the salt needs to be unique and preferably random; the password / salt combination should be unique (it should not repeat, not even in time, or over different domains).

  1. Can I use a fixed IV? Same plaintext and password should result in different cipher text, so I have to supply the IV for decryption in the payload.

No, unless the key changes value each time (see above). For CBC the IV should be unpredictable unless you use a fresh key each time.

  1. Can I use a salt for the key and keep the IV constant instead? Feels wrong.

That's possible, as long as you don't repeat the salt.

  1. Creating a nonce and deriving IV and key salt from it is a valid approach?

That depends on very specific details. In other words, I would not try it if you don't exactly know what you're doing.

  1. If .Net would support the GCM mode would I still have this problems?

Absolutely, and in a sense your problems would be worse if you'd use GCM, as using GCM with the same key and IV is completely broken.


Remember, GCM is just an algorithm, not a protocol, it cannot solve your use case by itself.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • What need is there for a salt? The key derivation is just a means of taking the password string and turning it into bytes. This is not a password database. – usr May 15 '19 at 07:50
  • 1
    You can use the encryption of a certain plaintext in a rainbow table as well. So at least the salt should be different for each password. You could use a static salt for each password, but then you'd have a static key and you would have to use a random IV; you might as well have used a random salt. More info e.g. [here](https://crypto.stackexchange.com/q/5868/1172). Note that PBKDF2 (often used to hash passwords for DB storage) has been designed for password based encryption / key derivation (as the name implies) and *uses a salt*. – Maarten Bodewes May 15 '19 at 08:56
  • 1
    So you're saying use a random salt instead of a random IV (use the 0 IV) and get better properties that way? A rainbow based attack on encryption seems very esoteric to me. It requires indexing all possible passwords *and* plaintexts in combination. But I guess it is a possibility. – usr May 15 '19 at 11:50
  • 1
    Just one block of encryption preferably with known plaintext could be enough. But yes, it would be a long shot. See it this way: you often need a random IV anyway if you use the same key, why not derive a different key? – Maarten Bodewes May 15 '19 at 12:17