轉載請标明出處:http://blog.csdn.net/zhaoyanjun6/article/details/120316606
本文出自【趙彥軍的部落格】
文章目錄
- RSA 簡介
- RSA 常用的加密填充模式
- 實際運用注意事項
- 實戰
-
- RSA/ECB/PKCS1Padding 封裝類
- RSA 預設實作
- Https 證書
RSA 簡介
RSA
——非對稱加密,會産生公鑰和私鑰,公鑰在用戶端,私鑰在服務端。公鑰用于加密,私鑰用于解密。
RSA
其實是三位數學家名字的縮寫,1977年,三位數學家
Rivest、Shamir 和 Adleman
設計了一種算法,可以實作非對稱加密。這種算法用他們三個人的名字命名,叫做RSA算法。從那時直到現在,RSA算法一直是最廣為使用的"非對稱加密算法"。毫不誇張地說,隻要有計算機網絡的地方,就有RSA算法。
在1977年的RSA論文裡,提到分解一個75位十進制數字大約需要104天。人類的技術進步是如此驚人!
當攻方的矛越來越鋒利時,守方的盾就必須越來越厚重。是以,1024比特RSA已經不安全,應用系統不應該使用少于2048比特的公鑰 值。而當需要高安全性時,選擇4096比特RSA。
RSA 常用的加密填充模式
- RSA/None/PKCS1Padding
- RSA/ECB/PKCS1Padding
Java 預設的 RSA 實作是
RSA/None/PKCS1Padding
, 預設實作如下:
使用模式方式的 Cipher 生成的密文總是不一緻的 ,
Bouncy Castle
的預設 RSA 實作是
RSA/None/NoPadding
。
為什麼 Java 預設的 RSA 實作每次生成的密文都不一緻呢,即使每次使用同一個明文、同一個公鑰?這是因為 RSA 的 PKCS #1 padding 方案在加密前對明文資訊進行了随機數填充。
實際運用注意事項
- 1、一般由伺服器建立秘鑰對,私鑰儲存在伺服器,公鑰下發至用戶端
-
2、公鑰是二進制資料,怎麼下發給用戶端呢?
第一種方式:伺服器把二進制資料寫入檔案,然後把檔案傳給用戶端。由用戶端從檔案讀取二進制資料。
第二種方式:伺服器把二進制資料轉成
字元串,用戶端擷取到base64
字元串後,再轉碼為二進制資料。base64
- 3、使用相同的公鑰加密後的資料,每次都不一樣,這是因為 RSA 的填充方案在加密前對明文資訊進行了随機數填充。但是不并不影響解密的結果
- 4、在建立RSA秘鑰對時,長度最好選擇 2048的整數倍,長度為1024在今天的網際網路已經不安全了。
實戰
RSA/ECB/PKCS1Padding 封裝類
我們封存一個
RSA
加解密工具類
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class RSA {
/**
* 使用公鑰加密
*
* @param data
* @param publicKey
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
// 得到公鑰對象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 加密資料
Cipher cp = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cp.init(Cipher.ENCRYPT_MODE, pubKey);
return cp.doFinal(data);
}
/**
* 使用私鑰解密
*
* @param encrypted
* @param privateKey
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
// 得到私鑰對象
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey keyPrivate = kf.generatePrivate(keySpec);
// 解密資料
Cipher cp = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cp.init(Cipher.DECRYPT_MODE, keyPrivate);
byte[] arr = cp.doFinal(encrypted);
return arr;
}
/**
* 建立非對稱加密RSA秘鑰對
* @param keyLength
* @return
* @throws NoSuchAlgorithmException
*/
public static KeyPair generateRSAKeyPair(int keyLength) throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(keyLength);
return kpg.genKeyPair();
}
/**
* 擷取公鑰,列印為48-12613448136942-12272-122-913111503-126115048-12...等等一長串用-拼接的數字
*/
public static byte[] getPublicKey(KeyPair keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
return rsaPublicKey.getEncoded();
}
/**
* 擷取私鑰,同上
*/
public static byte[] getPrivateKey(KeyPair keyPair) {
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
return rsaPrivateKey.getEncoded();
}
}
測試代碼
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.util.Base64;
public class Test {
public static void main(String[] args) {
String message = "星期二,我好開心哦";
try {
//建立秘鑰對
KeyPair keyPair = RSA.generateRSAKeyPair(1024);
//擷取公鑰
byte[] publicKey = RSA.getPublicKey(keyPair);
//擷取私鑰
byte[] privateKey = RSA.getPrivateKey(keyPair);
//公鑰base64編碼
String publicBase64 = Base64.getEncoder().encodeToString(publicKey);
System.out.println("publicBase64 = " + publicBase64);
//加密
byte[] enResult = RSA.encryptByPublicKey(message.getBytes(), publicKey);
//解密
byte[] deResult = RSA.decryptByPrivateKey(enResult, privateKey);
System.out.println("解密=" + new String(deResult, StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
}
}
}
測試結果:
解密=星期二,我好開心哦
RSA 預設實作
主要就是下面這一句代碼:
完整代碼如下:
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class RSA {
/**
* 使用公鑰加密
*
* @param data
* @param publicKey
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
// 得到公鑰對象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 加密資料
Cipher cp = Cipher.getInstance("RSA");
cp.init(Cipher.ENCRYPT_MODE, pubKey);
return cp.doFinal(data);
}
/**
* 使用私鑰解密
*
* @param encrypted
* @param privateKey
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
// 得到私鑰對象
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey keyPrivate = kf.generatePrivate(keySpec);
// 解密資料
Cipher cp = Cipher.getInstance("RSA");
cp.init(Cipher.DECRYPT_MODE, keyPrivate);
byte[] arr = cp.doFinal(encrypted);
return arr;
}
/**
* 建立非對稱加密RSA秘鑰對
*
* @param keyLength
* @return
* @throws NoSuchAlgorithmException
*/
public static KeyPair generateRSAKeyPair(int keyLength) throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(keyLength);
return kpg.genKeyPair();
}
/**
* 擷取公鑰,列印為48-12613448136942-12272-122-913111503-126115048-12...等等一長串用-拼接的數字
*/
public static byte[] getPublicKey(KeyPair keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
return rsaPublicKey.getEncoded();
}
/**
* 擷取私鑰,同上
*/
public static byte[] getPrivateKey(KeyPair keyPair) {
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
return rsaPrivateKey.getEncoded();
}
}
Https 證書
我們看一下百度的 https 證書資訊
公鑰資訊是 RSA 算法