密码学支持
在日常工作中经常需要涉及到对数据的加密、解密以及数据验证等操作,Java提供了一些类来完成加密解密功能。
哈希函数
哈希函数通常用于验证数据指纹,同一个文件或数据,使用同一种哈希函数得到的哈希值一定相同,如果发现对某文件两次哈希得到的结果不一样,就证明文件数据有被篡改过。
MessageDigest
MessageDigest类提供了常用的哈希函数包括SHA-1,SHA-224,SHA-256,SHA-384,SHA-512,MD5算法等。
MD5
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 加密解密。
*/
public class MessageDigestDemo {
public static void main(String[] args) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] digest = messageDigest.digest("hello world".getBytes());
String encode = HexBin.encode(digest);
System.out.println(encode);
}
}
运行结果为
5EB63BBBE01EEED093CB22BB8F5ACDC3
SHA-256
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 加密解密。
*/
public class MessageDigestDemo {
public static void main(String[] args) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] digest = messageDigest.digest("hello world".getBytes());
String encode = HexBin.encode(digest);
System.out.println(encode);
}
}
运行结果为
B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9
加密与解密
数据加密是最常见的数据安全保护手段,加密函数和哈希函数不一样,哈希函数不可逆,我们无法通过哈希值还原被哈希的数据。但是被加密的数据需要能解密还原内容,否则加密也就失去了意义。
加密前的数据称为“明文”,被加密后的数据称为“密文”。
在实际进行加密操作过程中,我们需要使用经验证安全可靠的加密算法,并且还需要生成加密所需的密钥。明文数据通过加密算法使用对应的密钥来对数据进行加密,从而得到密文。
即便知道了密文加密使用的加密算法,没有密钥的情况下,也无法在有限的时间内破解加密数据获取原文。
AES加密、解密数据
AES 加密的参数及其条件
密钥:加密的时候用秘钥,解密的时候需要同样的密钥才能解出来,密钥必须是16位字节或者24位字节或者32位字节(因为python3的字符串是unicode编码,需要encode才可以转换成字节型数据)
明文:需要加密的内容,字节长度需要是16位的倍数
模式:AES加密常用的有ECB和CBC模式
iv偏移量:这个参数在ECB模式下不需要,在CBC模式下需要
AES ECB模式
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
* AES ECB模式例子。
*/
public class AESECBDemo {
private static final int KEY_LENGTH_16 = 16;
private static final int KEY_LENGTH_32 = 32;
private static final String AES_ECB_PKCS_5_PADDING = "AES/ECB/PKCS5Padding";
private static final String AES = "AES";
public static void main(String[] args) throws Exception {
//加密key长度需要为16位或32位
String key = "11112222333344441111222233334444";
//待加密内容
String content = "hello world";
System.out.println("加密前的内容:" + content);
AESECBDemo aesEncodeDemo = new AESECBDemo();
String encrypted = aesEncodeDemo.encryptByECB(key, content);
System.out.println("加密后的内容:" + encrypted);
String decrypt = aesEncodeDemo.decryptByECB(key, encrypted);
System.out.println("解密后的内容:" + decrypt);
}
/**
* AES ECB模式加密
* @param key 加密的秘钥
* @param content 待加密内容
* @return 加密后的内容
* @throws Exception 异常信息
*/
public String encryptByECB(String key, String content) throws Exception {
checkKey(key);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
//AES/ECB/PKCS5Padding 格式为 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(AES_ECB_PKCS_5_PADDING);
//设置加密模式,加密的key
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
//加密
byte[] bytes = cipher.doFinal(content.getBytes());
//base64编码进行二次加密
return new BASE64Encoder().encode(bytes);
}
/**
* 检查key是否合法
*
* @param key {@link String}秘钥信息
* @throws Exception 异常信息
*/
private void checkKey(String key) throws Exception {
if (key == null || key.length() != KEY_LENGTH_16 && key.length() != KEY_LENGTH_32) {
throw new Exception("加密秘钥不正确");
}
}
/**
\* AES ECB模式解密
\* @param key 加密的秘钥
\* @param encrypt 加密后的内容
\* @return 解密后的内容
\* @throws Exception 异常信息
*/
public String decryptByECB(String key, String encrypt) throws Exception {
checkKey(key);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
//AES/ECB/PKCS5Padding 格式为 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(AES_ECB_PKCS_5_PADDING);
//设置为解密模式,解密的key
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
//base64解密
byte[] decodeBuffer = new BASE64Decoder().decodeBuffer(encrypt);
//aes解密
byte[] bytes = cipher.doFinal(decodeBuffer);
return new String(bytes);
}
}
输出内容如下
加密前的内容:hello world
加密后的内容:E8QK4xNrtTlkU5Fh2rMNqg==
解密后的内容:hello world
AES CBC模式
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
* AES CBC模式例子。
*/
public class AESCBCDemo {
private static final int KEY_LENGTH_16 = 16;
private static final int KEY_LENGTH_32 = 32;
private static final String AES = "AES";
private static final String AES_CBC_NO_PADDING = "AES/CBC/NoPadding";
public static void main(String[] args) throws Exception {
//加密key长度需要为16位或32位
String key = "1111222233334444";
//偏移矢量,必须为16位
String iv = "5555666677778888";
//待加密内容
String content = "hello world";
System.out.println("加密前的内容:" + content);
AESCBCDemo aescbcDemo = new AESCBCDemo();
String encrypted = aescbcDemo.encryptByCBC(key, content, iv);
System.out.println("加密后的内容:" + encrypted);
String decrypt = aescbcDemo.decryptByCBC(key, encrypted, iv);
System.out.println("解密后的内容:" + decrypt);
}
/**
* AES ECB模式加密
* @param key 加密的秘钥
* @param content 待加密内容
* @param iv 偏移矢量
* @return 加密后的内容
* @throws Exception 异常信息
*/
public String encryptByCBC(String key, String content, String iv) throws Exception {
checkKey(key);
checkIV(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
//AES/ECB/PKCS5Padding 格式为 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(AES_CBC_NO_PADDING);
//加密内容长度必须要为blockSize的整数倍
int blockSize = cipher.getBlockSize();
int contentLength = content.getBytes().length;
if (contentLength % blockSize != 0) {
contentLength = contentLength + (blockSize - (contentLength % blockSize));
}
byte[] newBytes = new byte[contentLength];
System.arraycopy(content.getBytes(), 0, newBytes, 0, content.getBytes().length);
//偏移矢量
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
//设置加密模式,加密的key,偏移矢量
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
//加密
byte[] bytes = cipher.doFinal(newBytes);
//base64编码进行二次加密
return new BASE64Encoder().encode(bytes);
}
/**
* 检查key是否合法
* @param key {@link String}秘钥信息
* @throws Exception 异常信息
*/
private void checkKey(String key) throws Exception {
if (key == null || key.length() != KEY_LENGTH_16 && key.length() != KEY_LENGTH_32) {
throw new Exception("加密秘钥不正确");
}
}
/**
* 检查偏移矢量是否合法
* @param iv {@link String} 偏移矢量
* @throws Exception 异常信息
*/
private void checkIV(String iv) throws Exception {
if (iv == null || iv.length() != KEY_LENGTH_16) {
throw new Exception("偏移矢量不正确,必须为16位");
}
}
/**
* AES ECB模式解密
* @param key 加密的秘钥
* @param encrypt 加密后的内容
* @param iv 偏移矢量
* @return 解密后的内容
* @throws Exception 异常信息
*/
public String decryptByCBC(String key, String encrypt, String iv) throws Exception {
checkKey(key);
checkIV(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
//AES/ECB/PKCS5Padding 格式为 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(AES_CBC_NO_PADDING);
//偏移矢量
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
//设置为解密模式,解密的key,偏移矢量
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
//base64解密
byte[] decodeBuffer = new BASE64Decoder().decodeBuffer(encrypt);
//aes解密
byte[] bytes = cipher.doFinal(decodeBuffer);
return new String(bytes);
}
}