下面所有代碼都是基於
jdk8
所有基於Apache的實現都需要引入下面這個包:
<!-- apache.codec:編碼方法的工具類包 -->
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
一、編碼格式 (這個按道理來講是算不上加密的,但是比較常用)
二、消息摘要算法 (這個按道理來講是算不上加密的,但是比較常用)
基於上面的 編碼格式算法
和 消息摘要算法
寫了一個基礎加密工具,對稱加密
和 非對稱加密
有單獨的工具類,往下看就知道了。
package com.blog.www.util.coder.base;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.springframework.util.Base64Utils;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
import java.util.Random;
/**
* 基礎編碼工具類
* <br/>
* 包含以下幾種加密:
* <ul>
* <li>Base64</li>
* <li>URLEncoder、URLDecoder</li>
* <li>MD5</li>
* <li>MD5加隨機鹽</li>
* <li>SHA</li>
* <li>MAC</li>
* </ul>
* <p>
* 注意: <br>
* Base64加密可逆,一般用來編碼信息發送,甚至可以把圖片轉換成字符串發送到前端顯示。注意不要用來發送機密信息! <br>
* MD5、SHA、MAC這三種加密算法,是不可逆加密,我們通常只把他們作爲加密的基礎。單純的以上三種的加密並不可靠。
* <p>
* 創建人:leigq <br>
* 創建時間:2017年10月23日 下午10:39:06 <br>
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class BaseCoderUtils {
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ BASE64 編碼、解碼 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
/**
* BASE64加密
* <br/>
*
* @param binaryData 待加密二進制數據
* @return 加密後字符串
*/
public static String encryptBase64(final byte[] binaryData) {
// 安卓自帶實現,因爲安卓用不了Apache的實現,所以只能用自帶的實現。注意,Base64.CRLF纔對應Apache的默認模式!
// return Base64.encodeToString(key, Base64.CRLF);
return Base64Utils.encodeToString(binaryData);
}
/**
* BASE64解密
* <br/>
*
* @param base64String 加密後字符串
* @return 原始二進制數據
*/
public static byte[] decryptBase64(final String base64String) {
// 安卓自帶實現
// return Base64.decode(key, Base64.CRLF);
return Base64Utils.decodeFromString(base64String);
}
/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ BASE64 編碼、解碼 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ URL 編碼、解碼 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
/**
* 將 URL 編碼
*/
public static String encodeURL(final String data) {
String target;
try {
target = URLEncoder.encode(data, StandardCharsets.UTF_8.name());
} catch (Exception e) {
log.error("編碼出錯!", e);
throw new RuntimeException(e);
}
return target;
}
/**
* 將 URL 解碼
*/
public static String decodeURL(final String data) {
String target;
try {
target = URLDecoder.decode(data, StandardCharsets.UTF_8.name());
} catch (Exception e) {
log.error("解碼出錯!", e);
throw new RuntimeException(e);
}
return target;
}
/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ URL 編碼、解碼 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ MD5加密相關 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
/**
* MD5加密
*
* @param data
* @return 大寫
*/
public static String encryptMD5(final byte[] data) {
return DigestUtils.md5Hex(data).toUpperCase();
}
/**
* MD5加密,字符串到字符串
*
* @param data
* @return 大寫
*/
public static String encryptMD5(final String data) {
return encryptMD5(data.getBytes());
}
/**
* 3次MD5加密,字符串到字符串
*
* @param data
* @return 大寫
*/
public static String encryptTriMD5(final String data) {
int count = 3;
String md5 = data;
for (int i = 0; i < count; i++) {
md5 = encryptMD5(md5);
}
return md5;
}
/**
* 生成含有隨機鹽的加密字符串
*
* @param data 待加密的字符
* @return 加密後的字符(含 16 位隨機鹽),大寫
*/
public static String encryptMD5WithRandomSalt(final String data) {
return encryptMd5WithRandomSalt(data);
}
/**
* 校驗密碼是否正確
*
* @param data 待驗證的字符(明文)
* @param md5StringWithSalt 加密後的字符(含 16 位隨機鹽)
* @return 驗證結果
*/
public static boolean verifyMD5WithRandomSalt(final String data, final String md5StringWithSalt) {
return verifyMd5WithRandomSalt(data, md5StringWithSalt);
}
/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ MD5加密相關 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ SHA 加密 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
/**
* 默認使用 SHA-1 20字節 160位
* 其他還有:
* SHA-224 32字節 224位
* SHA-256 32字節 256位
* SHA-384 48字節 384位
* SHA-512 64字節 512位
* 由於它產生的數據摘要的長度更長,因此更難以發生碰撞,因此較之MD5更爲安全,它是未來數據摘要算法的發展方向。
* 由於SHA系列算法的數據摘要長度較長,因此其運算速度與MD5相比,也相對較慢。
*/
/**
* SHA 加密
*
* @param data
* @return
*/
public static String encryptSHA(final byte[] data) {
return DigestUtils.sha1Hex(data).toUpperCase();
}
/**
* SHA 加密,字符串到字符串 <br>
* <br>
*
* @param data
* @return
*/
public static String encryptSHA(final String data) {
return DigestUtils.sha1Hex(data).toUpperCase();
}
/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ SHA 加密 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ MAC加密相關 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
/**
* 默認使用 HmacMD5 加密。
* 其他還有:
* HmacSHA1
* HmacSHA256
* HmacSHA384
* HmacSHA512
*/
/**
* 初始化 MAC 密鑰
*
* @return MAC 密鑰
* @throws NoSuchAlgorithmException
*/
public static String initMacKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(HmacAlgorithms.HMAC_MD5.getName());
SecretKey secretKey = keyGenerator.generateKey();
return encryptBase64(secretKey.getEncoded());
}
/**
* MAC 加密
*
* @param data 待加密數據
* @param key 密鑰,可用 initMacKey() 方法生成,也可自定義
* @return 加密後數據
*/
public static String encryptHMAC(final byte[] data, final String key) {
HmacUtils hmacMd5 = new HmacUtils(HmacAlgorithms.HMAC_MD5, key);
return hmacMd5.hmacHex(data).toUpperCase();
}
/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ MAC加密相關 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
/**
* 生成含有隨機鹽的加密字符串
*
* @param data 待加密的字符
* @return 加密後的字符(含 16 位隨機鹽),大寫
*/
private static String encryptMd5WithRandomSalt(String data) {
Random r = new Random();
StringBuilder sb = new StringBuilder(16);
sb.append(r.nextInt(99999999))
.append(r.nextInt(99999999));
int len = sb.length();
if (len < 16) {
for (int i = 0; i < 16 - len; i++) {
sb.append("0");
}
}
String salt = sb.toString();
String md5WithSaltStr = DigestUtils.md5Hex(data + salt);
char[] cs = new char[48];
for (int i = 0; i < 48; i += 3) {
cs[i] = md5WithSaltStr.charAt(i / 3 * 2);
char c = salt.charAt(i / 3);
cs[i + 1] = c;
cs[i + 2] = md5WithSaltStr.charAt(i / 3 * 2 + 1);
}
return new String(cs).toUpperCase();
}
/**
* 校驗密碼是否正確
*
* @param data 待驗證的字符(明文)
* @param md5StrContainRandomSalt 加密後的字符(含 16 位隨機鹽)
* @return 驗證結果
*/
private static boolean verifyMd5WithRandomSalt(String data, String md5StrContainRandomSalt) {
// 32 位加密字符(不含鹽)
char[] cs1 = new char[32];
// 16 位的隨機鹽
char[] cs2 = new char[16];
for (int i = 0; i < 48; i += 3) {
cs1[i / 3 * 2] = md5StrContainRandomSalt.charAt(i);
cs1[i / 3 * 2 + 1] = md5StrContainRandomSalt.charAt(i + 2);
cs2[i / 3] = md5StrContainRandomSalt.charAt(i + 1);
}
String salt = new String(cs2);
return Objects.equals(DigestUtils.md5Hex(data + salt).toUpperCase(), new String(cs1));
}
}
三、對稱加密
四、非對稱加密
先來看看這兩篇文章,幫助理解 非對稱加密
、數字簽名
,寫得挺好的。