5分钟讲清楚常用加密算法

2023-03-23 14:23:57 浏览数 (1)

为什么要加密?

当然是为了保密。

最早的密码应用,都伴随着阴谋与战争。

中国古代的“阴符”、“阴书”就是其中的代表。


到了近代,二战最著名的一个故事,就是纳粹德国的Enigma密码机

以及图灵和他发明的密码破解机。

可以说,现代战争中,信息安全极其重要

如果你的部署、命令都被对手知道

你收到的指令都是对手伪造的

这还怎么打?


在当下,加密数据和密码的应用,

从军事,到商业领域,以及科学界

乃至每个人普通的生活

如果没有数据加密,每个人的信息都是透明的

想想可怕的电话诈骗,你还觉得自己的信息安全吗?

有一句话说的是:

大数据时代,没有秘密


加密方式

最常见的计算机加密方式有三种:

对称、非对称、摘要

对称的意思是:

代码语言:javascript复制
我有一个秘钥K,能把原文 A 加密成 A1,还能把 A1 还原成 A

f(A, K) =A1
f(A1, K) =A

对称加密用途最为广泛,

操作简单,速度快,

适合场合多样

但缺点很明显,秘钥K太关键了

如果你需要让密文被其他授信的人访问

必须给他发秘钥

但问题是,

只要得到秘钥,就能破解一切,包括非法用户


非对称安全性稍微高一点

有2个秘钥,一般是成对的公钥和私钥

通常是用公钥加密,私钥解密

这种情况下,解决了对称加密的发送秘钥问题

现在公钥是公开的,私钥只有我自己有

永远不发送,就不会泄密


摘要加密一般采用的是hash算法,

只加密不解密,

适合于只验证对不对,而不验证是什么。

举个朴素例子:

二中的学生,学号格式是 L220130103 格式

三中的学生,学号格式是 PLE821 格式

现在我要验证这个人是哪个学校的,

其实无需关注具体内容,

简单判断一下长度就可以了


当然真正的算法没这么简单

下面具体来谈

便于大家统一理解

我们建一个项目

统一使用hutool工具包

代码语言:javascript复制
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.5</version>
    </dependency>

对称加密

最简单的是des方法

他使用一个简单的秘钥,就可以实现加密和解密

我们写一个junit的test,或者写一个main也可以

代码语言:javascript复制
class Test1 {
	@Test
	void test() {
		String text0 = "这是原文";
		String key = "xiaomian";
		
		DES des = new DES(key.getBytes());
		String text1=des.encryptBase64(text0);
		System.out.println(text1);
	}
}

运行结果是 iJWs7q8 dyiRBouA8lDBA==

我们使用秘钥 “xiaomian”,

把“这是原文”,加密成了一个看不懂的字符串

然后开始解密

代码语言:javascript复制
class Test1 {
	@Test
	void test() {
		String key = "xiaomian";
		DES des = new DES(key.getBytes());
		
		String text2=des.decryptStr(" iJWs7q8 dyiRBouA8lDBA==");
		System.out.println(text2);
	}
}

运行结果是 这是原文

还是同一个key,

用相反的方法,把原文解出来了

操作非常简单。

des就是一个最简单的对称加解密方法

类似的还有aes算法

可以使用hutool工具包,

操作差不多

非对称加密

使用非对称的主要原因

就是 安全、安全、还是安全

非对称在性能方面和对称没的比

因此,建议非对称使用短数据场合

而长数据使用对称实现。


最常用的是RSA算法

首先要生成一对秘钥,这个就没法人工创造了

我们可以借助工具

代码语言:javascript复制
class Test2 {
	@Test
	void test() {
        KeyPair pair = SecureUtil.generateKeyPair("RSA");
        String privateKey = Base64.encode(pair.getPrivate().getEncoded());
        System.out.println("privateKey:"   privateKey);
        String publicKey = Base64.encode(pair.getPublic().getEncoded());
        System.out.println("publicKey:"   publicKey);
	}
}

输出结果为:

代码语言:javascript复制
privateKey:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALBgrUOQySQB72VrGyijYZcaNM26t49oaKlNddu4SLpEdJzpUnVjXtIi9Nj/DkFBXgSB9FYdrv7MpQWzTmLw0do6pcfo2cekmBGPnFRdChtEf8Npq0pAV2LlNahwkB8fxZcbQYaaWBuBkx7aHT09JGsEpi9P3y7ae4vwYmFlWgEHAgMBAAECgYBVBnXgEWb1Zb1rLaPNBl3gQrDb0Dv29QUnQIElZ4QfzypMDipDDqFCObnA9cuAZ6uUDrWj4fFfQXX39oU KwtuESddT9rJw1hPSy2i5ityEyYO5OZQRJaTVWPWzpph bqFLkngaOvlr3OBG7YtywUL0P8toz2pOQKpoQOkffetuQJBAP26GKiOcs/sKhrzuM/t0S2ERXXDcu07jWxJ8gioQBuQaM7AA14tbiRzaZm3Xt cGPjHJUGEJpDvGLQ9MavEZDMCQQCx9S JevTGpxHMX/R3uUawlTsks1keyd0rqBtqQQx7V9Bm8htrlVU3QbhacFd/FJodWX7mGOAO6nLVGK/5qXvdAkB13zSBadvwuDg/WSZsgoEA8kgKk4gaqeXjft7QIsnVhFsMYauu3tAiYvOWE9ghbbU0LeAi9a s UayxRMERzJ9AkA5Y7fL32I2 kQMI9nEDnUUl1u2bzxLaJAx9wm4T3gGAwDLYkKjhEoyArjAOcCN1AgLdQQuklEXnlYgENEXc6flAkEAwDbTBfOtuP/6S5nU0gX9D5fYlF/fz4YTfBjHC2dZ/wrmWlJA0f7QFf5DysQB9AXJX6ZWL/3LkMtHPafqBMKpJw==

publicKey:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwYK1DkMkkAe9laxsoo2GXGjTNurePaGipTXXbuEi6RHSc6VJ1Y17SIvTY/w5BQV4EgfRWHa7 zKUFs05i8NHaOqXH6NnHpJgRj5xUXQobRH/DaatKQFdi5TWocJAfH8WXG0GGmlgbgZMe2h09PSRrBKYvT98u2nuL8GJhZVoBBwIDAQAB

看吧,鬼也看不懂

不过能用就行

接下来,开始使用秘钥对,进行加密和解密

代码语言:javascript复制
class Test2 {
	static String privateKey;
	static String publicKey;
	
	@BeforeAll
	static void createKey() {
        KeyPair pair = SecureUtil.generateKeyPair("RSA");
        privateKey = Base64.encode(pair.getPrivate().getEncoded());
        System.out.println("privateKey:"   privateKey);
        publicKey = Base64.encode(pair.getPublic().getEncoded());
        System.out.println("publicKey:"   publicKey);
	}
	
	@Test
	void test() {
		String text = "这是原文";
		RSA rsa = new RSA(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(), privateKey, publicKey);

        String text1 = rsa.encryptBase64(text, KeyType.PublicKey);
        System.out.println("公钥加密: "   text1);
        String text2 = rsa.decryptStr(text1, KeyType.PrivateKey);
        System.out.println("私钥解密: "   text2);
	}
}

输出结果为:

代码语言:javascript复制
公钥加密: KSifTf6tS6LGlBT9LJC33VXzkXtaQEIGJcpf1BU2ptzRXTtBzvjx83EffCqntD7/M7ZciTr4MIBFBPFCxLs9NVEeC4K9/B8fQE/3hdsMzWBTnKQzQR2vd1i5mOgFaHYwTLwrq9Dbkv1YGluCb004YtLEqdjSO/Oljs7x2YVtvGc=
私钥解密: 这是原文

也可以反过来用

代码语言:javascript复制
	@Test
	void test() {
		String text = "这是原文";
		RSA rsa = new RSA(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(), privateKey, publicKey);

        String text1 = rsa.encryptBase64(text, KeyType.PrivateKey);
        System.out.println("私钥加密: "   text1);
        String text2 = rsa.decryptStr(text1, KeyType.PublicKey);
        System.out.println("公钥解密: "   text2);
	}
代码语言:javascript复制
私钥加密: Ty5YGXHUajKHtlmQ7yGYJtc39Rjb3IvSIm7WWg4 Ge6u8MDeJ3TLYZokgBbAMZRcZBZ8bIWiou12 KxoW8moRwke4PlWVipgwhC2l6qo7WtTMiwnGh 0 B8wnY3OySFK4ZeOMQFgYuiTL2uZqofLXyNzwID7GKCfWI6EL29wtbE=
公钥解密: 这是原文

对称算法的特点就是

随便先用谁,然后用另一个解

一般来说,有意义的信息,加密传递

建议使用 公钥加密,私钥解密

这种方法,确保信息只能由你指定的对象收到

另一种反过来的做法,

私钥加密,公钥解密

用于数字签名场合

证明这个签名是某人做的

因为只有他有他自己的私钥

摘要加密

摘要加密是单向的

能加不能解

主要目的是生成特征码

常用算法包括 md5,sha 等等

代码语言:javascript复制
class Test3 {
	@Test
	void test() {
		String text = "这是原文";
		String text1 = DigestUtil.md5Hex(text);
        System.out.println(text1);
	}
}

输出结果是:

代码语言:javascript复制
531ffeed49bb13a6cf93049cc1e5ee53

比如系统密码,一般采用摘要加密

假设我的密码是 123456,

md5之后变成了 e10adc3949ba59abbe56e057f20f883e

如果有人能看到数据库表,

哪怕他找到了我的密码 e10adc3949ba59abbe56e057f20f883e,

也不知道我实际的密码是123456

当登陆时,会对用户输入的密码再次md5,

如果结果还等于e10adc3949ba59abbe56e057f20f883e,

就表示输对了密码。


摘要加密的特点是

输出结果位数是固定的

比如上面列出的md5,加密后永远是固定长度

他的特点主要是:

代码语言:javascript复制
输入不同,加密结果就不同

输入相同,加密结果一定相同

现在一般md5用的也少,主要是长度和复杂度

一般使用 sha256 或 sha512

他可以做简化验证

比如,我有一个文档,大小为 100M

如何验证他的内容发生串改

我们不可能拿2个100M的文件,逐个字节比较

这时,只要对2者分别做摘要运算,

得到一个短的字符串,对他们比较就可以了

这种验证的特点是:我知道你对和不对,但我不知道哪错了

总结

三种方法中,一般使用原则是

  1. 速度快,用对称
  2. 安全高/数据少,用非对称
  3. 用于验证对错,保留特征码,用摘要

大家可以试试,顺便安利一下hutool,

非常优秀的工具包

之后我们会专文介绍国密算法

0 人点赞