编解码和加密详解

文章目录

    • 1. 编码和解码(encode/decode)算法
      • 1.1 Base64算法
      • 1.2 压缩算法
      • 1.3 Java序列化和反序列化
    • 2. 常见加密算法
      • 2.1 什么是加密算法
      • 2.2 MD5算法
      • 2.3 SHA1算法
      • 2.4 HMAC算法
    • 3. 结论

1. 编码和解码(encode/decode)算法

简单理解,编码就是把信息按照一定的规则进行组织,变换消息形式的过程,所谓解码就是把收到的信息按照一定的规则进行解析,并且这个规则往往是固定的,必须是编码者和解码者事先都知道或约定好的。常见的编码手段有Base64和压缩算法。

1.1 Base64算法

Base64最早起源于HTTP协议下的数据传输,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符(在ASCII码中规定,0~31、127这33个字符属于控制字符,32~126这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符)。使用Base64就是将不在这些字符范围内的其他字符转换为可打印字符
具体的64位的映射关系如下图:
编解码和加密详解_第1张图片
Base64算法的固定规则:因为索引最大63,所以Base64的索引最多只占6个Bit,但是ASCII码字符需要8个Bit来表示,所以,Base64按照三个一组,会把连续的3个字节变成4个字节,然后每个字节补齐高位的2个0,这样38就等于46了,也就是说,转换后的字符串理论上将要比原来的长1/3。同时Base64规定,当需要转换的字符不是3的倍数时,一律采用补0的方式凑足3的倍数。

百度百科的例子介绍很形象
转换前 10101101,10111010,01110110
转换后 00101011, 00011011 ,00101001 ,00110110
十进制 43 27 41 54
对应码表中的值 r b p 2
所以上面的24位编码,编码后的Base64值为 rbp2
解码同理,把 rbq2 的二进制位连接上再重组得到三个8位值,得出原码。
(解码只是编码的逆过程,有关MIME的RFC还有很多,如果需要详细情况请自行查找。)
第一个字节,根据源字节的第一个字节处理。
规则:源第一字节右移两位,去掉低2位,高2位补零。
既:00 + 高6位
第二个字节,根据源字节的第一个字节和第二个字节联合处理。
规则如下,第一个字节高6位去掉然后左移四位,第二个字节右移四位
即:源第一字节低2位 + 源第2字节高4位
第三个字节,根据源字节的第二个字节和第三个字节联合处理,
规则第二个字节去掉高4位并左移两位(得高6位),第三个字节右移6位并去掉高6位(得低2位),相加即可
第四个字节,规则,源第三字节去掉高2位即可
//用更接近于编程的思维来说,编码的过程是这样的:
//第一个字符通过右移2位获得第一个目标字符的Base64表位置,根据这个数值取到表上相应的字符,就是第一//个目标字符。
//然后将第一个字符与0x03(00000011)进行与(&)操作并左移4位,接着第二个字符右移4位与前者相或(|),即获得第二个目标字符。
//再将第二个字符与0x0f(00001111)进行与(&)操作并左移2位,接着第三个字符右移6位与前者相或(|),获得第三个目标字符。
//最后将第三个字符与0x3f(00111111)进行与(&)操作即获得第四个目标字符。
//在以上的每一个步骤之后,再把结果与 0x3F 进行 AND 位操作,就可以得到编码后的字符了。

java使用Base64进行编码和解码:

//编码
java.util.Base64.getEncoder().encodeToString("aaaa".getBytes("UTF-8"));
//解码
byte[] decode = Base64.getDecoder().decode(encode);
//转换为String
String aaaa = new String(decode, "UTF-8")

1.2 压缩算法

压缩算法是指在不丢失信息的前提下,缩减数据量以减少存储空间,提高传输、存储和处理效率的一种技术方法。或者是按照一定的算法对数据进行重新组织,减少数据的冗余和存储的空间,在电子与通信领域也常被称为信号编码。
消息压缩的基本原理是霍夫曼树,实现上有很多种,比如常见的gzip、LZW压缩、lz4、游程编码(RLC)、snappy等等,使用过程中根据业务场景,从性能、压缩率等方向去选择合适的。
霍夫曼树和常见的压缩算法详细介绍
压缩算法比较

1.3 Java序列化和反序列化

Java序列化就是指把Java对象转换为字节序列的过程,反之,反序列化就是指把字节序列恢复为Java对象的过程。其作用就是在跨进程跨服务的数据传输过程中,进行对象状态的保存和重建。

下面是摘自 jdk api 文档里面关于接口 Serializable 的描述

  • 类通过实现 java.io.Serializable 接口以启用其序列化功能。
  • 未实现此接口的类将无法使其任何状态序列化或反序列化。
  • 可序列化类的所有子类型本身都是可序列化的。因为实现接口也是间接的等同于继承。
    序列化接口没有方法或字段,仅用于标识可序列化的语义。

关于 serialVersionUID 的描述

  • 序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 “serialVersionUID” 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID;
  • 如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java™ 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 – serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。
    java序列化详解

2. 常见加密算法

2.1 什么是加密算法

加密算法分对称加密和非对称加密,其中对称加密算法的加密与解密 密钥相同,非对称加密算法的加密密钥与解密 密钥不同,此外,还有一类 不需要密钥 的 散列算法。
常见的 对称加密 算法主要有 DES、3DES、AES 等,常见的 非对称算法 主要有 RSA、DSA 等,散列算法 主要有 SHA-1、MD5 等。

2.2 MD5算法

MD5 用的是 哈希函数,它的典型应用是对一段信息产生 信息摘要,以 防止被篡改。严格来说,MD5 不是一种 加密算法 而是 摘要算法。无论是多长的输入,MD5 都会输出长度为 128bits 的一个串 (通常用 16 进制 表示为 32 个字符)。

java.security.MessageDigest.getInstance("MD5").digest(content);

2.3 SHA1算法

SHA1 和 MD5类似,也是一种较流行的消息摘要算法,它比 MD5 的安全性更强。对于长度小于 2 ^ 64 位的消息,SHA1 会产生一个 160 位的 消息摘要。基于 MD5、SHA1 的信息摘要特性以及 不可逆 (一般而言),可以被应用在检查 文件完整性 以及 数字签名 等场景。

java.security.MessageDigest.getInstance("SHA1").digest(content);

2.4 HMAC算法

HMAC 发送方 和 接收方 都基于 key ,利用哈希算法 (MD5、SHA1 等)计算,来传输和保存消息,这样,哪怕黑客窃取了消息,但是没有key,是 无法计算 出正确的 散列值的,这样就可以 防止数据被篡改。

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class EncryptUtil {

    public static String getBase64String(byte[] s) {
        return Base64.getEncoder().encodeToString(s);
    }

    public static byte[] hamcsha1(String data, String key) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            return mac.doFinal(data.getBytes());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

3. 结论

针对网络传输的url和参数保护可以使用编码算法,但是针对加密场景,比如数据保密存储等,千万别使用普通的编码算法,一定要使用加密算法,同理,传输过程中有安全性要求的,除了加密算法,还需要通过密钥来进一步保证数据安全性!

你可能感兴趣的:(温习,java序列化,加密算法,Base64,base64,hmac)