RSA
RSA算法是第一個能同時用於加密和數字簽名的算法,也易於理解和操作。RSA是被研究得最廣泛的公鑰算法,從提出到現今的三十多年里,經歷了各種攻擊的考驗,逐漸為人們接受,普遍認為是目前最優秀的公鑰方案之一。RSA公開密鑰密碼體制。所謂的公開密鑰密碼體制就是使用不同的加密密鑰與解密密鑰,是一種“由已知加密密鑰推導出解密密鑰在計算上是不可行的”密碼體制。
RSA的算法涉及三個參數,n、e1、e2。其中,n是兩個大質數p、q的積,n的二進制表示時所占用的位數,就是所謂的密鑰長度。e1和e2是一對相關的值,e1可以任意取。
算法過程
一、產生密鑰
-
為了產生兩個密鑰,選取兩個大素數,p和q,為了獲得最大程度的安全性,兩數的長度一樣。計算乘積
n=p*q; -
隨機取加密密鑰e,使得e 和(p-1)(q-1) 互素,最后采用擴展歐幾里得算法計算解密密鑰d,
d=e^-1 mod (p-1)(q-1)
注意
d和n也是互素。e和n是公開密鑰,d是私人密鑰。
RSA加解密的算法完全相同,設A為明文,B為密文,則:
A=B^d mod n;
B=A^e mod n;
公鑰加密體制中,一般用公鑰加密,私鑰解密
e1和e2可以互換使用,即
A=B^e mod n;
B=A^d mod n;
我們可以設計出一對公私密鑰,加密密鑰(公鑰)為:KU =(e,n)=(3,33),解密密鑰(私鑰)為:KR =(d,n)=(7,33)。
二、英文數字化
將明文信息數字化,並將每塊兩個數字分組。假定明文英文字母編碼表為按字母順序排列數值,即:

三、明文加密
- 加密消息m時,首先將它分為比n小的數據分組(采用二級制數,選取小於n的2的最大次冪),也就是說,若果p和n為100位的素數,那么n將有200位,每個消息分組m應該小於200位長
- 用戶加密密鑰(3,33) 將數字化明文分組信息加密成密文。由
C≡M^e(mod n)得:

四、密文解密
-
用戶B收到密文,若將其解密,只需要計算
M≡c^d(mod n)
-
用戶B得到明文信息為:11,05,25。根據上面的編碼表將其轉換為英文,我們又得到了恢復后的原文“key”
四、e值
最常用的三個e值:3, 17, 65537(2^16+1).
X.509中建議采用65537[1], PEM中建議采用3[2],PKCS#1建議采用3或65537[3].
PKCS#1填充
- RSA在應用中一般采用PKCS#1的標准進行填充

注意
- 開頭為00只為了保證原文大小小於私鑰
- 對於私鑰操作,你可以把BT的值設為01,這時PS填充的FF,那么用00字節就可以區分填充數據和明文數據對於公鑰操作,填充的都是非00字節,也能夠用00字節區分開。如果你使用私鑰加密,建議你BT使用01,保證了安全性。
- 對於BT為02和01的,PS至少要有8個字節長,BT為02肯定是公鑰加密,01肯定是私鑰加密,要保證PS至少有八個字節長 。因為EB= 00+BT+PS+00+D=k, 所以D<=k-11,所以當我們使用128字節密鑰對數據進行加密時,明文數據的長度不能超過過128-11=117字節當RSA要加密數據大於 k-11字節時怎么辦呢?把明文數據按照D的最大長度分塊然后逐塊加密,最后把密文拼起來就行。明文長度小於117ps處填充數據即可
附JAVA代碼
/**
* RSA algorithm.
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* digital signature algorithm
*/
public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
/**
* Gets public key.
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* Gets private key.
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA maximum encryption text size.
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA maximum decryption text size.
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* <p>
* Use the private key to generate digital signatures for the information.
* </p>
*
* @param data
* Encrypted data
* @param PrivateKey
* Private Key (BASE64 encoding)
* @return Digit signature (BASE64 encoding)
* @throws Exception
*/
public byte[] sign(byte[] data, String privateKey) {
try {
byte[] keyBytes = Base64Utils.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory;
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateK);
signature.update(data);
return signature.sign();
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
/**
* <p>
* Verify digit signature.
* </p>
*
* @param data
* Encrypted data
* @param publicKey
* Public Key(BASE64 encoding)
* @param sign
* Digit signature
*
* @return result from verify
* @throws Exception
*
*/
public boolean verify(byte[] data, String publicKey, byte[] sign) {
byte[] keyBytes = Base64Utils.decode(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory;
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicK = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicK);
signature.update(data);
return signature.verify(sign);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
/**
* <P>
* Decrypted with the private key
* </p>
*
* @param encryptedData
* Encrypted data
* @param privateKey
* Private Key (BASE64 encoding)
* @return decryptedData
* @throws Exception
*/
public byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) {
byte[] keyBytes = Base64Utils.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory;
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateK);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int inputLen = encryptedData.length;
int offSet = 0;
byte[] cache;
int i = 0;
// 對數據分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* <p>
* Encrypt with the public key.
* </p>
*
* @param data
* Plain text
* @param publicKey
* Public key(BASE64 encoding)
* @return encryptedData
* @throws Exception
*/
public byte[] encryptByPublicKey(byte[] data, String publicKey) {
byte[] keyBytes = Base64Utils.decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory;
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 對數據加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int inputLen = data.length;
int offSet = 0;
byte[] cache;
int i = 0;
// 對數據分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
