🔐 解密链路演进
1.
客户端定时调用 QIWE 会话拉取接口获取加密的密文包。
2.
使用企业在后台独立配置的 RSA 私钥 对密文中的 encrypt_random_key 进行解密,还原出本次会话的 aes_key。
3.
利用该 aes_key,通过 AES-256-CBC 算法解密出真实的文本聊天记录或媒体文件元数据。
💻 核心解密实现(以 Java 为例)
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; public class QIWEDataDecryptor { // 1. RSA 私钥解密获取 AES 秘钥 public static byte[] decryptRSAKey(String encryptedRandomKey, String privateKeyBase64) throws Exception { byte[] encryptedData = Base64.getDecoder().decode(encryptedRandomKey); byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(encryptedData); } // 2. AES-256-CBC 解密出真实会话明文 public static String decryptChatMsg(String encryptedChatMsg, byte[] aesKey) throws Exception { byte[] encryptedData = Base64.getDecoder().decode(encryptedChatMsg); // 规范:IV 向量固定取 aesKey 的前 16 字节 byte[] iv = new byte[16]; System.arraycopy(aesKey, 0, iv, 0, 16); SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] original = cipher.doFinal(encryptedData); return new String(original, "UTF-8"); } }📦 大文件拉取的流处理与断点续传
对于聊天记录中动辄数百 MB 的视频或文件,严禁一次性全量加载至服务器内存中:
指针持久化 (
index_buf):QIWE 会话接口在每次分页返回时都会携带一个进度指针index_buf。必须将该指针随同流水数据一起落库,下一轮调度拉取时以此为入参,实现原生层面的“断点续传”。分片流式下载:调用平台的分片媒体下载接口时,传入特定的
file_id并设定offset和length(如每次 5MB)。在内存中维护一个小缓冲区,边下载边以追加模式(Append Mode)写入对象存储(如 MinIO/OSS),严禁堆积在 JVM 堆内存中,避免 OOM(内存溢出)。