常用的加密算法_不可逆加密算法

对称加密和非对称加密的区别

识别密文方式

常用 URL 特殊字符的转义_%2f 转义

Enctypt() 这个函数很关键,一般来说加密方法里面都会有这个单词

加密是通过某种算法将原本的数据内容加密为特殊的参数
,网上也有很多自称是可以破解 MD5 密码的网站,其原理也是一样,就是有一个巨大的资源库,存放了许多字符串及对应的 MD5 加密后的字符串,通过你输入的 MD5 加密串来进行比较,如果过你的密码复杂度比较低,还是有很大机率验证出来的

加密方式

1
2
3
4
5
Md5加密: 通过md5加密会生成一个16位或者32位的加密参数               特征: 16位或32位  不可逆
对称加密DES/AES两个: 就是加密和解密用的是同一把钥匙 特征: 秘加密解密钥匙唯一 可逆
非对称加密RSA: 有加密公匙和解密私匙 特征: 加密解密钥匙不唯一 可逆
Base64伪加密: 将数据以另外一种编码方式呈现 特征: 表示真正的加密算法 可逆

# MD5 (不可逆向需要 Key)

MD5.js 是通过前台 js 加密的方式对用户信息,密码等私密信息进行加密处理的工具,也可称为插件

MD5 共有 6 种加密方法:

  • hex_md5(value)
  • b64_md5(value)
  • str_md5(value)

HMAC 消息认证码,就是最后加密完成的数据

  • hex_hmac_md5(key, data)
  • b64_hmac_md5(key, data)
  • str_hmac_md5(key, data)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./md5.js"></script>>
</head>
<body>
<p>你好世界</p>
<script>
var code = "123456";
var username = "123456";
var password = "123456";
var str1 = hex_md5("123456");
var str2 = b64_md5("123456");
var str3 = str_md5("123456");
var str4 = hex_hmac_md5(code,code);
var str5 = b64_hmac_md5(username,username);
var str6 = str_hmac_md5(password,password);
console.log(str1); // e10adc3949ba59abbe56e057f20f883e
console.log(str2); // 4QrcOUm6Wau+VuBX8g+IPg
console.log(str3); // áÜ9IºY«¾VàWò��
console.log(str4); // 30ce71a73bdd908c3955a90e8f7429ef
console.log(str5); // MM5xpzvdkIw5VakOj3Qp7w
console.log(str6); // 0Îq§;Ý��9U©��t)ï
</script>
</body>
</html>

# 判断方式

  • 密文一般为 16 位或者 32 位,其中 16 位是取的 32 位第 9~25 位的值;
  • 组成方式为字母 (a-f) 和数字(0-9)混合,字母可以全部是大写或者小写。

除了通过密文特征来判断以外,我们还可以搜索源代码,标准 MD5 的源码里是包含了一些特定的值的,没有这些特定值,就无法实现 MD5:

  • 0123456789ABCDEF0123456789abcdef
  • 1732584193-271733879-1732584194271733878

PS:某些特殊情况下,密文的长度也有可能不止 16 位或者 32 位,有可能是在官方算法上有一些魔改,通常也是在 16 位的基础上,左右填充了一些随机字符串。

# Base64 编码转换 (可逆)

Base64 使用了 64 个可打印字符(A-Z、a-z、0-9、+、/)Base16 使用了 16 个可打印字符(A-F、0-9)

下列代码可以将普通的字符串通过方法转换为 Base64 编码的数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<head>
<title>前端的base64使用方法</title>
</head>
<body>
</body>
<script>
var str = "hello";
var str64 = window.btoa("hello"); // 编码方法
console.log("字符串是:"+str);
console.log("经base64编码后:"+str64);
console.log("base64解码后:"+window.atob(str64)); // 解码方法
</script>
</html>

# 判断方式

Base 编码系统特征如下

Base16:结尾没有等号,数字要多于字母;

Base32:字母要多于数字,明文数量超过 10 个,结尾可能会有很多等号;

Base58:结尾没有等号,字母要多于数字;

Base64:一般情况下结尾都会有 1 个或者 2 个等号,明文很少的时候可能没有;

Base85:等号一般出现在字符串中间,含有一些奇怪的字符;

Base100:密文由 Emoji 表情组成

# sha1 (不可逆

哈希算法

SHA1(英语:Secure Hash Algorithm 1,中文名:安全散列算法 1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1 可以生成一个被称为消息摘要的 160 位(20 字节)散列值,散列值通常的呈现形式为 40 个十六进制数。

SHA256 是 SHA2 的一种,可产生 256 位的哈希值,较 SHA1 更加的安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>sha1</title>
<script src="https://cdn.bootcss.com/js-sha1/0.6.0/sha1.js"></script>
</head>
<body>
<p>sha1加解密</p>



<script type="text/javascript">
var sha1_1 = sha1("mosquito~");
console.log(sha1_1);
var sha1_2 = sha1("admin:1001");
console.log(sha1_2);
</script>
</body>
</html>

# 编码 / 解码字符串原生函数

使用 JS 函数的 escape()unescape() 分别是编码和解码字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>sha1</title>
</head>
<body>

<p>编码解码字符串</p>

<script type="text/javascript">
var escape1 =escape("我的名字是:mosquito~"); // 将里面的字符串编码
console.log(escape1);
var unescape1 = unescape(escape1); //解码 // 解码编码后的字符串
console.log(unescape1);
</script>
</script>
</body>
</html>

# 对称加密 AES/DES 唯一 可逆需要 key

对称加密算法只是为了区分非对称加密算法。其中鲜明的特点是对称加密是加密解密使用相同的密钥,而非对称加密加密和解密时使用的密钥不一样 对称就是相同的,不对称就是不唯一的 各有优点

使用场景

对于大部分情况我们都使用对称加密,而对称加密的密钥交换时使用非对称加密,这有效保护密钥的安全。非对称加密加密和解密密钥不同,那么它的安全性是无疑最高的,但是它加密解密的速度很慢,不适合对大数据加密。而对称加密加密速度快,因此混合使用最好

  • AES: 高级加密标准,新一代标准,加密速度更快,安全性更高(不用说优先选择)
  • DES:比较老的算法,一共有三个参数入口(原文,密钥,加密模式)。而 3DES 只是 DES 的一种模式,是以 DES 为基础更安全的变形,对数据进行了三次加密,也是被指定为 AES 的过渡算法

# AES 的使用

AES 密钥长度可以选择 128 位【16 字节】,192 位【24 字节】和 256 位【32 字节】密钥(其他不行,因此别乱设密码哦

AES 支持五种模式:CBC,CFB,ECB,OFB,PCBC,

1
2
3
4
5
ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。
CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度。
CFB/OFB实际上是一种反馈模式,目的也是增强破解的难度。
ECB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量

Java 语言实现 AES 算法的加解密
键盘录入需要录入的规则是加密时使用的密钥,也就是加密规则。加密规则需要和解密时使用的密钥相同,否则无法正确解密。因此,加密规则和解密规则之间非常重要。在实际应用中,加密规则需要保密,只有知道加密规则的人才能正确解密

注意:
如果攻击者能够获取加密使用的密钥,就可以通过逆向分析得出加密的规则,从而破解加密。因此,在实际应用中,加密规则需要保密,只有知道加密规则的人才能正确解密。同时,还需要采取其他安全措施,如加密传输通道、使用安全的存储方式等,以保护加密数据的安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Scanner;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class RSAUtil {
/*
* 加密 1.构造密钥生成器 2.根据ecnodeRules规则初始化密钥生成器 3.产生密钥 4.创建和初始化密码器 5.内容加密 6.返回字符串
*/
public static String AESEncode(String encodeRules, String content) {
try {
// 1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
// 2.根据ecnodeRules规则初始化密钥生成器
// 生成一个128位的随机源,根据传入的字节数组
keygen.init(128, new SecureRandom(encodeRules.getBytes()));
// 3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
// 4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
// 5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, "AES");
// 6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance("AES");
// 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
// 8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = content.getBytes("utf-8");
// 9.根据密码器的初始化方式--加密:将数据加密
byte[] byte_AES = cipher.doFinal(byte_encode);
// 10.将加密后的数据转换为字符串
// 这里用Base64Encoder中会找不到包
// 解决办法:
// 在项目的Build path中先移除JRE System Library,再添加库JRE System
// Library,重新编译后就一切正常了。
String AES_encode = new String(new BASE64Encoder().encode(byte_AES));
// 11.将字符串返回
return AES_encode;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

// 如果有错就返加nulll
return null;
}

/*
* 解密 解密过程: 1.同加密1-4步 2.将加密后的字符串反纺成byte[]数组 3.将加密内容解密
*/
public static String AESDncode(String encodeRules, String content) {
try {
// 1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
// 2.根据ecnodeRules规则初始化密钥生成器
// 生成一个128位的随机源,根据传入的字节数组
keygen.init(128, new SecureRandom(encodeRules.getBytes()));
// 3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
// 4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
// 5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, "AES");
// 6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance("AES");
// 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
// 8.将加密并编码后的内容解码成字节数组
byte[] byte_content = new BASE64Decoder().decodeBuffer(content);
/*
* 解密
*/
byte[] byte_decode = cipher.doFinal(byte_content);
String AES_decode = new String(byte_decode, "utf-8");
return AES_decode;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}

// 如果有错就返加nulll
return null;
}

public static void main(String[] args) {
RSAUtil jm = new RSAUtil();
Scanner scanner = new Scanner(System.in);
/*
* 加密
*/
System.out.println("使用AES对称加密,请输入加密的规则");
String encodeRules = scanner.next();
System.out. println ("请输入要加密的内容:");
String content = scanner.next();
System.out.println("根据输入的规则" + encodeRules + "加密后的密文是:"
+ jm.AESEncode(encodeRules, content));

/*
* 解密
*/
System.out.println("使用AES对称解密,请输入加密的规则:(须与加密相同)");
encodeRules = scanner.next();
System.out.println("请输入要解密的内容(密文):");
content = scanner.next();
System.out.println("根据输入的规则" + encodeRules + "解密后的明文是:"
+ jm.AESDncode(encodeRules, content));
}
}

------------------------------------------------------

输出:

使用AES对称加密,请输入加密的规则
zhang
请输入要加密的内容:
123
根据输入的规则zhang加密后的密文是:JXHBwSB8AAspwb5k4YvExQ==
使用AES对称解密,请输入加密的规则:(须与加密相同)
zhang
请输入要解密的内容(密文):
JXHBwSB8AAspwb5k4YvExQ==
根据输入的规则zhang解密后的明文是:123

# DES 的使用

DES 是对称加密算法领域中的典型算法,其密钥默认长度为 56

# 非对称 RSA 加密 不唯一 可逆

JS 中没有私钥是无法解密的,只能通过公钥去加密结果

非对称加密加密解密时使用的不同的密钥,分为公钥(public key)和私钥 (private key). 公钥可以公开,而私钥自己保存,通过公钥加密结果,必须私钥解密

由于非对称加密的密钥生成麻烦,所以无法做到一次一密,而且其加密速度很慢,无法对大量数据加密。因此最常用的使用场景就是数字签名密码传输,用作数字签名时使用私钥加密,公钥解密;用作加密解密时,使用公钥加密,私钥解密

长度要求:

RSA 加密是有长度限制的,1024 位密钥可以加密 128 字节(1024 位),不满 128 字节的使用随机数填充,但是 RSA 实现中必须要加随机数(11 字节以上),所以明文长度最大为 117 字节,然后剩下的加入随机数。这也产生了每次加密结果每一次都不一样的特点

下方是 Java 实现该 RSA 算法,最底部字符串我们可以自行选择,先是生成密钥对,然后加密,再解密。需要注意的是这个方法是不能跨语言使用的,因为里面对公钥和私钥用到的序列化是 java 的序列化。 由于加密后的密文都是字节码形式的,我们要以字符串方式保存或传输的话,可以使用 Base64 编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;

public class RSAUtil {


/** 指定加密算法为RSA */
private static String ALGORITHM = "RSA";
/*指定加密模式和填充方式*/
private static String ALGORITHM_MODEL = "RSA/ECB/PKCS1Padding";
/** 指定key的大小,一般为1024,越大安全性越高 */
private static int KEYSIZE = 1024;
/** 指定公钥存放文件 */
private static String PUBLIC_KEY_FILE = "PublicKey";
/** 指定私钥存放文件 */
private static String PRIVATE_KEY_FILE = "PrivateKey";
/**
* 生成密钥对
*/
private static void generateKeyPair() throws Exception {
/** RSA算法要求有一个可信任的随机数源 */
SecureRandom sr = new SecureRandom();
/** 为RSA算法创建一个KeyPairGenerator对象 */
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM);
/** 利用上面的随机数据源初始化这个KeyPairGenerator对象 */
kpg.initialize(KEYSIZE, sr);
/** 生成密匙对 */
KeyPair kp = kpg.generateKeyPair();
/** 得到公钥 */
Key publicKey = kp.getPublic();
/** 得到私钥 */
Key privateKey = kp.getPrivate();
/** 用对象流将生成的密钥写入文件 */
ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream(
PUBLIC_KEY_FILE));
ObjectOutputStream oos2 = new ObjectOutputStream(new FileOutputStream(
PRIVATE_KEY_FILE));
oos1.writeObject(publicKey);
oos2.writeObject(privateKey);
/** 清空缓存,关闭文件输出流 */
oos1.close();
oos2.close();
}
/**
* 加密方法 source: 源数据
*/
public static byte[] encrypt(String source) throws Exception {
/** 将文件中的公钥对象读出 */
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
PUBLIC_KEY_FILE));
Key key = (Key) ois.readObject();
ois.close();
/** 得到Cipher对象来实现对源数据的RSA加密 */
Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] b = source.getBytes();
/** 执行加密操作 */
byte[] b1 = cipher.doFinal(b);
return b1;
}
/**
* 解密算法 cryptograph:密文
*/
public static String decrypt(byte[] cryptograph) throws Exception {
/** 将文件中的私钥对象读出 */
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
PRIVATE_KEY_FILE));
Key key = (Key) ois.readObject();
/** 得到Cipher对象对已用公钥加密的数据进行RSA解密 */
Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL);
cipher.init(Cipher.DECRYPT_MODE, key);
/** 执行解密操作 */
byte[] b = cipher.doFinal(cryptograph);
return new String(b);
}
public static void main(String[] args) throws Exception {
generateKeyPair();//生成密钥对
String source = "邓鹏伟";// 要加密的字符串
System.out.println("加密的字符串为"+source);
byte[] cryptograph = encrypt(source);// 生成的密文
//可以将密文进行base64编码进行传输
System.out.println(new String(Base64.encode(cryptograph)));
String target = decrypt(cryptograph);// 解密密文
System.out.println(target);
}
}

------------------------------------------------------

输出:

加密的字符串为123
axejk/TwfbNQAB9dNlbu3x5bJl7l7bQmEvk1hbi+iV/ZseZOMEZdVpB2kgXYvACI9N8VMjW6+m13q14bRdhkrmwiP6fjVv85af+dc/gk5Jph1MB6nYey/8ZhWZI6VowH4OBncZX33kzomvwxycQfUXLM/yDGxM6neYt8PaIbtaQ=
123

# 判断方式

在网站中通常使用 JSEncrypt 库来实现,其最大的特征就是有一个设置公钥的过程,我们可以通过以下方法来快速初步判断是否为 RSA 算法

  • 搜索关键词 new JSEncrypt()JSEncrypt 等,一般会使用 JSEncrypt 库,会有 new 一个实例对象的操作
  • 搜索关键词  setPublicKeysetKeysetPrivateKeygetPublicKey  等,一般实现的代码里都含有设置密钥的过程。

RAS 的私匙 公匙 明文 密文 也有一一对应的关系

更新于 阅读次数