1、 BASE64
Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,可以查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息。
例如,在Java Persistence系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数。
在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码具有不可读性,即所编码的数据不会被人用肉眼所直接看到。
Java中是用”8个二进制数字”表示一个实际的字节。比如:我要用Base64编码一个字符串“abc”,实际算法如下:
‘a’,’b’,’c’的ASCII标准编码分别为(十进制)97,98,99,因此用二进制表示“abc”字符串就是:
01100001,01100010,01100011 —3组,每组8字节
Base64的原理:将这三组8字节,分成4组6字节
011000,010110, 001001,100011 —4组,每组6字节
高位补0
00011000,00010110, 00001001,00100011
这四个二进制数组对应十进制的数值分别是:24,22,9,35,RFC2045(Base64解码表)分别为:Y,W,J,j
即:”abc”经过Base64编码后,为”YWJj”。这个过程是可逆的。
-–每次Base64编码都会扩容33%
示例:
/**
* Base64编码
* @param message 待Base64编码的字符串
* @return 编码后的字符串
*/
public static String encode(String message){
if (message == null){
return null;
}
byte[] bytes = message.getBytes();
byte[] result = Base64.encodeBase64(bytes);
return new String(result);
}
/**
* Base64解码
* @param message 待Base64解码的字符串
* @return 解码后的字符串
*/
public static String decode(String message){
if (message == null){
return null;
}
byte[] bytes = message.getBytes();
byte[] result = Base64.decodeBase64(bytes);
return new String(result);
}
2、 MD5
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。
将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。广泛用于加密和解密技术,常用于文件校验。
MD5算法简单来说,就是把一段信息(明文),通过一种有损的方式压缩成定长的字符(32位密文)。因为这种压缩方式是会损失信息的,所以是无法还原出“明文”的。
虽然无法从数学上破解MD5算法,但由于现在计算机具备强大的计算能力,还是可以通过“穷举法”破解该算法。
如果想用该算法加密,还可以通过“加盐”的方式提高解密难度。该算法允许多传一个参数”salt”,指定通过MD5加密的次数,这样是能够提高解密难度的。
校验?不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都是MD5校验。
怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文件是否一致的。
示例:
/**
* M5加密
* @param bytes 要加密的信息
* @return 32位字符串
*/
public static String encrypt(byte[] bytes){
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] resultBytes = messageDigest.digest(bytes);
return Hex.encodeHexString(resultBytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
3、SHA
安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。
对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。
该算法经过加密专家多年来的发展和改进已日益完善,并被广泛使用。思想是接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文,也可以简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也称为信息摘要或信息认证代码)的过程。
散列函数值可以说是对明文的一种”指纹”或是”摘要”所以对散列值的数字签名就可以视为对此明文的数字签名。
SHA与MD5均有MD4导出,因此强度和特性比较相似的,都是不可逆的算法。
SHA与MD5的不同:
1、对强行攻击的安全性,SHA-1摘要比MD5摘长,对强行攻击有更大的强度。
2、对密码分析的安全性,由于MD5的设计,易受密码分析的攻击,SHA-1显得不易受这样的攻击。
3、在相同的硬件上,SHA-1的运行速度比MD5慢。
示例:
/**
* SHA加密,即安全散列算法加密
* @param bytes 要加密的信息
* @return 40位字符串
*/
public static String encrypt(byte[] bytes){
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA");
byte[] resultBytes = messageDigest.digest(bytes);
return Hex.encodeHexString(resultBytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
4、HMAC
HMAC(Hash Message Authentication Code,散列消息鉴别码,基于密钥的Hash算法的认证协议。
消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。
使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。
示例:
import java.security.MessageDigest;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class HMACUtil {
public static final String KEY_SHA = "SHA";
public static final String KEY_MD5 = "MD5";
/**
* MAC算法可选以下多种算法
*
* <pre>
* HmacMD5
* HmacSHA1
* HmacSHA256
* HmacSHA384
* HmacSHA512
* </pre>
*/
public static final String KEY_MAC = "HmacMD5";
/**
* BASE64解密
*
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
/**
* BASE64加密
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}
/**
* MD5加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptMD5(byte[] data) throws Exception {
MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
md5.update(data);
return md5.digest();
}
/**
* SHA加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptSHA(byte[] data) throws Exception {
MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
sha.update(data);
return sha.digest();
}
/**
* 初始化HMAC密钥
*
* @return
* @throws Exception
*/
public static String initMacKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
SecretKey secretKey = keyGenerator.generateKey();
return encryptBASE64(secretKey.getEncoded());
}
/**
* HMAC加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptHMAC(byte[] data, String key) throws Exception {
SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
return mac.doFinal(data);
}
}
5、AES算法
AES是高级加密标准,采用对称算法。
它的原理是:加密方和解密方保有同一个16位长的密钥,使用AES算法加解密时需要传入该密钥参数,通过Java实现AES算法提供的工具包加密后返回的是一个Base64格式的字节数组。
因此为保证密文“可读性”,需要在加密后对密文进行Base64编码,解密前进行Base64解码成密文。
AES算法的安全性,取决于密钥的安全性。因此一定不要在加解密的URL中传入该密钥参数,不然没有意义。一般的做法是:前后端协商好密钥,或者通过不对称加密的方式传递密钥。
AES加密算法作为新一代的数据加密标准汇聚了强安全性、高性能、高效率、易用和灵活等优点。
AES设计有三个密钥长度:128,192,256 位。
相对而言,AES的128密钥比DES的56密钥强了1021倍
示例:
public class AESUtil {
/**
* 用户自定义的密钥,由前后端协商确定
* (不要在加解密方法的参数中暴露),AES的安全性,取决于密钥的安全性。
*/
private static final String AES_KEY = "asdfghjkl";
/**
* AES:算法,ECB:模式,PKCS5Padding:补码方式
*/
private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
/**
* AES加密
* @param message 要加密的信息
* @return 加密后的字符串
*/
public static String encrypt(String message){
return doAES(message,AES_KEY,Cipher.ENCRYPT_MODE);
}
/**
* AES解密
* @param message 要解密的信息
* @return 解密后的字符串
*/
public static String decrypt(String message){
return doAES(message,AES_KEY,Cipher.DECRYPT_MODE);
}
/**
* 加密或解密的实际操作过程
* @param message 待处理的信息
* @param key AES加解密过程需要的密钥
* @param mode 加解密mode
* @return 加密或解密后的信息
*/
private static String doAES(String message,String key, int mode){
try {
if (StringUtils.isBlank(message) || StringUtils.isBlank(key)){
return null;
}
// 由于AES算法要求密钥的长度为16的倍数,(1,2,3,4)步的目的: 把用户自定义的密钥替换成16位的密钥
// 1. 构造密钥生成器,指定为AES算法
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
// 2. 根据用户自定义密钥对应的字节数组,生成一个128位的随机源(只能是128 or 192 or 256中的一个)
keyGenerator.init(128, new SecureRandom(key.getBytes()));
// 3. 生成AES算法的原始对称密钥
SecretKey secretKey = keyGenerator.generateKey();
// 4. 获取原始对称密钥的字节数组
byte[] enCodeFormat = secretKey.getEncoded();
// 5. 根据字节数组生成AES密钥
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat,"AES");
// 6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(mode, secretKeySpec);
if (mode == Cipher.ENCRYPT_MODE) {
byte[] content = message.getBytes();
byte[] result = cipher.doFinal(content);
// 加密,AES加密后的结果默认是Base64格式的字节数组
return Base64.encodeBase64String(result);
} else {
byte[] content = Base64.decodeBase64(message);
byte[] result = cipher.doFinal(content);
return new String(result);
}
} catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | NoSuchPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
}
6、RSA算法
RSA算法,是一种非常常用的不对称加密算法。不对称加密算法指的是:需要两个密钥来进行加密和解密,分别是公钥和私钥(公钥和私钥必须是一对)。
如果用公钥对数据进行加密,那么只有使用对应的私钥才能解密,反之亦然。由于加密和解密使用的是两个不同的密钥,因此,这种算法叫做非对称加密算法。
其算法具体实现基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
示例:
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
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.HashMap;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.apache.commons.codec.binary.Hex;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RSAUtil {
/**
* 生成公钥和私钥
*
* @return
*/
public static Map<String, Object> generateKey() {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
// initialize(512)表示生成的是128位字符
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, Object> keyPairs = new HashMap<>();
keyPairs.put("publicKey", rsaPublicKey);
keyPairs.put("privateKey", rsaPrivateKey);
return keyPairs;
} catch (NoSuchAlgorithmException e) {
log.error("", e);
}
return null;
}
/**
* RSA私钥加密
*
* @param message 要加密的信息
* @return 加密后的字符串
*/
public static String encrypt(String message, RSAPrivateKey rsaPrivateKey) {
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] resultBytes = cipher.doFinal(message.getBytes());
return Hex.encodeHexString(resultBytes);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
log.error("", e);
}
return null;
}
/**
* RSA公钥解密
*
* @param message 要解密的信息
* @return 解密后的字符串
*/
public static String decrypt(String message, RSAPublicKey rsaPublicKey) {
try {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] resultBytes = cipher.doFinal(Hex.decodeHex(message.toCharArray()));
return new String(resultBytes);
} catch (Exception e) {
log.error("", e);
}
return null;
}
}