最近在項目中使用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);
}
}