Android 使用支付接口,需要进行的加密逻辑:MD5、HMAC-SHA256以及RSA

06-01 1579阅读

目录

  1. 前言
  2. MD5
  3. HMAC-SHA256
  4. RSA
  5. 其他

前言

不使用加密​​:支付系统如同「裸奔」,面临数据泄露、资金被盗、法律追责等风险。

正确使用加密​​:构建「端到端安全防线」,确保交易合法可信,同时满足国际合规要求。

支付系统作为金融基础设施,加密不是可选项,而是业务存续的必要条件。任何加密方案的疏漏都可能导致系统性风险、。

在对接第三方支付的时候,最麻烦的问题是什么???没错,就是加密,每个第三方的,可能都还不一样,导致我们开发时间变长,这里我们就来梳理一下最常见的三种加密方式。


一、md5

MD5 是一种​​哈希函数​​,用于生成数据的唯一指纹(哈希值)。其特点是:

  • ✅ ​​快速计算​​:适合大数据量校验
  • ❌ ​​已被破解​​:易发生哈希碰撞(不同数据生成相同哈希)
  • ❌ ​​无密钥依赖​​:无法验证数据来源真实性
     /**
         * 生成签名
         * @param map
         * @return
         */
        public static String getSignToken(Map map) {
            String result = "";
            try {
                List infoIds = new ArrayList(map.entrySet());
                // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
                Collections.sort(infoIds, new Comparator() {
                    public int compare(Map.Entry o1, Map.Entry o2) {
                        return (o1.getKey()).toString().compareTo(o2.getKey());
                    }
                });
                // 构造签名键值对的格式
                StringBuilder sb = new StringBuilder();
                for (Map.Entry 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;
        }
    

    加密流程:

    1. 拿到所有参数的Map
    2. 然后根据Map的key进行 ASCII 码从小到大排序(字典序)
    3. 排序后,将map的key和value,使用=作为键值对,然后多个字段之间使用&连接在一起。
    4. 拼接上密钥。map.toString+=key=xxxxx
    5. 然后进行md5加密,然后将数据返回出去。
    6. 最后,返回出去加密的值,放到sign键值对里面。

    ​​1.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、接收方职责​​

    1. ​​重新计算 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 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),确保密钥参与签名
    3HMAC-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-SHA256MD5
    ​​算法类型​​带密钥的哈希消息认证码普通哈希函数
    ​​输出长度​​256位(32字节)128位(16字节)
    ​​安全性​​抗碰撞性强,目前无已知漏洞已被证实存在碰撞漏洞
    ​​密钥依赖​​必须使用密钥生成签名无需密钥,仅依赖输入数据
    ​​适用场景​​API签名、金融交易等安全要求高的领域简单校验、非敏感场景(已不建议使用)
    ​​计算性能​​较慢(设计目的为安全性优先)较快
    ​​标准化​​FIPS 198-1/NIST标准RFC 1321(已过时)

    三、RSA

    需要公钥和私钥。

    Android 使用支付接口,需要进行的加密逻辑:MD5、HMAC-SHA256以及RSA
    (图片来源网络,侵删)

    在非对称加密体系中,公钥和私钥的使用场景有明确的区分:

    • ​公钥(Public Key)​​:可公开分享,用于​​加密数据​​或​​验证签名​​。

      Android 使用支付接口,需要进行的加密逻辑:MD5、HMAC-SHA256以及RSA
      (图片来源网络,侵删)
    • ​​私钥(Private Key)​​:必须严格保密,用于​​解密数据​​或​​生成签名​​。

      3.1 下面,我们看看签名

      /**
       * 签名  直接调用
       * @param map
       * @return
       */
      public static String signature(Map map){
          String asciiSort = getAsciiSort(map);
          Log.d(TAG, "signature: "+asciiSort);
          return signater(privateKey, asciiSort);
      }
      public static String getAsciiSort(Map map) {
          List infoIds = new ArrayList(map.entrySet());
          // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
          Collections.sort(infoIds, new Comparator() {
              public int compare(Map.Entry o1, Map.Entry o2) {
                  return ((String) o1.getKey()).compareToIgnoreCase((String) o2.getKey());
              }
          });
          StringBuilder sb = new StringBuilder();
          for (Map.Entry 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 block
              e.printStackTrace();
          } catch (UnsupportedEncodingException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
          return Base64.encodeToString(signed, Base64.NO_WRAP);
      }
      

      逻辑流程:

      1. 首先,我们会准备好支付的一些参数,存进Map里面
      2. 然后signature方法,这个方法里面调用了getAsciiSort方法,这个方法主要是将传入参数按照字段名的 ASCII 码从小到大排序(字典序)然后再进行拼接。
      3. 然后使用​​自己的私钥​​对数据进行加密(生成签名),然后作为参数,发送给接收方。
      4. 接收方收到以后,使用公钥对签名解密,如果不成功,那么就不处理,如果成功,用​​自己的私钥​​解密数据。

      ​3.2、发送方(加密方)​​

      ​​职责与操作​​
      1. ​​获取接收方公钥​​

        • 通过可信渠道(如数字证书、HTTPS握手)获取对方公钥。
        • ​​加密数据​​

          • 使用接收方的公钥加密敏感数据,确保只有接收方可解密。
          • ​​生成签名(可选)​​

            • 如需身份验证,使用​​自己的私钥​​对数据生成签名。
      ​​所需材料​​
      ​​材料​​​​用途​​
      接收方的公钥加密数据,确保只有接收方能解密
      发送方的私钥(可选)生成数字签名,证明数据来源(需配合签名算法如SHA256withRSA)

      ​​3.3、接收方(解密方)​​

      ​​职责与操作​​
      1. ​​解密数据​​

        • 使用自己的私钥解密收到的加密数据。
        • ​​验证签名(可选)​​

          • 使用发送方的公钥验证签名,确认数据完整性和来源真实性。
          • ​​管理密钥​​

            • 确保私钥安全存储(如HSM硬件模块),定期轮换密钥。
      ​​所需材料​​
      ​​材料​​​​用途​​
      接收方的私钥解密发送方用公钥加密的数据
      发送方的公钥(可选)验证发送方签名(需发送方同时提供签名)

      3.4 与 HMAC-SHA256 的关键对比​​

      ​​特性​​SHA256withRSAHMAC-SHA256
      ​​算法类型​​非对称加密(公钥/私钥)对称加密(共享密钥)
      ​​密钥管理​​公钥可公开分发,私钥严格保密双方必须共享同一密钥
      ​​性能​​较慢(RSA 加密计算复杂)较快(适合高并发场景)
      ​​安全性​​依赖 RSA 密钥长度(建议 2048 位以上)依赖密钥保密性和哈希强度
      ​​典型应用​​数字证书、SSL/TLS、支付网关API 签名、JWT 令牌、内部服务通信
      ​​抗量子计算​​弱(RSA 易被量子计算破解)中(需升级到 HMAC-SHA3 等抗量子算法)

      四、其他

      不过,有些公司不用加密,而是使用一个特定key、mid。。。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码