說明:在上篇中提到Base64加解密(詳細參見上),但Base64本身並不是用來做加解密的。雖然可以通過變化的序列來達到加解密目的,但Base64有一個問題那就是不具備驗證性,它不驗證目標源是不是經過Base64加密過的,是否能解的開;通通來者不拒,解出來的東西有時候莫名其妙,所以一般還是建議少用Base64去做加解密。 本文提到RSA是一個標準的非對稱加解密演算法,使用場景:根據RSA的特性,它有公鑰和私鑰兩個密匙,通常可以用在一端加密、另外一通需要解密的場景,另外一個加密的長度問題在正文中討論;如果僅僅用來做驗籤則沒有必要用。
一、什麼是非對稱演算法
非對稱金鑰演算法是指一個加密演算法的加密金鑰和解密金鑰是不一樣的,或者說不能由其中一個金鑰推匯出另一個金鑰。具體實現演算法原理可參見官方說明,這裡主要基於java的應用級別介紹。
二、RSA中的長度問題
1、RSA初始化長度 Java中實現的初始化長度預設是512,所以如果小於512會拋異常,這裡的長度是bit。
2、目標文字長度 根據上面兩個圖示,同樣的初始化長度,唯一的區別就是第二個圖中的目標文字長度是54,導致加解密失敗。那目標文字長度與初始化長度有什麼關係?在Java對RSA的實現中有一個計算公式,目標文字的長度(位元組)<=RSA初化長度-11;結合公式分析一下上面RSA的位元組長度=512/8,即64個位元組;64-11=53位元組,所以要求目標文字長度一定是小於等於53個位元組長度。 這裡也可以解決在最開始應用場景中的另外一個問題,在使用RSA對文字加解密時一定要考慮目標文字的長度,如果文字較長,可以適當調節初始化長度(建議是8的倍數,原因不用多說了吧)。 注意:RSA的初始化長度越大,可以加密的目標文字長度越大,加密後的密文越長,但加密時的效能越差,需要綜合平衡(若目標文字過長,建議分段加密);另外注意上面長度的單位。三、RSA為什麼常和Base64一起使用
1、編碼一致性,Base64設計最初的目的就是解決網路傳速時,編碼不一致問題,這個很容易理解。 2、二進位制的不可見性
RSA中字串String用來存放明文,Byte陣列用來存放加密後的密文;既然是密文自然不想容易被轉換;通過上面的小demo可以很清楚的看出,String和Byte陣列之間可以很容易轉換,所以這也是為什麼引入Base64的原因。四、完整的DEMO
如果讓Base64具有加解密的功能,至少要一部分是變化的;這裡可以通過變化標準序列的方式;建議大家用到的時候,可以先看一下第3部分中標出那個類的原始碼(沒幾行程式碼);這個變化的序列可以根據時間、根據UUID、根據一切可以變換的東西來生成,這裡是根據UUID來隨機生成序列。 1、生成隨機序列
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public final class RSAUtils {
/**
* 指定長度來生成RSA公私鑰對。
*/
public static KeyPair generateRSAKeyPair(int bitLength) throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
if (bitLength > 0)
kpg.initialize(bitLength);
return kpg.generateKeyPair();
}
public static String getBase64Key(Key key) {
byte[] encoded = key.getEncoded();
return new BASE64Encoder().encode(encoded);
}
/**
* 根據提供的base64編碼生成RSA公鑰。
*/
public static PublicKey getPubKey(String base64String) throws Exception {
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(base64String));
return KeyFactory.getInstance("RSA").generatePublic(pubSpec);
}
/**
* 根據提供的base64編碼生成RSA私鑰。
*/
public static PrivateKey getPriKey(String base64String) throws Exception {
PKCS8EncodedKeySpec priSpec = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(base64String));
return KeyFactory.getInstance("RSA").generatePrivate(priSpec);
}
}
複製程式碼
public static void main(String[] args) throws Exception {
KeyPair key = RSAUtils.generateRSAKeyPair(960);
String publicKey = RSAUtils.getBase64Key(key.getPublic());
String privateKey = RSAUtils.getBase64Key(key.getPrivate());
System.out.println(publicKey);
System.out.println(privateKey);
StringBuilder sb = new StringBuilder();
sb.append("rank=1&sessionId=")
.append("0123456789ABCDEF0123456789ABCDEF").append("&ts=")
.append(System.currentTimeMillis() + 1000000000);
String en = encode(sb.toString(), key);
String de = decode(en, key);
System.out.println("原文:" + sb.toString());
System.out.println("密文:" + en);
System.out.println("密文:"+encode(sb.toString(), key));
System.out.println("解密:" + de);
}
public static String encode(String src, KeyPair key) {
String hashCode = null;
try {
PrivateKey publicKey = (PrivateKey) key.getPrivate();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] output = cipher.doFinal(src.getBytes());
hashCode = new BASE64Encoder().encode(output);
} catch (Exception e) {
}
return hashCode;
}
public static String decode(String src, KeyPair key) {
String returnRes = null;
try {
PublicKey publicKey = (PublicKey) key.getPublic();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] output = cipher.doFinal(new BASE64Decoder()
.decodeBuffer(src));
returnRes = new String(output);
} catch (Exception e) {
}
return returnRes;
}
複製程式碼
持續更新中,可以關注........