本文档讲述内容与代码示例可参见 Shoulder-Demo1
  1. 设计初衷

提供可配可扩展的常用加解密实现以及通用场景的加密方案。

能力激活方式:

Maven
<dependency>
    <groupId>cn.itlym</groupId>
    <artifactId>shoulder-starter-crypto</artifactId>
    <version>0.8.1<version>
</dependency>
Gradle
compile 'cn.itlym:shoulder-starter-crypto:0.8.1'

能力总览

Shoulder 根据 安全加密规范 做算法选型,并提供了以下算法实现:

  • 对称加密

    • AES

    • SM4国密 安全级别同 AES128

    • 自定义(几十种算法供选择,一行配置切换)

  • 非对称加密

    • RSA

    • ECC (高性能对称加密算法,ECC256 安全级别同 RSA2048

    • SM2国密 ECC 版本)

  • Hash / 摘要

    • MD5 (注意:尽管有 彩虹表 的存在,MD5 算法安全性有限,不适合在密码上使用,但由于其高性能,在信息摘要/大数据对比方面仍然是优秀算法)

    • SHA256

    • SHA256Ex (加随机盐版本的 SHA256

    • SM3国密 安全级别同 SHA256

  • 签名 / 防伪 / 防篡改

    • HmacSha256

    • RSA 基于上文 RSA

    • ECDSA 基于上文 ECC

  • 特殊领域

    • 本地绝密 (高性能多级密钥加密算法,适用于数据库/参数中心/配置中心内数据加解密)

    • 安全通信:ECDH (基于 ECC + DH 的密钥协商算法实现的安全通信算法,类似应用层 HTTPS,适用于非信任网络环境下的安全通信,如接口传输 身份证银行卡号 等敏感信息)

对称加密

大部分应用层场景是对 文本内容(String) 的加解密,请直接使用 SymmetricTextCipher 即可,而无需关心内部密钥细节。

对称加密-示例
@Service
public class SymmetricCryptoDemo {

    // 默认会使用 ECC256 算法
    @Autowired
    private SymmetricTextCipher textCipher;

    public void someMethod(String text) {

        byte[] key = ... 您的密钥
        byte[] iv = ... 您的初始化向量iv
        // 加密
        String cipher = textCipher.encrypt(key, iv, text);
        // 解密
        String decryptText = textCipher.decrypt(key, iv, cipher);

        // 加解密后和原文相等
        AssertUtils.equals(text, decryptText, CommonErrorCodeEnum.UNKNOWN);

    }
}
如果您需要对字节类对象进行加解密,可使用 SymmetricCipher 类,其提供了 byte[] 类型的加解密方法,您可以直接使用 byte[] 类型的加解密方法,无需转换成 String 类型。
若您需要对 数据库配置中 中字段加解密,请使用 本地加解密,更 安全 也更 便携

本地安全加解密

对于高频本地加解密场景:数据库内容缓存内容本地文件内容Shoulder 提供了更安全成熟的解决方案,直接使用 LocalTextCipher

本地安全加解密-示例
@Service
public class LocalCryptoDemo {

    @Autowired
    private LocalTextCipher localTextCipher;

    public void testCrypto(String text) {

        // 加密
        String cipher = localTextCipher.encrypt(text);
        // 解密
        String decryptText = localTextCipher.decrypt(cipher);

        // 加解密后和原文相等
        AssertUtils.equals(text, decryptText, CommonErrorCodeEnum.UNKNOWN);

    }

}

内部实现

LocalTextCipher 默认实现为 LocalTextCipherManager,其内部包含多个 JudgeAbleLocalTextCipher 用于判断密文版本,支持多种加密算法以便于可能的加密算法升级。

LocalTextCipher 需要一个持久化存储存放 密钥初始化向量 IV算法版本与优先级 等信息,默认会按照以下顺序尝试自动加载。

  • 数据库:若类路径中存在 JdbcTemplate.class 则会使用数据库存储,需要数据库中存在表 crypto_info(表结构详见:JdbcLocalCryptoInfoRepository#CREATE_STATEMENT

  • Redis:若类路径中存在 RedisTemplate.class 则会使用 Redis 存储。

  • 文件:若不满足以上两种条件,会自动使用本地文件存储,方便支持重启后仍能对数据加解密。

  • 内存:在内存中存储,重启后之前加密的数据无法再解密!仅演示模式使用,不要在生产中使用!

密钥部件持久化

本地安全加解密 需要外部存储保存密钥部件,以确保重启后仍可解密上次运行加密的数据。

Shoulder 默认在用户 home 目录保存密钥部件,若您想调整保存方式,可在 application.properties 中显示指定 shoulder.crypto.local.repository=file/jdbc 来主动明确要使用怎样的存储方式。

当指定为 jdbc 时,请确保您引入了数据库相关依赖、链接配置正确性 以及表结构已创建。

加密元信息-表结构
CREATE TABLE IF NOT EXISTS crypto_info
(
    app_id        varchar(32)                           not null comment '应用标识',
    header        varchar(32) default ''                not null comment '密文前缀/算法标识/版本标志',
    data_key      varchar(64)                           not null comment '数据密钥(密文)',
    root_key_part varchar(512)                          null comment '根密钥部件',
    vector        varchar(64)                           null comment '初始偏移向量',
    create_time   datetime    default CURRENT_TIMESTAMP null comment '创建时间',
    primary key (app_id, header)
)
    comment '加密元信息';

更多算法

Shoulder 提供了几十种可选的对称加密算法,详见 SymmetricAlgorithmEnum

非对称加密 AsymmetricTextCipher

大部分应用层场景是对 文本内容(String) 的加解密,请直接使用 AsymmetricTextCipher 即可,而无需关心内部密钥细节。

非对称加解密-示例
@Service
public class AsymmetricCryptoDemo {

    @Autowired
    private AsymmetricTextCipher asymmetricTextCipher;

    public void testCrypto(String text) {

        // 加密
        String cipher = asymmetricTextCipher.encrypt(text);
        // 解密
        String decryptText = asymmetricTextCipher.decrypt(cipher);

        // 生成签名摘要
        String sign = asymmetricTextCipher.sign(text);
        // 验签
        boolean signByServer = asymmetricTextCipher.verify(sign);

        // 获取公钥
        String publicKey = asymmetricTextCipher.getPublicKey();

    }

}

若您希望使用多个密钥达成更高程度的安全(如不同用户角色非对称密钥不同),也别担心,Shoulder 替您完成了多密钥管理,您只需在所有方法前添加 密钥对代号 即可。

非对称加解密-多密钥-示例
@Service
public class AsymmetricCryptoDemo {

    @Autowired
    private AsymmetricTextCipher asymmetricTextCipher;

    public void testCrypto(String text) {

        // 确认密钥对
        String keyPairId = ... 密钥对标识

        // 加密
        String cipher = asymmetricTextCipher.encrypt(keyPairId, text);
        // 解密
        String decryptText = asymmetricTextCipher.decrypt(keyPairId, cipher);

        // 生成签名摘要
        String sign = asymmetricTextCipher.sign(keyPairId, text);
        // 验签
        boolean signByServer = asymmetricTextCipher.verify(keyPairId, sign);

        // 获取公钥
        String publicKey = asymmetricTextCipher.getPublicKey(keyPairId);

    }

}
如果您需要对字节类对象进行加解密,可使用 AsymmetricCipher 类,其提供了 byte[] 类型的加解密方法,您可以直接使用 byte[] 类型的加解密方法,无需转换成 String 类型。

公钥端口

内置 /publicKey HTTP 接口(CryptoEndpoint ),以供外部获取服务端公钥,提供 验签非对称加密 必要的接口开放,而无需您手动编码。

若您想关闭(不建议)、或更改该接口的 ApiPath ,则可通过以下配置来调整:

TODO 开发

流式加密

对于大数据量的内容需要更高的性能,AES-GCM 结合了 CTR 的并行计算和 GMAC 消息认证能力,具有 高性能 + 可验证 的特点,无疑是大数据量下“最合适的选择”,在 Shoulder 中对流式内容加密,只需要使用 CryptoableOutputStream 包装即可。

大数据量加解密-示例
@Service
public class CryptoDemo {

    public void testCrypto(String text) {

        // 加解密文件
        String originFilePath = ... 源文件路径,如视频/图片/配置文件...
        String cipherFilePath = ... 加密后的文件路径
        String resultFilePath = ... 加密后再解密的文件路径

        // 加密文件
        CryptoableOutputStream.encryptFile(originFilePath, cipherFilePath);
        // 解密文件
        CryptoableOutputStream.decryptFile(cipherFilePath, resultFilePath);

        // 原生用法
        public OutputStream os = ... 如文件、网络流
        // 加密
        os = new CryptoableOutputStream(os, Cipher.ENCRYPT_MODE);
        // 解密
        os = new CryptoableOutputStream(os, Cipher.DECRYPT_MODE);

    }
}

摘要 Digest (Hash)

可直接使用 Sha256Utils 轻松将 Sha256 算法带到您的应用中

Hash 摘要-示例
@Service
public class DigestDemo {

    public void testCrypto(String text) {

        String text = "yourText";
        // 生成摘要
        String digest = Sha256Utils.digest(text);
        // 校验摘要是否正确
        boolean match = Sha256Utils.verify(digest);

    }
}
Shoulder 也提供了可加盐版本的工具类: Sha256UExtils

签名 Sign (防伪 / 防篡改)

可直接使用 SignUtil 轻松将 HmacSha256 算法带到您的应用中

Hash 摘要-示例
@Service
public class SignDemo {

    public void testSign(String text) {

        String key = ... 签名使用的key
        int validSeconds = ... 有效秒数...
        String text = "yourText";
        // 生成签名
        String sign = SignUtil.sign(text);
        // 校验签名是否正确
        boolean success = SignUtil.verify(sign, key, validSeconds);

    }
}

签名公式:Base64(AK.时间戳.SHA256(Base64(AK).Base64(时间戳), SK))

签名依赖非对称算法,不适合大数据量内容,若需要加密大数据量内容请参考流式加密,若仅需要大数据校验是否正确,请参考摘要 Digest (Hash)

拓展阅读(Shoulder 开发者)

解决问题对应场景

一款软件产品同时给多地区使用,不同地区对软件有不同的政策要求,以对称加密算法为例:

  • 部分海外产品线需要使用美国安全局推荐对称算法: AES256

  • 国内部分业务线有安全算法 国产化 ,需要使用 SM4

  • 某些内网等安全网络环境中,希望使用 AES128 在保证安全情况下以降低功耗,提高性能。

  • 部分产品线要求使用特殊的算法,这些算法还有不同的模式,Padding 填充算法。

Shoulder 中,只需修改一行配置文件,无需修改代码即可动态切换各种算法,满足上述所有场景~