Digitalni potpisi na Javi

Java Top

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ

1. Pregled

U ovom uputstvu naučit ćemo o Mehanizam digitalnog potpisa i kako ga možemo implementirati pomoću Java Cryptography Architecture (JCA). Istražit ćemo KeyPair, MessageDigest, Cipher, KeyStore, Certificate, i Potpis JCA API-ji.

Počet ćemo s razumijevanjem što je to Digital Signature, kako generirati par ključeva i kako ovjeriti javni ključ od tijela za izdavanje certifikata (CA). Nakon toga vidjet ćemo kako implementirati Digital Signature pomoću JCA API-ja na niskoj i visokoj razini.

2. Što je digitalni potpis?

2.1. Definicija digitalnog potpisa

Digitalni potpis tehnika je koja osigurava:

  • Integritet: poruka nije promijenjena tijekom prijenosa
  • Autentičnost: autor poruke zapravo je onaj za koga tvrde da je
  • Neodbacivanje: autor poruke ne može kasnije poreći da su oni izvor

2.2. Slanje poruke s digitalnim potpisom

Tehnički gledano, adigitalni potpis je šifrirani hash (sažetak, kontrolna suma) poruke. To znači da iz poruke generiramo hash i šifriramo ga privatnim ključem prema odabranom algoritmu.

Tada se šalju poruka, šifrirani hash, odgovarajući javni ključ i algoritam. Ovo je klasificirano kao poruka s digitalnim potpisom.

2.3. Primanje i provjera digitalnog potpisa

Da bi provjerio digitalni potpis, primatelj poruka generira novo raspršivanje iz primljene poruke, dešifrira primljeno šifrirano raspršivanje pomoću javnog ključa i uspoređuje ih. Ako se podudaraju, navodno je potvrđen Digitalni potpis.

Trebali bismo imati na umu da šifriramo samo hash poruke, a ne i samu poruku. Drugim riječima, Digital Signature ne pokušava zadržati poruku u tajnosti. Naš digitalni potpis samo dokazuje da poruka nije promijenjena tijekom prijenosa.

Kada je potpis provjeren, sigurni smo da je samo vlasnik privatnog ključa mogao biti autor poruke.

3. Digitalni certifikat i identitet javnog ključa

Potvrda je dokument koji pridružuje identitet danom javnom ključu. Potvrde potpisuje entitet treće strane koji se naziva Certificate Authority (CA).

Znamo da ako se hash koji dešifriramo objavljenim javnim ključem podudara sa stvarnim hashom, tada je poruka potpisana. Međutim, kako znamo da je javni ključ doista došao iz pravog entiteta? To se rješava uporabom digitalnih certifikata.

Digitalni certifikat sadrži javni ključ i sam ga potpisuje drugi entitet. Potpis tog entiteta može sam provjeriti drugi entitet i tako dalje. Na kraju imamo ono što nazivamo lancem certifikata. Svaki glavni entitet certificira javni ključ sljedećeg entiteta. Entitet najviše razine je samopotpisan, što znači da je njegov javni ključ potpisan vlastitim privatnim ključem.

X.509 je najčešće korišteni format certifikata i isporučuje se u binarnom formatu (DER) ili u obliku teksta (PEM). JCA već pruža primjenu za to putem X509 Potvrda razred.

4. KeyPair upravljanje

Budući da Digital Signature koristi privatni i javni ključ, koristit ćemo JCA klase PrivateKey i PublicKey za potpisivanje i provjeru poruke.

4.1. Dobivanje KeyPaira

Da biste stvorili par ključeva privatnog i javnog ključa, koristit ćemo Javu alat za ključeve.

Generirajmo par ključeva pomoću genkeypair naredba:

keytool -genkeypair -alias senderKeyPair -keyalg RSA -keyysize 2048 \ -dname "CN = Baeldung" -validity 365 -storetype PKCS12 \ -keystore sender_keystore.p12 -storepass changeit

Ovo stvara privatni ključ i odgovarajući javni ključ za nas. Javni ključ umotan je u samopotpisanu potvrdu X.509 koja je pak umotana u lanac certifikata s jednim elementom. Lanac certifikata i privatni ključ pohranjujemo u datoteku Keystore sender_keystore.p12, koju možemo obraditi pomoću API-ja KeyStore.

Ovdje smo koristili format pohrane ključeva PKCS12, jer je standardan i preporučuje se u odnosu na Java zaštićeni format JKS. Također, trebali bismo se sjetiti lozinke i zamjenskog imena, jer ćemo ih koristiti u sljedećem pododjeljku prilikom učitavanja datoteke Keystore.

4.2. Učitavanje privatnog ključa za potpisivanje

Da bismo potpisali poruku, potreban nam je primjerak PrivateKey.

Koristiti KeyStore API i prethodna datoteka Keystore, sender_keystore.p12, možemo dobiti a PrivateKey objekt:

KeyStore keyStore = KeyStore.getInstance ("PKCS12"); keyStore.load (novi FileInputStream ("sender_keystore.p12"), "changeit"); PrivateKey privateKey = (PrivateKey) keyStore.getKey ("senderKeyPair", "changeit");

4.3. Objavljivanje javnog ključa

Prije nego što možemo objaviti javni ključ, prvo moramo odlučiti hoćemo li koristiti samopotpisanu potvrdu ili CA-potvrdu.

Kada koristimo samopotpisani certifikat, trebamo ga samo izvesti iz datoteke Keystore. To možemo učiniti s exportcert naredba:

alat za ključeve -exportcert -alias senderKeyPair -storetype PKCS12 \ -keystore sender_keystore.p12 -file \ sender_certificate.cer -rfc -storepass changeit

Inače, ako ćemo raditi s CA certifikatom, tada moramo stvoriti zahtjev za potpisivanje certifikata (CSR). To radimo s certreq naredba:

keytool -certreq -alias senderKeyPair -storetype PKCS12 \ -keystore sender_keystore.p12 -file -rfc \ -storepass changeit> sender_certificate.csr

CSR datoteka, sender_certificate.csr, zatim se šalje tijelu za ovjere radi potpisivanja. Kada se to učini, primit ćemo potpisani javni ključ zamotan u certifikat X.509, bilo u binarnom (DER) ili tekstnom (PEM) formatu. Evo, koristili smo RFC opcija za PEM format.

Javni ključ koji smo dobili od CA, sender_certificate.cer, sada je potpisao CA i može se učiniti dostupnim klijentima.

4.4. Učitavanje javnog ključa za provjeru

Imajući pristup javnom ključu, prijamnik ga može učitati u svoju Keystore pomoću importcert naredba:

keytool -importcert -alias receiverKeyPair -storetype PKCS12 \ -keystore receiver_keystore.p12 -file \ sender_certificate.cer -rfc -storepass changeit

I koristeći KeyStore API kao i prije, možemo dobiti a PublicKey primjer:

KeyStore keyStore = KeyStore.getInstance ("PKCS12"); keyStore.load (novi FileInputStream ("receiver_keytore.p12"), "changeit"); Potvrda certifikata = keyStore.getCertificate ("receiverKeyPair"); PublicKey publicKey = certificate.getPublicKey ();

Sad kad imamo PrivateKey instanca na strani pošiljatelja i instanca PublicKey na strani primatelja možemo započeti postupak potpisivanja i provjere.

5. Digitalni potpis sa MessageDigest i Šifra Nastava

Kao što smo vidjeli, digitalni se potpis temelji na raspršivanju i šifriranju.

Obično koristimo MessageDigest razred sa SHA ili MD5 za raspršivanje i Šifra klasa za šifriranje.

Počnimo sada implementirati mehanizme digitalnog potpisa.

5.1. Generiranje hasha poruke

Poruka može biti niz, datoteka ili bilo koji drugi podatak. Uzmimo dakle sadržaj jednostavne datoteke:

bajt [] messageBytes = Files.readAllBytes (Paths.get ("message.txt"));

Sada, koristeći MessageDigest, iskoristimo probaviti metoda za generiranje hasha:

MessageDigest md = MessageDigest.getInstance ("SHA-256"); bajt [] messageHash = md.digest (messageBytes);

Ovdje smo koristili algoritam SHA-256, koji se najčešće koristi. Ostale alternative su MD5, SHA-384 i SHA-512.

5.2. Šifriranje generiranog hasha

Da bismo šifrirali poruku, trebaju nam algoritam i privatni ključ. Ovdje ćemo koristiti RSA algoritam. DSA algoritam je još jedna mogućnost.

Stvorimo a Šifra instance i inicijalizirajte ga za šifriranje. Tada ćemo nazvati doFinal () metoda za šifriranje prethodno raspršene poruke:

Šifra šifre = Cipher.getInstance ("RSA"); cipher.init (Cipher.ENCRYPT_MODE, privateKey); bajt [] digitalSignature = cipher.doFinal (messageHash);

Potpis se može spremiti u datoteku za kasnije slanje:

Files.write (Paths.get ("digital_signature_1"), digitalSignature);

U ovom su trenutku poslani poruka, digitalni potpis, javni ključ i algoritam, a primatelj može upotrijebiti te dijelove podataka za provjeru integriteta poruke.

5.3. Ovjera potpisa

Kad primimo poruku, moramo potvrditi njezin potpis. Da bismo to učinili, dešifriramo primljeni šifrirani heš i uspoređujemo ga s hešom koji napravimo od primljene poruke.

Pročitajmo primljeni digitalni potpis:

byte [] encryptedMessageHash = Files.readAllBytes (Paths.get ("digital_signature_1"));

Za dešifriranje izrađujemo Šifra primjer. Tada zovemo doFinal metoda:

Šifra šifre = Cipher.getInstance ("RSA"); cipher.init (Cipher.DECRYPT_MODE, publicKey); byte [] decryptedMessageHash = cipher.doFinal (encryptedMessageHash);

Zatim generiramo novo raspršivanje poruke od primljene poruke:

bajt [] messageBytes = Files.readAllBytes (Paths.get ("message.txt")); MessageDigest md = MessageDigest.getInstance ("SHA-256"); bajt [] newMessageHash = md.digest (messageBytes);

I na kraju, provjeravamo podudara li se novo generirani hash poruke s dešifriranim:

boolean isCorrect = Nizovi.equals (decryptedMessageHash, newMessageHash);

U ovom smo primjeru koristili tekstualnu datoteku message.txt da simuliramo poruku koju želimo poslati ili mjesto tijela poruke koju smo primili. Uobičajeno bismo očekivali da ćemo uz potpis dobiti i našu poruku.

6. Digitalni potpis korištenjem Potpis Razred

Do sada smo koristili API-je niske razine za izradu vlastitog postupka provjere digitalnog potpisa. To nam pomaže razumjeti kako to funkcionira i omogućuje nam da ga prilagodimo.

Međutim, JCA već nudi namjenski API u obliku Potpis razred.

6.1. Potpisivanje poruke

Da bismo započeli postupak potpisivanja, prvo stvorimo instancu Potpis razred. Da bismo to učinili, potreban nam je algoritam potpisivanja. Zatim inicijaliziramo Potpis s našim privatnim ključem:

Potpis potpisa = Signature.getInstance ("SHA256withRSA"); signature.initSign (privateKey);

Odabrani algoritam potpisivanja, SHA256s RSA u ovom primjeru, je kombinacija algoritma raspršivanja i algoritma šifriranja. Ostale mogućnosti uključuju SHA1sRSA, SHA1sDSA, i MD5s RSA, između ostalih.

Zatim nastavljamo s potpisivanjem bajt-polja poruke:

bajt [] messageBytes = Files.readAllBytes (Paths.get ("message.txt")); signature.update (messageBytes); bajt [] digitalSignature = signature.sign ();

Potpis možemo spremiti u datoteku za kasniji prijenos:

Files.write (Paths.get ("digital_signature_2"), digitalSignature);

6.2. Ovjera potpisa

Da bismo provjerili primljeni potpis, ponovno izrađujemo Potpis primjer:

Potpis potpisa = Signature.getInstance ("SHA256withRSA");

Dalje, inicijaliziramo Potpis objekt za provjeru pozivom na initVerify metoda koja uzima javni ključ:

signature.initVerify (publicKey);

Zatim, moramo dodati primljene bajtove poruke u objekt potpisa potpisom pozivajući ažuriranje metoda:

bajt [] messageBytes = Files.readAllBytes (Paths.get ("message.txt")); signature.update (messageBytes);

I na kraju, potpis možemo provjeriti pozivom na provjeriti metoda:

boolean isCorrect = signature.verify (полученSignature);

7. Zaključak

U ovom smo članku prvo pogledali kako funkcionira digitalni potpis i kako uspostaviti povjerenje u digitalni certifikat. Tada smo implementirali digitalni potpis koristeći MessageDigest,Šifra, i Potpis klase iz arhitekture Java Cryptography.

Detaljno smo vidjeli kako potpisati podatke pomoću privatnog ključa i kako provjeriti potpis pomoću javnog ključa.

Kao i uvijek, kôd iz ovog članka dostupan je na GitHubu.

Dno Java

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ