科技快速发展,广泛的数据传递成为人与人、人与物、物与物的平常。
高效、快速、安全的数据传递成了数据交流的基石。
为了确保数据的安全性,保护用户的隐私,出现了大量的加密算法。
今天对几种常见的加密算法浅显的记录,而且对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
以上文章都可转载,转载请注明原创。