In dit artikel breng ik je op de hoogte van de Advanced Encryption Standard (AES), veelgebruikte blokmodi, waarom je padding en initialisatievectoren nodig hebt en hoe je je gegevens kunt beschermen tegen wijziging. Tenslotte zal ik je laten zien hoe je dit eenvoudig kunt implementeren in Java, waarbij je de meeste beveiligingsproblemen kunt vermijden.
Wat iedere Software Engineer moet weten over AES
AES, ook bekend onder zijn oorspronkelijke naam Rijndael, werd in 2000 door het NIST geselecteerd om een opvolger te vinden voor de gedateerde Data Encryption Standard (DES). AES is een blokversleuteling, dat wil zeggen dat de versleuteling plaatsvindt op groepen bits van een vaste lengte. In ons geval definieert het algoritme blokken van 128 bits. AES ondersteunt sleutellengtes van 128, 192 en 256 bit.
Elk blok doorloopt vele cycli van transformatie rondes. Ik zal de details van het algoritme hier achterwege laten, maar de geïnteresseerde lezer wordt verwezen naar het Wikipedia-artikel over AES. Het belangrijkste is dat de sleutellengte geen invloed heeft op de blokgrootte, maar op het aantal herhalingen van transformatierondes (128 bit sleutel is 10 cycli, 256 bit is 14)
Tot mei 2009 waren de enige succesvolle gepubliceerde aanvallen tegen het volledige AES side-channel aanvallen op enkele specifieke implementaties. (Bron)
Wilt u meer dan één blok versleutelen?
AES versleutelt dus slechts 128 bits aan gegevens, maar als we hele berichten willen versleutelen, moeten we een blokmodus kiezen waarmee meerdere blokken tot één versleutelde tekst kunnen worden versleuteld. De eenvoudigste blokmodus is Electronic Codebook of ECB. Het gebruikt dezelfde ongewijzigde sleutel voor elk blok zoals dit:
Dit is bijzonder slecht omdat identieke klare tekstblokken worden gecodeerd tot identieke cijfertekstblokken.
Denk eraan dat u nooit voor deze modus moet kiezen, tenzij u alleen gegevens codeert die kleiner zijn dan 128 bit. Helaas wordt deze modus nog steeds vaak misbruikt omdat je geen initiële vector hoeft op te geven (waarover later meer) en hij daarom gemakkelijker te hanteren lijkt voor een ontwikkelaar.
Eén geval moet echter worden afgehandeld met blokmodi: wat gebeurt er als het laatste blok niet precies 128 bit is? Dat is waar padding in het spel komt, dat wil zeggen, het opvullen van de ontbrekende bits van het blok. De eenvoudigste vult de ontbrekende bits gewoon met nullen. De keuze voor opvulling bij AES heeft praktisch geen gevolgen voor de veiligheid.
Cipher Block Chaining (CBC)
Dus welke alternatieven voor ECB zijn er? Zo is er CBC, dat het huidige blok klare tekst XOR’t met het vorige blok cijfertekst. Op deze manier is elk cijfertekstblok afhankelijk van alle onbewerkte tekstblokken die tot op dat moment zijn verwerkt. Bij gebruik van dezelfde afbeelding als voorheen zou het resultaat ruis zijn die niet te onderscheiden is van willekeurige gegevens:
En hoe zit het dan met het eerste blok? De makkelijkste manier is om gewoon een blok met bijvoorbeeld nullen te gebruiken, maar dan zou elke encryptie met dezelfde sleutel en plaintext dezelfde cijfertekst opleveren. Als je dezelfde sleutel hergebruikt voor verschillende klaarteksten, wordt het ook makkelijker om de sleutel te achterhalen. Een betere manier is het gebruik van een willekeurige initialisatievector (IV). Dit is gewoon een mooi woord voor willekeurige data die ongeveer de grootte heeft van een blok (128 bit). Beschouw het als het zout van de encryptie, dat wil zeggen dat een IV openbaar kan zijn, willekeurig moet zijn en slechts één keer gebruikt mag worden. Bedenk wel dat het niet kennen van de IV alleen de ontcijfering van het eerste blok zal hinderen, omdat de CBC de cijfertekst XOR’t en niet de klare tekst van het vorige blok.
Bij het verzenden of bewaren van de gegevens is het gebruikelijk om de IV gewoon aan het eigenlijke cijfermedium toe te voegen. Als u wilt weten hoe u AES-CBC correct gebruikt, bekijk dan deel 2 van deze serie.
Counter Mode (CTR)
Een andere optie is om CTR mode te gebruiken. Deze blokmodus is interessant omdat het een blokcijfer verandert in een stroomcijfer, wat betekent dat er geen padding nodig is. In de basisvorm zijn alle blokken genummerd van 0 tot n. Elk blok wordt nu vercijferd met de sleutel, de IV (hier ook nonce genoemd) en de tellerwaarde.
Het voordeel is, in tegenstelling tot CBC, vercijfering parallel kan worden uitgevoerd en alle blokken afhankelijk zijn van de IV, niet alleen de eerste. Een groot nadeel is dat een IV nooit met dezelfde sleutel mag worden hergebruikt, omdat een aanvaller daaruit gemakkelijk de gebruikte sleutel kan berekenen.
Kan ik er zeker van zijn dat niemand mijn bericht heeft veranderd?
De harde waarheid: encryptie beschermt niet automatisch tegen het wijzigen van gegevens. Het is eigenlijk een vrij veel voorkomende aanval. Lees hier over een meer diepgaande discussie over dit onderwerp.
Dus wat kunnen we doen? We voegen gewoon een Message Authentication Code (MAC) toe aan het versleutelde bericht. Een MAC is vergelijkbaar met een digitale handtekening, met dit verschil dat de verifiërende en de authenticerende sleutel praktisch dezelfde zijn. Er zijn verschillende varianten van deze methode, maar de methode die door de meeste onderzoekers wordt aanbevolen heet Encrypt-then-Mac. Dat wil zeggen dat na versleuteling een MAC wordt berekend over de versleutelde tekst en deze wordt toegevoegd. Als type MAC wordt meestal Hash-based message authentication code (HMAC) gebruikt.
Dus nu wordt het ingewikkeld. Voor integriteit/authenticiteit moeten we een MAC algoritme kiezen, een encryptie tag mode kiezen, de mac berekenen en deze toevoegen. Dit is ook traag omdat het hele bericht twee keer moet worden verwerkt. De andere kant moet hetzelfde doen, maar dan voor het ontsleutelen en verifiëren.
Geauthenticeerde Encryptie met GCM
Zou het niet geweldig zijn als er modi waren die alle authenticatie voor je afhandelen? Gelukkig is er zoiets als geauthenticeerde encryptie, dat tegelijkertijd vertrouwelijkheid, integriteit en authenticiteit van de gegevens garandeert. Een van de meest populaire blokmodi die dit ondersteunt heet Galois/Counter Mode of kortweg GCM (het is bijvoorbeeld ook beschikbaar als een cipher suite in TLS v1.2)
GCM is in feite CTR-modus die tijdens de encryptie ook sequentieel een authenticatie-tag berekent. Deze authenticatie tag wordt dan gewoonlijk toegevoegd aan de cijfertekst. De grootte ervan is een belangrijke beveiligingseigenschap, dus moet hij minstens 128 bit lang zijn.
Het is ook mogelijk om extra informatie te authenticeren die niet in de klare tekst is opgenomen. Deze gegevens worden geassocieerde gegevens genoemd. Waarom is dit nuttig? De versleutelde gegevens hebben bijvoorbeeld een meta eigenschap, de creatie datum, die wordt gebruikt om te controleren of de inhoud opnieuw moet worden versleuteld. Een aanvaller zou nu triviaal de aanmaakdatum kunnen veranderen, maar als deze wordt toegevoegd als geassocieerde gegevens, zal GCM ook dit stukje informatie verifiëren en de verandering herkennen.
Een verhitte discussie: Welke sleutelgrootte moet ik gebruiken?
De intuïtie zegt: hoe groter, hoe beter – het is duidelijk dat het moeilijker is om een 256 bit random waarde te brute-forcen dan een 128 bit. Met onze huidige kennis zou het brute forceren van alle waarden van een 128 bit lang woord een astronomische hoeveelheid energie vergen, niet realistisch voor wie dan ook in een verstandige tijd (ik kijk naar jou, NSA). De keuze is dus eigenlijk tussen oneindig en oneindig maal 2¹²⁸.
AES heeft eigenlijk drie verschillende sleutelgroottes omdat het gekozen is als een US Federal Algorithm Apt at being used in various areas under the control of the US Federal government . (…) Dus kwamen de knappe koppen van het leger met het idee dat er drie “beveiligingsniveaus” moesten zijn, zodat de belangrijkste geheimen werden versleuteld met de zware methoden die ze verdienden, maar de gegevens van lagere tactische waarde konden worden versleuteld met meer praktische, zij het zwakkere, algoritmen. (…) Het NIST besloot dus formeel de voorschriften te volgen (drie sleutelgroottes te vragen), maar ook het slimme te doen (het laagste niveau moest onbreekbaar zijn met te verwachten technologie)(Bron)
Het argument volgt: een met AES versleuteld bericht zal waarschijnlijk niet worden gebroken door brute forcing van de sleutel, maar door andere, minder dure aanvallen (die momenteel niet bekend zijn). Deze aanvallen zijn net zo schadelijk voor de 128 bit sleutel als voor de 256 bit sleutel, dus het kiezen van een grotere sleutelgrootte helpt in dit geval niet.
Dus in principe is een 128 bit sleutel voldoende beveiliging voor het grootste deel van elk gebruik, met uitzondering van quantum computer beveiliging. 128 bit versleutelt ook sneller dan 256 bit en het sleutelschema voor 128 bit sleutels lijkt beter beschermd tegen gerelateerde-sleutel aanvallen (maar dit is niet relevant voor de meeste toepassingen in de echte wereld).
Als een kanttekening: Side Channel Attacks
Side channel attacks zijn aanvallen die gericht zijn op het uitbuiten van problemen die specifiek zijn voor bepaalde implementaties. Encryptieprogramma’s zelf kunnen er niet inherent tegen worden beschermd. Eenvoudige AES implementaties kunnen gevoelig zijn voor onder andere timing en caching aanvallen.
Een eenvoudig voorbeeld: een eenvoudig algoritme dat gevoelig is voor timing aanvallen is een equals()
methode die twee geheime byte arrays vergelijkt. Als de equals()
een quick-return heeft, wat betekent dat na het eerste paar bytes dat niet overeenkomt de lus wordt beëindigd, kan een aanvaller de tijd meten die nodig is voor de equals()
om te voltooien en kan hij byte voor byte raden totdat ze allemaal overeenkomen.