You are on page 1of 61

IDZ DO

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

Stosuj algorytmy kryptograficzne w aplikacjach


Poznaj architektur interfejsw kryptograficznych Javy
Zastosuj klucze symetryczne i asymetryczne
Naucz si zarzdzania certyfikatami w programach
W wiecie, w ktrym najcenniejszym towarem jest informacja, kryptografia coraz
bardziej zyskuje na znaczeniu. Cenne dane, przesyane w sieci lub przechowywane
w aplikacjach i bazach danych, musz by chronione za pomoc skomplikowanych
algorytmw szyfrowania i uwierzytelniania. Poniewa prby wama do serwerw
internetowych zdarzaj si regularnie, implementacja mechanizmw kryptograficznych
w aplikacjach sieciowych i platformach handlu elektronicznego ma szczeglnie wysoki
priorytet. Java, wykorzystywana bardzo czsto do tworzenia takich wanie rozwiza,
wyposaona zostaa w zestaw interfejsw programistycznych (API), ktre pozwalaj
szybko i skutecznie wzbogaca aplikacje o obsug kryptografii.
Ksika Kryptografia w Javie. Podstawy to podrcznik przedstawiajcy na
praktycznych przykadach narzdzia kryptograficzne Javy. Opisuje podstawowe
zasady ich uywania, uatwia zrozumienie zalenoci midzy poszczeglnymi
interfejsami API i uczy, jak w razie potrzeby korzysta z gotowych rozwiza,
by oszczdzi czas. Daje wiedz niezbdn do implementowania technik
kryptograficznych w aplikacjach bez niepotrzebnego komplikowania kodu rdowego.
Architektura interfejsw JCA i JCE
Szyfrowanie symetryczne
Generowanie kluczy
Stosowanie kluczy asymetrycznych
Podpisy cyfrowe
Obsuga certyfikatw
Szyfrowanie poczty elektronicznej
Twrz bezpieczne aplikacje,
wykorzystujc nowoczesne mechanizmy kryptograficzne

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

Rozdzia 2. Kryptografia z kluczem symetrycznym .............................................................................. 37


Pierwszy przykad ......................................................................................................... 38
Prosta klasa narzdziowa ........................................................................................ 38
Klasa SecretKeySpec .............................................................................................. 42
Klasa Cipher .......................................................................................................... 42
Dopenienie w symetrycznych szyfrach blokowych ........................................................... 44
Dopenienie PKCS #5/PKCS #7 ............................................................................... 44
Inne mechanizmy dopenienia .................................................................................. 47
Tryby szyfrowania w symetrycznych szyfrach blokowych ................................................... 48
Tryb ECB ................................................................................................................ 48
Tryb CBC ................................................................................................................ 50
Sowo o obiektach parametrw szyfru ....................................................................... 58
Klasa AlgorithmParameters ..................................................................................... 58
Tryb CTS specjalna odmiana CBC ........................................................................ 59
Tryby strumieniowe symetrycznych szyfrw blokowych ................................................ 59

Kryptografia w Javie. Od podstaw


Symetryczne szyfry strumieniowe ................................................................................... 63
Generowanie losowych kluczy ....................................................................................... 65
Interfejs Key ........................................................................................................... 67
Klasa KeyGenerator ................................................................................................ 67
Szyfrowanie z hasem ................................................................................................... 68
Podstawowe PBE .................................................................................................... 69
PBE w JCE .............................................................................................................. 70
Opakowywanie klucza ................................................................................................... 75
Szyfrowanie operacji wejcia-wyjcia .............................................................................. 78
Podsumowanie ............................................................................................................ 80
wiczenia .................................................................................................................... 81

Rozdzia 3. Skrty wiadomoci, MAC i HMAC ......................................................................................... 83


Klasa narzdziowa ....................................................................................................... 84
Problem modyfikacji wiadomoci ................................................................................... 86
Skrty wiadomoci ....................................................................................................... 88
Klasa MessageDigest ............................................................................................. 90
Modyfikacja skrtu ....................................................................................................... 92
HMAC MAC oparty na skrcie ................................................................................... 94
Klasa Mac .............................................................................................................. 97
Kody MAC oparte na szyfrach symetrycznych .................................................................. 98
Funkcje skrtu jako rdo danych pseudolosowych ...................................................... 100
Generowanie kluczy PBE ........................................................................................ 100
Generowanie maski .............................................................................................. 103
Operacje wejcia-wyjcia ze skrtami kryptograficznymi ................................................. 105
Podsumowanie .......................................................................................................... 107
wiczenia .................................................................................................................. 108

Rozdzia 4. Kryptografia asymetryczna ................................................................................................109


Klasa narzdziowa ..................................................................................................... 110
Interfejsy PublicKey i PrivateKey .................................................................................. 111
Algorytm RSA ............................................................................................................. 111
Klasa KeyFactory .................................................................................................. 114
Klasa RSAPublicKeySpec i interfejs RSAPublicKey ................................................... 115
Klasa RSAPrivateKeySpec i interfejs RSAPrivateKey ................................................. 115
Tworzenie losowych kluczy RSA .............................................................................. 115
Przyspieszanie pracy RSA ...................................................................................... 118
Mechanizmy dopeniania RSA ................................................................................ 120
Opakowywanie kluczy RSA ..................................................................................... 128
Wymiana kluczy tajnych .............................................................................................. 130
Uzgadnianie klucza .................................................................................................... 133
Algorytm Diffiego-Hellmana .................................................................................... 133
Diffie-Hellman bazujcy na krzywych eliptycznych ..................................................... 137
Diffie-Hellman z wieloma stronami ......................................................................... 142
Algorytm El Gamala .................................................................................................... 144
Klasa AlgorithmParameterGenerator ....................................................................... 146
Podpisy cyfrowe ......................................................................................................... 148
Klasa Signature .................................................................................................... 149
Algorytm podpisu cyfrowego DSA ........................................................................... 150
Algorytmy podpisu oparte na RSA ........................................................................... 156
Podsumowanie .......................................................................................................... 160
wiczenia .................................................................................................................. 161

Spis treci

Rozdzia 5. Opis obiektw kryptograficznych w notacji ASN.1 ............................................................163


Czym jest ASN.1? ...................................................................................................... 163
Klasa narzdziowa ..................................................................................................... 164
Podstawowa skadnia ASN.1 ....................................................................................... 165
Skadnia komentarzy ............................................................................................. 165
Identyfikatory obiektw .......................................................................................... 165
Struktura moduu .................................................................................................. 166
Typy danych w ASN.1 ................................................................................................. 168
Typy proste .......................................................................................................... 169
Typy cigw bitowych ............................................................................................ 170
Typy cigw znakowych ......................................................................................... 170
Typy strukturalizowane .......................................................................................... 172
Adnotacje typw ................................................................................................... 172
Znaczniki ............................................................................................................. 173
Typ CHOICE .......................................................................................................... 177
Typ CLASS ........................................................................................................... 178
Reguy kodowania ...................................................................................................... 179
Kodowanie BER .................................................................................................... 179
Kodowanie DER .................................................................................................... 181
API ASN.1 w Bouncy Castle ........................................................................................ 182
Tworzenie podstawowych typw ASN.1 ................................................................... 183
Obsuga znacznikw .............................................................................................. 185
Definiowanie wasnych obiektw ............................................................................ 186
Analiza nieznanego zakodowanego obiektu ............................................................. 192
Prawdziwe przykady wykorzystania ASN.1 w Javie ......................................................... 194
Podstawowe struktury ASN.1 ................................................................................. 194
Kodowanie IV ....................................................................................................... 195
Budowa podpisu PKCS #1 V1.5 ............................................................................. 196
Kodowanie parametrw podpisu PSS ..................................................................... 199
Kodowanie kluczy publicznych i prywatnych ............................................................. 201
Podsumowanie .......................................................................................................... 212
wiczenia .................................................................................................................. 213

Rozdzia 6. Nazwy wyrniajce i certyfikaty ......................................................................................215


Klasa narzdziowa ..................................................................................................... 216
Nazwy wyrniajce .................................................................................................... 216
Klasa X500Principal .............................................................................................. 218
Certyfikaty klucza publicznego ..................................................................................... 219
Klasa Certificate ................................................................................................... 220
Certyfikaty X.509 ....................................................................................................... 221
Klasa X509Certificate ........................................................................................... 221
Rozszerzenia X.509 .............................................................................................. 229
Interfejs X509Extension ........................................................................................ 230
Rozszerzenia obsugiwane bezporednio przez klas X509Certificate ........................ 231
Odczyt i zapis certyfikatw .......................................................................................... 238
Klasa CertificateFactory ........................................................................................ 238
dania certyfikacyjne ................................................................................................ 242
Tworzenie prostego centrum certyfikacji ....................................................................... 248

Kryptografia w Javie. Od podstaw


cieki i skady certyfikatw ........................................................................................ 253
Klasa CertPath ..................................................................................................... 254
Klasa CertStore .................................................................................................... 256
Klasa X509CertSelector ........................................................................................ 257
Podsumowanie .......................................................................................................... 259
wiczenia .................................................................................................................. 260

Rozdzia 7. Uniewanianie certyfikatw i walidacja cieek ...............................................................261


Klasa narzdziowa ..................................................................................................... 262
Listy uniewanionych certyfikatw ................................................................................ 265
Klasa CRL ............................................................................................................ 265
Listy uniewanionych certyfikatw X.509 ...................................................................... 266
Klasa X509CRL .................................................................................................... 267
Klasa X509CRLEntry ............................................................................................. 271
Rozszerzenia wpisw list CRL X.509 ...................................................................... 272
Rozszerzenia list CRL X.509 .................................................................................. 273
Pobieranie list CRL za pomoc klasy CertificateFactory ............................................ 278
Klasa X509CRLSelector ........................................................................................ 281
Protok OCSP weryfikacja statusu certyfikatw w czasie rzeczywistym ....................... 283
Klasa CertificateID ................................................................................................ 284
Klasa OCSPReq .................................................................................................... 285
Rozszerzenia da OCSP ..................................................................................... 288
Klasa OCSPResp .................................................................................................. 292
Klasa BasicOCSPResp .......................................................................................... 293
Rozszerzenia odpowiedzi OCSP .............................................................................. 295
Walidacja cieek certyfikatw .................................................................................... 301
Klasa TrustAnchor ................................................................................................ 301
Klasa PKIXParameters .......................................................................................... 302
Klasa CertPathValidator ........................................................................................ 304
Klasa PKIXCertPathValidatorResult ........................................................................ 305
Klasa PKIXCertPathChecker ................................................................................... 308
Budowanie poprawnej cieki na podstawie skadu CertStore ........................................ 313
Klasa CertPathBuilder ........................................................................................... 313
PKIXBuilderParameters ......................................................................................... 313
Podsumowanie .......................................................................................................... 316
wiczenia .................................................................................................................. 317

Rozdzia 8. Zarzdzanie kluczami i certyfikatami ................................................................................319


Klasa narzdziowa ..................................................................................................... 320
Klasa KeyStore .......................................................................................................... 321
Rodzaje repozytoriw ............................................................................................ 322
Podstawowe API klasy KeyStore ............................................................................. 323
Zagniedone klasy i interfejsy klasy KeyStore .............................................................. 330
Interfejs KeyStore.ProtectionParameter .................................................................. 330
Interfejs KeyStore.Entry ......................................................................................... 331
Klasa KeyStore.Builder ......................................................................................... 335
Interfejs KeyStore.LoadStoreParameter .................................................................. 338
Format PKCS #12 ...................................................................................................... 338
Korzystanie z plikw PKCS #12 w API KeyStore ....................................................... 341

Spis treci

Program keytool ......................................................................................................... 345


Polecenia programu keytool ................................................................................... 345
Repozytorium certyfikatw maszyny wirtualnej ......................................................... 349
Eksperymentowanie z programem keytool ............................................................... 349
Podpisywanie archiww JAR i pliki polityki bezpieczestwa Javy ..................................... 353
Narzdzie jarsigner ............................................................................................... 354
Pliki polityki bezpieczestwa Javy ........................................................................... 354
Podsumowanie .......................................................................................................... 355
wiczenia .................................................................................................................. 356

Rozdzia 9. CMS i S/MIME .......................................................................................................................357


Klasa narzdziowa ..................................................................................................... 357
Standard CMS ........................................................................................................... 360
Podstawy CMS ..................................................................................................... 361
Typ Data .............................................................................................................. 361
Interfejs CMSProcessable ..................................................................................... 362
Podpisane dane CMS ................................................................................................. 363
Struktura ASN.1 ................................................................................................... 363
Klasa SignerInformation ........................................................................................ 368
Klasa SignerInformationStore ................................................................................ 370
Klasa CMSSignedData .......................................................................................... 370
Koperty cyfrowe CMS ................................................................................................. 376
Struktura ASN.1 ................................................................................................... 376
Klasa RecipientInformation .................................................................................... 379
Klasa KeyTransRecipientInformation ...................................................................... 380
Klasa RecipientInformationStore ............................................................................ 381
Klasa CMSEnvelopedData ..................................................................................... 381
Klasa KEKRecipientInformation .............................................................................. 386
Kompresja danych w CMS .......................................................................................... 389
Struktura ASN.1 ................................................................................................... 389
Klasa CMSCompressedData .................................................................................. 390
Protok S/MIME ....................................................................................................... 391
Klasa CMSProcessableBodyPart ............................................................................ 392
Klasa SMIMEUtil ................................................................................................... 392
Podpisane wiadomoci S/MIME .................................................................................. 393
Klasa CMSProcessableBodyPartInbound ................................................................ 394
Klasa CMSProcessableBodyPartOutbound .............................................................. 394
Klasa SMIMESigned ............................................................................................. 394
Koperty cyfrowe S/MIME ............................................................................................ 399
Klasa SMIMEEnveloped ........................................................................................ 400
czenie podpisywania z szyfrowaniem ................................................................... 402
Kompresowane wiadomoci S/MIME ........................................................................... 407
Klasa SMIMECompressed ..................................................................................... 407
Podsumowanie .......................................................................................................... 409
wiczenia .................................................................................................................. 409

Rozdzia 10. Protokoy SSL i TLS ...............................................................................................................411


Protokoy SSL i TLS .................................................................................................... 411
Klasa narzdziowa ..................................................................................................... 413
Prosty klient i serwer SSL ........................................................................................... 415
Klasa SSLSocketFactory ........................................................................................ 416
Klasa SSLServerSocketFactory .............................................................................. 417

10

Kryptografia w Javie. Od podstaw


Klasa SSLSocket .................................................................................................. 417
Klasa SSLServerSocket ......................................................................................... 419
Interfejs HandshakeCompletedListener .................................................................. 424
Uwierzytelnianie klienta .............................................................................................. 425
Konfiguracja klasy SSLServerSocket ...................................................................... 426
Konfiguracja klasy SSLSocket w trybie serwerowym ................................................. 426
Klasa SSLContext ................................................................................................. 427
Klasa KeyManagerFactory ..................................................................................... 429
Klasa TrustManagerFactory ................................................................................... 434
Zarzdzanie danymi sesji SSL ..................................................................................... 437
Interfejs SSLSession ............................................................................................ 438
Obsuga protokou HTTPS ........................................................................................... 443
Klasa HttpsURLConnection .................................................................................... 444
Interfejs HostnameVerifier ..................................................................................... 446
Podsumowanie .......................................................................................................... 451
wiczenia .................................................................................................................. 451

Dodatek A Rozwizania wicze ............................................................................................................453


Rozwizania do rozdziau 1. ........................................................................................ 453
Rozwizania do rozdziau 2. ........................................................................................ 454
Rozwizania do rozdziau 3. ........................................................................................ 454
Rozwizania do rozdziau 4. ........................................................................................ 455
Rozwizania do rozdziau 5. ........................................................................................ 456
Rozwizania do rozdziau 6. ........................................................................................ 458
Rozwizania do rozdziau 7. ........................................................................................ 459
Rozwizania do rozdziau 8. ........................................................................................ 460
Rozwizania do rozdziau 9. ........................................................................................ 463
Rozwizania do rozdziau 10. ...................................................................................... 466

Dodatek B Algorytmy obsugiwane przez dostawc Bouncy Castle ...................................................467


Szyfry asymetryczne ................................................................................................... 467
Walidacja cieek certyfikatw .................................................................................... 467
Algorytmy uzgadniania klucza ...................................................................................... 468
Repozytoria kluczy i certyfikatw .................................................................................. 468
Algorytmy MAC ........................................................................................................... 468
Algorytmy podpisu ...................................................................................................... 468
Skrty wiadomoci ..................................................................................................... 468
Symetryczne szyfry blokowe ........................................................................................ 469
Symetryczne szyfry strumieniowe ................................................................................. 470

Dodatek C Krzywe eliptyczne w Bouncy Castle ....................................................................................471


Interfejsy obsugi krzywych eliptycznych ........................................................................ 471
Interfejs ECKey ..................................................................................................... 472
Interfejs ECPrivateKey ........................................................................................... 472
Interfejs ECPublicKey ............................................................................................ 472
Interfejs ECPointEncoder ....................................................................................... 472
Klasy obsugi krzywych eliptycznych ............................................................................. 472
Klasa ECNamedCurveParameterSpec ..................................................................... 473
Klasa ECNamedCurveSpec .................................................................................... 473
Klasa ECParameterSpec ....................................................................................... 473
Klasa ECPrivateKeySpec ....................................................................................... 474
Klasa ECPublicKeySpec ......................................................................................... 474

Spis treci

11

Dodatek D Bibliografia i dodatkowe zasoby ..........................................................................................475


Standardy ASN.1 ....................................................................................................... 475
Strony grup roboczych IETF ......................................................................................... 476
Publikacje NIST .......................................................................................................... 476
Standardy PKCS ........................................................................................................ 477
Dokumenty RFC ......................................................................................................... 477
Inne przydatne standardy ............................................................................................ 479
Przydatna literatura .................................................................................................... 479
Przydatne adresy internetowe ...................................................................................... 479

Skorowidz ...............................................................................................................................................481

Podstawowy problem zwizany z uywaniem szyfrw, kodw MAC i kodw HMAC


z kluczem symetrycznym polega na bezpiecznym dostarczeniu tajnego klucza do odbiorcy,
by mg on odszyfrowa otrzyman wiadomo. Jednego z rozwiza tego problemu dostarcza kryptografia asymetryczna. Nazwa pochodzi std, e do szyfrowania i deszyfrowania
uywane s rne klucze, najczciej nazywane kluczem publicznym i prywatnym.
W tym rozdziale poznamy podstawy kryptografii asymetrycznej, w tym moliwoci wykorzystania kluczy publicznych i prywatnych do wymiany kluczy tajnych i do generowania
podpisw cyfrowych. Istnieje wiele rnych rodzajw algorytmw asymetrycznych, wic
przyjrzymy si rwnie parametrom wymaganym do utworzenia klucza oraz odpowiednim
mechanizmom dopenienia podczas szyfrowania wiadomoci. W przeciwiestwie do algorytmw symetrycznych algorytmy asymetryczne nie s najlepsze do szyfrowania duych
iloci danych, std te zajmiemy si metodami czenia obu rodzajw szyfrowania, by ten
cel osign.
Pod koniec tego rozdziau powiniene:
n

zna i rozumie najpopularniejsze obecnie algorytmy,

rozumie metody wymiany i uzgadniania klucza, jak rwnie najwaniejsze


ich wady i zalety,

rozumie zasady uywania mechanizmw dopenienia z algorytmami


asymetrycznymi,

umie tworzy i weryfikowa podpisy cyfrowe.

Poznasz te w praktyce rne API Javy wspomagajce generowanie, modyfikacj i stosowanie kluczy i szyfrw asymetrycznych.

110

Kryptografia w Javie. Od podstaw

W tym rozdziale zostanie wykorzystana wersja klasy Utils rozszerzona o implementacj


SecureRanSec zwracajc przewidywalne wyniki. Z kryptograficznego punktu widzenia
moe si to wyda szalestwem, ale bardzo uatwi korzystanie z przykadw w tym rozdziale, gdy bdzie moliwe uzyskanie dokadnie takich wynikw, jak w ksice.
Oto kod klasy:
package rozdzial4;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* Klasa narzdziowa dla przykadw z rozdziau 4.
*/
public class ptils extends rozdziale.ptils
{
private static class FixedRand extends SecureRandom
{
MessageDigest sha;
byte[] state;
FixedRand()
{
try
{
this.sha = MessageDigest.getInstance("SHA-1");
this.state = sha.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("nie znaleziono SHA-1!");
}
}
public void nextBytes(byte[] bytes)
{
int off = 0;
sha.update(state);
while (off < bytes.length)
{
state = sha.digest();
if (bytes.length - off > state.length)
{
System.arraycopy(state, 0, bytes, off, state.length);
}
else
{
System.arraycopy(state, 0, bytes, off, bytes.length - off);
}

Rozdzia 4. n Kryptografia asymetryczna

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.

Omwienie kryptografii asymetrycznej w Javie nie obejdzie si bez dwch interfejsw:


java.security.PublicKey i java.security.PrivateKey. Kady klucz wykorzystywany podczas szyfrowania asymetrycznego bdzie implementowa jeden z tych interfejsw, std te
czsto pojawiaj si one jako typy obiektw zwracanych przez wszelkiego rodzaju klasy
operujce na kluczach i danych klucza.
Same interfejsy bezporednio rozszerzaj interfejs java.security.Key, ale nie wprowadzaj
adnych wasnych metod jedynym powodem ich istnienia jest zapewnienie bezpieczestwa typw. Uzasadnienie takiego stanu rzeczy poznamy podczas szczegowego omawiania poszczeglnych algorytmw. Wikszo algorytmw ma wasne zestawy interfejsw
obsugi klucza, gdy w przeciwiestwie do algorytmw klucza symetrycznego, ktre mona stosowa w zasadzie zamiennie, kady algorytm asymetryczny rni si od pozostaych
nie tylko sposobem dziaania, ale i parametrami potrzebnymi do utworzenia klucza.
Na pocztek zajmiemy si zwykym szyfrowaniem danych, ale w dalszej czci rozdziau
zobaczymy, e kryptografia asymetryczna stanowi te podstaw mechanizmw uzgadniania
klucza i podpisw cyfrowych.

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

Kryptografia w Javie. Od podstaw


Podstaw bezpieczestwa algorytmu jest trudno rozkadu duych liczb na czynniki pierwsze (faktoryzacji). Mechanizm polega na tym, e klucze publiczny i prywatny s funkcjami
pary duych liczb pierwszych, a odtworzenie tekstu jawnego na podstawie znajomoci szyfrogramu i klucza publicznego, ktrym zosta zaszyfrowany, jest uwaane za zadanie
o trudnoci porwnywalnej z zadaniem ustalenia liczb pierwszych, na podstawie ktrych
wygenerowano klucze. Istotn kwesti jest wymagana dugo klucza. Klucze w przykadach zostay tak dobrane, by byo je atwo wpisa, a wynik ich uycia mieci si w jednym
wierszu, wic nie maj one nic wsplnego z bezpieczestwem. W praktyce klucz powinien
mie dugo co najmniej 1024 bitw, a jeli tworzona aplikacja ma chroni wiadomoci
przez wicej ni 10 lat, to dugo t naley podwoi.
Wyprowadzenie penego algorytmu znacznie wykracza poza ramy tej ksiki, ale sam ide
dziaania mona wyrazi bardzo prosto. Niech p i q bd liczbami pierwszymi, a n, e i d
bd liczbami takimi, e:
n = pq
oraz
ed 1mod((p - 1)(q - 1))
Dla danej wiadomoci m mamy wtedy:
c = me mod n
m = cd mod n
gdzie c jest szyfrogramem. Liczby n, e i d maj te dusze nazwy s to odpowiednio
modu, wykadnik publiczny i wykadnik prywatny. Ogln zasad jest taki dobr wartoci
wykadnika publicznego, by etap szyfrowania by mao kosztowny obliczeniowo, po czym
wykadnik prywatny jest generowany zgodnie z powyszym wzorem. Dugo wygenerowanego klucza RSA jest okrelana dugoci n, wic kada z liczb p i q musi mie dugo
poowy klucza.
Przed skorzystaniem z algorytmu naley si upewni, e kad szyfrowan wiadomo da
si zapisa w postaci duej, dodatniej liczby cakowitej, nieprzekraczajcej n. Na szczcie
nie jest to trudne. W Javie wystarczy stworzy dodatni obiekt BigInteger na podstawie
bajtw skadajcych si na wiadomo.
Cay czas korzystamy z JCE, wic nigdy nie bdziemy musieli samodzielnie zamienia
wiadomoci na obiekt BigInteger. Trzeba jednak mie wiadomo, e taka wanie operacja odbywa si za kulisami, gdy ma ona wpyw na bezpieczne stosowanie algorytmu RSA.
Wspomniaem na przykad, e liczbowa reprezentacja wiadomoci m musi by arytmetycznie
mniejsza od liczby n. Powodem takiego wymagania jest to, e obliczenia w ramach algorytmu RSA odbywaj si modulo n, czyli wymagaj dzielenia przez n i przyjmowania
reszty z tego dzielenia jako wyniku. Oznacza to, e kada warto wiksza od n zostanie
podczas szyfrowania zredukowana do mod n. Nieco pniej zobaczymy, jak mona sobie
z tym ograniczeniem poradzi i szyfrowa dugie wiadomoci szyfrem RSA, ale na razie wystarczy pamita, e dziaanie algorytmu opiera si na arytmetyce duych liczb cakowitych.

Rozdzia 4. n Kryptografia asymetryczna

113

Sprbuj sam: Podstawowe RSA


Na pocztek sprbuj uruchomi poniszy przykad. Wprowadza on podstawowe klasy obsugujce algorytm RSA i pokazuje, e JCE pozwala nadal korzysta z jednolitego API, niezalenie od tego, e faktyczna implementacja RSA diametralnie rni si od wczeniej poznanych algorytmw. Dugo klucza wynosi 128 bitw, czyli zaledwie jedn sm minimalnej
wymaganej dugoci, ale dla potrzeb przykadu to w zupenoci wystarczy.
package rozdzial4;
import
import
import
import
import
import

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

Kryptografia w Javie. Od podstaw

System.out.println("dane odszyfrowane: " + ptils.toHex(plaincext));

Uruchomienie przykadu powinno da nastpujcy wynik:


dane wejciowe: beef
dane zaszyfrowane: d2db158e8f6c1c98702c5d54fe0add42
dane odszyfrowane: beef

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.

Rozdzia 4. n Kryptografia asymetryczna

115

Klasa RSAPublicKeySpec i interfejs RSAPublicKey


Jak sugeruje kocwka nazwy, klasa RSAPublicKeySpec pozwala tworzy obiekty wartoci
zawierajce parametry niezbdne do wygenerowania klucza publicznego RSA, czyli modu
i wykadnik publiczny.
Przekazujc obiekt RSAPublicKeySpec metodzie KeyFactery.generatePublic(), mona uzyska obiekt implementujcy PublicKey. Poniewa chodzi o szyfr RSA, klasa KeyFactery
zwrci w tym przypadku obiekt implementujcy RSAPublicKey o takich samych sygnaturach metod, co klasa RSAPublicKeySpec. Istnieje te metoda getMeSulus() zwracajca obiekt
BigInteger odpowiadajcy moduowi oraz metoda getPublicgepenent() zwracajca analogiczny obiekt dla wykadnika publicznego.

Klasa RSAPrivateKeySpec i interfejs RSAPrivateKey


Klasa RSAPrivateKeySpec pozwala tworzy obiekty wartoci zawierajce parametry niezbdne
do wygenerowania klucza prywatnego RSA, czyli modu i wykadnik prywatny.
Dziaanie klasy opisujcej klucz oraz interfejsu RSAPrivateKey stanowi odzwierciedlenie
dziaania ich odpowiednikw do tworzenia obiektw klucza publicznego, z t tylko rnic, e
w miejsce metody getPublicgepenent() wywoywana jest metoda getPrivategepenent().
Symetria klas, metod i interfejsw nie powinna tu dziwi, gdy wynika bezporednio z symetrii samego algorytmu RSA.

Tworzenie losowych kluczy RSA


Zamiast podawa gotowe dane dla klucza, mona go te wygenerowa losowo. Z opisu dziaania algorytmw asymetrycznych wiemy, e potrzebne s dwa klucze: publiczny i prywatny.
Oznacza to, e zamiast klasy KeyGenerater generujcej pojedynczy obiekt Key trzeba bdzie
skorzysta z klasy KeyPairGenerater, generujcej par kluczy w postaci obiektu KeyPair.
Szczegowym omwieniem tych klas zajmiemy si ju niebawem, ale najlepiej zapozna
si z nimi na praktycznym przykadzie.

Sprbuj sam: Generowanie losowych kluczy RSA


Sprbuj uruchomi poniszy przykad. Generuje on par kluczy RSA na podstawie losowych danych, szyfruje prost wiadomo pobranym z tej pary kluczem publicznym, a nastpnie deszyfruje szyfrogram pobranym z pary kluczem prywatnym.
package rozdzial4;
import
import
import
import

java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;

116

Kryptografia w Javie. Od podstaw


import javax.crypto.iipher;
/**
* Przykad RSA z losowym generowaniem klucza.
*/
public class RandomKeyRSAExample
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] { (byte)0xbe, (byte)0xef };
iipher cipher = iipher.getInstance("RSA/NhNE/NoPadding", "Bi");
SecureRandom random = ptils.createFixedRandom();
// generowanie 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
cipher.init(iipher.DEiR PciMhDE, privKey);
byte[] plaincext = cipher.doFinal(ciphercext);

System.out.println("dane odszyfrowane: " + ptils.toHex(plaincext));

Uruchomienie przykadu daje nastpujcy wynik:


dane wejciowe: beef
dane zaszyfrowane: 8274caf4a1f54beb58f6798755d2cfceeeeef710aef520865c0ccdca0a672601
dane odszyfrowane: beef

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.

Rozdzia 4. n Kryptografia asymetryczna

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

Kryptografia w Javie. Od podstaw


Biorc konkretny przykad, wykorzystanie jednego ze standardowych wykadnikw publicznych w postaci zalecanej w standardzie X.509 wartoci F4 (w przypadku dostawcy Bouncy
Castle jest to warto domylna) wymagaoby zamiany wywoania metody initialize()
obiektu generater na:
generator.initialize(
new RSAKeyGenParameterSpec(256, RSAKeyGenParameterSpec.F4), random);

Po takiej inicjalizacji wszystkie klucze publiczne RSA generowane przez obiekt generater
bd miay wykadnik publiczny o wartoci odpowiadajcej staej F4, czyli liczbie cakowitej 0x10001.

Przyspieszanie pracy RSA


Wystarczy rzut oka na pierwszy przykad algorytmu RSA, by stwierdzi, e wykadnik
prywatny klucza prywatnego jest znacznie wikszy od wykadnika publicznego. Z tego te
wzgldu korzystanie z klucza prywatnego RSA jest znacznie wolniejsze od korzystania
z klucza publicznego. Zachowanie to mona atwo uzasadni dziki niemu szyfrowanie
danych jest szybkie i mona je wykonywa na klientach o ograniczonej mocy obliczeniowej.
Jak si wkrtce przekonamy, dodatkow zalet jest szybkie sprawdzanie podpisw cyfrowych.
Czy da si przyspieszy proces deszyfrowania kluczem prywatnym? Okazuje si, e tak.
Wymaga to wprawdzie wykorzystania znajomoci liczb uytych do stworzenia moduu, a te
powinny raczej by utrzymywane w tajemnicy, ale skoro wszelkie informacje o wykadniku prywatnym i tak powinny by tajne, to dooenie jeszcze kilku tajnych danych nie powinno by wielkim problemem. Znajomo wykorzystanych liczb pierwszych jest tu konieczna,
gdy pozwala wykorzysta w praktyce chiskie twierdzenie o resztach (ang. Chinese Remainder Theorem, CRT).

Chiskie twierdzenie o resztach


Odkryte w pierwszym wieku naszej ery przez chiskiego matematyka Sun Tsu twierdzenie
o resztach w uproszczeniu gosi, e:
Dla liczby n o rozkadzie na liczby pierwsze p1 * p2 * * pi ukad kongruencji postaci:
(x mod pj) aj, j = 1, 2,, i
ma dokadnie jedno rozwizanie modulo n.
Ujmujc to samo nieco inaczej, dowoln liczb mniejsz od n mona jednoznacznie wyrazi jako cig reszt z dzielenia jej przez kolejne czynniki pierwsze n.
Nie bd si zagbia w matematyczny dowd prawdziwoci tego twierdzenia, gdy jest on
podany w wielu innych publikacjach, ale najwaniejszym nastpstwem praktycznym jest to,
e obliczenia dotyczce wykadnika prywatnego mona wykonywa inaczej ni obliczenia
dla wykadnika publicznego. Obliczenia takie odbywaj si na liczbach cakowitych znacznie mniejszych od moduu, dziki czemu s duo szybsze. Z tego wanie wzgldu przedstawione poniej klasy RSAPrivateCrtKey i RSAPrivateCrtKeySpec maj metody do pobierania nie dwch, lecz omiu wartoci.

Rozdzia 4. n Kryptografia asymetryczna

119

Klasy RSAPrivateCrtKeySpec i RSAPrivateCrtKey


Klasa java.security.spec.RSAPrivateCrtKeySpec dostarcza obiektw wartoci zawierajcych dane klucza dla klucza prywatnego RSA, ktre mona wykorzysta zgodnie z CRT.
Obiekt RSAPrivateCrtKeySpec mona przekaza obiektowi KeyFactery dla RSA w dokadnie
taki sam sposb, jak opisany wczeniej obiekt RSAPrivateKeySpec. Klasa fabrykujca
powinna zwrci obiekt klucza implementujcy interfejs java.security.interjaces.RSAPrivateCrtKey, ktry z kolei rozszerza RSAPrivateKey. Jeli mimo przekazania RSAPrivateCrtKeySpec zwracany jest klucz implementujcy jedynie sam RSAPrivateKey, to uywany
dostawca prawdopodobnie nie implementuje CRT. To samo moe by powodem powolnego wykonywania operacji RSA z kluczem prywatnym.
RSAPrivateCrtKeySpec i RSAPrivateCrtKey maj identyczne metody. Dostpne metody pozwalaj pobiera poszczeglne wartoci skadajce si na klucz CRT:
n getMeSulus()

zwraca warto moduu n,

n getPrivategepenent()

zwraca warto prywatnego wykadnika d,

n getPriceP()

zwraca warto liczby pierwszej p,

n getPriceQ()

zwraca warto liczby pierwszej q.

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.

Chiskie twierdzenie o resztach z wieloma liczbami pierwszymi


Z podanego wczeniej opisu CRT wynika, e twierdzenie opiera si na swobodnym dostpie do liczb pierwszych stanowicych czynniki moduu. Co ciekawe, ani w CRT, ani w algorytmie RSA nigdzie nie jest powiedziane, e takie liczby musz by akurat dwie mona
rwnie dobrze wzi na przykad cztery. Gdyby wic pracowa z kluczem 2048-bitowym,
to zamiast wykonywa obliczenia dla generowania i uywania klucza na dwch liczbach
1024-bitowych, mona by te same operacje wykonywa na czterech liczbach 512-bitowych.
Niezalenie od dodatkowych trudnoci wynikajcych z obecnoci wicej ni dwch czynnikw w procesie generowania moduu operacje na kluczu prywatnym w wersji z czterema
liczbami pierwszymi bd znacznie szybsze od tych samych operacji dla wersji z dwiema
liczbami.
T metod oblicze obsuguje interfejs java.security.interjaces.RSAMultiPricePrivateCrtKey oraz klasy java.security.spec.RSAMultiPricePrivateCrtKeySpec i java.security.
spec.RSAOtherPriceInje. W tej ksice nie bd tej techniki omawia, gdy obecnie nie
ma jej implementacji w dostawcy Bouncy Castle, a sam algorytm jest opatentowany. Jeli

120

Kryptografia w Javie. Od podstaw


jednak chcesz si dowiedzie wicej, to polecam zapoznanie si z dokumentacj i dotyczcym midzy innymi tego zagadnienia standardem PKCS #1 firmy RSA Security. Zawarte
tam informacje stanowi naturalne rozwinicie przedstawionego wyej opisu CRT.
W ten sposb koczymy omawianie zasad dziaania samego algorytmu RSA. Pora zaj si
mechanizmami sucymi do przeksztacenia bajtw szyfrowanej wiadomoci w du liczb
cakowit, ktr mona nastpnie wprowadzi na wejcie tego algorytmu.

Mechanizmy dopeniania RSA


Najwiksz rnic midzy RSA a zwykym szyfrem symetrycznym mona zobaczy, zastpujc w ostatnim przykadzie wiersz:
byte[] input = new byte[] { (byte)0xbe, (byte)0xef };

wierszem:
byte[] input = new byte[] { 0x00, (byte)0xbe, (byte)0xef };

Po wprowadzeniu tej zmiany wynik dziaania programu wyglda tak:


dane wejciowe: 00beef
dane zaszyfrowane: 8274caf4a1f54beb58f6798755d2cfceeeeef710aef520865c0ccdca0a672601
dane odszyfrowane: beef

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);

Wprowadzenie tej zmiany i ponowne uruchomienie programu powinno da taki wynik:


dane wejciowe: 00beef
dane zaszyfrowane: 00000000000000000000000000000000000000000000000000006ae5dddec9cf
dane odszyfrowane: beef

Rozdzia 4. n Kryptografia asymetryczna

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.

Dopenienie PKCS #1 V1.5


Oryginalna wersja PKCS #1 opisywaa prosty mechanizm z trzema trybami dopenienia
bloku szyfrowanego. Pierwszym jest typ 0, polegajcy na dopenieniu samymi zerami i stanowicy odpowiednik trybu NePaSSing z JCE. Drugi tryb (typ 1) jest uywany podczas szyfrowania danych algorytmem RSA z kluczem publicznym, a tryb trzeci (typ 2) jest stosowany przy deszyfrowaniu danych kluczem prywatnym. Ostatnie dwie techniki nosz w JCE
wanie nazw PKCS1PaSSing.
Dopenienie PKCS #1 typu 1 jest bardzo proste. Dla danej wiadomoci M odpowiadajca
jej wiadomo dopeniona Mp to:
Mp = 0x00 || 0x01 || F || 0x00 || M
gdzie F jest cigiem bajtw 0xFF, a || operatorem sklejenia. Dodatkowo istnieje ograniczenie dugoci M: F musi mie co najmniej 8 bajtw, wic M nie moe by dusze od dugoci klucza w bajtach pomniejszonej o 11 bajtw.
Dopenianie PKCS #1 typu 2 jest rwnie proste. Dla danej wiadomoci M odpowiadajca
jej wiadomo dopeniona Mp to:
Mp = 0x00 || 0x02 || R || 0x00 || M
gdzie R jest cigiem co najmniej 8 bajtw pseudolosowych.
Rnica midzy tymi mechanizmami jest do ciekawa. Oba generuj due liczby cakowite
o dugoci zblionej do dugoci klucza (rnice dugoci s rzdu jednego bajta), ale typ 1
gwarantuje, e ta sama warto M zaszyfrowana tym samym kluczem zawsze da ten sam
szyfrogram, podczas gdy typ 2 gwarantuje zachowanie przeciwne. Innymi sowy, jest bardzo mao prawdopodobne, e ta sama wiadomo z dopenieniem typu 2 dwukrotnie da ten
sam szyfrogram. Z tego te wzgldu (jak si przekonamy w kolejnych przykadach) typ 1
jest stosowany w podpisach, gdy te s generowane z wykorzystaniem klucza prywatnego

122

Kryptografia w Javie. Od podstaw


w kocu ten sam dokument podpisany tym samym kluczem powinien zawsze dawa ten
sam podpis. Dopenianie typu 2 jest z kolei uywane przez obiekt Cipher stworzony w trybie PKCS1PaSSing na etapie szyfrowania kluczem publicznym.

Sprbuj sam: Dopenienie PKCS #1 V1.5


Sprbuj uruchomi poniszy przykad. Szyfrowane dane zaczynaj si teraz od zera, a do
szyfrowania podany zosta tryb dopenienia PKCS1PaSSing. W tym przypadku szyfrowanie
odbywa si z wykorzystaniem klucza publicznego, wic stosowany jest typ 2 dopenienia,
co na og oznacza uycie danych losowych. Aby zapewni przewidywalno wynikw
przykadu, podaem jako rdo danych losowych stay generator liczb losowych z klasy
narzdziowej.
package rozdzial4;
import
import
import
import

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

Rozdzia 4. n Kryptografia asymetryczna

123

cipher.init(iipher.DEiR PciMhDE, privKey);


byte[] plaincext = cipher.doFinal(ciphercext);

System.out.println("dane odszyfrowane: " + ptils.toHex(plaincext));

Powinno si okaza, e wprowadzenie dopenienia pozwolio zachowa wiodce zera


w szyfrowanym tekcie:
dane wejciowe: 00beef
dane zaszyfrowane: 01fce4a90be26bb1ceebc2f969a84024d1574990e8f7eee0e6e5c4e6ffbee77e
dane odszyfrowane: 00beef

Sprbuj te wprowadzi opisan wczeniej zmian wykadnika publicznego na warto F0.


Jak wida, dopenienie nie tylko zachowuje tre oryginalnej wiadomoci, lecz rwnie
pomaga j chroni.

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

Kryptografia w Javie. Od podstaw


Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard
PKCS #1), polegajcy na wygenerowaniu duej liczby przeksztace odczytywanego szyfrogramu, a nastpnie wykorzystaniu informacji zwracanych przez wyroczni otrzymujc
takie wiadomoci do stopniowego odtworzenia oryginalnego tekstu kryjcego si za szyfrogramem.
Jest to moliwe, gdy niektre cechy struktury wiadomoci PKCS #1 z dopenianiem typu
2 mona przewidzie. Przewaga dopeniania OAEP polega na tym, e wynikowa wiadomo jest nierozrnialna od losowego cigu bajtw. Co wicej, poprawnie odszyfrowana
wiadomo bdzie wewntrznie spjna, ale dziki zastosowaniu funkcji maskujcej bazujcej na kryptograficznej funkcji skrtu spjnoci tej nie da si sprawdzi, dopki cao nie
zostanie XOR-owana z odpowiednim strumieniem bajtw. Strumie jest generowany przez
funkcj, ktrej dziaania nie mona przewidzie bez znajomoci poprawnych danych. Efekt
poczenia funkcji skrtu i generowania maski jest tu podobny, jak w przypadku zastosowania funkcji skrtu przy uzgadnianiu klucza: pozbawienie atakujcego moliwoci wykorzystania matematycznych wasnoci algorytmu bazowego. Nawet w przypadku dostpu do
wyroczni odszyfrowane dane bd zawsze rwnie losowe.
Wicej informacji na temat modelu losowej wyroczni mona znale w artykule Bellarea i Rogawaya Random Oracles Are Practical: A Paradigm for Designing Efficient
Protocols. Jako metody losowej wyroczni jako sposobu dowodzenia bezpieczestwa
pozostaje wprawdzie kwesti kontrowersyjn, ale wypada przyzna, e jest to krok we
waciwym kierunku.

Pomimo zasadnoci powyszego wywodu i zalece odnonie do stosowania w aplikacjach


dopenienia OAEP, gdy tylko jest to moliwe, caa ta historia niekoniecznie musi wiadczy o nieadekwatnoci dopenienia PKCS #1. Prawdziwy mora jest taki, e odrzucajc
wiadomo, naley unika ujawniania czytelnych informacji. Obsuenie odrzucenia wiadomoci powinno trwa dokadnie tyle samo, co jej przyjcie, a ilo zwracanych informacji naley ograniczy do minimum najlepiej do zera (jeli tylko jest to moliwe). Po prostu unikaj wyciekw!
Pora przyjrze si samej metodzie OAEP. Jak ju wspomniaem, korzysta ona z jednokierunkowej funkcji skrtu, dalej oznaczanej H(), i opartej na niej funkcji generowania maski
Mask(). Nieco upraszczajc, cay proces skada si z nastpujcych trzech etapw przetwarzania wiadomoci M, cigu parametryzujcego P i losowego ziarna S:
1. M1 = Mask((H(P) || PS || 0x01 || M), S)
2. M2 = Mask(S, M1)
3. Mp = 0x00 || M2 || M1

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).

Rozdzia 4. n Kryptografia asymetryczna

125

Rzut oka na rwnania pokazuje, e ostateczna zamaskowana i dopeniona wiadomo Mp


jest tworzona przez sklejenie dwch zamaskowanych wiadomoci. Pierwsza z nich ukrywa
ziarno wykorzystane do zamaskowania chronionej wiadomoci, natomiast druga maskuje
sam wiadomo. Przy okazji wida wyranie, e korzystanie z tego mechanizmu wie si
ze sporym narzutem, gdy wymaga przechowania dodatkowo ziarna oraz skrtu cigu parametryzujcego P. Ziarno jest dugoci samego skrtu, wic przyjmujc hLen za dugo
skrtu i kLen za dugo klucza (obie dugoci w oktetach), mona wyrazi maksymaln
dugo szyfrowanej wiadomoci jako:
MaxLen = kLen 2hLen 2
W przeciwiestwie do oryginalnego dopeniania PKCS #1 cig dopeniajcy PS moe mie
dugo zerow.

Sprbuj sam: Dopenienie OAEP


Porwnaj poniszy kod z wczeniejsz klas PKCS1PaSSeSRSAgeacple. Cho dugo szyfrowanej wiadomoci nie ulega zmianie, to jednak dugo klucza musiaa wzrosn z 256
do 384 bitw, by pomieci dopenienie.
package rozdzial4;
import
import
import
import

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

Kryptografia w Javie. Od podstaw


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);

System.out.println("dane odszyfrowane: " + ptils.toHex(plaincext));

Uruchomienie przykadu powinno spowodowa wywietlenie wyniku z duszym ni dotd


szyfrogramem:
dane wejciowe: 00beef
dane zaszyfrowane:
020692d99b7b7ee82841e4590f1f04dbdbdfeee627deda72a18acf244e41da4a012a8e4c1c89021ea850
8f5406816ef74b
dane odszyfrowane: 00beef

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

Rozdzia 4. n Kryptografia asymetryczna

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 zawiera te kilka obiektw wartoci: MGF1ParaceterSpec.SHA1, MGF1ParaceterSpec.SHA256,


MGF1ParaceterSpec.SHA3M4 i MGF1ParaceterSpec.512. Pozwalaj one definiowa wersje funkcji
MGF1 odpowiednio dla algorytmw SHA-1, SHA-256, SHA-384 i SHA-512.
Klasa MGF1ParaceterSpec ma tylko jedn wasn metod i jest ni getDigestAlgerithc(),
zwracajca nazw algorytmu skrtu uywanego przez funkcj MGF1.

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

Kryptografia w Javie. Od podstaw


Jak wida, stworzenie obiektu OAgPParaceterSpec wymaga przekazania wartoci odpowiadajcych parametrom z rwnania opisujcego dziaanie mechanizmu OAEP. Pierwszy argument to funkcja skrtu H, za drugi to nazwa funkcji generowania maski Mask. Trzecim
argumentem jest obiekt zawierajcy parametry dla funkcji generowania maski domylnie bdzie to obiekt odpowiadajcy funkcji MGF1 opartej na SHA-1. Jako ostatni argument
przekazywany jest cig parametryzujcy P w tym przypadku pusta tablica bajtw.
Celem uycia obiektu OAgPParaceterSpec jest najczciej okrelenie wasnego cigu P. Samodzielnie tworzc taki obiekt, naley jedynie pamita, e dokument PKCS #1 zaleca, by
funkcja H i funkcja generujca mask korzystay z tego samego algorytmu funkcji skrtu.
Oznacza to, e w przypadku wykorzystania SHA-256 jako algorytmu dla H naleaoby poda mniej wicej taki kod:
hAEPParameterSpec sha256hAEPSpec = new hAEPParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFApLc);

Oczywicie stosownie do potrzeb mona tu poda inn warto cigu parametryzujcego P.


MGF1 jest na razie jedyn obsugiwan funkcj generowania maski, a kierunki aktualnych
zmian w tej kwestii wskazuj dokumenty PKCS #1 i IEEE P1361.
Klasa OAgPParaceterSpec ma kilka metod get pobierajcych wartoci wykorzystane do jej
stworzenia, ale podobnie jak w przypadku klas PSeurce i MGF1ParaceterSpec, s one przede
wszystkim uywane wewntrznie przez dostawc.
Pozostaje jeszcze kwestia wykorzystania utworzonego obiektu OAgPParaceterSpec. Jak mona
si spodziewa, jest on po prostu przekazywany metodzie Cipher.init(), co w przypadku
stworzonego powyej obiektu sha256OAgPSPec mogoby wyglda na przykad tak:
iipher c = iipher.getInstance("RSA/None/hAEPiithSHA256AndMGF1Padding");
c.init(iipher.ENiR PciMhDE, publicKey, sha256hAEPSpec, new SecureRandom());

Opakowywanie kluczy RSA


W rozdziale 2. poznalimy zasady uywania kluczy symetrycznych do opakowywania innych kluczy symetrycznych. Jak si okazuje, dokadnie to samo API mona wykorzysta
do opakowywania kluczy asymetrycznych.

Sprbuj sam: Opakowywanie klucza prywatnego RSA


Przyjrzyj si poniszemu przykadowi. W odrnieniu od kodu z rozdziau 2. w tym przypadku uywany jest tryb Cipher.PRIVATg_KgY informujcy szyfr opakowujcy, e powinien
si spodziewa klucza prywatnego. Gdyby opakowywa klucz publiczny (cho byoby to
raczej dziaanie pozbawione sensu), naleaoby poda Cipher.PUBLIC_KgY.
package rozdzial4;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;

Rozdzia 4. n Kryptografia asymetryczna

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.");
}

Wykonanie programu powinno si szczliwie zakoczy komunikatem Klucz eSzyskany

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

Kryptografia w Javie. Od podstaw


Co ciekawe, nie trzeba tu korzysta z adnego konkretnego mechanizmu opakowujcego.
W przypadku kluczy symetrycznych metoda getgnceSeS() zwracaa same bajty skadajce
si na klucz, natomiast dla kluczy asymetrycznych zwraca ona nie tylko warto klucza, ale
rwnie sporo informacji dotyczcych ich struktury. Oznacza to, e podanie niewaciwego
klucza tajnego przy wykonaniu metody unwrap() dla klucza asymetrycznego zakoczy si
niepowodzeniem odpakowania, gdy algorytm odpakowujcy nie bdzie w stanie zrekonstruowa klucza.

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.

Sprbuj sam: Wymiana kluczy tajnych


Sprbuj uruchomi nastpujcy program:
package rozdzial4;
import
import
import
import
import
import

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());

Rozdzia 4. n Kryptografia asymetryczna


bhut.write(key.getEncoded());
}

return bhut.toByteArray();

private static hbject[] unpackKeyAndIV(byte[] data)


{
byte[] keyD = new byte[16];
byte[] iv = new byte[data.length - 16];

return new hbject[] {


new SecretKeySpec(data, 16, data.length - 16, "AES"),
new IvParameterSpec(data, 0, 16)
};

public static void main(String[] args) throws Exception


{
byte[] input = new byte[] { 0x00, (byte)0xbe, (byte)0xef };
SecureRandom
random = ptils.createFixedRandom();
// utworzenie klucza RSA
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "Bi");
generator.initialize(1024, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("dane wejciowe: " + ptils.toHex(input));
// utworzenie klucza symetrycznego i IV
Key sKey = ptils.createKeyForAES(256, random);
IvParameterSpec sIvSpec = ptils.createitrIvForAES(0, random);
// opakowanie klucza symetrycznego i IV
iipher xiipher = iipher.getInstance("RSA/NhNE/hAEPiithSHA1AndMGF1Padding", "Bi");
xiipher.init(iipher.ENiR PciMhDE, pubKey, random);
byte[] keyBlock = xiipher.doFinal(packKeyAndIv(sKey, sIvSpec));
// szyfrowanie
iipher siipher = iipher.getInstance("AES/iRc/NoPadding", "Bi");
siipher.init(iipher.ENiR PciMhDE, sKey, sIvSpec);
byte[] ciphercext = siipher.doFinal(input);
System.out.println("dugo bloku klucza: " + keyBlock.length);
System.out.println("dugo szyfrogramu: " + ciphercext.length);
// opakowanie klucza symetrycznego i IV
xiipher.init(iipher.DEiR PciMhDE, privKey);
hbject[] keyIv = unpackKeyAndIV(xiipher.doFinal(keyBlock));

131

132

Kryptografia w Javie. Od podstaw


// deszyfrowanie
siipher.init(iipher.DEiR PciMhDE, (Key)keyIv[0], (IvParameterSpec)keyIv[1]);
byte[] plaincext = siipher.doFinal(ciphercext);

System.out.println("dane odszyfrowane: " + ptils.toHex(plaincext));

Uruchomienie przykadu powinno da nastpujcy wynik:


dane wejciowe: 00beef
dugo bloku klucza: 128
dugo szyfrogramu: e
dane odszyfrowane: 00beef

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.

Do tego typu szyfrowania mona uywa algorytmu RSA (o czym przekonalimy si


w powyszym przykadzie) lub algorytmu El Gamal. Cay proces sprawia bardzo dobre wraenie. Algorytm asymetryczny jest uywany do tego, do czego si najlepiej nadaje, a algorytm symetryczny do tego, do czego on z kolei najlepiej si nadaje wszystko na swoim
miejscu.
Do peni bezpieczestwa w powyszym przykadzie brakuje jeszcze jednego elementu: nie
dostarczylimy adnego sposobu sprawdzenia integralnoci szyfrowanych danych. W rzeczywistych zastosowaniach koniecznie trzeba pamita o doczaniu do wiadomoci kodu
MAC, skrtu lub innych danych pozwalajcych zweryfikowa integralno deszyfrowanych informacji.

Rozdzia 4. n Kryptografia asymetryczna

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

Kryptografia w Javie. Od podstaw


Jeszcze jedna informacja w kwestii konwencji zapisu: przyjo si oznacza warto prywatn kadej ze stron liter X, a warto publiczn (GX mod P) liter Y.
Algorytm wyglda na prosty i na szczcie wykorzystujcy go kod rwnie nie jest skomplikowany.

Sprbuj sam: Uzgadnianie klucza metod Diffiego-Hellmana


Sprbuj uruchomi nastpujcy przykad:
package rozdzial4;
import
import
import
import

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");

Rozdzia 4. n Kryptografia asymetryczna

135

byte[] aShared = hash.digest(aKeyAgree.generateSecret());


byte[] bShared = hash.digest(bKeyAgree.generateSecret());

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.

Jak ju wspomniaem, faza przygotowawcza programu jest bardzo podobna do przygotowa


RSA. Obsuga metody Diffiego-Hellmana objawia si midzy innymi podaniem cigu DH
przy wyborze generatora pary kluczy, ale jest te kilka osobnych klas JCE obsugujcych t
procedur uzgadniania klucza. Klasy te zostan omwione poniej.

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

Lub: atak metod przechwytywania przez podmiot poredniczcy przyp. red.

136

Kryptografia w Javie. Od podstaw

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);

Do generowania obiektw DHParaceterSpec mona te wykorzysta dostawc metodzie


tej przyjrzymy si nieco pniej, przy okazji omawiania klasy AlgerithcParaceterGenerater.

Obiekty parametrw dla kluczy Diffiego-Hellmana


JCE udostpnia kilka obiektw pozwalajcych przenosi dane parametrw kluczy dla metody Diffiego-Hellmana w sposb niezaleny od konkretnego dostawcy. Uywane s do tego
celu klasy javae.crypte.spec.DHPrivateKeySpec i javae.crypte.spec.DHPublicKeySpec, ktrych obiekty mog przenosi informacje dla dowolnego algorytmu bazujcego na tej metodzie.
Konstruktor DHPrivateKeySpec przyjmuje warto prywatn X oraz niezbdne do jej uywania wartoci P i G. Przykadowe wywoanie konstruktora wyglda wic tak:
DHPrivateKeySpec dhPrivateSpec = new DHPrivateKeySpec(x, p, g);

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);

Rozdzia 4. n Kryptografia asymetryczna

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.

Interfejsy dla kluczy Diffiego-Hellmana


W poprzednim przykadzie wykorzystana zostaa klasa Key. Gdyby potrzebne byo bezpieczniejsze typowanie, mona skorzysta z interfejsw javae.crypte.interjaces.DHPrivateKey,
javae.crypte.interjaces.DHPublicKey i ich interfejsu nadrzdnego javae.crypte.interjaces.DHKey.
Interfejs DHKey deklaruje tylko jedn metod DHKey.getParacs(), zwracajc obiekt DHParaceterSpec zawierajcy klucz publiczny i prywatny.
DHPrivateKey rwnie deklaruje tylko jedn metod jest to DHPrivateKey.getD(), zwra-

cajca warto prywatn nieprzesyan w procedurze uzgadniania klucza.


Wreszcie DHPublicKey te ma jedn metod DHPublicKey.getY(). Zwraca ona warto GX
mod P, gdzie X jest wartoci zwracan przez metod getD() odpowiedniego klucza prywatnego, a G i P s wartociami pobranymi z obiektu DHParaceterSpec.

Diffie-Hellman bazujcy na krzywych eliptycznych


Tradycyjny algorytm Diffiego-Hellmana nie jest jedyn metod uzgadniania klucza to
samo zadanie mona wykona z pomoc kryptografii bazujcej na krzywych eliptycznych.
Wykorzystanie krzywych eliptycznych w kryptografii opiera si na wasnociach cia skoczonych. Jako metoda kryptograficzna zostaa niezalenie opracowana przez Neala Koblitza
z University of Washington i Victora Millera z IBM w 1985 roku.
Ciao to struktura matematyczna pozwalajca wykonywa na swych elementach operacje
dodawania, odejmowania, mnoenia i dzielenia zwracajce wyniki nalece do tego ciaa.
Mowa tu o ciaach skoczonych, czyli posiadajcych skoczon liczb elementw, dziki
czemu mog one by wykorzystane do operacji kryptograficznych korzystajcych z krzywych nad takimi ciaami. Szczeglnie istotne s tu dwie konstrukcje ciaa: Fp zawiera
krzywe nad skoczonym ciaem liczb pierwszych p, a F2m jest ciaem zoonym z krzywych
dajcych si wyprowadzi z tzw. optymalnej reprezentacji bazy normalnej, czyli wielomianowej reprezentacji wyprowadzonej z cigw m-bitowych. F2m jest przestrzeni o tyle ciekaw, e za spraw swej binarnej natury moe ona by bardzo wydajnie przetwarzana przez
komputery. Niestety algorytmy jej przetwarzania s objte kilkoma patentami, wic dla potrzeb tej ksiki bdziemy si zajmowa przede wszystkim krzywymi nad Fp. Na przykadach przekonamy si jednak, e z punktu widzenia JCE/JCA nie ma specjalnej rnicy
midzy korzystaniem z krzywych nad F2m i nad Fp.
Bezpieczestwo metody opiera si na trudnoci rozwizania problemu logarytmu dyskretnego na krzywej eliptycznej, ktry mona pokrtce przedstawi nastpujco:
Dla danych dwch punktw na krzywej P i Q znale tak liczb k, e kP = Q.

138

Kryptografia w Javie. Od podstaw


Okazuje si, e dla dostatecznie duego k znalezienie rozwizania jest zadaniem bardzo
trudnym obliczeniowo. Zrozumienie rde bezpieczestwa kryptosystemu na krzywej eliptycznej pozwoli te lepiej zrozumie sam algorytm.
Zamy, e A i B uzgadniaj pewn krzyw i wybieraj punkt G, przy czym informacje te
nie musz by utrzymywane w tajemnicy. Podobnie jak w przypadku algorytmu Diffiego-Hellmana, A i B wybieraj nastpnie dwie tajne wartoci x i y, dziki ktrym moliwa bdzie bezpieczna komunikacja:
1. A wysya B xG (klucz publiczny A).
2. B wysya A yG (klucz publiczny B).
3. Odbywa si wymiana danych szyfrowanych kluczem sesji opartym na xyG,

ktry kada ze stron wylicza niezalenie.


Istotn kwesti praktyczn jest znalezienie odpowiedniej krzywej. Na szczcie opisanych
zostao wiele takich krzywych, na przykad w dokumentach X9.62 i FIPS PUB 186-2. Poniszy przykad wykorzystuje wanie jedn z tych standardowych krzywych.

Sprbuj sam: Diffie-Hellman na krzywej eliptycznej


Przyjrzyj si nastpujcemu przykadowi:
package rozdzial4;
import
import
import
import
import
import
import
import

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)),

Rozdzia 4. n Kryptografia asymetryczna

139

new BigInteger("ffffffffffffffffffffffff99def8e6146bc9b1b4d228e1", 16),


1);
keyGen.initialize(ecSpec, ptils.createFixedRandom());
// przygotowania
KeyAgreement aKeyAgree = KeyAgreement.getInstance("EiDH", "Bi");
KeyPair aPair = keyGen.generateKeyPair();
KeyAgreement bKeyAgree = KeyAgreement.getInstance("EiDH", "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");
byte[] aShared = hash.digest(aKeyAgree.generateSecret());
byte[] bShared = hash.digest(bKeyAgree.generateSecret());

System.out.println(ptils.toHex(aShared));
System.out.println(ptils.toHex(bShared));

Uruchomienie programu powinno da nastpujcy wynik:


5ea61569aed14f67b67e77dc6ca22ee7ab01e844
5ea61569aed14f67b67e77dc6ca22ee7ab01e844

Zgodnie z planem po obu stronach wygenerowany zosta ten sam klucz.

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.

ECField, ECFieldFp i ECFieldF2m


Interfejs java.security.spec.gCFielS jest bazowym interfejsem do obsugi cia skoczonych
dla krzywych eliptycznych. Implementuj go klasy java.security.spec.gCFielSFp i java.
security.spec.gCFielSF2c. gCFielS deklaruje tylko jedn metod gCFielS.get-FielSSize(),

140

Kryptografia w Javie. Od podstaw


zwracajc dugo ciaa skoczonego w przypadku gCFielSFp bdzie to dugo liczby
pierwszej p w bitach, natomiast dla gCFielSF2c bdzie to dugo m (rwnie w bitach).
gCFielSFp jest prostym pojemnikiem dla liczby pierwszej okrelajcej ciao, nad ktrym
operuje krzywa. Klasa ma jeden konstruktor przyjmujcy liczb pierwsz i jedn metod
gCFielSFp.getP(), ktra zwraca t liczb.
gCFielSF2c jest jak sama nazwa wskazuje pojemnikiem dla informacji okrelajcych

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-

SF2c, jak i gCFielSFp su po prostu do identyfikacji ciaa.

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.

Rozdzia 4. n Kryptografia asymetryczna

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

Kryptografia w Javie. Od podstaw


Tak si skada, e krzywa wykorzystana w programie BasicgCDHgeacple nosi nazw prime192v1, a jej specyfikacja podana jest w dokumencie X9.62. Oznacza to, e zamiast rcznie
wypisywa definicj krzywej i jej parametrw jako:
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)),
new BigInteger("ffffffffffffffffffffffff99def8e6146bc9b1b4d228e1", 16),
1);

mona byo importowa klas java.security.spec.gCGenParaceterSpec i po prostu napisa:


EiGenParameterSpec ecSpec = new EiGenParameterSpec("prime192v1");

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.

Kryptografia krzywej eliptycznej przed JDK 1.5


W wersjach Javy poprzedzajcych JDK 1.5 nie byo bezporedniej obsugi kryptografii
krzywej eliptycznej, wic API uywane do tego celu zaleay od konkretnego dostawcy.
Dodatek B przedstawia opis obsugi kryptografii krzywej eliptycznej w wersjach JDK starszych od 1.5 za pomoc API Bouncy Castle. Stosowane tam klasy s podobne do swych
odpowiednikw ze wspczesnego JCA, ale jednak nie s identyczne (i dotyczy to wikszoci dostawcw). Jeli zamierzasz intensywnie korzysta z kryptografii opartej na krzywych eliptycznych w Javie, to obecnie warto si przesi na JDK 1.5 pozwala to zaoszczdzi sporo pisania.

Diffie-Hellman z wieloma stronami


Ciekaw cech uzgadniania klucza tradycyjn metod Diffiego-Hellmana jest to, e moe
ona posuy do uzgodnienia klucza midzy wicej ni dwoma stronami. Wanie z tego
wzgldu metoda KeyAgreecent.SePhase() jest w stanie zwrci klucz.

Sprbuj sam: Trjstronny Diffie-Hellman


Przerobienie wczeniejszego przykadu BaseDHgeacple tak, by uzgadnianie klucza dotyczyo trzech stron, wymaga jedynie kilku prostych zmian. Na pocztek trzeba bdzie doda
trzeci obiekt KeyAgreecent i utworzy dla niego klucz:

Rozdzia 4. n Kryptografia asymetryczna

143

KeyAgreement cKeyAgree = KeyAgreement.getInstance("DH", "Bi");


KeyPair cPair = keyGen.generateKeyPair();

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);

Na tym etapie wszystkie trzy obiekty KeyAgreecent powinny ju by w takim stanie, by


wywoania metody KeyAgreecent.generateSecret() zwracay t sam warto dla kadego
obiektu. Mona to sprawdzi, dodajc na kocu kodu fragment obsugujcy generowanie
i wywietlanie wyniku dla trzeciego obiektu:
byte[] cShared = hash.digest(cKeyAgree.generateSecret());
System.out.println(ptils.toHex(cShared));

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

Kryptografia w Javie. Od podstaw

Algorytm El Gamala jest odmian metody Diffiego-Hellmana, wic jego bezpieczestwo


opiera si na tych samych zagadnieniach matematycznych. Cho mona twierdzi, e jest
mniej elegancki od RSA, algorytm ten jest powszechnie uywany, na przykad jako domylny algorytm dla wikszoci kluczy szyfrujcych w Open PGP (RFC 2440).
Aby metod El Gamala wysa wiadomo do odbiorcy o kluczu publicznym Gy mod P,
naley utworzy tymczasowy klucz publiczny Gx mod P, zaszyfrowa wiadomo, mnoc
j modulo P przez Gxy mod P, i wysa szyfrogram w jednym bloku z tymczasowym kluczem publicznym. Algorytm dziaa bez zarzutu, ale (jak si przekonamy w praktyce) ma t
wad, e szyfrogram jest dwukrotnej dugoci klucza.

Sprbuj sam: Szyfrowanie metod El Gamala


Poniszy kod ilustruje generowanie losowego klucza i szyfrowanie algorytmem El Gamala.
Cao wykonuje si znacznie wolniej od rwnowanego kodu dla RSA. Przyczyny takiego
stanu rzeczy poznamy nieco pniej sprbuj uruchomi ten przykad, a przekonasz si,
co w tym przypadku znaczy duo wolniej.
package rozdzial4;
import
import
import
import

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.

Rozdzia 4. n Kryptografia asymetryczna

145

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);

System.out.println("dane odszyfrowane: " + ptils.toHex(plaincext));

Uruchomienie programu daje nastpujcy wynik:


dane wejciowe: beef
dane zaszyfrowane: 8c2e699772c14496bc82400d11decae4f662fe90864e8c55
eb781e6679fcdfaa60ce78b56908e525c021fcf77e40f661525da56ed41eedf92848aaba2459dff5
dane odszyfrowane : beef

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

Kryptografia w Javie. Od podstaw

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.

Sprbuj sam: Algorytm El Gamala z obiektem AlgorithmParameterGenerator


Przyjrzyj si poniszemu przykadowi i porwnaj go z kodem programu RanSecKeyglGacal-

geacple. Uruchom program i czytaj dalej.


package rozdzial4;
import
import
import
import
import
import
import

java.security.AlgorithmParameterGenerator;
java.security.AlgorithmParameters;
java.security.Key;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.spec.AlgorithmParameterSpec;

Rozdzia 4. n Kryptografia asymetryczna

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);

System.out.println("dane odszyfrowane: " + ptils.toHex(plaincext));

Wykorzystany tu generator liczb losowych otrzymuje to samo ziarno, co w poprzednich


przykadach, dziki czemu program powinien wypisa taki sam wynik, jak wczeniejszy
RanSecKeyglGacalgeacple. Przyczyna jest prosta: przetwarzanie danych wykonywane wewntrznie przez dostawc jest (przynajmniej w przypadku dostawcy Bouncy Castle) dokadnie takie samo, jak rcznie zakodowane operacje z powyszego programu.

148

Kryptografia w Javie. Od podstaw

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);

mona ograniczy dugo wartoci prywatnej na przykad do 200 bitw, wywoujc:


apg.init(new DHGenParameterSpec(256, 200), random);

gdzie argumentami dla konstruktora DHGenParaceterSpec s dugo liczby pierwszej P


w bitach oraz maksymalna dugo wartoci prywatnej Y (rwnie w bitach). Takie wywoanie powodowaoby tworzenie obiektw, ktrych metoda DHParaceterSpec.getL() zwracaaby warto 200, co tym samym ograniczaoby dugo wartoci prywatnych do 200 bitw.

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

Rozdzia 4. n Kryptografia asymetryczna

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.

Z podpisami cyfrowymi wi si dwa procesy: tworzenie podpisu i sprawdzanie podpisu.


Wbrew pozorom operacje te rni si nieco od swych odpowiednikw w wiecie szyfrw,
czyli szyfrowania i deszyfrowania odbiorcy wystarczy sprawdzi podpis, bez koniecznoci jego czytania. Operacje tworzenia i sprawdzania podpisw cyfrowych hermetyzuje w Javie
klasa Signature.

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.

Uywanie klasy Signature w trybie tworzenia podpisu


Istniej dwie wersje metody Signature.initSign() sucej do przeczenia obiektu Signature w tryb tworzenia podpisu. Obie przyjmuj najwaniejsz informacj, czyli klucz prywatny, a jedna przyjmuje dodatkowo rdo danych losowych.
Gdy obiekt Signature zostanie ju inicjalizowany w trybie skadania podpisu cyfrowego,
korzystanie z niego bardzo przypomina prac z poznan wczeniej klas MessageDigest.
Dane s przekazywane do zainicjalizowanego obiektu Signature za porednictwem jednej
z kilku metod upSate(), a po wprowadzeniu wszystkich danych wywoywana jest metoda
Signature.sign(), ktra w zalenoci od wersji moe zwrci podpis cyfrowy w postaci
tablicy bajtw lub zapisa go w tablicy bajtw przekazanej podczas wywoania.

150

Kryptografia w Javie. Od podstaw

Uywanie klasy Signature w trybie sprawdzania podpisu


Rwnie metoda Signature.initVerijy() ma dwie odmiany, lecz w odrnieniu od metody
initSign() jedna z wersji suy wycznie wygodzie programisty przyjmuje ona za argument obiekt klasy Certijicate, podczas gdy wersja podstawowa przyjmuje klucz publiczny.
Wprowadzanie danych do obiektu Signature odbywa si tu identycznie, jak w trybie tworzenia klucza, czyli poprzez wywoania metody upSate(), jednak etap weryfikacji wyglda ju
nieco inaczej. Gdy obiektowi Signature zostanie zaadowany komplet danych do weryfikacji podpisu, wywoywana jest zwracajca warto logiczn metoda Signature.verijy(),
ktrej przekazywana jest tablica bajtw zawierajca sprawdzany podpis. Jeli metoda verijy()
zwrci warto true, to podpis jest w porzdku; jeli jalse, to dane nie pasuj do podpisu.

Metody Signature.setParameter() i Signature.getParameters()


Podobnie jak obiekty Cipher czy KeyPairGenerater, obiekty klasy Signature mog korzysta z obiektw AlgerithcParaceterSpec w celu dostarczenia dodatkowych informacji lub
zwrcenia obiektu AlgerithcParaceters zawierajcego ustawienia pozwalajce w razie
potrzeby powtrzy wykonywan operacj. Jak mona si spodziewa, metoda Signature.
getParaceters() zwraca obiekt AlgerithcParaceterSpec zawierajcy parametry biecej
operacji. Nieco inaczej wyglda korzystanie z takiego obiektu z klas Signature, gdy nie
jest on przekazywany w ramach inicjalizacji obiektu, lecz za pomoc metody Signature.
setParaceter(). Jeli przekazane metodzie parametry nie nadaj si dla danej implementacji algorytmu podpisu, metoda ta zgosi wyjtek InvaliSAlgerithcParacetergeceptien.
Przykad uycia parametrw zobaczymy nieco pniej, przy okazji omawiania podpisw
cyfrowych typu RSA PSS. Najpierw jednak zajmiemy si algorytmem specjalnie stworzonym wycznie do tworzenia i sprawdzania podpisw cyfrowych.

Algorytm podpisu cyfrowego DSA


Pierwsz wersj algorytmu podpisu cyfrowego DSA (ang. Digital Signature Algorithm)
przedstawi w sierpniu 1991 roku NIST. DSA sta si nastpnie pierwszym na wiecie algorytmem podpisu cyfrowego oficjalnie zatwierdzonym przez rzd zosta on opisany w dokumencie FIPS PUB 186 jako standard podpisu cyfrowego DSS (ang. Digital Signature
Standard). Nazwy DSA i DSS s niekiedy stosowane zamiennie, cho midzy algorytmami
jest pewna rnica: DSS wymaga korzystania konkretnie z SHA-1 jako funkcji skrtu.
Ciekaw cech DSA jest to, e nie da si go uy do szyfrowania. Na rysunku 4.4 wida, e
celem dziaania algorytmu jest wycznie weryfikacja podpisu za pomoc klucza publicznego, czyli bez ujawniania danych uytych do utworzenia podpisu.

Rozdzia 4. n Kryptografia asymetryczna

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

liczba pierwsza Q taka, e 2159 < Q < 2160;

liczba pierwsza P taka, e Q jest dzielnikiem (P 1);

generator G dla niepowtarzalnej grupy cyklicznej rzdu Q w ciele P.

Nie bd si tu zagbia w operacje matematyczne zwizane z obliczaniem generatora.


Wystarczy wiedzie, e znajc P, Q i G, mona utworzy klucz publiczny, wybierajc
warto prywatn X tak, e 1 X Q, a nastpnie obliczajc warto publiczn Y = GX
mod P. Klucz publiczny stanowi zbir wartoci Y, P, Q i G, natomiast kluczem prywatnym
s X, P, Q i G.
Znajc klucz publiczny i prywatny, mona ju tworzy i sprawdza podpisy cyfrowe.
W odrnieniu od algorytmu RSA, gdzie operacje z kluczem publicznym i prywatnym s
wykonywane w ten sam sposb, algorytm DSA korzysta z rnych oblicze do generowania klucza i sprawdzania go.
Dla danej funkcji skrtu H() (w przypadku DSS bdzie to zawsze SHA-1) generowanie
podpisu DSA dla wiadomoci M obejmuje nastpujce etapy:
1. Wybr tajnej, losowej liczby cakowitej K takiej, e 0 < K < Q.
2. Obliczenie R = (GK mod P) mod Q.
3. Obliczenie S = ((K1 mod Q)(H(M) + XR)) mod Q.

Podpis stanowi liczby R i S.


Sprawdzanie podpisu wymaga znajomoci klucza publicznego strony podpisujcej oraz
wartoci R i S. Etapy weryfikacji podpisu to:

152

Kryptografia w Javie. Od podstaw


1. Sprawdzenie, czy 0 < R < Q i 0 < S < Q jeli nie, podpis jest odrzucany.
2. Obliczenie A = S1 mod Q, B = (AH(M)) mod Q oraz C = (RA) mod Q.
3. Wyliczenie V = (GBYC mod P) mod Q.

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.

Sprbuj sam: DSA


Oto przykad zastosowania algorytmu DSA, a przy okazji pierwszy program korzystajcy
z klasy Signature. Jak wida, w zasadzie wystarczy zna nazw algorytmu klasa Signature
skutecznie ukrywa faktyczne obliczenia, dziki czemu praca z DSA sprowadza si do korzystania z wynikw procesu tworzenia i sprawdzania podpisu.
package rozdzial4;
import
import
import
import

java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.Signature;

public class BasicDSAExample


{
public static void main(String[] args) throws Exception
{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "Bi");
keyGen.initialize(512, new SecureRandom());
KeyPair keyPair = keyGen.generateKeyPair();
Signature signature = Signature.getInstance("DSA", "Bi");
// tworzenie podpisu
signature.initSign(keyPair.getPrivate(), ptils.createFixedRandom());
byte[] message = new byte[] { (byte)'a', (byte)'b', (byte)'c' };
signature.update(message);
byte[] sigBytes = signature.sign();
// sprawdzanie podpisu
signature.initVerify(keyPair.getPublic());
signature.update(message);
if (signature.verify(sigBytes))
{

Rozdzia 4. n Kryptografia asymetryczna

153

System.out.println("ieryfikacja podpisu zako czona powodzeniem.");


}
else
{
System.out.println("Bd weryfikacji podpisu.");
}

Uruchomienie programu powinno si zakoczy komunikatem:


ieryfikacja podpisu zako czona powodzeniem.

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

Kryptografia w Javie. Od podstaw


Podobnie jak obiekty klasy DHParaceterSpec, obiekty klasy DSAParaceterSpec mona generowa za pomoc obiektw klasy AlgerithcParaceterGenerater. Tak wic do wygenerowania obiektu DSAParaceterSpec dla kluczy 512-bitowych (z ktrych korzystalimy ju we
wczeniejszych przykadach) mona by utworzy obiekt parametrw postaci nastpujcej:
AlgorithmParameterGenerator apg = AlgorithmParameterGenerator.getInstance("DSA", "Bi");
apg.init(512, new SecureRandom());
AlgorithmParameters params = apg.generateParameters();
AlgorithmParameterSpec dsaSpec = params.getParameterSpec(DSAParameterSpec.class);

Utworzonego w ten sposb obiektu parametrw mona uywa do generowania wasnych


par kluczy poprzez przekazanie go jako argumentu wywoania metody KeyPairGenerater.
initialize().
Poza generowaniem kluczy losowo mona je rwnie tworzy za pomoc klasy KeyFactery.
JCA obsuguje tak moliwo, dostarczajc klasy pozwalajce przenosi parametry dla
kluczy DSA.

Obiekty parametrw dla kluczy DSA


Dostarczane przez JCA obiekty parametrw pozwalaj przenosi dane i parametry dla kluczy DSA. S to proste obiekty wartoci, mogce suy do przekazywania informacji okrelajcych klucze DSA java.security.spec.DSAPrivateKeySpec dla klucza prywatnego
i java.security.spec.DSAPublicKeySpec dla klucza publicznego.
Klasa DSAPrivateKeySpec ma pojedynczy konstruktor przyjmujcy po prostu warto prywatn X i parametry pozwalajce wyliczy z niej warto publiczn, czyli pobrane z obiektu
DSAParaceterSpec wartoci P, Q i G. Przykadowe wywoanie moe wyglda tak:
DSAPrivateKeySpec dsaPrivateSpec = new DSAPrivateKeySpec(x, p, q, g);

Klasa DSAPublicKeySpec rwnie ma tylko jeden konstruktor, przyjmujcy warto publiczn Y


oraz parametry P, Q i G, z ktrych zostaa ona wygenerowana. Wywoanie konstruktora wyglda zatem tak:
DSAPublicKeySpec dsaPublicSpec = new DSAPublicKeySpec(y, p, q, g);

Podobnie jak w przypadku innych obiektw wartoci przenoszcych dane klucza, jedynymi
metodami klas DSAPrivateKeySpec i DSAPublicKeySpec s metody get() pobierajce poszczeglne parametry skadowe.

Interfejsy dla kluczy DSA


W programie BasicDSAgeacple klasa KeyPair zostaa wykorzystana bezporednio po wygenerowaniu kluczy. W sytuacjach, w ktrych potrzebne jest bezpieczniejsze typowanie,
mona korzysta z interfejsw java.security.interjaces.DSAPrivateKey i java.security.
interjaces.DSAPublicKey, jak rwnie ich interfejsu nadrzdnego java.security.interjaces.DSAKey (rozszerzajcego Key). Interfejsy te pozwalaj odrni klucze DSA od innych kluczy asymetrycznych.

Rozdzia 4. n Kryptografia asymetryczna

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.

DSA oparte na krzywej eliptycznej


Istnieje te algorytm generowania podpisw DSA za pomoc krzywej eliptycznej zamiast
metod przewidzianych w standardzie DSS nosi on nazw ECDSA. Tak jak uzgadnianie
klucza metod Diffiego-Hellmana bazujc na krzywej eliptycznej byo podobne do wersji
tego algorytmu korzystajcej z liczb pierwszych, tak rwnie DSA oparte na krzywej eliptycznej polega na stworzeniu podpisu skadajcego si z wartoci R i S. Poprawno klucza
jest sprawdzana tak samo, jak w wersji oryginalnej poprzez sprawdzenie, czy warto V
wyliczona na podstawie podpisu i klucza publicznego podpisujcego jest rwna wartoci R.
Algorytm ECDSA jest opisany szczegowo w standardzie X9.62, a jego dokadne zrozumienie wymaga pewnej znajomoci matematycznych podstaw kryptosystemw opartych na
krzywych eliptycznych, wic nie bd si tu zagbia w szczegy implementacji. Jak si
przekonamy, pomimo zupenie innego aparatu matematycznego korzystanie z ECDSA w Javie
nie rni si specjalnie od zwykego DSA.

Sprbuj sam: DSA oparte na krzywej eliptycznej


Przyjrzyj si poniszemu przykadowi i porwnaj go z kodem stworzonej wczeniej klasy
BasicDSAgeacple. Jak mona si byo spodziewa, tworzony jest inny obiekt KeyPairGenerater, ale poza tym jedyn rnic jest parametr gCDSA przekazywany podczas wywoania
metody Signature.getInstance(). Przebieg procesw podpisywania i sprawdzania podpisu
jest identyczny, jak w poprzednim przykadzie.
package rozdzial4;
import
import
import
import
import

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

Kryptografia w Javie. Od podstaw


KeyPair keyPair = keyGen.generateKeyPair();
Signature signature = Signature.getInstance("EiDSA", "Bi");
// tworzenie podpisu
signature.initSign(keyPair.getPrivate(), ptils.createFixedRandom());
byte[] message = new byte[] { (byte)'a', (byte)'b', (byte)'c' };
signature.update(message);
byte[] sigBytes = signature.sign();
// sprawdzanie podpisu
signature.initVerify(keyPair.getPublic());
signature.update(message);

if (signature.verify(sigBytes))
{
System.out.println("ieryfikacja podpisu zako czona powodzeniem.");
}
else
{
System.out.println("Bd weryfikacji podpisu.");
}

Uruchomienie programu powinno si zakoczy komunikatem Weryjikacja peSpisu zakeczena peweSzeniec.

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");

Algorytmy podpisu oparte na RSA


Stworzenie podpisu algorytmem RSA sprowadza si do zaszyfrowania odpowiednich danych kluczem prywatnym i udostpnienia wyniku jako podpisu. Mechanizm dziaania algorytmu RSA oznacza, e taki podpis mona odszyfrowa kluczem publicznym proces ten

Rozdzia 4. n Kryptografia asymetryczna

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.

Podobnie jak w przypadku trybw dopenienia, najpopularniejsze metody uywania RSA


do tworzenia podpisw s opisane w PKCS #1. Analogicznie do dopenie, istniej dwa typy podpisu: jeden oparty na wersji 1.5 PKCS #1, a drugi (nowszy) bazujcy na wersji 2
PKCS#1.

Podpisy PKCS #1 V1.5


Podpisy w wersji 1.5 PKCS #1 wykorzystuj dopenienie PKCS1 typu 1. Z opisu dopenienia RSA pamitamy, e dla dopenienia PKCS #1 typu 1 dopeniona wersja Mp wiadomoci
M tworzona jest nastpujco:
Mp = 0x00 || 0x01 || F || 0x00 || M
gdzie F jest cigiem oktetw 0xFF, a || jest operatorem sklejenia. F musi zawiera co najmniej osiem oktetw, a dugo M nie moe przekracza dugoci klucza w oktetach pomniejszonej o 11.

Sprbuj sam: Generowanie podpisu algorytmem RSA


Sprbuj uruchomi poniszy przykad. Jak si przekonasz, rwnie w tym przypadku
zmiany s niewielkie.
package rozdzial4;
import
import
import
import

java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.Signature;

public class PKiS1SignatureExample


{
public static void main(String[] args) throws Exception
{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "Bi");

158

Kryptografia w Javie. Od podstaw


keyGen.initialize(512, new SecureRandom());
KeyPair keyPair = keyGen.generateKeyPair();
Signature signature = Signature.getInstance("SHA1withRSA", "Bi");
// tworzenie podpisu
signature.initSign(keyPair.getPrivate(), ptils.createFixedRandom());
byte[] message = new byte[] { (byte)'a', (byte)'b', (byte)'c' };
signature.update(message);
byte[] sigBytes = signature.sign();
// sprawdzenie podpisu
signature.initVerify(keyPair.getPublic());
signature.update(message);

if (signature.verify(sigBytes))
{
System.out.println("ieryfikacja podpisu zako czona powodzeniem.");
}
else
{
System.out.println("Bd weryfikacji podpisu.");
}

Po raz kolejny powinien si pojawi komunikat Weryjikacja peSpisu zakeczena pewe-

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.

Rozdzia 4. n Kryptografia asymetryczna

159

Utworzenie podpisu Ms dla wiadomoci M z kluczem K obejmuje nastpujce etapy:


1. M1 = 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || H(M) || S
2. M2 = P || 0x01 || S
3. M3 = Mask(M2, H(M1)) || H(M1) || 0xBC
4. Ms = RSAEncrypt(K, M3)

P jest w tym przypadku dopenieniem skadajcym si z oktetw o wartoci 0x00. Dugo


P wyraa si wzorem:
kLen sLen hLen 2
gdzie kLen jest najczciej dugoci bloku, sLen jest dugoci S, a hLen jest dugoci
skrtu (wszystkie dugoci w oktetach).
Sprawdzenie podpisu PSS wymaga odszyfrowania go kluczem publicznym, oddzielenia
soli i odtworzenia H(M1) na podstawie podpisywanej wiadomoci. Jeli odtworzona warto jest taka sama jak warto w odszyfrowanym bloku, to podpis jest wany dla tych danych. W przeciwnym wypadku podpis trzeba odrzuci.
W przeciwiestwie do starszej metody podpisywania obecno losowej soli w procesie generowania podpisu oznacza, e kolejne podpisy generowane tym samym kluczem prywatnym dla tych samych danych bd rne. Jeli taka przewidywalno jest potrzebna, to
mona skorzysta z soli o zerowej dugoci wtedy wynik oblicze dla tych samych danych
i tego samego klucza prywatnego bdzie zawsze taki sam.
Jak wida, PSS znacznie si rni od starszego kodowania metod PKCS #1, ale poniewa
jest on oparty na algorytmie RSA, wic przejcie na PSS wymaga zmiany tylko jednego
wiersza w kodzie programu PKCS1Signaturegeacple.
Sprbuj zastpi wiersz:
Signature signature = Signature.getInstance("SHA1withRSA", "Bi");

wierszem nastpujcym:
Signature signature = Signature.getInstance("SHA1withRSAandMGF1", "Bi");

i ju mamy algorytm PSS.


Podobnie jak w przypadku mechanizmu PKCS #1, wida, e nazwa algorytmu dla podpisu
PSS rwnie ma okrelon struktur. Format cigu przypomina nam, e PSS ma wiele
wsplnego z OAEP jest to SkrtwithRSAanSFunkcjaMaski. O ile jedyn obsugiwan jak
dotd funkcj generowania maski jest MGF1, o tyle zamiast SHA-1 mona uywa innych
funkcji skrtu, na przykad SHA-256, SHA-384 czy SHA-512. Podajc nazwy tych funkcji,
trzeba pamita o pomijaniu cznika (-), tak wic SHA-256 zostanie zapisane jako SHA256.
Podpisy PSS mog te przyjmowa obiekty parametrw, ktrym przyjrzymy si w nastpnej kolejnoci.

160

Kryptografia w Javie. Od podstaw

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);

Przekazanie tak utworzonego obiektu parametrw obiektowi signature uywajcemu PSS


wygldaoby tak:
signature.setParameter(defaultSpec);

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.

Rozdzia 4. n Kryptografia asymetryczna

161

W trakcie tego rozdziau nauczye si:


n

tworzy klucze asymetryczne z obiektw parametrw klucza za pomoc klasy


KeyFactery,

tworzy losowe klucze asymetryczne za pomoc klasy KeyPairGenerater,

przeprowadza szyfrowanie asymetryczne z wykorzystaniem klasy Cipher,

przeprowadza procedur uzgadniania klucza za pomoc klasy KeyAgreecent,

tworzy podpisy cyfrowe z uyciem klasy Signature,

uywa obiektw AlgerithcParaceters z klasami Cipher i Signature,

uywa klasy AlgerithcParaceterGenerater do tworzenia obiektw


AlgerithcParaceters.

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.

1. Kolega prbuje wykorzysta algorytm RSA do wymiany kluczy, ale jego

implementacja zawodzi, gdy tylko wiodcym bajtem klucza jest zero.


Co powoduje problem? Jak mona mu zaradzi?
2. Maksymalna dugo danych, jakie mona zaszyfrowa algorytmami w rodzaju

RSA czy El Gamala, jest z reguy ograniczona dugoci klucza, dodatkowo


pomniejszon o ewentualne bajty dopenienia. W jaki sposb mona wykorzysta
jeden z tych algorytmw w procesie szyfrowania danych o dowolnej dugoci?
3. Uzgadnianie klucza tym si rni od wymiany klucza, e pozwala komunikujcym

si stronom niezalenie wyliczy ten sam klucz. Co trzeba doda do kadej


procedury uzgadniania klucza, by bya ona bezpieczna?
4. We wczeniejszym rozdziale zobaczylimy, e moliwe jest uwierzytelnianie

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?

You might also like