Wprowadzenie do kryptografii w Javie
JCA, czyli Java Cryptography Architecture jest standardową biblioteką kryptograficzną Javy, którą chciałbym dzisiaj pokrótce przedstawić. Jeszcze w wersji 1.4 Javy, JCA niejako składało się z dwóch oddzielnych komponentów. Jednym z nich był wspomniany już JCA. Drugim komponentem był JCE, czyli Java Cryptography Extension, który stanowił rozszerzenie JCA. Począwszy natomiast od JDK w wersji 5.0 (1.5), JCE stało się integralną częścią JCA i w takim właśnie rozszerzonym zakresie zamierzam JCA się tutaj zajmować.
Naszą przygodę rozpoczniemy od wprowadzenia do podstawowych możliwości biblioteki spoglądając na nią w bardziej abstrakcyjny sposób. Zaledwie kilka z możliwości, jakie dostarcza nam JCA to:
- bezpieczne generatory liczb pseudolosowych
- szyfrowanie (symetryczne/asymetryczne)
- funkcje skrótów (ang. hash functions)
- podpisy cyfrowe
- certyfikaty elektroniczne
- generacja i zarządzanie kluczami
W większości typowych przypadków nie będzie konieczne korzystanie z możliwości, które nie zostały wymienione powyżej.
Z punktu widzenia architektonicznego biblioteka była przygotowana z myślą o jej łatwym rozszerzeniu o nowe algorytmy oraz ich implementacje. Dzięki wprowadzeniu komponentu Provider (dalej również: dostawca) udało się uzyskać wysoki poziom niezależności pomiędzy samymi algorytmami jak i ich konkretnymi implementacjami. Provider odpowiedzialny jest za dostarczenie (poprzez mechanizm SPI) specjalizowanego silnika (ang. engine) kryptograficznego dla w/w czynności. Dokumentacja JCA wyróżnia wiele engine‘ów i w tym między innymi SecureRandom, MessageDigest, Signature. Po nazwie klasy w dosyć łatwy sposób można się zorientować do jakich celów służy konkretny silnik. Provider sam decyduje które czynności kryptograficzne wspomaga, a co za tym idzie, których silników będzie dostarczał.
Funkcjonalność kryptograficzną biblioteki można rozszerzyć poprzez instalację nowych dostawców kryptograficznych dostarczanych przez firmy trzecie lub też, jeżeli jest to zupełnie konieczne, napisanie własnego Provider‘a. Dużą zaletą przyjętej architektury jest fakt, że dostawca nie musi implementować wszystkich dostępnych algorytmów, lecz może się jedynie skupić na algorytmach z jego punktu widzenia interesujących. W momencie zgłoszenia zapotrzebowania na wykorzystanie konkretnego algorytmu kryptograficznego, zarejestrowany dostawca jest odpytywany w celu ustalenia, czy implementuje on tenże algorytm. W wypadku istnienia wielu dostawców obsługujących ten sam algorytm możemy pozwolić JCA wybrać najbardziej właściwego z nich lub też określić go w sposób bezpośredni.
Lista providerów kryptograficznych Sun załączana do biblioteki standardowej Javy dostarcza relatywnie bogatego zbioru gotowych do użycia silników. Zainteresowanych kompletnym opisem ich możliwości zapraszam do zapoznania się z bibliografią.
Przejdziemy teraz do praktycznego wykorzystania JCA omawiając kilka typowych scenariuszy jego użycia.
Generacja bezpiecznych ciągów liczb pseudolosowych
Uzyskiwanie instancji silnika klasy SecureRandom
Uzyskanie konkretnej implementacji silnika kryptograficznego wymaga od nas skorzystania z jednej z dostarczonych statystycznych metod fabrykujących getInstance() klasy konkretnego silnika.
W przypadku klasy SecureRandom mamy do dyspozycji poniższe metody:
static SecureRandom getInstance(String algorithm) static SecureRandom getInstance(String algorithm, Provider provider) static SecureRandom getInstance(String algorithm, String provider)
Informacja, jaką zawsze musimy dostarczyć do metody getInstance to nazwa algorytmu, jaki chcemy wykorzystywać. Dodatkowo możemy także wymusić użycie implementacji dostarczonej przez konkretnego Provider‘a. W wypadku nieodnalezienia algorytmu spełniającego określone warunki rzucony zostanie wyjątek. Uzyskiwanie instancji w przypadku pozostałych silników wygląda podobnie, dlatego też w dalszej części tego artykułu będę jedynie zwracał uwagę na pewne subtelne różnice.
W JDK w wersji 6 dla silnika SecureRandom standardowo dostarczony jest tylko jeden algorytm (SHA1PRNG). Dostęp do niego można uzyskać na jeden z dwóch przedstawionych poniżej sposobów.
SecureRandom.getInstance("SHA1PRNG");
new SecureRandom();
W przypadku innych silników najczęściej zmuszeni jesteśmy do korzystania z metody getInstance.
Przykład
W przykładzie zainicjalizujemy silnik 1024 bitowym seed‘em oraz wygenerujemy 5 liczb typu long, które następnie wypiszemy na konsolę.
secureRandom.setSeed(secureRandom.generateSeed(128));
for (int i = 0; i < 5; i++) {
System.out.println(secureRandom.nextLong());
}
Jak widać korzystanie z klasy SecureRandom nie różni się znacznie od korzystania z klasy Random. Wynika to także z faktu, że pierwsza z nich jest klasą pochodną drugiej.
Motywacja
Dlaczego wykorzystywać java.security.SecureRandom zamiast po prostu java.util.Random? Korzystanie z java.util.Random daje możliwość łatwego zsynchronizowania dwóch instancji generatora w taki sposób, aby zwracały one te same ciągi losowe. Jeżeli wykorzystujemy nasz generator np. do generowania haseł, to osoba, której udałoby się zsynchronizować swój generator z naszym byłaby w stanie poznać wszystkie wygenerowane do tej pory przez nas hasła, jak i wszystkie hasła, które jeszcze w przyszłości wygenerujemy. Powyższy przykład powinien być wystarczająco przekonywający, że korzystanie ze zwykłego generatora w warstwie, gdzie istotne jest bezpieczeństwo nie powinno mieć nigdy miejsca. Czytelników zainteresowanych zgłębieniem tego tematu zapraszam tutaj.
Klasa SecureRandom posiada także bardziej rozbudowany interfejs w stosunku do klasy Random, co może stanowić kolejny powód dla którego warto się jej bliżej przyjrzeć.
Generowanie skrótu wiadomości
Technikę generowania skrótu wiadomości (ang. message digest) wykorzystuje się w bardzo szerokim zakresie. Jest ona podstawą wielu algorytmów kryptograficznych. Aby uzyskać skrót wiadomości wykorzystuje się funkcję skrótu (ang. hash function).
Niech f będzie taką funkcją, że przyjmuje na wejściu ciąg dowolnej długości, ale zawsze zwraca ciąg o tej samej, ustalonej długości. Oraz załóżmy, że h = f(m), gdzie m jest wiadomością, a h jest jej skrótem.
f nazwiemy bezpieczną funkcją skrótu jeżeli:
- dla dowolnego h trudno jest znaleźć takie m, że h = f(m)
- dla dowolnego m trudno jest znaleźć takie m’, że f(m) = f(m’)
W powyższych dwóch warunkach poprzez trudno znaleźć uznawana jest sytuacja, kiedy algorytm odnajdywania ma wystarczająco dużą złożoność obliczeniową (potencjalnie wykładniczą). Ze względu na brak formalnej definicji dochodzi do nieporozumień. Funkcja MD5 uważana, jeszcze do niedawna za bezpieczną, jak udowodniono nie spełnia drugiego z w/w warunków. Podobne wątpliwości pojawiają się w stosunku do funkcji SHA.
Przykład
Jako przykładowe wykorzystanie klasy MessageDigest policzymy wartość funkcji skrótu MD5 dla łańcucha znaków JAVA RULEZ!.
package pl.j2ee.example.jca;
public class MessageDigestExample {
public static void main(String[] args)
throws IOException, NoSuchAlgorithmException {
// wejściowy ciąg znaków
final String inputString = "JAVA RULEZ!";
// uzyskiwanie instancji silnika skrótu wiadomości
final MessageDigest messageDigest = MessageDigest
.getInstance("MD5");
// wyliczanie MD5 dla tekstu "JAVA RULEZ!"
final InputStream inputStream = new ByteArrayInputStream(
inputString.getBytes());
final byte[] buffer = new byte[4];
int bufferLength;
while ((bufferLength = inputStream.read(buffer)) != -1) {
System.out.println("messageDigest.update(\""
+ new String(buffer, 0, bufferLength) + "\")");
messageDigest.update(buffer, 0, bufferLength);
}
System.out.println("messageDigest.digest()");
final byte[] digest = messageDigest.digest();
// wypisywanie wyniku w czytelnej postaci
final Formatter formatter = new Formatter();
for (final byte digestByte : digest) {
formatter.format("%02x", digestByte);
}
System.out.println("digest: " + formatter.toString());
}
}
Dla skomplikowania przykładu i pokazania standardowego sposobu korzystania z klasy MessageDigest przetwarzanie (tak jak w większości rzeczywistych przypadków) zostało rozdzielone na kilka faz. W pierwszej z nich dostarczamy kolejne partie wiadomości przy pomocy metody update, aż do skompletowania całości. Następnie korzystając z metody digest wyliczamy wartość skrótu wiadomości. Wyliczona wartość ma postać tablicy bajtów.
Po zakończeniu działania aplikacji wydruk na konsoli powinien wyglądać identycznie do zaprezentowanego poniżej.
messageDigest.update("JAVA")
messageDigest.update(" RUL")
messageDigest.update("EZ!")
messageDigest.digest()
digest: 3bb058a89a01a8f087177b2f05962598
Motywacja
Istnieje wiele powodów do korzystania ze skrótu wiadomości. Jednym z najbardziej podstawowych zastosowań jest sprawdzenie integralności wiadomości. Znając skrót wiadomości możemy w bardzo łatwy sposób sprawdzić, czy otrzymana wiadomość jest tą samą wiadomością, której skrót posiadamy. Często zamieszczone w internecie pliki posiadają swój skrót, by można było zweryfikować, czy w czasie ściągania ich nie nastąpiło żadne przekłamanie.
Pośrednio skróty wiadomości stosowane są także w procesie podpisywania cyfrowego oraz weryfikacji tego podpisu.
Ogólnie rzecz biorąc prostota korzystania z funkcji skrótów oraz możliwości, jakie umożliwiają spowodowały stosowanie ich na bardzo szeroką skalę. Trudno byłoby wymienić tutaj wszystkie znane zastosowania.
Szyfrowanie informacji
Wprowadzenie do szyfrowania
Algorytmy szyfrujące dzieli się na dwa podstawowe rodzaje:
- symetryczne
- w procesie szyfrowania, jak i deszyfracji wykorzystywany jest ten sam, tajny klucz
- do poprawnej i bezpiecznej komunikacji wymagane jest by klucz był w posiadaniu komunikujących się stron i nikogo więcej
- asymetryczne
- w procesie deszyfracji wykorzystywany jest klucz prywatny
- w procesie szyfrowania wykorzystywany jest (powstały na podstawie klucza prywatnego) klucz publiczny
- klucz prywatny zna jedynie strona uprawniona do odebrania zaszyfrowanej wiadomości
- klucz publiczny podawany jest do publicznej wiadomości
- z reguły zdecydowanie wolniejsze od algorytmów symetrycznych
- często wykorzystywane w połączeniu z algorytmami symetrycznymi; mogą służyć do ustalenia bezpiecznego kanału do przesłania klucza symetrycznego
Więcej informacji na temat podstaw kryptografii można uzyskać tutaj.
Generacja kluczy
JCA dostarcza bogatego API służącego do generacji kluczy (zarówno tych symetrycznych, jak i asymetrycznych).
Generacja kluczy symetrycznych
final KeyGenerator keyGenerator = KeyGenerator
.getInstance("Blowfish");
keyGenerator.init(256);
final SecretKey secretKey = keyGenerator.generateKey();
Powyżej przedstawiony został najprostszy sposób generacji 256 bitowego klucza algorytmu Blowfish. Mimo widocznej tutaj prostoty JCA dostarcza bogatych mechanizmów pozwalających na zaawansowane dostrojenie parametrów generacji kluczy. Możliwość dostrojenia konfiguracji tyczy się nie tylko aspektu generowania kluczy, ale także pozostałych elementów stanowiących JCA.
Generacja kluczy asymetrycznych
Generacja kluczy asymetrycznych jest bardzo podobna do generacji klucza symetrycznego. W tym przypadku wynikiem generacji nie jest SecretKey, a KeyPair zawierający w sobie klucz publiczny i klucz prywatny.
final KeyPairGenerator generator = KeyPairGenerator
.getInstance("RSA");
final KeyPair keyPair = generator.generateKeyPair();
final PublicKey publicKey = keyPair.getPublic(); //klucz publiczny
final PrivateKey privateKey = keyPair.getPrivate(); //klucz prywatny
Przykład szyfrowania z wykorzystaniem klucza symetrycznego
package pl.j2ee.example.jca;
public class SecretKeyCipher {
private final SecretKey secretKey;
public SecretKeyCipher(SecretKey secretKey) {
this.secretKey = secretKey;
}
public void process(int opMode, InputStream input,
OutputStream output) throws IOException,
NoSuchAlgorithmException, NoSuchPaddingException,
IllegalBlockSizeException, BadPaddingException,
InvalidKeyException {
final Cipher cipher = Cipher.getInstance(this.secretKey
.getAlgorithm());
cipher.init(opMode, this.secretKey);
final byte[] inputBuffer = new byte[cipher
.getBlockSize()];
int bufferLength;
while ((bufferLength = input.read(inputBuffer)) != -1) {
output.write(cipher.update(inputBuffer, 0,
bufferLength));
}
output.write(cipher.doFinal());
}
}
Zaimplementowana przykładowa klasa SecretKeyCipher pobiera jako argument tryb pracy, strumień wejściowy oraz strumień wyjściowy. Godny zauważenia jest fakt, że zarówno szyfrowanie, jak i deszyfrowanie przebiega w ten sam sposób. Możliwe tryby pracy dla klasy Cipher to odpowiednio:
- Cipher.ENCRYPT_MODE dla szyfrowania
- Cipher.DECRYPT_MODE dla deszyfrowania
Poniżej zamieszczony został test JUnit‘y w prosty sposób sprawdza poprawność implementacji. Przy okazji pokazuje on także w jaki sposób można wykorzystać zaimplementowaną klasę kryptograficzną.
package pl.j2ee.example.jca;
import org.apache.commons.lang.RandomStringUtils; // commons-lang
public class SecretKeyEncoderTest extends TestCase {
public void testProcess() throws NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException, IOException, InvalidKeyException {
final SecretKey secretKey = KeyGenerator.getInstance(
"Blowfish").generateKey();
final SecretKeyCipher keyEncoder = new SecretKeyCipher(
secretKey);
final String openText = RandomStringUtils
.randomAscii(4096);
System.out.println("OpenText: " + openText);
final byte[] encoded;
final String decoded;
{
final InputStream openTextAsInputStream = new ByteArrayInputStream(
openText.getBytes());
final ByteArrayOutputStream cipherTextAsOutputStream = new ByteArrayOutputStream();
keyEncoder.process(Cipher.ENCRYPT_MODE,
openTextAsInputStream, cipherTextAsOutputStream);
encoded = cipherTextAsOutputStream.toByteArray();
}
System.out.println("CipherText: "
+ Arrays.toString(encoded));
{
final InputStream cipherTextInputStream = new ByteArrayInputStream(
encoded);
final ByteArrayOutputStream openTextAsOutputStream = new ByteArrayOutputStream();
keyEncoder.process(Cipher.DECRYPT_MODE,
cipherTextInputStream, openTextAsOutputStream);
decoded = new String(openTextAsOutputStream
.toByteArray());
}
System.out.println("DecodedText: " + decoded);
assertEquals(openText, decoded);
}
}
Należy także zwrócić uwagę, że przedstawiona implementacja klasy szyfrującej/deszyfrującej nie jest optymalna w odniesieniu do strumieni, gdyż w bibliotece JCA istnieją już pomocnicze klasy CipherInputStream oraz CipherOutputStream wspomagające przetwarzanie strumieniowe.
Wymiana klucza symetrycznego przy wykorzystaniu algorytmu asymetrycznego
Szyfrowanie z wykorzystaniem klucza asymetrycznego wykorzystuje się znacznie rzadziej (biorąc pod uwagę kryterium ilości przesłanych danych). Ze względu na swoją słabą wydajność nie może, a przynajmniej nie powinno być stosowane do szyfrowania dużych partii danych. Często wykorzystywanym schematem podczas korzystania z szyfrowania asymetrycznego jest ustanowienie (na możliwie krótki czas) bezpiecznego kanału poprzez który zostanie wymieniony pomiędzy komunikującymi się stronami klucz algorytmu symetrycznego. Od tego momentu dalsza część komunikacji odbywa się już wyłącznie przy wykorzystaniu właśnie wymienionego klucza symetrycznego. Algorytmy szyfrowania asymetrycznego traktowane są jako pełnoprawni konkurenci specjalizowanych algorytmów do wymiany kluczy (np. Diffie-Hellman).
Szyfrowanie asymetryczne z wykorzystaniem JCA w dużym stopniu przypomina szyfrowanie symetryczne. Należy jednak pamiętać o pewnych znaczących różnicach:
- do generacji kluczy wykorzystywany jest KeyPairGenerator generujący parę kluczy (prywatny i publiczny)
- do szyfrowania wykorzystywany jest klucz publiczny
- do deszyfrowania wykorzystywany jest klucz prywatny
Podpis elektroniczny
Ostatnim algorytmem, jaki mam zamiar opisać jest podpis elektroniczny. Stanowi on niejako rozszerzenie możliwości skrótu wiadomości. Jego podstawowe cechy to:
- integralność – zapewnienie, że odebrana wiadomość jest tą samą wiadomością, która została wysłana
- niezaprzeczalność – zapewnia, że wiadomość została utworzona przez osobę, która identyfikuje się jako autor
Jednym z problemów jakich nie rozwiązuje elektroniczny podpis samoczynnie jest kwestia powtórzonych wiadomości. Osoba, która wejdzie w posiadania wiadomości oraz jej podpisu jest w stanie wysłać wiadomość w niezmienionej formie ponownie podszywają się pod jej prawdziwego autora. Aby rozwiązać ten problem często do właściwej wiadomości dopisuje się jej unikalny identyfikator, który zapewnia, że żadne 2 wiadomości posiadające tą samą treść nie będą posiadały tego samego podpisu cyfrowego. Strona odbierająca tak opatrzoną wiadomość o tej samej wartości podpisu (o tym samym identyfikatorze wiadomości) będzie w stanie natychmiastowo taką wiadomość zignorować.
Teoretyczne podstawy podpisu elektronicznego
W podpisie elektronicznym wykorzystywany jest skrót wiadomości oraz szyfrowanie asymetryczne. Kolejne kroki, jakie należy wykonać, by uzyskać podpis wiadomości, to:
- wyliczenie skrótu wiadomości przy wykorzystaniu ustalonej funkcji skrótu
- deszyfracja uzyskanego skrótu za pomocą klucza prywatnego
Uzyskana wartość jest nazywana podpisem cyfrowym wiadomości. Do weryfikacji podpisu cyfrowego wymagane jest wykorzystanie klucza publicznego. Kolejne kroki, jakie należy wykonać, to:
- wyliczenie skrótu wiadomości przy wykorzystaniu ustalonej funkcji skrótu
- zaszyfrowanie podpisu cyfrowego przy pomocy klucza publicznego
- równość dwóch powyższych wartości oznacza poprawne zweryfikowanie podpisu elektrowniczego
Powyższy algorytm jest skuteczny, gdyż, aby móc utworzyć podpis cyfrowy wymagana jest znajomość klucza prywatnego. Nie ma możliwości wygenerowania podpisu cyfrowego na podstawie wyliczonego skrótu wiadomości oraz klucza publicznego.
Ilustrację przedstawionej koncepcji można znaleźć tutaj.
Tworzenie podpisu cyfrowego wiadomości w JCA
- uzyskanie implementacji silnika klasy Signature przy pomocy statycznej metody fabrykującej getInstance(…)
- zainicjalizowanie obiekty klasy Signature przy pomocy klucza prywatnego i metody initSign(PrivateKey privateKey)
- dostarczenie podpisywanej wiadomości przy pomocy metody update(…)
- wyliczenie wartości podpisu cyfrowego przy pomocy metody sign()
Weryfikacja podpisu cyfrowego wiadomości w JCA
- uzyskanie implementacji silnika klasy Signature przy pomocy statycznej metody fabrykującej getInstance(…)
- zainicjalizowanie obiekty klasy Signature przy pomocy klucza prywatnego i metody initVerify(PublicKey publicKey)
- dostarczenie podpisanej wiadomości (bez podpisu) przy pomocy metody update(…)
- weryfikacja podpisu cyfrowego przy pomocy metody verify(…) dostarczając jej jako argument podpis cyfrowy wiadomości
- zwrócona wartość true oznacza poprawne zweryfikowanie podpisu cyfrowego
Podsumowanie
JCA jest na tyle bogatą biblioteką, że opisanie dokładnie jej możliwości wymaga nie lada trudu i czasu. Z drugiej strony mało komu z nas przyjdzie bezpośrednio z niej korzystać. Wynika to z faktu, że JCA jest rozwiązaniem (jak na Javę) bardzo niskiej warstwy. Najczęściej korzystamy z jej możliwości kryptograficznych w sposób pośredni przy wykorzystaniu narzędzi oraz bibliotek wyższej warstwy. Mimo to uważam, że podstawowa znajomość możliwości kryptograficznym na stosunkowo podstawowym poziomie jest wartościowa.
Jestem świadom, że artykuł ten przedstawia jedynie bardzo wąski przekrój poprzez możliwości JCA. Ubolewam nad tym, że nie udało mi się zaprezentować pełni możliwości opisywanej biblioteki tym bardziej, że wiele ważnych aspektów ze względu na niedobór czasu musiałem po prostu przemilczeć.
Mam jednak nadzieję, że mimo swojego ubóstwa artykuł ten dla każdego z Was okaże się w pewien sposób pomocny.
Uwagi końcowe
W zaprezentowanych przykładach zastosowano pewne uproszczenia pozwalające na skupienie uwagi czytelnika na meritum problemu. Przy implementowaniu rzeczywistych rozwiązań nie należy między innymi stosować:
- zaniedbywania poprawnej obsługi wyjątków
- wypisywania informacji logowania bezpośrednio na konsolę
- zamieszczania logiki aplikacji bezpośrednio w metodzie main()
- wykorzystywania bardzo krótkich buforów danych
- bezmyślnego wykorzystywania domyślnych wartość inicjalizacyjnych dla klas silników kryptograficznych
Bibliografia
- Cryptography
- JCA Reference Guide
- JCA Sun Providers Architecture
- Ruining security with java.util.Random
- Cryptographic hash function
- Funkcja skrótu (ang. hash function)
- MD5
- SHA hash function
- Cryptanalysis of MD5 and SHA: Time for a new standard
- Secret key exchange
- Digital Signature
- Formatting Output With the New Formatter and Using Static Imports for Constants and Methods Tech Tips
- Replaceable Components and the Service Provider Interface
- Factory Method pattern








October 8th, 2007 at 14:08
Kurczę, jak tak patrzę na ilość kodu potrzebnego na wyprodukowanie hasha md5 to zaczynam się zastanawiać gdzie tu wygoda.. w starym poczciwym PHP miałem od tego jedną funkcję i załatwiałem to w jednej linii:
md5(’java rulez’)
W Javie muszę wyprodukować masę kodu, do tego bawić się z tablicami i pętlami… (nie wnikam na ile słuszna) komplikacja.
October 8th, 2007 at 20:36
We fragmencie dotyczącym wyliczania skrótów wiadomości wyraźnie zaznaczyłem, że moim celem było przedstawienie bardziej ogólnego schematu korzystania z klasy MessageDigest.
Mamy tutaj do czynienia z przetwarzaniem strumienia wejściowego, którego potencjalna długości może nam nie być znana. Dlatego też traktujemy napis tak jakbyśmy nie znali jego długości i z góry zakładali, że może on być bardzo długi (nawet kilka terabajtów).
W przypadku, gdy długość danych jest znana i stosunkowo krótka wyliczenie skrótu możemy dokonać w jednej linijce kodu:
MessageDigest.getInstance(”MD5″).digest(”JAVA RULEZ!”.getBytes());
Przyznaje, że być może od tego powinienem właśnie zacząć :)
October 10th, 2007 at 19:27
Funkcje skrótu pracują na tablicach bajtów a nie znaków. W przypadku md5(’java rulez’) nie ma problemu, ale np. dla md5(’ąćęłńóśżźĄĆĘŁŃÓŚŻŹ’) wynik funkcji skrótu będzie inny dla UTF-8, inny dla iso-8859-2 itd. Stąd dodatkowe konstrukcje w javie.
December 11th, 2007 at 17:33
Gratuluje świetnego artu, trafiłem do was dzisiaj szukając info o MD5, a tu przy okazji dowiaduję się o SecureRandom – dzięki wam o jeden błąd mniej:)
No ale a propos MD5 – jak z byte[] zwracanego przez digest(”JAVA RULEZ!”.getBytes()) zrobić String’a? Wiem że można new String(bytes[] bytes) ale to chyba nie jest to – w wyniku otrzymuje coś w stylu ‘)???;,???l??En?%T?’.
December 11th, 2007 at 18:17
Udało mi się znaleźć jakieś rozwiązanie, oto ono:
StringBuilder stringBuilder =
new StringBuilder();
for (byte b : MessageDigest.getInstance(”SHA”).
digest(message.getBytes())) {
stringBuilder.append(Integer.toHexString(b & 0xff));
}
return stringBuilder.toString();
January 11th, 2008 at 11:45
Aby md5 z javy było czyste i przejrzyste jak w php …
ja zrobiłem coś takiego:
public static String md5(String text){
String ret = “”;
try{
MessageDigest mdAlgorithm = MessageDigest.getInstance(”MD5″);
mdAlgorithm.update(text.getBytes());
byte[] digest = mdAlgorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < digest.length; i++){
text = Integer.toHexString(0xFF & digest[i]);
if (text.length() < 2){
text = “0″ + text;
}
hexString.append(text);
}
ret = hexString.toString();
}
catch(NoSuchAlgorithmException e) {}
return ret;
}
January 19th, 2008 at 21:48
“Mam jednak nadzieję, że mimo swojej _ubogości_ artykuł ten dla każdego z Was okaże się w pewien sposób pomocny.”
Błagam, “ubóstwa”!
January 20th, 2008 at 12:24
Na całe szczęście nie jest to Rada Gabinetowa… ale dziękuję za poprawkę :)
February 2nd, 2008 at 21:19
“Podobne wątpliwości pojawiają się w stosunku do funkcji SHA.”
Kryptolodzy Wang, Yin i Yu opracowali atak na funkcję SHA-1 zmniejszający złożoność znalezienia kolizji z 2^80 do 2^63. Nie dotyczy to całej rodziny funkcji SHA (na przykład nowszej SHA-2) a jedynie najpopularniejszej z nich i będącej jeszcze standardem SHA-1.
March 3rd, 2008 at 13:09
Witam,
Pytanko do autora artykułu i ewentualnych czytelników: czy widzieliście gdzieś jakieś informacje na temat podpisu elektronicznego oraz współpracy z certyfikatami w przeglądarce i kluczami na kartach?
Chodzi mi o biblioteki (klasy JCA?) które umożliwiałyby apletowi na stronie WWW odpytanie przeglądarki o zainstalowane certyfikaty, a następnie podpisanie zawartości formularza przy pomocy podpisu elektronicznego na karcie…
Pozdrawiam,
Marek.
March 10th, 2008 at 16:22
hmm.. dołączam się do powyższego pytania, bo udalo mi sie znalezc jedynie takie podstawowe info na ten temat: http://www.certum.pl/certum/cert,oferta_kwalifikowany_DVCS.xml