一、什么是RSA加密
上世纪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();
}
}