Hashelés .NET Core alatt

A minap belefutottam egy érdekes hibába, gondoltam, megosztom a megoldást. (Leginkább azért, mert jellemzően pár hét alatt elfelejtem. 🙂 )

A .NET Core marha jó, marha gyors, tiszta, imádom. A .NET Standard megjelenése óta pedig, hála a jó sátorvasnak egyre több eredetileg “nagy” .NET-re készült osztálykönyvárat, nuget package-et portolnak át rá.

Illetve a portolás szó nem teljesen megfelelő, mert sok esetben nem készítenek egy csak .NET Core-ral kompatibilis verziót a régebbi mellé, hanem az eredeti osztálykönyvár targetjét változtatják meg .NET Standardre. Ezzel azt érik el, hogy a package egyszerre lesz kompatibilis minden platformmal, amely implementálja a .NET Standard megjelölt verzióját.
Ez viszont azt jelenti, hogy a kód többféle környezetben futhat. Ilyen esetekben pedig előfordul, hogy egyes platformokon egyes funkciók máshogy működnek, vagy nem érhetők el (elég csak a Registry-re gondolni). Mivel a .NET Standard még viszonylag új dolog, bármelyik pillanatban szembe jöhet egy ilyen eset, ezt pedig általában egy PlatformNotSupportedException képében jelenik meg.

Egy ilyet találtam a minap, amikor hasht próbáltam generálni.

A HMAC-alapú hash algoritmusok típusai a System.Security.Cryptography névtérben lévő HMAC osztályból származnak. Ez az osztály definiál egy Create statikus metódust, aminek segítségével létrehozhatunk egy hash-számító objektumot. A metódus overloadja egy stringet fogad, amelynek segítségével az alapértelmezett HMAC algó helyett bármelyik “leszármazott” algoritmust példányosíthatjuk. (HMACMD5, HMACRIPEMD160 HMACSHA256 stb. Véletlenül ezek pont a kutyáim nevei.)

Eme hasznos kis tulajdonsága miatt, illetve amiatt, hogy sok helyütt ennek a metódusnak a használata volt az ajánlás, hozzá is szoktam, hogy a Create-tel hozom létre a hash-objektumokat.

Nemrégiben egy hegesztés alatt álló .NET Core-szerverben kellett elővennem a .NET Frameworkből származó tudásomat.
Az alábbi kódot írtam:
var hashAlgo = HMACSHA1.Create();
hashAlgo.Key = Encoding.UTF8.GetBytes("akármi");
var token = hashAlgo.ComputeHash(Encoding.UTF8.GetBytes(target));
Papíron működnie kellene, gyakorlatban viszont elszállt a minden. Kis kutakodás (nem, Visual Studio Code-ban egyáltalán nem egyszerű debugolni!) után megtaláltam, hogy a Create metódus a ludas. Ami még szebb, hogy bizony nem akármilyen kivételt dob, hanem PlatformNotSupportedExceptiont.

Itt kicsit leizzadtam, hogy ha egy sima HMACSHA1 nincs implementálva .NET Core alatt, akkor kalap-kabát, mert oké, hogy nem a legbiztonságosabb algó, jobb lenne valamelyik újabbat használni, ámde az építés alatt álló rendszer bizonyos részei, amiket nem mi tartunk a kezünkben konkrétan HMACSHA1-et tudnak visszaolvasni, szóval nincs választásunk.

Fejvakarás, próbaképpen visszaállás a sima HMAC Create-jére, hátha… Ugyanaz.
Fejvakarás, elvek feladása… new-zuk meg!
var hashAlgo = new HMACSHA1();
Tökéletesen működik!
Emmeg mi?
Kis (sok) utánajárás után kiderült, hogy mi a helyzet, és ez itt a tanulság. (Bár remélem, hogy a megoldás is segített valakinek.)

Az történt, hogy a .NET Core fejlesztésénél az egyik szempont az volt, hogy a Core API-jait próbálják minél közelebb vinni a .NET Frameworkhöz, a könnyebb portolás érdekében. (Mármint, hogy nekünk könnyebb legyen átportolni az alkalmazásainkat Core alá.) Ennek jegyében pedig többek között a hash algoritmusok interfésze is jobbára érintetlen maradt.
Viszont egy másik szempont az volt, hogy amennyire lehet, modernizálják is az API-kat olyan értelemben, hogy ha valahol anno hoztak egy döntést, és utóbb kiderült, hogy az nem volt a legjobb, akkor próbálják meg kijavítani. Ennek keretében pedig sikerült elkaszálni a HMAC Create metódusokat. Az interfész maradt, de a funkcionalitás ment a levesbe.

A ráció emögött az, hogy a Create bár látszólag egy factory method, gyakorlatilag felesleges, ugyanis semmi nélkülözhetetlent nem csinál azon kívül, hogy megnewz egy objektumot. Az alap HMAC osztálynál még volt valamicske értelme a string paraméteres változatnak, hiszen leszármazottat tudott példányosítani, így néhány esetben hasznos volt. Ámde két+egy gond volt ezzel.
1. A leszármazottak megörökölték a metódust. További leszármazás nem lévén ezek gyakorlatilag felesleges metódusok voltak, hiszen mindegyik osztálynál ott volt a konstruktor is ugyanerre a feladatra. (Eleve, anno a paraméterezett konstruktorok megjelenése bizonyos esetben feleslegessé tette a factory methodokat.*)
2. A HMAC paraméter nélküli Create metódusa egy “alapértelmezett” algoritmust példányosított, ami egyébként pont a fenti SHA1 változat. Ennek a gyakorlati értéke nulla, hiszen hello world szint felett mindig tudjuk, hogy pontosan milyen algoritmust akarunk használni.
3. .NET alatt ezt az alapértelmezett algoritmust kívülről át is lehetett állítani, ami kb tiltott volt, éppen azért, mert innentől fogva a fene se tudta, mit kapunk vissza egy HMAC.Create() hívás után.
Innentől fogva tehát igazából nem volt értelme az egész Create metódusos mizériának. Tehát a verdikt: szántsuk be sóval.
Viszont ha eldobjuk a metódust, akkor sérül az API kompatibilitási szempontból…

Szóval sikerült egy ilyen félmegoldást találni. A metódus ugyan ott van, a .NET-kód elvileg rögtön fordul Core-on is, csak éppen majd futás közben közli, hogy bocs, “ezen a platformon” sajnos nem működöm. Ami persze nem akkora nagy baj, hiszen a portolás után a tengernyi teszt első lefuttatásakor ki is bukik. Merugye írtunk teszteket. Ugye?

Úgyhogy óvatosan a portolásnál, de azért ne ijedjünk meg, ha hirtelen PlatformNotSupportedExceptiont kapunk, mert jó eséllyel megvolt az oka, hogy miért kellett megváltoztatni a korábbi működést… és jó eséllyel van workaround, különben nem változtatták volna meg.

Itt egy beszélgetés (=bugreport) az ügyről, ha továbbiakra vagytok kiváncsiak.
*: írhatnám, hogy gyármetódus, de nem írom.
Reklámok

Szerző: Fülöp Dávid

I'm a software developer, trainer, Microsoft MVP interested in .NET and related technologies. Currently mostly .NET Core, Docker, Kubernetes...

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés /  Módosítás )

Google kép

Hozzászólhat a Google felhasználói fiók használatával. Kilépés /  Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés /  Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés /  Módosítás )

Kapcsolódás: %s