一、对称加密与非对称加密
1、对称加密
对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥加密,这种方法在密码学中叫做对称加密算法,对称加密算法使用起来简单快捷,密钥较短,且破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,而且对计算机功能要求也没有那么高。
常见的对称加密算法有DES、3DES、Blowfish、IDEA、RC4、RC5、RC6和AES
2、非对称加密
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。
公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
3、对称加密与非对称加密的区别
- 1、加密和解密过程不同
对称加密的加密过程和解密过程使用的同一个密钥,加密过程相当于用原文+密钥可以传输出密文,同时解密过程用密文-密钥可以推导出原文。
但非对称加密采用了两个密钥,一般使用公钥进行加密,使用私钥进行解密。 - 2、加密解密速度不同
对称加密解密的速度比较快,适合数据比较长时的使用。
非对称加密和解密花费的时间长、速度相对较慢,只适合对少量数据的使用。 - 3、传输的安全性不同
对称加密的过程中无法确保密钥被安全传递,密文在传输过程中是可能被第三方截获的,如果密码本也被第三方截获,则传输的密码信息将被第三方破获,安全性相对较低。
非对称加密算法中私钥是基于不同的算法生成不同的随机数,私钥通过一定的加密算法推导出公钥,但私钥到公钥的推导过程是单向的,也就是说公钥无法反推导出私钥。所以安全性较高。
二、加密算法实例
1、MD5加密单向加密
- MD5是一种单向加密算法,只能加密不能解密
- MD5 主要用做数据一致性验证、数字签名和安全访问认证,而不是用作加密。
package com.example.demo.password;
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)));
}
}
2、Base64加密解密
Java 8 内置了 Base64 编码的编码器和解码器。
Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:
- 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
- URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
- MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割。
static class Base64.Decoder
:该类实现一个解码器用于,使用 Base64 编码来解码字节数据。static class Base64.Encoder
:该类实现一个编码器,使用 Base64 编码来编码字节数据。
package com.example.demo.password;
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());
}
}
}
Base 64主要用途不是加密,而是把一些二进制数转成普通字符,方便在网络上传输。 由于一些二进制字符在传输协议中属于控制字符,不能直接传送,所以需要转换一下才可以。由于某些系统中只能使用ASCII字符,Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法,Base64特别适合在http,mime协议下快速传输数据。比如网络中图片的传输。
package com.example.demo.password;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
public class ImgToBase64String {
/**
* 图片转base64
* @param filePath 图片文件路径
* @return
*/
public static String toBase64String(String filePath){
if (null == filePath || 0 == filePath.length()){
throw new RuntimeException("图片路径不能为空!!");
}
InputStream in = null;
byte[] data = null;
try {
//读取图片字节数组
in = new FileInputStream(filePath);
data = new byte[in.available()];
in.read(data);
in.close();
}catch (IOException e){
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
sb.append("data:image/jpg;base64,");
sb.append(Base64.getEncoder().encodeToString(data));
//对字节数组Base64编码,返回Base64编码过的字节数组字符串
return sb.toString();
}
public static void main(String[] args) {
System.out.println(toBase64String("D:\\下载\\desktop.png"));
}
}
3、DES对称加密解密
因jdk1.8自带的base64有所不同,使用jdk其他版本是可适当自行修改。
package com.example.demo.password;
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 world";
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);
}
}
4、AES对称加密解密
AES 与 DES 一样,一共有四种加密模式:电子密码本模式(ECB)、加密分组链接模式(CBC)、加密反馈模式(CFB)和输出反馈模式(OFB)。
package com.example.demo.password;
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 = "thisisrule";
String text = "hello word";
String encodeText = AESEncode(encodeRules, text);
System.out.println("加密后的密文是:" + encodeText);
System.out.println("解密后的内容是:" + AESDncode(encodeRules, encodeText));
}
}
5、非对称加密——RSA
RSA算法是一种非对称加密算法,RSA 加密主要有这么几步:生成密钥对、公开公钥、公钥加密私钥解密、私钥加密公钥解密。
package com.example.demo.password;
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 world";
String encodeText = encrypt(text, publicKey);
System.out.println("加密后:" + encodeText);
System.out.println("解密后:" + decrypt(encodeText, privateKey));
}
}