什么是MD5?SHA?DES?AES?有没有被这些 缩写搞晕?晕就对了。
MD5、SHA是属于 哈希算法。
DES、AES是属于 加密算法。
都是JDK提供了。拿来就用。
该文不涉及原理剖析,仅 是 使用 示例。
MDA、SHA核心代码:
/**
* 哈希,可用于 文件校验、口令存储等
*/
//哈希算法(Hash)又称摘要算法(Digest)
// https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest
// 创建一个MessageDigest实例:
// MD5:得到16个字节,SHA-1:得到20个字节, SHA-256:得到32个字节,SHA-512:得到64个字节;字节越多,碰撞就少
MessageDigest md = MessageDigest.getInstance("SHA-1");
// 反复调用update输入数据:相当于调用一次 md.update("HelloWorld".getBytes("UTF-8"));
md.update("Hello".getBytes("UTF-8"));
md.update("World".getBytes("UTF-8"));
byte[] result = md.digest(); // 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6
System.out.println(new BigInteger(1, result).toString(16));
// 使用第三方哈希包:注册BouncyCastle:
Security.addProvider(new BouncyCastleProvider());
// 按名称正常调用:
MessageDigest md2 = MessageDigest.getInstance("RipeMD160");
md2.update("HelloWorld".getBytes("UTF-8"));
byte[] result2 = md2.digest();
System.out.println(new BigInteger(1, result2).toString(16));
//Hmac Hash-based Message Authentication Code
// Hmac 算法是一种标准的基于密钥的哈希算法,可以配合MD5、SHA-1等哈希算法,计算的摘要长度和原摘要算法长度相同
KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
SecretKey key = keyGen.generateKey();
Mac mac = Mac.getInstance("HmacMD5");
mac.init(key);
mac.update("HelloWorld".getBytes("UTF-8"));
byte[] result3 = mac.doFinal();
System.out.println(new BigInteger(1, result3).toString(16));
SecretKey key2 = new SecretKeySpec(key.getEncoded(), "HmacMD5");
Mac mac2 = Mac.getInstance("HmacMD5");
mac2.init(key2);
mac2.update("HelloWorld".getBytes("UTF-8"));
byte[] result4 = mac2.doFinal();
System.out.println(new BigInteger(1, result4).toString(16));
DES、AES核心代码:
ECB填充模式:
// 加密:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
// 根据 算法名称/工作模式/填充模式获取Cipher实例
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// 根据算法名称初始化一个 SecretKey 实例,密钥必须是指定长度
SecretKey keySpec = new SecretKeySpec(key, "AES");
// 使用 SecretKey 初始化Cipher实例,并设置加密或解密模式
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 传入明文或密文,获得密文或明文
return cipher.doFinal(input);
}
// 解密:
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
return cipher.doFinal(input);
}
CBC填充模式:
// 加密:
public static byte[] encrypt2(byte[] key, byte[] input,byte[] iv) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivps = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
byte[] data = cipher.doFinal(input);
return data;
}
// 解密:
public static byte[] decrypt2(byte[] key, byte[] input,byte[] iv) throws GeneralSecurityException {
// 解密:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivps = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
return cipher.doFinal(input);
}
解释:
/**
* 在软件开发中,常用的对称加密算法有:
*
* 算法 密钥长度 工作模式 填充模式
* DES 56/64 ECB/CBC/PCBC/CTR/... NoPadding/PKCS5Padding/...
* AES 128/192/256 ECB/CBC/PCBC/CTR/... NoPadding/PKCS5Padding/PKCS7Padding/...
* IDEA 128 ECB PKCS5Padding/PKCS7Padding/...
*
* 不同加密算法在使用时,需要 使用 自己对应的 密钥长度、工作模式、填充模式
* 比如:
* DES 需要 密钥长度 是 56或者64 个 bit,对应7个byte或者8个byte
* AES 需要 密钥长度是 128/192/256 个 bit,对应16个byte,24个byte,32个byte
*
* Cipher [ˈsaɪfə(r)] 密码算法;密码;密文;加密算法;欣技, 知道这个单词,有利于学习后面的加密算法
*
* public class IvParameterSpec implements AlgorithmParameterSpec
*
* AlgorithmParameterSpec 接口,没有任何方法,表示 加密算法 参数规范
* IvParameterSpec 是一个实现类,简称IV: initialization vector ,表示 初始化向量
* CBC 需要这个类作为参数
*
* AES算法的 IV参数长度必须是 16 个 byte
* DES算法的 IV参数长度必须是 8 个 byte
*
* 所以,如果需要使用 CBC模式,IV参数的 字节数 还不一样。
*
* 简单步骤:
* ECB模式:
* 1. 获取 加密算法 对象 Cipher
* 2. 获取 密钥对象 SecretKey
* 3. 利用 密钥对象 对 加密算法 进行初始化
* 4. 加密 数据
*
* CBC模式:
* 1. 获取 加密算法 对象 Cipher
* 2. 获取 密钥对象 SecretKey
* 3. 获取 IV 对象(IvParameterSpec)
* 4. 利用 密钥对象 和 IV对象 初始化 加密算法
* 5. 加密数据
*
* 加密算法的 的加密方法的入参是字节数组,
* 意味着 需要程序把 密钥 和 原文转为字节流,
* 加密后的数据 也是字节流, 可以 使用 Base64最字节进行编码成文本
*/
/**
* 以下使用 AES 算法演示,
*
* 如果 换成 DES,则需要 修改一下 密钥 和 IV,因为 不同算法对应的长度不一样
*/
// 原文:
String message = "Hello, world!";
System.out.println("Message: " + message);
// 128位密钥 = 16 bytes Key:
byte[] key85 = "1234567890abcdef".getBytes("UTF-8");
// 加密:
byte[] data = message.getBytes("UTF-8");
byte[] encrypted = encrypt(key85, data);
System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
// 解密:
byte[] decrypted = decrypt(key85, encrypted);
System.out.println("Decrypted: " + new String(decrypted, "UTF-8"));
/**
* ECB模式是最简单的AES加密模式,它只需要一个固定长度的密钥,
* 固定的明文会生成固定的密文,这种一对一的加密方式会导致安全性降低,
* 更好的方式是通过CBC模式,它需要一个随机数作为IV参数,这样对于同一份明文,每次生成的密文都不同
*/
// 原文:
String message116 = "Hello, world!";
System.out.println("Message: " + message116);
// 256位密钥 = 32 byte Key:
byte[] key119 = "1234567890abcdef1234567890abcdef".getBytes("UTF-8");
// CBC模式需要生成一个16 byte的 initialization vector:
// 在CBC模式下,需要一个随机生成的16字节IV参数,必须使用SecureRandom生成
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] iv = sr.generateSeed(16);
// 加密:
byte[] data121 = message116.getBytes("UTF-8");
byte[] encrypted122 = encrypt2(key119, data121,iv);
System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted122));
// 解密:
byte[] decrypted125 = decrypt2(key119, encrypted122,iv);
System.out.println("Decrypted: " + new String(decrypted125, "UTF-8"));
另外补充一下:
// URL编解码,对url进行编解码
String encoded = URLEncoder.encode("中文!", StandardCharsets.UTF_8.name());
System.out.println(encoded);
String decoded = URLDecoder.decode("%E4%B8%AD%E6%96%87%21", StandardCharsets.UTF_8.name());
System.out.println(decoded);
// Base64 编解码, 对 字节流 进行编码
byte[] input = new byte[] { (byte) 0xe4, (byte) 0xb8, (byte) 0xad };
String b64encoded = Base64.getEncoder().encodeToString(input);
System.out.println(b64encoded);
byte[] output = Base64.getDecoder().decode("5Lit");
System.out.println(Arrays.toString(output)); // [-28, -72, -83]
这种是属于对称加密,还有其他的加密方式。