场景
用户登录,用JS将密码进行RSA加密后发送至后端,用Java进行解密判断,最后再使用SHA-256+盐的方式加密入库。
这么做的目的就是防止密码等敏感信息在传输过程中被人截获。
生成密钥对
//bouncy castle(轻量级密码术包)是一种用于 Java 平台的开放源码的轻量级密码术包
Provider provider = new BouncyCastleProvider();
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", provider);
keyPairGen.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGen.generateKeyPair();
//打印公钥
System.out.println(keyPair.getPublic());
//打印私钥
System.out.println(keyPair.getPrivate());
运行上面的代码,你会看到输出的密钥对信息
RSA Public Key
modulus: e93159d1e94f8cc6c5de3e613b793de14b22d29308a31092038884aecade8cfdf61b6feab7cc55446a2ef6ff6495f41d3be2e6a9c4e7272e3d94557488f15869b0192ffc736cdce40524d88f909d9e25b04a682dc69f3de1cb309b21ac2caaee1cbc8adf06085a0e5bd9f1c1d7343210f995d4942c5d71f91cf2553c1a8b2615
public exponent: 10001
RSA Private CRT Key
modulus: e93159d1e94f8cc6c5de3e613b793de14b22d29308a31092038884aecade8cfdf61b6feab7cc55446a2ef6ff6495f41d3be2e6a9c4e7272e3d94557488f15869b0192ffc736cdce40524d88f909d9e25b04a682dc69f3de1cb309b21ac2caaee1cbc8adf06085a0e5bd9f1c1d7343210f995d4942c5d71f91cf2553c1a8b2615
public exponent: 10001
private exponent: 100292e39e0c97a238088ba8da4ff7272624f46722e32b2a933286c58769232df67d28c069310eb39d44af3196a2d2f944d8cdb45f2fed9937a21d01dcbb113242ce61829abb6a30f71983f92f94967095c30b17810b7c8ce9a7aa0ca71dacfa794199094478ca6f73e480fb4f77d08f010beb80ccdd2aa68d8ef90293256151
primeP: ffe8981d0e1ae8ba6647d7cb0ab357d06671d77f3ad4ee51bc6a5ea85b47e195096ff7f10b06fa47e0b5c974218560eaf0f1066e25eccd0e83d5b9bb10858dc9
primeQ: e946add5c446f2eaef162db413cbd5abbab0b7e88afe890111803e55594ba5cef2ebf3013d331b16c51b843a741aa1c3e22fdab5b8b9197428a82ae095f54bed
primeExponentP: 434395c7fd4f2160ed4570f91384f44df8bf6f8f279d944eb3a199e3d2aa6026d66631db8fb473d0b4c5d37fccd7cc13b4e50575aa4e45cfcce33e8066e553d1
primeExponentQ: 5532fb4a97e551d37c7f1d322808ca816e2e1cfd51c6a5433ea1182313fa0d1508a4249ccf3d5b2775d616f5427277a29379e6e7781022bbca1da18b6539b201
crtCoefficient: 64d25a445aa31b32013d058d96b13c39938d079407a58eae91d8282c87ce00fd5ce8b1fd95a07a21b980ec596c2d228de96e1ea19292bd339ea506b08bee2014
其中 private exponent 就是私钥,不能泄露出去。
modulus 和 public exponent 要交给前端用于加密。
JS加密
下载js文件
新建一个html文件,并与js文件放在同目录下。
复制下面代码并替换对应的key,就能在控制台看到加密结果。
<html>
<body>
<script src="security.js" type="text/javascript"></script>
<script>
var key = RSAUtils.getKeyPair('10001', '', 'c6e442535e1dd6968e4ccd7735299278d989cb938a2f97c1081c4e6796895a3063510592e2e90ed427d5a604428ce46391dcb2ba6b5f4a86af1347237d1de489a0dc2f68a1f9a265d1ec350fccd8a76be5004211cee5bf05a083afa17cf335871b141e5c4329f69d1a3546613e0fa7833b7a253c460e5bb0c075dacfccfd6d0d');
console.log(RSAUtils.encryptedString(key, '123456'));
</script>
</body>
</html>
服务端解密
这里服务端使用的是Java,也可以基于.NET实现。
给出可直接执行的测试代码:
String hexModulus = "c6e442535e1dd6968e4ccd7735299278d989cb938a2f97c1081c4e6796895a3063510592e2e90ed427d5a604428ce46391dcb2ba6b5f4a86af1347237d1de489a0dc2f68a1f9a265d1ec350fccd8a76be5004211cee5bf05a083afa17cf335871b141e5c4329f69d1a3546613e0fa7833b7a253c460e5bb0c075dacfccfd6d0d";
String hexPrivateExponent = "1ce625c15c66146a983d82cd493c95242ae35603ba73b4f810682c838d0f4bbb242d5c2bc9cee12b41bff1108b369885fadaa05f0f68bafeb915445e5cd006645eb816d9a8d89b155aeb8478a60325cf4d7e69c12b3076a4cf31b8c24530e7f13533826bc7e87ddf65b7ccc65951bd7c34238b9f08a1e8a1c2c4dd762c50309";
// 这就是上面html输出的密文
String encryptPassword = "14678fe360b5661f09443d4e377489930aed7c7b29b49223679527bc31bd93dfc575e97f331af79c84b8a76d396e8d8a12c872c800440547b538003b4241cf4078d998644ac444e65d762f6317f71c2b315d891c1980b868dbcad07d211e82fb39f5f2877d849687864be017b5663db6b6f382ab345ef3dce72babb97fe3fbdc";
Provider provider = new BouncyCastleProvider();
KeyFactory keyFac = KeyFactory.getInstance("RSA", provider);
RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(hexModulus, 16), new BigInteger(hexPrivateExponent, 16));
// 生成用于解密的私钥
RSAPrivateKey pk = (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
// 解密
Cipher cipher = Cipher.getInstance("RSA", provider);
cipher.init(2, pk);
byte[] rawPasswordByte = cipher.doFinal(Hex.decodeHex(encryptPassword.toCharArray()));
System.out.println(StringUtils.reverse(new String(rawPasswordByte)));