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


Posted in PHP onJuly 25, 2018

背景与问题解决方式

老项目重构支付宝部分代码整合支付宝新的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少了几个条件,在此也希望大家发现问题的时候,可以先去解决问题,然后有兴趣的话可以去查看源代码分析下问题所导致的原因。

PHP 相关文章推荐
PHP脚本的10个技巧(7)
Oct 09 PHP
关于Intype一些小问题的解决办法
Mar 28 PHP
PHP 巧用数组降低程序的时间复杂度
Jan 01 PHP
php解决约瑟夫环示例
Apr 09 PHP
laravel 5 实现模板主题功能(续)
Mar 02 PHP
PHP获取远程图片并保存到本地的方法
May 12 PHP
php实现统计目录文件大小的函数
Dec 25 PHP
理解php依赖注入和控制反转
May 11 PHP
PHP JWT初识及其简单示例
Oct 10 PHP
在 PHP 和 Laravel 中使用 Traits的方法
Nov 13 PHP
Laravel 5+ .env环境配置文件详解
Apr 06 PHP
利用ajax+php实现商品价格计算
Mar 31 PHP
PHP实现的多维数组去重操作示例
Jul 21 #PHP
php实现生成PDF文件的方法示例【基于FPDF类库】
Jul 21 #PHP
记录Yii2框架开发微信公众号遇到的问题及解决方法
Jul 20 #PHP
ThinkPHP 3使用OSS的方法
Jul 19 #PHP
php命令行写shell实例详解
Jul 19 #PHP
php工具型代码之印章抠图
Jul 18 #PHP
php压缩文件夹最新版
Jul 18 #PHP
You might like
用PHP和ACCESS写聊天室(三)
2006/10/09 PHP
IIS6的PHP最佳配置方法
2007/03/19 PHP
php获取本地图片文件并生成xml文件输出具体思路
2013/04/27 PHP
php去除HTML标签实例
2013/11/06 PHP
基于PHP+jQuery+MySql实现红蓝(顶踩)投票代码
2015/08/25 PHP
PHP实现中国公民身份证号码有效性验证示例代码
2017/05/03 PHP
Laravel 5.4重新登录实现跳转到登录前页面的原理和方法
2017/07/13 PHP
Windows平台实现PHP连接SQL Server2008的方法
2017/07/26 PHP
多浏览器兼容的获取元素和鼠标的位置的js代码
2009/12/15 Javascript
Jquery中children与find之间的区别详细解析
2013/11/29 Javascript
jQuery操作cookie方法实例教程
2014/11/25 Javascript
js实现精美的银灰色竖排折叠菜单
2015/05/16 Javascript
JavaScript知识点总结之如何提高性能
2016/01/15 Javascript
Javascript中匿名函数的调用与写法实例详解(多种)
2016/01/26 Javascript
Angularjs结合Bootstrap制作的一个TODO List
2016/08/18 Javascript
浅谈jQuery中的$.extend方法来扩展JSON对象
2017/02/12 Javascript
bootstrap table动态加载数据示例代码
2017/03/25 Javascript
vue.js国际化 vue-i18n插件的使用详解
2017/07/07 Javascript
Javascript中this关键字指向问题的测试与详解
2017/08/11 Javascript
Angular 向组件传递模板的两种方法
2018/02/23 Javascript
在vue中,v-for的索引index在html中的使用方法
2018/03/06 Javascript
详细介绍Ruby中的正则表达式
2015/04/10 Python
python实现全盘扫描搜索功能的方法
2019/02/14 Python
Python Django 添加首页尾页上一页下一页代码实例
2019/08/21 Python
Kiwi.com中国:找到特价机票并发现新目的地
2019/10/27 全球购物
建筑工程技术应届生求职信
2013/11/17 职场文书
商务英语求职自荐信范文
2013/12/24 职场文书
前台文员个人求职信范文
2014/01/05 职场文书
十佳班主任事迹材料
2014/01/18 职场文书
2014办公室副主任四风对照检查材料思想汇报
2014/09/20 职场文书
2014财务年终工作总结
2014/12/08 职场文书
2015年妇幼卫生工作总结
2015/05/23 职场文书
孝女彩金观后感
2015/06/10 职场文书
天气温馨提示语
2015/07/14 职场文书
中学图书馆工作总结
2015/08/11 职场文书
基于HTML十秒做出淘宝页面
2021/10/24 HTML / CSS