老項目重構支付寶部分代碼整合支付寶新的sdk時發現驗籤老是失敗,才發現是open_verify最後的參數傳輸問題。而open_sign一樣如此。本文主要說明open_verify的解決方式和代碼解析。而問題的解決方式也是修改最後的加密類型參數,解決方式代碼以下:php
// 將最後的常量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 ] )
複製代碼
參數註釋bash
data 之前用來生成簽名的數據字符串。php7
signature 原始二進制字符串,經過openssl_sign()或相似的函數生成。函數
pub_key_idui
signature_alg加密
官方文檔給出的signature_alg參數能夠爲int或者string類型,int類型直接調用對應的枚舉值,string則是openssl_get_md_methods函數返回的可用字符串,調用openssl_get_md_methods方法打印參數以下,而這些字符串也是對應加密方式的摘要信息,後文源碼中可能會看的對函數調用稍微明白那麼一丟丟。spa
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擴展的問題上。那下面來看看對應的源碼去發現問題出如今哪吧。code
openssl_verify源碼中有這樣一段,若是參數method爲string類型的時候,調用openssl庫的EVP_get_digestbyname方法,在網上查看了下此方法的做用,主要是根據摘要信息返回 EVP_MD結構,而EVP_get_digestbyname方法因爲是openssl庫源代碼而且對C語言知之甚少,熊某就沒去查看, 只是瞭解php代碼調用背後的一些處理邏輯,有興趣的能夠看看openssl庫的代碼實現。ip
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函數,果真發現了問題所在。源碼以下:
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;
}
複製代碼
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少了幾個條件,在此也但願你們發現問題的時候,能夠先去解決問題,而後有興趣的話能夠去查看源代碼分析下問題所致使的緣由。