Шифрование с общим ключом против шифрования с открытым ключом

Безопасность
Шифрование с общим ключом против шифрования с открытым ключом

предисловие

Существует два метода шифрования данных: «шифрование с общим ключом», при котором и шифрование, и дешифрование используют один и тот же ключ, и «шифрование с открытым ключом», при котором каждый из них использует свой ключ.

В этой статье мы объясним два механизма шифрования и связанные с ними вопросы в виде изображений и текстов, выберем два наиболее широко используемых алгоритма шифрования и реализуем их в JAVA.Заинтересованные разработчики могут прочитать эту статью.

Шифрование с общим ключом

концепция

Шифрование с общим ключом — это метод шифрования, в котором используется один и тот же ключ как для шифрования, так и для дешифрования. Поскольку используется один и тот же ключ, этот алгоритм также называется «симметричным шифрованием». Алгоритмы для реализации общего шифрования:"AES","DES","Динамический пароль"д., среди которых наиболее широко используется AES.

Иллюстрация технологического процесса

Например, A собирается отправить данные B через Интернет.Из-за риска прослушивания данные должны быть зашифрованы перед отправкой.

  • A шифрует данные с помощью ключа и отправляет секретный текст B.
  • Получив секретный текст, B расшифровывает его с помощью того же ключа. Таким образом, B получает исходные данные. Данные в это время уже зашифрованы, и нет необходимости беспокоиться о краже данных третьей стороной, поскольку у нее нет ключа для расшифровки зашифрованного текста.

возможные проблемы

Как показано на рисунке, когда B получает зашифрованный текст, отправленный A, зашифрованный текст может быть подслушан X.

  • В этот момент B не знает, какой ключ использовался для шифрования.
  • A должен каким-то образом передать ключ B. Как и в случае с зашифрованным текстом, A снова отправляет ключ B через Интернет.
  • B использует полученный ключ для расшифровки зашифрованного текста, но ключ также может быть перехвачен X, так что X также может использовать ключ для расшифровки зашифрованного текста.

При использовании шифрования с общим ключом, если получатель не знает, что такое ключ, отправитель должен отправить ключ получателю через Интернет.В это время ключ может отслеживаться третьей стороной, что является самой большой проблемой шифрования с общим ключом.

решение

Как упоминалось выше, шифрование с общим ключом имеет проблему доставки ключа.Чтобы решить эту проблему, мы можем использовать два метода «протокол обмена ключами» и «шифрование с открытым ключом».

Шифровальная машина Энигма

Во время Второй мировой войны метод шифрования, используемый немецкой армией на «шифровальной машине Enigma», представлял собой шифрование с общим ключом.Мы известные британские математики."Алан Тьюринг"Я взломал зашифрованный текст, сгенерированный этой шифровальной машиной, и внес большой вклад в Соединенное Королевство во время Второй мировой войны, например, знаменитый инцидент «высадка в Нормандии». Вчера вечером я смотрел фильм «Игра в имитацию», в котором рассказывается о «Жизнь Тьюринга», в которой есть часть взлома шифровальной машины «Энигма», — очень хороший фильм, и заинтересованные друзья могут пойти и посмотреть его.

JAVA реализует шифрование AES

Мы реализуем шифрование AES в Java.

  • Создайте файл AESUtils, напишите класс инструмента шифрования AES.
package com.lk.util;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;

public class AESUtils {
    /**
     * 密钥长度: 128, 192 or 256
     */
    private static final int KEY_SIZE = 128;

    /**
     * 加密/解密算法名称
     */
    private static final String ALGORITHM = "AES";

    /**
     * 随机数生成器(RNG)算法名称
     */
    private static final String RNG_ALGORITHM = "SHA1PRNG";

    /**
     * 生成密钥对象
     * @param key byte[] 类型参数
     * @return AES密钥对象
     */
    private static SecretKey generateKey(byte[] key) throws Exception {
        // 创建安全随机数生成器
        SecureRandom random = SecureRandom.getInstance(RNG_ALGORITHM);
        // 设置 密钥key的字节数组 作为安全随机数生成器的种子
        random.setSeed(key);

        // 创建 AES算法生成器
        KeyGenerator gen = KeyGenerator.getInstance(ALGORITHM);
        // 初始化算法生成器
        gen.init(KEY_SIZE, random);

        // 生成 AES密钥对象
        return gen.generateKey();
    }

    /**
     * 数据加密: 明文 -> 密文
     */
    public static byte[] encrypt(byte[] plainBytes, byte[] key) throws Exception {
        // 生成密钥对象
        SecretKey secKey = generateKey(key);

        // 获取 AES 密码器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // 初始化密码器(加密模型)
        cipher.init(Cipher.ENCRYPT_MODE, secKey);

        // 加密数据, 返回密文

        return cipher.doFinal(plainBytes);
    }

    /**
     * 数据解密: 密文 -> 明文
     */
    public static byte[] decrypt(byte[] cipherBytes, byte[] key) throws Exception {
        // 生成密钥对象
        SecretKey secKey = generateKey(key);

        // 获取 AES 密码器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // 初始化密码器(解密模型)
        cipher.init(Cipher.DECRYPT_MODE, secKey);

        // 解密数据, 返回明文
        return cipher.doFinal(cipherBytes);
    }

    /**
     * byte数组转16进制
     *
     * @param bytes byte数组
     * @return 返回16进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(aByte & 0xFF);
            if (hex.length() < 2) {
                sb.append(0);
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    /**
     * 16进制转byte
     * @param inHex 16进制字符串
     * @return byte
     */
    public static byte hexToByte(String inHex) {
        return (byte) Integer.parseInt(inHex, 16);
    }

    /**
     * 16进制转byte数组
     * @param inHex 16进制字符串
     * @return byte数组
     */
    public static byte[] hexToByteArray(String inHex) {
        int hexlen = inHex.length();
        byte[] result;
        if (hexlen % 2 == 1) {
            //奇数
            hexlen++;
            result = new byte[(hexlen / 2)];
            inHex = "0" + inHex;
        } else {
            //偶数
            result = new byte[(hexlen / 2)];
        }
        int j = 0;
        for (int i = 0; i < hexlen; i += 2) {
            result[j] = hexToByte(inHex.substring(i, i + 2));
            j++;
        }
        return result;
    }

    /**
     * 加密文件: 明文输入 -> 密文输出
     */
    public static void encryptFile(File plainIn, File cipherOut, byte[] key) throws Exception {
        aesFile(plainIn, cipherOut, key, true);
    }

    /**
     * 解密文件: 密文输入 -> 明文输出
     */
    public static void decryptFile(File cipherIn, File plainOut, byte[] key) throws Exception {
        aesFile(plainOut, cipherIn, key, false);
    }

    /**
     * AES 加密/解密文件
     */
    private static void aesFile(File plainFile, File cipherFile, byte[] key, boolean isEncrypt) throws Exception {
        // 获取 AES 密码器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // 生成密钥对象
        SecretKey secKey = generateKey(key);
        // 初始化密码器
        cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secKey);

        // 加密/解密数据
        InputStream in = null;
        OutputStream out = null;

        try {
            if (isEncrypt) {
                // 加密: 明文文件为输入, 密文文件为输出
                in = new FileInputStream(plainFile);
                out = new FileOutputStream(cipherFile);
            } else {
                // 解密: 密文文件为输入, 明文文件为输出
                in = new FileInputStream(cipherFile);
                out = new FileOutputStream(plainFile);
            }

            byte[] buf = new byte[1024];
            int len = -1;

            // 循环读取数据 加密/解密
            while ((len = in.read(buf)) != -1) {
                out.write(cipher.update(buf, 0, len));
            }
            out.write(cipher.doFinal());    // 最后需要收尾

            out.flush();

        } finally {
            close(in);
            close(out);
        }
    }

    private static void close(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException e) {
                // nothing
            }
        }
    }
}

  • Протестируйте служебный класс в основной функции
 public static void main(String[] args) throws Exception {
        // 原文内容
        String content = "你好,我是要发送的数据";

        // AES加密/解密用的原始密码
        String key = "MagicalProgrammer";

        // 加密数据, 返回密文
        byte[] cipherBytes = AESUtils.encrypt(content.getBytes(), key.getBytes());

        // byte[]转16进制
        String cipherString = AESUtils.bytesToHex(cipherBytes);

        // 输出加密后的16进制密文
        System.out.println("加密后的密文为: ");
        System.out.println(cipherString);

        // 解密数据, 返回明文
        byte[] plainBytes = AESUtils.decrypt(AESUtils.hexToByteArray(cipherString), key.getBytes());

        // 输出解密后的明文
        System.out.println("解密后结果为: ");
        System.out.println(new String(plainBytes));
    }

шифрование с открытым ключом

концепция

Шифрование с открытым ключом — это метод шифрования, в котором для шифрования и дешифрования используются разные ключи. Из-за разных используемых ключей этот алгоритм также называют «асимметричным шифрованием». Ключ, используемый для шифрования, называется «открытым ключом», а ключ, используемый для дешифрования, называется «закрытым ключом».

Иллюстрация технологического процесса

Как показано на рисунке, A отправляет данные в B через Интернет.

  • Во-первых, открытый ключ и закрытый ключ генерируются получателем B.
  • Затем отправьте открытый ключ Б.
  • A шифрует данные с помощью открытого ключа, отправленного B
  • A отправляет зашифрованный текст B, а B использует закрытый ключ для расшифровки зашифрованного текста. Таким образом, B получает исходные данные.
  • И открытый ключ, и зашифрованный текст передаются через Интернет, поэтому X может их перехватить. Однако, поскольку зашифрованный текст нельзя расшифровать с помощью открытого ключа, X также не может получить исходные данные.

Алгоритмы, реализующие шифрование с открытым ключом,"Алгоритм RSA","Криптография на эллиптических кривых"д., из которых наиболее широко используется алгоритм RSA.

Удобно для нескольких человек для передачи данных

Использование шифрования с открытым ключом удобно при передаче данных нескольким людям.

Например, B заранее подготовил открытый ключ и закрытый ключ,

Открытый ключ не боится быть известным, поэтому B может опубликовать открытый ключ в Интернете.На данный момент есть много людей, которые хотят отправить данные в B.

  • Во-первых, человек, который хочет отправить данные, должен получить открытый ключ, выданный B из Wangshan.
  • Затем отправляемые данные шифруются полученным открытым ключом.
  • Наконец, отправьте зашифрованный текст B
  • B расшифровывает полученный зашифрованный текст с помощью закрытого ключа, чтобы получить исходные данные. В этом случае нет необходимости готовить соответствующий ключ для каждого отправляющего объекта. Закрытый ключ, который необходимо держать в секрете, хранится у получателя, поэтому безопасность также выше.

Если используется шифрование с общим ключом, количество необходимых ключей резко возрастает по мере увеличения числа отправителей. Например, если 2 человека отправляют данные друг другу, необходимо 2 ключа, но 5 людям нужно 10 ключей для отправки данных друг другу, а 100 людям нужно 4950 ключей. Предположим, что есть n человек, которым нужно отправить данные друг другу, тогда необходимое количество ключей равно"n(n-1)/2".

атака «человек посередине»

Шифрование с открытым ключом связано с проблемой надежности открытого ключа. Когда B отправляет открытый ключ A, открытый ключ может быть перехвачен третьей стороной. После того, как третья сторона получит открытый ключ, он будет сохранен локально. Он регенерирует новый открытый ключ и отправляет его А. После того, как А зашифрует данные открытым ключом третьей стороны, при отправке данных А третья сторона захватывает данные, отправленные А, и расшифровывает данные своим собственным закрытым ключом. , третья сторона получает данные, которые B хочет отправить, а затем третья сторона снова шифрует расшифрованные данные с помощью открытого ключа B, а затем отправляет их B, и B использует свой собственный закрытый ключ, чтобы разблокировать B в обычном режиме. проблема во всем процессе отправки и получения отправленных данных, поэтому А не замечает утечки данных. Такой метод атаки подслушивания данных путем замены открытого ключа в середине называется"атака «человек посередине»".

Мы вернемся к тому моменту, когда B сгенерировал открытый ключ и закрытый ключ, мы использовали PB для открытого ключа и SB для закрытого ключа.

X хочет подслушать данные, отправленные A B, поэтому он готовит открытый ключ PX и закрытый ключ SX.

  • Когда B отправляет открытый ключ PB в A
  • X заменяет открытый ключ PB своим собственным PX.
  • Таким образом, открытый ключ Px передается А. Поскольку открытый ключ не может раскрыть, кто его сгенерировал, А не обнаружит, что полученный открытый ключ был заменен кем-то другим.
  • A шифрует данные с помощью открытого ключа PX.
  • Когда A отправляет зашифрованный текст, который он хочет отправить B, X получает зашифрованный текст.
  • Этот зашифрованный текст зашифрован открытым ключом PX, сгенерированным X, так что X может расшифровать зашифрованный текст своим собственным закрытым ключом SX.
  • X шифрует данные с помощью открытого ключа PB, сгенерированного B.
  • X отправляет зашифрованный текст B, и этот зашифрованный текст зашифрован открытым ключом PB, выданным B, поэтому B может расшифровать его с помощью своего личного ключа SB, и нет проблем от получения зашифрованного текста до расшифровки зашифрованного текста, поэтому B также вряд ли поймет, что его подслушали.

решение

Будут проблемы с надежностью открытого ключа, поэтому А не сможет определить, получен ли открытый ключ от Б. Для решения этой проблемы необходимо использовать «цифровой сертификат.

Другая проблема с шифрованием с открытым ключом заключается в том, что и шифрование, и дешифрование занимают много времени. Поэтому этот метод не подходит для непрерывной отправки фрагментированных данных, для решения этой проблемы используется «гибридное шифрование».

Трудности реализации

Алгоритмы, реализующие шифрование с открытым ключом, найти непросто. Учитывая вычислительный процесс, необходимый для шифрования, алгоритм должен удовлетворять следующим условиям.

  • Данные могут быть зашифрованы с определенным значением
  • Вычисление зашифрованных данных с другим значением может восстановить данные в исходное состояние.
  • Невозможно вывести один ключ из другого.

Немного подумав, вы увидите, насколько сложно найти алгоритм, удовлетворяющий вышеуказанным условиям. Таким образом, RSA и другие алгоритмы, которые могут реализовать шифрование с открытым ключом, имеют большое значение для безопасности современного интернет-сообщества.

JAVA реализует шифрование RSA

Мы используем Java для реализации шифрования RSA

  • Создайте файл RSAUtils и напишите класс средства шифрования RSA.
package com.lk.util;

import java.util.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA加密工具类
 */
public class RSAUtils {
    /**
     * 密钥长度 于原文长度对应 以及越长速度越慢
     */
    private final static int KEY_SIZE = 1024;
    /**
     * 用于封装随机产生的公钥与私钥
     */
    private static Map<Integer, String> keyMap = new HashMap<Integer, String>();

    /**
     * 随机生成密钥对
     */
    public static Map genKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器
        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 得到私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        // 得到私钥字符串
        String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
        // 将公钥和私钥保存到Map
        //0表示公钥
        keyMap.put(0, publicKeyString);
        //1表示私钥
        keyMap.put(1, privateKeyString);
        return keyMap;
    }

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt(String str, String publicKey) throws Exception {
        //base64编码的公钥
        byte[] decoded = Base64.getDecoder().decode(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(String str, String privateKey) throws Exception {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.getDecoder().decode(str);
        //base64编码的私钥
        byte[] decoded = Base64.getDecoder().decode(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }
}

  • Протестируйте служебный класс в основной функции
public static void main(String[] args) throws Exception {
        // 随机生成的密钥和公钥
        Map<Integer, String> keyMap = new HashMap<Integer, String>();
        //生成公钥和私钥
        keyMap = RSAUtils.genKeyPair();
        System.out.println("公钥:" + keyMap.get(0));
        System.out.println("私钥:" + keyMap.get(1));

        // 要加密的数据
        String message = "你好,我是通过RSA加密的数据";

        // 使用公钥加密
        String messageEn = RSAUtils.encrypt(message, keyMap.get(0));
        System.out.println("密文:" + messageEn);

        // 使用私钥解密
        String messageDe = RSAUtils.decrypt(messageEn, keyMap.get(1));
        System.out.println("解密:" + messageDe);
    }

напиши в конце

  • Картинки, использованные в этой статье, взяты из "Моя первая книга алгоритмов". Если есть какие-либо нарушения, пожалуйста, оставьте сообщение в области комментариев, и автор немедленно удалит соответствующие картинки.
  • Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь 😊
  • Эта статья была впервые опубликована на Наггетс, перепечатка без разрешения запрещена 💌