常用的對稱加密算法


需要對加密和解密使用相同密鑰的加密算法。由於其速度快,對稱性加密通常在消息發送方需要加密大量數據時使用。對稱性加密也稱爲密鑰加密。

所謂對稱,就是採用這種加密方法的雙方使用方式用同樣的密鑰進行加密和解密。密鑰是控制加密及解密過程的指令。算法是一組規則,規定如何進行加密和解密。

因此加密的安全性不僅取決於加密算法本身,密鑰管理的安全性更是重要。因爲加密和解密都使用同一個密鑰,如何把密鑰安全地傳遞到解密者手上就成了必須要解決的問題。

在對稱加密算法中常用的算法有:DES、3DES、TDEA、Blowfish、RC2、RC4、RC5、IDEA、SKIPJACK等。

下面列舉幾個比較常用的:

1、DES(Data Encryption Standard)

DES加密算法出自IBM的研究,後來被美國政府正式採用,之後開始廣泛流傳,但是近些年使用越來越少,因爲DES使用56位(8字節)密鑰,以現代計算能力,
24小時內即可被破解。雖然如此,在某些簡單應用中,我們還是可以使用DES加密算法

特點:數據加密標準,速度較快,適用於加密大量數據的場合

提供一個 DES 加密工具類:

package com.blog.www.util.coder;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

/**
 * DES 加密工具
 * <br/>
 * 參考:<a href='https://blog.csdn.net/bbaiggey/article/details/79414646'>java加解密之DES多種使用方式</a>
 * <br/>
 * DES加密介紹
 * <br/>
 * DES是一種對稱加密算法,所謂對稱加密算法即:加密和解密使用相同密鑰的算法。DES加密算法出自IBM的研究,
 * 後來被美國政府正式採用,之後開始廣泛流傳,但是近些年使用越來越少,因爲DES使用56位密鑰,以現代計算能力,
 * 24小時內即可被破解。雖然如此,在某些簡單應用中,我們還是可以使用DES加密算法,本文簡單講解DES的JAVA實現
 * 。
 * 注意:DES加密和解密過程中,密鑰長度都必須是8的倍數
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DESCoder {

	/**
	 * Cipher加密器初始化需要一個字符串,字符串裏提供了三種設置。
	 * 一是,加解密算法;二是,加解密模式;三是,是否需要填充。
	 * <br/>
	 * ECB(電碼本模式),CBC(加密塊鏈模式),OFB(輸出反饋模式),CFB(加密反饋模式)
	 */
	private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding";

	private static final String ALGORITHM = "DES";


	/**
	 * 生成密鑰
	 *
	 * @param seed 偶數種子
	 */
	public static String initDesKey(String seed) throws DecoderException, NoSuchAlgorithmException {
		SecureRandom secureRandom;
		if (StringUtils.isNotBlank(seed)) {
			secureRandom = new SecureRandom(Hex.decodeHex(seed));
		} else {
			secureRandom = new SecureRandom();
		}
		// init key生成器
		KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
		kg.init(secureRandom);
		// 生成一個Key
		SecretKey secretKey = kg.generateKey();
		// 轉變爲字節數組
		byte[] encoded = secretKey.getEncoded();
		// 生成密鑰字符串
		return Hex.encodeHexString(encoded);
	}


	/**
	 * 加密
	 *
	 * @param data 原始數據
	 * @param key  DES密鑰 可使用 initDesKey() 方法獲取,也可自定義(密鑰長度都必須是8的倍數)
	 * @return 加密後數據
	 */
	public static String encrypt(@NonNull final String data, @NonNull String key) throws InvalidKeyException,
			InvalidKeySpecException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
			NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
		// 創建一個DESKeySpec對象
		DESKeySpec desKeySpec = createDesKeySpec(key);
		Cipher cipher = getCipher(desKeySpec, Cipher.ENCRYPT_MODE);
		// 現在,獲取數據並加密
		return Hex.encodeHexString(cipher.doFinal(data.getBytes())).toUpperCase();
	}


	/**
	 * 解密
	 *
	 * @param data 待解密內容
	 * @param key  DES密鑰 可使用 initDesKey() 方法獲取,也可自定義(密鑰長度都必須是8的倍數)
	 * @return 原始數據
	 */
	public static String decrypt(@NonNull final String data, @NonNull String key) throws InvalidKeyException,
			InvalidKeySpecException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
			NoSuchPaddingException, DecoderException, BadPaddingException, IllegalBlockSizeException {
		// 創建一個DESKeySpec對象
		DESKeySpec desKeySpec = createDesKeySpec(key);
		Cipher cipher = getCipher(desKeySpec, Cipher.DECRYPT_MODE);
		// 解密操作
		return new String(cipher.doFinal(Hex.decodeHex(data)));
	}


	private static DESKeySpec createDesKeySpec(String key) throws InvalidKeyException {
		// 創建一個DESKeySpec對象
		return new DESKeySpec(key.getBytes());
	}

	private static Cipher getCipher(DESKeySpec desKeySpec, Integer mode) throws NoSuchAlgorithmException, InvalidKeySpecException,
			NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
		// 創建一個密匙工廠
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
		// 將DESKeySpec對象轉換成SecretKey對象
		SecretKey secureKey = keyFactory.generateSecret(desKeySpec);
		// Cipher對象實際完成解密操作
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		// 用密匙初始化Cipher對象
		cipher.init(mode, secureKey, new IvParameterSpec(desKeySpec.getKey()));
		return cipher;
	}
}

使用測試:

/**
 * DES 加密測試
 */
class DesTest {
	public static void main(String[] args) {
		try {
			// 待加密字符
			String originalStr = "七里香";
			System.out.println(String.format("待加密字符: %s", originalStr));
			String desKey = DESCoder.initDesKey("12345678");
			System.out.println(String.format("密鑰:%s", desKey));
			String encrypt = DESCoder.encrypt(originalStr, desKey);
			System.out.println(String.format("%s 加密結果:%s", originalStr, encrypt));
			System.out.println(String.format("%s 解密結果:%s", originalStr, DESCoder.decrypt(encrypt, desKey)));
		} catch (DecoderException | NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException
				| InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) {
			e.printStackTrace();
		}
	}
}

測試結果:

20191026102555.png

2、3DES(Triple DES)

基於DES,對一塊數據用三個不同的密鑰進行三次加密,強度更高

3DES是三重數據加密,且可以逆推的一種算法方案。但由於3DES的算法是公開的,所以算法本身沒有密鑰可言,主要依靠唯一密鑰來確保數據加解密的安全。到目前爲止,仍沒有人能破解3DES。

3DES 加密工具類:

package com.blog.www.util.coder;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.DESedeKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

/**
 * 3DES 加密工具
 * <br/>
 * 參考:<a href='https://www.cnblogs.com/shindo/p/6021976.html'>3DES加密算法</a>
 * <br/>
 * 3DES加密介紹
 * <br/>
 * 3DES是三重數據加密,且可以逆推的一種算法方案。
 * 但由於3DES的算法是公開的,所以算法本身沒有密鑰可言,
 * 主要依靠唯一密鑰來確保數據加解密的安全。到目前爲止,仍沒有人能破解3DES。
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DES3Coder {

	/**
	 * Cipher加密器初始化需要一個字符串,字符串裏提供了三種設置。
	 * 一是,加解密算法;二是,加解密模式;三是,是否需要填充。
	 * <br/>
	 * ECB(電碼本模式),CBC(加密塊鏈模式),OFB(輸出反饋模式),CFB(加密反饋模式)
	 */
	private static final String CIPHER_ALGORITHM = "DESede/ECB/PKCS5Padding";

	private static final String ALGORITHM = "DESede";


	/**
	 * 生成密鑰
	 *
	 * @param seed 偶數種子
	 */
	public static String initKey(String seed) throws DecoderException, NoSuchAlgorithmException {
		SecureRandom secureRandom;
		if (StringUtils.isNotBlank(seed)) {
			secureRandom = new SecureRandom(Hex.decodeHex(seed));
		} else {
			secureRandom = new SecureRandom();
		}
		// init key生成器
		KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
		kg.init(secureRandom);
		// 生成一個Key
		SecretKey secretKey = kg.generateKey();
		// 轉變爲字節數組
		byte[] encoded = secretKey.getEncoded();
		// 生成密鑰字符串
		return Hex.encodeHexString(encoded);
	}


	/**
	 * 加密
	 *
	 * @param data 原始數據
	 * @param key  3DES密鑰對象 使用 initKey() 方法獲取,也可自定義,key長度必須是大於等於 3*8 = 24 位
	 * @return 加密後數據
	 */
	public static String encrypt(@NonNull final String data, @NonNull String key) throws InvalidKeyException,
			InvalidKeySpecException, NoSuchAlgorithmException,
			NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
		// 創建一個DESKeySpec對象
		DESedeKeySpec deSedeKeySpec = createDesKeySpec(key);
		Cipher cipher = getCipher(deSedeKeySpec, Cipher.ENCRYPT_MODE);
		// 現在,獲取數據並加密
		return Hex.encodeHexString(cipher.doFinal(data.getBytes())).toUpperCase();
	}


	/**
	 * 解密
	 *
	 * @param data 待解密內容
	 * @param key  3DES密鑰對象 可使用 initKey() 方法獲取,也可自定義,key長度必須是大於等於 3*8 = 24 位
	 * @return 原始數據
	 */
	public static String decrypt(@NonNull final String data, @NonNull String key) throws InvalidKeyException,
			InvalidKeySpecException, NoSuchAlgorithmException,
			NoSuchPaddingException, DecoderException, BadPaddingException, IllegalBlockSizeException {
		// 創建一個DESKeySpec對象
		DESedeKeySpec deSedeKeySpec = createDesKeySpec(key);
		Cipher cipher = getCipher(deSedeKeySpec, Cipher.DECRYPT_MODE);
		// 解密操作
		return new String(cipher.doFinal(Hex.decodeHex(data)));
	}


	/**
	 * @param key 3DES 加密, key必須是長度大於等於 3*8 = 24 位
	 * @return {@link DESedeKeySpec}
	 * @throws InvalidKeyException invalidKeyException
	 */
	private static DESedeKeySpec createDesKeySpec(String key) throws InvalidKeyException {
		// 創建一個 DESedeKeySpec 對象
		return new DESedeKeySpec(key.getBytes());
	}

	private static Cipher getCipher(DESedeKeySpec deSedeKeySpec, Integer mode) throws NoSuchAlgorithmException, InvalidKeySpecException,
			NoSuchPaddingException, InvalidKeyException {
		// 創建一個密匙工廠
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
		// 將 DESedeKeySpec 對象轉換成 SecretKey 對象
		SecretKey secureKey = keyFactory.generateSecret(deSedeKeySpec);
		// Cipher對象實際完成解密操作
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		// 用密匙初始化 Cipher對象
		cipher.init(mode, secureKey);
		return cipher;
	}

}

3DES測試:

/**
 * 3DES 加密測試
 */
class Des3Test {
	public static void main(String[] args) {
		try {
			// 待加密字符
			String originalStr = "發如雪";
			System.out.println(String.format("待加密字符: %s", originalStr));
			String des3Key = DES3Coder.initKey("4545454545AAAA");
			System.out.println(String.format("密鑰:%s", des3Key));
			String encrypt = DES3Coder.encrypt(originalStr, des3Key);
			System.out.println(String.format("%s 加密結果:%s", originalStr, encrypt));
			System.out.println(String.format("%s 解密結果:%s", originalStr, DES3Coder.decrypt(encrypt, des3Key)));
		} catch (DecoderException | NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException
				| NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) {
			e.printStackTrace();
		}
	}
}

測試結果:

20191026105618.png

3、AES(Advanced Encryption Standard)推薦使用

密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。

這個標準用來替代原先的DES(Data Encryption Standard),已經被多方分析且廣爲全世界所使用。經過五年的甄選流程,高級加密標準由美國國家標準與技術研究院 (NIST)於2001年11月26日發佈於FIPS PUB 197,並在2002年5月26日成爲有效的標準。2006年,高級加密標準已然成爲對稱密鑰加密中最流行的算法之一 [1] 。
該算法爲比利時密碼學家Joan Daemen和Vincent Rijmen所設計,結合兩位作者的名字,以Rijdael之名命之,投稿高級加密標準的甄選流程。(Rijdael的發音近於 “Rhine doll”。)

高級加密標準,是下一代的加密算法標準,速度快,安全級別高,支持128、192、256、512位密鑰的加密。

AES 加密工具類

package com.blog.www.util.coder.symmetry;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * AES加密
 * <br/>
 * 介紹:
 * <br/>
 * 密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。
 * <br/>
 * 這個標準用來替代原先的DES(Data Encryption Standard),已經被多方分析且廣爲全世界所使用。
 * 經過五年的甄選流程,高級加密標準由美國國家標準與技術研究院 (NIST)於2001年11月26日發佈於FIPS PUB 197,
 * 並在2002年5月26日成爲有效的標準。2006年,高級加密標準已然成爲對稱密鑰加密中最流行的算法之一
 * <br/>
 * 參考:
 * <ul>
 *     <li>
 *         <a href='【JAVA】AES加密 簡單實現 AES-128/ECB/PKCS5Padding'>https://segmentfault.com/a/1190000015943620</a>
 *     </li>
 *     <li>
 *         <a href='Java加密技術(二)——對稱加密算法DES&AES'>https://www.iteye.com/blog/snowolf-380034</a>
 *     </li>
 * </ul>
 * <p>
 * <br/>
 *
 * @author :leigq
 * @date :2019/8/8 17:20
 */
@Slf4j
public class AESCoder {

	/**
	 * Cipher加密器初始化需要一個字符串,字符串裏提供了三種設置。
	 * 一是,加解密算法;二是,加解密模式;三是,是否需要填充。
	 * <br/>
	 * ECB(電碼本模式),CBC(加密塊鏈模式),OFB(輸出反饋模式),CFB(加密反饋模式)
	 */
	private static final String CIPHER_MODE = "AES/ECB/PKCS5Padding";

	private static final String ALGORITHM = "AES";

	/**
	 * 生成密鑰
	 */
	public static String initAesKey(String seed) throws NoSuchAlgorithmException, DecoderException {
		SecureRandom secureRandom;
		if (StringUtils.isNotBlank(seed)) {
			secureRandom = new SecureRandom(Hex.decodeHex(seed));
		} else {
			secureRandom = new SecureRandom();
		}
		// init key生成器
		KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
		// 要生成多少位,只需要修改這裏即可 128, 192 或 256,單位 bit
		kg.init(128, secureRandom);
		// 生成一個Key
		SecretKey secretKey = kg.generateKey();
		// 轉變爲字節數組
		byte[] encoded = secretKey.getEncoded();
		// 生成密鑰字符串
		return Hex.encodeHexString(encoded);
	}

	/**
	 * AES加密
	 *
	 * @param data 待加密的數據
	 * @param key  密鑰
	 * @return 加密後的數據
	 */
	public static String encrypt(String data, String key) {
		try {
			Cipher cipher = getCipher(key, Cipher.ENCRYPT_MODE);
			return Hex.encodeHexString(cipher.doFinal(data.getBytes())).toUpperCase();
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | DecoderException | IllegalBlockSizeException | BadPaddingException e) {
			log.error("加密異常:", e);
			return null;
		}
	}


	/**
	 * AES解密
	 *
	 * @param data 待解密的數據
	 * @param key  密鑰
	 * @return 解密後的數據
	 */
	public static String decrypt(String data, String key) {
		try {
			Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);
			return new String(cipher.doFinal(Hex.decodeHex(data)));
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | DecoderException | IllegalBlockSizeException | BadPaddingException e) {
			log.error("解密異常:", e);
			return null;
		}
	}

	private static Cipher getCipher(String key, Integer mode) throws NoSuchAlgorithmException,
			NoSuchPaddingException, InvalidKeyException, DecoderException {
		// 創建密匙
		SecretKey secretKey = new SecretKeySpec(Hex.decodeHex(key), ALGORITHM);
		// Cipher 對象實際完成解密操作
		Cipher cipher = Cipher.getInstance(CIPHER_MODE);
		// 用密匙初始化 Cipher 對象
		cipher.init(mode, secretKey);
		return cipher;
	}

}

使用測試:

/**
 * AES 加密測試
 */
class AesTest {
	public static void main(String[] args) {
		try {
			// 待加密字符
			String originalStr = "美女,約嗎?";
			System.out.println(String.format("待加密字符: %s", originalStr));
			String aesKey = AESCoder.initAesKey(null);
			System.out.println(String.format("密鑰:%s", aesKey));
			String encrypt = AESCoder.encrypt(originalStr, aesKey);
			System.out.println(String.format("%s 加密結果:%s", originalStr, encrypt));
			System.out.println(String.format("%s 解密結果:%s", originalStr, AESCoder.decrypt(encrypt, aesKey)));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

測試結果:

20191026152340.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章