在平时的开发中,涉及到的加密、解密功能基本上都是使用语言自带的库函数来完成,或者使用第三方开源的库。最近想探究下这些加密、解密算法的实现机制,下面大概记录了相关的探究过程。
计算机的编码
原码、反码、补码
- 原码:符号位+真值的绝对值,即用第一位表示符号,其余位表示值。所以8位二进制数的取值范围为
[1111 1111, 0111 1111]
,即[-127, 127]
; - 反码:正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余各位取反;
- 补码:正数的补码是其本身,负数的补码是在其反码的基础上加1;
注意:
1 | String str = "好"; |
字符集
Java中的常用字符编码ASCII、Unicode和UTF-8
在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000到11111111。
ASCII码:上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。ASCII码一共规定了128个字符的编码,比如空格“SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
UTF-8:UTF-8是Unicode的实现方式之一,UTF-8是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。UTF-8的编码规则:
- 单字节的符号,字节的第一位(bit)规定为0,后面7位为这个符号的Unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的;
- n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode码;
编码方式
Base64编码
Base64,即选出64个字符—-小写字母a-z、大写字母A-Z、数字0-9、符号”+”、”/“(再加上作为垫字的”=”,实际上是65个字符)—-作为一个基本字符集,然后其他所有符号都转换成这个字符集中的字符。
Base64转换方式:
- 将每三个字节作为一组,一共是24个二进制位;
- 将这24个二进制位分为四组,每个组有6个二进制位;
- 在每组前面加两个00,扩展成32个二进制位,即四个字节;
- 根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值;
1 | 0 A 17 R 34 i 51 z |
对称加密和非对称加密
对称加密:加密方和解密方使用同一密钥;加密和解密的速度比较快,适用于需要加密的数据量比较大时;密钥传输的过程不安全,且容易被破解,密钥管理也比较麻烦:
- DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合;
- 3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高;
- AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高;
非对称加密:即公私钥加密,加密和解密使用的是两个不同的密钥,假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密:
- RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变;
- DSA(Digital Signature Algorithm):数字签名算法,是一种标准的 DSS(数字签名标准);
- ECC(Elliptic Curves Cryptography):椭圆曲线密码编码学;
公钥负责加密,私钥负责解密;私钥负责签名,公钥负责验证
散列算法:
散列是信息的提炼,通常其长度要比信息小得多,且为一个固定长度。加密性强的散列一定是不可逆的,这就意味着通过散列结果,无法推出任何部分的原始信息。散列应该是防冲突的,即找不出具有相同散列结果的两条信息。具有这些特性的散列结果就可以用于验证信息是否被修改。
散列算法是一种单向算法,用户可以通过Hash算法对目标信息生成一段特定长度的、唯一的Hash值,却不能通过这个Hash值重新获得目标信息。因此Hash算法常用在不可还原的密码存储、信息完整性校验等。
常见的Hash算法:
- MD5(Message Digest Algorithm 5):是RSA数据安全公司开发的一种单向散列算法,非可逆,相同的明文产生相同的密文
- SHA(Secure Hash Algorithm):可以对任意长度的数据运算生成一个160位的数值
HTTPS涉及到的加密、解密
HTTPS 在内容传输的加密上使用的是对称加密,非对称加密只作用在证书验证阶段。
HTTPS的整体过程分为证书验证和数据传输阶段,具体的交互过程如下:
① 证书验证阶段:
1)浏览器发起 HTTPS 请求;
2)服务端返回 HTTPS 证书;
3)客户端验证证书是否合法,如果不合法则提示告警。
② 数据传输阶段:
1)当证书验证合法后,在本地生成随机数;
2)通过公钥加密随机数,并把加密后的随机数传输到服务端;
3)服务端通过私钥对随机数进行解密;
4)服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输。
Java实现
非对称加密、解密
生成公钥、私钥:
1 | // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 |
使用公钥进行加密:
1 | String expr = "i love you"; |
使用私钥进行解密:
1 | byte[] inputByte = Base64.getDecoder().decode(encryStr.getBytes()); |
消息摘要
1 | String message = "i love you"; |
数字签名与验证
生成公钥、私钥文件:
1 | java.security.KeyPairGenerator keygen = |
生成数字签名:
1 | // 从文件中读取私钥,将一个字符串以及其签名保存在一个文件中(字符串和其签名也可以放在不同文件中/‘) |
对数字签名进行验证:
1 | ObjectInputStream targetIn = new ObjectInputStream(new FileInputStream("public.key")); |