概述
之前一直對加密相關的算法知之甚少,只知道類似DES、RSA等加密算法能對數據傳輸進行加密,且各種加密算法各有優缺點。但是一直沒有詳細的研究并形成系統的認知。剛好最近看到一篇關于Android開發加密相關的文章,因此決定把加密相關的東西簡單梳理一下。
定義
數據加密的基本過程就是對原來為明文的文件或數據按某種算法進行處理,使其成為不可讀的一段代碼,通常稱為“密文”,使其只能在輸入相應的密鑰之后才能顯示出本來內容,通過這樣的途徑來達到保護數據不被非法人竊取、閱讀的目的。 該過程的逆過程為解密,即將該編碼信息轉化為其原來數據的過程。
該段文字摘自百度百科
分類
單向加密算法
單向加密又稱為不可逆加密算法,在加密過程中不使用密鑰,明文由系統加密處理成密文,密文無法解密。一般適合于驗證,在驗證過程中,重新輸入明文,并經過同樣的加密算法處理,得到相同的密文并被系統重新認證。廣泛使用于口令加密。例如,用戶在注冊后密碼被經過MD5加密后保存在數據庫,這樣即使是數據庫管理員也沒法知道密碼是什么。
該算法有如下特點:
1、 對同一消息反復執行加密得到相同的密文。
2、加密算法生成的密文不可預見,和明文沒任何關系。
3、 明文的任何微小的變化都會對密文產生很大影響。
4、不可逆,即不能通過密文獲取明文。
比較流行的加密算法:MD5和SHA1。MD5與SHA1都是Hash算法,兩個相比。MD5輸出是128位的,SHA1輸出是160位的,MD5比SHA1快,SHA1比MD5強度高。下面會有兩種算法的java代碼實現。對稱式加密算法
對稱加密算法的特點是:使用的密鑰只有一個,發收信雙方都使用這個密鑰對數據進行加密和解密,要求解密方事先必須知道加密密鑰。它的優點是:算法公開、計算量小、加密速度快、加密效率高。缺點是:雙方都使用同樣鑰匙,安全性得不到保證。每對用戶每次使用對稱加密算法時,都需要使用其他人不知道的惟一鑰匙,這會使得發收信雙方所擁有的鑰匙數量呈幾何級數增長,密鑰管理成為用戶的負擔。對稱加密算法在分布式網絡系統上使用較為困難,主要是因為密鑰管理困難,使用成本較高。
常見的對稱加密算法有:DES、3DES、AES等。非對稱式加密算法
非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰對數據進行加密,那么只有用對應的公開密鑰才能解密。簡單的說是“公鑰加密,私鑰解密;私鑰加密,公鑰解密”。
常見的非對稱加密算法有:RSA。值得一提的是,RSA是目前最有影響力的公鑰加密算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰數據加密標準。
常見的加密算法
- MD5
MD5,全稱是Message-Digest Algorithm 5(信息-摘要算法5),它是一種單向加密算法,也就是只能加密,不能解密。MD5的作用是讓大容量信息在用數字簽名軟件簽署私人密鑰前被"壓縮成一種保密的格式。作用基本與SHA1類似。
MD5目前的應用一般有三種:
1.一致性驗證。
MD5的典型應用是對一段信息(Message)產生信息摘要(Message-Digest),以防止被篡改。我們常常在某些軟件下載站點的某軟件信息中看到其MD5值,它的作用就在于我們可以在下載該軟件后,對下載回來的文件用專門的軟件(如Windows MD5 Check等)做一次MD5校驗,以確保我們獲得的文件與該站點提供的文件為同一文件。
2.數字簽名。
MD5的典型應用是對一段Message(字節串)產生fingerprint(指紋),以防止被“篡改”。舉個例子,你將一段話寫在一個叫 readme.txt文件中,并對這個readme.txt產生一個MD5的值并記錄在案,然后你可以傳播這個文件給別人,別人如果修改了文件中的任何內容,你對這個文件重新計算MD5時就會發現(兩個MD5值不相同)。如果再有一個第三方的認證機構,用MD5還可以防止文件作者的“抵賴”,這就是所謂的數字簽名應用。
3.安全訪問認證
MD5還廣泛用于操作系統的登陸認證上,如在Unix系統中用戶的密碼是以MD5(或其它類似的算法)經Hash運算后存儲在文件系統中。當用戶登錄的時候,系統把用戶輸入的密碼進行MD5 Hash運算,然后再去和保存在文件系統中的MD5值進行比較,進而確定輸入的密碼是否正確。通過這樣的步驟,系統在并不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統的合法性。這可以避免用戶的密碼被具有系統管理員權限的用戶知道。MD5將任意長度的“字節串”映射為一個128bit的大整數,并且是通過該128bit反推原始字符串是困難的,換句話說就是,即使你看到源程序和算法描述,也無法將一個MD5的值變換回原始的字符串。
MD5的加密算法代碼如下:
public static String MD5Encrypt(String msg){
if (msg==null||msg.trim().length()==0){
return "";
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(msg.getBytes());
byte[] encrypMsg = md.digest();
return new String(encrypMsg);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
SHA的加密算法代碼如下:
//安全散列算法(Secure Hash Algorithm),對長度不超過264位的二進制消息產生160位的消息摘要輸出。
public static String SHAEncrypt(String msg){
if (msg==null||msg.trim().length()==0){
return "";
}
try {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(msg.getBytes());
byte[] encrypMsg = md.digest();
return new String(encrypMsg);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
- Base64
Base64是網絡上最常見的用于傳輸8Bit字節代碼的編碼方式之一,Base64并不是安全領域的加密算法,其實Base64只能算是一個編碼算法,對數據內容進行編碼來適合傳輸。標準Base64編碼解碼無需額外信息即完全可逆,即使你自己自定義字符集設計一種類Base64的編碼方式用于數據加密,在多數場景下也較容易破解。Base64編碼本質上是一種將二進制數據轉成文本數據的方案。對于非二進制數據,是先將其轉換成二進制形式,然后每連續6比特(2的6次方=64)計算其十進制值,根據該值在A--Z,a--z,0--9,+,/ 這64個字符中找到對應的字符,最終得到一個文本字符串。基本規則如下幾點:
1、標準Base64只有64個字符(英文大小寫、數字和+、/)以及用作后綴等號;
2、Base64是把3個字節變成4個可打印字符,所以Base64編碼后的字符串一定能被4整除(不算用作后綴的等號);
3、等號一定用作后綴,且數目一定是0個、1個或2個。這是因為如果原文長度不能被3整除,Base64要在后面添加\0湊齊3n位。為了正確還原,添加了幾個\0就加上幾個等號。顯然添加等號的數目只能是0、1或2;
嚴格來說Base64不能算是一種加密,只能說是編碼轉換。
作用:在計算機中任何數據都是按ascii碼存儲的,而ascii碼的128~255之間的值是不可見字符。而在網絡上交換數據時,比如說從A地傳到B地,往往要經過多個路由設備,由于不同的設備對字符的處理方式有一些不同,這樣那些不可見字符就有可能被處理錯誤,這是不利于傳輸的。所以就先把數據先做一個Base64編碼,統統變成可見字符,這樣出錯的可能性就大降低了。
- DES
DES算法全稱為Data Encryption Standard,即數據加密標準算法,它是IBM公司于1975年研究成功并公開發表的。DES算法的入口參數有三個:Key、Data、Mode。其中 Key為7個字節共56位,是DES算法的工作密鑰;Data為8個字節64位,是要被加密或被解密的數據;Mode為DES的工作方式,有兩種:加密 或解密。
DES算法的標準加密解密代碼如下:
private static final String PASSWORD_CRYPT_KEY = "!@#$%^&*()_+~`=-";
private final static String DES = "DES";
/**
* 加密
*
* @param src 數據源
* @param key 密鑰,長度必須是8的倍數
* @return 返回加密后的數據
*/
public static byte[] encrypt(byte[] src, byte[] key) throws Exception {
//步驟一:根據key生成密鑰
//DES算法要求有一個可信任的隨機數源
SecureRandom sr = new SecureRandom();
// 從原始密匙數據創建DESKeySpec對象
DESKeySpec dks = new DESKeySpec(key);
// 創建一個密匙工廠,然后用它把DESKeySpec轉換成
// 一個SecretKey對象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
//步驟二:加密操作
// Cipher對象實際完成加密操作
Cipher cipher = Cipher.getInstance(DES);
// 用密匙初始化Cipher對象
cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
// 現在,獲取數據并加密
// 正式執行加密操作
return cipher.doFinal(src);
}
/**
* 解密
*
* @param src 數據源
* @param key 密鑰,長度必須是8的倍數
* @return 返回解密后的原始數據
*/
public static byte[] decrypt(byte[] src, byte[] key) throws Exception {
// DES算法要求有一個可信任的隨機數源
SecureRandom sr = new SecureRandom();
// 從原始密匙數據創建一個DESKeySpec對象
DESKeySpec dks = new DESKeySpec(key);
// 創建一個密匙工廠,然后用它把DESKeySpec對象轉換成
// 一個SecretKey對象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
// Cipher對象實際完成解密操作
Cipher cipher = Cipher.getInstance(DES);
// 用密匙初始化Cipher對象
cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
// 現在,獲取數據并解密
// 正式執行解密操作
return cipher.doFinal(src);
}
- 3DES
3DES也稱為3DESede或TripleDES,是三重數據加密算法(TDEA,Triple Data Encryption Algorithm)塊密碼的通稱。它相當于是對每個數據塊應用三次DES加密算法。由于計算機運算能力的增強,原版DES密碼的密鑰長度變得容易被暴力破解;3DES即是設計用來提供一種相對簡單的方法,即通過增加DES的密鑰長度來避免類似的攻擊。可以簡單理解為DES加密的升級版。
3DES加密的過程為:C=Ek3(Dk2(Ek1(M)))。即明文M通過密鑰K1進行DES加密,得到密文1。然后對得到的密文1通過密鑰K2進行解密得到密文2。最后對得到的密文2通過密鑰3進行加密得到最終的密文3。
3DES解密過程為:M=Dk1(EK2(Dk3(C)))。解密的過程基本是加密的反向操作,依次通過密鑰K3、K2、K1進行解密、加密以及解密的操作得到明文內容。
3DES算法的標準加密解密代碼如下:
public class TripleDESUtils {
// 密鑰, 3DES的密鑰必須是24位的byte數組,否則會報錯
private final static String secretKey = "123456789012345678901234";
// 向量
private final static String iv = "01234567";
// 加解密統一使用的編碼方式
private final static String encoding = "utf-8";
/**
* 3DES加密
*
* @param plainText 普通文本
* @return
* @throws Exception
*/
public static String encode(String plainText) throws Exception {
Key deskey = null;
System.out.println("secretkey.length="+secretKey.length());
System.out.println("iv.length="+iv.length());
DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes());
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
byte[] encryptData = cipher.doFinal(plainText.getBytes(encoding));
return new String(Base64.encode(encryptData,Base64.DEFAULT),encoding);//從byte數組轉成字符串,一般有兩種方式,base64處理和十六進制處理。
}
/**
* 3DES解密
*
* @param encryptText 加密文本
* @return
* @throws Exception
*/
public static String decode(String encryptText) throws Exception {
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes());
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
byte[] decryptData = cipher.doFinal(Base64.decode(encryptText,Base64.DEFAULT));
return new String(decryptData, encoding);
}
}
- AES
高級加密標準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯邦政府采用的一種區塊加密標準。它屬于對稱加密算法。它的出現是為了替代DES加密算法。
AES算法的標準加密解密代碼如下:
/**偏移量,必須是16位字符串*/
private static final String IV_STRING = "16-Bytes--String";
/**
* 默認的密鑰
*/
public static final String DEFAULT_KEY = "1bd83b249a414036";
/**
* 產生隨機密鑰(這里產生密鑰必須是16位)
*/
public static String generateKey() {
String key = UUID.randomUUID().toString();
key = key.replace("-", "").substring(0, 16);// 替換掉-號
return key;
}
public static String encryptData(String key, String content) {
byte[] encryptedBytes = new byte[0];
try {
byte[] byteContent = content.getBytes("UTF-8");
// 注意,為了能與 iOS 統一
// 這里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
byte[] enCodeFormat = key.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
// 指定加密的算法、工作模式和填充方式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
encryptedBytes = cipher.doFinal(byteContent);
// 同樣對加密后數據進行 base64 編碼
return Base64Utils.encode(encryptedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decryptData(String key, String content) {
try {
// base64 解碼
byte[] encryptedBytes = Base64Utils.decode(content);
byte[] enCodeFormat = key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES");
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] result = cipher.doFinal(encryptedBytes);
return new String(result, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String plainText = AESUtils.decryptData("F431E6FF9051DA07", "q8jHYk6LSbwC2K4zmr/wRZo8mlH0VdMzPEcAzQadTCpSrPQ/ZnTmuIvQxiLOnUXu");
System.out.println("aes加密后: " + plainText);
}
- RSA
RSA加密算法的名稱由三位發明人的名字首字母組合而成,它屬于非對稱加密算法。因此基本的加密方式也是通過兩個密鑰,實現過程是:公鑰加密,私鑰解密;私鑰加密,公鑰解密。RSA是目前最有影響力的公鑰加密算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰數據加密標準。
RSA算法的標準加密解密代碼如下:
public static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcA";
public static final String PUBLIC_KEY = "IGfMA0GCSqGSIb3DQEBAQUAA4GNA";
/** RSA最大加密明文大小 */
private static final int MAX_ENCRYPT_BLOCK = 117;
/** RSA最大解密密文大小 */
private static final int MAX_DECRYPT_BLOCK = 128;
/** 加密算法RSA */
private static final String KEY_ALGORITHM = "RSA";
/**
* 生成公鑰和私鑰
*
* @throws Exception
*
*/
public static void getKeys() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyStr = getPublicKeyStr(publicKey);
String privateKeyStr = getPrivateKeyStr(privateKey);
System.out.println("公鑰\r\n" + publicKeyStr);
System.out.println("私鑰\r\n" + privateKeyStr);
}
public static String getPrivateKeyStr(PrivateKey privateKey)
throws Exception {
return new String(Base64.encode(privateKey.getEncoded(),Base64.DEFAULT));
}
public static String getPublicKeyStr(PublicKey publicKey) throws Exception {
return new String(Base64.encode(publicKey.getEncoded(),Base64.DEFAULT));
}
/**
* 公鑰加密
*
* @param data
* @return
* @throws Exception
*/
public static String encryptByPublicKey(String data) throws Exception {
byte[] dataByte = data.getBytes();
byte[] keyBytes = Base64.decode(PUBLIC_KEY,Base64.DEFAULT);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 對數據加密
// Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicK);
int inputLen = dataByte.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對數據分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(dataByte, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataByte, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return new String(Base64.encode(encryptedData,Base64.DEFAULT));
}
/**
* 私鑰解密
*
* @param data
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String data) throws Exception {
byte[] encryptedData = Base64.decode(data,Base64.DEFAULT);
byte[] keyBytes = Base64.decode(PRIVATE_KEY,Base64.DEFAULT);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
// Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
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 new String(decryptedData);
}
常見的幾種加密算法已經基本都有提到了,但是目前還只是對它們的概念和標準加密算法進行了總結,而且在實際項目中,目前也只用了DES和3DES加密,所以還需要對各種加密算法的使用場景進行理解。希望上面的介紹也可以幫到大家。
參考鏈接:
http://www.cnblogs.com/whoislcj/p/5887859.html
https://mp.weixin.qq.com/s/-Jb-_PpbEN5HYpueUqtxzA