1. Base64

1.1 为什么要使用 Base64

在项目中,将报文进行压缩、加密后,最后一步必然是使用 base64 编码,因为 base64 编码的字符串,更适合不同平台、不同语言的传输

  • 这算法是编码, 不是压缩, 编码后只会增加字节数;(比之前多3分之一,如之前是3,编码后是4)
  • 算法简单, 几乎不会影响效率;
  • 算法可逆, 解码很方便, 不用于私密信息通信;
  • 虽然解码方便, 但毕竟编码了, 肉眼还是不能直接看出原始内容;
  • 加密后的字符串只有[0-9a-zA-Z+/=], 不可打印字符(包括转移字符)也可传输;

公钥证书也好,电子邮件数据也好,经常要用到Base64编码,那么为什么要作一下这样的编码呢?

我们知道在计算机中任何数据都是按 ascii 码存储的,而ascii码的128~255之间的值是不可见字符。
而在网络上交换数据时,比如说从 A 地传到 B 地,往往要经过多个路由设备,
由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。
所以就先把数据先做一个 Base64 编码,统统变成可见字符,这样出错的可能性就大降低了。

public String encode(byte[] bytes){
    byte[] encoded = Base64.getEncoder().encode(bytes);
    return Base64.getEncoder().encodeToString(bytes);
}

public byte[] decode(String s){
    return Base64.getDecoder().decode(s);
}

2. MD5

md5 是签名算法,不是加密算法,一般用来校验、防篡改,不用来加密,因为是不可逆的。

public static String getMD5(final String content) {
  if (StringUtils.isBlank(content)) {
    return null;
  }
  char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  try {
    byte[] btInput = content.getBytes(StandardCharsets.UTF_8);
    // 获得MD5摘要算法的 MessageDigest 对象
    MessageDigest mdInst = MessageDigest.getInstance("MD5");
    // 使用指定的字节更新摘要
    mdInst.update(btInput);
    // 获得密文
    byte[] md = mdInst.digest();
    // 把密文转换成十六进制的字符串形式
    int j = md.length;
    char[] str = new char[j * 2];
    int k = 0;
    for (byte elem : md) {
      // 取字节的高4位
      str[k++] = hexDigits[elem >>> 4 & 0xf];
      // 取字节的低4位
      str[k++] = hexDigits[elem & 0xf];
    }
    return new String(str).toLowerCase();
  } catch (Exception e) {
    log.error("Failed to get md5, error:{}", Throwables.getStackTraceAsString(e));
    return null;
  }
}

3. AES

参考:JAVA实现AES加密、解密

AES

应用场景:传递不适合明文传输的报文

方案:如果用一个密码把消息加密,解密的时候还用这个密码,这种加密算法就是对称加密算法

AES-128-CBC加密模式

/**
 *
 * @param plainText 要加密的内容
 * @param key 盐
 * @return aes 加密后 base64 编码内容
 */
public static String aesEncrypt(String plainText, String key) {
    try {
        SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // CBC模式,需要一个向量iv,可增加加密算法的强度,16 位
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec("fb7Yt0PG6nmf40ZX".getBytes()));
        byte[] byteEncode = plainText.getBytes(StandardCharsets.UTF_8);
        byte[] byteAES = cipher.doFinal(byteEncode);
        return Base64.getEncoder().encodeToString(byteAES);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * 
 * @param encryptedText 要解密的内容
 * @param key 盐
 * @return 解密后的内容
 */
public static String aesDecrypt(String encryptedText, String key) {
    try {
        SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec("fb7Yt0PG6nmf40ZX".getBytes()));
        byte[] byteContent = Base64.getDecoder().decode(encryptedText);
        byte[] byteDecode = cipher.doFinal(byteContent);
        return new String(byteDecode, StandardCharsets.UTF_8);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

4. RSA

非对称加密

方案:

  1. 发送方根据接收方的公钥,对原文进行加密;
  2. 接收方根据自己的私钥,对原文进行解密;
  3. 发送方会对原文摘要,根据自己的私钥,进行(加密)签名;
  4. 接收方会对原文解密后,根据发送方的公钥,进行(解密)验签;
  5. 发送方和接收方只需公开公钥,保护各自的私钥,无需向彼此发送秘钥;
  6. 中间人没有双方的私钥,无法解密报文,也无法伪造签名(报文);

签名:对明文进行摘要后,使用秘钥对摘要进行加密,得到签名
验签:对签名使用秘钥进行解密,与明文的摘要对比,进行验签

缺点:

  • 需要事先生成公钥私钥对
  • 针对每个用户各自生成的一对公钥私钥,要对原文进行多次加密和签名
  • 计算量较大

5. SHA

SHA 和 MD5 一样,属于摘要算法,不算加密。

摘要算法是一种能产生特殊输出格式的算法,这种算法的特点是:无论用户输入什么长度的原始数据,经过计算后输出的密文都是固定长度的,这种算法的原理是根据一定的运算规则对原数据进行某种形式的提取,这种提取就是摘要,被摘要的数据内容与原数据有密切联系,只要原数据稍有改变,输出的“摘要”便完全不同,因此,基于这种原理的算法便能对数据完整性提供较为健全的保障。

但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证。

SHA 的长度比 MD5 长,更安全。

6. HMAC_SHA

HMAC_SHA 和 SHA 不同,HMAC 计算哈希时需要一个密钥,不同密钥算出的结果不同,而 SHA 不需要。

可用作服务端签名验证。