# Cipher 消息加密

MornBoot提供统一加密组件,主要目的是屏蔽不同算法之间的差异,规避高昂的学习成本。JDK、Spring等框架虽然提供了非常完善的加密算法,但一些较为复杂的算法(例如:AES),则需要大量的时间学习概念和API的使用。

Since:v1.2.1

当前支持:

  • AES
    • ECB
    • GCM
  • BCrypt
  • MD5

# 必要配置

application.properties

#加密密码(16位字符串)
morn.cipher.password=HZlhUvtwrscvQl2c

在网络安全中,一般来说不会防范有文件读写权限的攻击者,这应该是另一个维度的安全问题。应用中通常只能提高这类攻击者的破译成本,因此建议集成配置中心设置密码。如果对密码的安全要求极高,目前建议继承加密实现类,重写密码的读取方式,在后续版本中,可能会推出密钥的多种读取方式。

# AES

默认密钥长度为128位,向量长度为12位。配置参考:CipherProperties

根据目前的科学技术,128位的密钥已经达到了暴力破解远远无法承受的地步,比起盲目提高密钥长度,更应该保证的是密钥的安全。

注意:AES密文均采用Base64编码,将加密后的字节编译为可读文本。

# AES-ECB

ECB算法相对简单,没有防篡改功能,适合加密有隐私性,但不重要的数据。

public class CiphersTest {

  /**
   * 原始文本
   */
  private static final String PLAIN_TEXT = "password";

  /**
   * ECB加密
   */
  public void testAESForECB() {
    // 加密
    Algorithm algorithm = AlgorithmBuilder.withName(AES).mode(AES_ECB_PKCS5PADDING).build();
    String encrypt = Ciphers.encrypt(algorithm, PLAIN_TEXT);
    // 解密
    String decrypt = Ciphers.decrypt(algorithm, encrypt);
    // 校验
    boolean matches = Ciphers.matches(algorithm, PLAIN_TEXT, encrypt);
  }
}

密文示例:

3Rfxf5ENwbS924owowS9QA==

# AES-GCM

GCM算法更加复杂,并且验证了完整性、真实性,同时MornBoot几乎屏蔽了ECB和GCM的使用差异(随机向量、连接消息),大部分场景比ECB更加适用。

public class CiphersTest {

  /**
   * 原始文本
   */
  private static final String PLAIN_TEXT = "password";

  /**
   * GCM加密
   */
  public void testAESForGCM() {
    // 加密
    Algorithm algorithm = AlgorithmBuilder.withName(AES).mode(AES_GCM_NO_PADDING).build();
    String encrypt = Ciphers.encrypt(algorithm, PLAIN_TEXT);
    // 解密
    String decrypt = Ciphers.decrypt(algorithm, encrypt);
    // 校验
    boolean matches = Ciphers.matches(algorithm, PLAIN_TEXT, encrypt);
  }
}

密文示例:

AAAADOiw5/1GeSwJpxcM/mcskV9Dkn6DXl1hpFkgTmKdi1uYFMB6XA==

# BCrypt

准确来说是SpringBCrypt,是SpringSecurity (opens new window)的默认加密算法,作用类似MD5,通常用于加密登录密码。SpringSecurity (opens new window)会在加密文本中记录算法标识,与BCrypt (opens new window)略有差别。

public class CiphersTest {

  /**
   * 原始文本
   */
  private static final String PLAIN_TEXT = "password";

  public void encryptBCrypt() {
    // 加密
    String password = Ciphers.encrypt(SPRING_B_CRYPT, PLAIN_TEXT);
    // 匹配
    boolean matches = Ciphers.matches(SPRING_B_CRYPT, PLAIN_TEXT,
        "{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG");
  }
}

密文示例:

{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG

# MD5

信息摘要算法,主要用于防篡改,验证信息完整性。

public class CiphersTest {

  /**
   * 原始文本
   */
  private static final String PLAIN_TEXT = "password";

  public void encryptMD5() {
    // 加密
    String password = Ciphers.encrypt(MD5, PLAIN_TEXT);
    // 匹配
    boolean matches = Ciphers.matches(MD5, PLAIN_TEXT, "5f4dcc3b5aa765d61d8327deb882cf99");
  }
}

密文示例:

5f4dcc3b5aa765d61d8327deb882cf99

# Other

针对MornBoot尚未收录的算法,或业务项目中的特殊算法,MornBoot也支持自由扩充算法池。

# 扩充加密算法

使用@AlgorithmName标注算法名称,实现AlgorithmEncryption接口,即可编写自定义算法。同理,实现AlgorithmDecryption接口,即可编写自定义解密算法。

通常还需要继承SimpleAlgorithmHolder,可以访问Algorithm对象。

@AlgorithmName(SPRING_B_CRYPT)
public class SecurityBCryptEncryption extends SimpleAlgorithmHolder implements AlgorithmEncryption {

  @Override
  public String encrypt(CharSequence text) {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(text);
  }
}

注意通过@Bean/@Component等任意方式注入Spring容器中,使用Ciphers调用加密算法。

# 扩充匹配算法

实现方式和加密类似。一般来说,不可逆加密需要将明文加密后与密文匹配;可逆加密则需要将密文解密后与明文匹配,很多可逆加密会参入随机数进行运算。

@Slf4j
@AlgorithmName(SPRING_B_CRYPT)
public class SecurityBCryptMatcher extends SimpleAlgorithmHolder implements AlgorithmMatcher {

  @Override
  public boolean matches(CharSequence rawText, CharSequence encodedText) {
    try {
      return PasswordEncoderFactories.createDelegatingPasswordEncoder()
          .matches(rawText, encodedText.toString());
    } catch (Exception e) {
      log.warn(e.getMessage(), e);
      return false;
    }
  }
}

相对复杂的示例可以参考AES加密:AESAlgorithms