Java常用加解密工具
这篇主要给大家分享几个常用的加解密工具,包括对称加密 AES 和对称加密 DES,还有非对称加密 RSA,希望对大家有用。
对称加密 AES
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。 这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。 2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。 采用对称分组密码体制,密钥的长度最少支持为128、192、256位;加密分组长度128位,如果数据块及密钥长度不足时,会补齐进行加密。
public class AESUtil {
private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);
private static final String ALGORITHM = "AES";
// 默认密码算法
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
/**
* 生成指定长度的 key
* @param keySize 用来指定 key 的长度
* @return
*/
public static byte[] createKey(int keySize) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(keySize);
return keyGenerator.generateKey().getEncoded();
} catch (NoSuchAlgorithmException e) {
logger.error("AES密钥/向量生成异常_" + e.getMessage(), e);
return null;
}
}
/**
* AES 加密
* @param source 需要加密的字符串,要加密的明文
* @param key 密钥,AES固定格式为128/256 bits.即:16/32bytes,即加密内容应该为 16字节的倍数。DES固定格式为128bits,即8bytes。
* @param iv 偏移量,AES 为 16bytes. DES 为 8bytes
* @return 返回加密后的字节数组,可以选择编码。主要编解码方式有Base64, HEX, UUE,7bit等等。此处看服务器需要什么编码方式
* 所以也可以这样返回:return new BASE64Encoder().encode(encryptedData); 返回Base64转码后的加密数据
*/
public static String enc(byte[] source, byte[] key, byte[] iv) {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, ALGORITHM), new IvParameterSpec(iv));
byte[] encryptedData = cipher.doFinal(source);
return Base64Util.encodeBytesToString(encryptedData);
} catch (Exception e) {
logger.error("AES加密异常_" + e.getMessage(), e);
return null;
}
}
/**
* AES解密
* @param source 已加密的密文
* @param key 密钥,AES固定格式为128/256 bits.即:16/32bytes,即加密内容应该为 16字节的倍数。DES固定格式为128bits,即8bytes。
* @param iv iv 偏移量,AES 为 16bytes. DES 为 8bytes
* @return 返回解密后的明文
*/
public static String dec(byte[] source, byte[] key, byte[] iv) {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, ALGORITHM), new IvParameterSpec(iv));
byte[] result = cipher.doFinal(source);
return new String(result, StandardCharsets.UTF_8);
} catch (Exception e) {
logger.error("AES解密异常_" + e.getMessage(), e);
return null;
}
}
}
使用案例
public class CryptoTest {
/**
* 测试 AES 可逆加密
*/
@Test
public void testAES(){
// 测试 AES 加密
byte[] aesKey = AESUtil.createKey(256);
byte[] aesIv = AESUtil.createKey(128);
Map<String, String> user = new HashMap<>();
user.put("name","张三");
user.put("age","21");
user.put("phone","182****6871");
String encryptData = AESUtil.enc(JSON.toJSON(user).getBytes(), aesKey, aesIv);
System.out.println("encrypt:" + encryptData);
// encrypt:n3el9w1sXguCKcZmoSTCLf6VRWDwPQZ58/1FasPyNP2pPl1jfPE135QdzryfduDH+NDaJFqQeD9tLkSVupOMRDosgppJR0Nbypq27EiXiMM=
testAESTransportWithBase64(encryptData, aesKey, aesIv);
}
/**
* 测试 AES 密钥及 IV 使用 base64 编码传输,并解密
* @param encryptData
* @param aesKey
* @param aesIv
*/
public static void testAESTransportWithBase64(String encryptData, byte[] aesKey, byte[] aesIv){
// 测试 AES 传输
// 我们的密文已经经过了 Base64 编码,可以传输了,但是 aesKey 和 aesIv 还待处理,提供两种处理方式
// (1)base64 编码 aesKey 和 aesIv
String aesKeyStr = Base64Util.encodeBytesToString(aesKey);
String aesIvStr = Base64Util.encodeBytesToString(aesIv);
// 测试 AES 解密
// 因为密文经过了 Base64 编码,所以需要 Base64 解码
byte[] encryptedData = Base64Util.decodeStringToBytes(encryptData);
byte[] sessionKey = Base64Util.decodeStringToBytes(aesKeyStr);
byte[] iv = Base64Util.decodeStringToBytes(aesIvStr);
String decryptData = AESUtil.dec(encryptedData, sessionKey, iv);
JSONObject decryptInfo = JSON.parse(decryptData, JSONObject.class);
System.out.println(decryptInfo.toString());
// {"phone":"182****6871","name":"张三","age":"21"}
}
}
对称加密 DES
public class DESUtils {
private static final String INSTANCE_STR = "DES";
public static String encrypt(byte[] datasource, String password) {
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(INSTANCE_STR);
SecretKey secureKey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance(INSTANCE_STR);
// 用密匙初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, secureKey, random);
// 现在,获取数据并加密
// 正式执行加密操作
byte[] bytes = cipher.doFinal(datasource);
return Base64.getEncoder().encodeToString(bytes);
} catch (Throwable e) {
throw new RuntimeException("encrypt error", e);
}
}
public static String decrypt(String source, String password) {
try {
byte[] src = Base64.getDecoder().decode(source);
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
// 创建一个DESKeySpec对象
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(INSTANCE_STR);
// 将DESKeySpec对象转换成SecretKey对象
SecretKey secureKey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance(INSTANCE_STR);
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, secureKey, random);
// 真正开始解密操作
byte[] bytes = cipher.doFinal(src);
return new String(bytes, StandardCharsets.UTF_8);
} catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException |
IllegalBlockSizeException | BadPaddingException e) {
throw new RuntimeException("解密失败",e);
}
}
private DESUtils() {
// no instance
}
}
测试
public class CryptoTest {
private static final String SECRET_KEY = "4bP5T8UG7hxNMnkJa%Kr4%bSz7!yFA&k";
@Test
public void testDES(){
Map<String, String> user = new HashMap<>();
user.put("name","李四");
user.put("age","21");
user.put("phone","182****6871");
String encryptData = DESUtils.encrypt(JSON.toJSON(user).getBytes(), SECRET_KEY);
System.out.println("encrypt:" + encryptData);
//encrypt:fRqG1ADBOv0XZmfgp21xfP809NklJi/73EMMj5YB5holalroe0hepaUnwcRM3cjSSyuQ6nABvAIOpmttz2Ie/ADlTwKqDqa8
String decrypt = DESUtils.decrypt(encryptData, SECRET_KEY);
System.out.println("decrypt:" + decrypt);
// decrypt:{
// "phone" : "182****6871",
// "name" : "李四",
// "age" : "21"
//}
}
}
非对称加密 RSA
RSA算法广泛应用于加密与认证两个领域
1.加密(保证数据安全性) 使用公钥加密,需使用私钥解密。 这种广泛应用在保证数据的安全性的方面,用户将自己的公钥广播出去,所有人给该用户发数据时使用该公钥加密,但是只有该用户可以使用自己的私钥解密,保证了数据的安全性。
2.认证(用于身份判断) 使用私钥签名,需使用公钥验证签名。 用户同样将自己的公钥广播出去,给别人发送数据时,使用私钥加密,在这里,我们更乐意称它为签名,然后别人用公钥验证签名,如果解密成功,则可以判断对方的身份。
/**
* 密钥对
* private=私钥, public=公钥
*/
public class RSAKey {
private String publicKey;
private String privateKey;
public RSAKey(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
以下是工具方法
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
@Slf4j
public class RSAUtil {
private static final String ALGORITHM = "RSA/ECB/PKCS1Padding";
private static final String KEY_ALGORITHM = "RSA";
/**
* 生成密钥对
* @param keySize the keySize. This is an algorithm-specific metric, such as modulus length, specified innumber of bits
* @return 返回公钥和私钥对
*/
public static RSAKey generateKeyPair(int keySize) {
try {
SecureRandom sr = new SecureRandom();
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
kpg.initialize(keySize, sr);
KeyPair kp = kpg.generateKeyPair();
Key publicKey = kp.getPublic();
byte[] publicKeyBytes = publicKey.getEncoded();
String pub = Base64.getEncoder().encodeToString(publicKeyBytes);
Key privateKey = kp.getPrivate();
byte[] privateKeyBytes = privateKey.getEncoded();
String pri = Base64.getEncoder().encodeToString(privateKeyBytes);
return new RSAKey(pub, pri);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* RSA公钥加密
* @param source 内容
* @param key 公钥(PublicKey) 或者 私钥(PrivateKey)
* @return 加密后内容, 可以选择编码, 这里返回Base64编码后的加密数据
*/
public static String enc(byte[] source, Key key) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal(source);
return Base64Util.encodeBytesToString(encryptedData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* RSA私钥解密
* @param source 已加密内容
* @param key 私钥(PrivateKey) 或者 公钥(PublicKey)
* @return 解密后内容
*/
public static String dec(byte[] source, Key key) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(source);
return new String(result, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据公钥字符串获取 公钥对象
* @param key 公钥字符串
* @return 公钥对象
*/
public static PublicKey getPublicKey(String key) {
try {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据私钥字符串获取 私钥对象
* @param key 私钥字符串
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String key) {
try {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
注意上面方法中的加密解密方法 如果是使用公钥加密则需要私钥进行解密,如果是使用私钥加密则需要公钥进行解密
测试
public class CryptoTest {
/**
* 测试 RSA,数据加密(如果是使用公钥加密则需要私钥进行解密)
*/
@Test
public void testRSA(){
try {
RSAKey rsaKey = RSAUtil.generateKeyPair(1024);
String publicKey = rsaKey.getPublicKey();
String privateKey = rsaKey.getPrivateKey();
System.out.println("公钥:" + publicKey);
System.out.println("私钥:" + privateKey);
//公钥:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBdePukjrUG7Co8iqaQCLSh57fNQsAqcAVUT1RGJrcJ0KBzyswhCCvcMqXi08ATSCYfL3NAp3LoJoc8OaTeKtpsryIsl5L4AmQoqY5BF6bWHsCtbLiHtvZYGuSVkJrlGekoq3VUepi8WYil+hM4/uhH+TVO+9Y4JItpG8mfynnQIDAQAB
//私钥:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALEF14+6SOtQbsKjyKppAItKHnt81CwCpwBVRPVEYmtwnQoHPKzCEIK9wypeLTwBNIJh8vc0Cncugmhzw5pN4q2myvIiyXkvgCZCipjkEXptYewK1suIe29lga5JWQmuUZ6SirdVR6mLxZiKX6Ezj+6Ef5NU771jgki2kbyZ/KedAgMBAAECgYB07a/yPxEVZf6TUI4mic8TMnUqCk03eNUIskonW/FKmIsSaa9ZSaKQSXoVjTmBziX6DWrogJZ9HNqE1hqY3ONhxnLuIevBrVlnyPLv4FWj03AgPi+JvUqG09Q39k0uf2+ZElTfO1t3KriFsz8Il9vCOxGX94dOdXJtyUD7Mw6x4QJBAPZdXPvKuDWKIa8tdz+10Kl5WTtkezllXJg8iu1B35oBu7zSWcPshywb3yOp3/lmiBWl2owu1zpWfwSX43Ap/DkCQQC38jfw1/lPYnCL8ZXKz+QRgkeJ00mmrE+2FoJukG1GB0Af4n9hl60S0NRN37Ow93tQwaiY+Yp8yT7s2sTWbY6FAkEAvS8YtioHpuV51GGfjwb5QwryYM9aaMrTffwP2v2YzVTDa85ELFW3v0Fv6p0KZyQ7IBSKbNeTYzMEuuxANOOo0QJAG+HdVOaH5d6YDL5r5Dgq6/SBf4TFbzxDQOickMHXrnu+3pZolJR3KZj59WvpQvPgf1c7hSSeN1gR77khiRdGOQJAeEMT7Q3EyxudtIwKM41HenrxEr6etwfeMXvk6wMXH6g7McJ67mlqViTg4Ob3dl11tIYOhvF23ZENFKJTmE9HoA==
Map<String, String> user = new HashMap<>();
user.put("name","张三");
user.put("age","21");
user.put("phone","182****6871");
// RSA 加密,公钥加密
String encryptData = RSAUtil.enc(JSON.toJSON(user).getBytes(StandardCharsets.UTF_8), RSAUtil.getPublicKey(publicKey));
System.out.println("encryptData:" + encryptData);
//encryptData:FI44nR80K9x5nlAR3q/KodgOpmo36AgBex3s/e2pvhF03oB4n2zBstWmhV8qe7ezI1Avp389KqtZ/L9YefETik/QqW2hDuuT06Kt2YbpDmOC5q6FFCsbIhLd8x2HUq/7Nq6EJcyL2oPofWi1YPKzRKBWXzITm+H+Snxwygd0tQ4=
// 因为我们的密文经过 Base64 编码,所以需要 Base64 解码, 编码的原因是方便传输
byte [] b = Base64.getDecoder().decode(encryptData);
//RSA 解密,私钥解密
String decryptData = RSAUtil.dec(b, RSAUtil.getPrivateKey(privateKey));
JSONObject decryptInfo = JSON.parse(decryptData, JSONObject.class);
System.out.println("decryptData:" + decryptInfo.toString());
// decryptData:{"phone":"182****6871","name":"张三","age":"21"}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 测试 RSA,身份认证(如果是使用私钥加密则需要公钥进行解密)
*/
public void testRSA1(){
try {
RSAKey rsaKey = RSAUtil.generateKeyPair(1024);
String publicKey = rsaKey.getPublicKey();
String privateKey = rsaKey.getPrivateKey();
System.out.println("公钥:" + publicKey);
System.out.println("私钥:" + privateKey);
//公钥:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBdePukjrUG7Co8iqaQCLSh57fNQsAqcAVUT1RGJrcJ0KBzyswhCCvcMqXi08ATSCYfL3NAp3LoJoc8OaTeKtpsryIsl5L4AmQoqY5BF6bWHsCtbLiHtvZYGuSVkJrlGekoq3VUepi8WYil+hM4/uhH+TVO+9Y4JItpG8mfynnQIDAQAB
//私钥:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALEF14+6SOtQbsKjyKppAItKHnt81CwCpwBVRPVEYmtwnQoHPKzCEIK9wypeLTwBNIJh8vc0Cncugmhzw5pN4q2myvIiyXkvgCZCipjkEXptYewK1suIe29lga5JWQmuUZ6SirdVR6mLxZiKX6Ezj+6Ef5NU771jgki2kbyZ/KedAgMBAAECgYB07a/yPxEVZf6TUI4mic8TMnUqCk03eNUIskonW/FKmIsSaa9ZSaKQSXoVjTmBziX6DWrogJZ9HNqE1hqY3ONhxnLuIevBrVlnyPLv4FWj03AgPi+JvUqG09Q39k0uf2+ZElTfO1t3KriFsz8Il9vCOxGX94dOdXJtyUD7Mw6x4QJBAPZdXPvKuDWKIa8tdz+10Kl5WTtkezllXJg8iu1B35oBu7zSWcPshywb3yOp3/lmiBWl2owu1zpWfwSX43Ap/DkCQQC38jfw1/lPYnCL8ZXKz+QRgkeJ00mmrE+2FoJukG1GB0Af4n9hl60S0NRN37Ow93tQwaiY+Yp8yT7s2sTWbY6FAkEAvS8YtioHpuV51GGfjwb5QwryYM9aaMrTffwP2v2YzVTDa85ELFW3v0Fv6p0KZyQ7IBSKbNeTYzMEuuxANOOo0QJAG+HdVOaH5d6YDL5r5Dgq6/SBf4TFbzxDQOickMHXrnu+3pZolJR3KZj59WvpQvPgf1c7hSSeN1gR77khiRdGOQJAeEMT7Q3EyxudtIwKM41HenrxEr6etwfeMXvk6wMXH6g7McJ67mlqViTg4Ob3dl11tIYOhvF23ZENFKJTmE9HoA==
Map<String, String> user = new HashMap<>();
user.put("name","张三");
user.put("age","21");
user.put("phone","182****6871");
// RSA 加密,私钥加密
String encryptData = RSAUtil.enc(JSON.toJSON(user).getBytes(StandardCharsets.UTF_8), RSAUtil.getPrivateKey(privateKey));
System.out.println("encryptData:" + encryptData);
//encryptData:FI44nR80K9x5nlAR3q/KodgOpmo36AgBex3s/e2pvhF03oB4n2zBstWmhV8qe7ezI1Avp389KqtZ/L9YefETik/QqW2hDuuT06Kt2YbpDmOC5q6FFCsbIhLd8x2HUq/7Nq6EJcyL2oPofWi1YPKzRKBWXzITm+H+Snxwygd0tQ4=
// 因为我们的密文经过 Base64 编码,所以需要 Base64 解码, 编码的原因是方便传输
byte [] b = Base64.getDecoder().decode(encryptData);
//RSA 解密,公钥解密
String decryptData = RSAUtil.dec(b, RSAUtil.getPublicKey(publicKey));
JSONObject decryptInfo = JSON.parse(decryptData, JSONObject.class);
System.out.println("decryptData:" + decryptInfo.toString());
// decryptData:{"phone":"182****6871","name":"张三","age":"21"}
} catch (Exception e) {
e.printStackTrace();
}
}
}