系统间接口通信的加密与签名机制一、为什么需要加密和签名两个系统通过 HTTP 接口通信时面临三个安全威胁威胁描述后果窃听第三方截获请求内容业务数据泄露订单号、金额等篡改第三方修改请求内容后转发伪造业务操作修改审核结果等伪造第三方冒充合法系统发送请求非法调用接口解决方案加密防窃听即使截获也看不懂内容签名防篡改 防伪造内容被改签名就不对没有密钥造不出签名注博客https://blog.csdn.net/badao_liumang_qizhi二、核心概念2.1 对称加密AES用同一个密钥加密和解密。双方事先约定密钥。明文 密钥 → 加密算法 → 密文 密文 密钥 → 解密算法 → 明文特点速度快适合加密大段业务数据。2.2 签名MD5/SHA 密钥将内容和密钥拼接后做哈希运算生成固定长度的摘要。接收方用相同方式计算比对是否一致。内容 密钥 → 哈希运算 → 签名值固定长度字符串特点不可逆无法从签名反推内容内容变一个字签名就完全不同没有密钥就无法伪造正确的签名2.3 Base64 编码将二进制数据转为可安全传输的 ASCII 字符串。不是加密只是编码格式转换。二进制/中文/特殊字符 → Base64编码 → 纯英文数字/字符串三、完整通信协议设计3.1 协议格式发送方将业务数据加密后连同签名一起作为表单参数发送POST /api/receive-data Content-Type: application/x-www-form-urlencoded sourceSYSTEM_Abutypebusiness_typetypejsonnotifytime2026-08-12 10:00:00contentAES加密后的密文sign签名值参数含义是否加密source来源系统标识明文butype业务类型明文type数据格式json/xml明文notifytime请求时间明文content业务数据AES加密sign签名MD5Base643.2 发送方处理流程原始业务数据JSON/XML ↓ AES加密密钥双方约定的key ↓ URL编码处理特殊字符 ↓ 得到 content 参数 同时对原始业务数据做签名 原始数据 password → MD5哈希 → Base64编码 → 得到 sign 参数 ↓ 组装HTTP请求发送3.3 接收方处理流程收到HTTP请求解析参数 ↓ URL解码 content ↓ AES解密 content密钥双方约定的key→ 得到原始业务数据 ↓ 验证签名 原始业务数据 password → MD5哈希 → Base64编码 → 与收到的 sign 比对 ↓ 比对一致 → 数据可信处理业务 比对不一致 → 拒绝请求数据被篡改或来源不合法四、通用示例代码4.1 AES 加解密工具类importjavax.crypto.Cipher;importjavax.crypto.spec.SecretKeySpec;importjava.util.Base64;publicclassAesUtil{privatestaticfinalStringALGORITHMAES;privatestaticfinalStringTRANSFORMATIONAES/ECB/PKCS5Padding;/** * AES加密. * * param data 原始数据 * param key 密钥16/24/32字节 * return Base64编码的密文 */publicstaticStringencrypt(Stringdata,Stringkey)throwsException{SecretKeySpeckeySpecnewSecretKeySpec(key.getBytes(UTF-8),ALGORITHM);CiphercipherCipher.getInstance(TRANSFORMATION);cipher.init(Cipher.ENCRYPT_MODE,keySpec);byte[]encryptedcipher.doFinal(data.getBytes(UTF-8));returnBase64.getEncoder().encodeToString(encrypted);}/** * AES解密. * * param encryptedData Base64编码的密文 * param key 密钥 * return 原始数据 */publicstaticStringdecrypt(StringencryptedData,Stringkey)throwsException{SecretKeySpeckeySpecnewSecretKeySpec(key.getBytes(UTF-8),ALGORITHM);CiphercipherCipher.getInstance(TRANSFORMATION);cipher.init(Cipher.DECRYPT_MODE,keySpec);byte[]decodedBase64.getDecoder().decode(encryptedData);byte[]decryptedcipher.doFinal(decoded);returnnewString(decrypted,UTF-8);}}4.2 签名工具类importorg.apache.commons.codec.digest.DigestUtils;importjava.util.Base64;publicclassSignUtil{/** * 生成签名. * 规则MD5(原始数据 密码) → Base64编码 * * param data 原始业务数据 * param password 签名密码双方约定 * return 签名字符串 */publicstaticStringgenerateSign(Stringdata,Stringpassword){Stringmd5HexDigestUtils.md5Hex(datapassword);returnBase64.getEncoder().encodeToString(md5Hex.getBytes());}/** * 验证签名. * * param data 原始业务数据 * param password 签名密码 * param sign 收到的签名值 * return 签名是否匹配 */publicstaticbooleanverifySign(Stringdata,Stringpassword,Stringsign){StringexpectedSigngenerateSign(data,password);returnexpectedSign.equals(sign);}}4.3 发送方 — 组装加密请求publicclassMessageSender{privateStringaesKeyxxxxx;// AES密钥双方约定privateStringsignPasswordxxxx;// 签名密码双方约定privateStringtargetUrlhttp://target-system/api/receive-data;/** * 发送加密签名请求. */publicStringsend(StringbusinessData,Stringbutype)throwsException{// 1. AES加密业务数据StringencryptedContentAesUtil.encrypt(businessData,aesKey);// 2. URL编码处理/等特殊字符StringencodedContentURLEncoder.encode(encryptedContent,UTF-8);// 3. 计算签名基于原始明文数据StringsignSignUtil.generateSign(businessData,signPassword);// 4. 组装请求参数StringformBodyString.format(source%sbutype%stype%snotifytime%scontent%ssign%s,SYSTEM_A,butype,json,2026-08-12 10:00:00,encodedContent,sign);// 5. 发送HTTP POSTHttpPostpostnewHttpPost(targetUrl);post.setHeader(Content-Type,application/x-www-form-urlencoded);post.setEntity(newStringEntity(formBody));HttpResponseresponsehttpClient.execute(post);returnEntityUtils.toString(response.getEntity());}}4.4 接收方 — 解密验签处理RestControllerpublicclassMessageReceiver{privateStringaesKeyxxxx;privateStringsignPasswordxxxx;PostMapping(value/api/receive-data,producesMediaType.TEXT_HTML_VALUE)publicStringreceiveData(RequestBodyStringrawParam){try{// 1. 解析表单参数MapString,StringparamsparseUrlParams(rawParam);Stringsourceparams.get(source);Stringbutypeparams.get(butype);Stringtypeparams.get(type);Stringcontentparams.get(content);Stringsignparams.get(sign);// 2. 基本校验if(isEmpty(source)||isEmpty(content)||isEmpty(sign)){returnerrorResponse(参数不完整);}// 3. URL解码 AES解密StringdecodedContentURLDecoder.decode(content,UTF-8);StringdecryptedDataAesUtil.decrypt(decodedContent,aesKey);log.info(解密后的业务数据: {},decryptedData);// 4. 验证签名if(!SignUtil.verifySign(decryptedData,signPassword,sign)){returnerrorResponse(签名校验失败);}// 5. 签名通过解析业务数据if(json.equalsIgnoreCase(type)){BusinessDtodtoJSON.parseObject(decryptedData,BusinessDto.class);// 处理业务逻辑...processBusinessData(dto);}elseif(xml.equalsIgnoreCase(type)){BusinessDtodtoXmlUtil.fromXml(decryptedData,BusinessDto.class);processBusinessData(dto);}returnsuccessResponse();}catch(Exceptione){log.error(处理请求异常,e);returnerrorResponse(系统异常: e.getMessage());}}}五、完整调用时序发送方 接收方 │ │ │ 原始数据: {orderNo:001,reason:xxx} │ │ │ │ ① AES加密 → xxx... │ │ ② MD5(原始数据密码) → Base64 → aBcD... │ │ ③ URL编码密文 │ │ │ │──── POST contentxK9f2mQ...signaBcD... ──────────→│ │ │ │ ④ URL解码 content │ │ ⑤ AES解密 → 原始数据 │ │ ⑥ MD5(原始数据密码) │ │ → Base64 → 与sign比对 │ │ ⑦ 匹配数据可信 │ │ ⑧ 处理业务 │ │ │ │←──────────── 返回处理结果 ──────────────────────────── │六、为什么加密和签名要分开加密签名目的保密别人看不到内容完整性身份认证内容没被改确实是对方发的可逆性可逆能解密还原不可逆只能比对不能反推缺一不可只加密不签名→内容保密但可能被篡改只签名不加密→内容防篡改但可被窃听实际场景中两者通常一起用加密保护数据隐私即使网络被监听也看不到业务数据签名保证数据完整性接收方能确认数据没被中间人篡改七、密钥管理7.1 密钥约定双方系统对接前线下交换密钥AES Key 签名 Password通常写在配置文件中xxx:sign:key:xxxx# AES加密密钥password:xxxx# 签名密码7.2 配置注入Value(${xxx.sign.key})privateStringxxxKey;Value(${xxx.sign.password})privateStringxxxPassword;7.3 安全注意事项密钥不能硬编码在代码中不同环境dev/pre/prod使用不同密钥密钥定期轮换可以使用 KMS密钥管理服务动态获取八、与 HTTPS 的关系HTTPS应用层加密签名防窃听✅ TLS加密传输层✅ AES加密业务层防篡改✅ TLS完整性校验✅ 签名验证防伪造✅ 证书验证服务端身份✅ 密钥验证发送方身份保护范围传输过程端到端即使中间有代理/网关也安全为什么有 HTTPS 还要做应用层加密xxx → 网关(TLS终止) → 内部HTTP转发 → xxx服务 ↑ ↑ HTTPS保护这段 这段是明文HTTP网关通常会终止 TLSSSL Termination内部转发用 HTTP。应用层加密保证即使内部链路被监听数据也是安全的。另外应用层签名能验证确实是 xxx 发的而不只是网络连接是安全的。九、使用场景场景为什么用这种方式支付回调微信/支付宝 → 商户验证回调确实来自支付平台防伪造xxx 推送数据 → 仓储系统跨公司系统对接互不信任需要双向验证银行接口对接金融级安全要求内容必须加密开放平台 API第三方调用方通过 AppKeyAppSecret 签名不需要这种方式的场景同一公司内部微服务间调用走内部网络用 JWT 或 IP 白名单即可前端调用后端用 HTTPS JWT 登录态即可十、与 JWT 认证的区别加密签名协议JWT 认证认证对象系统对系统用户对系统携带方式请求体中的 contentsign 参数Header 中的 Authorization: Bearer密钥持有者双方系统各一份认证中心签发服务端公钥验证保护内容业务数据加密仅认证身份不加密请求体典型场景跨公司/跨平台对接用户登录后的接口调用