Хочете зрозуміти досить гарну конфіденційність? Імітуйте це.

Як випливає з назви, Pretty Good Privacy (або PGP) - це програма шифрування, яка насправді забезпечує досить хорошу конфіденційність. «Досить хороший» біт має бути трохи іронічним заниженням. Це було однією з домінуючих форм наскрізного шифрування електронних повідомлень після його розробки Філом Циммерманом у 1991 році. Стало дедалі популярнішим після використання його викривачем Едвардом Сноуденом.

PGP забезпечує дві речі, необхідні для безпечного спілкування:

  1. Конфіденційність: забезпечується використанням симетричного шифрування блоків, стиснення за допомогою алгоритму ZIP та сумісності електронної пошти за допомогою схеми кодування radix64
  2. Аутентифікація: забезпечується використанням цифрових підписів

Без зайвих сумнівів, давайте перейдемо до роботи PGP.

Як це працює

Я поясню концепцію PGP з точки зору реалізації в контексті Еліс (відправник) та Боба (одержувач). Ми будемо використовувати наступні алгоритми:

  1. RSA як асиметричний алгоритм шифрування
  2. SHA-512 як алгоритм хешування
  3. DES як симетричний алгоритм шифрування та
  4. ZIP для стиснення

Ви можете використовувати і інші алгоритми. (Я знаю, що DES занадто старий для використання, але мета тут - зрозуміти концепцію PGP.)

Аліса і Боб обидва генерують свої пари ключів (відкриті та приватні ключі), використовуючи алгоритм RSA. Відкриті ключі Аліси та Боба повинні бути відомі один одному.

Сторона Аліси / Відправника:

  1. Аліса пише повідомлення М, яке вона має намір надіслати Бобу.
  2. M надається як вхід для алгоритму SHA-512 для отримання 512-бітового двійкового хешу (представленого як 128-бітна шістнадцятковий рядок) його.
  3. Цей хеш підписується цифровим способом за допомогою алгоритму RSA, тобто хеш зашифровується приватними ключами Аліси. Вхідними даними для RSA є приватні ключі Аліси та хеш. Результатом роботи RSA є цифровий підписаний хеш або зашифрований хеш EH.
  4. Тепер M і EH додаються разом. (Додано в тому сенсі, що вони розміщені в масиві рядків).
  5. M та EH (які є у масиві рядків) діють як вхідні дані до алгоритму стиснення ZIP, щоб отримати стиснуте M та стиснене EH, знову ж таки у масиві рядків.
  6. Вихідні дані цього кроку тепер зашифровані за допомогою симетричного алгоритму шифрування DES. Для цього ми спочатку створимо SecretKey для DES. Цей ключ і висновок кроку 5 діятимуть як вхідні дані до алгоритму шифрування DES, який надасть нам зашифрований висновок (знову в масиві рядків).
  7. Останнє, але не менш важливе, оскільки M зашифровано за допомогою SecretKey, його також потрібно надіслати Бобу. Ми зашифруємо алгоритм SecretKey DES за допомогою відкритого ключа Боба. Для цього ми будемо використовувати RSA, а вхідними даними буде відкритий ключ Боба та SecretKey.
  8. Виходи кроків 6 і 7 тепер додаються і надсилаються Бобу як остаточне повідомлення.

Повідомлення надсилається у вигляді масиву рядків ( String finalmessage[]), що містить наступне в індексах:

0: Стиснене повідомлення M, яке зашифровано за допомогою SecretKey

1: хеш EH з цифровим підписом, який потім стискається та шифрується SecretKey

2: Результат кроку 7

Сторона Боба / приймача:

  1. Боб спочатку розшифрує SecretKey DES з його приватними ключами. Вхідними даними для алгоритму RSA для цього будуть Private Keys of Bob and finalmessage[2]. Результат від RSA дасть Бобу SecretKey.
  2. Цей SecretKey тепер буде діяти як один із входів алгоритму розшифровки DES для розшифровки finalmessage[0]та finalmessage[1]. Ці два також будуть виконувати функції входу в алгоритм розшифровки DES. Результатом цього кроку буде decrypted version: finalmessage[0]і finalmessage[1].
  3. Виходи з вищезазначеного кроку повинні надаватися як вхідні дані до алгоритму ZIP для декомпресії.
  4. З результатів вищевказаного кроку ми отримаємо цифровий підпис хеш і оригінальне повідомлення М. Ми перевіримо, чи підписав хеш Аліса. Для цього ми обчислимо хеш вихідного повідомлення M, використовуючи SHA-512 ( calculated_hash). Ми також розшифруємо хеш із цифровим підписом за допомогою відкритих ключів Аліси, використовуючи RSA (вхідні дані до RSA: хеш із цифровим підписом та відкриті ключі Аліси та вихід від RSA:) decrypted_hash.
  5. Порівняйте decrypted_hashі calculated_hash. Якщо вони виявляються однаковими, то аутентифікація досягається, що означає, що повідомлення справді було надіслано Алісою.

Далі наведено моделювання PGP, зроблене найпростішим способом з використанням Java.

import java.util.*; import java.math.*; import javax.crypto.Cipher; import java.security.*; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import javax.crypto.spec.*; public class PGP{ static Cipher ecipher, dcipher;//Required for DES public static void main(String args[]) throws Exception{ //Generating sender keys KeyPair senderkeyPair = buildKeyPair(); PublicKey senderpubKey = senderkeyPair.getPublic(); PrivateKey senderprivateKey = senderkeyPair.getPrivate(); //Generating receiver keys KeyPair receiverkeyPair = buildKeyPair(); PublicKey receiverpubKey = receiverkeyPair.getPublic(); PrivateKey receiverprivateKey = receiverkeyPair.getPrivate(); //Sending both public keys and private keys for choice of digital signature or normal assymetric encryption String messagetoreceiver[] = senderside(senderpubKey, senderprivateKey, receiverpubKey, receiverprivateKey); receiverside(messagetoreceiver, senderpubKey, senderprivateKey, receiverpubKey, receiverprivateKey); } public static void receiverside(String messagetoreceiver[], PublicKey senderpubKey, PrivateKey senderprivateKey, PublicKey receiverpubKey, PrivateKey receiverprivateKey) throws Exception { //Receiver receives the message messagetoreceiver[] with messagetoreceiver[2] as secret key encrypted with receiver pub key //Receiver decrypts the messagetoreceiver[2] with his/her privatekey String receiverencodedsecretkey = decrypt(receiverpubKey, receiverprivateKey, messagetoreceiver[2], 1); //Key after decryption is in base64 encoded form byte[] decodedKey = Base64.getDecoder().decode(receiverencodedsecretkey); SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "DES"); System.out.println("\nReceiver Side: Receiver SecretKey DES after Decryption with his/her Private Key=\n"+originalKey.toString()); //Decrypt the rest of the message in messagetoreceiver with SecretKey originalKey String receiverdecryptedmessage[] = new String[messagetoreceiver.length-1]; System.out.println("\nReceiver Side: Message After Decryption with SecretKey="); for (int i=0;iencryptwithprivatekey 1->encryptwithpublickey public static String encrypt(PublicKey publicKey, PrivateKey privateKey, String message, int ch) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); if (ch == 0) { cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] utf8 = cipher.doFinal(message.getBytes("UTF-8")); return new sun.misc.BASE64Encoder().encode(utf8); } else { cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] utf8 = cipher.doFinal(message.getBytes("UTF-8")); return new sun.misc.BASE64Encoder().encode(utf8); } } //n: 0->decryptwithpublickey 1->decryptwithprivatekey public static String decrypt(PublicKey publicKey,PrivateKey privateKey, String st, int ch) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); byte[] encrypted = new sun.misc.BASE64Decoder().decodeBuffer(st); if (ch == 0) { cipher.init(Cipher.DECRYPT_MODE, publicKey); byte[] utf8 = cipher.doFinal(encrypted); return new String(utf8, "UTF8"); } else { cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] utf8 = cipher.doFinal(encrypted); return new String(utf8, "UTF8"); } } }

Ми використовували схему кодування base64, подібну до системи radix64, яка використовується в PGP.

Примітка:

  1. Ми base64 кодуємо рядки після шифрування та стиснення, щоб отримати читабельну текстову форму.
  2. Для дешифрування та декомпресії ми надсилаємо декодовані входи base64 як фактичні входи в алгоритми дешифрування та декомпресії.
  3. Ключ був закодований і декодований base64, оскільки я використовував Java для моделювання PGP, що вимагає закодованої форми на стороні приймача, щоб його можна було перетворити на тип даних SecretKey для процесу дешифрування.

Будь ласка, слідкуйте, плескайте та діліться. Коментуйте будь-які помилки, покращення чи пропозиції. Ви навіть можете стежити за мною у Twitter.