הצפנה ופענוח Java AES

ג'אווה טופ

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

1. סקירה כללית

צופן החסימה של סימטרי-מפתח מפתח תפקיד חשוב בהצפנת הנתונים. המשמעות היא שאותו מפתח משמש גם להצפנה וגם לפענוח. תקן ההצפנה המתקדם (AES) הוא אלגוריתם הצפנה של מפתח סימטרי.

במדריך זה נראה כיצד ליישם הצפנה ופענוח AES באמצעות אדריכלות Java Cryptography (JCA) בתוך ה- JDK.

2. אלגוריתם AES

אלגוריתם ה- AES הוא צופן בלוקים איטרטיבי, מפתח סימטרי תומך במפתחות הצפנה (מפתחות סודיים) של 128, 192 ו- 256 סיביות להצפנה ופענוח נתונים בבלוקים של 128 סיביות. האיור שלהלן מציג את אלגוריתם AES ברמה גבוהה:

אם הנתונים שיש להצפין אינם עומדים בגודל החסימה של דרישת 128 סיביות, עליהם להיות מרופדים. ריפוד הוא תהליך של מילוי הבלוק האחרון ל 128 ביט.

3. וריאציות AES

לאלגוריתם AES שישה מצבי פעולה:

  1. ECB (ספר קוד אלקטרוני)
  2. CBC (שרשור בלוק צופן)
  3. CFB (Cipher FeedBack)
  4. OFB (פידבקים)
  5. CTR (מונה)
  6. GCM (מצב Galois / Counter)

ניתן להפעיל את אופן הפעולה על מנת לחזק את ההשפעה של אלגוריתם ההצפנה. יתר על כן, אופן הפעולה עשוי להמיר את צופן הבלוקים לצופן זרם. לכל מצב יש את החוזק והחולשה שלו. בואו נעבור סקירה מהירה.

3.1. ECB

אופן פעולה זה הוא הפשוט מכולם. הטקסט הפשוט מחולק לבלוקים בגודל 128 סיביות. ואז כל בלוק יוצפן עם אותו מפתח ואלגוריתם. לכן, זה מייצר את אותה תוצאה עבור אותו בלוק. זוהי החולשה העיקרית של מצב זה ו זה לא מומלץ להצפנה. זה דורש נתוני ריפוד.

3.2. CBC

על מנת להתגבר על חולשת ה- ECB, מצב CBC משתמש בווקטור אתחול (IV) כדי להגדיל את ההצפנה. ראשית, CBC משתמש בגוש xor עם טקסט רגיל עם ה- IV. ואז זה מצפין את התוצאה לבלוק ההצפנה. בבלוק הבא הוא משתמש בתוצאת ההצפנה כדי xor עם גוש הטקסט הרגיל עד לחסימה האחרונה.

במצב זה, לא ניתן להקביל את ההצפנה, אך ניתן להקביל את הפענוח. זה גם דורש נתוני ריפוד.

3.3. CFB

מצב זה יכול לשמש כצופן זרם. ראשית, הוא מצפין את הרביעי, ואז הוא יעבור לבלוק טקסט רגיל כדי לקבל טקסט צופן. ואז CFB מצפין את תוצאת ההצפנה ל x או לטקסט הרגיל. זה צריך IV.

במצב זה, ניתן להקביל פענוח אך לא ניתן להקביל להצפנה.

3.4. OFB

מצב זה יכול לשמש כצופן זרם. ראשית, הוא מצפין את הרביעי. ואז הוא משתמש בתוצאות ההצפנה כדי לקבל את הטקסט הרגיל כדי לקבל טקסט צופן.

זה לא דורש נתוני ריפוד ולא יושפע מהחסימה הרועשת.

3.5. שיעור קליקים

מצב זה משתמש בערך המונה כ- IV. זה דומה מאוד ל- OFB, אך הוא משתמש בדלפק כדי להיות מוצפן בכל פעם במקום ה- IV.

למצב זה יש שתי חוזקות, כולל הקבלה בין הצפנה / פענוח, ורעש בבלוק אחד אינו משפיע על חסימות אחרות.

3.6. GCM

מצב זה הוא הרחבה של מצב CTR. ה- GCM זכה לתשומת לב משמעותית ומומלץ על ידי NIST. דגם GCM מפיק טקסט צופן ותג אימות. היתרון העיקרי של מצב זה, לעומת מצבי פעולה אחרים של האלגוריתם, הוא היעילות שלו.

במדריך זה נשתמש ב- AES / CBC / PKCS5Padding אלגוריתם מכיוון שהוא נמצא בשימוש נרחב בפרויקטים רבים.

3.7. גודל הנתונים לאחר ההצפנה

כאמור, ל- AES יש גודל בלוק של 128 סיביות או 16 בתים. ה- AES אינו משנה את הגודל וגודל הטקסט הצפני שווה לגודל הטקסט הצלול. כמו כן, במצבי ECB ו- CBC, עלינו להשתמש באלגוריתם ריפוד אוהב PKCS 5. אז גודל הנתונים לאחר ההצפנה הוא:

ciphertext_size (בתים) = cleartext_size + (16 - (cleartext_size% 16))

לאחסון IV בטקסט צופן, עלינו להוסיף 16 בתים נוספים.

4. פרמטרים של AES

באלגוריתם AES, אנו זקוקים לשלושה פרמטרים: נתוני קלט, מפתח סודי ו- IV. IV אינו משמש במצב ECB.

4.1. נתוני קלט

נתוני הקלט ל- AES יכולים להיות מבוססים על מחרוזת, קובץ, אובייקט וסיסמא.

4.2. מפתח סודי

ישנן שתי דרכים ליצירת מפתח סודי ב- AES: יצירת מספר אקראי או נגזרת מסיסמה נתונה.

בגישה הראשונה, המפתח הסודי צריך להיווצר ממחולל מספרים אקראי מאובטח קריפטוגרפי (פסאודו) כמו SecureRandom מעמד.

ליצירת מפתח סודי, אנו יכולים להשתמש ב- KeyGenerator מעמד. בואו נגדיר שיטה ליצירת מפתח AES בגודל נ (128, 192 ו- 256) סיביות:

ציבורי סטטי ציבורי SecretKey createKey (int n) זורק NoSuchAlgorithmException {KeyGenerator keyGenerator = KeyGenerator.getInstance ("AES"); keyGenerator.init (n); מפתח SecretKey = keyGenerator.generateKey (); מפתח החזרה; }

בגישה השנייה, ניתן לגזור את המפתח הסודי של AES מסיסמה נתונה באמצעות פונקציית גזירת מפתח מבוססת סיסמה כמו PBKDF2. אנו זקוקים גם לערך מלח על מנת להפוך סיסמה למפתח סודי. המלח הוא גם ערך אקראי.

אנחנו יכולים להשתמש ב- SecretKeyFactory שיעור עם PBKDF2WithHmacSHA256 אלגוריתם ליצירת מפתח מסיסמה נתונה.

בואו נגדיר שיטה ליצירת מפתח AES מסיסמה נתונה עם 65,536 חזרות ואורך מפתח של 256 ביט:

סטטי ציבורי SecretKey getKeyFromPassword (סיסמת מחרוזת, מלח מחרוזת) זורק NoSuchAlgorithmException, InvalidKeySpecException {SecretKeyFactory factory = SecretKeyFactory.getInstance ("PBKDF2WithHmacSHA256"); מפרט KeySpec = PBEKeySpec חדש (password.toCharArray (), salt.getBytes (), 65536, 256); SecretKey סוד = SecretKeySpec חדש (factory.generateSecret (spec) .getEncoded (), "AES"); להחזיר סוד; }

4.3. וקטור אתחול (IV)

IV הוא ערך פסאודו-אקראי ובעל אותו גודל כמו הבלוק שמוצפן. אנחנו יכולים להשתמש ב- SecureRandom בכיתה כדי ליצור IV אקראי.

בואו נגדיר שיטה ליצירת IV:

IvParameterSpec סטטי ציבורי generateIv () {בתים [] iv = בתים חדשים [16]; חדש SecureRandom (). nextBytes (iv); להחזיר IvParameterSpec חדש (iv); }

5. הצפנה ופענוח

5.1. חוּט

כדי ליישם הצפנת מחרוזת קלט, ראשית עלינו ליצור את המפתח הסודי ו- IV בהתאם לסעיף הקודם. כשלב הבא, אנו יוצרים מופע מתוך ה- צוֹפֶן בשיעור באמצעות getInstance () שיטה.

בנוסף, אנו מגדירים מופע צופן באמצעות ה- init () שיטה עם מפתח סודי, IV ומצב הצפנה. לבסוף, אנו מצפינים את מחרוזת הקלט על ידי הפעלת ה- doFinal () שיטה. שיטה זו מקבלת בתים של קלט ומחזירה טקסט צופן בבתים:

הצפנת מחרוזת סטטית ציבורית (אלגוריתם מחרוזת, קלט מחרוזת, מקש SecretKey, IvParameterSpec iv) זורק NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockScipher. cipher.init (Cipher.ENCRYPT_MODE, מפתח, iv); בתים [] cipherText = cipher.doFinal (input.getBytes ()); החזר Base64.getEncoder () .encodeToString (cipherText); }

לפענוח מחרוזת קלט נוכל לאתחל את הצופן שלנו באמצעות ה- DECRYPT_MODE לפענוח התוכן:

פענוח מחרוזת סטטי ציבורי (אלגוריתם מחרוזת, מחרוזת cipherText, מקש SecretKey, IvParameterSpec iv) זורק NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, Illegal Cipheral; cipher.init (Cipher.DECRYPT_MODE, מפתח, iv); בייט [] plainText = cipher.doFinal (Base64.getDecoder () .decode (cipherText)); להחזיר מחרוזת חדשה (plainText); }

בוא נכתוב שיטת בדיקה להצפנה ופענוח קלט מחרוזת:

@Test בטל givenString_whenEncrypt_thenSuccess () זורק NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException {String input = " מקש SecretKey = AESUtil.generateKey (128); IvParameterSpec ivParameterSpec = AESUtil.generateIv (); אלגוריתם מחרוזות = "AES / CBC / PKCS5Padding"; מחרוזת cipherText = AESUtil.encrypt (אלגוריתם, קלט, מפתח, ivParameterSpec); מחרוזת plainText = AESUtil.decrypt (אלגוריתם, cipherText, key, ivParameterSpec); Assertions.assertEquals (קלט, טקסט רגיל); }

5.2. קוֹבֶץ

כעת בואו להצפין קובץ באמצעות אלגוריתם AES. השלבים זהים, אבל אנחנו צריכים כמה IO שיעורים לעבודה עם הקבצים. בואו להצפין קובץ טקסט:

encryptFile חלל סטטי ציבורי (אלגוריתם מחרוזת, מפתח KeyKey, IvParameterSpec iv, File inputFile, File outputFile) זורק IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException; cipher.init (Cipher.ENCRYPT_MODE, מפתח, iv); FileInputStream inputStream = FileInputStream חדש (inputFile); FileOutputStream outputStream = FileOutputStream חדש (outputFile); בתא [] חיץ = בית חדש [64]; int bytesRead; בעוד ((bytesRead = inputStream.read (buffer))! = -1) {byte [] output = cipher.update (buffer, 0, bytesRead); אם (פלט! = null) {outputStream.write (פלט); }} בתים [] outputBytes = cipher.doFinal (); אם (outputBytes! = null) {outputStream.write (outputBytes); } inputStream.close (); outputStream.close (); }

לידיעתך, לא מומלץ לנסות לקרוא את כל הקובץ - במיוחד אם הוא גדול - בזיכרון. במקום זאת, אנו מצפינים חיץ בכל פעם.

לפענוח קובץ אנו משתמשים בצעדים דומים ומאתחלים את הצופן שלנו באמצעות DECRYPT_MODE כמו שראינו קודם.

שוב, בואו נגדיר שיטת בדיקה להצפנה ופענוח קובץ טקסט. בשיטה זו אנו קוראים את baeldung.txt קובץ מספריית משאבי הבדיקה, הצפן אותו לקובץ שנקרא baeldung. מוצפןולאחר מכן פענח את הקובץ לקובץ חדש:

@Test בטל givenFile_whenEncrypt_thenSuccess () זורק NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException; אלגוריתם מחרוזות = "AES / CBC / PKCS5Padding"; IvParameterSpec ivParameterSpec = AESUtil.generateIv (); משאב משאבים = ClassPathResource חדש ("inputFile / baeldung.txt"); קובץ inputFile = resource.getFile (); קובץ encryptedFile = קובץ חדש ("classpath: baeldung.encrypted"); קובץ decryptedFile = קובץ חדש ("document.decrypted"); AESUtil.encryptFile (אלגוריתם, מפתח, ivParameterSpec, inputFile, encryptedFile); AESUtil.decryptFile (אלגוריתם, מפתח, ivParameterSpec, encryptedFile, decryptedFile); assertThat (inputFile) .hasSameTextualContentAs (decryptedFile); }

5.3. מבוסס סיסמא

אנו יכולים לבצע את ההצפנה והפענוח של AES באמצעות המפתח הסודי שמקורו בסיסמה נתונה.

להפקת מפתח סודי, אנו משתמשים ב- getKeyFromPassword () שיטה. שלבי ההצפנה והפענוח זהים לאלה המוצגים בסעיף קלט המחרוזת. לאחר מכן נוכל להשתמש בצופן המיידי ובמפתח הסודי המסופק לביצוע ההצפנה.

בואו נכתוב שיטת בדיקה:

@Test בטל givenPassword_whenEncrypt_thenSuccess () זורק InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException; סיסמת מחרוזת = "baeldung"; מלח מיתרים = "12345678"; IvParameterSpec ivParameterSpec = AESUtil.generateIv (); מפתח SecretKey = AESUtil.getKeyFromPassword (סיסמה, מלח); מחרוזת cipherText = AESUtil.encryptPasswordBased (plainText, key, ivParameterSpec); מחרוזת decryptedCipherText = AESUtil.decryptPasswordBased (cipherText, key, ivParameterSpec); Assertions.assertEquals (plainText, decryptedCipherText); }

5.4. לְהִתְנַגֵד

להצפנת אובייקט Java עלינו להשתמש ב- SealedObject מעמד. האובייקט צריך להיות ניתן לבצע סדרתי. נתחיל בהגדרת א סטוּדֶנט מעמד:

מחלקה ציבורית מכשירי סטודנטים ניתן להתאמה לסידור {שם פרטי מחרוזת; גיל פרטי פרטי; // סטרים וקובעים סטנדרטיים} 

לאחר מכן, בואו להצפין את ה- סטוּדֶנט חפץ:

ציבורי סטטי ציבורי SealedObject encryptObject (אלגוריתם מחרוזת, אובייקט ברצף סידורי, מפתח KeyKey, IvParameterSpec iv) זורק NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOExceptionSignal; cipher.init (Cipher.ENCRYPT_MODE, מפתח, iv); SealedObject sealedObject = חדש SealedObject (אובייקט, צופן); להחזיר אטום אובייקט; }

מאוחר יותר ניתן לפענח את האובייקט המוצפן באמצעות הצופן הנכון:

פתיחה סטטית ציבורית decryptObject (אלגוריתם מחרוזת, SealedObject sealedObject, מפתח KeyKey, IvParameterSpec iv) זורק NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, ExceptionException; cipher.init (Cipher.DECRYPT_MODE, מפתח, iv); UnsealObject Serializable = (Serializable) sealedObject.getObject (צופן); להחזיר unsealObject; }

בוא נכתוב מקרה מבחן:

@Test בטל שניתןObject_whenEncrypt_thenSuccess () זורק NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, BadPaddingException; מקש SecretKey = AESUtil.generateKey (128); IvParameterSpec ivParameterSpec = AESUtil.generateIv (); אלגוריתם מחרוזות = "AES / CBC / PKCS5Padding"; SealedObject sealedObject = AESUtil.encryptObject (אלגוריתם, תלמיד, מפתח, ivParameterSpec); אובייקט סטודנט = (סטודנט) AESUtil.decryptObject (אלגוריתם, sealedObject, מפתח, ivParameterSpec); assertThat (סטודנט) .isEqualToComparingFieldByField (אובייקט); }

6. מסקנה

לסיכום, למדנו כיצד להצפין ולפענח נתוני קלט כמו מחרוזות, קבצים, אובייקטים ונתונים מבוססי סיסמה באמצעות אלגוריתם AES ב- Java. בנוסף, דנו בווריאציות ה- AES ובגודל הנתונים לאחר ההצפנה.

כמו תמיד, קוד המקור המלא של המאמר זמין באתר GitHub.

תחתית Java

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

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