RSA和AES加密解密過程
RSA:
非對稱加密,公鑰為服務提供方用來加密,私鑰為請求方收到消息后用來解密,由於加密和解密時,所使用的秘鑰不同,所以稱之為非對稱加密
是公開密鑰系統的代表;
安全性:建立在具有大素數因子的合數,其因子分解困難這一法則之上;
處理速度慢;
密鑰管理:加解密過程中不必網絡傳輸保密的密鑰;密鑰管理優於AES算法;
RSA加解密速度慢,不適合大量數據文件加密;
AES:
對稱加密:需要使用隨機碼或者其他字符串來作為秘鑰加密,同時解密也是使用相同的秘鑰解密,因此稱之為對稱加密
Rijndael算法是新一代的高級加密標准,運行時不需計算機有非常高的處理能力和大的內存;
操作可以很容易的抵御時間和空間的攻擊,在不同的運行環境下始終保持良好的性能;
AES密鑰長度:最長只有256bit,可用軟件和硬件實現高速處理;
密鑰管理:要求在通信前對密鑰進行秘密分配,解密的私鑰必須通過網絡傳送至加密數據接收方;
AES加密速度很快;
AES+RSA:
混合加密:安全性和優點都發揮出來了,是企業常用的加密方式,博主的例子,也是混合加密
使用AES對稱密碼體制對傳輸數據加密,同時使用RSA不對稱密碼體制來傳送AES的密鑰,就可以綜合發揮AES和RSA的優點同時
避免它們缺點來實現一種新的數據加密方案
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import net.sf.json.JSONString;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
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 HOPoifHeadUtil {
private static final Logger logger = LoggerFactory.getLogger(HOPoifHeadUtil.class);
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/*
* 加密
* 1.構造密鑰生成器
* 2.根據ecnodeRules規則初始化密鑰生成器
* 3.產生密鑰
* 4.創建和初始化密碼器
* 5.內容加密
* 6.返回字符串
*/
public static String encodeAES(String encodeRules, String content) {
try {
//1.構造密鑰生成器,指定為AES算法,不區分大小寫
KeyGenerator keygen = KeyGenerator.getInstance("AES");
//2.根據ecnodeRules規則初始化密鑰生成器
//生成一個128位的隨機源,根據傳入的字節數組
//防止linux下 隨機生成key
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(encodeRules.getBytes());
keygen.init(128, secureRandom);
//3.產生原始對稱密鑰
SecretKey original_key = keygen.generateKey();
//4.獲得原始對稱密鑰的字節數組
byte[] raw = original_key.getEncoded();
//5.根據字節數組生成AES密鑰
SecretKey key = new SecretKeySpec(raw, "AES");
//6.根據指定算法AES自成密碼器
Cipher cipher = Cipher.getInstance("AES");
//7.初始化密碼器,第一個參數為加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二個參數為使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
//8.獲取加密內容的字節數組(這里要設置為utf-8)不然內容中如果有中文和英文混合中文就會解密為亂碼
byte[] byte_encode = content.getBytes("utf-8");
//9.根據密碼器的初始化方式--加密:將數據加密
byte[] byte_AES = cipher.doFinal(byte_encode);
//10.將加密后的數據轉換為字符串
//這里用Base64Encoder中會找不到包
//解決辦法:
//在項目的Build path中先移除JRE System Library,再添加庫JRE System Library,重新編譯后就一切正常了。
String AES_encode = new BASE64Encoder().encode(byte_AES);
//11.將字符串返回
return AES_encode;
} catch (NoSuchAlgorithmException e) {
logger.error("encodeAES.NoSuchAlgorithmException異常" , e.getMessage());
} catch (NoSuchPaddingException e) {
logger.error("encodeAES.NoSuchPaddingException異常" , e.getMessage());
} catch (InvalidKeyException e) {
logger.error("encodeAES.InvalidKeyException異常" , e.getMessage());
} catch (IllegalBlockSizeException e) {
logger.error("encodeAES.IllegalBlockSizeException異常" , e.getMessage());
} catch (BadPaddingException e) {
logger.error("encodeAES.BadPaddingException異常" , e.getMessage());
} catch (UnsupportedEncodingException e) {
logger.error("encodeAES.UnsupportedEncodingException異常" , e.getMessage());
}
//如果有錯就返加nulll
return null;
}
/*
* 解密
* 解密過程:
* 1.同加密1-4步
* 2.將加密后的字符串反紡成byte[]數組
* 3.將加密內容解密
*/
public static String deCodeAES(String encodeRules, String content) {
try {
//1.構造密鑰生成器,指定為AES算法,不區分大小寫
KeyGenerator keygen = KeyGenerator.getInstance("AES");
//2.根據ecnodeRules規則初始化密鑰生成器
//生成一個128位的隨機源,根據傳入的字節數組
//防止linux下 隨機生成key
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(encodeRules.getBytes());
keygen.init(128, secureRandom);
//3.產生原始對稱密鑰
SecretKey original_key = keygen.generateKey();
//4.獲得原始對稱密鑰的字節數組
byte[] raw = original_key.getEncoded();
//5.根據字節數組生成AES密鑰
SecretKey key = new SecretKeySpec(raw, "AES");
//6.根據指定算法AES自成密碼器
Cipher cipher = Cipher.getInstance("AES");
//7.初始化密碼器,第一個參數為加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二個參數為使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
//8.將加密並編碼后的內容解碼成字節數組
byte[] byte_content = new BASE64Decoder().decodeBuffer(content);
/*
* 解密
*/
byte[] byte_decode = cipher.doFinal(byte_content);
String AES_decode = new String(byte_decode, "utf-8");
return AES_decode;
} catch (NoSuchAlgorithmException e) {
logger.error("deCodeAES.NoSuchAlgorithmException異常" , e.getMessage());
} catch (NoSuchPaddingException e) {
logger.error("deCodeAES.NoSuchPaddingException異常" , e.getMessage());
} catch (InvalidKeyException e) {
logger.error("deCodeAES.InvalidKeyException異常" , e.getMessage());
} catch (IOException e) {
logger.error("deCodeAES.IOException異常" , e.getMessage());
} catch (IllegalBlockSizeException e) {
logger.error("deCodeAES.IllegalBlockSizeException" , e.getMessage());
} catch (BadPaddingException e) {
logger.error("deCodeAES.BadPaddingException異常" , e.getMessage());
}
//如果有錯就返加nulll
return null;
}
/**
* 獲取密鑰對
*
* @return 密鑰對
*/
public static KeyPair getKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
return generator.generateKeyPair();
}
/**
* 獲取私鑰
*
* @param privateKey 私鑰字符串
* @return
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = org.apache.commons.codec.binary.Base64.decodeBase64(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
return keyFactory.generatePrivate(keySpec);
}
/**
* 獲取公鑰
*
* @param publicKey 公鑰字符串
* @return
*/
public static PublicKey getPublicKey(String publicKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = org.apache.commons.codec.binary.Base64.decodeBase64(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
return keyFactory.generatePublic(keySpec);
}
/**
* RSA加密
*
* @param data 待加密數據
* @param publicKey 公鑰
* @return
*/
public static String encrypt(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = data.getBytes().length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0;
byte[] cache;
int i = 0;
// 對數據分段加密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
// 獲取加密內容使用base64進行編碼,並以UTF-8為標准轉化成字符串
// 加密后的字符串
return new String(org.apache.commons.codec.binary.Base64.encodeBase64String(encryptedData));
}
/**
* RSA解密
*
* @param data 待解密數據
* @param privateKey 私鑰
* @return
*/
public static String decrypt(String data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] dataBytes = org.apache.commons.codec.binary.Base64.decodeBase64(data);
int inputLen = dataBytes.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0;
byte[] cache;
int i = 0;
// 對數據分段解密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
// 解密后的內容
return new String(decryptedData, "UTF-8");
}
public static void main(String[] args) {
// 獲取6位隨機碼
String randomCode = SmsUtil.getRandNum(6);
try {
// 生成密鑰對
KeyPair keyPair = getKeyPair();
String privateKey = new String(org.apache.commons.codec.binary.Base64.encodeBase64(keyPair.getPrivate().getEncoded()));
String publicKey = new String(org.apache.commons.codec.binary.Base64.encodeBase64(keyPair.getPublic().getEncoded()));
System.out.println("私鑰:" + privateKey);
System.out.println("公鑰:" + publicKey);
// RSA加密
String data = randomCode;
String encryptData = encrypt(data, getPublicKey(publicKey));
System.out.println("加密后內容:" + encryptData);
// RSA解密
String decryptData = decrypt(encryptData, getPrivateKey(privateKey));
System.out.println("解密后內容:" + decryptData);
System.out.println("--------------------------加密過程:先RAS(非對稱)將隨機碼加密,然后再用隨機碼(明文)AES(對稱)加密-----------------------------------------");
System.out.println("--------------------------解密過程(核心,要先拿到秘鑰,即隨機碼):先RAS(非對稱)解密獲取隨機碼,然后再用隨機碼AES(對稱)獲取內容-----------------------------------------");
String jsonStr = "{\"switchStatus\":1,\"aaa\":\"120\",\"bbb\":\"500\"}";
System.out.println("AES加密規則:" + decryptData + "\t加密后的字符串為:" + encodeAES(decryptData, jsonStr));
String encodeAES = encodeAES(decryptData, jsonStr);
System.out.println("AES解密規則:" + decryptData + "\t解密后的字符串為:" + deCodeAES(decryptData, encodeAES));
String jsonStr2 = "{\"cccc\":\"2\"}";
System.out.println("AES加密規則:" + decryptData + "\t加密后的字符串為:" + encodeAES(decryptData, jsonStr2));
String encodeAES2 = encodeAES(decryptData, jsonStr2);
System.out.println("AES解密規則:" + decryptData + "\t解密后的字符串為:" + deCodeAES(decryptData, encodeAES2));
//String lastData = deCodeAES(decryptData,encodeAES);
//PaymentLimitPO paymentLimitPO = JSON.parseObject(lastData, PaymentLimitPO.class);
//System.out.println("JSON解析內容:" + paymentLimitPO.getSwitchStatus());
} catch (Exception e) {
logger.error("加密解密異常",e.getMessage());
}
}
}
總結:SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(encodeRules.getBytes());
注釋上也給出了說明,直白點的話就是window下加密解密沒問題,到了linux環境下就失敗。
運行結果:

