科技快速發展,廣泛的數據傳遞成爲人與人、人與物、物與物的平常。
高效、快速、安全的數據傳遞成了數據交流的基石。
爲了確保數據的安全性,保護用戶的隱私,出現了大量的加密算法。
今天對幾種常見的加密算法淺顯的記錄,而且對AES算法三端統一加密問題給出解決方法。javascript
1.明文:加密前的信息
2.密文:加密後的信息
3.算法:加密或解密算法
4.密鑰:算法使用的鑰匙
5.對稱加密算法:加密算法和解密算法相對稱
6.非對稱加密算法:加密算法和解密算法不對稱前端
對於這些概念,你們都不會陌生,可是須要本身去實現,咱們須要作哪些思考呢?java
舉一個小例子讓你們更清晰的理解這些概念:
將 123456 的每位數字加 1 後獲得的是 234567
其中明文就是 123456,密文就是 234567,算法就是給每位數字加 (+1) ,密鑰就是 1
這樣就很清晰了,那麼能夠看出,最核心的部分就是算法部分
由於加密方式的算法不一樣,從而誕生了各類各樣的加密方式。
而根據加密算法和解密算法的對稱性,產生了對稱算法和非對稱算法的概念
繼續使用上面的例子展示:
123456 ---> (+1) ---> 234567 的加密祕鑰爲 1 ,加密算法爲每位 (+1)
234567 ---> ( -1) ---> 123456 的解密祕鑰爲 1 ,解密算法爲每位 ( -1)
其中加密算法 (+1) 和解密算法 ( -1) 相對稱,這種加密算法就稱做對稱加密
一樣,若是加密算法和解密算法非對稱,就稱爲非對稱加密
複製代碼
嚴格來講Base64不能算是一種加密方式,更確切的來講是一種編碼方式。
可是對於別的算法Base64有着不可忽視的重要性。
計算機中的數據都是二進制的,無論是字符串仍是文件,加密後的數據也是二級制的。
不少算法加密後輸出的都是byte[],而咱們須要的每每是字符串,因此須要須要使用Base64對其進行編碼。git
嚴格來講也不是加密算法,中文名爲消息摘要算法第五版,是一種哈希算法,是計算機普遍使用的雜湊算法之一。
其最重要的性質就是不可逆、無衝突
所謂的不可逆就是:當你知道x的HASH值,沒法求出x
所謂的無衝突就是:但你知道x,沒法求出一個y,使x與y的值相同
這兩條性質在數學上都是不成立的。
由於一個函數必然可逆,且因爲HASH函數的值域有限,理論上會有無窮多個不一樣的原始值,它們的HASH值都相同。
MD5作到的是求逆和求衝突在計算上的不可能,也就是正向計算很容易,而反向計算即便窮盡人類全部的計算資源都作不到
由於HASH的散列性,其在計算的過程當中散列了一些信息,因此沒法逆向求解,可是有人說網上有破解的方法。
網上的方法能夠說是一種對MD5創建的字典查詢,而不是真正意義的算法破解,我也有MD5破解腳本,須要的能夠q我。
想深刻研究的能夠百度彩虹表
舉個簡單的例子來理解HASH算法的不可逆性,好比每一個人都有指紋,虹膜等,警察叔叔能夠將你們的指紋,虹膜都錄入一個庫。
查詢的時候就能夠匹配出對應的信息。可是若是沒有錄入庫中,能夠根據指紋來推斷這我的的長相,身體特徵嗎?
若是HASH算法可逆,那麼數據壓縮技術會獲得里程碑式的發展
你愛的蒼老師大片被用HASH算法壓縮成一個長度爲128bit的大整數,那還須要種子幹嘛啊?github
特性: ① 壓縮性:任意長度的數據,算出的MD5值長度都是固定的
② 容易計算:用原數據計算出MD5很容易
③ 抗修改性:對原數據進行改動,哪怕只修改一個字節,所得的MD5值有很大差異。
④ 強抗碰撞:已知原數據和其MD5值,想找到一個具備相同MD5值的數據(僞造數據)很是困難
(除了MD5外,比較有名的HASH還有sha-一、RIPEMD、Haval等)算法
① 優勢:算法公開、計算量小、加密速度快、加密效率高、可逆
② 缺點:雙方使用相同祕鑰,安全性下降。對稱密碼體中只有一種密鑰,而且是非公開的,若是要解密就得對方知道密鑰,因此保證其安全性就是保證密鑰的安全。
③ 常見算法:AES、DES、3DES、RC二、RC四、RC五、IDEA、TDEA、Blowfish、SKIPJACK等後端
① 特色:
非對稱加密有兩個密鑰:公開密鑰(公鑰publickey)和私有密鑰(私鑰privatekey)
公開密鑰和私有密鑰是一對,若是用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;
若是用私有密鑰對數據進行加密,那麼只有用對應的公開密鑰才能解密。
算法強度複雜、安全依賴於算法與密鑰可是因爲其算法複雜,而使得加密解密速度沒有對稱加密解密的速度快
非對稱密鑰體制有兩種密鑰,其中一個是公開的,這樣就能夠不須要像對稱密碼那樣傳輸對方的密鑰了。
非對稱加密算法通常效率差,對大型數據加密時間很長,通常用於小數據。
② 常見算法:RSA、Rabin、Elgamal、D - H、ECC(橢圓曲線加密算法)、揹包算法等。數組
AES是一種被普遍使用的加密算法,因爲其快速、高效、安全的特色,被許多人所青睞。
然而因爲先後端開發使用的語言同,常常致使前端加密後然後端沒法解密,或者各端對相同的明文加出來的密文不一樣。
然而不管什麼語言,AES的算法老是相同的,所以致使結果不一樣的緣由是加密設置的參數不一致。
所以咱們須要統一的參數有如下幾個:
① 密鑰長度:Key Size
② 加密模式:Cipher Mode
③ 填充方式:Padding
④ 初始向量:Initialization Vector安全
AES算法下,key的長度有三種:12八、19二、256(bits)。
JDK默認只支持不大於128bits 的密鑰,而128bits已可以知足商用需求,在此使用126bits長度。
實現:key爲16位128bits,本身定義,保證各端一致
private static String key = "128bitslength*@#";
NSString *key = @"128bitslength*@#";
bash
AES屬於塊加密(Block Cipher),塊加密中有CBC、ECB、CTR、OFB、CFB等幾種工做模式。
要保證加出的密文一致,先後端必須使用一樣的加密模式,此處使用CBC模式來實現。
因爲塊加密只能對特定長度數據塊進行加密,所以CBC、ECB模式須要在最後一數據塊加密前進行數據填充。
CFB、OFB、CTR模式因爲與key進行加密操做的是上一塊加密後的密文,所以不須要對最後一段明文進行填充。
在 IOS SDK中提供了PKCS7Padding,而JDK則提供了PKCS5Padding。
原則上PKCS5Padding限制了填充的Block Size爲8 bytes。
而Java實際上當塊大於該值時,其PKCS5Padding與PKCS7Padding是相等的:每須要填充X個字節,填充的值就是X。
使用除ECB之外的其餘加密模式均須要傳入一個初始向量,其大小與Block Size相等(AES的Block Size默認爲128bits)
兩個平臺的API均指出當不傳入初始向量時,系統將默認使用一個全0的初始向量。
AESCipher.java
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
public class AESCipher {
private static String key = "128bitslength*@#";
private static final String IV_STRING = "A-16-Byte-String";
private static final String charset = "UTF-8";
public static String aesEncryptString(String content, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
byte[] contentBytes = content.getBytes(charset);
byte[] keyBytes = key.getBytes(charset);
byte[] encryptedBytes = aesEncryptBytes(contentBytes, keyBytes);
Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(encryptedBytes);
}
public static String aesDecryptString(String content, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
Decoder decoder = Base64.getDecoder();
byte[] encryptedBytes = decoder.decode(content);
byte[] keyBytes = key.getBytes(charset);
byte[] decryptedBytes = aesDecryptBytes(encryptedBytes, keyBytes);
return new String(decryptedBytes, charset);
}
public static byte[] aesEncryptBytes(byte[] contentBytes, byte[] keyBytes) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException {
return cipherOperation(contentBytes, keyBytes, Cipher.ENCRYPT_MODE);
}
public static byte[] aesDecryptBytes(byte[] contentBytes, byte[] keyBytes) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException {
return cipherOperation(contentBytes, keyBytes, Cipher.DECRYPT_MODE);
}
private static byte[] cipherOperation(byte[] contentBytes, byte[] keyBytes, int mode)
throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
byte[] initParam = IV_STRING.getBytes(charset);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(mode, secretKey, ivParameterSpec);
return cipher.doFinal(contentBytes);
}
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String pdaes = AESCipher.aesEncryptString("wylq2018",key);
System.out.println(pdaes);
String pd = AESCipher.aesDecryptString(pdaes,key);
System.out.println(pd);
}
}
複製代碼
AESCipher.h
#import <Foundation/Foundation.h>
NSString * aesEncryptString(NSString *content, NSString *key);
NSString * aesDecryptString(NSString *content, NSString *key);
NSData * aesEncryptData(NSData *data, NSData *key);
NSData * aesDecryptData(NSData *data, NSData *key);
AESCipher.m
#import "AESCipher.h"
#import <CommonCrypto/CommonCryptor.h>
NSString const *kInitVector = @"A-16-Byte-String";
size_t const kKeySize = kCCKeySizeAES128;
NSData * cipherOperation(NSData *contentData, NSData *keyData, CCOperation operation) {
NSUInteger dataLength = contentData.length;
void const *initVectorBytes = [kInitVector dataUsingEncoding:NSUTF8StringEncoding].bytes;
void const *contentBytes = contentData.bytes;
void const *keyBytes = keyData.bytes;
size_t operationSize = dataLength + kCCBlockSizeAES128;
void *operationBytes = malloc(operationSize);
if (operationBytes == NULL) {
return nil;
}
size_t actualOutSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
keyBytes,
kKeySize,
initVectorBytes,
contentBytes,
dataLength,
operationBytes,
operationSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:operationBytes length:actualOutSize];
}
free(operationBytes);
operationBytes = NULL;
return nil;
}
NSString * aesEncryptString(NSString *content, NSString *key) {
NSCParameterAssert(content);
NSCParameterAssert(key);
NSData *contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSData *encrptedData = aesEncryptData(contentData, keyData);
return [encrptedData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}
NSString * aesDecryptString(NSString *content, NSString *key) {
NSCParameterAssert(content);
NSCParameterAssert(key);
NSData *contentData = [[NSData alloc] initWithBase64EncodedString:content options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSData *decryptedData = aesDecryptData(contentData, keyData);
return [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
}
NSData * aesEncryptData(NSData *contentData, NSData *keyData) {
NSCParameterAssert(contentData);
NSCParameterAssert(keyData);
NSString *hint = [NSString stringWithFormat:@"The key size of AES-%lu should be %lu bytes!", kKeySize * 8, kKeySize];
NSCAssert(keyData.length == kKeySize, hint);
return cipherOperation(contentData, keyData, kCCEncrypt);
}
NSData * aesDecryptData(NSData *contentData, NSData *keyData) {
NSCParameterAssert(contentData);
NSCParameterAssert(keyData);
NSString *hint = [NSString stringWithFormat:@"The key size of AES-%lu should be %lu bytes!", kKeySize * 8, kKeySize];
NSCAssert(keyData.length == kKeySize, hint);
return cipherOperation(contentData, keyData, kCCDecrypt);
}
ViewController.m
#import "ViewController.h"
#import "AESCipher.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *plainText = @"wy123456";
NSString *key = @"128bitslength*@#";
NSString *cipherText = aesEncryptString(plainText, key);
NSLog(@"%@", cipherText);
NSString *decryptedText = aesDecryptString(cipherText, key);
NSLog(@"%@", decryptedText);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
複製代碼
Android端可使用Java的端的代碼,可是由於java.util.Base64包的aesEncryptBytes()方法和getEncoder()方法須要API26以上纔可使用,因此須要進行一些更改,建立Base64Util來進行Base64編碼。
具體實現爲:
AESUtils.java
package com.moie.wy.lib.utils.aes;
import com.tzcm.factory.chumeifactory.utils.LogUtils;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESUtils {
private static String key = "128bitslength*@#";
private static final String IV_STRING = "A-16-Byte-String";
private static final String charset = "UTF-8";
public static String EnCode(String content) {
try {
byte[] contentBytes = content.getBytes(charset);
byte[] keyBytes = key.getBytes(charset);
byte[] encryptedBytes = aesEncryptBytes(contentBytes, keyBytes);
return Base64Util.encode(encryptedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String DeCode(String content) {
try {
byte[] encryptedBytes = Base64Util.decode(content);
byte[] keyBytes = key.getBytes(charset);
byte[] decryptedBytes = aesDecryptBytes(encryptedBytes, keyBytes);
return new String(decryptedBytes, charset);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] aesEncryptBytes(byte[] contentBytes, byte[] keyBytes) {
try {
return cipherOperation(contentBytes, keyBytes, Cipher.ENCRYPT_MODE);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] aesDecryptBytes(byte[] contentBytes, byte[] keyBytes) {
try {
return cipherOperation(contentBytes, keyBytes, Cipher.DECRYPT_MODE);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static byte[] cipherOperation(byte[] contentBytes, byte[] keyBytes, int mode) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
byte[] initParam = IV_STRING.getBytes(charset);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(mode, secretKey, ivParameterSpec);
return cipher.doFinal(contentBytes);
}
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String pdaes = AESUtils.EnCode("wy123456");
LogUtils.error(pdaes);
String pd = AESUtils.DeCode(pdaes);
LogUtils.error(pd);
}
}
複製代碼
Base64Util.java
package com.moie.wy.lib.utils.aes;
import java.io.ByteArrayOutputStream;
/** * @author yangyang_2000 * @version v1.0 * @date 2017/12/20 16:22 */
public class Base64Util {
private static final char[] base64EncodeChars = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
private static byte[] base64DecodeChars = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1,
-1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1};
private Base64Util() {
}
/** * 將字節數組編碼爲字符串 */
public static String encode(byte[] data) {
StringBuffer sb = new StringBuffer();
int len = data.length;
int i = 0;
int b1, b2, b3;
while (i < len) {
b1 = data[i++] & 0xff;
if (i == len) {
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
sb.append("==");
break;
}
b2 = data[i++] & 0xff;
if (i == len) {
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
sb.append("=");
break;
}
b3 = data[i++] & 0xff;
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
sb.append(base64EncodeChars[b3 & 0x3f]);
}
return sb.toString();
}
/** * 將字符串編碼爲字節數組 */
public static byte[] decode(String str) throws Exception {
byte[] data = str.getBytes("GBK");
int len = data.length;
ByteArrayOutputStream buf = new ByteArrayOutputStream(len);
int i = 0;
int b1, b2, b3, b4;
while (i < len) {
/* b1 */
do {
b1 = base64DecodeChars[data[i++]];
} while (i < len && b1 == -1);
if (b1 == -1) {
break;
}
/* b2 */
do {
b2 = base64DecodeChars[data[i++]];
} while (i < len && b2 == -1);
if (b2 == -1) {
break;
}
buf.write((b1 << 2) | ((b2 & 0x30) >>> 4));
/* b3 */
do {
b3 = data[i++];
if (b3 == 61) {
return buf.toByteArray();
}
b3 = base64DecodeChars[b3];
} while (i < len && b3 == -1);
if (b3 == -1) {
break;
}
buf.write(((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2));
/* b4 */
do {
b4 = data[i++];
if (b4 == 61) {
return buf.toByteArray();
}
b4 = base64DecodeChars[b4];
} while (i < len && b4 == -1);
if (b4 == -1) {
break;
}
buf.write(((b3 & 0x03) << 6) | b4);
}
return buf.toByteArray();
}
}
複製代碼
Java端:使用工具eclipse
本文參考:
簡書WeLKinXie的文章
CSDN做者uikoo9的文章
百度百科Base64,RSA,AES算法
知乎蔣又新對《什麼是哈希算法》的回覆
知乎《爲何MD5算法不可逆》的回覆
長路漫漫,菜不是原罪,墮落纔是原罪。
個人CSDN:blog.csdn.net/wuyangyang_…
個人簡書:www.jianshu.com/u/20c2f2c35…
個人掘金:juejin.im/user/58009b…
個人GitHub:github.com/wuyang2000
我的網站:www.xiyangkeji.cn
我的app(茜茜)蒲公英鏈接:www.pgyer.com/KMdT
個人微信公衆號:茜洋 (按期推送優質技術文章,歡迎關注)
Android技術交流羣:691174792
以上文章都可轉載,轉載請註明原創。