主要內(nèi)容
1.加密算法分類
2.常用的加密算法實現(xiàn)
今天主要講些加密算法的事。
相關(guān)代碼:https://github.com/GrassQing/AlgorithmUtils
代碼的相關(guān)內(nèi)容,就是以下的所有內(nèi)容。
加密算法分類
加密算法通常有3種分類。
分別是:
1.國際算法,國內(nèi)加密算法(簡稱國密)。
2.對稱算法,和非對稱算法。
3.摘要算法。
1.國際算法
簡單的講就是國際算法由美國的安全局發(fā)布,是現(xiàn)今最通用的商用算法,常用的國際算法有DES,3DES,AES,RSA等。這些算法用的地方可就多了,網(wǎng)上的資源也很多。
2.國密
簡單的講就是由國家密碼局發(fā)布,包含SM1\ SM2\ SM3\ SM4\ SSF33算法。
通常在大部分開發(fā)過程中很少會用到國密算法,那是因為時下國密算法大多數(shù)用在金融領(lǐng)域,硬件加密方面。
3.對稱算法
加密和解密都使用同一把秘鑰,這種加密方法稱為對稱加密,也稱為單密鑰加密。
如下圖所示,簡單的講就是加密方和解密方都持有相同的秘鑰,持有相同的加密方式。一旦二者中有一個泄密,加密如同虛設(shè)。

常用的對稱算法有:DES,3DES,SM4
4.非對稱算法
非對稱密鑰算法是指一個加密算法的加密密鑰和解密密鑰是不一樣的,或者說不能由其中一個密鑰推導(dǎo)出另一個密鑰。
和對稱算法相反,簡單的講就是加密方持有 私鑰 對數(shù)據(jù)進行加密。然后 解密方持有 加密方提供的 公鑰進行解密。
對于非對稱算法而言:
算法強度復(fù)雜、安全性依賴于算法與密鑰但是由于其算法復(fù)雜,而使得加密解密速度沒有對稱加密解密的速度快。但是重在安全。

常用的非對稱算法有,RSA,SM2
4.摘要算法
摘要算法的主要特征是加密過程不需要密鑰,并且經(jīng)過加密的數(shù)據(jù)無法被解密,只有輸入相同的明文數(shù)據(jù)經(jīng)過相同的消息摘要算法才能得到相同的密文。
當(dāng)然,這邊有點要注意的是摘要算法是****不可逆的****。而且信息摘要是****隨機的****,也就是說某種程度上可能不一樣的數(shù)據(jù)會算出一樣的密文,當(dāng)然這種概率是非常小的。
舉個栗子:
A,B通訊:
規(guī)定 報文是這樣子的:
發(fā)送方:json字符串+TOKEN令牌+時間戳的形式+除時間戳外的數(shù)據(jù) 進行Md5加密后傳輸過來。
接收方:截取json字符串,進行TOKEN令牌校驗,校驗服務(wù)器時間和傳輸方時間不超過30分鐘,超過無效,不進行處理,同時校驗md5值的是否一致。
這個栗子就是我職業(yè)生涯過程中經(jīng)歷過的一種校驗方式,當(dāng)時看起來感覺在一定程度上可以避免一些數(shù)據(jù)被截取利用,現(xiàn)在想想,這種摘要加密,協(xié)商的形式還不是較為安全的一種方式(因為你懂的,容易被泄密,也可以稱的上是對稱的一種加密方式)。
回歸正題,上面的MD5加密其實就是摘要算法的一種。常用的還有****SHA,CRC****等。
常用的加密算法實現(xiàn)
常用的加密算法實現(xiàn),這邊主要講解的是國際加密算法,為啥不講國密算法?

好吧,今天國密不是主題,反正

好吧,實際上不想拿出來坑你們,自己搞了些,反正一堆問題,我就不拿出來獻丑了。
那問題來了,我們講講今天的主題。
常用的國際算法如何實現(xiàn)
MD5(摘要算法)
代碼如下:
public static byte[] encryptMD5(byte[] data) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(data);
return md5.digest();
}
就這么簡單,把數(shù)據(jù)放進去就可以了。
SHA(摘要算法)
SHA和Md5的代碼類似
/**
*
* @param data to be encrypted
* @param shaN encrypt method,SHA-1,SHA-224,SHA-256,SHA-384,SHA-512
* @return 已加密的數(shù)據(jù)
* @throws Exception
*/
public static byte[] encryptSHA(byte[] data, String shaN) throws Exception {
MessageDigest sha = MessageDigest.getInstance(shaN);
sha.update(data);
return sha.digest();
}
填坑即可。
DES/3DES(對稱算法)
簡單了解下DES/3DES算法
DES
全稱為Data Encryption Standard,即數(shù)據(jù)加密標(biāo)準(zhǔn),是一種使用密鑰加密的塊算法,1976 年被美國聯(lián)邦政府的國家標(biāo)準(zhǔn)局確定為聯(lián)邦資料處理標(biāo)準(zhǔn)(FIPS),隨后在國際上廣泛流傳開來。
3DES
也叫Triple DES,是三重數(shù)據(jù)加密算法(TDEA,Triple Data Encryption Algorithm)塊密碼的通稱。
它相當(dāng)于是對每個數(shù)據(jù)塊應(yīng)用三次DES 加密算法。由于計算機運算能力的增強,原版DES 密碼的密鑰長度變得容易被暴力破解;3DES 即是設(shè)計用來提供一種相對簡單的方法,即通過增加DES 的密鑰長度來避免類似的攻擊,而不是設(shè)計一種全新的塊密碼算法。
DES 算法代碼實現(xiàn)
public static final String ALGORITHM = "DES";
public static byte[] decrypt(byte[] data, String key) throws Exception {
Key k = toKey(decryptBASE64(key));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return cipher.doFinal(data);
}
public static byte[] encrypt(byte[] data, String key) throws Exception {
Key k = toKey(decryptBASE64(key));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return cipher.doFinal(data);
}
public static byte[] decryptBASE64(String key) throws Exception {
return Base64.decode(key, Base64.DEFAULT);
}
public static String encryptBASE64(byte[] key) throws Exception {
return Base64.encodeToString(key, Base64.DEFAULT);
}
3DES 算法代碼實現(xiàn)
3Des的算法有很多種,然后現(xiàn)在開發(fā)中很少用到這個加密方式,用到最多的基本是金融方面,例如銀聯(lián),pos機等,主要是用來加密那些交易的聯(lián)機密碼等。
所以,還是有必要認真的說下這方面的東西。
****首先****
3des加密有分為普通的,單倍長加密,雙倍長加密
這邊由于篇幅有限,我就上些普通的加密方式
加密
/**
* 3des加密
*
* @param key 密鑰
* @param data 明文數(shù)據(jù) 16進制且長度為16的整數(shù)倍
* @return 密文數(shù)據(jù)
*/
public static byte[] Union3DesEncrypt(byte key[], byte data[]) {
try {
byte[] k = new byte[24];
int len = data.length;
if (data.length % 8 != 0) {
len = data.length - data.length % 8 + 8;
}
byte[] needData = null;
if (len != 0)
needData = new byte[len];
for (int i = 0; i < len; i++) {
needData[i] = 0x00;
}
System.arraycopy(data, 0, needData, 0, data.length);
if (key.length == 16) {
System.arraycopy(key, 0, k, 0, key.length);
System.arraycopy(key, 0, k, 16, 8);
} else {
System.arraycopy(key, 0, k, 0, 24);
}
KeySpec ks = new DESedeKeySpec(k);
SecretKeyFactory kf = SecretKeyFactory.getInstance("DESede");
SecretKey ky = kf.generateSecret(ks);
Cipher c = Cipher.getInstance(TriDes);
c.init(Cipher.ENCRYPT_MODE, ky);
return c.doFinal(needData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 3des解密
*
* @param key 密鑰
* @param data 密文數(shù)據(jù) 16進制且長度為16的整數(shù)倍
* @return 明文數(shù)據(jù)
*/
public static byte[] Union3DesDecrypt(byte key[], byte data[]) {
try {
byte[] k = new byte[24];
int len = data.length;
if (data.length % 8 != 0) {
len = data.length - data.length % 8 + 8;
}
byte[] needData = null;
if (len != 0)
needData = new byte[len];
for (int i = 0; i < len; i++) {
needData[i] = 0x00;
}
System.arraycopy(data, 0, needData, 0, data.length);
if (key.length == 16) {
System.arraycopy(key, 0, k, 0, key.length);
System.arraycopy(key, 0, k, 16, 8);
} else {
System.arraycopy(key, 0, k, 0, 24);
}
KeySpec ks = new DESedeKeySpec(k);
SecretKeyFactory kf = SecretKeyFactory.getInstance("DESede");
SecretKey ky = kf.generateSecret(ks);
Cipher c = Cipher.getInstance(TriDes);
c.init(Cipher.DECRYPT_MODE, ky);
return c.doFinal(needData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
****最后****
剩下的單倍長和雙倍長加密,放到我的github上的工程,有需要著請自行前往下載。
AES(對稱算法)
高級加密標(biāo)準(zhǔn)(英語:Advanced Encryption Standard,縮寫:AES),在密碼學(xué)中又稱Rijndael 加密法,是美國聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。這個標(biāo)準(zhǔn)用來替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。經(jīng)過五年的甄選流程,高級加密標(biāo)準(zhǔn)由美國國家標(biāo)準(zhǔn)與技術(shù)研究院(NIST)于2001 年11 月26 日發(fā)布于FIPS PUB 197,并在2002 年5 月26 日成為有效的標(biāo)準(zhǔn)。2006 年,高級加密標(biāo)準(zhǔn)已然成為對稱密鑰加密中最流行的算法之一。
代碼也比較的簡單。
public static final String ALGORITHM = "AES";
//解密
public static byte[] decrypt(byte[] data, String key) throws Exception {
Key k = toKey(decryptBASE64(key));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return cipher.doFinal(data);
}
//加密
public static byte[] encrypt(byte[] data, String key) throws Exception {
Key k = toKey(decryptBASE64(key));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return cipher.doFinal(data);
}
RSA(非對稱算法)
RSA 是目前非常安全的一種算法,主要是由于其算法的特性導(dǎo)致的。
具體rsa如何如何的安全,請自行百度腦補。
rsa代碼實現(xiàn)關(guān)鍵是如何進行分段加密或者解密,rsa加密或者解密的數(shù)據(jù)長度和秘鑰的位數(shù)有直接的聯(lián)系,所以經(jīng)常會在開發(fā)過程中遇到這些解密數(shù)據(jù)過長的問題。
首選是rsa秘鑰的長度,目前主流可選值:1024、2048、3072、4096...
秘鑰越長解密和加密效率越低,加密長度越來越長。
加密長度規(guī)則:長度/8-11
例如:128字節(jié)(1024bits)-減去11字節(jié)正好是117字節(jié)。
以下是,rsa的加密流程:
(1) 實現(xiàn)者尋找出兩個大素數(shù)p和q
(2) 實現(xiàn)者計算出n=pq 和φ(n)=(p-1)(q-1)
(3) 實現(xiàn)者選擇一個隨機數(shù)e (0<e<></e<>
(4) 實現(xiàn)者使用輾轉(zhuǎn)相除法計算d=e-1(modφ(n))
(5) 實現(xiàn)者在目錄中公開n和e作為公鑰
有興趣的可以自行了解下其原理。
****不正常版的加密,解密方式****
/**
* 解密<br>
* 用公鑰解密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String key)
throws Exception {
// 對密鑰解密
byte[] keyBytes = Base64.decryptBASE64(key);
// 取得公鑰
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 對數(shù)據(jù)解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 加密<br>
* 用公鑰加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String key)
throws Exception {
// 對公鑰解密
byte[] keyBytes = Base64.decryptBASE64(key);
// 取得公鑰
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 對數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
一般來說,rsa的加解密方式是這樣的,如果是私鑰加密,那么必須是公鑰解密,相反公鑰加密,私鑰就是用來解密的。
****以上的示例代碼是有缺陷的。****
上面的代碼針對加密數(shù)據(jù)不長的情況才有效果,如果加密的數(shù)據(jù)超過規(guī)則,則會發(fā)生錯誤,必須要進行分段加密,分段解密。
合理的代碼如下:
/** *//**
* RSA最大加密明文大小,1024的秘鑰對
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/** *//**
* RSA最大解密密文大小,根據(jù)實際秘鑰對進行替換
*/
private static final int MAX_DECRYPT_BLOCK = 256;
/** *//**
* <p>
* 公鑰解密
* </p>
*
* @param
* @param publicKey 公鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKeySort(byte[] encryptedData1, String publicKey)
throws Exception {
byte[] encryptedData=Base64.decode(encryptedData1,Base64.DEFAULT);
byte[] keyBytes =publicKey.trim().getBytes();
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decode(keyBytes, Base64.DEFAULT));
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
//
LoggerUtil.loge("TAG",keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
byte[] temp = new byte[MAX_DECRYPT_BLOCK];
// cache = cipher.doFinal(encryptedData);
// 對數(shù)據(jù)分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
System.arraycopy(encryptedData, offSet, temp, 0, MAX_DECRYPT_BLOCK);
// cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
cache = cipher.doFinal(temp);
} else {
byte[] temp1 = new byte[inputLen - offSet];
System.arraycopy(encryptedData, offSet, temp1, 0, inputLen - offSet);
// cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
cache = cipher.doFinal(temp1);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
/** *//**
* <p>
* 私鑰加密
* </p>
*
* @param data 源數(shù)據(jù)
* @param privateKey 私鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKeySort(byte[] data, String privateKey)
throws Exception {
byte[] keyBytes = Base64Utils.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.ENCRYPT_MODE, privateK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對數(shù)據(jù)分段加密
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;
}
此處省略一大坨代碼,詳細去我的github工程下載。