Android 使用支付接口,需要进行的加密逻辑:MD5、HMAC-SHA256以及RSA
目录
- 前言
- MD5
- HMAC-SHA256
- RSA
- 其他
前言
不使用加密:支付系统如同「裸奔」,面临数据泄露、资金被盗、法律追责等风险。
正确使用加密:构建「端到端安全防线」,确保交易合法可信,同时满足国际合规要求。
支付系统作为金融基础设施,加密不是可选项,而是业务存续的必要条件。任何加密方案的疏漏都可能导致系统性风险、。
在对接第三方支付的时候,最麻烦的问题是什么???没错,就是加密,每个第三方的,可能都还不一样,导致我们开发时间变长,这里我们就来梳理一下最常见的三种加密方式。
一、md5
MD5 是一种哈希函数,用于生成数据的唯一指纹(哈希值)。其特点是:
- ✅ 快速计算:适合大数据量校验
- ❌ 已被破解:易发生哈希碰撞(不同数据生成相同哈希)
- ❌ 无密钥依赖:无法验证数据来源真实性
/*** 生成签名* @param map* @return*/public static String getSignToken(Map<String, String> map) {String result = "";try {List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(map.entrySet());// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {return (o1.getKey()).toString().compareTo(o2.getKey());}});// 构造签名键值对的格式StringBuilder sb = new StringBuilder();for (Map.Entry<String, String> item : infoIds) {if (item.getKey() != null || item.getKey() != "") {String key = item.getKey();String val = item.getValue();if (!(val == "" || val == null)) {sb.append(key + "=" + val + "&");}}}//密钥result = sb.toString()+"key=xxxx";System.out.println(result);Log.d("getPayUrl", "payUrlBean getSignToken1: "+result);//进行MD5加密result = md5(result);
// result = getMD5Value(result);} catch (Exception e) {return null;}return result;}
加密流程:
- 拿到所有参数的Map
- 然后根据Map的key进行 ASCII 码从小到大排序(字典序)
- 排序后,将map的key和value,使用=作为键值对,然后多个字段之间使用&连接在一起。
- 拼接上密钥。map.toString+=key=xxxxx
- 然后进行md5加密,然后将数据返回出去。
- 最后,返回出去加密的值,放到sign键值对里面。
1.1、发送方职责
-
生成 MD5 哈希值
- 对原始数据计算 MD5 哈希值(如文件、消息等)。
-
发送数据包
- 将 原始数据 + MD5 哈希值 一起发送给接收方。
// Java示例:发送方生成MD5
public class Md5Sender {public static String generateMd5(String data) {try {MessageDigest md = MessageDigest.getInstance("MD5");byte[] hashBytes = md.digest(data.getBytes());return bytesToHex(hashBytes); // 转换为十六进制字符串} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);}}private static String bytesToHex(byte[] bytes) {StringBuilder sb = new StringBuilder();for (byte b : bytes) {sb.append(String.format("%02x", b));}return sb.toString();}
}
1.2、接收方职责
-
重新计算 MD5 哈希值
- 对接收到的原始数据重新计算 MD5。
-
验证哈希一致性
- 对比接收到的哈希值与本地计算的哈希值是否一致。
// Java示例:接收方验证MD5
public class Md5Receiver {public static boolean verify(String data, String receivedHash) {String calculatedHash = Md5Sender.generateMd5(data);return calculatedHash.equals(receivedHash);}
}
二、HMAC-SHA256
HMAC-SHA256 是一种对称加密的签名方案,其核心特征是:
✅ 发送方和接收方必须共享同一个密钥
✅ 密钥用于生成和验证消息的哈希值
✅ 密钥的保密性直接决定系统安全性
/*** 生成API请求签名(HMAC-SHA256算法)* @param kvMap 按ASCII顺序排序的参数键值对* @param apiKey 商户API密钥* @return 全大写的签名字符串*/
public static String generateSign(SortedMap<String, String> kvMap, String apiKey) {// 阶段1:构建基础签名字符串StringBuilder sb = new StringBuilder();boolean isFirst = true;for (String key : kvMap.keySet()) {String value = kvMap.get(key);// 过滤规则:// 1. 空值参数不参与签名// 2. 签名参数本身(sign)不参与签名// 3. 严格区分参数名大小写if (value != null && !value.trim().isEmpty() && !"sign".equals(key)) {if (!isFirst) {sb.append("&"); // 参数分隔符}sb.append(key).append("=").append(value);isFirst = false;}}String stringA = sb.toString();System.out.println("[DEBUG] 基础参数字符串: " + stringA);// 阶段2:拼接API密钥String stringSignTemp = stringA + "&key=" + apiKey; // 标准格式结尾System.out.println("[DEBUG] 待加密字符串: " + stringSignTemp);// 阶段3:HMAC-SHA256加密String encodedString = encode(stringSignTemp, apiKey);System.out.println("[DEBUG] 原始签名: " + encodedString);// 最终处理:统一转为大写return encodedString.toUpperCase();
}/*** HMAC-SHA256加密实现* @param stringSignTemp 待加密字符串* @param apiKey 加密密钥* @return 十六进制格式的哈希值*/
private static String encode(String stringSignTemp, String apiKey) {String encodedString = null;try {// 1. 转换API密钥为加密规范SecretKeySpec sks = new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");// 2. 初始化MAC加密器Mac mac = Mac.getInstance("HmacSHA256");mac.init(sks);// 3. 执行加密并转换字节数组为十六进制byte[] encodedBytes = mac.doFinal(stringSignTemp.getBytes(StandardCharsets.UTF_8));encodedString = byte2hex(encodedBytes);} catch (InvalidKeyException | NoSuchAlgorithmException e) {System.err.println("加密失败: " + e.getMessage());}return encodedString;
}/** 字节数组转十六进制字符串 */
private static String byte2hex(byte[] bytes) {StringBuilder hexString = new StringBuilder();for (byte b : bytes) {String hex = Integer.toHexString(0xff & b);if (hex.length() == 1) {hexString.append('0');}hexString.append(hex);}return hexString.toString();
}
2.1 代码执行流程解析
阶段 | 操作 | 技术要点 |
---|---|---|
1 | 参数过滤与排序 | 过滤空值参数和sign 字段,按SortedMap的ASCII顺序拼接键值对 |
2 | 构建待加密字符串 | 固定格式拼接API密钥(&key=xxx ),确保密钥参与签名 |
3 | HMAC-SHA256加密 | 使用API密钥作为HMAC密钥,确保签名与密钥强关联 |
4 | 十六进制编码 | 将二进制哈希值转换为可读字符串 |
5 | 统一大写输出 | 消除大小写差异,符合多数API接口规范 |
2.2 通信双方职责
发送方(客户端)
操作 | 密钥用途 |
---|---|
生成 HMAC 签名 | 使用共享密钥对原始消息计算哈希值,生成签名(HMAC-SHA256(key, message) ) |
发送数据包 | 将原始消息 + HMAC 签名一起发送(如:{data: "...", sign: "xxxx"} ) |
接收方(服务端)
操作 | 密钥用途 |
---|---|
重新计算 HMAC | 使用相同密钥对收到的消息计算哈希值(HMAC-SHA256(key, received_message) ) |
验证签名 | 比较接收到的签名与自己计算的哈希值是否一致 |
处理结果 | 一致 → 数据可信;不一致 → 数据可能被篡改或密钥错误 |
2.3 MAC-SHA256 vs MD5 对比
特性 | HMAC-SHA256 | MD5 |
---|---|---|
算法类型 | 带密钥的哈希消息认证码 | 普通哈希函数 |
输出长度 | 256位(32字节) | 128位(16字节) |
安全性 | 抗碰撞性强,目前无已知漏洞 | 已被证实存在碰撞漏洞 |
密钥依赖 | 必须使用密钥生成签名 | 无需密钥,仅依赖输入数据 |
适用场景 | API签名、金融交易等安全要求高的领域 | 简单校验、非敏感场景(已不建议使用) |
计算性能 | 较慢(设计目的为安全性优先) | 较快 |
标准化 | FIPS 198-1/NIST标准 | RFC 1321(已过时) |
三、RSA
需要公钥和私钥。
在非对称加密体系中,公钥和私钥的使用场景有明确的区分:
-
公钥(Public Key):可公开分享,用于加密数据或验证签名。
-
私钥(Private Key):必须严格保密,用于解密数据或生成签名。
3.1 下面,我们看看签名
/*** 签名 直接调用* @param map* @return*/
public static String signature(Map<String, Object> map){String asciiSort = getAsciiSort(map);Log.d(TAG, "signature: "+asciiSort);return signater(privateKey, asciiSort);}public static String getAsciiSort(Map<String, Object> map) {List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(map.entrySet());// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {return ((String) o1.getKey()).compareToIgnoreCase((String) o2.getKey());}});StringBuilder sb = new StringBuilder();for (Map.Entry<String, Object> infoId : infoIds) {System.out.println("key -->"+infoId.getKey()+",value--->"+infoId.getValue());;sb.append(infoId.getValue());}return sb.toString();
}public static String publicKey="xxxxx";
public static String privateKey="xxxx";
/*** 签名** @param privateKey* 私钥* @param plain_text* 明文* @return*/
public static String signater(String privateKey, String plain_text) {byte[] signed = null;try {Signature Sign = Signature.getInstance("SHA256WithRSA");PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( Base64.decode(privateKey.getBytes(), Base64.NO_WRAP));KeyFactory keyf = KeyFactory.getInstance("RSA");PrivateKey priKey = keyf.generatePrivate(priPKCS8);Sign.initSign(priKey);Sign.update(plain_text.getBytes("UTF-8"));signed = Sign.sign();System.out.println("SHA256withRSA签名后-----》" + Base64.encodeToString(signed, Base64.NO_WRAP));} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {e.printStackTrace();} catch (InvalidKeySpecException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}return Base64.encodeToString(signed, Base64.NO_WRAP);
}
逻辑流程:
- 首先,我们会准备好支付的一些参数,存进Map里面
- 然后signature方法,这个方法里面调用了getAsciiSort方法,这个方法主要是将传入参数按照字段名的 ASCII 码从小到大排序(字典序)然后再进行拼接。
- 然后使用自己的私钥对数据进行加密(生成签名),然后作为参数,发送给接收方。
- 接收方收到以后,使用公钥对签名解密,如果不成功,那么就不处理,如果成功,用自己的私钥解密数据。
3.2、发送方(加密方)
职责与操作
-
获取接收方公钥
- 通过可信渠道(如数字证书、HTTPS握手)获取对方公钥。
-
加密数据
- 使用接收方的公钥加密敏感数据,确保只有接收方可解密。
-
生成签名(可选)
- 如需身份验证,使用自己的私钥对数据生成签名。
所需材料
材料 | 用途 |
---|---|
接收方的公钥 | 加密数据,确保只有接收方能解密 |
发送方的私钥(可选) | 生成数字签名,证明数据来源(需配合签名算法如SHA256withRSA) |
3.3、接收方(解密方)
职责与操作
-
解密数据
- 使用自己的私钥解密收到的加密数据。
-
验证签名(可选)
- 使用发送方的公钥验证签名,确认数据完整性和来源真实性。
-
管理密钥
- 确保私钥安全存储(如HSM硬件模块),定期轮换密钥。
所需材料
材料 | 用途 |
---|---|
接收方的私钥 | 解密发送方用公钥加密的数据 |
发送方的公钥(可选) | 验证发送方签名(需发送方同时提供签名) |
3.4 与 HMAC-SHA256 的关键对比
特性 | SHA256withRSA | HMAC-SHA256 |
---|---|---|
算法类型 | 非对称加密(公钥/私钥) | 对称加密(共享密钥) |
密钥管理 | 公钥可公开分发,私钥严格保密 | 双方必须共享同一密钥 |
性能 | 较慢(RSA 加密计算复杂) | 较快(适合高并发场景) |
安全性 | 依赖 RSA 密钥长度(建议 2048 位以上) | 依赖密钥保密性和哈希强度 |
典型应用 | 数字证书、SSL/TLS、支付网关 | API 签名、JWT 令牌、内部服务通信 |
抗量子计算 | 弱(RSA 易被量子计算破解) | 中(需升级到 HMAC-SHA3 等抗量子算法) |
四、其他
不过,有些公司不用加密,而是使用一个特定key、mid。。。
相关文章:
Android 使用支付接口,需要进行的加密逻辑:MD5、HMAC-SHA256以及RSA
目录 前言MD5HMAC-SHA256RSA其他 前言 不使用加密:支付系统如同「裸奔」,面临数据泄露、资金被盗、法律追责等风险。 正确使用加密:构建「端到端安全防线」,确保交易合法可信,同时满足国际合规要求。 支付系…...
软件工程效率优化:一个分层解耦与熵减驱动的系统框架
软件工程效率优化:一个分层解耦与熵减驱动的系统框架** 摘要 (Abstract) 本报告构建了一个全面、深入、分层的软件工程效率优化框架,旨在超越简单的技术罗列,从根本的价值驱动和熵减原理出发,系统性地探讨提升效率的策略与实践。…...

鸿蒙ArkUI之相对布局容器(RelativeContainer)实战之狼人杀布局,详细介绍相对布局容器的用法,附上代码,以及效果图
在鸿蒙应用开发中,若是遇到布局相对复杂的场景,往往需要嵌套许多层组件,去还原UI图的效果,若是能够掌握相对布局容器的使用,对于复杂的布局场景,可直接减少组件嵌套,且随心所欲完成复杂场景的布…...
详解 Servlet 处理表单数据
Servlet 处理表单数据 1. 什么是 Servlet?2. 表单数据如何发送到 Servlet?2.1 GET 方法2.2 POST 方法 3. Servlet 如何接收表单数据?3.1 获取单个参数:getParameter()示例: 3.2 获取多个参数:getParameterV…...
Spring Cloud Gateway 如何将请求分发到各个服务
前言 在微服务架构中,API 网关(API Gateway)扮演着非常重要的角色。它负责接收客户端请求,并根据预定义的规则将请求路由到对应的后端服务。Spring Cloud Gateway 是 Spring 官方推出的一款高性能网关,支持动态路由、…...
解释器体系结构风格-笔记
解释器(Interpreter)是一种软件设计模式或体系结构风格,主要用于为语言(或表达式)定义其语法、语义,并通过解释器来解析和执行语言中的表达式。解释器体系结构风格广泛应用于编程语言、脚本语言、规则引擎、…...

线程函数库
pthread_create函数 pthread_create 是 POSIX 线程库(pthread)中的一个函数,用于创建一个新的线程。 头文件 #include <pthread.h> 函数原型 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*s…...

[C]基础13.深入理解指针(5)
博客主页:向不悔本篇专栏:[C]您的支持,是我的创作动力。 文章目录 0、总结1、sizeof和strlen的对比1.1 sizeof1.2 strlen1.3 sizeof和strlen的对比 2、数组和指针笔试题解析2.1 一维数组2.2 字符数组2.2.1 代码12.2.2 代码22.2.3 代码32.2.4 …...

OpenCV 图形API(60)颜色空间转换-----将图像从 YUV 色彩空间转换为 RGB 色彩空间函数YUV2RGB()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 将图像从 YUV 色彩空间转换为 RGB。 该函数将输入图像从 YUV 色彩空间转换为 RGB。Y、U 和 V 通道值的常规范围是 0 到 255。 输出图像必须是 8…...
11.原型模式:思考与解读
原文地址:原型模式:思考与解读 更多内容请关注:7.深入思考与解读设计模式 引言 在软件开发中,尤其是当需要创建大量相似对象时,你是否遇到过这样的情况:每次创建新对象时,是否都需要重新初始化一些复杂的…...
深度解析 Java 泛型通配符 `<? super T>` 和 `<? extends T>`
Java 泛型中的通配符 ? 与 super、extends 关键字组合形成的 <? super T> 和 <? extends T> 是泛型系统中最重要的概念之一,也是许多开发者感到困惑的地方。本文将全面剖析它们的语义、使用场景和设计原理。 一、基础概念回顾 1. 泛型通配符 ? ?…...

hbuilderx云打包生成的ipa文件如何上架
使用hbuilderx打包,会遇到一个问题。开发的ios应用,需要上架到app store,因此,就需要APP store的签名证书,并且还需要一个像xcode那样的工具来上架app store。 我们这篇文章说明下,如何在windows电脑&…...

Golang | 位运算
位运算比常规运算快,常用于搜索引擎的筛选功能。例如,数字除以二等价于向右移位,位移运算比除法快。...
天能资管(SkyAi):大数据洞察市场,引领投资新风向
在金融市场的浩瀚海洋中,信息如同灯塔,指引着投资者前行的方向。谁能更准确地把握市场动态和趋势,谁就能在激烈的市场竞争中占据先机。天能资管(SkyAi),作为卡塔尔投资局(QIA)旗下的科技先锋,凭借其强大的大数据处理能力与前沿的技术架构,为全球投资者提供了前所未有的市场洞察…...

产品动态|千眼狼sCMOS科学相机捕获单分子荧光信号
单分子荧光成像技术,作为生物分子动态研究的关键工具,对捕捉微弱信号要求严苛。传统EMCCD相机因成本高昂,动态范围有限,满阱容量低等问题,制约单分子研究成果产出效率。 千眼狼精准把握科研需求与趋势,自研…...
基于大牛直播SDK的Android屏幕扬声器采集推送RTMP技术解析
在移动互联网时代,直播技术的应用越来越广泛,而屏幕采集推送作为直播内容源的重要获取方式之一,也备受关注。本文将基于大牛直播SDK,深入剖析如何实现Android屏幕采集推送RTMP的完整流程,带你领略其背后的技术细节与魅…...
Linux防火墙工具UFW介绍
UFW(Uncomplicated Firewall)是 Ubuntu、Debian 等 Debian 系 Linux 发行版默认的防火墙管理工具,基于 iptables 开发,旨在通过简化的命令行接口(CLI)降低防火墙配置门槛,适合新手和简单场景。 核心目标:让用户无需深入理解 iptables 的 “表 - 链” 结构,通过直观的命…...
k8s 手动续订证书
注意:如果是高可用环境,本文的操作需要在所有控制节点都执行。 查看证书是否过期 kubeadm certs check-expirationkubeadm certs renew可以续订任何特定证书,或者使用子命令all可以续订所有证书: kubeadm certs renew all使用 kubeadm 构建的集群通常会将admin.conf证书复…...
vc++ 如何调用poco库
1. 下载并安装 Poco 库 你可以从 Poco 的官方网站(POCO C Libraries - Simplify C Development )下载其源代码压缩包。下载完成后,按照下面的步骤进行编译和安装: 解压源代码:把下载的压缩包解压到指定目录。配置编译…...

Hot100方法及易错点总结2
本文旨在记录做hot100时遇到的问题及易错点 五、234.回文链表141.环形链表 六、142. 环形链表II21.合并两个有序链表2.两数相加19.删除链表的倒数第n个节点 七、24.两两交换链表中的节点25.K个一组翻转链表(坑点很多,必须多做几遍)138.随机链表的复制148.排序链表 N…...

网络:手写HTTP
目录 一、HTTP是应用层协议 二、HTTP服务器 三、HTTP服务 认识请求中的uri HTTP支持默认首页 响应 功能完善 套接字复用 一、HTTP是应用层协议 HTTP下层是TCP协议,站在TCP的角度看,要提供的服务是HTTP服务。 这是在原来实现网络版计算器时&am…...
C++[类和对象][3]
C[类和对象][3] 赋值运算符的重载(operator) 1.是一个默认成员函数,重载必须为成员函数,用于两个已经存在的对象,(d1d3赋值重载)(Stack d4d1拷贝构造(因为d4未存在,初始化)) 2.建议写成引用返回提高效率,可以连续赋值重载 3.没有写的时候会自动生成,完成值拷贝/浅拷贝对(对于…...

【计算机视觉】CV实战项目 - 基于YOLOv5的人脸检测与关键点定位系统深度解析
基于YOLOv5的人脸检测与关键点定位系统深度解析 1. 技术背景与项目意义传统方案的局限性YOLOv5多任务方案的优势 2. 核心算法原理网络架构改进关键点回归分支损失函数设计 3. 实战指南:从环境搭建到模型应用环境配置数据准备数据格式要求数据目录结构 模型训练配置文…...

【python】如何将python程序封装为cpython的库
python程序在发布时,往往会打包为cpython的库,并且根据应用服务器的不同架构(x86/aarch64),以及python的不同版本,封装的输出类型也是非常多。本文介绍不同架构指定python下的代码打包方式: 首…...
【人工智能】DeepSeek 的开源生态:释放 AI 潜能的社区协同与技术突破
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 DeepSeek 作为中国 AI 领域的先锋,以其高效的混合专家模型(MoE)和彻底的开源策略,在全球 AI 社区掀起波澜。本文深入剖析 DeepSeek 的开…...
【差分隐私】假设检验的视角(高斯差分隐私)
在差分隐私中,假设检验的框架被用来量化攻击者通过机制输出区分两个相邻数据集 S S S 和 S ′ S S′ 的难度。这种区分的根本困难直接反映了隐私保护强度。以下是对问题的详细解释: 1. 假设检验的基本设定 原假设 H 0 H_0 H0:数据集为 …...

计算机组成原理 课后练习
例一: 例二: 1. 原码一位乘 基本原理 原码是一种直接表示数值符号和大小的方式:最高位为符号位(0表示正,1表示负),其余位表示数值的绝对值。原码一位乘的核心思想是逐位相乘,并通…...
pytorch手动调整学习率
文章目录 1. 为什么引入学习率衰减?2. 针对不同层设置不一样的学习率3. 手动更新学习率4. 使用学习率调度器5. 推荐做法 在前面的文章中,介绍了各种学习率。在此,将进行拓展,学习如何手动更新学习率(即不使用pytorch自…...

SVN仓库突然没有权限访问
如果svn仓库突然出现无法访问的情况,提示没有权限,所有账号都是如此,新创建的账号也不行。 并且会突然提示要输入账号密码。 出现这个情况时,大概率库里面的文件有http或者https的字样,因为单独给该文件添加权限导致…...

【Qt】文件
🌈 个人主页:Zfox_ 🔥 系列专栏:Qt 目录 一:🔥 Qt 文件概述 二:🔥 输入输出设备类 三:🔥 文件读写类 四:🔥 文件和目录信息类 五&…...