在线支付系列支付安全之数字签名

Posted 云烟成雨TD

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在线支付系列支付安全之数字签名相关的知识,希望对你有一定的参考价值。

有道无术,术尚可求,有术无道,止于术。

文章目录

信息摘要

信息摘要就是一段数据的特征信息,当数据发生了改变,信息摘要也会发生改变,发送方会将数据和信息摘要一起传给接收方,接收方会根据接收到的数据重新生成一个信息摘要,若此摘要和接收到的摘要相同,则说明数据正确。

信息摘要是由哈希函数生成的

信息摘要原数据通过某个算法生成的一个固定长度的单向Hash散列值。特点:

  • 固定长度:不论原文内容多大,其生成的信息摘要都是固定长度的。
  • 不可重:任何不同的输入数据,都会产生不同的信息摘要。
  • 单向性:即只能由数据生成信息摘要,不能由信息摘要还原数据。

摘要算法

摘要算法又称哈希算法、散列算法。通过哈希函数,计算出数据的哈希值。

常用算法有:

  • MD:消息摘要算法
  • SHA:安全散列算法
  • Mac:消息认证码算法
算法输出长度(位)输出长度(字节)
SM3256 bits32 bytes
MD5128 bits16 bytes
SHA-1160 bits20 bytes
RipeMD-160160 bits20 bytes
SHA-256256 bits32 bytes
SHA-512512 bits64 bytes

基于JDK 的摘要算法实现:

    private static String getMd(String message) throws NoSuchAlgorithmException 
        // 获取消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        // 更新摘要,传入原文数据
        messageDigest.update(message.getBytes(StandardCharsets.UTF_8));
        // 运行获取摘要值
        byte[] result = messageDigest.digest();
        return Base64.getEncoder().encodeToString(result);
    

数据完整性

可以使用信息摘要保证数据完整性,防止被篡改。

工作流程:

  1. 小明将信息进行摘要计算,将摘要标记在情书中发送给小红
  2. 小红收到信件后,对信息计算摘要,对比自己计算的摘要是否和情书中的一致,若不一致,则说明被篡改过,内容不可信

实现代码:

    public static void main(String[] args) throws Exception 
        // 获取信息摘要
        String message = "爱老虎油~";// 原文
        String md = getMd(message);

        // 验证数据完整性
        String sendUpdateMsg = "爱老虎油~哈哈";// 被篡改
        String mdUpdate = getMd(sendUpdateMsg);
        if (md.equals(mdUpdate))
            System.out.println("摘要信息一致,未被篡改");
        else 
            System.out.println("摘要信息不一致,被篡改");
        

        String sendMsg = "爱老虎油~";// 未篡改
        String sendMd = getMd(sendMsg);
        if (md.equals(sendMd))
            System.out.println("摘要信息一致,未被篡改");
        else 
            System.out.println("摘要信息不一致,被篡改");
        
    

数字签名

上述流程还存在缺陷,如果情书被非法拦截,将篡改后的摘要附在信件上,小红收到后,使用被篡改的摘要进行对比,结果始终会一致,并不能发现数据已被篡改。

这个时候需要加入非对称加密,使用数字签名技术。

数字签名(Digital Signature),一般是附加在某一电子文档中的一组特定的符号或代码,用于表示签发者的身份以及签发者对电子文档的认可,并能被接收者用来验证该电子文档在传输过程中是否被篡改或伪造。

一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。签名算法或签名密钥是秘密的,只有签名人掌握。验证算法是公开的,以便他人进行验证。

基于密码学的数字签名有着如下的优势:

  • 消息源认证性:数字签名可以表示签发者的身份,也就是说具有消息源认证性。

  • 不可否认性:数字签名生成时需要输入签名者私钥。换句话说,数字签名对应唯一签名主体,并且签名者需要承担不可推卸的责任,即数字签名可以实现不可否认性。

  • 消息完整性:数字签名可以检查电子文档在传输过程中是否被篡改或伪造,即保障消息完整性。

签名流程

首先生成非对称秘钥。

签名流程如下:

  1. 小明使用摘要算法对原文进行摘要计算,生成数据指纹(摘要)。
  2. 使用私钥将摘要进行加密,得到数字签名,并将其附在文档中,发送给小红

验签流程

验签流程如下:

  1. 小红取出签名,使用公钥解密摘要,得要小明的摘要。
  2. 对文档进行摘要计算,对比两个摘要是否一致。

还可以加入对称算法,对原文数据进行加密,安全性更高。

如果第三方冒充发送方发送了一个文件,因为接收方在对数字签名进行解密时使用的是发送方的公开密钥,只要第三方不知道发送方的私用密钥,解密出来的数字摘要与计算机计算出来的新摘要必然是不同的。这就提供了一个安全的确认发送方身份的方法。

实现代码

使用JDK实现数字签名:

        // 原文
        String message = "爱老虎油~";
        byte[] result = message.getBytes(StandardCharsets.UTF_8);
        String md = Base64.getEncoder().encodeToString(result);
        System.out.println("原文数据:" + md);

        // 2. 签名
        // 将私钥字符串转为私钥对象
        String privateKeyBase64 = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAwQLaA6koZPMJ0nh+Vvo6P1CZvBUA2bAufTMGRkg16kYR5ae85HGW15qRIrTc7ZeC0dIbJbivnbQoG28oRx0UAQIDAQABAkEAmQgjx6dFadTxQrlaoqe/qxfC7MdSZ0czdP0RyoPSi64k942Rj46in5zDw60SynysVvMmVinxPPFS+6rv5kIWaQIhAPMXt8l7jPGmF6pYTmE2cx9OjB4Q9q8uhfIKZc0E7T9HAiEAy0JkNwSVxK76I9mFZpoRSfH37xAqLyUKhdCRAn5fBncCIQDGw3Pg6Ia750SeYgnkbrL+vCjRRKmPX4jh+SJ32jlqbQIgIXe6FpELtAn3qAV+AKnnpNxRraxktcSMmgIAjn+OV/sCIFlpFzkKXkW2cTv6oVHbFkqgrDPhTAXbshsa/U301Oac";
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBase64));
        KeyFactory privateKeyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKeyByValue = privateKeyFactory.generatePrivate(privateKeySpec);
        // 实例化签名对象
        Signature signature = Signature.getInstance("MD5withRSA");
        // 初始化 传入私钥
        signature.initSign(privateKeyByValue);
        // 更新签名,传入摘要值
        signature.update(Base64.getDecoder().decode(md));
        // 签名,获取签名
        byte[] enSign = signature.sign();
        String singData = Base64.getEncoder().encodeToString(enSign);
        System.out.println("签名数据:" + singData);


        // 验签
        // 将公钥字符串转为公钥对象
        String publicKeyBase64 = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMEC2gOpKGTzCdJ4flb6Oj9QmbwVANmwLn0zBkZINepGEeWnvORxlteakSK03O2XgtHSGyW4r520KBtvKEcdFAECAwEAAQ==";
        X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKeyByValue = keyFactory.generatePublic(bobPubKeySpec);
        // 实例化 签名对象
        Signature verifySignature = Signature.getInstance("MD5withRSA");
        // 初始化,传入公钥
        verifySignature.initVerify(publicKeyByValue);
        // 更新签名,传入签名前摘要数据
        verifySignature.update(Base64.getDecoder().decode(md));
        // 验证,传入签名数据
        boolean verify = verifySignature.verify(Base64.getDecoder().decode(singData));
        System.out.println("验签结果:" + verify);

以上是关于在线支付系列支付安全之数字签名的主要内容,如果未能解决你的问题,请参考以下文章

企业支付开发基础 | 微信支付 | 支付安全(证书/秘钥/签名)

企业支付开发基础 | 微信支付 | 支付安全(证书/秘钥/签名)

iOS 安全规范指南之对请求参数进行签名请求参数按照ASCII码从小到大排序拼接加密(采用递归的方式进行实现)应用案例:条码支付综合前置平台申请退款

iOS 安全规范指南之对请求参数进行签名请求参数按照ASCII码从小到大排序拼接加密(采用递归的方式进行实现)应用案例:条码支付综合前置平台申请退款

iOS开发之支付宝集成

iOS开发之支付宝集成