操作场景
使用 DES、AES 加密算法可以对请求参数进行加密,并对其响应数据解密,防止明文请求在传输过程中被恶意篡改。本文将指导您如何使用 DES、AES 加密算法。
说明:
使用 HTTPS 请求方式查询,传输的数据会因为 TLS 通道而被加密保护,因此不需要主动对传入的数据额外加密。
前提条件
掌握 Java、JavaScript、Go 或者 C++ 语言。
操作步骤
Java 版本
AES 加密
首先,创建
org/example
目录,并在org/example
目录下,创建文件HTTPDNSAESHelper.java
,执行javac ./org/example/HTTPDNSAESHelper.java
命令,得到HTTPDNSAESHelper$1.class
、HTTPDNSAESHelper.class
文件。
执行
java org.example.HTTPDNSAESHelper
命令。依次输入 HTTPDNS 的 id、查询的域名、16位的 AES 加解密的密钥。
HTTPDNSAESHelper.java
代码package org.example;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.HashMap;import java.util.Map;import java.util.Random;import java.util.Scanner;public class HTTPDNSAESHelper {private static final int IVSize = 16;private static final int KeySize = 16;private static final String AlgorithmName = "AES";// 下面代码中有对应的pkcs7算法实现private static final String AlgorithmCombinationName = "AES/CBC/NoPadding";private static final String Base16Table = "0123456789ABCDEF";private static final Map<Character, Byte> ReversedBase16Table = new HashMap<Character, Byte>() {{put('0', (byte) 0);put('1', (byte) 1);put('2', (byte) 2);put('3', (byte) 3);put('4', (byte) 4);put('5', (byte) 5);put('6', (byte) 6);put('7', (byte) 7);put('8', (byte) 8);put('9', (byte) 9);put('A', (byte) 10);put('B', (byte) 11);put('C', (byte) 12);put('D', (byte) 13);put('E', (byte) 14);put('F', (byte) 15);put('a', (byte) 10);put('b', (byte) 11);put('c', (byte) 12);put('d', (byte) 13);put('e', (byte) 14);put('f', (byte) 15);}};public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.println("请输入你的id:");String id = scanner.nextLine();System.out.println("请输入你要查询的域名:");String data = scanner.nextLine();System.out.println("请输入你的key:");// 16个字节的keyString key = scanner.nextLine();if (key.length() != 16) {throw new IllegalArgumentException("key的长度必须为16字节");}HTTPDNSAESHelper aesHelper = new HTTPDNSAESHelper();System.out.println("测试域名加密和解密是否正确: ");String encrypt = aesHelper.encrypt(data, key);System.out.println("加密后的域名: " + encrypt);String decrypt = aesHelper.decrypt(encrypt, key);System.out.println("解密后: " + decrypt);try {String line;StringBuilder response = new StringBuilder();URL url = new URL("http://119.29.29.98/d?dn=" + encrypt + "&id=" + id + "&alg=aes");HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setDoOutput(true);conn.setRequestMethod("GET");int responseCode = conn.getResponseCode();if (responseCode != HttpURLConnection.HTTP_OK) {throw new RuntimeException("请求HTTP DNS接口失败, 响应码: " + responseCode);}try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {while ((line = reader.readLine()) != null) {response.append(line);}System.out.println("响应结果: " + response);System.out.println("解密后: " + aesHelper.decrypt(response.toString(), key));}} catch (MalformedURLException e) {throw new RuntimeException("请求HTTP DNS接口失败", e);} catch (IOException e) {throw new RuntimeException(e);}}/*** base16编码** @param data 字节数组* @return base16编码后的字符串*/public static String hexEncode(byte[] data) {if (data == null || data.length == 0) {return "";}StringBuilder result = new StringBuilder(data.length * 2);for (byte datum : data) {int high = (datum >> 4) & 0xf;int low = datum & 0xf;result.append(Base16Table.charAt(high));result.append(Base16Table.charAt(low));}return result.toString();}/*** base16解码** @param data base16编码后的字符串* @return 字节数组*/public static byte[] hexDecode(String data) {if (data == null || data.length() == 0) return null;if (data.length() % 2 != 0) {throw new RuntimeException("invalid hex encoded string");}byte[] result = new byte[data.length() / 2];for (int i = 0, j = 0; i < result.length; i++, j += 2) {byte high = ReversedBase16Table.get(data.charAt(j));byte low = ReversedBase16Table.get(data.charAt(j + 1));result[i] = (byte) ((high << 4) | (low & 0xf));}return result;}public static byte[] getUTF8Bytes(String str) {try {return str.getBytes("UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException("invalid data", e);}}public static boolean isBlank(String str) {return str == null || str.length() == 0;}/*** 加密** @param data 数据* @param key 16个字节长度的key* @return 加密后的数据*/public String encrypt(String data, String key) {if (key == null || key.length() != KeySize) {throw new IllegalArgumentException("key length must be 16");} else if (isBlank(data)) {throw new IllegalArgumentException("data is blank");}// 使用pkcs7Padding算法填充, 参考rfc5652byte[] rawData = pkcs7Padding(getUTF8Bytes(data), IVSize);byte[] ivBytes = generateRandomIVBytes();byte[] keyBytes = getUTF8Bytes(key);SecretKeySpec aesKey = new SecretKeySpec(keyBytes, AlgorithmName);IvParameterSpec iv = new IvParameterSpec(ivBytes);try {// AES, CBC模式加密Cipher cipher = Cipher.getInstance(AlgorithmCombinationName);cipher.init(Cipher.ENCRYPT_MODE, aesKey, iv);byte[] encryptedBytes = cipher.doFinal(rawData);// hex编码return hexEncode(concatBytes(ivBytes, encryptedBytes));} catch (Exception e) {throw new RuntimeException("failed to encrypt data", e);}}/*** 解密** @param data 数据* @param key 16个字节长度的key* @return 解密后的数据*/public String decrypt(String data, String key) {if (key == null || key.length() != KeySize) {throw new IllegalArgumentException("key length must be 16");} else if (isBlank(data)) {throw new IllegalArgumentException("data is blank");}// hex解码byte[] decodedBytes = hexDecode(data);if (decodedBytes == null || decodedBytes.length < IVSize) {throw new IllegalArgumentException("invalid encryptedData, missing iv");}byte[] keyBytes = getUTF8Bytes(key);byte[] encryptedBytes = extractEncryptedData(decodedBytes);SecretKeySpec aesKey = new SecretKeySpec(keyBytes, AlgorithmName);IvParameterSpec iv = extractIV(decodedBytes);try {// AES, CBC模式解密Cipher cipher = Cipher.getInstance(AlgorithmCombinationName);cipher.init(Cipher.DECRYPT_MODE, aesKey, iv);byte[] decryptedBytes = cipher.doFinal(encryptedBytes);// 去掉pkcs7Padding填充的字节return new String(unPkcs7Padding(decryptedBytes, IVSize));} catch (Exception e) {throw new RuntimeException("failed to decrypt data", e);}}private byte[] concatBytes(byte[] first, byte[] second) {if (first == null || first.length == 0) {return second;}if (second == null || second.length == 0) {return first;}byte[] target = new byte[first.length + second.length];System.arraycopy(first, 0, target, 0, first.length);System.arraycopy(second, 0, target, first.length, second.length);return target;}/*** 从响应数据中提取iv参数** @param data 响应数据* @return iv参数*/private IvParameterSpec extractIV(byte[] data) {if (data.length < IVSize) {throw new IllegalArgumentException("missing iv part");}byte[] ivBytes = new byte[IVSize];System.arraycopy(data, 0, ivBytes, 0, ivBytes.length);return new IvParameterSpec(ivBytes);}/*** 从响应数据中提取加密数据** @param data 响应数据* @return 加密数据*/private byte[] extractEncryptedData(byte[] data) {if (data.length - IVSize <= 0) {throw new IllegalArgumentException("missing encrypted data part");}byte[] encryptedBytes = new byte[data.length - IVSize];System.arraycopy(data, IVSize, encryptedBytes, 0, encryptedBytes.length);return encryptedBytes;}/*** 生成随机的16字节的IV** @return 随机的16字节的IV*/private byte[] generateRandomIVBytes() {byte[] iv = new byte[16];Random random = new Random();random.nextBytes(iv);return iv;}private byte[] pkcs7Padding(byte[] data, int blockSize) {int paddingBytesAmount = blockSize - (data.length % blockSize);byte[] paddingResult = new byte[data.length + paddingBytesAmount];System.arraycopy(data, 0, paddingResult, 0, data.length);for (int i = 0; i < paddingBytesAmount; i++) {paddingResult[data.length + i] = (byte) paddingBytesAmount;}return paddingResult;}private byte[] unPkcs7Padding(byte[] data, int blockSize) {if (data == null || data.length == 0 || data.length % blockSize != 0) {throw new RuntimeException("data isn't aligned to blockSize");}int paddingBytesAmount = data[data.length - 1];for (int i = 0; i < paddingBytesAmount; i++) {if (data[data.length - i - 1] != paddingBytesAmount) {throw new RuntimeException("padding had malformed entries");}}byte[] result = new byte[data.length - paddingBytesAmount];System.arraycopy(data, 0, result, 0, result.length);return result;}}
DES 加密
首先,创建
org/example
目录,并在org/example
目录下,创建文件HTTPDNSDESHelper.java
,执行javac ./org/example/HTTPDNSDESHelper.java
命令,得到HTTPDNSDESHelper$1.class
和HTTPDNSDESHelper.class
文件。
执行
java org.example.HTTPDNSDESHelper
命令。依次输入 HTTPDNS 的 id、查询的域名、8位的 DES 加解密的密钥。
HTTPDNSDESHelper.java
代码package org.example;import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.nio.charset.StandardCharsets;import java.util.HashMap;import java.util.Map;import java.util.Scanner;public class HTTPDNSDESHelper {private static final int KeySize = 8;private static final String AlgorithmName = "DES";private static final String AlgorithmCombinationName = "DES/ECB/PKCS5Padding";private static final String Base16Table = "0123456789ABCDEF";private static final Map<Character, Byte> ReversedBase16Table = new HashMap<Character, Byte>() {{put('0', (byte) 0);put('1', (byte) 1);put('2', (byte) 2);put('3', (byte) 3);put('4', (byte) 4);put('5', (byte) 5);put('6', (byte) 6);put('7', (byte) 7);put('8', (byte) 8);put('9', (byte) 9);put('A', (byte) 10);put('B', (byte) 11);put('C', (byte) 12);put('D', (byte) 13);put('E', (byte) 14);put('F', (byte) 15);put('a', (byte) 10);put('b', (byte) 11);put('c', (byte) 12);put('d', (byte) 13);put('e', (byte) 14);put('f', (byte) 15);}};public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.println("请输入你的id:");String id = scanner.nextLine();System.out.println("请输入你要查询的域名:");String data = scanner.nextLine();System.out.println("请输入你的key:");// 8个字节的keyString key = scanner.nextLine();if (key.length() != 8) {throw new IllegalArgumentException("key的长度必须为8字节");}HTTPDNSDESHelper desHelper = new HTTPDNSDESHelper();System.out.println("测试域名加密和解密是否正确: ");String encrypt = desHelper.encrypt(data, key);System.out.println("加密后的域名: " + encrypt);String decrypt = desHelper.decrypt(encrypt, key);System.out.println("解密后: " + decrypt);try {String line;StringBuilder response = new StringBuilder();URL url = new URL("http://119.29.29.98/d?dn=" + encrypt + "&id=" + id + "&alg=des");HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setDoOutput(true);conn.setRequestMethod("GET");int responseCode = conn.getResponseCode();if (responseCode != HttpURLConnection.HTTP_OK) {throw new RuntimeException("请求HTTP DNS接口失败, 响应码: " + responseCode);}try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {while ((line = reader.readLine()) != null) {response.append(line);}System.out.println("响应结果: " + response);System.out.println("解密后: " + desHelper.decrypt(response.toString(), key));}} catch (MalformedURLException e) {throw new RuntimeException("请求HTTP DNS接口失败", e);} catch (IOException e) {throw new RuntimeException(e);}}/*** base16编码** @param data 字节数组* @return base16编码后的字符串*/public static String hexEncode(byte[] data) {if (data == null || data.length == 0) {return "";}StringBuilder result = new StringBuilder(data.length * 2);for (byte datum : data) {int high = (datum >> 4) & 0xf;int low = datum & 0xf;result.append(Base16Table.charAt(high));result.append(Base16Table.charAt(low));}return result.toString();}/*** base16解码** @param data base16编码后的字符串* @return 字节数组*/public static byte[] hexDecode(String data) {if (data == null || data.length() == 0) return null;if (data.length() % 2 != 0) {throw new RuntimeException("invalid hex encoded string");}byte[] result = new byte[data.length() / 2];for (int i = 0, j = 0; i < result.length; i++, j += 2) {byte high = ReversedBase16Table.get(data.charAt(j));byte low = ReversedBase16Table.get(data.charAt(j + 1));result[i] = (byte) ((high << 4) | (low & 0xf));}return result;}public static byte[] getUTF8Bytes(String str) {return str.getBytes(StandardCharsets.UTF_8);}public static boolean isBlank(String str) {return str == null || str.length() == 0;}public String encrypt(String data, String key) {if (key == null || key.length() != KeySize) {throw new IllegalArgumentException("key length must be 8");} else if (isBlank(data)) {throw new IllegalArgumentException("data is blank");}byte[] rawBytes = getUTF8Bytes(data);byte[] keyBytes = getUTF8Bytes(key);SecretKeySpec desKey = new SecretKeySpec(keyBytes, AlgorithmName);try {Cipher cipher = Cipher.getInstance(AlgorithmCombinationName);cipher.init(Cipher.ENCRYPT_MODE, desKey);byte[] encryptedData = cipher.doFinal(rawBytes);return hexEncode(encryptedData);} catch (Exception e) {throw new RuntimeException("failed to encrypt", e);}}public String decrypt(String data, String key) {if (key == null || key.length() != KeySize) {throw new IllegalArgumentException("key length must be 8");} else if (isBlank(data)) {throw new IllegalArgumentException("data is blank");}byte[] encryptedBytes = hexDecode(data);byte[] keyBytes = getUTF8Bytes(key);SecretKeySpec desKey = new SecretKeySpec(keyBytes, AlgorithmName);try {Cipher cipher = Cipher.getInstance(AlgorithmCombinationName);cipher.init(Cipher.DECRYPT_MODE, desKey);byte[] decryptedData = cipher.doFinal(encryptedBytes);return new String(decryptedData);} catch (Exception e) {throw new RuntimeException("failed to decrypt", e);}}}
Go 版本
创建一个目录
test
,进入test
目录,创建main.go
文件,把以下代码复制到main.go
文件中,然后运行go run main.go
命令。分别会进行 AES 加解密和 DES 加解密,在此过程中需要分别输入 HTTPDNS 的 id、请求域名、16位的 AES 加解密密钥、HTTPDNS 的 id、请求域名、8位的 DES 加解密密钥。
package mainimport ("bytes""crypto/aes""crypto/cipher""crypto/des""encoding/hex""errors""fmt""io""math/rand""net/http")var (ErrInvalidBlockSize = errors.New("invalid block size")ErrInvalidPKCSData = errors.New("invalid PKCS data")ErrInvalidPKCSPadding = errors.New("invalid padding on input"))// AesDecrypt aes解密// 步骤:// 1. 先把加密文本base16(hex)解码// 2. 从解码后的数据前16位提取出IV, 其他部分作为待解密数据// 3. 根据IV和key解密数据// 4. 把解密后的数据尝试去除填充数据(pkcs7)func AesDecrypt(encryptedData, key string) (string, error) {cipherText, err := hex.DecodeString(encryptedData)if err != nil {return "", err}if len(cipherText) < aes.BlockSize {return "", errors.New("missing iv")}// 前16字节为IViv := cipherText[:aes.BlockSize]cipherText = cipherText[aes.BlockSize:]c, err := aes.NewCipher([]byte(key))if err != nil {return "", err}mode := cipher.NewCBCDecrypter(c, iv)mode.CryptBlocks(cipherText, cipherText)unPaddingData, err := pkcs7UnPad(cipherText, aes.BlockSize)if err != nil {return "", err}return string(unPaddingData), nil}// AesEncrypt aes加密// 步骤:// 1. 生成16位的随机IV// 2. 对加密数据尝试进行填充(pkcs7)// 3. 使用key和IV加密填充后的数据// 4. 拼接IV和加密后的数据// 5. 把字节数组通过base16(hex)编码为字符串func AesEncrypt(data, key string) (string, error) {iv := randomIV(aes.BlockSize)c, err := aes.NewCipher([]byte(key))if err != nil {return "", err}mode := cipher.NewCBCEncrypter(c, iv)cipherText, err := pkcs7([]byte(data), aes.BlockSize)if err != nil {return "", err}mode.CryptBlocks(cipherText, cipherText)// 前16字节为IVcipherText = append(iv, cipherText...)return hex.EncodeToString(cipherText), nil}func randomIV(blockSize int) []byte {iv := make([]byte, blockSize)for i := 0; i < blockSize; i++ {iv[i] = byte(rand.Int())}return iv}func DesEncrypt(data, key string) (string, error) {c, err := des.NewCipher([]byte(key))if err != nil {return "", err}cipherText, err := pkcs5([]byte(data), des.BlockSize)if err != nil {return "", err}dst := make([]byte, len(cipherText))for i := 0; i < len(cipherText); i += des.BlockSize {c.Encrypt(dst[i:i+des.BlockSize], cipherText[i:i+des.BlockSize])}return hex.EncodeToString(dst), nil}func DesDecrypt(encryptedData, key string) (string, error) {cipherText, err := hex.DecodeString(encryptedData)if err != nil {return "", err}if len(cipherText)%des.BlockSize != 0 {return "", errors.New("invalid encryptedData")}c, err := des.NewCipher([]byte(key))if err != nil {return "", err}dst := make([]byte, len(cipherText))for i := 0; i < len(cipherText); i += des.BlockSize {c.Decrypt(dst[i:i+des.BlockSize], cipherText[i:i+des.BlockSize])}unPaddingData, err := pkcs5UnPad(dst, des.BlockSize)if err != nil {return "", err}return string(unPaddingData), nil}func pkcs7(data []byte, blockSize int) ([]byte, error) {if blockSize <= 0 {return nil, ErrInvalidBlockSize}if len(data) == 0 {return nil, ErrInvalidPKCSData}n := blockSize - (len(data) % blockSize)pd := make([]byte, len(data)+n)copy(pd, data)copy(pd[len(data):], bytes.Repeat([]byte{byte(n)}, n))return pd, nil}func pkcs7UnPad(data []byte, blockSize int) ([]byte, error) {if blockSize <= 0 {return nil, ErrInvalidBlockSize}if len(data) == 0 {return nil, ErrInvalidPKCSData}if len(data)%blockSize != 0 {return nil, ErrInvalidPKCSPadding}c := data[len(data)-1]n := int(c)if n == 0 || n > len(data) {return nil, ErrInvalidPKCSPadding}for i := 0; i < n; i++ {if data[len(data)-i-1] != c {return nil, ErrInvalidPKCSPadding}}return data[:len(data)-n], nil}func pkcs5(data []byte, blockSize int) ([]byte, error) {if blockSize <= 0 {return nil, ErrInvalidBlockSize}if len(data) == 0 {return nil, ErrInvalidPKCSData}pd := blockSize - (len(data) % blockSize)return append(data, bytes.Repeat([]byte{byte(pd)}, pd)...), nil}func pkcs5UnPad(data []byte, blockSize int) ([]byte, error) {if blockSize <= 0 {return nil, ErrInvalidBlockSize}if len(data) == 0 {return nil, ErrInvalidPKCSData}if len(data)%blockSize != 0 {return nil, ErrInvalidPKCSPadding}c := data[len(data)-1]n := int(c)if n == 0 || n > len(data) {return nil, ErrInvalidPKCSPadding}for i := 0; i < n; i++ {if data[len(data)-i-1] != c {return nil, ErrInvalidPKCSPadding}}return data[:len(data)-n], nil}func executeHTTPDNSRequest(id, dn, alg string) (string, error) {resp, err := http.Get(fmt.Sprintf("http://119.29.29.98/d?dn=%s&id=%s&alg=%s", dn, id, alg))if err != nil {return "", err}if resp.StatusCode != http.StatusOK {return "", errors.New(fmt.Sprintf("请求HTTP DNS接口失败, 状态码: %d", resp.StatusCode))}defer resp.Body.Close()data, err := io.ReadAll(resp.Body)if err != nil {return "", err}return string(data), nil}func scan(tip string, dest *string) {fmt.Println(tip)_, err := fmt.Scanln(dest)if err != nil {panic(err)}}func aesEncryptAndDecryptTest() {fmt.Println("AES加解密测试:")var id, key, domain stringscan("请输入你的id:", &id)scan("请输入你要请求的域名:", &domain)scan("请输入你16字节的key:", &key)encryptedData, err := AesEncrypt(domain, key)if err != nil {panic(err)}fmt.Println("加密后的域名:", encryptedData)decryptedData, err := AesDecrypt(encryptedData, key)if err != nil {panic(err)}fmt.Println("解密后的域名:", decryptedData)data, err := executeHTTPDNSRequest(id, encryptedData, "aes")if err != nil {panic(err)}decryptedIPAddress, err := AesDecrypt(data, key)if err != nil {panic(err)}fmt.Println("解密后的IP地址:", decryptedIPAddress)}func desEncryptAndDecryptTest() {fmt.Println("DES加解密测试:")var id, key, domain stringscan("请输入你的id:", &id)scan("请输入你要请求的域名:", &domain)scan("请输入你8字节的key:", &key)encryptedData, err := DesEncrypt(domain, key)if err != nil {panic(err)}fmt.Println("加密后的域名:", encryptedData)decryptedData, err := DesDecrypt(encryptedData, key)if err != nil {panic(err)}fmt.Println("解密后的域名:", decryptedData)data, err := executeHTTPDNSRequest(id, encryptedData, "des")if err != nil {panic(err)}decryptedIPAddress, err := DesDecrypt(data, key)if err != nil {panic(err)}fmt.Println("解密后的IP地址:", decryptedIPAddress)}func main() {aesEncryptAndDecryptTest()desEncryptAndDecryptTest()}
C++ 版本
安装 make、g++、以及 libcurl 和 libcrypto 库。
在 Centos 系统上执行以下命令:
yum install -y libcurl-devel openssl openssl-dev make gcc gcc-c++
创建
test
目录,进入test
目录,复制以下文件到test
目录,执行make
,然后执行main
可执行文件,根据提示输入获取响应 HTTPDNS 响应结果。Makefile
all:g++ -Wall -Werror -g -o main -lcrypto -lcurl main.cpp des.cpp aes.cpp hex.cppclean:- rm -rf ./main
main.cpp
#include "aes.h"#include "des.h"#include <iostream>#include <curl/curl.h>size_t http_dns_response_callback(char *ptr, size_t size, size_t nmemb, void *userdata){size_t real_size = size * nmemb;std::string *response = (std::string *)userdata;response->append(ptr, real_size);return real_size;}void send_http_dns_request(const std::string &encrypted_domain, const std::string &id,const std::string &alg, std::string &response){std::string url;CURL *easy_handle;CURLcode res;easy_handle = curl_easy_init();if (!easy_handle)throw std::runtime_error("curl_easy_init() failed");url.append("http://119.29.29.98/d?dn=").append(encrypted_domain).append("&id=").append(id).append("&alg=").append(alg);res = curl_easy_setopt(easy_handle, CURLOPT_URL, url.c_str());if (res != CURLE_OK){curl_easy_cleanup(easy_handle);throw std::runtime_error("failed to set url option");}res = curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, http_dns_response_callback);if (res != CURLE_OK){curl_easy_cleanup(easy_handle);throw std::runtime_error("failed to set writedata option");}res = curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &response);if (res != CURLE_OK){curl_easy_cleanup(easy_handle);throw std::runtime_error("failed to set writedata option");}res = curl_easy_perform(easy_handle);if (res != CURLE_OK){curl_easy_cleanup(easy_handle);throw std::runtime_error(std::string("curl_easy_perform() failed: ").append(curl_easy_strerror(res)));}curl_global_cleanup();}void get_user_input(const std::string &tip, std::string &id, std::string &domain,std::string &key, const size_t key_length){std::cout << tip << std::endl;std::cout << "请输入你的id:" << std::endl;std::cin >> id;std::cout << "请输入需要解析的域名:" << std::endl;std::cin >> domain;std::cout << "请输入" << key_length << "个字节的key:" << std::endl;std::cin >> key;if (id.size() == 0)throw std::runtime_error("请输入有效的id");if (domain.size() == 0)throw std::runtime_error("请输入有效的域名");if (key.size() != key_length)throw std::runtime_error(std::string("key长度必须为").append(std::to_string(key_length)).append("个字节"));}void aes_test(){std::string id, domain, key, response;get_user_input("AES加解密", id, domain, key, 16);std::string &&encrypted_data = aes_encrypt(domain, key);send_http_dns_request(encrypted_data, id, "aes", response);std::string &&decrypted_data = aes_decrypt(response, key);std::cout << "响应结果: " << decrypted_data << std::endl;}void des_test(){std::string id, domain, key, response;get_user_input("DES加解密", id, domain, key, 8);std::string &&encrypted_data = des_encrypt(domain, key);send_http_dns_request(encrypted_data, id, "des", response);std::string &&decrypted_data = des_decrypt(response, key);std::cout << "响应结果: " << decrypted_data << std::endl;}int main(){aes_test();des_test();}
aes.cpp
#include "aes.h"#include "hex.h"#include <openssl/evp.h>#include <openssl/aes.h>#include <time.h>#include <stdlib.h>#include <stdexcept>typedef unsigned char uchar;const size_t IV_SIZE = 16;const size_t KEY_SIZE = 16;const size_t BLOCK_SIZE = 16;static std::string generate_iv(){size_t i;std::string iv;srand(time(NULL));for (i = 0; i < IV_SIZE; i++)iv.push_back(rand() % 256);return iv;}std::string aes_encrypt(const std::string &data, const std::string &key){if (data.size() == 0)throw std::runtime_error("data is empty");else if (key.size() != KEY_SIZE)throw std::runtime_error("key length must be 16");EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();std::string &&iv = generate_iv();if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, (uchar *)key.c_str(), (uchar *)iv.c_str())){EVP_CIPHER_CTX_free(ctx);throw std::runtime_error("EVP_EncryptInit_ex() failed");}EVP_CIPHER_CTX_set_key_length(ctx, KEY_SIZE);uchar *output = new uchar[data.size() + 2 * BLOCK_SIZE];int output_len = 0;if (1 != EVP_EncryptUpdate(ctx, output, &output_len, (uchar *)data.c_str(), data.size())){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_EncryptUpdate() failed");}uchar *pad_buf = output + output_len;int pad_len;if (1 != EVP_EncryptFinal_ex(ctx, pad_buf, &pad_len)){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_EncryptFinal_ex() failed");}std::string result;result.append(iv);result.append((char *)output, output_len + pad_len);EVP_CIPHER_CTX_free(ctx);delete[] output;return encode(result);}std::string aes_decrypt(const std::string &data, const std::string &key){if (data.size() == 0)throw std::runtime_error("data is empty");else if (key.size() != KEY_SIZE)throw std::runtime_error("key length must be 16");std::string &&decoded_data = decode(data);if (decoded_data.size() <= IV_SIZE)throw std::runtime_error("invalid data");std::string &&iv = decoded_data.substr(0, IV_SIZE);std::string &&encrypted_data = decoded_data.substr(IV_SIZE);EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, (uchar *)key.c_str(), (uchar *)iv.c_str())){EVP_CIPHER_CTX_free(ctx);throw std::runtime_error("EVP_DecryptInit_ex() failed");}EVP_CIPHER_CTX_set_key_length(ctx, KEY_SIZE);uchar *output = new uchar[data.size() + 2 * BLOCK_SIZE];int output_len = 0;if (1 != EVP_DecryptUpdate(ctx, output, &output_len, (uchar *)encrypted_data.c_str(), encrypted_data.size())){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_DecryptUpdate() failed");}int pad_len = 0;if (1 != EVP_DecryptFinal_ex(ctx, output + output_len, &pad_len)){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_DecryptFinal_ex() failed");}output_len += pad_len;output[output_len] = 0;std::string result = (char *)output;EVP_CIPHER_CTX_free(ctx);delete[] output;return result;}
aes.h
#ifndef AES_H#define AES_H#include <string>std::string aes_encrypt(const std::string &data, const std::string &key);std::string aes_decrypt(const std::string &data, const std::string &key);#endif
des.cpp
#include "des.h"#include "hex.h"#include <openssl/evp.h>#include <openssl/des.h>#include <stdexcept>typedef unsigned char uchar;const size_t BLOCK_SIZE = 16;const size_t KEY_SIZE = 8;std::string des_encrypt(const std::string &data, const std::string &key){if (data.size() == 0)throw std::runtime_error("data is empty");else if (key.size() != KEY_SIZE)throw std::runtime_error("key length must be 8");EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();if (1 != EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, (uchar *)key.c_str(), NULL)){EVP_CIPHER_CTX_free(ctx);throw std::runtime_error("EVP_EncryptInit_ex() failed");}EVP_CIPHER_CTX_set_key_length(ctx, KEY_SIZE);uchar *output = new uchar[data.size() + BLOCK_SIZE * 2];int output_len = 0;if (1 != EVP_EncryptUpdate(ctx, output, &output_len, (uchar *)data.c_str(), data.size())){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_EncryptUpdate() failed");}int pad_len = 0;if (1 != EVP_EncryptFinal_ex(ctx, output + output_len, &pad_len)){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_EncryptFinal_ex() failed");}output[output_len + pad_len] = 0;std::string result = (char *)output;EVP_CIPHER_CTX_free(ctx);delete[] output;return encode(result);}std::string des_decrypt(const std::string &data, const std::string &key){if (data.size() == 0)throw std::runtime_error("data is empty");else if (key.size() != KEY_SIZE)throw std::runtime_error("key length must be 8");std::string &&decoded_data = decode(data);EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();if (1 != EVP_DecryptInit_ex(ctx, EVP_des_ecb(), NULL, (uchar *)key.c_str(), NULL)){EVP_CIPHER_CTX_free(ctx);throw std::runtime_error("EVP_DecryptInit_ex() failed");}EVP_CIPHER_CTX_set_key_length(ctx, KEY_SIZE);uchar *output = new uchar[data.size() + 2 * BLOCK_SIZE];int output_len = 0;if (1 != EVP_DecryptUpdate(ctx, output, &output_len, (uchar *)decoded_data.c_str(), decoded_data.size())){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_DecryptUpdate() failed");}int pad_len = 0;if (1 != EVP_DecryptFinal_ex(ctx, output + output_len, &pad_len)){EVP_CIPHER_CTX_free(ctx);delete[] output;throw std::runtime_error("EVP_DecryptFinal_ex() failed");}output_len += pad_len;output[output_len] = 0;std::string result = (char *)output;EVP_CIPHER_CTX_free(ctx);delete[] output;return result;}
des.h
#ifndef DES_H#define DES_H#include <string>std::string des_encrypt(const std::string &data, const std::string &key);std::string des_decrypt(const std::string &data, const std::string &key);#endif
hex.h
#ifndef HEX_H#define HEX_H#include <string>std::string encode(const std::string &data);std::string decode(const std::string &data);#endif
hex.cpp
#include "hex.h"#include <stdexcept>#include <iostream>#include <map>static const char HEX_STRING[] = "0123456789ABCDEF";static std::map<char, int> HEX_TABLE = {{'0', 0},{'1', 1},{'2', 2},{'3', 3},{'4', 4},{'5', 5},{'6', 6},{'7', 7},{'8', 8},{'9', 9},{'a', 10},{'b', 11},{'c', 12},{'d', 13},{'e', 14},{'f', 15},{'A', 10},{'B', 11},{'C', 12},{'D', 13},{'E', 14},{'F', 15},};std::string encode(const std::string &data){size_t i;int high, low;std::string res;for (i = 0; i < data.size(); i++){high = (data[i] >> 4) & 0xf;low = data[i] & 0xf;res.push_back(HEX_STRING[high]);res.push_back(HEX_STRING[low]);}return res;}std::string decode(const std::string &data){size_t i;std::string res;if (data.size() == 0)return res;else if (data.size() % 2 != 0)throw std::runtime_error("invalid data length");for (i = 0; i < data.size(); i += 2){if (HEX_TABLE.find(data[i]) == HEX_TABLE.end() || HEX_TABLE.find(data[i + 1]) == HEX_TABLE.end())throw std::runtime_error("invalid character");res.push_back((HEX_TABLE[data[i]] << 4) | HEX_TABLE[data[i + 1]]);}return res;}
JavaScript版本
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>DES和AES加解密实践</title></head><body><h1>DES和AES加解密实践</h1></body><!-- 注意: 需要引入crypto-js库 --><script>class AESHelper {constructor(key) {if (typeof (key) !== "string" || key.length < 16) {throw "[AES] failed to construct AESHelper, invalid key";}this.key = CryptoJS.enc.Utf8.parse(key);}Encrypt(data) {if (typeof (data) !== "string" || data.length === 0) {throw "[AES] failed to encrypt, invalid data";}const iv = CryptoJS.lib.WordArray.random(16);const encryptedContent = CryptoJS.AES.encrypt(data, this.key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return iv.toString(CryptoJS.enc.Hex) + encryptedContent.ciphertext.toString(CryptoJS.enc.Hex);}Decrypt(data) {if (typeof (data) !== "string" || data.length <= 32) {throw "[AES] failed to decrypt, invalid data";}// 前32个字符是16个字节的iv值通过hex编码的const iv = CryptoJS.enc.Hex.parse(data.substr(0, 32));data = data.substr(32, data.length - 32);const decryptionParam = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(data)});const decryptedContent = CryptoJS.AES.decrypt(decryptionParam, this.key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return decryptedContent.toString(CryptoJS.enc.Utf8);}}class DESHelper {constructor(key) {if (typeof (key) !== "string" || key.length < 8) {throw "[DES] failed to construct DESHelper, invalid key";}this.key = CryptoJS.enc.Utf8.parse(key);}Encrypt(data) {if (typeof (data) !== "string" || data.length === 0) {throw "[DES] failed to encrypt, invalid data";}const encryptedContent = CryptoJS.DES.encrypt(data, this.key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});return encryptedContent.ciphertext.toString(CryptoJS.enc.Hex);}Decrypt(data) {if (typeof (data) !== "string" || data.length == 0) {throw "[DES] failed to decrypt, invalid data";}const decryptionParam = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(data)});const decryptedContent = CryptoJS.DES.decrypt(decryptionParam, this.key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});return decryptedContent.toString(CryptoJS.enc.Utf8);}}{// 输入对应的aes key和域名const aesHelper = new AESHelper("your aes key");const encryptedData = aesHelper.Encrypt("your domain");console.log('AES加密后的数据', encryptedData);const decryptedData = aesHelper.Decrypt(encryptedData);console.log('AES解密后的数据', decryptedData);}{// 输入对应的des key和域名const desHelper = new DESHelper("your des key");const encryptedData = desHelper.Encrypt("your domain");console.log('DES加密后的数据', encryptedData);const decryptedData = desHelper.Decrypt(encryptedData);console.log('DES解密后的数据', decryptedData);}</script></html>