一、URLENCODE
(1)为什么使用
1、是因为当字符串数据以url的形式传递给web服务器时,字符串中是不允许出现空格和特殊字符的。
2、因为 url 对字符有限制,比如把一个邮箱放入 url,就需要使用 urlencode 函数,因为 url 中不能包含 @ 字符。
3、url转义其实也只是为了符合url的规范而已。因为在标准的url规范中中文和很多的字符是不允许出现在url中的。
(2)转换规则
urlencode:
返回字符串,此字符串中除了-_.之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)
按照每个字符对应的字符编码,不是符合我们范围的,统统的转化为%的形式也就是了。自然也是16进制的形式。
那哪些字符是需要转化的呢
ASCII 的控制字符
这些字符都是不可打印的,自然需要进行转化。
什么是可打印字符?
在ASCII码中规定,0-31、128这33个字符属于控制字符,32-127这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64。
一些非ASCII字符
这些字符自然是非法的字符范围。转化也是理所当然的了。
一些保留字符
很明显最常见的就是“&”了,这个如果出现在url中了,那你认为是url中的一个字符呢,还是特殊的参数分割用的呢?
就是一些不安全的字符了。
例如:空格。为了防止引起歧义,需要被转化为“+”。
和编码无关
通过urlencode的转化规则和目的,我们也很容易的看出,urleocode是基于字符编码的。同样的一个汉字,不同的编码类型,肯定对应不同的urleocode的串。gbk编码的有gbk的encode结果。
apache等服务器,接受到字符串后,可以进行decode,但是还是无法解决编码的问题。编码问题,还是需要靠约定或者字符编码的判断解决。
因此,urleocode只是为了url中一些非ascii字符,可以正确无误的被传输,至于使用哪种编码,就不是eocode所关心和解决的问题了。
(3)java 实现
/**
-
@author itnanls
-
@date 2021/3/15
*/
public class UrlEncode {public static void main(String[] args) throws UnsupportedEncodingException {
String encode = URLEncoder.encode(“http://itnanls.cn/中文 ,。?”, “UTF-8”);
System.out.println(encode);String decode = URLDecoder.decode(encode, "UTF-8"); System.out.println(decode);
}
}
二、Base64编解码
1、Base64是什么
Base64是一种用64个字符来表示任意二进制数据的方法。
它是一种编码方式,而非加密方式。
它通过将二进制数据转变为64个“可打印字符”,完成了数据在HTTP协议上的传输。
2、使用场景
Base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。
什么是可打印字符?
在ASCII码中规定,0-31、128这33个字符属于控制字符,32-127这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64。
既然可打印的只有95,而比95小的整数最大的2的次方的数字就是64了。
3、加密规则
关于这个编码的规则:
首先将待编码的内容转换成8位二进制,每3个字符为一组;
如果编码前的长度是3n+1,编码后的内容最后面补上2个 ‘=’,如果编码前的长度是3n+2,编码后 的内容最后面补上1个 ‘=’。
再将每一组的二进制内容拆分成6位的二进制,不足6位的后面补足0;
每个6进制的数字前面补足0,保证变成8位二进制;
将补足后的内容根据base64编码表转换成base64内容输出;
(不足四个字符的时候会用 ‘=’ 来补足,下面会说明)
例子
编码前 “hb”
1.根据ascii码转换成8位二进制,3个为一组:
01101000,01100010
2.编码前长度是3n+2,所以后面补1个 ‘=’:
01101000,01100010,=
3.拆分成6位二进制,不足6位的在后面补足0,0010补足变成001000:
011010,000110,001000,=
4.每个6进制的数字前面补足0:
0011010,00000110,00000010,=
5.根据base64编码表输出:
aGI=
Table 1: The Base64 Alphabet
索引 对应字符 索引 对应字符 索引 对应字符 索引 对应字符
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y
4、总结
可以将任意的二进制数据进行Base64编码。
数据加密之后,数据量会变大,变大1/3左右。
编码后有个非常显著的特点,末尾有个=号。
可进行反向解密。
Base64编码具有不可读性
5、Base64程序
/**
-
@author itnanls
-
@date 2021/3/15
*/
public class Base64Test {public static void main(String[] args) {
// 获取编码器
Base64.Encoder encoder = Base64.getEncoder();
byte[] encode = encoder.encode(“abc”.getBytes());
System.out.println(new String(encode));// 获取解码器 Base64.Decoder decoder = Base64.getDecoder(); byte[] decode = decoder.decode(encode); System.out.println(new String(decode));
}
}
三、hash摘要算法
1、MD5
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
/**
- @author itnanls
- @date 2021/3/15
*/
public static void main(String[] args) throws Exception {
MessageDigest md5 = MessageDigest.getInstance(“MD5”);
byte[] digest = md5.digest(“123”.getBytes());
System.out.println(digest.length);
}
2、SHA1
SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
SHA 家族
正式名称为 SHA 的家族第一个成员发布于 1993年。然而人们给它取了一个非正式的名称 SHA-0 以避免与它的后继者混淆。两年之后, SHA-1,第一个 SHA 的后继者发布了。 另外还有四种变体,曾经发布以提升输出的范围和变更一些细微设计: SHA-224, SHA-256, SHA-384 和 SHA-512 (这些有时候也被称做 SHA-2):
public static void main(String[] args) Exception {
MessageDigest sha1 = MessageDigest.getInstance(“SHA1”);
byte[] digest = sha1.digest(“123”.getBytes());
System.out.println(digest.length);
}
20
3、SHA256
SHA256算法使用的哈希值长度是256位。
public static void main(String[] args) throws Exception {
MessageDigest sha256 = MessageDigest.getInstance(“SHA-256”);
byte[] digest = sha256.digest(“123”.getBytes());
System.out.println(digest.length);
}
32
4、SHA512
算法使用的哈希值长度是512位。
public static void main(String[] args) throws Exception {
MessageDigest sha512 = MessageDigest.getInstance(“SHA-512”);
byte[] digest = sha512.digest(“123”.getBytes());
System.out.println(digest.length);
}
64
四、对称加密
1、DES算法
DES 加密算法是一种 分组密码,以 64 位为 分组对数据 加密,它的密钥长度是56位,加密解密用同一算法。
数据加密标准(Data Encryption Standard),简称DES,是由IBM公司提交,美国政府于1977年1月5日颁布的一种加密算法。
DES的设计目标是,用于加密保护静态存储和传输信道中的数据,安全使用10~15年。
DES综合运用了置换、代替、代数等多种密码技术。它设计精巧、实现容易、使用方便,堪称是适应计算机环境的近代分组密码的一个典范。DES的设计充分体现了Shannon所阐述的设计密码的思想,标志着密码的设计与分析达到了新的水平。
DES是一种分组密码。明文、密文和密钥的分组长度都是64位。
DES是面向二进制的密码算法。因而能够加解密任何形式的计算机数据。
DES是对合运算,因而加密和解密共用同一算法,从而使工程实现的工作量减半。
DES的密码结构属于Feistel结构。
(1)DES的加密过程
64位密钥经子密钥产生算法产生出16个48位子密钥:K1,K2,…,K16,分别供第1次,第2次,…,第16次加密迭代使用。
64位明文首先经过初始置换IP,将数据打乱重新排列并分成左右两半,左边32位构成L0,右边32位构成R0。
第i次加密迭代:由轮函数f实现子密钥Ki对Ri-1的加密,结果为32位的数据组f ( Ri-1 , Ki )。f ( Ri-1 , Ki )再与Li-1模2相加,又得到一个32位的数据组Li-1 ⊕ f ( Ri-1 , Ki )。以Li ⊕ f ( Ri-1 , Ki )作为下一次加密迭代的Ri,以Ri-1作为下一次加密迭代的Li ( i = 1,2,…,16)。
按照上一步的规则进行16次加密迭代。
第16次加密迭代结束后,以R16为左,L16为右,合并产生一个64位的数据组。再经过逆初始置换IP-1,将数据重新排列,便得到64位密文。
(2)DES的解密过程
64位密钥经子密钥产生算法产生出16个48位子密钥:K1,K2,…,K16,分别供第1次,第2次,…,第16次解密迭代使用。
64位密文首先经过初始置换IP,将数据打乱重新排列并分成左右两半,左边32位构成R16,右边32位构成L16。
第17-i次解密迭代:由轮函数f实现子密钥Ki对Li的解密,结果为32位的数据组f ( Li , Ki )。f ( Li , Ki )再与Ri模2相加,又得到一个32位的数据组Ri ⊕ f ( Li , Ki )。以Ri ⊕ f ( Li , Ki )作为下一次解密迭代的Li-1,以Li作为下一次解密迭代的Li-1 ( i = 16,15,…,1)。
按照上一步的规则进行16次解密迭代。
第16次解密迭代结束后,以L0为左,R0为右,合并产生一个64位的数据组。再经过逆初始置换IP-1,将数据重新排列,便得到64位明文。
(3)java实现
/**
-
@author itnanls
-
@date 2021/3/15
*/
public class DES {/**
- 算法秘钥
*/
private static final byte[] DES_KEY = {11, 21, 1, -110, 82, -32, -85, -128, -65 ,44,-2};
/**
- 算法名字
/
private static final String ALGORITHM_NAME = “DES”;
/* - 数据加密,算法(DES)
- @param data
- 要进行加密的数据
- @return 加密后的数据
*/
public static byte[] encryptDes(byte[] data) {
try {
// DES算法要求有一个可信任的随机数源
DESKeySpec desKey = new DESKeySpec(DES_KEY);
// 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_NAME);
SecretKey key = keyFactory.generateSecret(desKey);
// 加密对象
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
cipher.init(Cipher.ENCRYPT_MODE, key);
// 加密
return cipher.doFinal(data);
} catch (Exception e) {
throw new RuntimeException(“加密错误,错误信息:”, e);
}
}
/**
- 解密
- @param cryptData
- @return
*/
public static byte[] decryptBasedDes(byte[] cryptData) {
try {
// DES算法要求有一个可信任的随机数源
DESKeySpec desKey = new DESKeySpec(DES_KEY);
// 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_NAME);
SecretKey key = keyFactory.generateSecret(desKey);
// 解密对象
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
cipher.init(Cipher.DECRYPT_MODE, key);
// 把字符串解码为字节数组,并解密
return cipher.doFinal(cryptData);
} catch (Exception e) {
throw new RuntimeException(“解密错误,错误信息:”, e);
}
}
public static void main(String[] args) {
String data = "itnanls is so shuai"; byte[] result = encryptDes(data.getBytes()); System.out.println("加密后:" + Base64.getEncoder().encode(result)); // DES数据解密 byte[] source = decryptBasedDes(result); System.err.println("解密后:" + new String(source));
}
} - 算法秘钥
2、AES
(1)介绍
密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
这个标准用来替代原先的DES(Data Encryption Standard),已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院 (NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一 [1] 。
(2)java实现
/**
-
/**
-
@author itnanls
-
@date 2021/3/15
*/
public class AESUtil {/**
- 算法秘钥
*/
private static final byte[] AES_KEY = {11, 21, 1, -110, 82, -32, -85,
-128, -65, 44, -2, 23, 12, 32, 23, 45};
/**
- 算法名字
*/
private static final String ALGORITHM_NAME = “AES”;
public static byte[] encrypt(byte[] src) {
try {
// 根据秘钥和加密算法,生成秘钥对象
SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, ALGORITHM_NAME);
// 得到加密器的实例
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
// 初始化密码器
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(src);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}public static byte[] decrypt(byte[] src) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, ALGORITHM_NAME);
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
return cipher.doFinal(src);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}public static void main(String[] args) {
String src = "itnanls is so cool!"; byte[] encrypt = encrypt(src.getBytes()); assert encrypt != null; System.out.println(new String(encrypt)); byte[] decrypt = decrypt(encrypt); assert decrypt != null; System.out.println(new String(decrypt));
}
} - 算法秘钥
五、非对称加密
(1)RSA介绍
RSA加密算法:RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。
解密者拥有私钥,并且将由私钥计算生成的公钥发布给加密者。加密都使用公钥进行加密,并将密文发送到解密者,解密者用私钥解密将密文解码为明文。
(2)java实现
/**
-
@author itnanls
-
@date 2021/3/15
*/
public class RSATest {private static final String PUBLIC_KEY = “publicKey”;
private static final String PRIVATE_KEY = “privateKey”;
private static final String ALGORITHM_NAME = “RSA”;
private static final Integer KEY_SIZE = 1024;
public static final RsaKeyPair RSA_KEY_PAIR = new RsaKeyPair();
/**
- 存放密钥对的静态内部类
*/
private static final class RsaKeyPair {
public RSAPublicKey publicKey;
public RSAPrivateKey privateKey;
}
public static void main(String[] args) throws Exception {
initKey(); // 公钥加密,私钥解密,别人加密给我看,就是加密 byte[] publicKeyEncrypt = encryptByPublicKey("123".getBytes()); byte[] privateKeyDecrypt = decryptByPrivateKey(publicKeyEncrypt); System.out.println(new String(privateKeyDecrypt)); // 私钥加密,公钥解密,我加密了,别人拿公钥解密看,能证明文件是我的,数字签名 byte[] privateKeyEncrypt = encryptByPrivateKey("123".getBytes()); byte[] publicKeyDecrypt = decryptByPublicKey(privateKeyEncrypt); System.out.println(new String(publicKeyDecrypt));
}
// 初始化密钥对
public static void initKey(){
// 实例化密钥对生成器
KeyPairGenerator keyPairGen = null;
try {
keyPairGen = KeyPairGenerator.getInstance(ALGORITHM_NAME);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}// 初始化密钥对生成器 assert keyPairGen != null; keyPairGen.initialize(KEY_SIZE); // 生成密钥对 KeyPair keyPair = keyPairGen.generateKeyPair(); // 公钥 RSA_KEY_PAIR.publicKey = (RSAPublicKey) keyPair.getPublic(); // 私钥 RSA_KEY_PAIR.privateKey = (RSAPrivateKey) keyPair.getPrivate();
}
/**
-
公钥解密
-
@param data
-
待解密数据
-
@return byte[] 解密数据
-
@throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data)
throws Exception {// 取得公钥
// 此类表示根据 ASN.1 类型 SubjectPublicKeyInfo 进行编码的公用密钥的 ASN.1 编码。
// X.509 标准中定义的 SubjectPublicKeyInfo 语法
X509EncodedKeySpec x509KeySpec =
new X509EncodedKeySpec(RSA_KEY_PAIR.publicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);// 生成公钥
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
-
公钥加密
-
@param data
-
待加密数据
-
@return byte[] 加密数据
-
@throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data)
throws Exception {// 取得公钥
X509EncodedKeySpec x509KeySpec =
new X509EncodedKeySpec(RSA_KEY_PAIR.publicKey.getEncoded());KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);// 完成加密
return cipher.doFinal(data);
}
/**
-
私钥加密
-
@param data
-
待加密数据
-
@return byte[] 加密数据
-
@throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data)
throws Exception {// 取得私钥
// 该类代表私有密钥的ASN.1编码,根据ASN.1类型PrivateKeyInfo进行编码。
// PrivateKeyInfo语法在PKCS#8标准中
PKCS8EncodedKeySpec pkcs8KeySpec =
new PKCS8EncodedKeySpec(RSA_KEY_PAIR.privateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);// 生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);// 初始化加密器
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 进行加密
return cipher.doFinal(data);
}
/**
-
私钥解密
-
@param data
-
待解密数据
-
@return byte[] 解密数据
-
@throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data)
throws Exception {// 取得私钥
// 该类代表私有密钥的ASN.1编码,根据ASN.1类型PrivateKeyInfo进行编码。
// PrivateKeyInfo语法在PKCS#8标准中
PKCS8EncodedKeySpec pkcs8KeySpec =
new PKCS8EncodedKeySpec(RSA_KEY_PAIR.privateKey.getEncoded());KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
// 生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
}
- 存放密钥对的静态内部类