предисловие
Существует два метода шифрования данных: «шифрование с общим ключом», при котором и шифрование, и дешифрование используют один и тот же ключ, и «шифрование с открытым ключом», при котором каждый из них использует свой ключ.
В этой статье мы объясним два механизма шифрования и связанные с ними вопросы в виде изображений и текстов, выберем два наиболее широко используемых алгоритма шифрования и реализуем их в 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);
}
напиши в конце
- Картинки, использованные в этой статье, взяты из "Моя первая книга алгоритмов". Если есть какие-либо нарушения, пожалуйста, оставьте сообщение в области комментариев, и автор немедленно удалит соответствующие картинки.
- Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь 😊
- Эта статья была впервые опубликована на Наггетс, перепечатка без разрешения запрещена 💌