Criptografía de la ruta de crecimiento del programador: explicación detallada del modo de cifrado de bloque cifrado y IV (compensación)

Cipher.getInstance("AES/ECB/PKCS5Padding");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Al realizar la programación de cifrado y descifrado, muchos amigos deberían haber estado expuestos a las oraciones anteriores, pero ¿ha entendido el significado y la diferencia de ECB/CBC y el significado de PKCS5Padding durante el proceso de codificación? Si no está seguro, entonces espero que este artículo pueda ayudarlo.

¿Qué es un cifrado de bloque?

Un cifrado de bloque es un algoritmo de cifrado y descifrado que cifra el texto sin formato/descifra el texto cifrado de acuerdo con los caracteres de longitud fija. Los comunes son DES, AES, etc. Específicamente como se muestra en la siguiente figura
inserte la descripción de la imagen aquí

¿Qué es IV (compensación)?

Puede entenderse como una variable variable establecida durante el proceso de cifrado y descifrado, que puede ser una marca de tiempo, un uuid u otros valores aleatorios, con el fin de aumentar la confusión y reducir el riesgo de ser descifrado.

¿Qué es el modo de cifrado de bloques?

Para el cifrado y descifrado general (o ECB), simplemente llamamos a la función de cifrado y descifrado de bloque de cifrado y se acabó, pero si se repite el ataque (la misma solicitud se inicia varias veces), entonces el hacker puede interceptar fácilmente el texto cifrado Descifrado de acuerdo con el grupo, entonces supondrá una amenaza para la seguridad del sistema en este momento. Para evitar que esto suceda, podemos tomar ciertas medidas para procesar iterativamente el cifrado de bloques.El ECB (modo de libro de códigos electrónico)/CBC (modo de encadenamiento de bloques de texto cifrado) mencionado anteriormente pertenece al modo de cifrado de bloques. Además, CFB (modo de retroalimentación de texto cifrado) también pertenece al modo de cifrado de bloques.
Analicemos el significado de los diferentes modos de cifrado de bloques y sus respectivas ventajas y desventajas uno por uno.

Proceso/principio ECB (modo de libro de códigos electrónico)
inserte la descripción de la imagen aquí
:
este modo de cifrado de bloque pertenece al modo de cifrado de bloque sin procesar más simple y cifra o descifra directamente el texto sin formato o el texto cifrado por grupo para obtener el resultado.

Ventajas:
-Admite procesamiento asíncrono de subprocesos múltiples, alta eficiencia
-No es necesario agrupar rondas de cálculos iterativos, y la cantidad de cálculos es menor

Desventajas:
- Desempeño de seguridad deficiente, fácil de violar


inserte la descripción de la imagen aquí
Proceso/principio CBC (modo de encadenamiento de bloques de texto cifrado)
: Cifrado:
primera ronda: generar el vector de inicialización iv y cifrar conjuntamente con el primer grupo de operaciones XOR de bloques de texto sin formato.
Seguimiento: tome el resultado de la última ronda de la operación CBC y la operación XOR de texto sin formato del bloque actual y luego cifre.
Descifrado:
la primera ronda: genere un vector de inicialización iv, primero descifre el primer grupo de bloques de texto cifrado y luego realice una operación XOR con el vector de inicialización.
Seguimiento: primero descifre el grupo de texto cifrado actual y luego realice la operación XOR con la ronda anterior del grupo de texto cifrado
Principio de cifrado y descifrado:
A XOR B XOR B = A
Por ejemplo, el primer grupo de texto cifrado e1 = Ek después de la primera ronda de cifrado (iv xor el primer grupo de texto sin formato)
luego el primer grupo de texto sin formato después de la primera ronda de descifrado d1 = (Ek'(primer grupo de texto cifrado) xor iv) = (iv xor primer grupo de texto sin formato) xor iv = primer grupo de texto plano

Ventajas:
-Usar iv como variable aleatoria aumenta la dificultad de descifrado, lo que hace que los resultados de cada encriptación del mismo texto sin formato sean inconsistentes
-La encriptación usa la salida de la ronda anterior como la entrada de la siguiente ronda

Desventajas:
-Aumenta la cantidad de cálculo y aumenta la sobrecarga de cálculo


inserte la descripción de la imagen aquí
Proceso/principio CFB (Modo de retroalimentación de texto cifrado)
: Cifrado:
Primera ronda: Generar el vector de inicialización iv, realizar el cálculo de cifrado primero y luego realizar la operación XOR con el primer grupo de grupos de texto sin formato.
Seguimiento: tome el resultado de la última ronda de la operación CFB y cifre primero, luego realice la operación XOR con el texto sin formato del grupo actual.
Descifrado:
la primera ronda: genere el vector de inicialización iv, realice primero el cálculo de cifrado y luego realice la operación XOR con el primer grupo de grupos de texto cifrado.
Seguimiento: tome el resultado de la última ronda de la operación CFB y cifre primero, luego realice la operación XOR con el texto cifrado del bloque actual.
Principio de cifrado y descifrado:
A XOR B XOR B = A
Por ejemplo, el primer grupo de texto cifrado e1 después de la primera ronda de cifrado = Ek(iv) xor el primer grupo de texto sin formato,
luego el primer grupo de texto sin formato después de la primera ronda de descifrado d1 = ( Ek(iv) xor primer conjunto de texto cifrado) = (Ek(iv) xor (Ek(iv) xor primer conjunto de texto sin formato) ) = primer conjunto de texto sin formato

Ventajas:
-Usar iv como variable aleatoria aumenta la dificultad de descifrar, lo que hace que los resultados sean inconsistentes para cada encriptación del mismo texto sin formato -La encriptación
usa la salida de la ronda anterior como la entrada de la siguiente ronda
-iv se puede usar solo en la uso del algoritmo de cifrado de bloques

Desventajas:
-Aumenta la cantidad de cálculo y aumenta la sobrecarga de cálculo

Implementación del código del modo de cifrado de bloques

Nota: si necesita el código AES, consulte el siguiente artículo para obtener más detalles:
Criptografía sobre el crecimiento de los programadores: explicación detallada del descifrado del algoritmo AES y presentación del código https://blog.csdn.net/qq_31236027/article/details/131206018

tipo enumerado

public enum EncryptMode {
    
    
	CBC("CBC"),
	CFB("CFB");
	
	private String name;
	
	private EncryptMode(String name) {
    
    
		this.setName(name);
	}

	public String getName() {
    
    
		return name;
	}

	public void setName(String name) {
    
    
		this.name = name;
	}
}

parte encriptada

/**
	 * 分组加密(128位一组)【完整版】
	 * @param text 明文
	 * @param mode 加密模式(CFB、CBC)
	 * @param iv 偏移量
	 */
	@Override
	public String encrypt(String text, String iv, EncryptMode mode) {
    
    
		if(mode == null) {
    
    
			return encrypt(text);
		}
		String result = null;
		switch(mode) {
    
    
			case CBC: result = encryptCBC(text,iv); break;
			case CFB: result = encryptCFB(text,iv);break;
			default: result = encrypt(text);break;
		}
		return result;
	}
	

	/**
	 * 分组加密(128位一组)(无iv【偏移量】版)
	 * @param text 明文
	 */
	private String encrypt(String text) {
    
    
		StringBuilder sb = new StringBuilder();
		int textLen = text.length();
		//获取分组长度
		// DIV_LEN * CHAR_LEN = 128
		// 根据DIV_LEN进行分组,如CHAR_LEN=16位,那么就每8个字符一组
		int divLen = textLen % AESConstant.DIV_LEN == 0 ? textLen / AESConstant.DIV_LEN : (textLen / AESConstant.DIV_LEN + 1);
		//分组加密处理
		for (int i = 0; i < divLen; i++) {
    
    
			int startIndex = i * AESConstant.DIV_LEN;
			int endIndex = (startIndex + AESConstant.DIV_LEN) >= textLen ? textLen : (startIndex + AESConstant.DIV_LEN);
			String substr = text.substring(startIndex, endIndex);
			//尾部填充
			while(substr.length() < AESConstant.DIV_LEN) {
    
    
				substr += " ";
			}
			sb.append(EncodeUtil.binaryToHexStr(baseEncrypt(substr)).trim());
		}
		return new BASE64Encoder().encode(sb.toString().trim().getBytes());
	}
	
	/**
	 * 分组加密(128位一组),(有iv【偏移量】CBC版,更安全)
	 * 
	 * CBC特性
	 * 1. 每一组分组的密文都依赖于上一组的结果
	 * 2. 加入了iv偏移量使得每次加密执行后的结果都不一致
	 * 
	 * @param text 明文
	 * @param iv 偏移量
	 */
	private String encryptCBC(String text,String iv) {
    
    
		StringBuilder sb = new StringBuilder();
		int textLen = text.length();
		//获取分组长度
		// DIV_LEN * CHAR_LEN = 128
		// 根据DIV_LEN进行分组,如CHAR_LEN=16位【UNICODE】,那么就每8个字符一组
		int divLen = textLen % AESConstant.DIV_LEN == 0 ? textLen / AESConstant.DIV_LEN : (textLen / AESConstant.DIV_LEN + 1);
		// CFB加密初始化向量
		String encryptedPart = iv;
		//分组加密处理
		for (int i = 0; i < divLen; i++) {
    
    
			int startIndex = i * AESConstant.DIV_LEN;
			int endIndex = (startIndex + AESConstant.DIV_LEN) >= textLen ? textLen : (startIndex + AESConstant.DIV_LEN);
			String substr = text.substring(startIndex, endIndex);
			//尾部填充
			while(substr.length() < AESConstant.DIV_LEN) {
    
    
				substr += " ";
			}
			while(encryptedPart.length() < AESConstant.DIV_LEN) {
    
    
				encryptedPart += " ";
			}
			//CBC关键,需要拿明文与上一轮结果进行异或得到的结果共同加密作为下一轮的输入
			encryptedPart = EncodeUtil.binaryToStr(
					baseEncrypt(strXor(encryptedPart,substr)), 16
			);
			sb.append(encryptedPart);
		}
		//批量处理为16进制后base64运算
		String result = sb.toString().trim();
		result = EncodeUtil.strtoBinary(result, 16);
		result = EncodeUtil.binaryToHexStr(result);
		return new BASE64Encoder().encode(result.getBytes());
	}
	
	/**
	 * 分组加密(128位一组),(有iv【偏移量】CFB版,更安全)
	 * 
	 * CFB特性
	 * 
	 * @param text 明文
	 * @param iv 偏移量
	 */
	private String encryptCFB(String text,String iv) {
    
    
		StringBuilder sb = new StringBuilder();
		int textLen = text.length();
		//获取分组长度
		// DIV_LEN * CHAR_LEN = 128
		// 根据DIV_LEN进行分组,如CHAR_LEN=16位【UNICODE】,那么就每8个字符一组
		int divLen = textLen % AESConstant.DIV_LEN == 0 ? textLen / AESConstant.DIV_LEN : (textLen / AESConstant.DIV_LEN + 1);
		// CFB加密初始化向量
		String encryptedPart = iv;
		//分组加密处理
		for (int i = 0; i < divLen; i++) {
    
    
			int startIndex = i * AESConstant.DIV_LEN;
			int endIndex = (startIndex + AESConstant.DIV_LEN) >= textLen ? textLen : (startIndex + AESConstant.DIV_LEN);
			String substr = text.substring(startIndex, endIndex);
			//尾部填充
			while(substr.length() < AESConstant.DIV_LEN) {
    
    
				substr += " ";
			}
			while(encryptedPart.length() < AESConstant.DIV_LEN) {
    
    
				encryptedPart += " ";
			}
			//CFB关键,需要拿明文与上一轮加密结果进行异或得到的结果作为下一轮的输入
			encryptedPart = strXor(EncodeUtil.binaryToStr(
					baseEncrypt(encryptedPart), 16
			),substr);
			sb.append(encryptedPart);
		}
		//批量处理为16进制后base64运算
		String result = sb.toString().trim();
		result = EncodeUtil.strtoBinary(result, 16);
		result = EncodeUtil.binaryToHexStr(result);
		return new BASE64Encoder().encode(result.getBytes());
	}

parte de descifrado

	/**
	 * 分组解密(128位一组)【完整版】
	 * @param encrytedText 密文
	 * @param mode 加密模式(CFB、CBC)
	 * @param iv 偏移量
	 */
	@Override
	public String decrypt(String encrytedText, String iv, EncryptMode mode) {
    
    
		if(mode == null) {
    
    
			return decrypt(encrytedText);
		}
		String result = null;
		switch(mode) {
    
    
			case CBC: result = decryptCBC(encrytedText,iv); break;
			case CFB: result = decryptCFB(encrytedText,iv);break;
			default: result = decrypt(encrytedText);break;
		}
		return result;
	}


	/**
	 * 分组解密
	 * @param encrytedText 密文
	 */
	private String decrypt(String encrytedText) {
    
    
		try {
    
    
			//base64解码
			byte[] bytes = new BASE64Decoder().decodeBuffer(encrytedText);
			String str = new String(bytes,Charset.forName("UTF8"));
			int textLen = str.length();
			StringBuilder sb = new StringBuilder();
			int divLen = textLen < 32 ? 1 : (int)(Math.ceil(textLen/(4*8*1.0))); //因为加密后会自动填充所以长度必为字符长度的倍数(HEX 4位)
			//分组解密
			for (int i = 0; i< divLen; i++) {
    
    
				int startIndex = i * (4*8);
				int endIndex = (startIndex + (4*8));
				String temp = str.substring(startIndex, endIndex);
				sb.append(baseDecrypt(temp));
			}
			return sb.toString();
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 分组解密(128位一组),(有iv【偏移量】CBC版)
	 * @param encrytedText 密文
	 * @param iv 偏移量
	 * @return 明文
	 */
	private String decryptCBC(String encrytedText,String iv) {
    
    
		try {
    
    
			//base64解码
			byte[] bytes = new BASE64Decoder().decodeBuffer(encrytedText);
			String str = new String(bytes,Charset.forName("UTF8"));
			int textLen = str.length();
			StringBuilder sb = new StringBuilder();
			int divLen = textLen < 32 ? 1 : (int)(Math.ceil(textLen/(4*8*1.0))); //因为加密后会自动填充所以长度必为字符长度的倍数(HEX 4位)
			//CFB解密初始化向量
			String decryptedPart = iv;
			//分组解密
			for (int i = 0; i< divLen; i++) {
    
    
				int startIndex = i * (4*8);
				int endIndex = (startIndex + (4*8));
				String temp = str.substring(startIndex, endIndex);
				//尾部填充
				while(decryptedPart.length() < AESConstant.DIV_LEN) {
    
    
					decryptedPart += " ";
				}
				//转换成16位的字符,方便strXor运算
				sb.append(strXor(baseDecrypt(temp),decryptedPart));
				//位数转换
				decryptedPart = EncodeUtil.binaryToStr(EncodeUtil.toBinary(temp, EncodeRadix.HEX), 16);
			}
			return sb.toString();
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 分组解密(128位一组),(有iv【偏移量】CFB版)
	 * @param encrytedText 密文
	 * @param iv 偏移量
	 * @return 明文
	 */
	private String decryptCFB(String encrytedText,String iv) {
    
    
		try {
    
    
			//base64解码
			byte[] bytes = new BASE64Decoder().decodeBuffer(encrytedText);
			String str = new String(bytes,Charset.forName("UTF8"));
			int textLen = str.length();
			StringBuilder sb = new StringBuilder();
			int divLen = textLen < 32 ? 1 : (int)(Math.ceil(textLen/(4*8*1.0))); //因为加密后会自动填充所以长度必为字符长度的倍数(HEX 4位)
			//CFB解密初始化向量(转为16进制方便计算
			String decryptedPart = iv;
			//分组解密
			for (int i = 0; i< divLen; i++) {
    
    
				int startIndex = i * (4*8);
				int endIndex = (startIndex + (4*8));
				String temp = str.substring(startIndex, endIndex);
				//转换成16位的字符,方便strXor运算
				temp = EncodeUtil.binaryToStr(EncodeUtil.toBinary(temp, EncodeRadix.HEX), 16);
				//尾部填充
				while(decryptedPart.length() < AESConstant.DIV_LEN) {
    
    
					decryptedPart += " ";
				}
				//转换成16位的字符,方便strXor运算
				sb.append(
					strXor(EncodeUtil.binaryToStr(baseEncrypt(decryptedPart), 16),temp)
				);
				decryptedPart = temp;
			}
			return sb.toString();
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
		return null;
	}

Ejecutar código

public static void main(String[] args) {
    
    
		AesUtil util = new AesUtil();
		//偏移量(8个字符,每个字符16位)
		String iv = UUID.randomUUID().toString().substring(0,8);
		//CFB(密文反馈模式)
		String encrytedStr = util.encrypt(
				"{\"code\":200,\"message\":\"成功!\",\"data\":{\"id\":\"2103813902831\",\"name\":\"章鱼哥是我啊\",\"gender\":\"男\"}}"
				,iv
				,EncryptMode.CFB
		);
		System.out.println("encrytedStr = " + encrytedStr);
		System.out.println("result= " + util.decrypt(encrytedStr,iv,EncryptMode.CFB));
	}

Finalmente, ejecute la captura de pantalla
inserte la descripción de la imagen aquí
———————————————PKCS5Padding se discutirá más adelante—————————————————————

Supongo que te gusta

Origin blog.csdn.net/qq_31236027/article/details/132224276
Recomendado
Clasificación