移动解析 HTTPDNS HTTPDNS 加解密实践

使用 DES、AES 加密算法可以对请求参数进行加密,并对其响应数据解密,防止明文请求在传输过程中被恶意篡改。这篇将指导你如何使用 DES、AES 加密算法。

相关产品
移动解析 HTTPDNS 产品页,含版本规格、价格与购买入口。

一、操作场景

注意:说明:

注意:使用 HTTPS 请求方式查询,传输的数据会因为 TLS 通道而被加密保护,因此不需要主动对传入的数据额外加密。

二、前提条件

掌握 Java、JavaScript、Go 或者 C++ 语言。

三、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个字节的key String 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算法填充, 参考rfc5652 byte[] 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个字节的key String 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字节为IV iv := 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字节为IV cipherText = 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 string scan("请输入你的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 string scan("请输入你的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 响应结果。

依次输入授权 ID、需要通过 AES 加解密解析的域名、AES 密钥、授权 ID、需要通过 DES 加解密解析的域名、DES 密钥。

Makefile

all: g++ -Wall -Werror -g -o main main.cpp des.cpp aes.cpp hex.cpp -lcrypto -lcurlclean: – 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 src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script><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>

赞 (0)
上一篇 4天前
下一篇 4天前