最近在项目中使用ras算法进行数据加密传输,加密后的数据需要存储到数据库。在使用过程中发现一个问题在windows上面每次生成的公钥和私钥是一致的,然而把代码上传到服务器(Linux系统)后每次生成的公钥和私钥就不一样了,这样造成之前加密过后的数据,后面服务器重新启动后生成不一样的公钥和私钥就没法解密之前加密过的数据了。
需要引入bcprov-jdk15on-1.55.jar包和commons-codec-1.9.jar
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.9</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.60</version> </dependency>
最初的代码
public class RSAEncrypt { /** * 随机生成密钥对 * @throws NoSuchAlgorithmException */ public static Map<String,String> genKeyPair() throws NoSuchAlgorithmException { Map<String, String> keyMap = new HashMap<>(); // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // 初始化密钥对生成器,密钥大小为96-1024位 keyPairGen.initialize(1024,new SecureRandom()); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.genKeyPair(); System.out.println(keyPair.getPublic().getEncoded()); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥 String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); // 得到私钥字符串 String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded()))); // 将公钥和私钥保存到Map keyMap.put("pub",publicKeyString); keyMap.put("pri",privateKeyString); return keyMap; } /** * RSA公钥加密 * * @param str * 加密字符串 * @param publicKey * 公钥 * @return 密文 * @throws Exception * 加密过程中的异常信息 */ public static String encrypt( String str, String publicKey ) throws Exception{ //base64编码的公钥 byte[] decoded = Base64.decodeBase64(publicKey); RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); //RSA加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8"))); return outStr; } /** * RSA私钥解密 * * @param str * 加密字符串 * @param privateKey * 私钥 * @return 铭文 * @throws Exception * 解密过程中的异常信息 */ public static String decrypt(String str, String privateKey) throws Exception{ //64位解码加密后的字符串 byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8")); //base64编码的私钥 byte[] decoded = Base64.decodeBase64(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); //RSA解密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, priKey); String outStr = new String(cipher.doFinal(inputByte)); return outStr; } public static void main(String[] args) throws Exception { //生成公钥和私钥 Map<String, String> keyMap = genKeyPair(); //加密字符串 String message = "123456"; System.out.println("随机生成的公钥为:" + keyMap.get("pub")); System.out.println("随机生成的私钥为:" + keyMap.get("pri")); String messageEn = encrypt(message,keyMap.get("pub")); System.out.println(message + "\t加密后的字符串为:" + messageEn); String messageDe = decrypt(messageEn,keyMap.get("pri")); System.out.println("还原后的字符串为:" + messageDe); } }
这种生成的密钥对的方式由于是随机数,所以每次都是不一样的,适合前端加密完就进行解密,不需要进行存储,只是做加密传输。
keyPairGen.initialize(1024,new SecureRandom());
每次生成的密钥一致,修改
//如果使用SecureRandom random = new SecureRandom();//windows和linux默认不同,导致两个平台生成的公钥和私钥不同 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); //使用种子则生成相同的公钥和私钥 secureRandom.setSeed("seed".getBytes()); keyPairGenerator.initialize(1024, secureRandom);
后端解密乱码的话可以这样配置
//相同的原文、公钥能生成相同的密文。如果使用Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());//相同的原文、公钥生成的密文不同 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding",new BouncyCastleProvider());
最终修改的代码,windows,linux都生成一致的密钥对
import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; public class RSAEncrypt { /** * 随机生成密钥对 * @throws NoSuchAlgorithmException */ public static Map<String,String> genKeyPair(String seed) throws NoSuchAlgorithmException { Map<String, String> keyMap = new HashMap<>(); // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); //使用种子则生成相同的公钥和私钥 secureRandom.setSeed(seed.getBytes()); // 初始化密钥对生成器,密钥大小为96-1024位 keyPairGen.initialize(1024,secureRandom); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.genKeyPair(); System.out.println(keyPair.getPublic().getEncoded()); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥 String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); // 得到私钥字符串 String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded()))); // 将公钥和私钥保存到Map keyMap.put("pub",publicKeyString); keyMap.put("pri",privateKeyString); return keyMap; } /** * RSA公钥加密 * * @param str * 加密字符串 * @param publicKey * 公钥 * @return 密文 * @throws Exception * 加密过程中的异常信息 */ public static String encrypt( String str, String publicKey ) throws Exception{ //base64编码的公钥 byte[] decoded = Base64.decodeBase64(publicKey); RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); //RSA加密 //相同的原文、公钥能生成相同的密文。如果使用Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());//相同的原文、公钥生成的密文不同 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding",new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, pubKey); String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8"))); return outStr; } /** * RSA私钥解密 * * @param str * 加密字符串 * @param privateKey * 私钥 * @return 铭文 * @throws Exception * 解密过程中的异常信息 */ public static String decrypt(String str, String privateKey) throws Exception{ //64位解码加密后的字符串 byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8")); //base64编码的私钥 byte[] decoded = Base64.decodeBase64(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); //RSA解密 //相同的原文、公钥能生成相同的密文。如果使用Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());//相同的原文、公钥生成的密文不同 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding",new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, priKey); String outStr = new String(cipher.doFinal(inputByte)); return outStr; } public static void main(String[] args) throws Exception { //生成公钥和私钥 Map<String, String> keyMap = genKeyPair("seed"); //加密字符串 String message = "123456"; System.out.println("随机生成的公钥为:" + keyMap.get("pub")); System.out.println("随机生成的私钥为:" + keyMap.get("pri")); String messageEn = encrypt(message,keyMap.get("pub")); System.out.println(message + "\t加密后的字符串为:" + messageEn); String messageDe = decrypt(messageEn,keyMap.get("pri")); System.out.println("还原后的字符串为:" + messageDe); } }
前端使用的是vue,可以npm安装至Vue项目
npm install jsencrypt --dev
页面引入jsencrypt
import { JSEncrypt } from 'jsencrypt'
公钥为后端提供,如前端需要解密数据,则也需要后端提供私钥。
methods: { // 加密 encryptedData(publicKey, data) { // 新建JSEncrypt对象 let encryptor = new JSEncrypt(); // 设置公钥 encryptor.setPublicKey(publicKey); // 加密数据 return encryptor.encrypt(data); }, // 解密 decryptData(privateKey,data){ // 新建JSEncrypt对象 let decrypt= new JSEncrypt(); // 设置私钥 decrypt.setPrivateKey(privateKey); // 解密数据 return decrypt.decrypt(secretWord); } }