JDK中JCA的簡單使用(三)---RSA加密解密
Cipher 類
Cipher類提供用於加密和解密的加密密碼功能。加密是獲取資料(稱為明文)和 金鑰,並且生成資料(密文)對於不知道金鑰的第三方無意義的過程。解密是一個相反的過程:採用密文和金鑰並生成明文。
對稱與非對稱加密
有兩種主要的加密型別:對稱(也稱為金鑰)和非對稱(或公鑰加密))。在對稱加密中,加密和解密資料的金鑰相同。保持金鑰的私密性對於保持資料機密至關重要。另一方面,非對稱密碼術使用公鑰/私鑰對來加密資料。用一個金鑰加密的資料用另一個金鑰解密。使用者首先生成公鑰/私鑰對,然後在任何人都可以訪問的可信資料庫中釋出公鑰。希望與該使用者安全通訊的使用者使用檢索到的公鑰加密資料。只有私鑰的持有者才能解密。保密私鑰對於此計劃至關重要。
非對稱演算法(例如RSA)通常比對稱演算法慢得多。這些演算法不是為有效保護大量資料而設計的。實際上,非對稱演算法用於交換較小的金鑰,用於初始化對稱演算法。
流與分組密碼
密碼有兩種主要型別:塊和 流。分組密碼一次處理整個塊,通常長度為多個位元組。如果沒有足夠的資料來構成完整的輸入塊,則必須填充資料:即,在加密之前,必須新增虛擬位元組以使密碼的塊大小為倍。然後在解密階段剝離這些位元組。填充可以由應用程式完成,也可以通過初始化密碼來使用填充型別,例如“PKCS5PADDING”。相反,流密碼一次處理一個小單元(通常是一個位元組或甚至一個位元)的輸入資料。這允許密碼處理任意數量的資料而無需填充。
運作模式
當使用簡單的分組密碼進行加密時,兩個相同的明文塊將始終產生相同的密文塊。試圖打破密文的密碼分析者如果注意到重複文字的塊,將會有一個更容易的工作。為了增加文字的複雜性,反饋模式使用前一個輸出塊來改變輸入塊,然後再應用加密演算法。第一個塊需要一個初始值,這個值稱為初始化向量(IV)。由於IV只是在加密之前改變資料,因此IV應該是隨機的,但不一定需要保密。存在多種模式,諸如CBC(密碼塊連結),CFB(密碼反饋模式)和OFB(輸出反饋模式)。ECB(電子密碼本模式)是不受塊位置或其他密文塊影響的模式。由於ECB密文使用相同的明文/金鑰是相同的,因此該模式通常不適用於加密應用程式。
主要應用場景
需要將傳輸的資料進行加密,例如支付資訊等,這裡需要注意的是,加密解密字串有長度限制,需要分塊加解密
使用流程:
1.使用私鑰加密
/** * 私鑰加密 * @param data * @param privateKey * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return encrypt(data, cipher); }
2.使用公鑰解密
public static byte[] decryptByPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return decrypt(encryptedData, cipher);
}
完整程式碼:
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* 私鑰解密
*
* @param encryptedData 已加密資料
* @param privateKey 私鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateK);
return decrypt(encryptedData, cipher);
}
/**
* 公鑰解密
*
* @param encryptedData 已加密資料
* @param publicKeyStr 公鑰
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKeyStr)
throws Exception {
PublicKey publicKey = getPublickey(publicKeyStr);
return decryptByPublicKey(encryptedData,publicKey);
}
public static byte[] decryptByPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return decrypt(encryptedData, cipher);
}
/**
* 公鑰加密
*
* @param data 源資料
* @param publicKey 公鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String publicKey)
throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 對資料加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
return encrypt(data, cipher);
}
/**
*
* 私鑰字串加密
*
* @param data 源資料
* @param privateKey 私鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
throws Exception {
PrivateKey privateK = getPrivateKey(privateKey);
return encryptByPrivateKey(data,privateK);
}
/**
* 私鑰加密
* @param data
* @param privateKey
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return encrypt(data, cipher);
}
/**
* 分塊加密
*
* @param data
* @param cipher
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] data, Cipher cipher) throws Exception {
return dealWithData(data, cipher, MAX_ENCRYPT_BLOCK);
}
/**
* 分塊解密
*
* @param encryptedData
* @param cipher
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] encryptedData, Cipher cipher) throws Exception {
return dealWithData(encryptedData, cipher, MAX_DECRYPT_BLOCK);
}
/**
* 分塊處理資料
*
* @param data
* @param cipher
* @param block
* @return
* @throws Exception
*/
public static byte[] dealWithData(byte[] data, Cipher cipher, int block) throws Exception {
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對資料分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > block) {
cache = cipher.doFinal(data, offSet, block);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * block;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
public static void main(String[] args) throws Exception {
String data = "Signature類是一個引擎類,提供加密的數字簽名演算法,例如DSA或RSAwithMD5。加密安全簽名演算法採用任意大小的輸入和私鑰,並生成一個相對較短(通常是固定大小)的位元組串——簽名。RSA最大解密密文大小為128個位元組,RSA最大加密明文大小為117個位元組,測試加密解密字串";
Map keyPair = getKeyPair();
String privateKeyStr = getPrivateKeyStr(keyPair);
String publicKeyStr = getPublicKeyStr(keyPair);
byte[] encryptData = encryptByPrivateKey(data.getBytes(),privateKeyStr);
String encryptStr = Base64.getEncoder().encodeToString(encryptData);
log.info("加密後的字串為:{}",encryptStr);
byte[] decryptData = Base64.getDecoder().decode(encryptStr);
byte[] decrypt = decryptByPublicKey(decryptData,publicKeyStr);
log.info("解密後的字串為:{}",new String(decrypt));
}
獲得的Cipher物件getInstance必須初始化為四種模式之一,這四種模式被定義為類中的最終整數常量Cipher。這些模式可以通過它們的符號名稱引用,如下所示,以及每種模式的用途說明:
ENCRYPT_MODE
加密資料。
DECRYPT_MODE
解密資料。
WRAP_MODE
將a包裝java.security.Key成位元組,以便可以安全地傳輸金鑰。
UNWRAP_MODE
將先前包裝的金鑰展開到 java.security.Key物件中。
完整工具類下載:https://download.csdn.net/download/cappadocia_/10730247