Professional Documents
Culture Documents
PRZYKADOWY ROZDZIA
SPIS TRECI
KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG
TWJ KOSZYK
DODAJ DO KOSZYKA
CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK
CZYTELNIA
FRAGMENTY KSIEK ONLINE
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Kryptografia w Javie.
Od podstaw
Autor: David Hook
Tumaczenie: Zbigniew Banach
ISBN: 83-246-0277-1
Tytu oryginau: Beginning Cryptography with Java
Format: B5, stron: 512
Przykady na ftp: 600 kB
O autorze ....................................................................................................................................................13
Wstp ..........................................................................................................................................................15
Rozdzia 1. JCA i JCE ...................................................................................................................................21
Podstawowa architektura .............................................................................................. 21
Podpisywanie dostawcw .............................................................................................. 24
Pliki polityki ogranicze ................................................................................................ 25
Instalacja plikw polityki nieograniczajcych siy algorytmw ....................................... 25
Rozwizywanie innych problemw ............................................................................. 27
Skd wiadomo, e pliki dostarczone
przez firm Sun naprawd dziaaj tak, jak powinny? .............................................. 28
Instalacja dostawcy Bouncy Castle ................................................................................ 28
Instalacja poprzez konfiguracj rodowiska uruchomieniowego ................................... 28
Instalacja na etapie wykonania ................................................................................ 31
Priorytety dostawcw .................................................................................................... 31
Sprawdzanie moliwoci dostawcy ................................................................................. 33
Podsumowanie ............................................................................................................ 34
wiczenia .................................................................................................................... 35
Spis treci
Spis treci
10
Spis treci
11
Skorowidz ...............................................................................................................................................481
Poznasz te w praktyce rne API Javy wspomagajce generowanie, modyfikacj i stosowanie kluczy i szyfrw asymetrycznych.
110
111
off += state.length;
sha.update(state);
/**
* Zwraca obiekt SecureRandom generujcy sta warto.
* <b>Wycznie do celw testowych!</b>
* @return staa warto losowa
*/
public static SecureRandom createFixedRandom()
{
return new FixedRand();
}
Jak wida, wykorzystany tu pomys opiera si na uyciu skrtu wiadomoci do wygenerowania pseudolosowego strumienia bajtw. Przepisz t wersj klasy Utils, skompiluj j jako
pierwsz klas pakietu rezSzial4 i moesz zaczyna.
Algorytm RSA zosta opublikowany w 1977 roku przez Ronalda Rivesta, Adi Shamira i Leonarda Adlemana. Odtajnione w ostatnich latach dokumenty ujawniy, e metod uywan
w RSA odkry jeszcze w 1973 roku Clifford Cocks z brytyjskiego GCHQ.
112
113
java.math.BigInteger;
java.security.KeyFactory;
java.security.interfaces.RSAPrivateKey;
java.security.interfaces.RSAPublicKey;
java.security.spec.RSAPrivateKeySpec;
java.security.spec.RSAPublicKeySpec;
import javax.crypto.iipher;
/**
* Podstawowy przykad RSA.
*/
public class BaseRSAExample
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] { (byte)0xbe, (byte)0xef };
iipher cipher = iipher.getInstance("RSA/None/NoPadding", "Bi");
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "Bi");
// tworzenie kluczy
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
new BigInteger("d46f47ea2d7465e7de2056aee092c451", 16),
new BigInteger("11", 16));
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(
new BigInteger("d46f47ea2d7465e7de2056aee092c451", 16),
new BigInteger("57791d54e0d59e1640820e6ad8b29fb1", 16));
RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec);
RSAPrivateKey privKey =
(RSAPrivateKey)keyFactory.generatePrivate(privKeySpec);
System.out.println("dane wejciowe: " + ptils.toHex(input));
// szyfrowanie
cipher.init(iipher.ENiR PciMhDE, pubKey);
byte[] ciphercext = cipher.doFinal(input);
System.out.println("dane zaszyfrowane: " + ptils.toHex(ciphercext));
// deszyfrowanie
cipher.init(iipher.DEiR PciMhDE, privKey);
byte[] plaincext = cipher.doFinal(ciphercext);
114
Jak to dziaa
Sposb uycia klasy Cipher nie rni si tu specjalnie od przykadw z wczeniejszych
rozdziaw. Rnica polega jedynie na tym, e klasy reprezentujce warto klucza oraz
interfejsy odpowiadajce samym kluczom s bardziej wyspecjalizowane ju z samych
ich nazw wynika, e s one przeznaczone dla RSA.
Poszczeglne klasy opisujce klucz omwi szczegowo nieco pniej, ale z kodu wida,
e inicjalizacja szyfru RSA wymaga zdefiniowania obiektw wartoci zawierajcych liczby
odpowiadajce kluczowi publicznemu i prywatnemu. Obiekty te s nastpnie przeksztacane
w klucze i przekazywane obiektowi szyfru za porednictwem klasy fabrykujcej obsugujcej klucze asymetryczne klasy KeyFactery. Potem wszystko postpuje zgodnie ze znanymi ju zasadami. Przyjrzyjmy si zatem bliej klasie KeyFactery, pamitajc, e jest ona
abstrakcj, wic wszystko, co powiemy na jej temat, odnosi si do wszystkich algorytmw
asymetrycznych.
Klasa KeyFactory
Klasa java.security.KeyFactery stanowi warstw abstrakcji pozwalajc zamienia obiekty
parametrw klucza utworzone poza rodowiskiem danego dostawcy na obiekty Key, jak
rwnie konwertowa ju istniejce klucze na specyfikacje nadajce si do eksportu. Podobnie jak klasa Cipher, KeyFactery uywa metody fabrykujcej getInstance(), ktra
w kwestii lokalizacji i priorytetw dostawcw zachowuje si dokadnie tak samo, jak analogiczne metody innych klas JCA.
Klasa KeyFactery udostpnia cztery metody konwersji. Metody generatePublic() i generatePrivate() konwertuj specyfikacje klucza na obiekty reprezentujce klucz, getKeySpec()
pozwala tworzy i eksportowa parametry klucza, a metoda translateKey() przeksztaca
klucze jednego dostawcy do postaci nadajcej si do uytku z innym dostawc.
Z punktu widzenia poprzedniego przykadu najciekawsze s metody generatePublic()
i generatePrivate(). Jak wida z kodu, su one do zamiany obiektw zawierajcych parametry kluczy RSA na faktyczne obiekty klucza, wypada zatem bliej przyjrze si klasom
parametrw kluczy i interfejsom do obsugi kluczy.
115
java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
116
Jak wida, dugo szyfrogramu ronie wraz z dugoci klucza. Cho w tym przykadzie
wykorzystany zosta klucz o dugoci zaledwie 256 bitw (czyli jednej czwartej dugoci
klucza dla aplikacji produkcyjnej), to i tak wyranie wida, e zastosowanie RSA spowodowao znaczne wyduenie pierwotnej 4-bajtowej wiadomoci. W przypadku RSA nie ma
adnego odpowiednika trybu CRT, wic trzeba si pogodzi z tym efektem jako cen tej
metody szyfrowania.
117
Jak to dziaa
Podobnie jak we wczeniejszym programie BaseRSAgeacple, kod wyglda tu bardzo podobnie do kodw z rozdziau 2. Jedyn znaczc rnic jest wprowadzenie obiektw KeyPair
i KeyPairGenerater, gdy mamy tym razem do czynienia z szyfrem asymetrycznym. Do
zrozumienia przykadu wystarczy zrozumienie dziaania klas biorcych udzia w generowaniu klucza publicznego i prywatnego.
Klasa KeyPair
Klasa java.security.KeyPair stanowi pojemnik dla klucza prywatnego i odpowiadajcego
mu klucza publicznego. Obiekt KeyPair jest prostym obiektem wartoci udostpniajcym
metody getPrivate() i getPublic(), zwracajce odpowiednio klucz prywatny i klucz publiczny.
Klasa KeyPairGenerator
Instancje klasy java.security.KeyPairGenerater s pobierane za pomoc typowej metody
fabrykujcej getInstance(), ktrej zachowanie w kwestii wyboru i priorytetu dostawcy
usug jest identyczne, jak w przypadku metod getInstance() innych poznanych ju klas JCA.
Obsuga samej klasy jest bardzo prosta. Dostpne s cztery wersje metody initialize():
dwie przyjmujce liczb cakowit okrelajc dugo klucza (z czego jedna wymaga dodatkowo przekazania rda danych losowych) i dwie przyjmujce penicy t sam funkcj
obiekt AlgerithcParaceterSpec. Jak si okae w dalszych przykadach, najwicej moliwoci
daje wykorzystanie wersji przyjmujcych obiekty AlgerithcParaceterSpec, gdy w przypadku algorytmw asymetrycznych dugo klucza jest tylko jednym z wielu moliwych
parametrw, ktrych wartoci przydaje si kontrolowa.
Pozostaj ju tylko metody zwracajce generowany przez klas obiekt pary kluczy: KeyPairGenerater.generateKeyPair() i KeyPairGenerater.genKeyPair(). Metody te dziaaj identycznie, a rni si wycznie nazw, wic wybr jednej z nich zaley wycznie od uznania
programisty.
Istnieje te waciwa algorytmowi RSA klasa implementujca AlgerithcParaceterSpec
jest to RSAKeyGenParaceterSpec. Klasa ta jest bardzo prosta, wic przyjrzyjmy jej si od razu.
Klasa RSAKeyGenParameterSpec
Jak ju wspomniaem, generowanie par kluczy RSA wie si najczciej z wybraniem
wykadnika publicznego i wygenerowaniem dla niego odpowiedniego wykadnika prywatnego. JCA pozwala okreli zarwno podan dugo klucza, jak i wykadnik publiczny
dla pary kluczy generowanej za pomoc obiektu KeyPairGenerater wystarczy przekaza
metodzie KeyPairGenerater.initialize() odpowiedni parametr w postaci obiektu implementujcego AlgerithcParaceterSpec. Powinien to by obiekt klasy java.security.spec.RSAKeyGenParaceterSpec, ktra pojawia si w JDK 1.3. Stworzenie takiego obiektu jest bardzo
proste i wymaga jedynie podania w konstruktorze wykadnika publicznego i dugoci klucza.
118
Po takiej inicjalizacji wszystkie klucze publiczne RSA generowane przez obiekt generater
bd miay wykadnik publiczny o wartoci odpowiadajcej staej F4, czyli liczbie cakowitej 0x10001.
119
n getPrivategepenent()
n getPriceP()
n getPriceQ()
Poza tym dostpne s jeszcze metody getPricegepenentP(), getPricegepenentQ() i getCrtCeejjicient() zwracajce wstpnie wyliczone wartoci bazujce na p i q, ktrych wykorzystanie pozwala dodatkowo przyspieszy wykonywanie oblicze z kluczem prywatnym RSA.
W przypadku dostawcy Bouncy Castle obiekty RSAPrivateCrtKey s zwracane przez implementacj klasy KeyPairGenerater, ale mona si samodzielnie przekona o znacznym koszcie operacji na kluczu publicznym bez wykorzystania CRT, tworzc wasn implementacj
klasy RSAPrivateKeySpec, pobierajc potrzebne wartoci z obiektu RSAPrivateCrtKey.
120
wierszem:
byte[] input = new byte[] { 0x00, (byte)0xbe, (byte)0xef };
Zwracaj tu uwag dwie rzeczy. Najbardziej oczywiste jest to, e dane odszyfrowane rni
si od danych wejciowych znikno pocztkowe zero. Nietrudno te zauway, e pomimo innych danych wejciowych szyfrogram jest ten sam, co poprzednio. W pewnym
sensie moe to tumaczy brak pocztkowego zera w tekcie odszyfrowanym, ale nadal nie
wiadomo, dlaczego zero zostao usunite przed szyfrowaniem. Czyby bd algorytmu RSA,
a przynajmniej jego implementacji?
Na szczcie zarwno algorytm, jak i jego implementacja dziaaj bez zarzutu. W czci
powiconej opisowi dziaania algorytmu RSA dowiedzielimy si, e przetworzenie tablicy
bajtw przekazywanej jako dane wejciowe algorytmu wymaga jej zamiany na du liczb
cakowit. Pocztkowe zera gin wanie na etapie konwersji w wiecie liczb pocztkowe
zera nie maj znaczenia.
Oczywicie w naszym przypadku zera pocztkowe maj znaczenie, wic przydaby si mechanizm dopenienia, ktry pozwoliby je zachowa. Dopenienie ma te znacznie waniejsze
zadanie od zachowywania pocztkowych zer. Sprbuj zmieni przykad tak, by wykadnikiem publicznym by F0 (warto 0x3), a nie F4 (warto 0x100001). W tym celu wystarczy zaimportowa klas java.security.spec.RSAKeyGenParaceterSpec i zmieni tre wywoania generater.initialize() w nastpujcy sposb:
generator.initialize(
new RSAKeyGenParameterSpec(256, RSAKeyGenParameterSpec.F0), random);
121
Straci wiodce zero to jedno, ale tutaj stao si co znacznie gorszego oryginaln wiadomo mona teraz odtworzy, po prostu obliczajc pierwiastek szecienny szyfrogramu.
Dzieje si tak dlatego, e po konwersji na liczb cakowit i podniesieniu do potgi wykadnika publicznego dane wejciowe s mniejsze od moduu. Oznacza to, e etap dzielenia
modulo wcale nie utrudni ycia napastnikowi usiujcemu odczyta oryginaln wiadomo, gdy zwrci po prostu pierwotn warto podniesion do potgi. Pokazuje to dobitnie, e dopenienie wiadomoci w celu uzyskania przed potgowaniem liczby bliszej dugoci moduowi suy nie tylko zachowaniu wiodcych zer.
Przy okazji metod wykorzystania chiskiego twierdzenia o resztach wspomniaem dokument PKCS #1. Tak si skada, e ta sama publikacja opisuje te mechanizmy dopenienia
dla algorytmu RSA. Pierwotnie by to tylko jeden mechanizm, std te nieco dziwna konwencja nazewnictwa omawianych dalej metod, ale obecnie standard przewiduje te inne
mechanizmy dopenienia. W Javie przyjo si nazywa oryginalny mechanizm zdefiniowany w PKCS #1 V1.5 jako PKCS1PaSSing, podczas gdy mechanizmy dodane pniej nosz
nazwy nadane im w PKCS #1. Na pocztek zajmijmy si mechanizmem oryginalnym.
122
java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
import javax.crypto.iipher;
/**
* Przykad RSA z dopenieniem PKCS #1.
*/
public class PKiS1PaddedRSAExample
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] { 0x00, (byte)0xbe, (byte)0xef };
iipher cipher = iipher.getInstance("RSA/NhNE/PKiS1Padding", "Bi");
SecureRandom random = ptils.createFixedRandom();
// tworzenie kluczy
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "Bi");
generator.initialize(256, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("dane wejciowe: " + ptils.toHex(input));
// szyfrowanie
cipher.init(iipher.ENiR PciMhDE, pubKey, random);
byte[] ciphercext = cipher.doFinal(input);
System.out.println("dane zaszyfrowane: " + ptils.toHex(ciphercext));
// deszyfrowanie
123
Jak to dziaa
Rnica polega oczywicie na tym, e wiodce zero jest zachowywane, gdy oryginalna
wiadomo jest najpierw dopeniona, a dopiero wynikowy cig oktetw jest zamieniany na
liczb cakowit. W poprzedniej wersji (bez dopenienia) konwersji podlegaa sama wiadomo.
Innym skutkiem dopenienia jest to, e obliczenia odbywaj si na liczbie cakowitej o dugoci znacznie bliszej dugoci moduu, dziki czemu arytmetyka modulo moe sensownie
dziaa i stosowanie RSA ma sens, nawet w przypadku tak maych wartoci wykadnika
publicznego, jak F0 (czyli warto 3).
Dopenienie OAEP
Optymalne dopenianie dla szyfrowania asymetrycznego, czyli OAEP (ang. Optimal Asymmetric Encryption Padding), jest najmodsz z metod dopenienia okrelonych w PKCS #1.
Metod t pierwotnie opracowali Mihir Bellare i Phillip Rogaway w 1994 roku, a obecnie
jest ona oficjalnie jedn z metod PKCS #1 o penej nazwie RSAES-OAEP. Zalet OAEP
jest moliwo udowodnienia jej bezpieczestwa w ramach modelu losowej wyroczni (ang.
random oracle model), co oznacza, e jeli funkcje skrtu wykorzystane w OAEP s idealne,
to jedynym sposobem zamania wiadomoci zaszyfrowanej RSA z kodowaniem OAEP jest
zamanie samego RSA. Zanim przyjrzymy si faktycznej postaci wiadomoci zakodowanych
metod OAEP, warto dokadniej przeanalizowa znaczenie tego ostatniego stwierdzenia.
W tym kontekcie wyrocznia jest swego rodzaju czarn skrzynk, z ktrej mona uzyska
pewne informacje w tym przypadku deszyfruje ona wiadomoci zaszyfrowane z dopenianiem PKCS #1. Zamy, e chcemy pozna odszyfrowan tre pewnej wiadomoci
i moemy nakoni wyroczni, by zwracaa wyniki deszyfrowania przesyanych jej wiadomoci. W takiej sytuacji moliwy jest atak z milionem wiadomoci (szczegowo przedstawiony w dokumencie RFC 3218, a oryginalnie opisany w artykule Daniela Bleichenbachera
124
gdzie PS jest dopeniajcym cigiem zer, || jest operatorem sklejania, a Mp jest ostateczn
wiadomoci po dopenieniu i zamaskowaniu. Dziaanie funkcji maski polega na wykorzystaniu drugiego argumentu jako ziarna dla generatora pseudolosowych oktetw, ktrego
wynik suy nastpnie do zamaskowania oktetw podanych w ramach pierwszego argumentu. Funkcji generujcej mask nie bd tu szczegowo omawia, gdy jest ona dokadnie opisana w dokumencie PKCS #1, wic podam jedynie jej nazw MGF1. Funkcja ta
pojawi si jeszcze w kilku innych miejscach. Cig parametryzujcy P jest domylnie pusty,
wic na og nie trzeba go podawa (co zobaczymy na przykadach).
125
java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
import javax.crypto.iipher;
/**
* Przykad RSA z dopenieniem OAEP i generowaniem losowego klucza.
*/
public class hAEPPaddedRSAExample
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] { 0x00, (byte)0xbe, (byte)0xef };
iipher cipher = iipher.getInstance("RSA/NhNE/hAEPiithSHA1AndMGF1Padding", "Bi");
SecureRandom random = ptils.createFixedRandom();
// tworzenie kluczy
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "Bi");
generator.initialize(e86, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("dane wejciowe: " + ptils.toHex(input));
// szyfrowanie
cipher.init(iipher.ENiR PciMhDE, pubKey, random);
126
384 bity to oczywicie dugo znacznie mniejsza od dopuszczalnej dugoci klucza, wic
wymagane przez OAEP dodatkowe miejsce nie sprawia w praktyce problemw. Trzeba jedynie pamita, e wykorzystanie OAEP pozostawia w bloku RSA mniej miejsca ni starszy mechanizm dopenienia okrelony w PKCS #1 V1.5.
Jak to dziaa
Od strony praktycznej caa filozofia sprowadza si do podania w nazwie szyfru przekazywanej
do Cipher.getInstance() innego cigu dla metody dopenienia reszt zajmuje si JCE.
Warto w tym miejscu zwrci uwag na format nazwy trybu dopenienia. Nazwa trybu jest
tworzona wedug wzorca OAgPWithFunkcjaSkrtuAnSFunkcjaGenerowaniaMaskiPaSSing, zgodnie
z opisem podanym w dokumentacji w sekcji Java Cryptography Architecture API Specification and Reference. Konwencja ta pozwala tworzy nazwy trybw korzystajcych
z wielu rnych funkcji skrtu i funkcji generowania maski, wic na przykad kodowaniu
OAEP opartym na SHA-256 i domylnej funkcji generowania maski odpowiadaby cig
OAgPWithSHA256AnSMGF1PaSSing. W podobny sposb mona korzysta z dowolnych obsugiwanych przez danego dostawc funkcji skrtu i generowania maski.
Poprzedni przykad powinien dziaa z dowoln wersj JCE z dostawc BC. W wersji JDK 1.5
do JCE dodano jednak kilka nowych klas majcych na celu lepsz obsug OAEP, a poniewa
ta wanie metoda dopenienia jest obecnie zalecana, wic wypada przyjrze im si bliej.
Klasa PSource
Klasa javae.crypte.spec.PSeurce pozwala okreli rdo wartoci parametru P, ktry
w przedstawionych wyej rwnaniach uczestniczy w tworzeniu dopenionego cigu. Jak dotd
obsugiwany jest tylko jeden typ parametru w postaci publicznego rozszerzenia PSeurce
127
klasy PSeurce.PSpecijieS. Konstruktor obiektu parametru przyjmuje tablic bajtw. Domyln dugoci tej tablicy jest zero, wic utworzenie domylnego obiektu PSeurce uywanego do tworzenia dopenienia OAEP i rwnowanego PSeurce.PSpecijieS.DgFAULT wygldaoby tak:
PSource defaultPSource = new PSource.PSpecified(new byte[]);
Najczciej nie ma powodu, by to ustawienie zmienia. Gdyby jednak zasza potrzeba jawnego etykietowania kadego komunikatu kodowanego metod OAEP z tym samym kluczem tego samego serwera za pomoc numeru wiadomoci, to zamiast powyszego kodu
mona by uy:
PSource labeledPSource = new PSource.PSpecified(msgNumber);
gdzie csgNucber jest tablic bajtw odpowiadajc numerowi wiadomoci dla wysyanego
bloku szyfrogramu. Wszystkie wiadomoci o nieprawidowej wartoci parametru P zostan
po odszyfrowaniu odrzucone.
Klasa PSeurce ma tylko jedn metod pobierajc jest to PSeurce.getAlgerithc(), zwracajca nazw algorytmu. Jeszcze jedna metoda pochodzi z klasy rozszerzajcej PSeurce.
PSpecijieS i jest to metoda getValue(), zwracajca po prostu tablic bajtw, ktra posuya
do utworzenia obiektu. Z metod tych na og bezporednio korzysta jedynie dostawca JCE.
Klasa MGF1ParameterSpec
Klasa java.security.spec.MGF1ParaceterSpec reprezentuje standardow funkcj generowania maski MGF1. Konstruktor tej klasy rwnie przyjmuje tylko jeden argument w postaci nazwy funkcji skrtu, ktra ma by wykorzystana w ramach funkcji maski.
Domylnym algorytmem skrtu dla MGF1 jest SHA-1, wic domylna definicja klasy
MGF1ParaceterSpec dla zwykego OAEP wyglda tak:
MGF1ParameterSpec defaultMGF1 = new MGF1ParameterSpec("SHA-1");
Klasa OAEPParameterSpec
Wykorzystanie obiektw dwch poprzednich klas polega na przekazaniu ich obiektowi klasy
javae.crypte.spec.OAgPParaceterSpec. Podobnie jak klasa PSeurce.PSpecijieS, ma ona
warto domyln, dostpn jako OAgPParaceterSpec.DgFAULT. Pena rczna definicja domylnej wartoci OAgPParaceterSpec wygldaaby tak:
hAEPParameterSpec defaulthAEPSpec = new hAEPParameterSpec(
"SHA-1", "MGF1", MGF1ParametersSpec.SHA1, PSource.PSpecified.DEFApLc);
128
129
import java.security.SecureRandom;
import javax.crypto.iipher;
/**
* Opakowywanie klucza RSA za pomoc AES.
*/
public class AESirapRSAExample
{
public static void main(String[] args) throws Exception
{
iipher cipher = iipher.getInstance("AES/EiB/PKiS7Padding", "Bi");
SecureRandom random = new SecureRandom();
KeyPairGenerator fact = KeyPairGenerator.getInstance("RSA", "Bi");
fact.initialize(1024, new SecureRandom());
KeyPair keyPair = fact.generateKeyPair();
Key wrapKey = ptils.createKeyForAES(256, random);
// opakowanie klucza prywatnego RSA
cipher.init(iipher.iRAPiMhDE, wrapKey);
byte[] wrappedKey = cipher.wrap(keyPair.getPrivate());
// odpakowanie klucza prywatnego RSA
cipher.init(iipher.pNiRAPiMhDE, wrapKey);
Key key = cipher.unwrap(wrappedKey, "RSA", iipher.PRIVAcEiKE );
if (keyPair.getPrivate().equals(key))
{
System.out.println("Klucz odzyskany pomylnie.");
}
else
{
System.out.println("hdzyskanie klucza zako czone niepowodzeniem.");
}
pecylnie.
Jak to dziaa
Podobnie jak w przykadzie z rozdziau 2., zajmujcy si opakowywaniem klucza obiekt
Cipher wewntrznie wywouje metod Key.getgnceSeS() obiektu PrivateKey przekazanego
metodzie Cipher.wrap(), uzyskujc w ten sposb zaszyfrowany strumie bajtw zawierajcy
opakowany klucz. Przez analogi, odpakowanie klucza jest kwesti przekazania szyfrowanych bajtw metodzie Cipher.unwrap(). Zalet opakowywania kluczy w przypadku implementowanych sprztowo dostawcw nieujawniajcych klucza na zewntrz jest to, e wewntrzne
wywoanie odpowiednika metody getgnceSeS() i towarzyszce mu operacje szyfrowania
mog si odbywa wycznie w obrbie sprztowego urzdzenia szyfrujcego.
130
Jak ju wiemy, korzystanie z RSA wie si z ograniczeniem rozmiaru szyfrowanej wiadomoci do dugoci jednego bloku. Dostpne miejsce czsto kurczy si jeszcze bardziej ze
wzgldu na obecno dopenienia. Pozostaje wic pytanie: jak wykorzysta RSA do bezpiecznego szyfrowania wikszych iloci danych? Teoretycznie mona by po prostu podzieli
dane na bloki, ale przy wikszych ilociach blokw byoby to niezwykle powolne. Co wicej,
jeli napastnik posiada pewne informacje na temat szyfrowanych danych, to dzielenie tych
danych na bloki moe dodatkowo zagrozi bezpieczestwu klucza prywatnego odbiorcy.
Na szczcie rozwizanie tego problemu jest bardzo proste.
java.io.ByteArrayhutputStream;
java.io.IhException;
java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
import javax.crypto.iipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Przykad szyfrowania RSA z dopenieniem OAEP i generowaniem losowych kluczy.
*/
public class RSAKeyExchangeExample
{
private static byte[] packKeyAndIv(
Key key,
IvParameterSpec ivSpec)
throws IhException
{
ByteArrayhutputStream bhut = new ByteArrayhutputStream();
bhut.write(ivSpec.getIV());
return bhut.toByteArray();
131
132
Jak wida, z wykorzystaniem tej techniki wie si pewien narzut, ale z drugiej strony pozwala ona przezwyciy ograniczenia rozmiaru wiadomoci i w peni wykorzysta wiksz szybko szyfrowania przez algorytmy symetryczne.
Jak to dziaa
Dziaanie przykadu opiera si na opakowaniu klucza symetrycznego za pomoc obiektu
reprezentujcego szyfr asymetryczny eCipher, a nastpnie zaszyfrowaniu danych tym kluczem symetrycznym (obiekt sCipher). Na rysunku 4.1 wida, e do odbiorcy przesyana
jest zarwno zaszyfrowana wiadomo, jak i zaszyfrowany klucz. Odbiorca odtwarza tajny
klucz symetryczny, deszyfrujc go swoim kluczem prywatnym, po czym z jego pomoc deszyfruje wiadomo.
Rysunek 4.1.
133
Algorytmy uzgadniania klucza nie pozwalaj szyfrowa danych w taki sposb, jak na przykad RSA. Dostarczaj one za to mechanizmw uzgodnienia przez komunikujce si strony
(najczciej dwie, ale moe by ich wicej) wsplnego klucza tajnego, ktry posuy nastpnie do szyfrowania przesyanych informacji.
Algorytm Diffiego-Hellmana
Nazwa algorytmu Diffiego-Hellmana pochodzi od nazwisk jego twrcw, Whitfielda Diffiego i Martina Hellmana, ktrzy wynaleli go w roku 1976. Odtajnione niedawno dokumenty brytyjskiego GCHQ wskazuj na to, e ten sam algorytm opracowa dwa lata wczeniej Malcolm Williamson. Podobnie jak RSA, algorytm ten korzysta z arytmetyki modulo,
jednak jego bezpieczestwo opiera si na trudnoci obliczania logarytmw dyskretnych
oraz rozwizania problemu Diffiego-Hellmana. Problemy te s ze sob powizane i oba
sprowadzaj si do trudnoci wyznaczenia dla danego y liczby x takiej, e Gx = y, gdzie G
jest generatorem liczb z ciaa skoczonego nad P (du liczb pierwsz). Jak si okazuje,
zadanie to jest na tyle trudne (przynajmniej przy obecnym stanie wiedzy), e mona je
uzna za przeszkod nie do przejcia.
Zastosowany proces nie nadaje si do szyfrowania, ale okrela zestaw oblicze pozwalajcych dwm stronom wyliczy ten sam klucz tajny. Porwnanie rysunkw 4.1 i 4.2 wyranie wykazuje rnice midzy uzgadnianiem klucza a szyfrowaniem. Sam algorytm jest
do prosty. Dla danej duej liczby pierwszej P i generatora G dla grupy nad P strona A
wybiera liczb prywatn U, a strona B wybiera liczb prywatn V. Wtedy:
1. A przesya B warto GU mod P (klucz publiczny A).
2. B przesya A warto GV mod P (klucz publiczny B).
3. Odbywa si wymiana danych szyfrowanych kluczem sesji opartym na GUV mod P.
Rysunek 4.2.
134
java.math.BigInteger;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.MessageDigest;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.DHParameterSpec;
/**
* Dwustronne uzgadnianie klucza metod Diffiego-Hellmana
*/
public class BasicDHExample
{
private static BigInteger g512 = new BigInteger(
"15ed5d6172adb4e045b68ae8e1de1070b61e7005686d29ded7ea7"
+ "749199681ee5b212c9b96bfdcfa5b20cd5eefd2044895d609cf9b"
+ "410b7a0f12ca1cb9a428cc", 16);
private static BigInteger p512 = new BigInteger(
"9494fec095feb85ee286542be8e6fc81a5dd0a0e49b4c2e9dde87"
+ "44d488cf8ee1db8bcb7deeb41abb9e5aeecca9144b1cefee2c94b"
+ "f057ebf047aeaca98cdfeb", 16);
public static void main(String[] args) throws Exception
{
DHParameterSpec dhParams = new DHParameterSpec(p512, g512);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH", "Bi");
keyGen.initialize(dhParams, ptils.createFixedRandom());
// przygotowania
KeyAgreement aKeyAgree = KeyAgreement.getInstance("DH", "Bi");
KeyPair aPair = keyGen.generateKeyPair();
KeyAgreement bKeyAgree = KeyAgreement.getInstance("DH", "Bi");
KeyPair bPair = keyGen.generateKeyPair();
// uzgadnianie dwustronne
aKeyAgree.init(aPair.getPrivate());
bKeyAgree.init(bPair.getPrivate());
aKeyAgree.doPhase(bPair.getPublic(), true);
bKeyAgree.doPhase(aPair.getPublic(), true);
// generowanie bajtw klucza
MessageDigest hash = MessageDigest.getInstance("SHA1", "Bi");
135
System.out.println(ptils.toHex(aShared));
System.out.println(ptils.toHex(bShared));
Wykonanie programu powinno wykaza, e kada ze stron wygenerowaa taki sam klucz tajny:
98f2669e0458195dece06ee99f0be55598eb096b
98f2669e0458195dece06ee99f0be55598eb096b
W kwestii generowania par kluczy program ten nie rni si specjalnie od poprzednich,
jednak dokadniejsze ogldziny pozwalaj doceni konsekwencje korzystania z metody
Diffiego-Hellmana i algorytmw uzgadniania klucza w ogle.
Pierwsz konsekwencj wyranie ilustruje fakt, e podanie staej zamiast wartoci losowej
pozwala uzyska przewidywalny wynik ostateczny. Oznacza to, e ponowne wykorzystanie
kluczy uytych do uzgodnienia klucza da w wyniku t sam tajn warto, a wic ten sam
tajny klucz symetryczny. Std te wynika oglna zasada dotyczca dugowiecznoci kluczy
uywanych z klas KeyAgreecent:
Czas ycia kluczy uywanych w ramach procedury uzgadniania klucza nie powinien
przekracza czasu ycia klucza symetrycznego, ktry jest z ich pomoc generowany.
Klucze uywane z klas KeyAgreecent powinny z definicji by tymczasowe.
Druga, mniej oczywista konsekwencja jest taka, e uzgadnianie klucza rzadko odbywa si
w bezpiecznych czterech cianach pliku, a czciej ma miejsce w okolicznociach, gdzie
napastnik mgby podstawi wasne etapy procesu uzgadniania. Jest to tak zwany atak porednika1 (ang. man-in-the-middle), przedstawiony na rysunku 4.3. Kod poprzedniego programu nie uwzgldnia adnego sposobu weryfikacji pochodzenia klucza nadesanego przez
drug stron.
Klasy KeyAgreecent naley uywa wycznie w poczeniu z mechanizmem uwierzytelniania pozwalajcym zapobiec atakowi porednika.
Klasa DHParameterSpec
Klasa javae.crypte.spec.DHParaceterSpec pozwala tworzy obiekty wartoci zawierajce
dwa lub trzy parametry. Jak wida z kodu, standardowy konstruktor tej klasy przyjmuje
dwa argumenty w postaci wartoci P i G, czyli odpowiednio liczby pierwszej i generatora
dla grupy odpowiadajcej tej liczbie.
1
136
Rysunek 4.3.
Istnieje te drugi konstruktor, pozwalajcy przyspieszy proces wymiany klucza. Konstruktor ten przyjmuje dugo wartoci prywatnej w bitach i ogranicza generowan warto do podanego rozmiaru. Warto prywatna tworzona domylnie w ramach procesu generowania klucza jest krtsza od P, ale o nie wicej ni osiem bitw. Jeli P ma 1024 bity,
to wyliczanie klucza publicznego dla potrzeb procesu uzgadniania klucza bdzie (czsto
niepotrzebnie) do powolne. Moe si okaza, e w konkretnym zastosowaniu wystarczy
warto prywatna o dugoci nieprzekraczajcej 512 bitw. Przyjmujc, e zmienne p1p24
i g1p24 odpowiadaj 1024-bitowej liczbie pierwszej i jej generatorowi, mona stworzy
obiekt DHParaceterSpec postaci nastpujcej:
DHParameterSpec dhSpec = new DHParameterSpec(p1024, g1024, 512);
Z kolei konstruktor DHPublicKeySpec przyjmuje warto publiczn Y i uyte do jej stworzenia wartoci P i G. W kodzie wyglda to po prostu tak:
DHPublicKeySpec dhPublicSpec = new DHPublicKeySpec(y, p, g);
137
Podobnie jak w przypadku innych klas specyfikacji, obiekty DHPrivateKeySpec i DHPublicKeySpec s prostymi obiektami wartoci, wic jedynymi ich metodami s metody get() pobierajce poszczeglne wartoci.
138
java.math.BigInteger;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.MessageDigest;
java.security.spec.EiFieldFp;
java.security.spec.EiParameterSpec;
java.security.spec.EiPoint;
java.security.spec.Ellipticiurve;
import javax.crypto.KeyAgreement;
/**
* Diffie-Hellman w kryptosystemie na krzywej eliptycznej.
*/
public class BasicEiDHExample
{
public static void main(String[] args) throws Exception
{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EiDH", "Bi");
Ellipticiurve curve = new Ellipticiurve(
new EiFieldFp(new
BigInteger("fffffffffffffffffffffffffffffffeffffffffffffffff", 16)),
new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
new BigInteger("64210519e59c80e70fa7e9ab7224e049feb8deecc146b9b1", 16));
EiParameterSpec ecSpec = new EiParameterSpec(
curve,
new EiPoint(
new BigInteger("188da80eb0e090f67cbf20eb4ea18800f4ff0afd82ff1012", 16),
new BigInteger("f8e6d46a00e725879cefee1294dbe2298c06885ee186b7ee", 16)),
139
System.out.println(ptils.toHex(aShared));
System.out.println(ptils.toHex(bShared));
Jak to dziaa
Rzut oka na kod pokazuje wyranie, e poza zmian parametrw pocztkowych dla generatora klucza i podaniem w ramach argumentu metody KeyAgreecent.getInstance() cigu
gCDH zamiast DH cao nie rni si niczym od kodu dla tradycyjnej metody DiffiegoHellmana. Stojce za caym procesem operacje matematyczne s wprawdzie zupenie inne,
ale na poziomie warstwy abstrakcji JCE korzystanie z obu metod wyglda w zasadzie tak samo.
A gdzie rnice? Przyjrzyj si bliej procesowi tworzenia parametrw pozwoli to dokadniej
zrozumie dziaanie caej metody i pokae zwizek midzy obiektami w kodzie a parametrami omwionymi w czci teoretycznej.
140
ciao, nad ktrym operuje dowolna z uywanych krzywych. Klasa ta ma wicej moliwoci
konstrukcji od gCFielSFp, gdy pozwala stworzy F2m krzywych zarwno nad baz normaln,
jak i baz wielomianow. Z tego te wzgldu niektre metody get() tej klasy mog zwraca NULL, w zalenoci od uytej bazy. Dogbne zrozumienie tej klasy nie obejdzie si bez
samodzielnego zgbienia teorii krzywych eliptycznych.
Na szczcie od strony czysto praktycznej wystarczy pamita, e zarwno klasa gCFiel-
Po ustaleniu ciaa, nad ktrym krzywa bdzie operowa, trzeba zdefiniowa sam krzyw.
Klasa EllipticCurve
Klasa java.security.spec.gllipticCurve peni rol pojemnika dla wartoci opisujcych
krzyw eliptyczn. Jak mona si spodziewa, posiada ona komplet metod get() pozwalajcych pobiera obiekty odpowiadajce poszczeglnym skadowym, jednak najciekawszy
jest w jej przypadku przebieg konstrukcji obiektu.
Zrozumienie zasad tworzenia obiektu gllipticCurve znacznie uatwi znajomo typowego
rwnania dla uywanej w tym przypadku krzywej eliptycznej (z drobnymi wariacjami):
y2 = x3 + ax2 + b
gdzie wartoci x i y nale do danego ciaa skoczonego, a wartoci a i b s stae.
Spjrzmy raz jeszcze na kod tworzcy krzyw wykorzystan w poprzednim przykadzie.
Jest to wywoanie podstawowego konstruktora klasy gllipticCurve:
Ellipticiurve curve = new Ellipticiurve(
new EiFieldFp(new BigInteger(
"fffffffffffffffffffffffffffffffeffffffffffffffff", 16)),
new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16),
new BigInteger("64210519e59c80e70fa7e9ab7224e049feb8deecc146b9b1", 16));
Porwnujc kod ze wzorem krzywej, widzimy, e pierwszym argumentem jest samo pole,
w tym przypadku oparte na liczbie pierwszej reprezentowanej przez obiekt BigInteger przekazany do konstruktora obiektu gCFielSFp. Pozostae dwa argumenty to (zgodnie z rwnaniem) wartoci staych a i b.
Drugi konstruktor krzywej eliptycznej dodatkowo przyjmuje warto inicjalizujc.
Krzywa zostaa ju okrelona, wic do kompletu informacji pozostaje jeszcze ustali punkt
na krzywej, na podstawie ktrego wyliczane bd klucze publiczne.
141
Klasa ECPoint
Klasa java.security.spec.gCPeint stanowi pojemnik dla wsprzdnych punktu na krzywej eliptycznej (czyli rwnie wsprzdnych w przestrzeni operowania krzywej).
Klasa ta nie jest specjalnie ciekawa. Jest ona tworzona na podstawie wsprzdnych (X, Y)
punktu (dwch obiektw BigInteger), natomiast metody gCPeint.getAjjineD() i gCPeint.
getAjjineY() zwracaj wartoci poszczeglnych wsprzdnych. Dobrze tylko pamita,
e klasa ta zawiera statyczny obiekt reprezentujcy punkt w nieskoczonoci gCPeint.POINT_
INFINITY, zwracajcy dla obu metod warto NULL.
Klasa ECParameterSpec
Klasa java.security.spec.gCParaceterSpec pozwala poczy wszystkie wymienione wyej elementy w cao i wykorzysta je w kryptosystemie opartym na krzywej eliptycznej.
Klasa ma jeden konstruktor, ktrego argumentami s: krzywa, punkt bazowy, rzd punktu
bazowego oraz dopenienie algebraiczne. Wiemy ju, jak wyglda obiekt krzywej i czym
jest punkt bazowy (czyli generator). Pozostaje jeszcze przybliy ostatnie dwa parametry.
Tym razem przyda si mie przed oczami definicj klasy oto odpowiedni fragment kodu
z ostatniego przykadu:
EiParameterSpec ecSpec = new EiParameterSpec(
curve,
new EiPoint(
new BigInteger("188da80eb0e090f67cbf20eb4ea18800f4ff0afd82ff1012", 16),
new BigInteger("f8e6d46a00e725879cefee1294dbe2298c06885ee186b7ee", 16)),
new BigInteger("ffffffffffffffffffffffff99def8e6146bc9b1b4d228e1", 16),
1);
Rzd to ostatni argument typu BigInteger przekazywany do konstruktora. Wraz z dopenieniem algebraicznym (w tym przypadku rwnym 1) wyraa on pewne waciwoci
punktu bazowego wzgldem krzywej. Rzd jest du liczb pierwsz, ktra po pomnoeniu
przez dopenienie daje liczb punktw dostpnych na krzywej. Ze wzgldw wydajnoci
warto dopenienia algebraicznego powinna by moliwie najmniejsza, std te najczciej spotyka si wartoci od 1 do 4.
Poczenie wszystkich tych wartoci pozwala wyliczy warto prywatn i wybra punkt na
krzywej, ktrego parametry mona nastpnie udostpni stronom, z ktrymi chcemy si
komunikowa czy to w ramach uzgadniania klucza, czy te w celu weryfikacji podpisw
cyfrowych.
Klasa ECGenParameterSpec
Wspomniaem wczeniej, e istnieje wiele predefiniowanych (czyli nazwanych) krzywych
eliptycznych wraz ze skojarzonymi z nimi punktami. rdem informacji o takich krzywych s
przede wszystkim dokumenty X9.62 i FIPS PUB-186-2. W zalenoci od krzywych obsugiwanych przez konkretnego dostawc mona korzysta z krzywych nazwanych i parametrw
okrelonych na nich punktw za pomoc klasy java.security.spec.gCGenParaceterSpec.
142
Sprbuj wprowadzi tak zmian w programie wynik powinien by dokadnie taki sam.
Wykaz nazwanych krzywych eliptycznych obsugiwanych przez dostawc Bouncy Castle
przedstawia dodatek B. Listy krzywych obsugiwanych przez innych dostawcw s na og
podawane w ich dokumentacji.
143
Teraz trzeba inicjalizowa utworzony obiekt KeyAgreecent tak samo, jak pozostae dwa
obiekty:
cKeyAgree.init(cPair.getPrivate());
W tym miejscu nastpuje zmiana w dotychczasowym algorytmie postpowania. Przed wywoaniem metod SePhase() obiektw KeyAgreecent z wartoci true parametru lastPhase
konieczne jest wprowadzenie dodatkowego etapu, w ramach ktrego metoda ta bdzie wywoywana z wartoci jalse dla lastPhase. Wynikiem wykonania tego etapu s trzy klucze
porednie, ktre bd uywane w nastpnej fazie. Poniszy kod wymaga dodatkowo importowania interfejsu java.security.Key:
Key ac = aKeyAgree.doPhase(cPair.getPublic(), false);
Key ba = bKeyAgree.doPhase(aPair.getPublic(), false);
Key cb = cKeyAgree.doPhase(bPair.getPublic(), false);
Wygenerowane w ten sposb klucze porednie s nastpnie uywane w wywoaniach odpowiadajcych ostatniej fazie negocjacji:
aKeyAgree.doPhase(cb, true);
bKeyAgree.doPhase(ac, true);
cKeyAgree.doPhase(ba, true);
Jeli program nadal korzysta ze staej wartoci losowej z klasy Utils, to jego uruchomienie
powinno da nastpujcy wynik:
dd602f60aee82db7b4e5dd71b0674f5ab64bbb7b
dd602f60aee82db7b4e5dd71b0674f5ab64bbb7b
dd602f60aee82db7b4e5dd71b0674f5ab64bbb7b
Jak to dziaa
Jak wida, pomimo do podeszego wieku algorytm Diffiego-Hellmana nadal jest przydatny. Sprbuj sobie wyobrazi trjstronne uzgadnianie klucza za pomoc algorytmu RSA.
Przy podejciu naiwnym wymagaoby to wymiany kluczy z kad z pozostaych stron,
przez co uzgodnienie klucza dla trjstronnej rozmowy mogoby wymaga nawet szeciokrotnego przesania klucza RSA. Kada ze stron miaaby wtedy dodatkowy problem w postaci koniecznoci ledzenia, ktrego klucza uywa dla ktrego rozmwcy zaradzenie
tej trudnoci wymagaoby wprowadzenia jeszcze jednej rundy negocjacji, by wszystkie
strony mogy uzgodni wsplny klucz. Uzgadnianie klucza metod Diffiego-Hellmana jest
wic nadal najprostszym sposobem uzgodnienia wsplnego klucza dla kilku stron, cho
naley cay czas pamita o problemie uwierzytelnienia klucza.
144
java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
import javax.crypto.iipher;
/**
* Przykad uycia algorytmu El Gamala z losowym generowaniem klucza.
*/
public class RandomKeyElGamalExample
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] { (byte)0xbe, (byte)0xef };
iipher cipher = iipher.getInstance("ElGamal/None/NoPadding", "Bi");
KeyPairGenerator generator = KeyPairGenerator.getInstance("ElGamal", "Bi");
SecureRandom random = ptils.createFixedRandom();
// tworzenie kluczy
generator.initialize(256, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
2
Nazwa algorytmu pochodzi od nazwiska jego twrcy, dr. Tahera Elgamala. W literaturze wystpuje
kilka pisowni nazwy algorytmu, w tym Elgamal, ElGamal i (stosowana w tej ksice) El Gamal
przyp. tum.
145
Jak to dziaa
Nie ma powodw, by specjalnie si rozwodzi nad samym sposobem korzystania z algorytmu. W kwestii szyfrowania danych kluczem publicznym El Gamal nie rni si zbytnio
od RSA szyfrowanie bazuje na operacjach arytmetycznych, wic dla poprawnego obsuenia danych zaczynajcych si zerami trzeba korzysta z dopenienia. Najwaniejsz rnic w porwnaniu z RSA jest to, e szyfrogram jest dwa razy duszy od klucza (w RSA
mia on dugo klucza). Praktyczne znaczenie tego faktu zaley od ogranicze konkretnej
aplikacji, ale duszy szyfrogram jest jednym z powodw, dla ktrych El Gamal nie jest
oglnie algorytmem preferowanym.
Najwikszym problemem (przynajmniej w przypadku naszego przykadowego programu)
jest mizerna szybko generowania klucza. Jak si wczeniej przekonalimy, wygenerowanie pary kluczy dla algorytmu El Gamala wymaga takich samych parametrw, jak algorytm
Diffiego-Hellmana, a wyliczenie tych wartoci od zera jest bardzo kosztowne. Obiekt KeyPairGenerater zainicjalizowany wycznie dugoci klucza musi najpierw wygenerowa
wartoci P i G, a dopiero potem moe wygenerowa par kluczy. W praktyce jest to koszt
ponoszony jednokrotnie (przynajmniej w przypadku dostawcy Bouncy Castle) generowanie kolejnych par kluczy jest ju znacznie szybsze, gdy mona uywa gotowych wartoci P i G wyliczonych dla pierwszej pary. Nie powinno dziwi, e generator par kluczy
dla metody Diffiego-Hellmana zachowuje si podobnie.
Najwygodniej byoby wic wstpnie wygenerowa obiekt DHParaceterSpec, za pomoc
ktrego mona by poda gotowe parametry (identycznie jak w przypadku algorytmu Diffiego-Hellmana). Na szczcie jest to moliwe wystarczy wygenerowa niezbdne parametry, korzystajc z obiektu klasy AlgerithcParaceterGenerater.
146
Klasa AlgorithmParameterGenerator
Podobnie do wielu innych klas w JCA, instancje klasy AlgerithcParaceterGenerater s
tworzone za porednictwem metody fabrykujcej getInstance() i podobnie jak one przestrzegaj oglnych regu priorytetu dostawcw w przypadku znalezienia wicej ni jednego
dostawcy implementujcego dany algorytm. Spord metod klasy AlgerithcParaceterGenerater najbardziej bdzie nas interesowa metoda init() (dostpna w czterech odmianach) oraz metoda generateParaceters() suca do pobrania wygenerowanego obiektu
AlgerithcParaceters.
AlgorithmParameterGenerator.init()
Metoda init() jest dostpna w czterech wersjach. Pierwsza z nich przyjmuje tylko wielko generatora, druga wielko generatora i rdo danych losowych, a pozostae dwie
przyjmuj obiekty klas implementujcych AlgerithcParaceterSpec, co przydaje si w sytuacjach wymagajcych przekazania innych parametrw ni tylko rozmiar generatora. Wybr
metody zaley tu od sytuacji. Dla algorytmw Diffiego-Hellmana czy El Gamala wystarczy
sam rozmiar, by wygenerowa podstawowe parametry w postaci liczby pierwszej P i generatora G. Jeli jednak zajdzie potrzeba utworzenia obiektu parametrw pozwalajcego
ograniczy dugo wartoci prywatnej w metodzie Diffiego-Hellmana, to sama wielko
generatora nie wystarczy dodatkowe informacje trzeba bdzie przekaza w ramach
obiektu klas implementujcych AlgerithcParaceterSpec.
Kolejne punkty przedstawiaj przykady obu zastosowa.
AlgorithmParameterGenerator.generateParameters()
Metoda ta zwraca obiekt klasy AlgerithcParaceters zawierajcy generowane parametry.
Szczegowy opis tej klasy przedstawiem ju w rozdziale 2., wic nie bd si tu nad nim
rozwodzi. Wspomn tylko, e (jak zapewne ju si domylasz) obiekty AlgerithcParaceters
przydaj si dosownie wszdzie.
Starczy tej teorii pora zapozna si z kodem.
java.security.AlgorithmParameterGenerator;
java.security.AlgorithmParameters;
java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.spec.AlgorithmParameterSpec;
147
import javax.crypto.iipher;
import javax.crypto.spec.DHParameterSpec;
/**
* Uycie algorytmu El Gamal z generowaniem losowego klucza i obiektem klasy AlgorithmParameters.
*/
public class AlgorithmParameterExample
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] { (byte)0xbe, (byte)0xef };
iipher cipher = iipher.getInstance("ElGamal/None/NoPadding", "Bi");
SecureRandom random = ptils.createFixedRandom();
// tworzenie parametrw
AlgorithmParameterGenerator a =
AlgorithmParameterGenerator.getInstance("ElGamal", "Bi");
a.init(256, random);
AlgorithmParameters params = a.generateParameters();
AlgorithmParameterSpec dhSpec = params.getParameterSpec(DHParameterSpec.class);
// tworzenie kluczy
KeyPairGenerator generator = KeyPairGenerator.getInstance("ElGamal", "Bi");
generator.initialize(dhSpec, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("dane wejciowe: " + ptils.toHex(input));
// szyfrowanie
cipher.init(iipher.ENiR PciMhDE, pubKey, random);
byte[] ciphercext = cipher.doFinal(input);
System.out.println("dane zaszyfrowane: " + ptils.toHex(ciphercext));
// deszyfrowanie
cipher.init(iipher.DEiR PciMhDE, privKey);
byte[] plaincext = cipher.doFinal(ciphercext);
148
Jak to dziaa
W tym przykadzie parametry odpowiadajce wartociom P i G s jawnie generowane
i przekazywane do obiektu generatora pary kluczy, podczas gdy w poprzednim programie
byy one tworzone automatycznie na podstawie zadanej dugoci klucza. Zwalnia to generator par kluczy z obowizku samodzielnego generowania parametrw.
Rnic w szybkoci dziaania trudno oceni, po prostu uruchamiajc oba przykady, ale
wystarczy wok operacji generowania klucza w obu programach doda kod mierzcy czas
wykonania, by przekona si, e czas wykonywania metody KeyPairGenerater.generateKeyPair() jest znacznie krtszy dla drugiego programu.
Wczeniej wspominaem, e klasa AlgerithcParaceterGenerater moe te przyjmowa
obiekt implementujcy AlgerithcParaceterSpec jako argument swej metody init(). Tak
si skada, e istnieje implementujca ten interfejs klasa przeznaczona wanie do generowania parametrw dla algorytmw w rodzaju Diffiego-Hellmana jest to klasa DHGenParaceterSpec.
Klasa DHGenParameterSpec
Wiemy ju, e algorytm Diffiego-Hellmana mona przyspieszy, ograniczajc dugo
wartoci prywatnej powizanej z kluczem publicznym. Wiemy te, e mona stworzy
obiekt DHParaceterSpec, ktry naoy takie ograniczenie na dugo wartoci prywatnych
generowanych podczas uywania go z odpowiednim obiektem KeyPairGenerater. Dobrze
byoby mie moliwo uwzgldnienia tego ograniczenia rwnie w generowanych parametrach, std te JCE udostpnia klas pozwalajc konfigurowa obiekt AlgerithcParaceterSpec tworzony dla potrzeb algorytmu Diffiego-Hellmana jest to klasa javae.crypte.
spec.DHGenParaceterSpec. Zamiast okrela wycznie dugo liczby pierwszej P, jak ma
to miejsce w wierszu:
apg.init(256, random);
Uyteczno podpisw cyfrowych polega na tym, e podpis jest tworzony na podstawie tajnych danych znanych wycznie jego autorowi, ale moe zosta sprawdzony za pomoc informacji publicznie udostpnianych przez podpisujcego. Podpis cyfrowy nie tylko powiadcza
149
autentyczno wiadomoci, ale rwnie czyni j niezaprzeczaln. Jeli konkretny uytkownik podpisze wiadomo, a nastpnie zaprzeczy, e j napisa, to istniej tylko dwie moliwoci:
albo tajne dane zostay wykradzione, albo podpisujcy mija si z prawd.
Wszystkie algorytmy podpisw cyfrowych bazuj na jednokierunkowych funkcjach skrtu,
co wynika z ograniczonej dugoci wiadomoci, jakie mog przetwarza algorytmy asymetryczne. Ma to jedn bardzo istotn konsekwencj: maksymalna ilo danych, jak mona
bezpiecznie podpisa danym algorytmem, jest ograniczona moliwociami uywanej funkcji skrtu. Dugo klucza wyznacza jedynie bezpieczestwo samego skrtu. Jeli zostanie
podpisana wiadomo o zbyt duej dugoci dla danego algorytmu skrtu, to zwikszenie
dugoci klucza nic nie pomoe.
Ilo danych, jakie mona bezpiecznie podpisa dan metod, jest ograniczona maksymaln dugoci danych wejciowych dla uywanego algorytmu skrtu.
Klasa Signature
Obiekty udostpniajce operacje klasy java.security.Signature s tworzone za pomoc
metody fabrykujcej getInstance(), podobnie jak wiele innych klas w JCA. Metoda ta
przestrzega te zwykych priorytetw okrelajcych zasady wyboru dostawcy w przypadku
braku dania konkretnego dostawcy. W przeciwiestwie do klasy Cipher tryby dziaania
klasy Signature nie s okrelane przez skadowe statyczne, lecz przez wywoanie jednej
z dwch metod inicjalizacyjnych. Wywoanie Signature.initSign() przeczy obiekt
Signature w tryb tworzenia podpisu, natomiast Signature.initVerijy() spowoduje przejcie w tryb sprawdzania.
150
151
Rysunek 4.4.
Zwyky DSA
Podobnie jak w przypadku algorytmu Diffiego-Hellmana, bezpieczestwo tradycyjnego algorytmu DSA opiera si na trudnoci obliczania logarytmw dyskretnych. Do skorzystania
z mechanizmu DSA potrzebne s nastpujce dane:
n
152
Jeli V jest rwne R, to podpis jest akceptowany. W przeciwnym razie jest on odrzucany.
Nie bd specjalnie wnika w matematyczn stron caej procedury, gdy szczegowy jej
opis mona znale w ksikach przedstawionych w dodatku D, na przykad w Handbook
of Applied Cryptography Menezesa, van Oorschota i Vanstonea. W tym miejscu bardziej
interesuje nas zwizek midzy tymi obliczeniami a praktyczn stron korzystania z klasy
Signature w Javie.
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.Signature;
153
Jak to dziaa
Podobnie jak w kadym algorytmie asymetrycznym, pierwszym krokiem jest stworzenie
pary kluczy dla uywanego algorytmu. Nastpnie trzeba inicjalizowa obiekt signature
tak, by korzysta z klucza prywatnego:
signature.initSign(keyPair.getPrivate(), ptils.createFixedRandom());
Jak wspominaem w teoretycznym opisie algorytmu DSA, do wygenerowania podpisu potrzebna jest liczba losowa. W tym przykadzie wykorzystana zostaa wersja metody initSign()
przyjmujca poza kluczem prywatnym rwnie rdo danych losowych. Gdyby rdo to
nie zostao jawnie podane, dostawca utworzyby rdo domylne.
Kolejnym etapem jest wprowadzenie podpisywanych danych do obiektu signature poprzez
wywoanie metody upSate(). Wywoanie metody sign() powoduje obliczenie podpisu
i zwrcenie zawierajcej go tablicy bajtw.
W drugiej czci przykadu wykonywane jest sprawdzenie podpisu utworzonego w czci
pierwszej. W tym celu dla obiektu podpisu wywoywana jest metoda initVerijy() z kluczem publicznym podpisujcego, po czym tre podpisywanej wiadomoci jest przekazywana do obiektu jako argument metody upSate(). Samo sprawdzenie polega na wywoaniu
metody verijy() z argumentem w postaci tablicy bajtowej zawierajcej otrzymany podpis.
Jeli podpis pasuje do danych (a w tym przykadzie tak jest), to metoda verijy() zwrci
warto true w przeciwnym przypadku zwrcona zostanie warto jalse.
Rzut oka na uycie obiektu klasy KeyPairGenerater pokazuje brak omawianych wczeniej
parametrw DSA. S one przekazywane niejawnie w analogiczny sposb, jak w pierwszym
przykadzie z algorytmem El Gamala z braku jawnie przekazanych wartoci obiekt KeyPairGenerater generuje parametry samodzielnie. Podobnie jak w przypadku algorytmw
Diffiego-Hellmana i El Gamala, moliwe jest wstpne wyliczenie niezbdnych parametrw. Do ich przenoszenia suy klasa DSAParaceterSpec.
Klasa DSAParameterSpec
Obiekty klasy java.security.spec.DSAParaceterSpec peni rol pojemnikw dla omwionych wczeniej parametrw algorytmu DSA. Klasa ta ma tylko jeden konstruktor, przyjmujcy jako parametry uywane do generowania klucza wartoci P, Q i G, oraz trzy metody
get() pobierajce wartoci poszczeglnych parametrw.
154
Podobnie jak w przypadku innych obiektw wartoci przenoszcych dane klucza, jedynymi
metodami klas DSAPrivateKeySpec i DSAPublicKeySpec s metody get() pobierajce poszczeglne parametry skadowe.
155
DSAKey deklaruje tylko jedn metod o nazwie getParacs(), zwracajc obiekt klasy DSAParaceterSpec odpowiadajcy parametrom kluczy: publicznego i prywatnego.
DSAPrivateKey te deklaruje tylko jedn metod jest ni getD(), zwracajca tajn war-
to prywatn.
Wreszcie DSAPublicKey rwnie posiada pojedyncz metod o nazwie getY(). Zwraca ona
warto GX mod P, gdzie X jest wartoci zwracan przez metod getD() danego klucza
prywatnego, a wartoci G i P s zawarte w opisujcym klucz obiekcie DSAParaceterSpec.
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.Signature;
java.security.spec.EiGenParameterSpec;
/**
* Prosty przykad tworzenia i weryfikacji podpisu algorytmem ECDSA.
*/
public class BasicEiDSAExample
{
public static void main(String[] args) throws Exception
{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EiDSA", "Bi");
EiGenParameterSpec ecSpec = new EiGenParameterSpec("prime192v1");
keyGen.initialize(ecSpec, new SecureRandom());
156
if (signature.verify(sigBytes))
{
System.out.println("ieryfikacja podpisu zako czona powodzeniem.");
}
else
{
System.out.println("Bd weryfikacji podpisu.");
}
Jak to dziaa
Jak ju wspominaem, ca brudn robot ukrywa tu warstwa abstrakcji JCA. Dziki temu
wystarcz kosmetyczne zmiany w kodzie, by korzysta on z zupenie innej implementacji.
Warto zwrci uwag, e powyszy przykad korzysta z omwionej wczeniej klasy gCGenParaceterSpec, wic bardzo atwo mona przej na zupenie inn krzyw i klucz, po prostu zmieniajc cig przekazywany do konstruktora obiektu gCGenParaceterSpec. Na przykad przejcie z klucza 192-bitowego na 239-bitowy wymagaoby zamiany tego wiersza:
EiGenParameterSpec ecSpec = new EiGenParameterSpec("prime192v1");
na ten:
EiGenParameterSpec ecSpec = new EiGenParameterSpec("prime2e9v1");
157
ilustruje rysunek 4.5. Skuteczno tej metody wynika std, e podpis dajcy si odszyfrowa danym kluczem publicznym mg by utworzony wycznie za pomoc odpowiadajcego mu klucza prywatnego.
Rysunek 4.5.
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.Signature;
158
if (signature.verify(sigBytes))
{
System.out.println("ieryfikacja podpisu zako czona powodzeniem.");
}
else
{
System.out.println("Bd weryfikacji podpisu.");
}
Szeniec.
Jak to dziaa
Podobnie jak w poprzednim przykadzie, wystarczy tylko zmieni posta wywoania metody
Signature.getInstance() i dostarczy odpowiedni par kluczy, a caa reszta dzieje si w zasadzie sama.
W kodzie mona zauway co, czego nie byo w poprzednich przykadach z klas Signature, a mianowicie wyran struktur nazwy algorytmu podpisu, opisan zreszt w dokumentacji JCA wraz z innymi konwencjami nazewniczymi. Nazwa ma format SkrtwithSzyfr, tak
wic cig okrelajcy podpis RSA ze skrtem SHA-224 to SHA224withRSA, ze skrtem SHA-256
SHA256withRSA i tak dalej.
Podpisy PSS
Metoda RSASSA-PSS, lub krcej PSS, peni podobn funkcj do dopenienia OAEP i jest
opisana w wersji drugiej standardu PKCS #1. Podobnie jak w przypadku zwykych podpisw,
wymaga ona uycia funkcji skrtu H() do obliczenia skrtu podpisywanej wiadomoci.
Blok danych zawierajcy skrt jest nastpnie dopeniany i maskowany funkcj generujc
mask Mask(), opart na funkcji skrtu H() z dodatkiem losowej soli S. Tak utworzony blok
danych jest ostatecznie szyfrowany kluczem prywatnym podpisujcego.
159
wierszem nastpujcym:
Signature signature = Signature.getInstance("SHA1withRSAandMGF1", "Bi");
160
Klasa PSSParameterSpec
Klasa java.security.spec.PSSParaceterSpec po raz pierwszy pojawia si w JDK 1.4,
gdzie pozwalaa zmienia rozmiar soli uywanej przy tworzeniu podpisu. W nowszym JDK
1.5 klasa ta pozwala modyfikowa wszystkie dostpne parametry mechanizmu generowania
podpisw PSS. Oznacza to, e klasa ma dwa konstruktory, a wybr jednego z nich zaley
od parametrw, ktre chcemy zmieni. Dziaanie starszego konstruktora mieci si w zakresie dziaania konstruktora nowszego, wic omwimy tylko ten drugi.
Klasa PSSParaceterSpec zawiera warto domyln, dostpn jako PSSParaceterSpec.DgFAULT.
Stanowi ona odpowiednik rcznie utworzonego obiektu postaci:
PSSParameterSpec defaultSpec = new PSSParameterSpec(
"SHA-1", "MGF1", MGF1ParameterSpec.SHA1, 20, 1);
Porwnanie konstruktora klasy PSSParaceterSpec z konstruktorem OAgPParaceterSpec wykazuje oczywiste podobiestwa. Pierwsze trzy parametry s identyczne: nazwa funkcji
skrtu, nazwa funkcji generowania maski i algorytm stosowany przez funkcj generowania
maski. Rni si natomiast ostatnie dwa parametry.
Czwarty parametr to rozmiar soli uywanej w procesie generowania podpisu. Starsza wersja konstruktora przyjmuje tylko ten jeden parametr.
Pity parametr to tak zwane pole kocowe (ang. trailer field), informujce generator podpisw, jaki bajt ma si znale na kocu podpisu. Jak dotd obsugiwana jest tylko jedna
warto pola kocowego jest to warto 1, faktycznie odwzorowywana na bajt kocowy
podpisu 0xBC. Procesem odwzorowywania zajmiemy si w nastpnym rozdziale, wic na
razie musisz mi uwierzy na sowo.
Jak sama nazwa wskazuje, obiekty klasy PSSParaceterSpec s prostymi obiektami wartoci,
wic jedynymi ich metodami s metody get() pobierajce wartoci poszczeglnych parametrw dla danej instancji PSSParaceterSpec.
Tak oto dotarlimy do koca rozdziau powiconego podstawowym zagadnieniom kryptografii asymetrycznej. W trakcie rozdziau poznalimy podstawy szyfrowania asymetrycznego, wymiany kluczy symetrycznych i tworzenia podpisw cyfrowych, wraz z parametrami algorytmw, ktrych te procesy mog wymaga. Omwione te zostay algorytmy
szyfrujce RSA i El Gamala, algorytmy uzgadniania klucza Diffiego-Hellmana i DiffiegoHellmana bazujcego na krzywej eliptycznej oraz algorytmy podpisw cyfrowych wykorzystujce metody RSA, DSA i DSA oparte na krzywej eliptycznej.
161
Co rwnie wane, poznalimy metody szyfrowania (czyli opakowywania) kluczy symetrycznych kluczami asymetrycznymi, jak rwnie opakowywania kluczy asymetrycznych
kluczami tajnymi.
Wspomniaem wczeniej, e zakodowane klucze asymetryczne zawieraj nie tylko warto
klucza, ale rwnie sporo informacji opisujcych ich struktur. To samo dotyczy parametrw
algorytmw oraz treci niektrych typw podpisw. Informacje te s zapisywane w jzyku
stanowicym podstaw certyfikatw X.509 oraz licznych protokow zwizanych z kryptografi i zarzdzaniem certyfikatami. Zanim zagbimy si w bardziej zaawansowane kwestie
kryptograficzne, przydaoby si zatem pozna podstawy tego jzyka opisu struktury obiektw i tym wanie zajmiemy si w nastpnym rozdziale.
danych za pomoc kodu MAC, ale jego wad jest konieczno znajomoci
klucza tajnego przez wszystkie strony majce ten kod sprawdza. Jaka technika
asymetryczna pozwala unikn tego problemu? Jaki jej aspekt uatwia to zadanie?