Toolbox der Cryptografie

Cryptografie mag dan wiskunde zijn, het is ook heel erg toegepast, en eerlijk gezegd is het behoorlijk macho. Want je laat een hoeveelheid informatie aan de buitenwereld zien die behoorlijk uitdagend werkt voor een kraker, en toch kan die het eigenlijk wel schudden om ooit een kraak te plegen.

Dit artikel verscheen eerder in Linux Magazine, aflevering 4 van 2002

Cryptografie was vroeger het vakgebied van letters vervangen door andere letters volgens een geheim patroon (de `sleutel'). Ongetwijfeld heeft iedereen dat in de kindertijd, onder het mom van een geheimtaal, wel eens met een vriendje of vriendinnetje gedaan. Veel van die dingen zijn (kinderlijk) eenvoudig te kraken door te kijken naar patronen en frequenties van letters. Maar voor de moderne cryptografie ligt dat heel anders -- daar heb je het over rekentijden die de verwachte levensduur van het heelal overstijgt!

De moderne cryptografie is gebaseerd op een klein aantal technieken. Er is een secure hash, die documenten samenvat in een klein en vastliggend aantal bits, er is een symmetrische encryptie die met dezelfde sleutel heen- en terugvertaalt, en er is een public-key encryptie waarbij twee sleutels verschillend zijn, en elkaars werking omkeren. Dat is zo ongeveer alles wat tegenwoordig nog wordt gebruikt.

Cryptografie wordt pas een ingewikkeld vakgebied als je gaat kijken naar concrete algoritmen. RSA en DSA zijn voorbeelden van public-key algoritmen; SHA1 en MD5 zijn secure hashes, en DES en BlowFish zijn voorbeelden van encryptie met symmetrische sleutels. Deze algoritmen introduceren hele precieze voorwaarden, willen ze hun werk betrouwbaar doen, en dat ontaardt dan bijvoorbeeld in enorme problemen voor schijnbaar simpele taken als het genereren van random getallen.

Bij wijze van introductie geven we hier aan wat de drie basis-principes doen, en we gevan wat voorbeelden, maar de echte complexiteit van het vakgebied mijden we hier. Je kunt met deze kennis echter een beetje inschatten welke zekerheid allerhande cryptografische trucs je bieden, en vooral ook welke ontbreken.

Secure hashes

Een hash is een bondige samenvatting, vaak in een vastliggend aantal bits, van een groter stuk informatie. Die ken je mogelijk wel in de vorm van checksums. Bijvoorbeeld een streepjescode op produkten uit de supermarkt, die tellen 12+1 cijfers. De eerste 12 cijfers zijn het nummer van het produkt, en de laatste is een gewogen som van de voorgaande cijfers, modulo 11. Dat laatste cijfer gebruikt een scanner om te controleren of de code goed is ingelezen.

Hashes zijn in het algemeen zo ontworpen dat de kans groot is dat een afwijking in de gehashte informatie wordt herkend aan de hash. Die kans kan groot zijn, maar hij zal nooit volledig zijn, want immers, je perst veel informatie (veel bits) samen in een klein getal (weinig bits) en er zijn dus altijd stukken informatie die dezelfde hashwaarde hebben.

Een secure hash is in zoverre bijzonder, dat hij is ontworpen om de kans te minimaliseren dat je twee stukken informatie kunt vinden die dezelfde hash-waarde hebben. En als onderdeel daarvan is het niet zomaar mogelijk om een hash-waarde terug te rekenen naar de originele informatie. Of, wat op hetzelfde neerkomt, om een verandering in een hashwaarde terug te rekenen naar een verandering in de originele informatie.

Wat heb je daar aan? Je kunt er heel bondig mee naar informatie verwijzen die ergens beschikbaar is. Telefonisch kan een vertrouwde stem je bijvoorbeeld zeggen dat je een document met een bepaalde secure hash hebt ontvangen, en je kunt die dan uit je mailbox opvissen en controleren. Zelfs als het telefoongesprek wordt afgeluisterd is er toch niemand die een document kan construeren met dezelfde hashwaarde, zelfs niet als die bruut het originele document in bezit heeft.

Kijk, dat is nou stoer -- een kraker mag de hashwaarde horen, hij mag het document met die hash zien, en toch kan hij er niet aan tornen zonder dat de ontvanger het opmerkt!

Als je zelf eens een hash wilt uitrekenen dan kan dat simpelweg met openssl, bijvoorbeeld met

echo Hello world | openssl sha1
echo Hello world | openssl md5
Deze opdrachten leveren de uitkomsten van twee verschillende hash-algoritmen, genaamd SHA1 en MD5. Uit MD5 komen 128 bits uitkomsten, en uit SHA1 komen 160 bits uitkomsten. SHA1 is moderner, en beter bestand tegen aanvallen waarvan niet meer zeker is dat MD5 ze zal kunnen blijven weerstaan. Overigens zijn dat wel theoretisch getinte problemen, want met 128 bits kun je elk atoom in het universum een uniek nummer geven! Maar als je kunt kiezen, kies dan toch maar beter voor SHA1.

Om te zien hoe gevoelig de algoritmen reageren moet je maar eens een letter in de invoertekst veranderen in een hoofdletter. Dat verandert precies één bit aan de invoer, en toch klapt (gemiddeld) de helft van het aantal bits om! De truc is natuurlijk dat je niet zomaar kunt achterhalen welke er zullen omklappen.

Een simpele digitale handtekening kun je maken door niet alleen een document te sturen, maar ook een hash van het document met daarachter een password. Bijvoorbeeld als het password in de file pwd.txt staat:

cat somedoc.txt pwd.txt | openssl sha1
Stuur de uitkomst hiervan, samen met somedoc.txt naar een ontvanger, en als die ook de file pwd.txt heet, dan kan hij dezelfde berekening doen, en kijken of er hetzelfde uitkomt. Zonder de inhoud van pwd.txt kan deze berekening niet gedaan worden. En omdat een secure hash niet omkeerbaar is, kun je het password niet herleiden uit de hash waarden! Dit is zo sterk dat er e-commerce systemen zijn waarin betalingsnotificaties hiermee ondertekend worden.

Symmetrisch versleutelen

Wanneer je documenten onleesbaar wilt maken voor `outsiders', dan kun je dat doen door ze blok voor blok als een reeks bits te beschouwen. Die bits kun je dan qua volgorde door elkaar husselen, en je kunt ze op elkaar laten inwerken in bijvoorbeeld optellingen. Dat alles draagt bij aan de onleesbaarheid.

Als je het een beetje handig aanpakt, dan beperk je je tot operaties die terugdraaibaar zijn. Neem bijvoorbeeld de volgende berekening met bits genaamd A en B:

in:  A, B
uit: not B, (A+B) mod 2
Die bewerking kan teruggedraaid worden met de volgende berekening:
in:  A', B'
uit: (A'+B'+1) mod 2, not A'
Reken maar na -- het klopt voor elke waarde voor de bits A en B. In het algemeen is het terugrekenen gewoon het achterwaarts uitvoeren van de heengaande berekening.

Nu is een vast mechanisme niet altijd even handig, dus wat vaak wordt gedaan is zo'n algoritme afhankelijk maken van een parameter, of zo je wilt een husselvoorschrift. Dit staat ook wel bekend als een sleutel.

Als je een apart algoritme maakt voor heen- en terugrekenen, dan kun je dezelfde sleutel gebruiken voor beide berekeningen. Omdat je bij encryptie en decryptie dezelfde sleutel wordt gebruikt, wordt er in dit geval ook wel gesproken over symmetrische encryptie.

Populaire algoritmen voor symmetrische encryptie zijn de Digital Encryption Standard, of kortweg DES, en dat is onder andere veel gebruikt door banken. Maar DES berust op tamelijk korte sleutels van 56 bits, en met hedendaagse computers kun je die met `brute force' kraken, ofwel door alle mogelijkheden uit te proberen. Tegenwoordig gebruikt men daarom liever 3DES (uitspraak: triple DES) wat neer komt op drie maal DES achter elkaar, mat twee verschillende sleutels van elk 56 bits. Een andere populaire is BlowFish, een open source algoritme dat bedoeld is om het gesloten DES algoritme te kunnen vervangen.

Je kunt zelf experimenteren met DES op de commandline, waarbij je bij wijze van sleutel een password intikt. Je kunt een tekst coderen met

echo Hello world | openssl des -e >hw-encrypted.des
en dat levert ware abacadabra op in de genoemde uitvoerfile, maar toch is dat te decoderen, want de originele boodschap wordt gewoon afgedrukt met
openssl des -d <hw-encrypted.des
Verander voor de grap ook maar eens een karakter in de file hw-encrypted.txt, en je zult zien dat de uitvoer nergens meer op slaat, of zelfs een foutmelding geeft als het een detecteerbare verandering is. Het zou ook te gek voor woorden zijn als dit niet zo was, want dan zou er te gemakkelijk met je geheime document gemanipuleerd kunnen worden.

De truc van symmetrische codes is doorgaans dat elk bit in de invoer invloed heeft op elk bit van de uitvoer, en dat het zo'n 50% bepalend is voor de waarde van de uitvoer. Aangezien elk invoerbit zo invloed op de uitvoer uitoefent, ontstaat een kakefonie van bits, en dat is nou juist wat je wilt.

Een lange file wordt doorgaans opgebroken in blokken van vaste lengte, en elk van deze blokken wordt afzonderlijk door het symmetrische algoritme heen gehaald. Daarbij bereik je het beste resultaat wanneer de uitvoer van vorige blokken ook invloed uitoefenen op de latere blokken. Want dan zie je bijvoorbeeld nooit aan de gecodeerde uitvoer dat een bepaald stuk tekst herhaald voorkomt.

Ook symmetrische cryptografie is behoorlijk cool -- iedereen mag eigenlijk de algoritmen wel weten (hoewel DES indertijd gold als militair geheim was dat eigenlijk niet nodig). Zolang de sleutel echter het gedeelde geheim van beide zijden is, kan niemand de informatie achterhalen die ermee versleuteld is.

Symmetrische sleutels zijn vaak gewoon willekeurige getallen, en om zeker te stellen dat niemand daarnaar kan raden moeten ze ook echt zo willekeurig mogelijk zijn. Er mogen geen patronen in voorkomen, en ze mogen niet herhaalbaar zijn door de bron ervan opnieuw te starten. En hier zie je al een tipje van de sluier, die aangeeft waarom het zo nodig is om details, zoals goede random getallengeneratie, goed te regelen.

De kans om een sleutel te kraken door patronen te herkennen in de uitvoer van een symmetrisch algoritme kan kleiner worden gemaakt door de informatie, voordat die wordt gecodeerd, eerst te comprimeren. Immers, compressie-algoritmen zijn erop gespitst om patronen in hun invoer te verwijderen en te vervangen door afkortingen. En minder patronen in de invoer van een symmetrisch encryptie-algoritme is alleen maar goed om patronen in de uitvoer te voorkomen.

Symmetrische sleutels zijn redelijk efficient, en ze worden dan ook vaak toegepast. Er wordt daartoe vaak een willekeurige sleutel geprikt die alleen voor het te verzenden document wordt gebruikt, en daarna wordt dat document verzonden met daaraan de sleutel, die op zo'n manier is ingepakt dat alleen de ontvanger hem kan uitpakken. Dat kan door trucs met public keys.

Public key cryptografie

Mocht de cryptografie tot dusverre al indrukwekkend zijn, het wordt pas echt bruikbaar wanneer je er public key algoritmen aan toevoegt. Dit werkt met asymmetrische sleutels, ofwel een andere sleutel voor encryptie en decryptie, dus gewoon sleutels die elkaars werking inverteren.

Als je nou zorgt dat deze twee sleutels een ingewikkelde relatie hebben, waardoor het niet doenlijk is om gegeven de ene sleutel de andere uit te rekenen, dan kun je zonder bezwaar de ene sleutel publiceren. Iedereen kan die gebruiken, en jijzelf houdt de andere sleutel geheim. Voilá, daar heb je public key cryptografie.

Je moet beide sleutels tegelijk aanmaken, en je genereert dan ook een sleutelpaar in plaats van een sleutel. Tijdens de generatie gebruik je doorgaans wat extra informatie waarmee je beide kunt genereren, maar die je daarna niet meer nodig hebt. Is die extra informatie weggegooid of alleen beschikbaar in de geheime sleutel, dan kun je wel vergeten om de geheime sleutel te herleiden uit de publieke sleutel, zelfs al zijn ze elkaars inverse. En dat is maar goed ook!

Het eerste algoritme voor public key cryptografie was RSA. Dat is al zo oud (lees: volwassen) dat het een hele patentcyclus van 25 jaar heeft doorgemaakt, en nu vrij beschikbaar is. Een sleutelpaar aanmaken doe je met

openssl genrsa -des3 2048 >geheim.pem
Dit genereert een geheime sleutel plus de informatie die extra was tijdens generatie. De sleutel wordt met 3DES gecodeerd, waarvoor je dus een geschikt password moet bedenken. Je leidt nu de publieke sleutel af uit de geheime sleutel met
openssl rsa -in geheim.pem -pubout >publiek.pem
Aangezien je nu met de geheime sleutel moet werken, wordt die gedecodeerd en moet je het password weer ingeven. Dat zul je altijd moeten doen bij bewerkingen met de geheime sleutel, anders zou iemand misschien de file geheim.pem kunnen pikken en zich als jou voordoen! Je hebt nu een publieke sleutel in publiek.pem, en die kun je aan iedereen sturen die je wilt, en een geheime in geheim.pem die je angstvallig dient te bewaken. Met dit tweetal kun je alle public-key bewerkingen doen.

Public key cryptografie is ronduit macho -- je publiceert een sleutel, waarvan de relatie met je geheime sleutel volgens bekende wiskundige verbanden gaat, en je publiceert zelfs de algoritmen voor encryptie en decryptie. En toch is het hardstikke veilig!

Omdat de sleutels elkaars werking inverteren kun je twee kanten op werken. Iedereen kan een boodschap encrypten met de publieke sleutel, en alleen de houder van de geheime sleutel kan hem dan nog decrypten. Dat wordt gebruikt om documenten, bijvoorbeeld email, onleesbaar te maken voor nieuwsgierige aagjes. Overigens wordt in de praktijk niet de hele boodschap zo behandeld, maar een willekeurig geprikte symmetrische sleutel, en die wordt dan gebruikt om de eigenlijke boodschap te coderen. Dat is veel efficiënter

Met je zojuist gemaakte sleutels kun je dit doen met

echo Hello world | openssl rsautl -encrypt -pubin -inkey publiek.pem -out email.rsa
openssl rsautl -decrypt -inkey geheim.pem -in email.rsa

Als je de sleutels in de andere richting gebruikt, dan kan alleen de houder van een geheime sleutel een boodschap ermee encrypten, en iedereen kan die dan decrypten ter controle. Dit wordt gebruikt voor digitale handtekeningen. Daartoe wordt een secure hash van een document gemaakt, deze wordt encrypted, en dat resultaat wordt dan samen met het document verzonden. De ontvanger krijgt het document, en kan de handtekening controleren, om zeker te stellen dat het document niet veranderd is sinds het verzonden is door de afzender.

Wederom kun je dit doen met je zelfgemakte sleutels:

echo Hello world | openssl rsautl -sign -inkey geheim.pem -out signeddata.rsa
openssl rsautl -verify -pubin -inkey publiek.pem <signeddata.rsa
Hoewel de tussenfile signeddata.rsa hier onleesbaar lijkt, is het geen cryptografisch versleutelde informatie, maar de tekst met een cryptografische handtekening er aan geplakt. Verander rustig een enkel karakter in de tussenfile, en de foutmeldingen vliegen je om de oren. En zo hoort het.

Wat je tenslotte vaak tegenkomt in cryptografische protocollen is identiteitscontrole. Hiervoor wordt een `uitdaging' gestuurd in de vorm van een lang getal dat met een publieke sleutel encrypted is. Alleen de bezitter van de geheime sleutel kan dan achterhalen wat het originele getal is. Door dat getal terug te zenden is aangetoond dat we spreken met de beheerder van een geheime sleutel.

Conclusie

Moderne cryptografie doet maar een paar dingen, maar ze doet die dingen uitzonderlijk goed. Het interessante is dat de principes heel algemeen zijn, en dat er meerdere algoritmen zijn die zich conform die principes gedragen. Die algoritmen zijn doorgaans open source, en staan vermoedelijk gewoon op je Linux systeem geïnstalleerd, als onderdeel van OpenSSL of PGP. Je hebt je online veiligheid dus zelf in de hand!


 
   ------ 8< ---------- 8< ----------- 8< ------ | OpenFortress*