Bouncy Castle Secure programmeren in Java Kees van Rooijen June 17, 2014 1 Introductie Om in Java cryptografische methoden toe te kunnen passen wordt gebruik gemaakt van de Java Cryptography Architecture (JCA). De JCA bestaat uit een raamwerk, dat aangeeft welke cryptografische methoden er gebruikt kunnen worden. De werkelijke implementatie van die methoden is bevat in de zogenaamde providers, zoals de standaardprovider SunJCE. Buiten de standaard bij java meegeleverde providers kan er ook zelf een provider worden ge¨ımplementeerd. Bouncy Castle is zo’n aangepaste implementatie. Anders gezegd is Bouncy Castle dus een verzameling van een groot aantal ge¨ımplementeerde cryptografische algoritmen, die in de standaard Java omgeving gebruikt kunnen worden. Het doel van deze paper is een beeld te geven van het pakket Bouncy Castle. Eerst wordt kort ingegaan op de geschiedenis en ontwikkeling van de code. Vervolgens wordt gekeken op welke manier het in een Java programma te gebruiken is, met enkele concrete voorbeelden. Omdat volledige kennis van de implementatie van de algoritmen niet nodig is voor het gebruik daarvan, wordt daar ook niet uitgebreid op ingegaan. Ook is het niet mogelijk een beschrijving te geven van gebruik van alle aspecten van het pakket Bouncy Castle, dus wordt gekozen voor een uitgebreidere beschrijving van een kleine selectie. Ten slotte wordt nog een aantal andere mogelijkheden die zijn ge¨ımplementeerd in Bouncy Castle beschreven. 2 Ontstaan De eerste versie van Bouncy Castle verscheen in mei 2000. Oorspronkelijk was alleen de implementatie voor Java beschikbaar en die bestond uit 27 duizend regels code. Inmiddels is er een totaal van 335 duizend regels code, die alle oude functionaliteit bevat met nog een groter aantal algoritmen. In 2006 is ook ondersteuning voor C# toegevoegd, die bestaat uit 145 duizend regels code. 1 In 2013 werd besloten dat de manier waarop aan het project gewerkt werd gestructureerder moest, en werd de samenwerking Legion of the Bouncy Castle Inc opgestart. Deze non-profit organisatie kreeg in November 2013 de status van een liefdadigheidsinstelling. 3 Gebruik Om Bouncy Castle te kunnen gebruiken moet deze na het downloaden worden toegevoegd als provider. Als dit is gebeurd kan vervolgens in de code op de plaats waar de provider wordt gekozen ”BC” worden aangegeven. Het programma gebruikt dan verder het Bouncy Castle pakket en hoeft niet verder te worden aangepast. Eerst zal ik een voorbeeld geven van het gebruik bij een implementatie van het symmetrische AES algortime. Daarna volgt een demonstratie van het asymmetrische RSA algoritme. 3.1 AES De Advanced Encryption Standaard is een cryptografisch algoritme, dat sinds 2001 gebruikt wordt. Het algoritme is symmetrisch, dus de sleutel die bij dit algoritme wordt gebruikt voor encryptie is gelijk aan de sleutel voor decryptie. In het volgende voorbeeld wordt een sleutel gegenereerd en daarmee met behulp van het AES algoritme een tekst gecodeerd en weer gedecodeerd. Allereerst wordt een key gegenereerd, op basis van een gegeven byte-array. byte [ ] keyBytes = new byte 0 x00 , 0 x01 , 0 x02 , 0 x08 , 0 x09 , 0 x0a , 0 x10 , 0 x11 , 0 x12 , [] { 0 x03 , 0 x04 , 0 x05 , 0 x06 , 0 x07 , 0x0b , 0 x0c , 0x0d , 0 x0e , 0 x0f , 0 x13 , 0 x14 , 0 x15 , 0 x16 , 0 x17 } ; SecretKeySpec key = new SecretKeySpec ( keyBytes , ”AES ” ) ; De te coderen tekst wordt ook gegeven als byte-array. Om deze leesbaar weer te geven wordt elke byte afgedrukt als twee karakters. byte [ ] i n p u t = new byte [ ] { 0 x00 , 0 x11 , 0 x22 , 0 x33 , 0 x44 , 0 x55 , 0 x66 , 0 x77 , ( byte ) 0 x88 , ( byte ) 0 x99 , ( byte ) 0 xaa , ( byte ) 0 xbb , ( byte ) 0 xcc , ( byte ) 0 xdd , ( byte ) 0 xee , ( byte ) 0 x f f } ; System . out . p r i n t l n ( ” i n p u t t e x t : ” + U t i l s . toHex ( i n p u t ) ) ; input text : 00112233445566778899aabbccddeeff 2 Vervolgens wordt een Cypher object aangemaakt. Merk hier op dat we bij het cre¨eren van het object aangeven dat we de provider Bouncy Castle willen gebruiken (”BC”). Cipher c i p h e r = Cipher . g e t I n s t a n c e ( ”AES” , ”BC” ) ; Voor de encryptie wordt nu een aantal stappen genomen. De cypher wordt in encryptiemodus gezet. De methode cipher.update codeert de input van positie 0 tot eind. Het resultaat wordt in de nieuwe array cipherText gezet. In ctLength wordt bijgehouden hoeveel bytes er ook daadwerkelijk in de output array zijn geschreven. Dan wordt de methode cipher.doFinal aangeroepen, om afhankelijk van het algoritme nog laatste stappen te ondernemen. In het geval van AES is deze stap overbodig, maar om het programma niet afhankelijk van het algoritme te laten zijn is de stap toch nodig. Uiteindelijk wordt de gecodeerde tekst geprint. byte [ ] c i p h e r T e x t = new byte [ i n p u t . l e n g t h ] ; c i p h e r . i n i t ( Cipher .ENCRYPT MODE, key ) ; i n t ctL e ng t h = c i p h e r . update ( input , 0 , i n p u t . l e n g t h , c i p h e r T e x t , 0 ) ; ctLengt h += c i p h e r . d o F i n a l ( c i p h e r T e x t , c t L en g th ) ; System . out . p r i n t l n ( ” c i p h e r t e x t : ” + U t i l s . toHex ( c i p h e r T e x t ) + ” b y t e s : ” + c t Le n gt h ) ; cipher text: dda97ca4864cdfe06eaf70a0ec0d7191 bytes: 16 Om de tekst vervolgens weer te kunnen decoderen wordt de cypher in decryptiemodus gezet en soortgelijke aanroepen gedaan. Het resultaat wordt geprint. byte [ ] p l a i n T e x t = new byte [ c t Le n gt h ] ; c i p h e r . i n i t ( Cipher .DECRYPT MODE, key ) ; i n t ptLength = c i p h e r . update ( c i p h e r T e x t , 0 , ctLength , p l a i n T e x t , 0 ) ; ptLength += c i p h e r . d o F i n a l ( p l a i n T e x t , ptLength ) ; System . out . p r i n t l n ( ” p l a i n t e x t : ” + U t i l s . toHex ( p l a i n T e x t ) + ” b y t e s : ” + ptLength ) ; plain text : 00112233445566778899aabbccddeeff bytes: 16 3 3.2 RSA Een voorbeeld van een asymmetrische methode is RSA. Het algoritme maakt gebruik van een public key (die bij iedereen bekend is) en een private key (die dat niet is). Een boodschap kan dan door iedereen worden gecodeerd met de public key, en slechts worden gedecodeerd door hen die in het bezit zijn van de private key. De moeilijkheid hier ligt hem vooral in het genereren van de keys. De waarden waarmee de key objecten worden ge¨ınitialiseerd moeten namelijk aan specifieke eisen voldoen. In het voorbeeld wordt er van uit gegaan dat die waarden al bekend zijn en aan de eisen voldoen. De input is wat kleiner gekozen, omdat de key anders te lang moet worden om het leesbaar te houden. Om deze reden is RSA ook niet praktisch om te gebruiken voor het versleutelen van hele teksten. Het kan wel worden gebruikt om veilig keys uit te wisselen voor gebruik bij een symmetrisch algoritme zoals het eerder genoemde AES. De cypher wordt nu aangemaakt met ”RSA”. byte [ ] i n p u t = new byte [ ] { ( byte ) 0 xbe , ( byte ) 0 x e f } ; Cipher c i p h e r = Cipher . g e t I n s t a n c e ( ”RSA” , ”BC” ) ; De waarden zijn ons hier bekend en dus worden de keys als volgt gemaakt. Merk op dat er zoals genoemd nu een afzonderlijke public key en private key zijn. KeyFactory k ey Fa ct or y = KeyFactory . g e t I n s t a n c e ( ”RSA” , ”BC” ) ; RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec ( new B i g I n t e g e r ( ” d 4 6 f 4 7 3 a 2 d 7 4 6 5 3 7 d e 2 0 5 6 a e 3 0 9 2 c 4 5 1 ” , 1 6 ) , new B i g I n t e g e r ( ” 1 1 ” , 1 6 ) ) ; RSAPublicKey pubKey = ( RSAPublicKey ) ke yF ac to ry . g e n e r a t e P u b l i c ( pubKeySpec ) ; RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec ( new B i g I n t e g e r ( ” d 4 6 f 4 7 3 a 2 d 7 4 6 5 3 7 d e 2 0 5 6 a e 3 0 9 2 c 4 5 1 ” , 1 6 ) , new B i g I n t e g e r ( ” 5 7 7 9 1 d5430d593164082036ad8b29fb1 ” , 1 6 ) ) ; RSAPrivateKey privKey = ( RSAPrivateKey ) k ey Fa ct or y . g e n e r a t e P r i v a t e ( privKeySpec ) ; Vervolgens lijkt de encryptie en decryptie veel op die bij AES: System . out . p r i n t l n ( ” i n p u t : ” + U t i l s . toHex ( i n p u t ) ) ; input : beef 4 c i p h e r . i n i t ( Cipher .ENCRYPT MODE, pubKey ) ; byte [ ] c i p h e r T e x t = c i p h e r . d o F i n a l ( i n p u t ) ; System . out . p r i n t l n ( ” c i p h e r : ” + U t i l s . toHex ( c i p h e r T e x t ) ) ; cipher: d2db15838f6c1c98702c5d54fe0add42 c i p h e r . i n i t ( Cipher .DECRYPT MODE, privKey ) ; byte [ ] p l a i n T e x t = c i p h e r . d o F i n a l ( c i p h e r T e x t ) ; System . out . p r i n t l n ( ” p l a i n : ” + U t i l s . toHex ( p l a i n T e x t ) ) ; plain : beef 4 Mogelijkheden 4.1 Digests Een aanvaller die de gecodeerde tekst in handen krijgt kan deze misschien niet decoderen, maar wel aanpassen. Als de aanvaller kennis heeft van de structuur van de oorspronkeleijke tekst, kan hij op bepaalde posities aanpassingen doen aan de gecodeerde tekst, zodat hij weet dat wanneer de tekst weer wordt gedecodeerd, die aanpassingen ook bestaan op die positie. Om dit te voorkomen kan een zogenaamde message digest worden berekend. Dit gaat als volgt. Op basis van de oorspronkelijke boodschap wordt een hash berekend: een controlecijfer van die tekst. Deze wordt bij de tekst gevoegd en het geheel wordt gecodeerd. Als de boodschap daarna wordt gedecodeerd, wordt de hash van de gedecodeerde tekst berekend en vergeleken met de gedecodeerde hash die was megeleverd. Komen deze twee overeen dan vertelt dat ons dat de oorspronkelijke tekst nog intact is. In Bouncy Castle zijn hiervoor mogelijkheden beschikbaar. In de klasse MessageDigest kan met de methode update(byte array) de hash van een bepaalde tekst worden berekend. Bekende digest algoritmen zijn ge¨ımplementeerd, zoals bijvoorbeeld SHA256. 5 4.2 Diffie-Hellman Een manier om een te zorgen dat twee personen beschikken over dezelfde sleutel, is de Diffie-Hellman methode. Hierbij is er een public key G, en kiezen de personen zelf private keys xA en xB . Persoon A maakt nu Gxa bekend en persoon B maakt Gxb bekend. In bepaalde getalsystemen is daaruit niet af te leiden wat de keuze voor xA en xB is geweest, ook al is G bekend. Persoon A kan nu de key berekenen met (Gxb )xa , en persoon B met (Gxa )xb . Deze getallen zijn gelijk, waardoor A en B nu over dezelfde key beschikken. Niemand anders kan xa en xb afleiden en dus kent niemand anders verder de key. In Bouncy Castle kan nu gebruikt worden gemaakt van de klassen KeyAgreement en DHParameterSpec. Vervolgens bestaat de methode generateSecret van de klasse KeyAgreement om de geheime sleutel te genereren. Op deze manier is er dus een veilige overdracht van sleutels mogelijk, zodat een symmetrisch algoritme als AES gebruikt kan worden om de boodschap te versleutelen. 5 Conclusie Bouncy Castle is een uitgebreide open-source verzameling van cryptografische algoritmen. Het kan worden gebruikt als provider voor de cryptografische Java omgeving JCA. Het pakket bevat onder andere implementaties van verschillende symmetrische en asymmetrische algoritmes zoals AES en RSA. Ook biedt het de mogelijkheid tot berekenen van hash-waarden om de juistheid van het bericht te garanderen, en verschillende mogelijkheden om te zorgen dat de keys alleen bij de juiste personen bekend zijn. De mogelijkheden van Bouncy Castle zijn veel groter dan in deze paper genoemd. Zoals genoemd bestaat het pakket uit meer dan 300 duizend regels code en is het dus niet makkelijk samen te vatten. Door middel van een aantal voorbeelden waarbij simpele en bekende algoritmen worden gebruikt wordt wel ge¨ıllustreerd dat het ook zonder uitgebreide kennis van cryptografie en zonder volledige kennis van de mogelijkheden van het Bouncy Castle pakket goed mogelijk is de basismethoden uit het pakket te gebruiken en veiliger in Java te programmeren. 6 Referenties 1. Hook, D. (2005) Beginning Cryptography with Java. Wrox Press 2. Oracle (2014) Java Cryptography Architecture (JCA) Reference Guide. http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html 3. The Legion of the Bouncy Castle (2013) Bouncy Castle 1.50 https://www.bouncycastle.org/latest releases.html 4. The Legion of the Bouncy Castle (2013) About the Legion of the Bouncy Castle https://www.bouncycastle.org/about.html 7
© Copyright 2024 ExpyDoc