SecureString est-il toujours pratique dans une application C #?

224

N'hésitez pas à me corriger si mes hypothèses sont erronées ici, mais laissez-moi vous expliquer pourquoi je demande.

Tiré de MSDN, un SecureString:

Représente un texte qui doit rester confidentiel. Le texte est crypté pour plus de confidentialité lors de son utilisation et supprimé de la mémoire de l'ordinateur lorsqu'il n'est plus nécessaire.

Je comprends cela, il est parfaitement logique de stocker un mot de passe ou d'autres informations privées dans un SecureStringsur un System.String, car vous pouvez contrôler comment et quand il est réellement stocké en mémoire, car un System.String:

est à la fois immuable et, lorsqu'il n'est plus nécessaire, ne peut pas être programmé pour la collecte des ordures; en d'autres termes, l'instance est en lecture seule après sa création et il n'est pas possible de prédire quand l'instance sera supprimée de la mémoire de l'ordinateur. Par conséquent, si un objet String contient des informations sensibles telles qu'un mot de passe, un numéro de carte de crédit ou des données personnelles, il existe un risque que les informations soient révélées après leur utilisation car votre application ne peut pas supprimer les données de la mémoire de l'ordinateur.

Cependant, dans le cas d'une application GUI (par exemple, un client ssh), le SecureString doit être construit à partir d'un System.String . Tous les contrôles de texte utilisent une chaîne comme type de données sous-jacent .

Cela signifie donc que chaque fois que l'utilisateur appuie sur une touche, l'ancienne chaîne qui s'y trouvait est supprimée et une nouvelle chaîne est créée pour représenter la valeur à l'intérieur de la zone de texte, même si vous utilisez un masque de mot de passe. Et nous ne pouvons pas contrôler quand ou si l'une de ces valeurs est supprimée de la mémoire .

Il est maintenant temps de vous connecter au serveur. Devine quoi? Vous devez passer une chaîne sur la connexion pour l'authentification . Convertissons donc notre SecureStringen un System.String.... et maintenant nous avons une chaîne sur le tas sans aucun moyen de la forcer à passer par la récupération de place (ou d'écrire des 0 dans son tampon).

Mon point est : peu importe ce que vous faites, quelque part le long de la ligne, qui SecureStringest va être transformé en un System.String, ce qui signifie qu'il sera au moins exist sur le tas à un moment donné (sans aucune garantie de la collecte des ordures).

Mon point n'est pas : s'il existe des moyens de contourner l'envoi d'une chaîne à une connexion ssh, ou de contourner le fait qu'un contrôle stocke une chaîne (créer un contrôle personnalisé). Pour cette question, vous pouvez remplacer "connexion ssh" par "formulaire de connexion", "formulaire d'inscription", "formulaire de paiement", "aliments-vous-nourririez votre chiot mais pas vos enfants", etc.

  • Alors, à quel moment l'utilisation d'un système SecureStringdevient-elle réellement pratique?
  • Cela vaut-il la peine de consacrer plus de temps au développement pour éliminer complètement l'utilisation d'un System.Stringobjet?
  • Le but est-il SecureStringsimplement de réduire la quantité de temps qu'il System.Stringy a sur le tas (ce qui réduit son risque de passer à un fichier d'échange physique)?
  • Si un attaquant a déjà les moyens pour une inspection en tas, puis il le plus probable soit (A) a déjà les moyens de lire des séquences de touches, ou (B) déjà a physiquement la machine ... Alors serait l' aide d' un SecureStringl'empêcher de se rendre à la données de toute façon?
  • Est-ce juste "la sécurité par l'obscurité"?

Désolé si je pose des questions trop épaisses, la curiosité a pris le dessus sur moi. N'hésitez pas à répondre à une ou toutes mes questions (ou dites-moi que mes hypothèses sont complètement fausses). :)

Steven Jeffries
la source
25
Gardez à l'esprit que ce SecureStringn'est pas vraiment une chaîne sécurisée. C'est simplement un moyen de réduire la fenêtre de temps dans laquelle quelqu'un peut inspecter votre mémoire et obtenir avec succès les données sensibles. Ce n'est pas à l'épreuve des balles et ce n'était pas prévu. Mais les points que vous soulevez sont très valables. Connexe: stackoverflow.com/questions/14449579/…
Theodoros Chatzigiannakis
1
@TheodorosChatzigiannakis, oui, c'est à peu près ce que je pensais. Je viens de passer toute la journée à creuser la tête pour essayer de trouver un moyen sûr de stocker un mot de passe pour la durée de vie de l'application, et cela m'a fait me demander, est-ce que ça vaut le coup?
Steven Jeffries
1
Probablement pas la peine. Mais là encore, vous pourriez vous défendre contre un scénario extrême. Extrême pas dans le sens où il est peu probable que quelqu'un obtienne ce type d'accès à votre ordinateur, mais dans le sens où si quelqu'un obtient ce type d'accès, l'ordinateur est considéré (à toutes fins utiles) comme compromis et je ne le fais pas '' Je pense qu'il n'y a aucun langage ou aucune technique que vous pouvez utiliser pour vous défendre complètement contre cela.
Theodoros Chatzigiannakis du
8
Il protège contre un mot de passe visible pendant des années , longtemps après que tout le monde a cessé de penser qu'il pourrait y avoir un problème. Sur un disque dur jeté, stocké dans le fichier d'échange. Il est important de nettoyer la mémoire afin que les chances soient faibles, ne peut pas le faire avec System.String car il est immuable. Il nécessite une infrastructure à laquelle peu de programmes ont accès de nos jours, interopèrent avec du code natif, de sorte que les méthodes Marshal.SecureStringXxx () sont utiles se raréfie. Ou une manière décente d'inviter l'utilisateur à le faire :)
Hans Passant
1
SecureString est une technique de sécurité approfondie. Le compromis entre le coût de développement et le gain de sécurité n'est pas favorable dans la plupart des situations. Il existe très peu de bons cas d'utilisation.
usr

Réponses:

237

Il existe en fait des utilisations très pratiques de SecureString.

Savez-vous combien de fois j'ai vu de tels scénarios? (la réponse est: beaucoup!):

  • Un mot de passe apparaît accidentellement dans un fichier journal.
  • Un mot de passe est affiché quelque part - une fois qu'une interface graphique a montré une ligne de commande d'application qui était en cours d'exécution, et la ligne de commande était constituée d'un mot de passe. Oups .
  • Utilisation du profileur de mémoire pour profiler le logiciel avec votre collègue. Un collègue voit votre mot de passe en mémoire. Cela semble irréel? Pas du tout.
  • J'ai déjà utilisé un RedGatelogiciel qui pouvait capturer la "valeur" des variables locales en cas d'exceptions, incroyablement utile. Cependant, je peux imaginer qu'il enregistrera les "mots de passe de chaîne" accidentellement.
  • Un vidage sur incident qui inclut un mot de passe de chaîne.

Savez-vous comment éviter tous ces problèmes? SecureString. Il s'assure généralement que vous ne faites pas d'erreurs stupides en tant que telles. Comment l'évite-t-il? En vous assurant que le mot de passe est chiffré dans la mémoire non gérée et que la valeur réelle n'est accessible que lorsque vous êtes sûr à 90% de ce que vous faites.

En ce sens, SecureStringfonctionne assez facilement:

1) Tout est crypté

2) Appels des utilisateurs AppendChar

3) Décryptez tout dans la MÉMOIRE NON GÉRÉE et ajoutez le personnage

4) Chiffrez tout à nouveau dans la MÉMOIRE NON GÉRÉE.

Que faire si l'utilisateur a accès à votre ordinateur? Un virus pourrait-il avoir accès à tous les virus SecureStrings? Oui. Tout ce que vous avez à faire est de vous connecter RtlEncryptMemorylorsque la mémoire est décryptée, vous obtiendrez l'emplacement de l'adresse mémoire non cryptée et la lirez. Voila! En fait, vous pouvez créer un virus qui analysera en permanence l'utilisation SecureStringet enregistrera toutes les activités avec. Je ne dis pas que ce sera une tâche facile, mais cela peut être fait. Comme vous pouvez le voir, la "puissance" de a SecureStringcomplètement disparu une fois qu'il y a un utilisateur / virus dans votre système.

Vous avez quelques points dans votre message. Bien sûr, si vous utilisez certains des contrôles de l'interface utilisateur qui contiennent un "mot de passe de chaîne" en interne, l'utilisation de réel SecureStringn'est pas très utile. Cependant, il peut encore protéger contre une certaine stupidité que j'ai énumérée ci-dessus.

En outre, comme d'autres l'ont noté, WPF prend en charge PasswordBox qui utilise en SecureStringinterne via sa propriété SecurePassword .

L'essentiel est; si vous avez des données sensibles (mots de passe, cartes de crédit, ..), utilisez SecureString. C'est ce que le framework C # suit. Par exemple, la NetworkCredentialclasse stocke le mot de passe sous SecureString. Si vous regardez cela , vous pouvez voir plus de ~ 80 utilisations différentes dans le framework .NET de SecureString.

Il y a de nombreux cas où vous devez convertir SecureStringen chaîne, car certaines API l'attendent.

Le problème habituel est soit:

  1. L'API est GÉNÉRIQUE. Il ne sait pas qu'il existe des données sensibles.
  2. L'API sait qu'elle traite des données sensibles et utilise des "chaînes" - c'est juste une mauvaise conception.

Vous avez soulevé un bon point: que se passe-t-il quand SecureStringest converti string? Cela ne peut se produire qu'en raison du premier point. Par exemple, l'API ne sait pas que ce sont des données sensibles. Personnellement, je n'ai pas vu cela se produire. Extraire une chaîne de SecureString n'est pas si simple.

Ce n'est pas simple pour une raison simple ; il n'a jamais été conçu pour permettre à l'utilisateur de convertir SecureString en chaîne, comme vous l'avez dit: GC se mettra en marche. Si vous vous voyez faire cela, vous devez prendre du recul et vous demander: pourquoi suis-je même en train de faire cela, ou ai-je vraiment besoin C'est pourquoi?

Il y a un cas intéressant que j'ai vu. À savoir, la fonction WinApi LogonUser prend LPTSTR comme mot de passe, ce qui signifie que vous devez appeler SecureStringToGlobalAllocUnicode. Cela vous donne essentiellement un mot de passe non chiffré qui réside dans la mémoire non gérée. Vous devez vous en débarrasser dès que vous avez terminé:

// Marshal the SecureString to unmanaged memory.
IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
   //...snip...
}
finally 
{
   // Zero-out and free the unmanaged string reference.
   Marshal.ZeroFreeGlobalAllocUnicode(rawPassword);
}

Vous pouvez toujours étendre la SecureStringclasse avec une méthode d'extension, telle que ToEncryptedString(__SERVER__PUBLIC_KEY), qui vous en donne une stringinstance SecureStringchiffrée à l'aide de la clé publique du serveur. Seul le serveur peut alors le décrypter. Problème résolu: Garbage Collection ne verra jamais la chaîne "d'origine", car vous ne l'exposez jamais dans la mémoire gérée. C'est exactement ce qui se fait dans PSRemotingCryptoHelper( EncryptSecureStringCore(SecureString secureString)).

Et comme quelque chose de très proche : Mono SecureString ne crypte pas du tout . L'implémentation a été commentée parce que ... attendez ... "Cela provoque en quelque sorte une rupture du test d'unité" , ce qui amène à mon dernier point:

SecureStringn'est pas pris en charge partout. Si la plateforme / l'architecture ne prend pas en charge SecureString, vous obtiendrez une exception. Il existe une liste de plates-formes prises en charge dans la documentation.

Erti-Chris Eelmaa
la source
Je pense que ce SecureStringserait beaucoup plus pratique s'il y avait un mécanisme pour accéder à leurs caractères individuels. L'efficacité d'un tel mécanisme pourrait être améliorée en ayant un type struct SecureStringTempBuffersans aucun membre public, qui pourrait être passé refà la SecureStringméthode "lire un caractère" [si le cryptage / décryptage est traité en blocs de 8 caractères, une telle structure pourrait contenir des données pour 8 caractères et l'emplacement du premier de ces caractères dans SecureString].
supercat
2
Je le signale dans chaque audit de code source que j'effectue. Il convient également de répéter que si vous l'utilisez, SecureStringil doit être utilisé tout au long de la pile.
Casey
1
J'ai implémenté l'extension .ToEncryptedString () mais en utilisant un certificat. Pourriez-vous jeter un oeil et me faire savoir si je fais tout cela mal. Im espérant sa sécurité engouhg gist.github.com/sturlath/99cfbfff26e0ebd12ea73316d0843330
Sturla
@Sturla - Que contient EngineSettingsvotre méthode d'extension?
InteXX
15

Il y a peu de problèmes dans vos hypothèses.

Tout d'abord, la classe SecureString n'a pas de constructeur String. Pour en créer un, vous allouez un objet, puis ajoutez les caractères.

Dans le cas d'une interface graphique ou d'une console, vous pouvez très facilement passer chaque touche enfoncée à une chaîne sécurisée.

La classe est conçue de manière à ce que vous ne puissiez pas, par erreur, accéder à la valeur stockée. Cela signifie que vous ne pouvez pas obtenir le stringmot de passe directement à partir de celui-ci.

Ainsi, pour l'utiliser, par exemple, pour vous authentifier via le Web, vous devrez utiliser des classes appropriées qui sont également sécurisées.

Dans le framework .NET, vous avez quelques classes qui peuvent utiliser SecureString

  • Le contrôle PasswordBox de WPF conserve le mot de passe en tant que SecureString en interne.
  • La propriété Password de System.Diagnostics.ProcessInfo est une SecureString.
  • Le constructeur de X509Certificate2 prend une SecureString pour le mot de passe.

(plus)

Pour conclure, la classe SecureString peut être utile, mais nécessite plus d'attention de la part du développeur.

Tout cela, avec des exemples, est bien décrit dans la documentation MSDN de SecureString

Damian Leszczyński - Vash
la source
10
Je ne comprends pas très bien l'avant-dernier paragraphe de votre réponse. Comment transférez-vous un SecureStringsur le réseau sans le sérialiser en un string? Je pense que l'OP de la question est qu'il arrive un moment où vous voulez réellement utiliser la valeur conservée en toute sécurité dans un SecureString, ce qui signifie que vous devez la retirer de là, et ce n'est plus sûr. Dans toute la bibliothèque de classes Framework, il n'y a pratiquement aucune méthode qui accepte SecureStringdirectement une entrée (pour autant que je puisse voir), alors quel est l'intérêt de conserver une valeur à l'intérieur de a SecureStringen premier lieu?
stakx - ne contribue plus
@ DamianLeszczyński-Vash, je sais que SecureString n'a pas de constructeur de chaîne, mais mon point est que vous (A) créez une SecureString et ajoutez chaque char à partir d'une chaîne, ou (B) à un moment donné devez utiliser la valeur stockée dans SecureString sous forme de chaîne afin de la transmettre à une API. Cela étant dit, vous soulevez de bons points, et je peux voir comment, dans certains cas très spécifiques, cela peut être utile.
Steven Jeffries
1
@stakx Vous l'enverriez sur le réseau en obtenant le char[]et en l'envoyant charsur un socket - en ne créant aucun objet récupérable dans le processus.
user253751
Char [] est collectable et est mutable, il peut donc être remplacé.
Peter Ritchie
Bien sûr, il est également facile d'oublier d'écraser ce tableau. Dans tous les cas, toute API à laquelle vous transmettez les caractères peut également en faire une copie.
cHao
10

Un SecureString est utile si:

  • Vous le construisez caractère par caractère (par exemple à partir d'une entrée de console) ou l'obtenez à partir d'une API non gérée

  • Vous l'utilisez en le transmettant à une API non managée (SecureStringToBSTR).

Si jamais vous le convertissez en chaîne gérée, vous avez déjoué son objectif.

MISE À JOUR en réponse au commentaire

... ou BSTR comme vous le mentionnez, qui ne semble pas plus sûr

Une fois converti en BSTR, le composant non géré qui consomme le BSTR peut mettre à zéro la mémoire. La mémoire non gérée est plus sécurisée dans le sens où elle peut être réinitialisée de cette manière.

Cependant, il y a très peu d'API dans le .NET Framework qui prennent en charge SecureString, vous avez donc raison de dire que leur valeur est très limitée aujourd'hui.

Le cas d'utilisation principal que je verrais est dans une application cliente qui nécessite que l'utilisateur entre un code ou un mot de passe très sensible. L'entrée utilisateur peut être utilisée caractère par caractère pour créer une SecureString, puis elle peut être transmise à une API non gérée, qui met à zéro le BSTR qu'il reçoit après l'avoir utilisé. Tout vidage de mémoire ultérieur ne contiendra pas la chaîne sensible.

Dans une application serveur, il est difficile de voir où cela serait utile.

MISE À JOUR 2

Un exemple d'une API .NET qui accepte une SecureString est ce constructeur pour la classe X509Certificate . Si vous spelunk avec ILSpy ou similaire, vous verrez que SecureString est converti en interne en un tampon non géré ( Marshal.SecureStringToGlobalAllocUnicode), qui est ensuite mis à zéro une fois terminé avec ( Marshal.ZeroFreeGlobalAllocUnicode).

Joe
la source
1
+1, mais ne finirez-vous pas toujours par "vaincre son objectif"? Étant donné que presque aucune méthode dans la bibliothèque de classes Framework n'accepte une SecureStringentrée, quelle serait leur utilisation? Vous devrez toujours vous reconvertir stringtôt ou tard (ou, BSTRcomme vous le mentionnez, ce qui ne semble pas plus sûr). Alors, pourquoi s'en préoccuper SecureString?
stakx - ne contribue plus
5

Microsoft ne recommande pas d'utiliser SecureStringpour les codes plus récents.

À partir de la documentation de SecureString Class :

Important

Nous vous déconseillons d'utiliser la SecureStringclasse pour de nouveaux développements. Pour plus d'informations, voir SecureStringne doit pas être utilisé

Qui recommande:

Ne pas utiliser SecureStringpour le nouveau code. Lors du portage de code vers .NET Core, tenez compte du fait que le contenu du tableau n'est pas chiffré en mémoire.

L'approche générale du traitement des informations d'identification est de les éviter et de s'appuyer à la place sur d'autres moyens d'authentification, tels que les certificats ou l'authentification Windows. sur GitHub.

SᴇM
la source
4

Comme vous l'avez correctement identifié, SecureStringoffre un avantage spécifique par rapport à string: l'effacement déterministe. Il y a deux problèmes avec ce fait:

  1. Comme d'autres l'ont mentionné et comme vous l'avez remarqué par vous-même, cela ne suffit pas en soi. Vous devez vous assurer que chaque étape du processus (y compris la récupération des entrées, la construction de la chaîne, l'utilisation, la suppression, le transport, etc.) se déroule sans défaire l'objectif de l'utilisation SecureString. Cela signifie que vous devez faire attention de ne jamais créer un GC-géré immuable stringou tout autre tampon qui stockera les informations sensibles (ou vous devrez garder une trace de ce aussi bien). En pratique, cela n'est pas toujours facile à réaliser, car de nombreuses API ne proposent qu'un moyen de travailler string, non SecureString. Et même si vous parvenez à tout bien ...
  2. SecureStringprotège contre des types d'attaques très spécifiques (et pour certains d'entre eux, ce n'est même pas si fiable). Par exemple, SecureStringcela vous permet de réduire la fenêtre de temps dans laquelle un attaquant peut vider la mémoire de votre processus et d'extraire avec succès les informations sensibles (encore une fois, comme vous l'avez correctement souligné), mais en espérant que la fenêtre est trop petite pour que l'attaquant puisse prendre un instantané de votre mémoire n'est pas du tout considéré comme de la sécurité.

Alors, quand devriez-vous l'utiliser? Ce n'est que lorsque vous travaillez avec quelque chose qui peut vous permettre de travailler SecureStringpour tous vos besoins et même dans ce cas, vous devez toujours garder à l'esprit que cela n'est sécurisé que dans des circonstances spécifiques.

Theodoros Chatzigiannakis
la source
2
Vous supposez qu'un attaquant est capable de prendre un instantané de la mémoire d'un processus à un moment donné. Ce n'est pas nécessairement le cas. Considérez un vidage sur incident. Si le crash s'est produit après que l'application a été effectuée avec les données sensibles, le vidage sur incident ne doit pas contenir les données sensibles.
4

Le texte ci-dessous est copié depuis l'analyseur de code statique HP Fortify

Résumé: La méthode PassString () dans PassGenerator.cs stocke les données sensibles de manière non sécurisée (c'est-à-dire en chaîne), ce qui permet d'extraire les données via l'inspection du tas.

Explication: Les données sensibles (telles que les mots de passe, les numéros de sécurité sociale, les numéros de carte de crédit, etc.) stockées en mémoire peuvent être divulguées si elles sont stockées dans un objet String géré. Les objets chaîne ne sont pas épinglés, le garbage collector peut donc déplacer ces objets à volonté et laisser plusieurs copies en mémoire. Ces objets ne sont pas chiffrés par défaut, donc toute personne capable de lire la mémoire du processus pourra voir le contenu. De plus, si la mémoire du processus est échangée sur le disque, le contenu non chiffré de la chaîne sera écrit dans un fichier d'échange. Enfin, comme les objets String sont immuables, la suppression de la valeur d'une chaîne de la mémoire ne peut être effectuée que par le garbage collector CLR. Le garbage collector n'est pas obligé de s'exécuter à moins que le CLR manque de mémoire, il n'y a donc aucune garantie quant au moment où le garbage collection aura lieu.

Recommandations: au lieu de stocker des données sensibles dans des objets comme Strings, stockez-les dans un objet SecureString. Chaque objet stocke à tout moment son contenu dans un format crypté en mémoire.

vikrantx
la source
3

Je voudrais aborder ce point:

Si un attaquant a déjà les moyens pour une inspection en tas, alors il est fort probable soit (A) ont déjà les moyens de lire les touches ou (B) déjà avoir physiquement la machine ... Alors serait en utilisant un SecureStringles empêcher de se rendre à la données de toute façon?

Un attaquant peut ne pas avoir un accès complet à l'ordinateur et à l'application mais peut avoir les moyens d'accéder à certaines parties de la mémoire du processus. Elle est généralement causée par des bogues tels que des dépassements de tampon lorsque des entrées spécialement conçues peuvent entraîner l'application ou l'écrasement d'une partie de la mémoire.

HeartBleed fuite de mémoire

Prenez Heartbleed par exemple. Des requêtes spécialement construites peuvent amener le code à exposer des parties aléatoires de la mémoire du processus à l'attaquant. Un attaquant peut extraire des certificats SSL de la mémoire, mais la seule chose dont il a besoin est simplement d'utiliser une requête mal formée.

Dans le monde du code managé, les dépassements de tampon deviennent beaucoup moins souvent un problème. Et dans le cas de WinForms, les données sont déjà stockées de manière non sécurisée et vous ne pouvez rien y faire. Cela rend la protection SecureStringpratiquement inutile.

Cependant, l'interface graphique peut être programmée pour être utilisée SecureString, et dans ce cas, réduire la fenêtre de disponibilité des mots de passe dans la mémoire peut valoir la peine. Par exemple, PasswordBox.SecurePassword de WPF est de type SecureString.

Athari
la source
Hmm, vraiment intéressant à savoir. En toute honnêteté, je ne suis pas vraiment inquiet du type d'attaques nécessaires pour obtenir même une valeur System.String de ma mémoire, je demande juste par pure curiosité. Cependant, merci pour l'information! :)
Steven Jeffries
2

Il y a quelque temps, j'ai dû créer une interface ac # contre une passerelle de paiement par carte de crédit java et il fallait un cryptage des clés de communication sécurisé compatible. Comme l'implémentation Java était assez spécifique et j'ai dû travailler avec les données protégées d'une manière donnée.

J'ai trouvé cette conception assez simple à utiliser et plus simple que de travailler avec SecureString… pour ceux qui aiment l'utiliser… n'hésitez pas, aucune restriction légale :-). Notez que ces classes sont internes, vous devrez peut-être les rendre publiques.

namespace Cardinity.Infrastructure
{
    using System.Security.Cryptography;
    using System;
    enum EncryptionMethods
    {
        None=0,
        HMACSHA1,
        HMACSHA256,
        HMACSHA384,
        HMACSHA512,
        HMACMD5
    }


internal class Protected
{
    private  Byte[] salt = Guid.NewGuid().ToByteArray();

    protected byte[] Protect(byte[] data)
    {
        try
        {
            return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }

    protected byte[] Unprotect(byte[] data)
    {
        try
        {
            return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }
}


    internal class SecretKeySpec:Protected,IDisposable
    {
        readonly EncryptionMethods _method;

        private byte[] _secretKey;
        public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
        {
            _secretKey = Protect(secretKey);
            _method = encryptionMethod;
        }

        public EncryptionMethods Method => _method;
        public byte[] SecretKey => Unprotect( _secretKey);

        public void Dispose()
        {
            if (_secretKey == null)
                return;
            //overwrite array memory
            for (int i = 0; i < _secretKey.Length; i++)
            {
                _secretKey[i] = 0;
            }

            //set-null
            _secretKey = null;
        }
        ~SecretKeySpec()
        {
            Dispose();
        }
    }

    internal class Mac : Protected,IDisposable
    {
        byte[] rawHmac;
        HMAC mac;
        public Mac(SecretKeySpec key, string data)
        {

            switch (key.Method)
            {
                case EncryptionMethods.HMACMD5:
                    mac = new HMACMD5(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA512:
                    mac = new HMACSHA512(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA384:
                    mac = new HMACSHA384(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA256:
                    mac = new HMACSHA256(key.SecretKey);

                break;
                case EncryptionMethods.HMACSHA1:
                    mac = new HMACSHA1(key.SecretKey);
                    break;

                default:                    
                    throw new NotSupportedException("not supported HMAC");
            }
            rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));            

        }

        public string AsBase64()
        {
            return System.Convert.ToBase64String(Unprotect(rawHmac));
        }

        public void Dispose()
        {
            if (rawHmac != null)
            {
                //overwrite memory address
                for (int i = 0; i < rawHmac.Length; i++)
                {
                    rawHmac[i] = 0;
                }

                //release memory now
                rawHmac = null;

            }
            mac?.Dispose();
            mac = null;

        }
        ~Mac()
        {
            Dispose();
        }
    }
}
Walter Vehoeven
la source