Bouncy Castle

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