共享密钥加密与公开密钥加密

news/2024/12/30 2:49:07/

前言

加密数据的方法可以分为两种:加密和解密都使用相同密钥的“共享密钥加密”和分别使用不同密钥的“公开密钥加密”。

本文将以图文的形式讲解这两种加密的机制以及相关问题,挑选使用最为广泛两种加密算法,用JAVA将其实现,欢迎各位感兴趣的开发者阅读本文。

共享密钥加密

概念

共享密钥加密是加密和解密都是用相同密钥的一种加密方式,由于使用的密钥相同,所以这种算法也被称为“对称加密”,实现共享加密的算法有:「AES」「DES」、**「动态口令」**等,其中AES的应用最为广泛。

处理流程图解

例如,A准备通过互联网向B发送数据

  • A使用密钥加密数据,并将秘文发送给B。
  • B收到秘文后,使用相同的密钥对其进行解密。这样B就取得了原本的数据。此时的数据已经是加密好的了,就不需要担心第三者窃取数据了,因为它没有密钥解开此密文。

可能产生的问题

如图所示,B接收A发送的密文时,密文可能已经被X窃听了。

  • 此时,B不知道加密时使用的是什么密钥。
  • A需要通过某种手段将密钥交给B。和密文一样,A又在互联网上向B发送了密钥。
  • B使用收到的密钥对密文进行解密,但是该密钥也有可能会被X窃听,这样以来X也可以使用密钥对密文进行解密了。

使用共享密钥加密时,如果接收方不知道密钥是什么,发送方就要通过互联网发送密钥给接收方,此时密钥可能会被第三者监听,这就是共享密钥加密最大问题的所在。

解决方案

如上所述,共享密钥加密存在密钥送达问题,想要解决这个问题,我们可以使用“密钥交换协议”和“公开密钥加密”两种方法。

恩尼格玛密码机

第二次世界大战中,德军所用的”恩尼格玛密码机“使用的加密方式就是共享密钥加密,我们熟知的英国数学家**「艾伦·图灵」**就破解了这个密码机生成的密文,在二战中为英国做了很多的贡献,比如著名的“诺曼底登陆”事件,昨晚看了一部电影《模仿游戏》,该电影讲了图灵的一生,其中就包括了破解恩尼格玛密码机这一部分,挺好的一部电影,感兴趣的朋友可以去看看。

JAVA实现AES加密

我们用Java实现下AES加密。

  • 创建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}}}
}` 
  • 在main函数中测试工具类
 `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来生成公开密钥和私有密钥。
  • 然后,将公开密钥发送给B。
  • A使用B发来的公开密钥加密数据
  • A将密文发送给B,B再使用私有密钥对密文进行解密。这样,B就得到了原本的数据。
  • 公开密钥和密文都是通过互联网传输的,因此可能被X窃听。但是,使用公开密钥无法解密密文,因此X也无法得到原本的数据。

实现公开密钥加密的算法有**「RSA算法」「椭圆曲线加密算法」**等,其中使用最为广泛的是RSA算法。

方便多人传输数据

在和多人传输数据时,使用公开密钥加密十分方便。

例如,B预先准备好了公开密钥和私有密钥,

公开密钥是不怕被人知道的,所以B可以把公开密钥发布在网上。

  • 首先,想发送数据的人需要从王山取得B发布的公开密钥。
  • 然后,用获取到的公开密钥加密要发送的数据。
  • 最后,把密文发送给B
  • B用私有密钥对收到的密文进行解密,取得原本的数据。这种情况就不需要为每个发送对象都准备对应的密钥了。需要保密的私有密钥由接收方保管,所以安全性也更高。

如果使用共享密钥加密,密钥的需求数量会随着发送人数的增多而急剧增多。例如,有2个人相互发送数据,需要2个密钥,但是5个人相互发送数据就需要10个密钥,100人就需要4950个。假设有n个人需要相互发送数据,那么需要的密钥数量就为**「n(n-1)/2」**。

中间人攻击

公开密钥加密存在公开密钥可靠性的问题,B在给A发送公开密钥时,可能会被第三者拦截到这个公开密钥,第三者拿到公开密钥后,保存到本地,自己重新生成一个新的公开密钥发送给A,A使用第三者的公开密钥加密数据后,将数据发送给A时,第三者劫持A发送的数据,用自己的私有密钥解密数据,此时第三者就拿到了B要发送的数据,然后第三者用B的公开密钥再次对解密的数据进行加密,然后发送给B,B用自己的私有密钥正常解开了B发送的数据,整个发送与接收的过程中,没有发生任何问题,因此A也察觉不到数据已经泄漏,这种通过中途替换公开密钥来窃听数据的攻击方法就叫做**「中间人攻击」**。

我们回到B生成公开密钥和私有密钥的时候,我们用PB表示公开密钥,SB表示私有密钥。

X想要窃听A发送给B的数据,于是他准备了公开密钥PX和私有密钥SX。

  • 在B把公开密钥PB发送给A的时候
  • X把公开密钥PB替换成自己的PX
  • 于是公开密钥Px传到了A那里,由于公开密钥无法显示自己是由谁生成的,所以A不会发现自己收到的公开密钥已经被人替换。
  • A使用公开密钥PX对数据加密
  • 当A把想要给B的密文发送出去后,X接收了这个密文。
  • 这个密文由X生成的公开密钥PX加密而成,所以X可以用自己的私有密钥SX对密文进行解密。
  • X用B生成的公开密钥PB加密数据
  • X把密文发送给B,这个密文由B发出的公开密钥PB加密而成,所以B可以用自己的私有密钥SB来解密,从收到密文到解密密文都没发生任何问题,因此B也不可能意识到自己已经被窃听。

解决方案

公开密钥的可靠性会出现问题,因此A无法判断收到的公开密钥是否来自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;}
}` 
  • 在main函数中测试工具类
`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);}` 

写在最后

  • 文中使用的图片源自《我的第一本算法书》,如若侵权,请评论区留言,作者立即删除相关图片。
  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
  • 本文首发于掘金,未经许可禁止转载💌

http://www.ppmy.cn/news/917999.html

相关文章

密钥协商(密钥交换)机制的讲解

国标文件涉及密钥协商算法的函数 生成密钥协商参数并输出计算会话密钥产生协商数据并且计算会话密钥 密钥协商&#xff08;交换&#xff09;算法及其原理 密钥交换/协商目的 “密钥协商机制”是&#xff1a;&#xff08;在身份认证的前提下&#xff09;规避【偷窥】的风险…

什么是公钥,,什么是密钥啊 ?有什么区别吗?

感谢百度知道的用户&#xff1a;神行阿三 问题链接&#xff1a;https://zhidao.baidu.com/question/6671066.html 公钥和私钥是通过一种算法得到的一个密钥对(即一个公钥和一个私钥)&#xff0c;将其中的一个向外界公开copy&#xff0c;称为公钥&#xff1b;另一个自己保留&…

什么是公钥加密,它如何在证书中工作?

公钥加密&#xff0c;也称为非对称加密&#xff0c;是使用两个不同密钥&#xff08;即公钥和私钥&#xff09;的加密方案之一。每个人都可以访问公钥&#xff0c;因为它是公钥&#xff0c;私钥将与该密钥的所有者保持私有。虽然这些键在数学上是相关的&#xff0c;但它们并不完…

公钥、私钥、数字证书的概念

公钥和私钥 一直以来对公钥和私钥都理解得不是很透彻&#xff0c;感觉到模棱两可。今天在网上找了半天&#xff0c;通过查看对这个密钥对的理解&#xff0c;总算弄清楚了。 公钥和私钥就是俗称的不对称加密方式&#xff0c;是从以…

公开密钥密码学是什么

公开密钥密码学(Public-key cryptography)也称非对称式密码学(Asymmetric cryptography)是密码学的一种算法&#xff0c;它需要两个密钥&#xff0c;一个是公开密钥&#xff0c;另一个是私有密钥&#xff1b;公钥用作加密&#xff0c;私钥则用作解密。使用公钥把明文加密后所得…

对称密钥和非对称密钥有什么区别,区别在哪里

它们的不同之处主要有如下几个方面&#xff1a; 加解密时采用的密钥的差异 从上述对对称密钥算法和非对称密钥算法的描述中可看出&#xff0c;对称密钥加解密使用的同一个密钥&#xff0c;或者能从加密密钥很容易推出解密密钥﹔而非对称密钥算法加解密使用的不同密钥&#xff0…

公钥、密钥和数字证书

转自 我理解的数字证书-1-公钥&#xff0c;私钥和数字证书 英文原文地址&#xff1a; http://www.youdzone.com/signature.html 若下文有任何错误&#xff0c;请告知我&#xff0c;谢谢。79996286qq.com 主角介绍&#xff1a;Bob and Alice 提起RSA加密算法&#xff0c;公…

公钥和密钥

一、公钥与私钥 在非对称加密中&#xff0c;我们会用到两个密钥&#xff0c;一个是公钥&#xff0c;另一个是私钥。 公钥是给别人的&#xff0c;别人持有的&#xff1b;而私钥是你自己的&#xff0c;只能你持有&#xff0c;别人是不可以持有的。 二、签名验证算法和加密算法 …