Java AES šifriranje i dešifriranje

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

Blok šifra simetričnog ključa igra važnu ulogu u šifriranju podataka. To znači da se isti ključ koristi i za šifriranje i dešifriranje. Napredni standard šifriranja (AES) široko je korišten algoritam šifriranja sa simetričnim ključem.

U ovom uputstvu vidjet ćemo kako implementirati AES šifriranje i dešifriranje pomoću Java Cryptography Architecture (JCA) unutar JDK.

2. AES algoritam

AES algoritam je iterativna blok-šifra simetričnog ključa koja podržava kriptografske ključeve (tajne ključeve) od 128, 192 i 256 bita za šifriranje i dešifriranje podataka u blokovima od 128 bita. Sljedeća slika prikazuje algoritam visoke razine AES:

Ako podaci koji se šifriraju ne udovoljavaju zahtjevu veličine bloka od 128 bitova, moraju se popuniti. Padding je postupak popunjavanja zadnjeg bloka na 128 bitova.

3. AES varijacije

AES algoritam ima šest načina rada:

  1. ECB (elektronička knjiga kodova)
  2. CBC (lanac blokova šifri)
  3. CFB (povratna informacija šifre)
  4. OFB (povratni povratni signal)
  5. CTR (brojač)
  6. GCM (način rada Galois / brojač)

Način rada može se primijeniti kako bi se ojačao učinak algoritma šifriranja. Štoviše, način rada može pretvoriti blok šifru u protočnu šifru. Svaki način rada ima svoju snagu i slabost. Idemo na brzi pregled.

3.1. ECB

Ovaj način rada je najjednostavniji od svih. Otvoreni tekst podijeljen je u blokove veličine 128 bita. Tada će svaki blok biti šifriran istim ključem i algoritmom. Stoga daje isti rezultat za isti blok. To je glavna slabost ovog načina i ne preporučuje se za šifriranje. Potrebni su podaci o popunjavanju.

3.2. CBC

Kako bi se prevladala slabost ECB-a, CBC način rada koristi Inicijalizacijski vektor (IV) za povećanje enkripcije. Prvo, CBC koristi blok otvorenog teksta xor s IV. Zatim šifrira rezultat u blok šifriranog teksta. U sljedećem bloku koristi rezultat šifriranja za xor s blokom otvorenog teksta do zadnjeg bloka.

U ovom načinu rada šifriranje se ne može paralelizirati, ali dešifriranje se može paralelizirati. Također zahtijeva podatke o popunjavanju.

3.3. CFB

Ovaj se način može koristiti kao stream šifra. Prvo šifrira IV, zatim će se xor s blokom otvorenog teksta dobiti šifrirani tekst. Tada CFB šifrira rezultat šifriranja kako bi se xorrirao otvoreni tekst. Treba mu IV.

U ovom načinu dešifriranje se može paralelizirati, ali šifriranje ne može biti paralelizirano.

3.4. OFB

Ovaj se način može koristiti kao stream šifra. Prvo, šifrira IV. Zatim koristi rezultate šifriranja za xorisanje otvorenog teksta kako bi dobio šifrirani tekst.

Ne zahtijeva podatke o popunjavanju i na njih neće utjecati bučni blok.

3.5. CTR

Ovaj način koristi vrijednost brojača kao IV. Vrlo je sličan OFB-u, ali koristi brojač koji se šifrira svaki put umjesto IV.

Ovaj način rada ima dvije snage, uključujući paralelizaciju šifriranja / dešifriranja, a šum u jednom bloku ne utječe na druge blokove.

3.6. GCM

Ovaj je način produženje CTR načina. GCM je dobio veliku pozornost i preporučuje ga NIST. GCM model daje šifrirani tekst i oznaku za provjeru autentičnosti. Glavna prednost ovog načina rada u usporedbi s drugim načinima rada algoritma je njegova učinkovitost.

U ovom uputstvu koristit ćemo AES / CBC / PKCS5Dopadanje algoritam jer se široko koristi u mnogim projektima.

3.7. Veličina podataka nakon šifriranja

Kao što je ranije spomenuto, AES ima veličinu bloka od 128 bita ili 16 bajtova. AES ne mijenja veličinu, a veličina šifriranog teksta jednaka je veličini čistog teksta. Također, u načinima ECB-a i CBC-a, trebali bismo koristiti padding algoritam like PKCS 5. Dakle, veličina podataka nakon šifriranja je:

ciphertext_size (bytes) = cleartext_size + (16 - (cleartext_size% 16))

Za spremanje IV s šifriranim tekstom trebamo dodati još 16 bajtova.

4. AES parametri

U algoritmu AES potrebna su nam tri parametra: ulazni podaci, tajni ključ i IV. IV se ne koristi u načinu ECB-a.

4.1. Ulazni podaci

Ulazni podaci u AES mogu se temeljiti na nizovima, datotekama, objektima i lozinkama.

4.2. Tajni ključ

Postoje dva načina za generiranje tajnog ključa u AES-u: generiranje iz slučajnog broja ili dobivanje iz zadane lozinke.

U prvom pristupu tajni ključ trebao bi se generirati iz kriptografski sigurnog (pseudo-) generatora slučajnih brojeva poput SecureRandom razred.

Za generiranje tajnog ključa možemo koristiti KeyGenerator razred. Definirajmo metodu za generiranje AES ključa veličine n (128, 192 i 256) bitova:

javni statični SecretKey generiraKey (int n) baca NoSuchAlgorithmException {KeyGenerator keyGenerator = KeyGenerator.getInstance ("AES"); keyGenerator.init (n); Tajni ključ = keyGenerator.generateKey (); povratni ključ; }

U drugom pristupu, tajni ključ AES može se izvesti iz zadane lozinke pomoću funkcije za izvođenje ključa na temelju lozinke, poput PBKDF2. Također nam je potrebna vrijednost soli za pretvaranje lozinke u tajni ključ. Sol je također slučajna vrijednost.

Možemo koristiti SecretKeyFactory razred s PBKDF2SHmacSHA256 algoritam za generiranje ključa iz zadane lozinke.

Definirajmo metodu za generiranje AES ključa iz zadane lozinke s 65.536 iteracija i duljinom ključa od 256 bita:

javna statička SecretKey getKeyFromPassword (lozinka niza, niza sol) baca NoSuchAlgorithmException, InvalidKeySpecException {SecretKeyFactory factory = SecretKeyFactory.getInstance ("PBKDF2WithHmacSHA256"); KeySpec spec = novi PBEKeySpec (password.toCharArray (), salt.getBytes (), 65536, 256); SecretKey secret = novi SecretKeySpec (factory.generateSecret (spec) .getEncoded (), "AES"); povratna tajna; }

4.3. Vektor inicijalizacije (IV)

IV je pseudo-slučajna vrijednost i ima istu veličinu kao i blok koji je šifriran. Možemo koristiti SecureRandom razreda za generiranje slučajnog IV.

Definirajmo metodu za generiranje IV:

javni statički IvParameterSpec generiraj Iv () {bajt [] iv = novi bajt [16]; novi SecureRandom (). nextBytes (iv); vrati novi IvParameterSpec (iv); }

5. Šifriranje i dešifriranje

5.1. Niz

Da bismo implementirali enkripciju ulaznog niza, prvo moramo generirati tajni ključ i IV prema prethodnom odjeljku. Kao sljedeći korak, kreiramo instancu iz Šifra klase pomoću getInstance () metoda.

Uz to, konfiguriramo instancu šifre pomoću u tome() metoda s tajnim ključem, IV i načinom šifriranja. Na kraju, šifriramo ulazni niz pozivajući se na doFinal () metoda. Ova metoda dobiva bajtove unosa i vraća šifrirani tekst u bajtovima:

javna statička šifriranje niza (algoritam niza, unos niza, ključ SecretKey, IvParameterSpec iv) izbacuje NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException { cipher.init (Cipher.ENCRYPT_MODE, ključ, iv); bajt [] cipherText = cipher.doFinal (input.getBytes ()); vratiti Base64.getEncoder () .encodeToString (cipherText); }

Za dešifriranje ulaznog niza možemo inicijalizirati šifru pomoću DECRYPT_MODE za dešifriranje sadržaja:

javna statička dešifriranje niza (algoritam niza, niz šifriranog teksta, ključ SecretKey, IvParameterSpec iv) baca NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingExceptionBagePipdingExceptionBagePipdingException cipher.init (Cipher.DECRYPT_MODE, ključ, iv); bajt [] plainText = cipher.doFinal (Base64.getDecoder () .decode (cipherText)); vrati novi String (plainText); }

Napišimo test metodu za šifriranje i dešifriranje unosa niza:

@Test void givenString_whenEncrypt_thenSuccess () baca NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException; " Tajni ključ = AESUtil.generateKey (128); IvParameterSpec ivParameterSpec = AESUtil.generateIv (); Niz algoritma = "AES / CBC / PKCS5Padding"; Niz cipherText = AESUtil.encrypt (algoritam, ulaz, ključ, ivParameterSpec); Niz plainText = AESUtil.decrypt (algoritam, cipherText, ključ, ivParameterSpec); Assertions.assertEquals (input, plainText); }

5.2. Datoteka

Ajmo sada šifrirati datoteku pomoću AES algoritma. Koraci su isti, ali trebamo neke IO klase za rad s datotekama. Kriptirajmo tekstualnu datoteku:

javna statička void encryptFile (algoritam niza, ključ SecretKey, IvParameterSpec iv, File inputFile, datoteka outputFile) baca IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidheripetipExepipPeaderExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionExceptionException cipher.init (Cipher.ENCRYPT_MODE, ključ, iv); FileInputStream inputStream = novi FileInputStream (inputFile); FileOutputStream outputStream = novi FileOutputStream (outputFile); bajt [] međuspremnik = novi bajt [64]; int bytesRead; while ((bytesRead = inputStream.read (buffer))! = -1) {byte [] output = cipher.update (međuspremnik, 0, bytesRead); if (output! = null) {outputStream.write (output); }} bajt [] outputBytes = šifra.doFinal (); if (outputBytes! = null) {outputStream.write (outputBytes); } inputStream.close (); outputStream.close (); }

Imajte na umu da se ne preporučuje čitati cijelu datoteku - posebno ako je velika - u memoriju. Umjesto toga, šifriramo međuspremnik istovremeno.

Za dešifriranje datoteke koristimo slične korake i inicijaliziramo šifru pomoću DECRYPT_MODE kao što smo prije vidjeli.

Ponovno, definirajmo testnu metodu za šifriranje i dešifriranje tekstualne datoteke. U ovoj metodi čitamo baeldung.txt datoteku iz direktorija testnog resursa, šifrirajte je u datoteku pod nazivom baeldung.šifrirano, a zatim dešifrirajte datoteku u novu datoteku:

Void @Test givenFile_whenEncrypt_thenSuccess () baca NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException {SecretKey ključ = AESUtil.generateKey (128); Niz algoritma = "AES / CBC / PKCS5Padding"; IvParameterSpec ivParameterSpec = AESUtil.generateIv (); Resurs resursa = novi ClassPathResource ("inputFile / baeldung.txt"); Datoteka inputFile = resource.getFile (); Datoteka encryptedFile = nova datoteka ("put puta: baeldung.encrypted"); Datoteka decryptedFile = nova datoteka ("document.decrypted"); AESUtil.encryptFile (algoritam, ključ, ivParameterSpec, inputFile, encryptedFile); AESUtil.decryptFile (algoritam, ključ, ivParameterSpec, encryptedFile, decryptedFile); assertThat (inputFile) .hasSameTextualContentAs (decryptedFile); }

5.3. Na temelju lozinke

AES šifriranje i dešifriranje možemo izvršiti pomoću tajnog ključa izvedenog iz zadane lozinke.

Za generiranje tajnog ključa koristimo getKeyFromPassword () metoda. Koraci šifriranja i dešifriranja jednaki su onima prikazanim u odjeljku za unos niza. Tada za šifriranje možemo koristiti instanciranu šifru i priloženi tajni ključ.

Napišimo metodu ispitivanja:

@Test void givenPassword_whenEncrypt_thenSuccess () izbacuje InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterExcedingPressmeterExceptionPermeterExceptionPermeterExceptionPermeterExceptionPermeterExceptionPermeterExceptionPermeterExceptionPermeterExceptionPermeterExceptionPermeterExceptionPressmeterException Lozinka niza = "baeldung"; Struna sol = "12345678"; IvParameterSpec ivParameterSpec = AESUtil.generateIv (); Tajni ključ = AESUtil.getKeyFromPassword (lozinka, sol); Niz cipherText = AESUtil.encryptPasswordBased (plainText, key, ivParameterSpec); Niz decryptedCipherText = AESUtil.decryptPasswordBased (cipherText, key, ivParameterSpec); Assertions.assertEquals (plainText, decryptedCipherText); }

5.4. Objekt

Za šifriranje Java objekta trebamo koristiti Zapečaćeni objekt razred. Objekt bi trebao biti Serijalizirati. Počnimo s definiranjem a Student razred:

javni razred Student implementira Serializable {private String name; privatno int doba; // standardni postavljači i dobivači} 

Dalje, šifrirajmo Student objekt:

javni statički SealedObject encryptObject (algoritam niza, objekt koji se može serirati, ključ SecretKey, IvParameterSpec iv) baca NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyExceptionBizeExceptionBesegExceptionBesegExceptionBesegExceptionBize cipher.init (Cipher.ENCRYPT_MODE, ključ, iv); SealedObject sealedObject = novi SealedObject (objekt, šifra); povratak sealedObject; }

Šifrirani objekt kasnije se može dešifrirati pomoću ispravne šifre:

javni statički serializable decryptObject (String algoritam, SealedObject sealedObject, SecretKey ključ, IvParameterSpec iv) baca NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, ClassNotFoundException, BadPaddingException, IllegalBlockSizeException, IOException {šifra šifra = Cipher.getInstance (algoritam); cipher.init (Cipher.DECRYPT_MODE, ključ, iv); Serializable unsealObject = (Serializable) sealedObject.getObject (šifra); return unsealObject; }

Napišimo test:

@Test void givenObject_whenEncrypt_thenSuccess () baca NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, BadPexception, BadPexding, BadPexding, BadPejdžing, BadP Ključ tajnog ključa = AESUtil.generateKey (128); IvParameterSpec ivParameterSpec = AESUtil.generateIv (); Niz algoritma = "AES / CBC / PKCS5Padding"; SealedObject sealedObject = AESUtil.encryptObject (algoritam, student, ključ, ivParameterSpec); Objekt učenika = (Student) AESUtil.decryptObject (algoritam, zapečaćeni objekt, ključ, ivParameterSpec); assertThat (student) .isEqualToComparingFieldByField (objekt); }

6. Zaključak

Ukratko, naučili smo kako šifrirati i dešifrirati ulazne podatke poput nizova, datoteka, objekata i podataka zasnovanih na lozinci, koristeći AES algoritam u Javi. Uz to, razgovarali smo o AES varijacijama i veličini podataka nakon šifriranja.

Kao i uvijek, puni izvorni kôd članka dostupan je na GitHub-u.

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

$config[zx-auto] not found$config[zx-overlay] not found