主要內(nèi)容
1.加密算法分類
2.常用的加密算法實(shí)現(xiàn)
今天主要講些加密算法的事温技。
相關(guān)代碼:https://github.com/GrassQing/AlgorithmUtils
代碼的相關(guān)內(nèi)容喳坠,就是以下的所有內(nèi)容。
加密算法分類
加密算法通常有3種分類。
分別是:
1.國際算法涂召,國內(nèi)加密算法(簡稱國密)麻捻。
2.對(duì)稱算法,和非對(duì)稱算法兼蕊。
3.摘要算法初厚。
1.國際算法
簡單的講就是國際算法由美國的安全局發(fā)布,是現(xiàn)今最通用的商用算法孙技,常用的國際算法有DES产禾,3DES,AES牵啦,RSA等亚情。這些算法用的地方可就多了,網(wǎng)上的資源也很多哈雏。
2.國密
簡單的講就是由國家密碼局發(fā)布楞件,包含SM1\ SM2\ SM3\ SM4\ SSF33算法衫生。
通常在大部分開發(fā)過程中很少會(huì)用到國密算法,那是因?yàn)闀r(shí)下國密算法大多數(shù)用在金融領(lǐng)域土浸,硬件加密方面罪针。
3.對(duì)稱算法
加密和解密都使用同一把秘鑰,這種加密方法稱為對(duì)稱加密栅迄,也稱為單密鑰加密站故。
如下圖所示,簡單的講就是加密方和解密方都持有相同的秘鑰毅舆,持有相同的加密方式西篓。一旦二者中有一個(gè)泄密,加密如同虛設(shè)憋活。
常用的對(duì)稱算法有:DES岂津,3DES,SM4
4.非對(duì)稱算法
非對(duì)稱密鑰算法是指一個(gè)加密算法的加密密鑰和解密密鑰是不一樣的悦即,或者說不能由其中一個(gè)密鑰推導(dǎo)出另一個(gè)密鑰吮成。
和對(duì)稱算法相反,簡單的講就是加密方持有 私鑰 對(duì)數(shù)據(jù)進(jìn)行加密辜梳。然后 解密方持有 加密方提供的 公鑰進(jìn)行解密粱甫。
對(duì)于非對(duì)稱算法而言:
算法強(qiáng)度復(fù)雜、安全性依賴于算法與密鑰但是由于其算法復(fù)雜作瞄,而使得加密解密速度沒有對(duì)稱加密解密的速度快茶宵。但是重在安全。
常用的非對(duì)稱算法有宗挥,RSA乌庶,SM2
4.摘要算法
摘要算法的主要特征是加密過程不需要密鑰,并且經(jīng)過加密的數(shù)據(jù)無法被解密契耿,只有輸入相同的明文數(shù)據(jù)經(jīng)過相同的消息摘要算法才能得到相同的密文瞒大。
當(dāng)然,這邊有點(diǎn)要注意的是摘要算法是****不可逆的****搪桂。而且信息摘要是****隨機(jī)的****透敌,也就是說某種程度上可能不一樣的數(shù)據(jù)會(huì)算出一樣的密文,當(dāng)然這種概率是非常小的踢械。
舉個(gè)栗子:
A拙泽,B通訊:
規(guī)定 報(bào)文是這樣子的:
發(fā)送方:json字符串+TOKEN令牌+時(shí)間戳的形式+除時(shí)間戳外的數(shù)據(jù) 進(jìn)行Md5加密后傳輸過來。
接收方:截取json字符串裸燎,進(jìn)行TOKEN令牌校驗(yàn)顾瞻,校驗(yàn)服務(wù)器時(shí)間和傳輸方時(shí)間不超過30分鐘,超過無效德绿,不進(jìn)行處理荷荤,同時(shí)校驗(yàn)md5值的是否一致退渗。
這個(gè)栗子就是我職業(yè)生涯過程中經(jīng)歷過的一種校驗(yàn)方式,當(dāng)時(shí)看起來感覺在一定程度上可以避免一些數(shù)據(jù)被截取利用蕴纳,現(xiàn)在想想会油,這種摘要加密,協(xié)商的形式還不是較為安全的一種方式(因?yàn)槟愣墓琶菀妆恍姑芊妫部梢苑Q的上是對(duì)稱的一種加密方式)。
回歸正題稻薇,上面的MD5加密其實(shí)就是摘要算法的一種嫂冻。常用的還有****SHA,CRC****等。
常用的加密算法實(shí)現(xiàn)
常用的加密算法實(shí)現(xiàn)塞椎,這邊主要講解的是國際加密算法桨仿,為啥不講國密算法?
好吧案狠,今天國密不是主題服傍,反正
好吧,實(shí)際上不想拿出來坑你們骂铁,自己搞了些吹零,反正一堆問題,我就不拿出來獻(xiàn)丑了拉庵。
那問題來了灿椅,我們講講今天的主題。
常用的國際算法如何實(shí)現(xiàn)
MD5(摘要算法)
代碼如下:
public static byte[] encryptMD5(byte[] data) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(data);
return md5.digest();
}
就這么簡單名段,把數(shù)據(jù)放進(jìn)去就可以了。
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(對(duì)稱算法)
簡單了解下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卡啰,是三重?cái)?shù)據(jù)加密算法(TDEA静稻,Triple Data Encryption Algorithm)塊密碼的通稱。
它相當(dāng)于是對(duì)每個(gè)數(shù)據(jù)塊應(yīng)用三次DES 加密算法匈辱。由于計(jì)算機(jī)運(yùn)算能力的增強(qiáng)振湾,原版DES 密碼的密鑰長度變得容易被暴力破解;3DES 即是設(shè)計(jì)用來提供一種相對(duì)簡單的方法亡脸,即通過增加DES 的密鑰長度來避免類似的攻擊押搪,而不是設(shè)計(jì)一種全新的塊密碼算法树酪。
DES 算法代碼實(shí)現(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 算法代碼實(shí)現(xiàn)
3Des的算法有很多種,然后現(xiàn)在開發(fā)中很少用到這個(gè)加密方式大州,用到最多的基本是金融方面续语,例如銀聯(lián),pos機(jī)等厦画,主要是用來加密那些交易的聯(lián)機(jī)密碼等疮茄。
所以,還是有必要認(rèn)真的說下這方面的東西根暑。
****首先****
3des加密有分為普通的力试,單倍長加密,雙倍長加密
這邊由于篇幅有限购裙,我就上些普通的加密方式
加密
/**
* 3des加密
*
* @param key 密鑰
* @param data 明文數(shù)據(jù) 16進(jìn)制且長度為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進(jìn)制且長度為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上的工程,有需要著請(qǐng)自行前往下載躏率。
AES(對(duì)稱算法)
高級(jí)加密標(biāo)準(zhǔn)(英語:Advanced Encryption Standard躯畴,縮寫:AES),在密碼學(xué)中又稱Rijndael 加密法薇芝,是美國聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)蓬抄。這個(gè)標(biāo)準(zhǔn)用來替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用夯到。經(jīng)過五年的甄選流程嚷缭,高級(jí)加密標(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 年阅爽,高級(jí)加密標(biāo)準(zhǔn)已然成為對(duì)稱密鑰加密中最流行的算法之一。
代碼也比較的簡單荐开。
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(非對(duì)稱算法)
RSA 是目前非常安全的一種算法付翁,主要是由于其算法的特性導(dǎo)致的。
具體rsa如何如何的安全晃听,請(qǐng)自行百度腦補(bǔ)百侧。
rsa代碼實(shí)現(xiàn)關(guān)鍵是如何進(jìn)行分段加密或者解密,rsa加密或者解密的數(shù)據(jù)長度和秘鑰的位數(shù)有直接的聯(lián)系能扒,所以經(jīng)常會(huì)在開發(fā)過程中遇到這些解密數(shù)據(jù)過長的問題佣渴。
首選是rsa秘鑰的長度,目前主流可選值:1024初斑、2048辛润、3072、4096...
秘鑰越長解密和加密效率越低见秤,加密長度越來越長频蛔。
加密長度規(guī)則:長度/8-11
例如:128字節(jié)(1024bits)-減去11字節(jié)正好是117字節(jié)灵迫。
以下是,rsa的加密流程:
(1) 實(shí)現(xiàn)者尋找出兩個(gè)大素?cái)?shù)p和q
(2) 實(shí)現(xiàn)者計(jì)算出n=pq 和φ(n)=(p-1)(q-1)
(3) 實(shí)現(xiàn)者選擇一個(gè)隨機(jī)數(shù)e (0<e<></e<>
(4) 實(shí)現(xiàn)者使用輾轉(zhuǎn)相除法計(jì)算d=e-1(modφ(n))
(5) 實(shí)現(xiàn)者在目錄中公開n和e作為公鑰
有興趣的可以自行了解下其原理晦溪。
****不正常版的加密瀑粥,解密方式****
/**
* 解密<br>
* 用公鑰解密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String key)
throws Exception {
// 對(duì)密鑰解密
byte[] keyBytes = Base64.decryptBASE64(key);
// 取得公鑰
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 對(duì)數(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 {
// 對(duì)公鑰解密
byte[] keyBytes = Base64.decryptBASE64(key);
// 取得公鑰
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
一般來說,rsa的加解密方式是這樣的三圆,如果是私鑰加密狞换,那么必須是公鑰解密,相反公鑰加密舟肉,私鑰就是用來解密的修噪。
****以上的示例代碼是有缺陷的。****
上面的代碼針對(duì)加密數(shù)據(jù)不長的情況才有效果路媚,如果加密的數(shù)據(jù)超過規(guī)則黄琼,則會(huì)發(fā)生錯(cuò)誤,必須要進(jìn)行分段加密整慎,分段解密脏款。
合理的代碼如下:
/** *//**
* RSA最大加密明文大小,1024的秘鑰對(duì)
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/** *//**
* RSA最大解密密文大小裤园,根據(jù)實(shí)際秘鑰對(duì)進(jìn)行替換
*/
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);
// 對(duì)數(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;
// 對(duì)數(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;
}
此處省略一大坨代碼撤师,詳細(xì)去我的github工程下載。