1.背景
项目过程中遇到很多需要加密和解密的数据,用于记录学习
2.对称加密和非对称加密
3.对称加密
对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥加密,这种方法在密码学中叫做对称加密算法,对称加密算法使用起来简单快捷,密钥较短,且破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,而且对计算机功能要求也没有那么高。常见的对称加密算法有DES、3DES、Blowfish、IDEA、RC4、RC5、RC6和AES
4.非对称加密
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。
公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法
5.区别
1、加密和解密过程不同
对称加密的加密过程和解密过程使用的同一个密钥,加密过程相当于用原文+密钥可以传输出密文,同时解密过程用密文-密钥可以推导出原文。
但非对称加密采用了两个密钥,一般使用公钥进行加密,使用私钥进行解密。
2、加密解密速度不同
对称加密解密的速度比较快,适合数据比较长时的使用。
非对称加密和解密花费的时间长、速度相对较慢,只适合对少量数据的使用。
3、传输的安全性不同
对称加密的过程中无法确保密钥被安全传递,密文在传输过程中是可能被第三方截获的,如果密码本也被第三方截获,则传输的密码信息将被第三方破获,安全性相对较低。
非对称加密算法中私钥是基于不同的算法生成不同的随机数,私钥通过一定的加密算法推导出公钥,但私钥到公钥的推导过程是单向的,也就是说公钥无法反推导出私钥。所以安全性较高。
6、例子
6.1 DES加解密
DES 全称 Data Encryption Standard(数据加密标准),作为算法的DES称为数据加密算法(Data Encryption Algorithm, DEA),已与作为标准的DES区分开来
package com.cloud.user;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
public class DESUtil {
public static Key setKey(String strKey) {
Key key = null;
try {
KeyGenerator generator = KeyGenerator.getInstance("DES");
generator.init(new SecureRandom(strKey.getBytes())); // 根据参数生成key
key = generator.generateKey();
} catch (Exception e) {
e.printStackTrace();
}
return key;
}
/**
* @param source 编码内容
* @param key 密钥
* @param charSet 编码格式
* @return
*/
public static String encrypt(String source, String key, String charSet) {
String encrypt = null;
try {
byte[] ret = encrypt(source.getBytes(charSet), key);
encrypt = new String(Base64.getEncoder().encode(ret));
} catch (Exception e) {
e.printStackTrace();
encrypt = null;
}
return encrypt;
}
/**
* @param encryptedData 解码内容
* @param key 密钥
* @param charSet 编码格式
* @return
*/
public static String decrypt(String encryptedData, String key, String charSet) {
String descryptedData = null;
try {
byte[] ret = descrypt(Base64.getDecoder().decode(encryptedData.getBytes()), key);
descryptedData = new String(ret, charSet);
} catch (Exception e) {
e.printStackTrace();
descryptedData = null;
}
return descryptedData;
}
private static byte[] encrypt(byte[] primaryData, String key) {
Key desKey = setKey(key);
try {
Cipher cipher = Cipher.getInstance("DES"); // Cipher对象实际完成加密操作
cipher.init(Cipher.ENCRYPT_MODE, desKey); // 用密钥初始化Cipher对象(加密)
return cipher.doFinal(primaryData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static byte[] descrypt(byte[] encryptedData, String key) {
Key desKey = setKey(key);
try {
Cipher cipher = Cipher.getInstance("DES"); // Cipher对象实际完成解密操作
cipher.init(Cipher.DECRYPT_MODE, desKey); // 用密钥初始化Cipher对象(解密)
return cipher.doFinal(encryptedData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
String code = "hello";
String key = "thisisakey";
String unicode = "utf-8";
String encrypt = encrypt(code, key, unicode);
String decrypt = decrypt(encrypt, key, unicode);
System.out.println("原内容:" + code);
System.out.println("加密:" + encrypt);
System.out.println("解密:" + decrypt);
}
}
6.2 3DES加解密
三重数据加密算法(英语:Triple Data Encryption Algorithm,缩写为TDEA,Triple DEA),或称3DES(Triple DES),是一种对称密钥加密块密码,相当于是对每个数据块应用三次DES算法。由于计算机运算能力的增强,原版DES由于密钥长度过低容易被暴力破解;3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
添加依赖
<dependency> <groupId>io.lions</groupId> <artifactId>LionsUtils</artifactId> <version>0.0.1-SNAPSHOT</version> <exclusions> <exclusion> <groupId>cn.ccvnc</groupId> <artifactId>commons-logging</artifactId> </exclusion> <exclusion> <groupId>cn.ccvnc</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency>
工具类
package com.cloud.common.util;
import io.lions.security.encryption.bidirectional.DESede;
/**
* 常量工具类
*/
public class ConstantUtils {
/**
* 加密
*/
public static final DESede DES = new DESede();
/**
* 加密盐
*/
public static final String SALT = "@321Dr0wSsaP111";
}
测试
package com.cloud.user;
import com.cloud.common.util.ConstantUtils;
public class Demo3DES {
public static void main(String[] args) throws Exception {
System.out.println("------加密-------");
String mingwen = "admin1";
encrypt(mingwen);
System.out.println("------解密-------");
String miwen = "W6%2FfyeyMdWEI32ZCPTHLAw%3D%3D";
decrypt(miwen);
}
public static void encrypt(String str) {
String desEncrypt = ConstantUtils.DES.get3DESEncrypt(str, ConstantUtils.SALT);
System.out.println(desEncrypt);
}
public static void decrypt(String str) {
String desDecrypt = ConstantUtils.DES.get3DESDecrypt(str, ConstantUtils.SALT);
System.out.println(desDecrypt);
}
}
6.3 md5单向加密
MD5是一种单向加密算法,只能加密不能解密
MD5 主要用做数据一致性验证、数字签名和安全访问认证,而不是用作加密
package com.cloud.user;
import org.springframework.util.DigestUtils;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
/**
* java自带jar工具 java.security.MessageDigest 实现
*
* @param text 要加密的字符串
* @param rad 进制:比如 16、32
* @param isUpp 是否转换为大写
* @return
*/
public static String strToMD5(String text, int rad, boolean isUpp) {
MessageDigest messageDigest = null;
try {
//通过MessageDigest类来的静态方法getInstance获取MessageDigest对象
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 4、获取明文字符串对应的字节数组
byte[] input = text.getBytes();
// 5、执行加密
messageDigest.update(input);
//这里也可以直接使用byte[] output = messageDigest.digest(input)方法来进行加密,就省略了上面的update方法了
byte[] output = messageDigest.digest();
// 6、创建BigInteger对象
// signum为1表示正数、-1表示负数、0表示0。不写默认表示正数
int signum = 1;
BigInteger bigInteger = new BigInteger(signum, output);
// 7、按照16进制(或32进制)将bigInteger转为字符串
return isUpp ? bigInteger.toString(rad).toUpperCase() : bigInteger.toString(rad);
}
/**
* spring自带的工具 org.springframework.util.DigestUtils 实现
* @param text
* @return 16进制
*/
public static String _strToMD5(String text) {
return DigestUtils.md5DigestAsHex(text.getBytes());
}
public static void main(String[] args) {
String str = "hello";
System.out.println(strToMD5(str, 16, true));
System.out.println(_strToMD5(str));
System.out.println(strToMD5(str, 16, false).equals(_strToMD5(str)));
}
}
6.4 Base64加解密
Java 8 内置编码器和解码器。
static class Base64.Decoder:解码器,使用 Base64 编码来解码字节数据。
static class Base64.Encoder:编码器,使用 Base64 编码来编码字节数据。
package com.cloud.user;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.UUID;
public class Base64Util {
public static void main(String[] args) throws Exception {
String text = "hello";
try {
/**
* static Base64.Encoder getEncoder()
* 返回一个 Base64.Encoder ,编码使用基本型 base64 编码方案。
*/
String base64encodedString = Base64.getEncoder().encodeToString(text.getBytes("utf-8"));
System.out.println("Base64 编码字符串 (基本) :" + base64encodedString);
/**
* static Base64.Decoder getDecoder()
* 返回一个 Base64.Decoder ,解码使用基本型 base64 编码方案。
*/
byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
System.out.println("原始字符串: " + new String(base64decodedBytes, "utf-8"));
/**
* static Base64.Encoder getUrlEncoder()
* 返回一个 Base64.Encoder ,编码使用 URL 和文件名安全型 base64 编码方案。
*/
base64encodedString = Base64.getUrlEncoder().encodeToString(text.getBytes("utf-8"));
System.out.println("Base64 编码字符串 (URL) :" + base64encodedString);
/**
* static Base64.Decoder getUrlDecoder()
* 返回一个 Base64.Decoder ,解码使用 URL 和文件名安全型 base64 编码方案。
*/
byte[] base64decodedBytes2 = Base64.getUrlDecoder().decode(base64encodedString);
System.out.println("原始字符串: " + new String(base64decodedBytes2, "utf-8"));
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; ++i) {
stringBuilder.append(UUID.randomUUID().toString());
}
byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8");
String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes);
System.out.println("Base64 编码字符串 (MIME) :" + mimeEncodedString);
} catch (UnsupportedEncodingException e) {
System.out.println("Error :" + e.getMessage());
}
}
}
6.5 AES加解密
AES 与 DES 一样,一共有四种加密模式:电子密码本模式(ECB)、加密分组链接模式(CBC)、加密反馈模式(CFB)和输出反馈模式(OFB)
package com.cloud.user;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class AESUtil {
//指定为AES算法,不区分大小写
private static final String KEY_ALGORITHM = "AES";
private static final String CHARSET_NAME = "utf-8";
/*
* 加密 1.构造密钥生成器 2.根据ecnodeRules规则初始化密钥生成器 3.产生密钥 4.创建和初始化密码器 5.内容加密 6.返回字符串
*/
public static String AESEncode(String encodeRules, String content) {
try {
// 1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance(KEY_ALGORITHM);
// 2.根据ecnodeRules规则初始化密钥生成器
// 生成一个128位的随机源,根据传入的字节数组
//keygen.init(128, new SecureRandom(encodeRules.getBytes()));
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(encodeRules.getBytes());
keygen.init(128, secureRandom);
// 3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
// 4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
// 5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, KEY_ALGORITHM);
// 6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
// 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
// 8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = content.getBytes(CHARSET_NAME);
// 9.根据密码器的初始化方式--加密:将数据加密
byte[] byte_AES = cipher.doFinal(byte_encode);
// 10.将加密后的数据转换为字符串
// 这里用Base64Encoder中会找不到包
// 解决办法:
// 在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
String AES_encode = new String(Base64.getEncoder().encodeToString(byte_AES));
// 11.将字符串返回
return AES_encode;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 如果有错就返加nulll
return null;
}
/*
* 解密 解密过程: 1.同加密1-4步 2.将加密后的字符串反纺成byte[]数组 3.将加密内容解密
*/
public static String AESDncode(String encodeRules, String content) {
try {
// 1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance(KEY_ALGORITHM);
// 2.根据ecnodeRules规则初始化密钥生成器
// 生成一个128位的随机源,根据传入的字节数组
//keygen.init(128, new SecureRandom(encodeRules.getBytes()));
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(encodeRules.getBytes());
keygen.init(128, secureRandom);
// 3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
// 4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
// 5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, KEY_ALGORITHM);
// 6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
// 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
// 8.将加密并编码后的内容解码成字节数组
byte[] byte_content = Base64.getDecoder().decode(content);
/*
* 解密
*/
byte[] byte_decode = cipher.doFinal(byte_content);
String AES_decode = new String(byte_decode, CHARSET_NAME);
return AES_decode;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
// 如果有错就返加nulll
return null;
}
public static void main(String[] args) {
String encodeRules = "rule";
String text = "hello";
String encodeText = AESEncode(encodeRules, text);
System.out.println("加密后的密文是:" + encodeText);
System.out.println("解密后的内容是:" + AESDncode(encodeRules, encodeText));
}
}
6.6 非对称加密——RSA
RSA密钥对生成:http://web.chacuo.net/netrsakeypair
在线加密解密:在线加密解密 - chahuo.com
RSA算法是一种非对称加密算法,RSA 加密主要有这么几步:生成密钥对、公开公钥、公钥加内容加密、私钥+加密公钥解密。
package com.cloud.user;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class RSAUtil {
private static final String CHARSET_NAME = "utf-8";
private static final String TYPE = "RSA";
/**
* RSA公钥加密
*
* @param str 加密字符串
* @param publicKey 公钥
* @return 密文
* @throws Exception 加密过程中的异常信息
*/
public static String encrypt(String str, String publicKey) throws Exception {
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(TYPE).generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance(TYPE);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(CHARSET_NAME)));
return outStr;
}
/**
* RSA私钥解密
*
* @param str 加密字符串
* @param privateKey 私钥
* @return 铭文
* @throws Exception 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception {
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes(CHARSET_NAME));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(TYPE).generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance(TYPE);
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
public static void main(String[] args) throws Exception {
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKalqxfHXrZ0JZdLd2+BLhNS6bZxVyCOXvzFd0xCyX4oX/IbglKp9BGxQYaNl7stlHkQmMYBTAkIj0mAQzOVkqisYDevxKA5Yeitnim8R+N+a1SYaoQCfHLbCmMg4ZP0xaC30rA3DSMbEWQTD7p/g6v3sZevQUDVUcge9oaif9AQIDAQAB";
String privateKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMpqWrF8detnQll0t3b4EuE1LptnFXII5e/MV3TELJfihf8huCUqn0EbFBho2Xuy2UeRCYxgFMCQiPSYBDM5WSqKxgN6/EoDlh6K2eKbxH435rVJhqhAJ8ctsKYyDhk/TFoLfSsDcNIxsRZBMPun+Dq/exl69BQNVRyB72hqJ/0BAgMBAAECgYEAwOZKOArcddKaMJZCoWYY1/bOy9qZXWuNddHPJsAtnzGJcXK5AvJzgqBDrl99o5z15HYcG2MVY85aNn8IwahNh8CpFF8At7O2hVz1sTER2MPeiV5324BGOCPUkT4lY2iT2Dq6hXraZDI9sovit4FnfFlWH9nMOV5ckBvm5ypcCoECQQD/YLgSqXEERuu5qjU+PBCBKqbtOq5+EBNwLb9isxTPjRPoQ98sEzWsbyeLrc73Cjql5vU1io9rsG3IiYdEvFn9AkEAyuiaK95pbILOqK096d3Gt9oz0fqc5K4c9V44anaFzkEOysZlO3o1iZxqjfHdBlJiE4j0fq52+s+L9riqFbNMVQJATL3V0tXUPoLJZ3u8kD0ggJA+pV9S/FL8ZGN69b/26v/sEYoD0IzdPjoQ2iqa3SXXxe8HlNVUj/nuo6qgWYl4SQJBAJsXsYfoh7JeRXHegV15m8O5sDRGl5efkhjmfL67e0kMpy7M+GG+5p8ZhMScYzHK1JZT73XJCr5o13Ws7qyJkMUCQQD2EaVPOG+5M0VbzGGG820NRk9omSwO3GdSf4HZ6JWmeGk0FJN4NfAHnD8nA5L/HP+muqCdYQtYD068oZrNSabP";
String text = "hello";
String encodeText = encrypt(text, publicKey);
System.out.println("加密后:" + encodeText);
System.out.println("解密后:" + decrypt(encodeText, privateKey));
}
}