密码学
Byte和Bit
Byte
: 字节. 数据存储的基本单位,比如移动硬盘1T , 单位是byte
bit
: 比特, 又叫位. 一个位要么是0要么是1. 数据传输的单位 , 比如家里的宽带100MB,下载速度并没有达到100MB,一般都是12-13MB,那么是因为需要使用 100 / 8
关系: 1Byte = 8bit
英文对应的字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class ByteBit { public static void main(String[] args) { String a = "a"; byte[] bytes = a.getBytes(); for (byte b : bytes) { int c=b; System.out.println(c); String s = Integer.toBinaryString(c); System.out.println(s); } } }
|
打印英文字符串的字节,实际打印的就是ascii
编码!
中文对应的字节
1 2 3 4 5 6 7 8 9 10 11
| public class ByteBitDemo { public static void main(String[] args) throws Exception{ String a = "尚"; byte[] bytes = a.getBytes(); for (byte b : bytes) { System.out.print(b + " "); String s = Integer.toBinaryString(b); System.out.println(s); } } }
|
在编码UTF-8
的中,一个中文是由三个字节组成的!
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void main(String[] args) throws Exception{ String a = "尚"; byte[] bytes = a.getBytes("GBK"); for (byte b : bytes) { System.out.print(b + " "); String s = Integer.toBinaryString(b); System.out.println(s); } }
|
在编码GBK
的中,一个中文是由两个字节组成的!在UTF-8
编码格式中,一个中文由占3个字节组成。
常见加密
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
示例:
- 我们现在有一个原文3要发送给B
- 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
- B拿到密文324后, 使用324/108 = 3 得到原文
常见加密算法:
DES
: Data Encryption Standard
,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
AES
: Advanced Encryption Standard
, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
特点:
- 加密速度快, 可以加密大文件
密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
加密后Ascii
编码表找不到对应字符, 会出现乱码
一般结合Base64
使用
Base64
Base64
是网络上最常见的用于传输8Bit
字节码的可读性编码算法之一,可读性编码算法不是为了保护数据的安全性,而是为了可读性,可读性编码不改变信息内容,只改变信息内容的表现形式。
所谓Base64
,即是说在编码过程中使用了64种字符:
① 小写 a - z = 26个字母
② 大写 A - Z = 26个字母
③ 数字 0 - 9 = 10 个数字
④ + / = 2个符号
Base58
是Bitcoin
(比特币)中使用的一种编码方式,主要用于产生Bitcoin
的钱包地址,相比Base64
,Base58
不使用数字”0”,字母大写”O”,字母大写”I”,和字母小写”i”,以及”+”和”/“符号。
base64
是 3个字节为一组,一个字节 8位,一共 就是24位 ,然后,把3个字节转成4组,每组6位
3 * 8 = 4 * 6 = 24 ,每组6位,缺少的2位,会在高位进行补0,这样做的好处在于 ,base取的是后面6位,去掉高2位 ,那么Base64
的取值就可以控制在0-63位了,所以就叫base64,111 111 = 32 + 16 + 8 + 4 + 2 + 1 = 63
可能发现一个问题,base64
加密后有个=
号,但是在映射表里面没有发现 =
号 , 这个地方需要注意,等号非常特殊,因为base64是三个字节一组 ,如果当我们的位数不够的时候,会使用等号来补齐。
1 2 3 4 5 6 7 8 9 10 11 12
| import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
public class TestBase64 { public static void main(String[] args) { System.out.println(Base64.encode("1".getBytes())); System.out.println(Base64.encode("12".getBytes())); System.out.println(Base64.encode("123".getBytes())); System.out.println(Base64.encode("测试".getBytes())); } }
|
Base64
的相关包默认使用apache
提供的即可,不同的依赖包,提供的API
略有不同。
在Eclipse
中,可以使用import org.apache.tomcat.util.codec.binary.Base64;
,
在Idea
中,可以使用import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
DES加密
Cipher文档
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
| import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec;
import org.apache.tomcat.util.codec.binary.Base64; public class DesDemo { public static void main(String[] args) throws Exception { String input = "测试测试"; String key = "12345678"; String transformation = "DES"; String algorithm = "DES";
System.out.println("原文:" + input); String encryptDes = encryptDes(input, key, transformation, algorithm); System.out.println("加密:" + encryptDes); String decryptDes = decryptDes(encryptDes, key, transformation, algorithm); System.out.println("解密:" + decryptDes); }
public static String encryptDes(String input, String key, String transformation, String algorithm) throws Exception { Cipher cipher = Cipher.getInstance(transformation); SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] bytes = cipher.doFinal(input.getBytes()); return Base64.encodeBase64String(bytes); }
public static String decryptDes(String encryptDes, String key, String transformation, String algorithm) throws Exception { Cipher cipher = Cipher.getInstance(transformation); SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); byte[] bytes = cipher.doFinal(Base64.decodeBase64(encryptDes)); return new String(bytes); } }
|
DES
规定密钥key
必定是8个字节,如果加密算法的key不足8个字节时,如key=123456
将会出现以下错误:
1 2 3 4 5 6 7
| byte[] bytes = cipher.doFinal(input.getBytes()); for (byte b : bytes) { System.out.println(b); }
System.out.println(new String(bytes));
|
当在加密的过程中执行上述代码,以字符串的形式直接输出加密后的bytes
数组,会出现,将会出现以下错误:
出现乱码是因为对应的字节出现负数,但负数,没有出现在 ascii
码表里面,所以常使用base64
配合转码!
1 2 3
| byte[] bytes = cipher.doFinal(input.getBytes());
System.out.println(Base64.encodeBase64String(bytes));
|
AES加密
AES
加密解密和 DES
加密解密代码一样,只需要修改加密算法就行,可以直接拷贝 DSC
加解密代码。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class AesDemo { public static void main(String[] args) throws Exception { String input = "测试测试"; String key = "1234567812345678"; String transformation = "AES"; String algorithm = "AES";
String encryptDes = encryptDes(input, key, transformation, algorithm); System.out.println("加密:" + encryptDes); } }
|
AES
比DES
要高级,所以Key
的大小必须是16个字节,如果依旧使用8个字节,将会遇见以下错误:
加密模式
加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
ECB
ECB
: Electronic codebook, 电子密码本. 需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密。
优缺点:
优点 : 可以并行处理数据
缺点 : 同样的原文生成同样的密文, 不能很好的保护数据
CBC
CBC
: Cipher-block chaining, 密码块链接. 每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。
优缺点:
优点 : 同样的原文生成的密文不一样,加密很安全
缺点 : 串行处理数据,加密速度很慢
填充模式
当需要按块处理的数据, 数据长度不符合块处理需求时, 按照一定的方法填充满块长的规则。
NoPadding
不填充,在DES
加密算法下, 要求原文长度必须是8byte
的整数倍,在AES
加密算法下, 要求原文长度必须是16byte
的整数倍。
PKCS5Padding
数据块的大小为8位, 不够就补足
Tips:
- 如果没有写加密模式和填充模式,则默认使用的加 密模式和填充模式为 :
ECB/PKCS5Padding
- 如果使用
CBC
模式, 在初始化Cipher
对象时, 需要增加参数, 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());
加密模式和填充模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| AES/CBC/NoPadding (128) AES/CBC/PKCS5Padding (128) AES/ECB/NoPadding (128) AES/ECB/PKCS5Padding (128) DES/CBC/NoPadding (56) DES/CBC/PKCS5Padding (56) DES/ECB/NoPadding (56) DES/ECB/PKCS5Padding (56) DESede/CBC/NoPadding (168) DESede/CBC/PKCS5Padding (168) DESede/ECB/NoPadding (168) DESede/ECB/PKCS5Padding (168) RSA/ECB/PKCS1Padding (1024, 2048) RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048) RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
|
ECB案例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import org.apache.tomcat.util.codec.binary.Base64; public class DesDemo { public static void main(String[] args) throws Exception { String input = "测试测试"; String key = "12345678"; String transformation = "DES/ECB/PKCS5Padding"; String algorithm = "DES";
String encryptDes = encryptDes(input, key, transformation, algorithm); System.out.println("加密:" + encryptDes); } }
|
CBC案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public static String encryptDes(String input, String key, String transformation, String algorithm) throws Exception { Cipher cipher = Cipher.getInstance(transformation); SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm); IvParameterSpec iv = new IvParameterSpec(key.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv); byte[] bytes = cipher.doFinal(input.getBytes()); return Base64.encodeBase64String(bytes); }
public static String decryptDes(String encryptDes, String key, String transformation, String algorithm) throws Exception { Cipher cipher = Cipher.getInstance(transformation); SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm); IvParameterSpec iv = new IvParameterSpec(key.getBytes()); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,iv); byte[] bytes = cipher.doFinal(Base64.decodeBase64(encryptDes)); return new String(bytes); }
|
NoPadding案例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import org.apache.tomcat.util.codec.binary.Base64; public class DesDemo { public static void main(String[] args) throws Exception { String input = "测试12"; String key = "12345678"; String transformation = "DES/ECB/NoPadding"; String algorithm = "DES";
String encryptDes = encryptDes(input, key, transformation, algorithm); System.out.println("加密:" + encryptDes); } }
|
使用不填充模式Nopadding
时,加密的文本必须是8个字节的倍数,若上述的input = "测试"
,只有6个字节时(UTF-8下),将会出现以下错误:
值得一提的是,在使用iv
向量进行加密时,加密的key
也必须是8个字节的倍数!