Czy SecureString jest kiedykolwiek praktyczny w aplikacji C #?

224

Możesz mnie poprawić, jeśli moje założenia są tutaj błędne, ale pozwól mi wyjaśnić, dlaczego pytam.

Zaczerpnięte z MSDN SecureString:

Reprezentuje tekst, który powinien być poufny. Tekst jest szyfrowany w celu zachowania prywatności podczas używania i usuwany z pamięci komputera, gdy nie jest już potrzebny.

Rozumiem, to ma sens przechowywać hasło lub inne prywatne informacje w ciągu SecureStringponad System.String, ponieważ możesz kontrolować, w jaki sposób i kiedy są one faktycznie przechowywane w pamięci, ponieważ System.String:

jest zarówno niezmienny, jak i gdy nie jest już potrzebny, nie może być programowo zaplanowany do odśmiecania; oznacza to, że wystąpienie jest tylko do odczytu po utworzeniu i nie można przewidzieć, kiedy wystąpienie zostanie usunięte z pamięci komputera. W konsekwencji, jeśli obiekt String zawiera poufne informacje, takie jak hasło, numer karty kredytowej lub dane osobowe, istnieje ryzyko, że informacje mogą zostać ujawnione po ich użyciu, ponieważ aplikacja nie może usunąć danych z pamięci komputera.

Jednak w przypadku aplikacji GUI (na przykład klienta ssh) SecureString należy ją zbudować z System.String . Wszystkie kontrolki tekstu używają ciągu jako podstawowego typu danych .

Oznacza to, że za każdym razem, gdy użytkownik naciska klawisz, stary ciąg, który tam był, jest odrzucany, a nowy ciąg jest budowany, aby reprezentować wartość w polu tekstowym, nawet jeśli używa się maski hasła. Nie możemy kontrolować, kiedy lub czy którakolwiek z tych wartości zostanie usunięta z pamięci .

Czas zalogować się na serwerze. Zgadnij co? W celu uwierzytelnienia musisz przekazać ciąg znaków przez połączenie . Więc przekonwertujmy nasz SecureStringna System.String... i teraz mamy ciąg na stosie, bez możliwości zmuszenia go do przechodzenia do wyrzucania elementów bezużytecznych (lub zapisania zer do bufora).

Chodzi mi o to : nie ważne co robisz, gdzieś wzdłuż linii, która SecureStringma zamiar zostać przekształcony w System.String, co oznacza, że będzie istnieć przynajmniej na stercie w pewnym momencie (bez gwarancji garbage collection).

Nie mam na myśli: czy istnieją sposoby na obejście wysyłania łańcucha do połączenia ssh, czy obchodzenie przechowywania kontroli w łańcuchu (wykonaj niestandardową kontrolę). W przypadku tego pytania możesz zastąpić „połączenie ssh” „formularzem logowania”, „formularzem rejestracyjnym”, „formularzem płatności”, „formularzem karmy-którą-nakarmisz-swoim-szczeniakiem-ale-nie-twoimi dziećmi”, itp.

  • Więc w którym momencie używanie SecureStringfaktycznie staje się praktyczne?
  • Czy kiedykolwiek warto poświęcić dodatkowy czas na opracowanie, aby całkowicie wyeliminować użycie System.Stringobiektu?
  • Czy chodzi SecureStringpo prostu o to, aby po prostu ograniczyć czas, w którym a System.Stringjest na stercie (zmniejszając ryzyko przejścia do fizycznego pliku wymiany)?
  • Jeśli atakujący ma już środki na kontrolę stosu, najprawdopodobniej albo (A) już ma środki do odczytu naciśnięć klawiszy, albo (B) już fizycznie ma maszynę ... Więc użyłby SecureStringprzeszkody, aby nie dostał się do dane w każdym razie?
  • Czy to tylko „bezpieczeństwo przez zaciemnienie”?

Przepraszam, jeśli kładę pytania zbyt gęste, ciekawość właśnie mnie ogarnęła. Odpowiedz na dowolne lub wszystkie moje pytania (lub powiedz, że moje założenia są całkowicie błędne). :)

Steven Jeffries
źródło
25
Pamiętaj, że SecureStringtak naprawdę nie jest to bezpieczny ciąg. Jest to tylko sposób na ograniczenie czasu, w którym ktoś może sprawdzić twoją pamięć i pomyślnie uzyskać poufne dane. To nie jest kuloodporne i nie było przeznaczone. Ale punkty, które zbierasz, są bardzo ważne. Powiązane: stackoverflow.com/questions/14449579/…
Theodoros Chatzigiannakis
1
@TheodorosChatzigiannakis, tak, właściwie to właśnie wymyśliłem. Spędziłem dziś cały dzień, próbując znaleźć sposób na bezpieczne przechowywanie hasła przez cały okres użytkowania aplikacji, i zastanawiałem się, czy warto?
Steven Jeffries
1
Prawdopodobnie nie warto. Ale znowu możesz bronić się przed ekstremalnym scenariuszem. Ekstremalne nie w tym sensie, że mało prawdopodobne jest, aby ktoś uzyskał taki dostęp do twojego komputera, ale w tym sensie, że jeśli ktoś uzyska taki dostęp, komputer jest uważany (pod każdym względem i za cel) za zagrożony, a ja nie Sądzę, że istnieje jakikolwiek język lub technika, której można by całkowicie się przed tym obronić.
Theodoros Chatzigiannakis
8
Chroni przed widocznym hasłem przez lata , długo po tym, jak wszyscy przestali myśleć, że może być problem. Na odrzuconym dysku twardym przechowywanym w pliku stronicowania. Szorowanie pamięci, aby szanse na to były niskie, jest ważne, nie można tego zrobić za pomocą System.String, ponieważ jest niezmienna. Wymaga infrastruktury, do której obecnie ma dostęp niewiele programów, interakcja z natywnym kodem, więc metody Marshal.SecureStringXxx () są użyteczne, stają się coraz rzadsze. Lub przyzwoity sposób, aby poprosić użytkownika o to :)
Hans Passant
1
SecureString to szczegółowa technika bezpieczeństwa. Kompromis między kosztami rozwoju a zyskiem bezpieczeństwa w większości sytuacji nie jest korzystny. Jest bardzo niewiele dobrych przypadków użycia.
usr

Odpowiedzi:

237

Są bardzo praktyczne zastosowania SecureString.

Czy wiesz, ile razy widziałem takie scenariusze? (odpowiedź brzmi: wielu!):

  • Hasło pojawia się przypadkowo w pliku dziennika.
  • Gdzieś wyświetlane jest hasło - gdy GUI wyświetlił wiersz poleceń aplikacji, która była uruchomiona, a linia poleceń składała się z hasła. Ups .
  • Korzystanie z narzędzia do profilowania pamięci do profilowania oprogramowania wraz ze współpracownikiem. Kolega widzi twoje hasło w pamięci. Brzmi nierealnie? Ani trochę.
  • Kiedyś korzystałem z RedGateoprogramowania, które mogło uchwycić „wartość” zmiennych lokalnych w przypadku wyjątków, niezwykle przydatne. Chociaż mogę sobie wyobrazić, że przypadkowo zarejestruje „ciąg hasła”.
  • Zrzut awaryjny zawierający hasło ciągu.

Czy wiesz, jak uniknąć tych wszystkich problemów? SecureString. Generalnie upewnia się, że nie popełniasz głupich błędów jako takich. Jak tego uniknąć? Upewniając się, że hasło jest szyfrowane w niezarządzanej pamięci, a do rzeczywistej wartości można uzyskać dostęp tylko wtedy, gdy masz 90% pewności, co robisz.

W tym sensie SecureStringdziała dość łatwo:

1) Wszystko jest szyfrowane

2) Połączenia użytkowników AppendChar

3) Odszyfruj wszystko w NIEZARZĄDZANEJ PAMIĘCI i dodaj postać

4) Zaszyfruj wszystko ponownie w NIEZARZĄDZANEJ PAMIĘCI.

Co jeśli użytkownik ma dostęp do twojego komputera? Czy wirus byłby w stanie uzyskać dostęp do wszystkich SecureStrings? Tak. Wystarczy, że przyłączysz się do RtlEncryptMemoryodszyfrowywania pamięci, otrzymasz lokalizację niezaszyfrowanego adresu pamięci i przeczytasz go. Voila! W rzeczywistości możesz stworzyć wirusa, który będzie stale skanować w poszukiwaniu użycia SecureStringi rejestrować za jego pomocą wszystkie działania. Nie twierdzę, że będzie to łatwe zadanie, ale da się to zrobić. Jak widać, „moc” SecureStringcałkowicie zniknęła, gdy w twoim systemie pojawił się użytkownik / wirus.

Masz kilka punktów w swoim poście. Jasne, jeśli użyjesz niektórych elementów sterujących interfejsu użytkownika, które przechowują „hasło ciągu” wewnętrznie, użycie faktycznego SecureStringnie jest tak przydatne. Mimo to może chronić przed jakąś głupotą, którą wymieniłem powyżej.

Ponadto, jak zauważyli inni, WPF obsługuje PasswordBox, który używa SecureStringwewnętrznie poprzez swoją właściwość SecurePassword .

Najważniejsze jest: jeśli masz wrażliwe dane (hasła, karty kredytowe, ...), użyj SecureString. Oto, co przestrzega Framework C #. Na przykład NetworkCredentialklasa przechowuje hasło jako SecureString. Jeśli spojrzysz na to , możesz zobaczyć ponad ~ 80 różnych zastosowań w .NET Framework SecureString.

Istnieje wiele przypadków, gdy trzeba przekonwertować SecureStringna ciąg, ponieważ niektóre interfejsy API tego oczekują.

Typowy problem to:

  1. Interfejs API jest OGÓLNY. Nie wie, że istnieją poufne dane.
  2. Interfejs API wie, że zajmuje się wrażliwymi danymi i używa „łańcucha” - to po prostu zły projekt.

Podniosłeś rację: co się stanie, kiedy SecureStringzostanie przekonwertowany string? Może się to zdarzyć tylko z powodu pierwszego punktu. Np. Interfejs API nie wie, że to wrażliwe dane. Nie widziałem tego osobiście. Pobieranie ciągu z SecureString nie jest takie proste.

To nie jest proste z prostego powodu ; nigdy nie zamierzano pozwolić użytkownikowi przekonwertować SecureString na ciąg znaków, jak powiedziałeś: GC uruchomi się. Jeśli zobaczysz, że to robisz, musisz cofnąć się i zadać sobie pytanie: dlaczego to robię, czy naprawdę potrzebuję To dlaczego?

Widziałem jeden interesujący przypadek. Mianowicie, funkcja WinApi LogonUser przyjmuje LPTSTR jako hasło, co oznacza, że ​​musisz zadzwonić SecureStringToGlobalAllocUnicode. Zasadniczo daje to niezaszyfrowane hasło, które znajduje się w niezarządzanej pamięci. Musisz się tego pozbyć, gdy tylko skończysz:

// 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);
}

Zawsze możesz rozszerzyć SecureStringklasę za pomocą metody rozszerzenia, takiej jak ToEncryptedString(__SERVER__PUBLIC_KEY), która daje stringinstancję, SecureStringktóra jest zaszyfrowana przy użyciu klucza publicznego serwera. Tylko serwer może go odszyfrować. Problem rozwiązany: Garbage Collection nigdy nie zobaczy „oryginalnego” ciągu, ponieważ nigdy nie ujawnia się go w pamięci zarządzanej. Dokładnie tak się dzieje w PSRemotingCryptoHelper( EncryptSecureStringCore(SecureString secureString)).

I jako coś bardzo prawie związanego: Mono SecureString w ogóle nie szyfruje . Implementacja została skomentowana, ponieważ… czekaj na nią… „To w jakiś sposób powoduje zerwanie testu nunit” , co prowadzi do mojego ostatniego punktu:

SecureStringnie jest obsługiwany wszędzie. Jeśli platforma / architektura nie obsługujeSecureString , otrzymasz wyjątek. W dokumentacji znajduje się lista platform obsługiwanych.

Erti-Chris Eelmaa
źródło
Myślę, że SecureStringbyłoby o wiele bardziej praktyczne, gdyby istniał mechanizm dostępu do ich poszczególnych postaci. Wydajność takiego mechanizmu można zwiększyć, mając typ struktury SecureStringTempBufferbez żadnych publicznych członków, który można by przekazać refdo metody SecureString„czytaj jeden znak” [jeśli szyfrowanie / deszyfrowanie jest przetwarzane w 8-znakowych fragmentach, taka struktura może przechowywać dane dla 8 znaków i lokalizację pierwszego z tych znaków w SecureString].
supercat
2
Oznaczam to przy każdym audycie kodu źródłowego, który przeprowadzam. Należy również powtórzyć, że jeśli SecureStringgo używasz , musi być używany przez cały stos.
Casey
1
Zaimplementowałem rozszerzenie .ToEncryptedString (), ale za pomocą certyfikatu. Czy mógłbyś spojrzeć i dać mi znać, jeśli robię to wszystko źle? Mam nadzieję, że to bezpieczne engouhg gist.github.com/sturlath/99cfbfff26e0ebd12ea73316d0843330
Sturla
@Sturla - Co zawiera EngineSettingsmetoda rozszerzenia?
InteXX
15

Jest kilka problemów w twoich założeniach.

Po pierwsze klasa SecureString nie ma konstruktora String. Aby go utworzyć, należy przydzielić obiekt, a następnie dołączyć znaki.

W przypadku GUI lub konsoli możesz bardzo łatwo przekazać każdy naciśnięty klawisz do bezpiecznego ciągu.

Klasa została zaprojektowana w taki sposób, że przez pomyłkę nie można uzyskać dostępu do przechowywanej wartości. Oznacza to, że nie można uzyskaćstring bezpośrednio z niego hasła.

Aby więc na przykład użyć go do uwierzytelnienia w sieci, będziesz musiał użyć odpowiednich, bezpiecznych klas.

W środowisku .NET masz kilka klas, które mogą korzystać z SecureString

  • Kontrola PasswordBox WPF zachowuje hasło jako SecureString wewnętrznie.
  • Właściwość Hasło System.Diagnostics.ProcessInfo to SecureString.
  • Konstruktor dla X509Certificate2 przyjmuje SecureString dla hasła.

(więcej)

Podsumowując, klasa SecureString może być przydatna, ale wymaga większej uwagi ze strony programisty.

Wszystko to, wraz z przykładami, jest dobrze opisane w dokumentacji MSDN dotyczącej SecureString

Damian Leszczyński - Vash
źródło
10
Nie do końca rozumiem ostatni akapit twojej odpowiedzi. Jak mógłbyś przenieść SecureStringsieć bez szeregowania jej w sieć string? Myślę, że chodzi o to, że pytanie polega na tym, że przychodzi czas, kiedy naprawdę chcesz bezpiecznie wykorzystać wartość przechowywaną w a SecureString, co oznacza, że ​​musisz ją stamtąd wyciągnąć i nie jest to już bezpieczne. W całej bibliotece klas frameworka praktycznie nie ma metod, które akceptują SecureStringdane wejściowe bezpośrednio (o ile widzę), więc jaki jest sens utrzymywania wartości w środku SecureString?
stakx - nie przyczynia się już
@ DamianLeszczyński-Vash, wiem, że SecureString nie ma konstruktora ciągów, ale mam na myśli to, że albo (A) tworzysz SecureString i dołączasz do niego każdy znak, albo (B) w pewnym momencie musisz użyć wartość przechowywana w SecureString jako ciąg znaków w celu przekazania go do jakiegoś API. Biorąc to pod uwagę, przywołujesz kilka dobrych punktów i widzę, jak w niektórych bardzo szczególnych przypadkach może być przydatny.
Steven Jeffries
1
@stakx Wysłałbyś go przez sieć, pobierając char[]i wysyłając każdy charprzez gniazdo - nie tworząc w ten sposób żadnych obiektów do zbierania śmieci.
user253751
Char [] można kolekcjonować i można go modyfikować, więc można go zastąpić.
Peter Ritchie
Oczywiście łatwo również zapomnieć o zastąpieniu tej tablicy. Tak czy inaczej, dowolny interfejs API, któremu przekazujesz znaki, może również wykonać kopię.
cHao
10

SecureString jest przydatny, jeśli:

  • Budujesz go znak po znaku (np. Z danych wejściowych konsoli) lub uzyskujesz go z niezarządzanego interfejsu API

  • Używasz go, przekazując go do niezarządzanego interfejsu API (SecureStringToBSTR).

Jeśli kiedykolwiek przekonwertujesz go na zarządzany ciąg, pokonałeś jego cel.

AKTUALIZACJA w odpowiedzi na komentarz

... lub BSTR, jak wspomniałeś, co nie wydaje się bardziej bezpieczne

Po przekształceniu w BSTR niezarządzany składnik, który zużywa BSTR, może wyzerować pamięć. Niezarządzana pamięć jest bezpieczniejsza w tym sensie, że można ją zresetować w ten sposób.

Istnieje jednak bardzo niewiele interfejsów API w .NET Framework, które obsługują SecureString, więc masz rację, mówiąc, że ma dziś bardzo ograniczoną wartość.

Główny przypadek użycia, jaki widziałbym, to aplikacja kliencka, która wymaga od użytkownika wprowadzenia bardzo wrażliwego kodu lub hasła. Dane wejściowe użytkownika mogą być używane znak po znaku do zbudowania SecureString, a następnie mogą być przekazywane do niezarządzanego interfejsu API, który zeruje BSTR otrzymany po jego użyciu. Kolejny zrzut pamięci nie będzie zawierał wrażliwego ciągu.

W aplikacji serwerowej trudno jest zobaczyć, gdzie byłoby to przydatne.

AKTUALIZACJA 2

Jednym z przykładów interfejsu API .NET, który akceptuje SecureString, jest ten konstruktor dla klasy X509Certificate . Jeśli spelunkujesz z ILSpy lub podobnym, zobaczysz, że SecureString jest wewnętrznie konwertowany na niezarządzany bufor ( Marshal.SecureStringToGlobalAllocUnicode), który jest następnie zerowany po zakończeniu z ( Marshal.ZeroFreeGlobalAllocUnicode).

Joe
źródło
1
+1, ale czy nie zawsze „pokonasz swój cel”? Biorąc pod uwagę, że prawie żadna metoda w bibliotece klas ramowych nie akceptuje SecureStringdanych wejściowych, jakie byłoby ich zastosowanie? Zawsze będziesz musiał wrócić do stringwcześniejszego lub późniejszego (lub BSTRjak wspomniałeś, co nie wydaje się bardziej bezpieczne). Po co więc SecureStringw ogóle zawracać sobie głowę ?
stakx - nie przyczynia się już
5

Microsoft nie zaleca używania SecureString nowszych kodów.

Z dokumentacji klasy SecureString :

Ważny

Nie zalecamy używania tej SecureStringklasy do nowych prac rozwojowych. Aby uzyskać więcej informacji, zobacz SecureStringnie należy używać

Który zaleca:

Nie używaj SecureString do nowego kodu. Podczas przenoszenia kodu do .NET Core należy wziąć pod uwagę, że zawartość tablicy nie jest zaszyfrowana w pamięci.

Ogólne podejście do obsługi poświadczeń polega na unikaniu ich i poleganiu na innych sposobach uwierzytelnienia, takich jak certyfikaty lub uwierzytelnianie systemu Windows. na GitHub.

SᴇM
źródło
4

Jak prawidłowo zidentyfikowałeś, SecureStringoferuje jedną konkretną przewagę nad string: usuwanie deterministyczne. Istnieją dwa problemy z tym faktem:

  1. Jak wspomnieli inni i jak sam to zauważyłeś, samo to nie wystarczy. Musisz upewnić się, że każdy etap procesu (w tym pobieranie danych wejściowych, konstrukcja łańcucha, użycie, usuwanie, transport itp.) Odbywa się bez naruszenia celu użyciaSecureString . Oznacza to, że trzeba być ostrożnym, aby nie stworzyć GC zarządzane niezmienne stringlub dowolny inny bufor, który będzie przechowywany poufnych informacji (lub będziesz musiał śledzić że również). W praktyce nie zawsze jest to łatwe do osiągnięcia, ponieważ wiele interfejsów API oferuje tylko sposób pracy string, a nie SecureString. A nawet jeśli wszystko zrobisz dobrze ...
  2. SecureStringchroni przed bardzo szczególnymi rodzajami ataków (a dla niektórych z nich nie jest nawet tak niezawodny). Na przykład SecureStringpozwala zmniejszyć okno czasowe, w którym osoba atakująca może zrzucić pamięć procesu i pomyślnie wyodrębnić poufne informacje (ponownie, jak prawidłowo wskazano), ale ma nadzieję, że okno jest zbyt małe, aby atakujący mógł wykonanie migawki pamięci w ogóle nie jest uważane za bezpieczeństwo.

Kiedy więc go użyć? Tylko wtedy, gdy pracujesz z czymś, co pozwala ci pracować SecureStringdla wszystkich twoich potrzeb, a nawet wtedy powinieneś pamiętać, że jest to bezpieczne tylko w określonych okolicznościach.

Theodoros Chatzigiannakis
źródło
2
Zakładasz, że osoba atakująca może wykonać migawkę pamięci procesu w dowolnym momencie. Niekoniecznie tak jest. Rozważ zrzut awaryjny. Jeśli awaria nastąpiła po zakończeniu aplikacji z wrażliwymi danymi, zrzut awaryjny nie powinien zawierać wrażliwych danych.
4

Poniższy tekst został skopiowany z analizatora kodów statycznych HP Fortify

Streszczenie: Metoda PassString () w PassGenerator.cs przechowuje poufne dane w niepewny sposób (tj. W postaci ciągu), umożliwiając wyodrębnienie danych poprzez sprawdzenie sterty.

Wyjaśnienie: Wrażliwe dane (takie jak hasła, numery ubezpieczenia społecznego, numery kart kredytowych itp.) Przechowywane w pamięci mogą być wyciekane, jeśli są przechowywane w zarządzanym obiekcie String. Obiekty łańcuchowe nie są przypięte, więc moduł odśmiecający może dowolnie przenieść te obiekty i pozostawić kilka kopii w pamięci. Obiekty te nie są domyślnie szyfrowane, więc każdy, kto może odczytać pamięć procesu, będzie mógł zobaczyć zawartość. Ponadto, jeśli pamięć procesu zostanie zamieniona na dysk, niezaszyfrowana zawartość ciągu zostanie zapisana w pliku wymiany. Wreszcie, ponieważ obiekty String są niezmienne, usunięcie wartości String z pamięci może być wykonane tylko przez moduł odśmiecający CLR. Odśmiecanie nie jest wymagane do uruchomienia, chyba że CLR ma mało pamięci, więc nie ma gwarancji, kiedy nastąpi odśmiecanie.

Zalecenia: Zamiast przechowywać wrażliwe dane w obiektach takich jak Strings, przechowuj je w obiekcie SecureString. Każdy obiekt przez cały czas przechowuje w pamięci zaszyfrowaną zawartość.

vikrantx
źródło
3

Chciałbym poruszyć ten punkt:

Jeśli atakujący ma już środki na kontrolę stosu, najprawdopodobniej albo (A) już ma środki do odczytu naciśnięć klawiszy, albo (B) już fizycznie ma maszynę ... Więc użyłby SecureStringuniemożliwiając im dotarcie do dane w każdym razie?

Osoba atakująca może nie mieć pełnego dostępu do komputera i aplikacji, ale może mieć dostęp do niektórych części pamięci procesu. Jest to zwykle spowodowane błędami, takimi jak przepełnienie bufora, gdy specjalnie skonstruowane dane wejściowe mogą spowodować ujawnienie lub nadpisanie części pamięci przez aplikację.

Przeciekająca pamięć HeartBleed

Weźmy na przykład Heartbleed. Specjalnie skonstruowane żądania mogą spowodować, że kod ujawni atakującemu losowe części pamięci procesu. Atakujący może wyodrębnić certyfikaty SSL z pamięci, ale jedyne, czego potrzebuje, to po prostu użyć źle sformułowanego żądania.

W świecie zarządzanego kodu przepełnienia buforów stają się problemem znacznie rzadziej. W przypadku WinForms dane są już przechowywane w niepewny sposób i nie można nic z tym zrobić. To zapewnia ochronę za pomocąSecureString praktycznie bezużyteczną.

Można jednak zaprogramować GUI SecureString, a w takim przypadku warto ograniczyć okno dostępności hasła w pamięci. Na przykład PasswordBox.SecurePassword z WPF jest typu SecureString.

Athari
źródło
Hmm, zdecydowanie interesujące wiedzieć. Szczerze mówiąc, tak naprawdę nie martwię się o rodzaj ataków koniecznych do uzyskania nawet Systemu. Wyciągając z pamięci wartość, po prostu pytam z czystej ciekawości. Jednak dzięki za informację! :)
Steven Jeffries
2

Jakiś czas temu musiałem stworzyć interfejs ac # przeciwko bramie płatności kartą kredytową Java i potrzebowałem kompatybilnego bezpiecznego szyfrowania klucza komunikacyjnego. Ponieważ implementacja Java była raczej specyficzna i musiałem pracować z chronionymi danymi w określony sposób.

Uważam, że ten projekt jest dość łatwy w użyciu i łatwiejszy niż praca z SecureString… dla tych, którzy lubią używać… nie krępuj się, żadnych ograniczeń prawnych :-). Pamiętaj, że te klasy są wewnętrzne, być może trzeba je upublicznić.

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
źródło