前言
在这个信息共享的时代,信息安全无论是对于开发还是用户来说都是重点关注的问题,例如在表单提交时,采用密文的方式来代替明文,可以相对有效避免重要信息外泄,文中阐述了几种比较常用的加密方法
加密方式
1、BASE64
严格来说是编码格式,而非加密算法;特点是加密解密是双向的,可以求反解;主要是 BASE64Encoder、BASE64Decoder 两个类;常见用于邮件、http 加密
测试代码实现:
package encoder;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* create by [email protected] on 2018/12/13 9:22
* BASE64 严格来说是编码格式,而非加密算法
* 加密解密是双向的,可以求反解
* 主要是 BASE64Encoder、BASE64Decoder 两个类
* 常见于邮件、http 加密
**/
public class BASE64 {
/**
* BASE64 解密
* @param key 秘钥
* @throws Exception 异常
* */
private static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
/**
* BASE64 加密
* @param key 秘钥
* @throws Exception 异常
* */
private static String encrypyBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}
public static void main(String[] args) {
String str = "12345678";
try {
System.out.println("str====加密前====" + str);
String encodeData = BASE64.encrypyBASE64(str.getBytes());
System.out.println("encodeData====加密数据=====" + encodeData);
byte[] decodeData = BASE64.decryptBASE64(encodeData);
System.out.println("decodeData====解密数据=====" + new String(decodeData));
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
str====加密前====12345678
encodeData====加密数据=====MTIzNDU2Nzg=
decodeData====解密数据=====12345678
Process finished with exit code 0
2、MD5
MD5(Message Digest Algorithm 5) 信息摘要算法,用于确保信息传输完整一致,是计算机广泛使用的杂凑算法之一(又称摘要算法、哈希算法),将数据(如汉字)运算为另一固定长度值,常用语文件校验;通常是不直接使用上述 MD5 加密,通常将 MD5 产生的字节数组交给 BASE64 再加密一把,得到相应的字符串
具有以下特点:
- 压缩性:任意长度的数据,算出的 MD5 值长度是固定的
- 容易计算:从原数据计算出 MD5 值很容易
- 抗修改性:对于原数据进行任何更改,所得到的 MD5 值都有很大的区别
- 弱抗碰撞:已知原数据和其 MD5 值,想找到一个具有相同 MD5 值的数据非常困难
- 强抗碰撞:想找到两个不同的数据,使他们具有相同的 MD5 值,非常困难
测试代码实现:
package encoder;
import java.math.BigInteger;
import java.security.MessageDigest;
/**
* create by [email protected] on 2018/12/13 10:02
* MD5(Message Digest Algorithm 5) 信息摘要算法 用于确保信息传输完整一致
* 是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法)
* 将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,常用语文件校验
* 通常不直接使用上述 MD5 加密,通常将 MD5 产生的字节数组交给 BASE64 再加密一把,得到相应的字符串
* 具有以下特点:
* 1、压缩性:任意长度的数据,算出的 MD5 值长度是固定的
* 2、容易计算:从原数据计算出 MD5 值很容易
* 3、抗修改性:对原数据进行任何更改,所得到的 MD5 值都有很大区别
* 4、弱抗碰撞:已知原数据和其 MD5 值,想找到一个具有相同 MD5 值得数据时非常困难的
* 5、强抗碰撞:想找到两个不同的数据,使他们具有相同的 MD5 值,非常困难
**/
public class MD5 {
private static final String KEY_MD5 = "MD5";
private static String getResult(String inputStr) {
System.out.println("=====加密前的数据:" + inputStr);
BigInteger bigInteger = null;
try {
MessageDigest md = MessageDigest.getInstance(KEY_MD5);
byte[] inputData = inputStr.getBytes();
md.update(inputData);
bigInteger = new BigInteger(md.digest());
} catch (Exception e) {
e.printStackTrace();
}
return bigInteger.toString(16);
}
public static void main(String[] args) {
try {
String inputStr = "简单加密8888888888";
System.out.println("MD5 加密后:" + getResult(inputStr));
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
=====加密前的数据:简单加密8888888888
MD5 加密后:-60398b41d73fd45b9f90ce6a612ada8c
Process finished with exit code 0
3、SHA
SHA(Secure Hash Algorithm 安全散列算法)主要适用于数字签名标准(Digest Signature Standard DSS)里面定义的数字签名算法
算法思想:接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文
较之 MD5 更为安全
SHA-1 与 MD5 比较;因为二者均由 MD4 导出,SHA-1 和 MD5 彼此很相似,但还有以下几点不同:
- 对强行攻击的安全性:最显著和最重要的区别是 SHA-1 摘要要比 MD5 摘要长 32 位
- 对密码分析的安全性:由于 MD5 的设计,易受密码分析攻击,SHA-1 不易受攻击
- 速度:在相同的硬件基础上,SHA-1 的运行速度比 MD5 慢
测试代码实现:
package encoder;
import java.math.BigInteger;
import java.security.MessageDigest;
/**
* create by [email protected] on 2018/12/13 10:36
* SHA (Secure Hash Algorithm 安全散列算法)主要适用于数字签名标准(Digest Signature Standard DSS)里面定义的数字签名算法
* (Digest Signature Algorithm DSA) 对于长度小于 2^64 位的消息,SHA1 会产生一个 160 位的消息摘要
* 算法思想:接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文
* 较之 MD5 更为安全
* SHA-1 与 MD5 的比较
* 因为二者均由 MD4 导出,SHA-1 和 MD5 彼此很相似,但还有以下几点不同
* 1、对强行攻击的安全性:最显著和最重要的区别是 SHA-1 摘要要比 MD5 摘要长 32 位
* 2、对密码分析的安全性:由于 MD5 的设计,易受密码分析攻击,SHA-1 不易受攻击
* 3、速度:在相同的硬件基础上,SHA-1 的运行速度比 MD5 慢
**/
public class SHA {
private static final String KEY_SHA = "SHA";
private static String getResult(String inputStr) {
BigInteger bigInteger = null;
System.out.println("=======加密前的数据:" + inputStr);
byte[] inputData = inputStr.getBytes();
try {
MessageDigest messageDigest = MessageDigest.getInstance(KEY_SHA);
messageDigest.update(inputData);
bigInteger = new BigInteger(messageDigest.digest());
} catch (Exception e) {
e.printStackTrace();
}
return bigInteger.toString(32);
}
public static void main(String[] args) {
try {
String inputStr = "简单加密";
System.out.println("=======加密后的数据:" + getResult(inputStr));
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
=======加密前的数据:简单加密
=======加密后的数据:91k9vo7p400cjkgfhjh0ia9qthsjagfn
Process finished with exit code 0
4、AES
AES(Advanced Encryption Standard 高级加密标准)是现在对称加密算法中最流行的算法之一
AES 可以使用 128、192 和 256 位密钥,并且用 128 位分组加密和解密数据,相对来说安全
测试代码实现:
public class AES {
private static String src = "TestAES";
/**
* AES 加密字符串
* @param content 需要被加密的字符串
* @param password 加密需要的密码
* @return 密文
* */
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 创建 AES 的 key 生产者
keyGenerator.init(128, new SecureRandom(password.getBytes())); // 利用用户密码作为随机初始化出
// 128 位的 key 生产者,SecureRandom 是生成安全随机序列,password.getBytes() 是种子,只要种子相同,序列就一样
// 所以解密只要有 password 就行
SecretKey secretKey = keyGenerator.generateKey(); // 根据用户密码,生成一个密钥
byte[] enCodeFormate = secretKey.getEncoded(); // 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 null
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormate, "AES"); // 转换为 AES 专用密钥
Cipher cipher = Cipher.getInstance("AES"); // 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); // 初始化为加密模式的密码器
byte[] result = cipher.doFinal(byteContent); // 加密
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* AES 解密 解密AES 加密的字符串
* @param content AES 加密过的内容
* @param password 加密时的密码
* @return 明文
* */
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 创建 AES 的 key 生产者
keyGenerator.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = keyGenerator.generateKey(); // 根据用户密码,生成一个密钥
byte[] enCodeFormat = secretKey.getEncoded(); // 返回基本编码格式的密钥
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); // 转为 AES 专用密钥
Cipher cipher = Cipher.getInstance("AES"); // 创建密码器
cipher.init(Cipher.DECRYPT_MODE, key); // 初始化为解密模式的密码器
byte[] result = cipher.doFinal(content);
return result; // 明文
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String content = "美女,约吗?";
String password = "123";
System.out.println("====加密前的内容:" + content);
// 加密
byte[] encrypt = encrypt(content, password);
System.out.println("====加密后的内容,转换前:" + new String(encrypt));
System.out.println("====加密后的内容,转换后:" + ParseSystemUtil.parseByte2HexStr(encrypt));
// 解密
byte[] decrypt = decrypt(encrypt, password);
System.out.println("====解密后的内容:" + new String(decrypt));
}
}
运行结果:
====加密前的内容:美女,约吗?
====加密后的内容,转换前:P�d�g�K�3�g�����,Ꝏ?U納�_
====加密后的内容,转换后:50FE6401E867A34BD533FE67BB85EDABFED62CEA9D8E3F5516E7B48D01F21A5F
====解密后的内容:美女,约吗?
Process finished with exit code 0
编码转换工具:
ParseSystemUtil 类实现:
扫描二维码关注公众号,回复:
4682247 查看本文章
package encoder;
/**
* create by [email protected] on 2018/12/13 13:53
* 字节数组和十六进制之间的相互转换
**/
public class ParseSystemUtil {
/**
* 将字节数组转换成十六进制
* @param buf 字节数组
* @return 十六进制字符串
* */
public static String parseByte2HexStr(byte[] buf) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 将十六进制转换成字节数组
* @param hex 十六进制的字符串
* @return 字节数组
* */
public static byte[] parseHexStr2Byte(String hex) {
if (hex.length() < 1) {
return null;
}
byte[] result = new byte[hex.length() / 2];
for (int i = 0; i < hex.length() / 2; i++) {
int high = Integer.parseInt(hex.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hex.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte)(high * 16 + low);
}
return result;
}
// 字符串转字符数组
public static char[] change(String s) {
char[] result = new char[s.length()/2]; // 定义数组
for (int i = 0; i < result.length; i++)
result[i] = (char)(Integer.parseInt(s.substring(2*i, 2*i + 2),16 & 0xFF));
return result;
}
}