上世纪70年代产生的一种加密算法,其加密方式比较特殊,需要两个密钥:公开密钥简称公钥(publickey)和私有密钥简称私钥(privatekey)。公钥加密,私钥解密;私钥加密,公钥解密。这个算法就是伟大的RSA。
使用公钥将数据加密,并通过私钥对加密信息进行解密。针对我们遇到的问题,公钥放在前端对用户名密码进行加密,私钥放在服务端对前端提交的加密数据进行解密,然后在做登陆的业务操作。流程如下图所示:
根据模数和指数生成publicKey
前端使用 node-forge 插件进行加密,后端并不会返回pem格式的公钥,而是返回模数(modules)和公钥指数(publicExponent),前端根据这两个参数去生成公钥
npm install --save node-forge
import forge from 'node-forge' /** * * @param msg 需要加密的字符 * @param modules 模数 * @param publicExponent 公钥指数 * @returns 加密之后的字符串 16进制 */ function aesEncrypt(msg, modules, publicExponent) { const BigInteger = forge.jsbn.BigInteger const n = forge.util.createBuffer(modules).toString('hex') const e = forge.util.createBuffer(publicExponent).toString('hex') const publicKey = forge.pki.setRsaPublicKey(new BigInteger(n, 16), new BigInteger(e, 16)) const buffer = forge.util.createBuffer(msg, 'utf8') const bytes = buffer.getBytes() const res = publicKey.encrypt(bytes, 'RSA-OAEP', { md: forge.md.sha256.create(), mgf1: { md: forge.md.sha256.create() } }) return forge.util.bytesToHex(res) } /** * * @param serect 需要解密的字符串 * @param modules 模数 * @param publicExponent 公钥指数 * @param privateExponent 私钥指数 * @returns 解密后的字符串 */ function aesDecrypt(serect, modules, publicExponent, privateExponent) { const BigInteger = forge.jsbn.BigInteger const n = forge.util.createBuffer(modules).toString('hex') const e = forge.util.createBuffer(publicExponent).toString('hex') const d = forge.util.createBuffer(privateExponent).toString('hex') const privateKey = forge.pki.setRsaPrivateKey(new BigInteger(n, 16), new BigInteger(e, 16), new BigInteger(d, 16)) const res = privateKey.decrypt(res, 'RSA-OAEP', { md: forge.md.sha256.create(), mgf1: { md: forge.md.sha256.create() } }) return res }
package com.example; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.security.InvalidParameterException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Date; import javax.crypto.Cipher; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateFormatUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.Test; /** * Unit test for simple App. */ public class AppTest { public static String RSA_KEY = "RSA"; public static String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider(); private static KeyFactory keyFactory = null; public static String PrivateEx="4abbae70395..."; public static String PrivateMo="c8c156b0806..."; // modules public static String PublicEx="10001"; public static String PublicMo="c8c156b0806..."; // modules static { try { keyFactory = KeyFactory.getInstance("RSA", DEFAULT_PROVIDER); } catch (NoSuchAlgorithmException ex) { } } public static String UTF8 = "UTF-8"; /** * 通过私钥byte[]将公钥还原,适用于RSA算法 * * @param keyBytes * @return * @throws Exception */ public static RSAPrivateKey getPrivateKey(String modulus, String exponent) throws Exception { RSAPrivateKeySpec privateKey = new RSAPrivateKeySpec(new BigInteger(modulus,16), new BigInteger(exponent,16)); return (RSAPrivateKey) keyFactory.generatePrivate(privateKey); } public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception { RSAPrivateKey rsaKey = (RSAPrivateKey) privateKey; Cipher ci = Cipher.getInstance(RSA_ALGORITHM, DEFAULT_PROVIDER); ci.init(Cipher.DECRYPT_MODE, rsaKey); ByteArrayInputStream inputStream = new ByteArrayInputStream(data); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Integer index = 0; Integer blockSize = rsaKey.getModulus().bitLength() / 8; byte[] buffer = new byte[blockSize]; while ((index = inputStream.read(buffer, 0, blockSize)) > 0) { byte[] clpherBlock = ci.doFinal(buffer, 0, index); outputStream.write(clpherBlock); } byte[] result= ((ByteArrayOutputStream) outputStream).toByteArray(); outputStream.flush(); return result; } public static String decryptString(PrivateKey privateKey, String encrypttext) { try { byte[] en_data = Hex.decodeHex(encrypttext.toCharArray()); byte[] data = decrypt(privateKey, en_data); return new String(data); } catch (Exception ex) { ex.printStackTrace(); } return null; } /** * Rigorous Test :-) */ public void shouldAnswerWithTrue() { String exponent = PrivateEx; String modulus =PrivateMo; String encryption = "3532a86906a..."; // 需要解密的串 try { String result = decryptString(getPrivateKey(modulus, exponent),encryption); System.out.println("解密结果:" + result); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { new AppTest().shouldAnswerWithTrue(); } }
参考:
node-forge