# 加密算法的分类
# 是否需要key
- 不基于key的有:
- Base64算法
- MD5
- 基于key的有:
- 对称加密算法
- 非对称加密算法
- 数字签名算法
- 数字证书
- HMAC(Hash-based Message Authentication Code,hash消息认证码)
- RC4(对称加密)
# 加密算法是否可逆
- 单向加密
- MD5
- SHA
- HMAC
- 非单向加密
- BASE64
- 对称加密算法
- 非对称加密算法
- 数字签名算法
- 数字证书
# JAVA常见加解密示例
# MD5
- 核心功能位于 java.security.*包
package com.automannn.demo.security;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author automannn
* @Date 2022/7/24
*/
public class MD5Test {
public static void main(String[] args) throws NoSuchAlgorithmException {
String originMsg = "originMsg";
MessageDigest md5 = MessageDigest.getInstance("MD5");
System.out.println(new String(md5.digest(originMsg.getBytes(StandardCharsets.UTF_8))));
//BigInteger函数用于将 8位的字符串 转为 16位的 hex值,得到字符串形式的hash值
BigInteger digest = new BigInteger(md5.digest(originMsg.getBytes(StandardCharsets.UTF_8)));
System.out.println(digest.toString(16));
}
}
# BASE64
- 核心功能位于 java.util.Base64包
package com.automannn.demo.security;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* @author automannn
* @Date 2022/7/24
*/
public class Base64Test {
public static void main(String[] args) {
String originMessage = "originMessage";
byte[] encodedMsg = Base64.getEncoder().encode(originMessage.getBytes(StandardCharsets.UTF_8));
byte[] decodedMsg = Base64.getDecoder().decode(encodedMsg);
System.out.println(new String(decodedMsg));
}
}
# DES
- 核心方法位于javax.crypto.*包
package com.automannn.demo.security;
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
/**
* @author automannn
* @Date 2022/7/24
*/
public class DESTest {
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
String originMsg = "OriginMessage";
//加密密钥,长度至少要大于8位
String password = "aaddffee1";
//=================加密=======================
DESKeySpec desKey = new DESKeySpec(password.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKey);
Cipher cipher = Cipher.getInstance("DES");
//用密匙初始化 cipher对象
cipher.init(Cipher.ENCRYPT_MODE,secretKey,new SecureRandom());
byte[] encodedContent= cipher.doFinal(originMsg.getBytes(StandardCharsets.UTF_8));
System.out.println(new String(encodedContent));
//BigInteger函数用于将 8位的字符串 转为 16位的 hex值,得到字符串形式的值
BigInteger digest = new BigInteger(encodedContent);
System.out.println(digest.toString(16));
//=========================解密=============
cipher.init(Cipher.DECRYPT_MODE,secretKey,new SecureRandom());
byte[] decodedContent = cipher.doFinal(encodedContent);
System.out.println(new String(decodedContent));
}
}
# RSA
- 核心方法位于javax.crypto.*包
package com.automannn.demo.security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
/**
* @author automannn
* @Date 2022/7/24
*/
public class RSATest {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
String originMsg = "originMessage";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
// RSA算法的模长,即为 最大加密数据的大小
keyPairGenerator.initialize(1024);
KeyPair keyPair= keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
//===============公钥加密================
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
//将模长 转为字节数
int modulusSize = ((RSAPublicKey)publicKey).getModulus().bitLength()/8;
//PKCS Padding 长度为11字节,实际要加密的数据要去掉
int maxSingleSize = modulusSize -11;
//切分数组,分段加密
byte[][] dataArray = splitArray(originMsg.getBytes(StandardCharsets.UTF_8),maxSingleSize);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (byte[] arr: dataArray){
outputStream.write(cipher.doFinal(arr));
}
String encodedContent = Base64.getEncoder().encodeToString(outputStream.toByteArray());
System.out.println(encodedContent);
//===============私钥解密===========================
cipher.init(Cipher.DECRYPT_MODE,privateKey);
//获取加密算法的模长,转为字节数
int priModulusSize = ((RSAPrivateKey)privateKey).getModulus().bitLength()/8;
byte[] encodedData = outputStream.toByteArray();
byte[][] priSplitArrays = splitArray(encodedData,priModulusSize);
ByteArrayOutputStream desOutputStream = new ByteArrayOutputStream();
//分段解密
for (byte[] arr: priSplitArrays){
desOutputStream.write(cipher.doFinal(arr));
}
String decodedData = new String(desOutputStream.toByteArray());
System.out.println(decodedData);
}
//按照指定长度切分数组
private static byte[][] splitArray(byte[] data, int len) {
int dataLen = data.length;
if (dataLen <= len) {
return new byte[][]{data};
}
byte[][] result = new byte[(dataLen-1)/len + 1][];
int resultLen = result.length;
for (int i = 0; i < resultLen; i++) {
if (i == resultLen - 1) {
int slen = dataLen - len * i;
byte[] single = new byte[slen];
System.arraycopy(data, len * i, single, 0, slen);
result[i] = single;
break;
}
byte[] single = new byte[len];
System.arraycopy(data, len * i, single, 0, len);
result[i] = single;
}
return result;
}
}
# 字符串转为公私钥证书
package com.automannn.demo.security;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* @author automannn
* @Date 2022/7/24
*/
public class RSAStrUtil {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair= keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey =keyPair.getPrivate();
String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
//公钥从字符串复原为对象
RSAPublicKey rsaPublicKey = getPublicKey(publicKeyStr);
System.out.println(rsaPublicKey);
//私钥从字符串复原为对象
RSAPrivateKey rsaPrivateKey = getPrivateKey(privateKeyStr);
System.out.println(rsaPrivateKey);
}
//Java中 RSAPublicKeySpec, X509EncodedKeySpec 支持生成RSA公钥
public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey) keyFactory.generatePublic(x509EncodedKeySpec);
}
//java中, RSAPrivateKeySpec, PKCS8EncodedKeySpec支持生成 RSA私钥
public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8EncodedKeySpec);
}
}
# SHA
- 核心方法位于 java.security.* 包
package com.automannn.demo.security;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author automannn
* @Date 2022/7/24
*/
public class SHATest {
public static void main(String[] args) throws NoSuchAlgorithmException {
String originMsg = "originMessage";
MessageDigest sha = MessageDigest.getInstance("SHA");
byte[] sha_byte = sha.digest(originMsg.getBytes(StandardCharsets.UTF_8));
StringBuilder hexValue = new StringBuilder();
for (byte b: sha_byte){
//将每个字节转为十六进制字符串: byte类型的数据最高位为符号位,通过和 0xff与操作,转为int类型的正整数
String toHexString = Integer.toHexString(b&0xff);
hexValue.append(toHexString.length()==1?"0"+toHexString:toHexString);
}
System.out.println(hexValue);
BigInteger bigInteger = new BigInteger(sha_byte);
System.out.println(bigInteger.toString(16));
}
}
# HMAC
- 核心功能基于 javax.crypto.*包
package com.automannn.demo.security;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
/**
* @author automannn
* @Date 2022/7/24
*/
public class HMACTest {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String password = "adfdfsd";
String originMsg = "originMessage";
SecretKey secretKey = new SecretKeySpec(password.getBytes(StandardCharsets.UTF_8),"HmacMD5");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
byte[] encodedMsg = mac.doFinal(originMsg.getBytes(StandardCharsets.UTF_8));
System.out.println(Base64.getEncoder().encodeToString(encodedMsg));
}
}
# 在不引入三方库的情况下,JDK支持有限的摘要算法
- HmacMD5
- HmacSHA1
- HmacSHA224
- HmacSHA256
- HmacSHA384
- HmacSHA512
# JavaSecurity程序常见的加密算法
# 顶级接口passwordEncoder
public interface PasswordEncoder {
/*xxx: 加密,向数据库存储密码时使用*/
String encode(CharSequence rawPassword);
/*xxx: 判断登陆时输入的密码是否匹配*/
boolean matches(CharSequence rawPassword, String encodedPassword);
}
# BCrypt
- 密码哈希函数,它可以保证加密速度在一个特定范围内
- 基于blowfish,于1999年提出
public class BCryptPasswordEncoder implements PasswordEncoder {
private final int strength;
private final SecureRandom random;
public String encode(CharSequence rawPassword) {
String salt;
if (strength > 0) {
if (random != null) {
salt = BCrypt.gensalt(strength, random);
}
else {
salt = BCrypt.gensalt(strength);
}
}
else {
salt = BCrypt.gensalt();
}
/*xxx: 根据盐值加密*/
return BCrypt.hashpw(rawPassword.toString(), salt);
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
/*xxx: rawPassword代码密码原文*/
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
}
# 代理加密
public class DelegatingPasswordEncoder implements PasswordEncoder {
private static final String PREFIX = "{";
private static final String SUFFIX = "}";
private final Map<String, PasswordEncoder> idToPasswordEncoder;
private final String idForEncode;
@Override
public String encode(CharSequence rawPassword) {
return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
//xxx: 省略其它抽象...
PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
return delegate.matches(rawPassword, encodedPassword);
}
}
# NoOp
public final class NoOpPasswordEncoder implements PasswordEncoder {
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
}
# 国密(商用密码->商密->SM)
- 国家密码局发布的,一系列由我国自主研发的 商用密码(
Shangyong Mima ->SM
)标准算法;
# SM1(分组加密算法)
- SM1算法没有公开,只能集成在芯片中
- 该算法是对称加密算法
# SM2(椭圆曲线公钥密码算法)
- 国家密码局于2010年12月公布
- 非对称加密算法,使用公钥加密,私钥解密,安全性和运算速度方面,优于RSA算法
# SM3(杂凑算法)
- 不可逆加密算法,类似于md5,常用于签名
# SM4(对称算法)
- 对称加密算法,可用于替代
DES/AES
等国际算法
# SM7(对称密码)
- 适用于门禁类,票务类,支付与通卡类应用
# SM9(标志密码算法)
- 非对称加密算法
- 通过设备标志作为公钥,实现数据加密、身份认证等应用