主要内容
1.加密算法分类
2.常用的加密算法实现
今天主要讲些加密算法的事。
相关代码:https://github.com/GrassQing/AlgorithmUtils
代码的相关内容,就是以下的所有内容。
加密算法分类
加密算法通常有3种分类。
分别是:
1.国际算法,国内加密算法(简称国密)。
2.对称算法,和非对称算法。
3.摘要算法。
1.国际算法
简单的讲就是国际算法由美国的安全局发布,是现今最通用的商用算法,常用的国际算法有DES,3DES,AES,RSA等。这些算法用的地方可就多了,网上的资源也很多。
2.国密
简单的讲就是由国家密码局发布,包含SM1\ SM2\ SM3\ SM4\ SSF33算法。
通常在大部分开发过程中很少会用到国密算法,那是因为时下国密算法大多数用在金融领域,硬件加密方面。
3.对称算法
加密和解密都使用同一把秘钥,这种加密方法称为对称加密,也称为单密钥加密。
如下图所示,简单的讲就是加密方和解密方都持有相同的秘钥,持有相同的加密方式。一旦二者中有一个泄密,加密如同虚设。
常用的对称算法有:DES,3DES,SM4
4.非对称算法
非对称密钥算法是指一个加密算法的加密密钥和解密密钥是不一样的,或者说不能由其中一个密钥推导出另一个密钥。
和对称算法相反,简单的讲就是加密方持有 私钥 对数据进行加密。然后 解密方持有 加密方提供的 公钥进行解密。
对于非对称算法而言:
算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。但是重在安全。
常用的非对称算法有,RSA,SM2
4.摘要算法
摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。
当然,这边有点要注意的是摘要算法是****不可逆的****。而且信息摘要是****随机的****,也就是说某种程度上可能不一样的数据会算出一样的密文,当然这种概率是非常小的。
举个栗子:
A,B通讯:
规定 报文是这样子的:
发送方:json字符串+TOKEN令牌+时间戳的形式+除时间戳外的数据 进行Md5加密后传输过来。
接收方:截取json字符串,进行TOKEN令牌校验,校验服务器时间和传输方时间不超过30分钟,超过无效,不进行处理,同时校验md5值的是否一致。
这个栗子就是我职业生涯过程中经历过的一种校验方式,当时看起来感觉在一定程度上可以避免一些数据被截取利用,现在想想,这种摘要加密,协商的形式还不是较为安全的一种方式(因为你懂的,容易被泄密,也可以称的上是对称的一种加密方式)。
回归正题,上面的MD5加密其实就是摘要算法的一种。常用的还有****SHA,CRC****等。
常用的加密算法实现
常用的加密算法实现,这边主要讲解的是国际加密算法,为啥不讲国密算法?
好吧,今天国密不是主题,反正
好吧,实际上不想拿出来坑你们,自己搞了些,反正一堆问题,我就不拿出来献丑了。
那问题来了,我们讲讲今天的主题。
常用的国际算法如何实现
MD5(摘要算法)
代码如下:
public static byte[] encryptMD5(byte[] data) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(data);
return md5.digest();
}
就这么简单,把数据放进去就可以了。
SHA(摘要算法)
SHA和Md5的代码类似
/**
*
* @param data to be encrypted
* @param shaN encrypt method,SHA-1,SHA-224,SHA-256,SHA-384,SHA-512
* @return 已加密的数据
* @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,即数据加密标准,是一种使用密钥加密的块算法,1976 年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来。
3DES
也叫Triple DES,是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。
它相当于是对每个数据块应用三次DES 加密算法。由于计算机运算能力的增强,原版DES 密码的密钥长度变得容易被暴力破解;3DES 即是设计用来提供一种相对简单的方法,即通过增加DES 的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
DES 算法代码实现
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 算法代码实现
3Des的算法有很多种,然后现在开发中很少用到这个加密方式,用到最多的基本是金融方面,例如银联,pos机等,主要是用来加密那些交易的联机密码等。
所以,还是有必要认真的说下这方面的东西。
****首先****
3des加密有分为普通的,单倍长加密,双倍长加密
这边由于篇幅有限,我就上些普通的加密方式
加密
/**
* 3des加密
*
* @param key 密钥
* @param data 明文数据 16进制且长度为16的整数倍
* @return 密文数据
*/
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 密文数据 16进制且长度为16的整数倍
* @return 明文数据
*/
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(对称算法)
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael 加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001 年11 月26 日发布于FIPS PUB 197,并在2002 年5 月26 日成为有效的标准。2006 年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
代码也比较的简单。
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 是目前非常安全的一种算法,主要是由于其算法的特性导致的。
具体rsa如何如何的安全,请自行百度脑补。
rsa代码实现关键是如何进行分段加密或者解密,rsa加密或者解密的数据长度和秘钥的位数有直接的联系,所以经常会在开发过程中遇到这些解密数据过长的问题。
首选是rsa秘钥的长度,目前主流可选值:1024、2048、3072、4096...
秘钥越长解密和加密效率越低,加密长度越来越长。
加密长度规则:长度/8-11
例如:128字节(1024bits)-减去11字节正好是117字节。
以下是,rsa的加密流程:
(1) 实现者寻找出两个大素数p和q
(2) 实现者计算出n=pq 和φ(n)=(p-1)(q-1)
(3) 实现者选择一个随机数e (0<e<></e<>
(4) 实现者使用辗转相除法计算d=e-1(modφ(n))
(5) 实现者在目录中公开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);
// 对数据解密
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);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
一般来说,rsa的加解密方式是这样的,如果是私钥加密,那么必须是公钥解密,相反公钥加密,私钥就是用来解密的。
****以上的示例代码是有缺陷的。****
上面的代码针对加密数据不长的情况才有效果,如果加密的数据超过规则,则会发生错误,必须要进行分段加密,分段解密。
合理的代码如下:
/** *//**
* RSA最大加密明文大小,1024的秘钥对
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/** *//**
* RSA最大解密密文大小,根据实际秘钥对进行替换
*/
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);
// 对数据分段解密
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 源数据
* @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;
// 对数据分段加密
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工程下载。