• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    详解PHP版本兼容之openssl调用参数

    背景与问题解决方式

    老项目重构支付宝部分代码整合支付宝新的sdk时发现验签总是失败,才发现是open_verify最后的参数传输问题。而open_sign同样如此。本文主要说明open_verify的解决方式和代码解析。而问题的解决方式也是修改最后的加密类型参数,解决方式代码如下:

    // 将最后的常量OPENSSL_ALGO_SHA256修改成字符串
    openssl_verify($data, base64_decode($sign), $res, "sha256WithRSAEncryption");
    

    官方文档解释

    上面只说了问题的出现与对应的解决方式,如果有兴趣继续了解该函数的,可以继续往下读,首先来看下官方文档对此函数的解释。

    int openssl_verify ( string $data , string $signature , mixed $pub_key_id [, mixed $signature_alg = OPENSSL_ALGO_SHA1 ] )

    参数注释

    data

    以前用来生成签名的数据字符串。

    signature

    原始二进制字符串,通过openssl_sign()或类似的函数生成。

    pub_key_id

    resource - 一个密钥, 通过 openssl_get_publickey() 函数返回。

    string - 一个 PEM 格式的密钥, 比如, “—–BEGIN PUBLIC KEY—– MIIBCgK…”

    signature_alg

    int - 以下签名算法之一Signature Algorithms.

    string - 由openssl_get_md_methods()函数返回的可用字符串,比如, “sha1WithRSAEncryption” 或者 “sha512”.
    官方文档给出的signature_alg参数可以为int或者string类型,int类型直接调用对应的枚举值,string则是openssl_get_md_methods函数返回的可用字符串,调用openssl_get_md_methods方法打印参数如下,而这些字符串也是对应加密方式的摘要信息,后文源码中可能会看的对函数调用稍微明白那么一丢丢。

    Array
    (
    [0] => DSA
    [1] => DSA-SHA
    [2] => DSA-SHA1
    [3] => DSA-SHA1-old
    [4] => DSS1
    [5] => GOST 28147-89 MAC
    [6] => GOST R 34.11-94
    [7] => MD4
    [8] => MD5
    [9] => MDC2
    [10] => RIPEMD160
    [11] => RSA-MD4
    [12] => RSA-MD5
    [13] => RSA-MDC2
    [14] => RSA-RIPEMD160
    [15] => RSA-SHA
    [16] => RSA-SHA1
    [17] => RSA-SHA1-2
    [18] => RSA-SHA224
    [19] => RSA-SHA256
    [20] => RSA-SHA384
    [21] => RSA-SHA512
    [22] => SHA
    [23] => SHA1
    [24] => SHA224
    [25] => SHA256
    [26] => SHA384
    [27] => SHA512
    [28] => dsaEncryption
    [29] => dsaWithSHA
    [30] => dsaWithSHA1
    [31] => dss1
    [32] => ecdsa-with-SHA1
    [33] => gost-mac
    [34] => md4
    [35] => md4WithRSAEncryption
    [36] => md5
    [37] => md5WithRSAEncryption
    [38] => md_gost94
    [39] => mdc2
    [40] => mdc2WithRSA
    [41] => ripemd
    [42] => ripemd160
    [43] => ripemd160WithRSA
    [44] => rmd160
    [45] => sha
    [46] => sha1
    [47] => sha1WithRSAEncryption
    [48] => sha224
    [49] => sha224WithRSAEncryption
    [50] => sha256
    [51] => sha256WithRSAEncryption
    [52] => sha384
    [53] => sha384WithRSAEncryption
    [54] => sha512
    [55] => sha512WithRSAEncryption
    [56] => shaWithRSAEncryption
    [57] => ssl2-md5
    [58] => ssl3-md5
    [59] => ssl3-sha1
    [60] => whirlpool
    )

    由此也可看出函数是兼容两种模式的,但是为什么php版本会有兼容问题么?在openssl库版本是一致的情况下,接下来的原因应该只遗留在php扩展的问题上。那下面来看看对应的源码去发现问题出现在哪吧。

    函数源码

    openssl_verify函数源码

    openssl_verify源码中有这样一段,如果参数method为string类型的时候,调用openssl库的EVP_get_digestbyname方法,在网上查看了下此方法的作用,主要是根据摘要信息返回
    EVP_MD结构,而EVP_get_digestbyname方法由于是openssl库源代码并且对C语言知之甚少,熊某就没去查看,
    只是了解php代码调用背后的一些处理逻辑,有兴趣的可以看看openssl库的代码实现。

    if (method == NULL || Z_TYPE_P(method) == IS_LONG) {
        if (method != NULL) {
          signature_algo = Z_LVAL_P(method);
        }
        mdtype = php_openssl_get_evp_md_from_algo(signature_algo);
      } else if (Z_TYPE_P(method) == IS_STRING) {
        mdtype = EVP_get_digestbyname(Z_STRVAL_P(method));
      } else {
        php_error_docref(NULL, E_WARNING, "Unknown signature algorithm.");
        RETURN_FALSE;
      }

    原来是枚举值的问题?

    一开始本人以为php5.3版本会是method参数类型的限制,一看源代码才发现,openssl_verify函数的实现逻辑是一致的,都是检测method参数类型,那么问题就不出现在参数类型上,然后我查看了参数为long类型是所调用的php_openssl_get_evp_md_from_algo函数,果然发现了问题所在。源码如下:

    php5.3.27

    static EVP_MD * php_openssl_get_evp_md_from_algo(long algo) { /* {{{ */
      EVP_MD *mdtype;
    
      switch (algo) {
        case OPENSSL_ALGO_SHA1:
          mdtype = (EVP_MD *) EVP_sha1();
          break;
        case OPENSSL_ALGO_MD5:
          mdtype = (EVP_MD *) EVP_md5();
          break;
        case OPENSSL_ALGO_MD4:
          mdtype = (EVP_MD *) EVP_md4();
          break;
    #ifdef HAVE_OPENSSL_MD2_H
        case OPENSSL_ALGO_MD2:
          mdtype = (EVP_MD *) EVP_md2();
          break;
    #endif
        case OPENSSL_ALGO_DSS1:
          mdtype = (EVP_MD *) EVP_dss1();
          break;
        default:
          return NULL;
          break;
      }
      return mdtype;
    }

    php7.1.18

    static EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { /* {{{ */
      EVP_MD *mdtype;
    
      switch (algo) {
        case OPENSSL_ALGO_SHA1:
          mdtype = (EVP_MD *) EVP_sha1();
          break;
        case OPENSSL_ALGO_MD5:
          mdtype = (EVP_MD *) EVP_md5();
          break;
        case OPENSSL_ALGO_MD4:
          mdtype = (EVP_MD *) EVP_md4();
          break;
    #ifdef HAVE_OPENSSL_MD2_H
        case OPENSSL_ALGO_MD2:
          mdtype = (EVP_MD *) EVP_md2();
          break;
    #endif
    #if OPENSSL_VERSION_NUMBER  0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
        case OPENSSL_ALGO_DSS1:
          mdtype = (EVP_MD *) EVP_dss1();
          break;
    #endif
        case OPENSSL_ALGO_SHA224:
          mdtype = (EVP_MD *) EVP_sha224();
          break;
        case OPENSSL_ALGO_SHA256:
          mdtype = (EVP_MD *) EVP_sha256();
          break;
        case OPENSSL_ALGO_SHA384:
          mdtype = (EVP_MD *) EVP_sha384();
          break;
        case OPENSSL_ALGO_SHA512:
          mdtype = (EVP_MD *) EVP_sha512();
          break;
        case OPENSSL_ALGO_RMD160:
          mdtype = (EVP_MD *) EVP_ripemd160();
          break;
        default:
          return NULL;
          break;
      }
      return mdtype;
    }

    由上面源代码可以很清晰的发现问题所在,随着php版本的升级,其所在的openssl扩展对应的调用条件也增加了很多,最后导致上述问题的源码也只是switch…case少了几个条件,在此也希望大家发现问题的时候,可以先去解决问题,然后有兴趣的话可以去查看源代码分析下问题所导致的原因。

    您可能感兴趣的文章:
    • 使用openssl 生成免费证书的方法步骤
    • 升级 PHP7.1 后 openssl 解密 mcrypt AES 数据不兼容问题的处理方法
    • PHP7.1中使用openssl替换mcrypt的实例详解
    • linux环境下安装PHP的OpenSSL扩展的方法讲解
    • php7安装openssl扩展方法
    • 详解Linux(centos7)下安装OpenSSL安装图文方法
    • Linux下Nginx安装的方法(pcre和openssl)
    • Openssl实现双向认证教程(附服务端客户端代码)
    上一篇:PHP实现的多维数组去重操作示例
    下一篇:PHP上传文件及图片到七牛的方法
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    详解PHP版本兼容之openssl调用参数 详解,PHP,版本,兼容,之,openssl,