Haszowanie kryptograficzne za pomocą MessageDigest
Proszę haszować hasła za pomocą SHA-256, dodawać sól, aby zapobiegać atakom z użyciem tęczowych tablic, oraz bezpiecznie porównywać skróty.
Haszowanie kryptograficzne za pomocą MessageDigest to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Czym jest kryptograficzna funkcja skrótu?
Kryptograficzna funkcja skrótu odwzorowuje dane o dowolnym rozmiarze na skrót o stałym rozmiarze. Jest jednokierunkowa (nie można jej odwrócić), deterministyczna i odporna na kolizje. Stosuje się ją do przechowywania haseł, zapewniania integralności danych i tworzenia podpisów cyfrowych.
API MessageDigest
MessageDigest.getInstance("SHA-256") zwraca obiekt obliczający skrót SHA-256. Wywołanie digest(bytes) oblicza skrót i zwraca tablicę bajtów.
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest("password123".getBytes(StandardCharsets.UTF_8));
String hex = HexFormat.of().formatHex(hash);
System.out.println(hex); // 64-char hex stringPopularne algorytmy
JDK obsługuje następujące algorytmy: MD5 (złamany — nigdy do zastosowań związanych z bezpieczeństwem), SHA-1 (przestarzały ze względów bezpieczeństwa), SHA-256 (standardowy), SHA-512 (silniejszy), SHA3-256 (nowoczesny). W nowym kodzie należy zawsze używać SHA-256 lub SHA-512.
MessageDigest.getInstance("SHA-256"); // recommended
MessageDigest.getInstance("SHA-512"); // stronger
MessageDigest.getInstance("SHA3-256"); // SHA-3 family
// Never: MessageDigest.getInstance("MD5"); // cryptographically brokenKonwersja skrótu na ciąg szesnastkowy
Wynikowa tablica bajtów musi zostać przekonwertowana na ciąg szesnastkowy w celu przechowywania lub porównania. Należy użyć HexFormat (Java 17+) albo String.format w starszych wersjach.
// Java 17+:
String hex = HexFormat.of().formatHex(hash);
// Older Java:
StringBuilder sb = new StringBuilder();
for (byte b : hash) sb.append(String.format("%02x", b));
String hex = sb.toString();Sól: sposób na tęczowe tablice
Tęczowa tablica mapuje popularne hasła na ich skróty. Dodanie soli polega na dodaniu losowej wartości do każdego hasła przed obliczeniem skrótu, dzięki czemu tęczowe tablice stają się bezużyteczne. Sól należy przechowywać razem ze skrótem.
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] hash = md.digest("password".getBytes(StandardCharsets.UTF_8));
// Store: Base64.encode(salt) + ":" + Base64.encode(hash)Haszowanie haseł — zamiast tego należy użyć BCrypt
SHA-256 z solą jest lepszy niż zwykły SHA-256, ale nadal działa szybko — atakujący może wykonywać miliardy prób na sekundę. W przypadku haseł należy używać powolnej funkcji KDF: BCryptPasswordEncoder (Spring Security), PBKDF2 lub Argon2.
// Spring Security — proper password hashing:
PasswordEncoder encoder = new BCryptPasswordEncoder(12); // cost factor 12
String hash = encoder.encode("password123");
boolean match = encoder.matches("password123", hash); // trueSprawdzanie integralności plików
Obliczenie skrótu pliku pozwala wykryć jego uszkodzenie lub modyfikację. Należy porównać obliczony skrót ze znaną, prawidłową wartością referencyjną.
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (InputStream is = Files.newInputStream(Path.of("app.jar"))) {
byte[] buf = new byte[8192];
int n;
while ((n = is.read(buf)) > 0) md.update(buf, 0, n);
}
String hash = HexFormat.of().formatHex(md.digest());
System.out.println("SHA-256: " + hash);Bezpieczne porównywanie skrótów
Należy użyć MessageDigest.isEqual() do porównania w czasie stałym, aby zapobiec atakom czasowym. Do porównywania skrótów nigdy nie należy używać String.equals().
byte[] expected = computeHash(storedHash);
byte[] actual = computeHash(inputPassword);
boolean match = MessageDigest.isEqual(expected, actual); // constant-timeWielokrotne wywołania update
Przed wywołaniem md.digest() można wielokrotnie wywołać md.update(bytes), aby obliczyć skrót danych napływających w częściach (strumieniowo).
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update("part1".getBytes());
md.update("part2".getBytes());
byte[] hash = md.digest(); // hash of "part1" + "part2"Kodowanie skrótów na potrzeby przechowywania
Skróty binarne można przechowywać jako Base64 (krótsze niż zapis szesnastkowy) lub jako ciągi szesnastkowe. Base64 używa 43 znaków dla SHA-256, a zapis szesnastkowy — 64 znaków. Oba rozwiązania są poprawne — należy zachować konsekwencję.
byte[] hash = md.digest(data);
String base64 = Base64.getEncoder().encodeToString(hash); // 44 chars (with padding)
String hex = HexFormat.of().formatHex(hash); // 64 charsDigestInputStream w strumieniowaniu
Należy opakować InputStream w DigestInputStream, aby obliczać skrót podczas odczytu — pozwala to uniknąć wczytywania całego pliku do pamięci.
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (DigestInputStream dis = new DigestInputStream(Files.newInputStream(path), md)) {
dis.transferTo(OutputStream.nullOutputStream());
}
String hash = HexFormat.of().formatHex(md.digest());Szybkie sprawdzenie
Dlaczego SHA-256 z solą nadal nie jest zalecany do przechowywania haseł?
Podsumowanie
Do sprawdzania integralności danych należy używać MessageDigest.getInstance("SHA-256"). Hasła zawsze powinny być solone, ale do ich przechowywania należy używać BCrypt lub Argon2, a nie czystego SHA. Aby zapobiec atakom czasowym, należy stosować porównanie w czasie stałym (MessageDigest.isEqual).
Często zadawane pytania
Czy lekcja „Haszowanie kryptograficzne za pomocą MessageDigest” jest bezpłatna?
Tak — pełny tekst „Haszowanie kryptograficzne za pomocą MessageDigest” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Haszowanie kryptograficzne za pomocą MessageDigest”?
Proszę haszować hasła za pomocą SHA-256, dodawać sól, aby zapobiegać atakom z użyciem tęczowych tablic, oraz bezpiecznie porównywać skróty. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Haszowanie kryptograficzne za pomocą MessageDigest”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Haszowanie kryptograficzne za pomocą MessageDigest
- Szyfrowanie symetryczne AES
- HMAC-SHA256 na potrzeby integralności wiadomości
- JWT: struktura, tworzenie i weryfikacja