Documentation

A good document is like an intriguing story.

Usage

在特殊场景下,需要对带有敏感信息的请求进行加密传输,大部分情况可以通过 HTTPS 解决,secret-starter 则作为一个更灵活的补充选项。

secret 中的功能已经涉及到密码学,密码学和软件开发不同,软件开发是工程,是手艺,造轮子是写代码的一大乐趣。开发过程中常常要做出多方面的权衡(例如CAP),难有明确的对错。

密码学就不一样了。密码学是科学,不是工程,有严格的技术规范,严禁随意创造。要有严谨的理论建模,严密的数学证明。少有需要权衡的地方,正确就是正确,错误就是错误。

本着爬、走、跑原则,前期提供常用而简单的算法,不要怎么配置就能使用。后期会引入谷歌密码学家的开源项目 tink,做到跨平台跨语言。

                        Copy
                        
<dependency>
    <groupId>net.dragonshard</groupId>
    <artifactId>dragonshard-data-secret-starter</artifactId>
    <version>${latest.version}</version>
</dependency>
                        
                    

配置文件

                        Copy
                        
secret:
    enabled: true
    aes:
        enabled: true
        key: nLa7BnN8W0ORlQI501GLAw==
    rsa:
        enabled: true
        public-key: 10001
        private-key: 1649ac2c27bd8171ad10f8ec070a304dc9de3bb2d9b39616ce9fe83d0d89307a757f30e4713a7a9cf6d402e256e99fa401b39ea335a707d42dc868cb78bfbd064697af6aa0b39b6fb7afa92f9ab7ce197a35f8f6b6d2b78b571e0609924cdd933433fdc2d244ee74686cebfc1736fb17d04a44d79a7b9f480938363b4657087f
        modulus: 8f8ebda0a1f0524da6090c88af8171e2fea5666cbc3e018b6f324324991d21af4b4a8e1633e0d072299d3d42d9798878c4cf4fa6004cb1172f04a1a47297c4bee90e35e62962fdf516497d9ae2c939eb8a6dfd79261acd53edc2b38ad19279b09829c3778b5486920d854833ef3c22ec873aa025113e9825ef76a3ef67707321
                        
                    

配置项前缀dragonshard.secret

配置项 默认值 选项 描述
enabled true true / false 是否开启
aes.enabled false true / false 是否开启
aes.key String 密钥( Base64/Hex模式 )
rsa.enabled false true / false 是否开启
rsa.public-key String 公钥( Base64/Hex模式 )
rsa.private-key String 私钥( Base64/Hex模式 )
rsa.modulus String 系数( Hex模式 )

在想要保护的 Controller 方法上使用@SecretBody注解。

注解参数

配置项 默认值 选项/类型 描述
value aes / rsa 算法
ciphertextType BASE64 BASE64 / HEX 密文类型
providerClass Class<? extends Provider>[] 供应商类名称,仅在JS加密的RSA中使用

目前 providerClass 仅支持 BouncyCastleProvider.class。

AES

这里用 dragonshard-sample 项目中的测试用例进行讲解,源码 看这里

RoleTest#test03RoleInsert方法中,先将准备好的JSON请求报文进行加密,密钥一定要使用配置文件中的 aes.key

                        
// 密钥默认是 byte[] 的,可以进行 Base64 或 Hex 转码
// 下面这句就是生成一个默认 128bit 长度的AES密钥,并Base64转码后输出
String aesKey = EncryptUtils.generateAesKeyBase64();

// 把密钥改回与 dragonshard-sample 相同
aesKey = "nLa7BnN8W0ORlQI501GLAw==";
// 然后对报文加密,aesEncryptBase64 方法的入参密钥也必须是Base64的
String jsonByEncrypt = EncryptUtils.aesEncryptBase64(JSON.toJSONString(roleBO), aesKey);

// 贴出报文的加密结果
// IhZ5bGxPhoqJy83pGxx2A/pJlHrpp+TwkZxF3nPup6KS7CsFPgxLvkE9P1BZTfHi
                        
                    

如果开启了 P6SPY,能看到打印的真实SQL:
P6SPY Execute SQL > INSERT INTO sys_role ( ... ) VALUES ( x, 'testRoleInsert', '2019-08-15xx', '2019-08-15xx', 'remark' )

RSA

适用于服务端之间的非对称加密场景。

                        
// 在对应的controller方法上增加注解
@SecretBody(value = "rsa", ciphertextType = "hex")

// 生成RSA密钥对( hex )
String keyPairHexJson = EncryptUtils.generateRsaKeyPairHexJson();
                        
                    

将上面生成的密钥对添加到配置文件中,这种JDK默认RSA模式下 modulus 不用填写。

                        Copy
                        
secret:
    enabled: true
    rsa:
        enabled: true
        public-key: 30819f300d06092a864886f70d010101050003818d0030818902818100a10ab4526d4c68edb1240b89a12f1aef2ba0d25a1fa5b1a1d89ba3bc576a03bc6d47b5d214cc203745947745a4c5b5b97f9e7587ad69bd6dceeead8561ec0084b9c7016ff168a3c24f94a0b82305d8c0f3ccd63c9cbababfe02f0c61879d7f81f7d2107f6efcd7e63f2903c4fcb09c22941eaa105798e211871dde15f03059730203010001
        private-key: 30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100a10ab4526d4c68edb1240b89a12f1aef2ba0d25a1fa5b1a1d89ba3bc576a03bc6d47b5d214cc203745947745a4c5b5b97f9e7587ad69bd6dceeead8561ec0084b9c7016ff168a3c24f94a0b82305d8c0f3ccd63c9cbababfe02f0c61879d7f81f7d2107f6efcd7e63f2903c4fcb09c22941eaa105798e211871dde15f030597302030100010281810097e557d6135fa9ece053664a876cbdd3ef6bbe5ce152d0ec1e3a174353941c38033f4b40fefad63c2bf5f0561127a6d3738b0bc2508fd6eb96ee4b5eeed0c25ee65f0af2b59238f6ef3a2f7fc4a0e3b1c6f7eb7d701261ea892d699d71899fe8a2c3bbaf3e51e018e3e5ce4e0dad8a793290fcea49e94cb528a2c320e734a631024100fb880b93bbfa3f07c7e6739116b9651d322de1fed8766cb0aef31fb7424f3c118c1f446a0f5b7804c2899b2581c8210129e81bb2ca407f63d8d8ffa4f9404149024100a3e71db5acd1c9eb2fb77dd2a66f2e194d83a0956ef4768eeedf670d53f748bc228b1bc06249e4ec860cbc8403b54c02c8f693f3767ea969568e01bf7cf480db024100bf7c5e73e5932910df59cd7912f7a8c68540f0df762311b3a03c6e54b12268f462dc1ad53343cb26b482d59dc4237b1ccbae6c07bc794581d9bfb93efa91b421024100996249106299b55fce38e69c02bb5b25bcbfa8c10fa3e16ba3aa17d386378daeda98da30b10abc1c88da54752acf384206f592c1adab9d390212451a8182832f02403d7776922828cd8ad8d5047f49fcfe7521f8ffa7af7c7da73e83268c027fe3fc784dd41ec1cc0d08ac2edbbd2dfe662860b564cbae48586643e8d896c8c8dfad
        modulus:
                        
                    

用公钥加密你的请求报文

                        
String jsonByEncrypt = EncryptUtils.rsaEncryptHexByPublicKey("your json string", publicKey);
                        
                    

完整测试用例请参考 dragonshard-sample 的 RoleTest#test03RoleInsertByRSA()

RSA( Js加密服务端解密 )

这种前端加密后端解密的安全策略,在没有 HTTPS 支持的小型项目中非常普遍。

                        
// 注意 providerClass 的写法,目前仅支持 BouncyCastleProvider
@SecretBody(value = "rsa", ciphertextType = "hex", providerClass = BouncyCastleProvider.class)

// 生成RSA密钥对( hex )
System.out.println(EncryptUtils.generateRsaKeyPairByProviderHexJson(new BouncyCastleProvider()));
                        
                    

将上面生成的密钥对添加到配置文件中。

                        Copy
                        
secret:
    enabled: true
    rsa:
        enabled: true
        public-key: 10001
        private-key: 1649ac2c27bd8171ad10f8ec070a304dc9de3bb2d9b39616ce9fe83d0d89307a757f30e4713a7a9cf6d402e256e99fa401b39ea335a707d42dc868cb78bfbd064697af6aa0b39b6fb7afa92f9ab7ce197a35f8f6b6d2b78b571e0609924cdd933433fdc2d244ee74686cebfc1736fb17d04a44d79a7b9f480938363b4657087f
        modulus: 8f8ebda0a1f0524da6090c88af8171e2fea5666cbc3e018b6f324324991d21af4b4a8e1633e0d072299d3d42d9798878c4cf4fa6004cb1172f04a1a47297c4bee90e35e62962fdf516497d9ae2c939eb8a6dfd79261acd53edc2b38ad19279b09829c3778b5486920d854833ef3c22ec873aa025113e9825ef76a3ef67707321
                        
                    

在js中加密你的请求报文,你可以在 这里 找到js源码和 Demo。

                        
<script src="security.js" type="text/javascript"></script>
<script>
    var modulus = '8f8ebda0a1f0524da6090c88af8171e2fea5666cbc3e018b6f324324991d21af4b4a8e1633e0d072299d3d42d9798878c4cf4fa6004cb1172f04a1a47297c4bee90e35e62962fdf516497d9ae2c939eb8a6dfd79261acd53edc2b38ad19279b09829c3778b5486920d854833ef3c22ec873aa025113e9825ef76a3ef67707321';
    var publicKey = '10001';
    var key = RSAUtils.getKeyPair(publicKey, '', modulus);
    var inputText = '{"loginName":"ds-user","nickname":"ds","email":"ds@dragonshard.net","roleIds":["1"]}';
    console.log(RSAUtils.encryptedString(key, inputText));
</script>
                        
                    

完整测试用例请参考 dragonshard-sample 的 UserTest#test03Insert()