Let there be some object that performs some kind of cryptographic operation (for example, KeyedHashAlgorithm , SymmetricAlgorithm or AsymmetricAlgorithm ).

Let's say the password hashing object

 KeyedHashAlgorithm keyedSha512 = KeyedHashAlgorithm.Create("HMACSHA512"); 

which is somehow initialized

 keyedSha512.Key = ...; 

To calculate a password hash, you can create and initialize a new object whenever it is needed.

Suppose, however, that during initialization the key is loaded from another host, i.e. initialization can be relatively long. In this case, in order to optimize, you are asked to create and initialize the crypto object once, placing it in the static field of some class where it will live while the application is running.

Will it reduce security in any way? What are the recommendations and best-pracices on this?

  • one
    You can cache, but only encrypted. But just do not forget that you have a controlled language with GC. And this means that even if you create an object every time and then delete it, it does not mean that it will not be in memory. - KoVadim
  • To store secrets, you need to use the capabilities of the system; for Windows, this is the Crypto API. Keeping a secret in a class field is tantamount to going out and shouting out. - ixSci
  • 2
    Announce your threat model. Without it, talking about security does not make sense. For example, if you fight against a hostile code, running with your rights or administrator rights, then nothing, in fact, will help. - VladD

2 answers 2

First you need to decide on the attack vector and from what you are defending. If the server on which your application is running and is completely controlled by you, then you will not lose security, and you may even get it, because you will not go to a different host every time for the key, i.e. intercepting traffic with a key will be more difficult.

If the server is not yours, and is located remotely, it is more difficult. You can remove the application's memory dump and get the key (it will be stored in KeyValue from KeyedHashAlgorithm ), also if the application is running on a virtual machine at the hoster, you can remove the entire virtual machine dump. Well, quite from terrible - freeze the memory bar with liquid nitrogen and remove its contents.

At the same time, it is worth bearing in mind that in .NET, the key remains will in any case remain somewhere on the heap after the Garbage Collector, so that creating an object every time does not guarantee anything. You can use SecureString where possible, but it's inconvenient to use it and not all APIs in .NET support it.

So, most likely, you will not lose anything if you store the crypto object or the key itself in the memory of your application, if you do not have specific requirements.

    It may be useful to someone else.

    To reduce the time the key was found in memory in clear text, I decided to use the functionality of the ProtectedData class, which uses the Data Protection API (DPAPI) capabilities.

    Instead

     static KeyedHashAlgorithm keyedSha512 = KeyedHashAlgorithm.Create("HMACSHA512"); 

    I made an encrypted key static (and also entropy is an additional parameter used in the methods of the ProtectedData class):

     static byte[] encrKey = null; static byte[] entropy = new byte[64]; 

    Initialization of static fields is as follows:

     using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(entropy); byte[] key = ...; //получаем ключ encrKey = ProtectedData.Protect(key, entropy, DataProtectionScope.CurrentUser); Array.Clear(key, 0, key.Length); 

    those. generate entropy, get the key, encrypt it, and clear the unencrypted one.

    Then, if it is necessary to calculate the hash, a crypto object is created, the key is decrypted, the hash is calculated, after which the unencrypted key is cleared.

     byte[] hash = null; using (var keyedSha512 = KeyedHashAlgorithm.Create("HMACSHA512")) { byte[] key = ProtectedData.Unprotect(encrKey, entropy, DataProtectionScope.CurrentUser); keyedSha512.Key = key; hash = keyedSha512.ComputeHash(...); Array.Clear(key, 0, key.Length); } 

    When used in IIS, you will need to enable loading of the user profile (set the True Load User Profile property of the corresponding Application Pool to True)

    • Are you sure that Array.Clear will not be thrown by the optimizer, and that no other security-oriented API is needed? - VladD
    • @VladD, I thought about it. Not sure. However, I see the use of Array.Clear, for example, in the Dispose method of the KeyedHashAlgoritm class ( here ). Should I also do key = null ? I would be grateful if you tell me how to do it correctly. - i-one
    • I don't know, I'm not an expert, and my advice can only hurt. I am guided by this article: habrahabr.ru/post/181372 - VladD