Detailed explanation of PHP version compatible openssl call parameters

  • 2021-10-24 19:14:25
  • OfStack

Background and problem solving methods

When the old project reconstructed some codes of Alipay and integrated Alipay's new sdk, it was found that the signature verification always failed, only to find that it was the last parameter transmission problem of open_verify. The same is true of open_sign. This paper mainly explains the solution and code analysis of open_verify. The solution to the problem is to modify the final encryption type parameter. The solution code is as follows:


//  Will the last constant OPENSSL_ALGO_SHA256 Modify to a string 
openssl_verify($data, base64_decode($sign), $res, "sha256WithRSAEncryption");

Official document explanation

The above only said the emergence of the problem and the corresponding solution. If you are interested in continuing to understand this function, you can continue to read it. First, look at the explanation of this function in the official document.


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

Parameter comment

data

The data string previously used to generate signatures.

signature

The original binary string, generated by openssl_sign () or similar function.

pub_key_id

resource-1 key returned by the openssl_get_publickey () function.

string-1 key in PEM format, for example, "-BEGIN PUBLIC KEY-MIIBCgK …"

signature_alg

int-1Signature Algorithms of the following signature algorithm.

string-The available string returned by the openssl_get_md_methods () function, such as "sha1WithRSAEncryption" or "sha512".
signature_alg parameters given in official documents can be int or string type, int type directly calls the corresponding enumeration value, string is the available string returned by openssl_get_md_methods function, calling openssl_get_md_methods method to print parameters as follows, and these strings are also the summary information corresponding to encryption mode, which may be seen in the source code later.

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
)

It can be seen that the function is compatible with both modes, but why is the php version compatible? In the case of openssl library version 1, the next reason should only be left with php extension. Let's take a look at the corresponding source code to find out where the problem appears.

Function source code

openssl_verify function source code

openssl_verify source code has such a section, if the parameter method is string type, call openssl library EVP_get_digestbyname method, in the network to see the role of this method, mainly according to the summary information return
EVP_MD structure, and EVP_get_digestbyname method is the source code of openssl library and knows little about C language, so Xiong didn't check it.
Just understand the php code call behind some of the processing logic, interested can look at the openssl library code implementation.


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;
  }

It turned out to be an enumeration value problem?

At first, I thought that php version 5.3 would be the limitation of method parameter type. 1 Looking at the source code, I found that the implementation logic of openssl_verify function is 1, all of which detect method parameter type, so the problem does not appear on the parameter type. Then I looked at the php_openssl_get_evp_md_from_algo function called for long type. The source code is as follows:

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;
}

The above source code can clearly find the problem, With the upgrade of php version, the calling conditions corresponding to its openssl extension have also increased a lot. Finally, the source code that leads to the above problems is only switch … case lacks a few conditions. I also hope that when you find the problem, you can solve the problem first, and then if you are interested, you can check the causes of the problem under source code analysis.


Related articles: