php获取证书序列号时openssl_x509_parse内存泄漏的替代的方案


// 内存泄漏问题说明:
//     openssl_x509_parse疑似有内存泄漏,暂不清楚原因,可能和php、openssl版本有关,估计有bug。
//     windows下试过php5.4+openssl0.9.8,php7.0+openssl1.0.2都有这问题。mac下试过也有问题。
//     也有别人汇报过bug:https://bugs.php.net/bug.php?id=71519。
//
// 替代解决方案:
//     方案1. 所有调用openssl_x509_parse的地方都是为了获取证书序列号,可以尝试把证书序列号+证书/key以别的方式保存,
//            从其他地方(比如数据库)读序列号,而不直接从证书文件里读序列号。
//     方案2. 代码改成执行脚本的方式执行,这样执行完一次保证能释放掉所有内存。
//     方案3. 改用下面的CertSerialUtil取序列号,
//            此方法仅稍微用了几个证书做过测试,不保证没bug。如发现有bug或者可优化的地方可自行修改代码。
//            注意用了bcmath的方法,*nix下编译时需要 --enable-bcmath。http://php.net/manual/zh/bc.installation.php


使用样例:

$x509data = file_get_contents ( "d:/certs/xxx.cer" );
// $resource = openssl_x509_read ( $x509data );
// $certdata = openssl_x509_parse ( $resource ); //<=这句尼玛内存泄漏啊根本释放不掉啊啊啊啊啊啊啊
// echo $certdata ['serialNumber']; //<=就是需要这个数据啦
// echo $x509data;
// unset($certdata); //<=没有什么用
// openssl_x509_free($resource); //<=没有什么用x2
echo CertSerialUtil::getSerial ( $x509data, $errMsg ) . "\n";




class CertSerialUtil {
	 
	private static function bytesToInteger($bytes) {
		$val = 0;
		for($i = 0; $i < count ( $bytes ); $i ++) {
// 			$val += (($bytes [$i] & 0xff) << (8 * (count ( $bytes ) - 1 - $i)));
			$val += $bytes [$i] * pow(256, count ( $bytes ) - 1 - $i);
// 			echo $val . "
\n"; } return $val; } private static function bytesToBigInteger($bytes) { $val = 0; for($i = 0; $i < count ( $bytes ); $i ++) { $val = bcadd($val, bcmul($bytes [$i], bcpow(256, count ( $bytes ) - 1 - $i))); // echo $val . "
\n"; } return $val; } private static function toStr($bytes) { $str = ''; foreach($bytes as $ch) { $str .= chr($ch); } return $str; } public static function getSerial($fileData, &$errMsg) { // $fileData = str_replace('\n','',$fileData); // $fileData = str_replace('\r','',$fileData); $start = "-----BEGIN CERTIFICATE-----"; $end = "-----END CERTIFICATE-----"; $data = trim ( $fileData ); if (substr ( $data, 0, strlen ( $start ) ) != $start || substr ( $data, strlen ( $data ) - strlen ( $end ) ) != $end) { // echo $fileData; $errMsg = "error pem data"; return false; } $data = substr ( $data, strlen ( $start ), strlen ( $data ) - strlen ( $end ) - strlen ( $start ) ); $bindata = base64_decode ( $data ); $bindata = unpack ( 'C*', $bindata ); $byte = array_shift ( $bindata ); if ($byte != 0x30) { $errMsg = "1st tag " . $byte . " is not 30"; return false; } $length = CertSerialUtil::readLength ( $bindata ); $byte = array_shift ( $bindata ); if ($byte != 0x30) { $errMsg = "2nd tag " . $byte . " is not 30"; return false; } $length = CertSerialUtil::readLength ( $bindata ); $byte = array_shift ( $bindata ); // echo $byte . "
\n"; if ($byte == 0xa0) { //version tag. $length = CertSerialUtil::readLength ( $bindata ); CertSerialUtil::readData ( $bindata, $length ); $byte = array_shift ( $bindata ); } // echo $byte . "
\n"; if ($byte != 0x02) { //x509v1 has no version tag, x509v3 has. $errMsg = "4th/3rd tag " . $byte . " is not 02"; return false; } $length = CertSerialUtil::readLength ( $bindata ); $serial = CertSerialUtil::readData ( $bindata, $length ); // echo bin2hex(CertSerialUtil::toStr( $serial )); return CertSerialUtil::bytesToBigInteger($serial); } private static function readLength(&$bindata) { $byte = array_shift ( $bindata ); if ($byte < 0x80) { $length = $byte; } else { $lenOfLength = $byte - 0x80; for($i = 0; $i < $lenOfLength; $i ++) { $lenBytes [] = array_shift ( $bindata ); } $length = CertSerialUtil::bytesToInteger ( $lenBytes ); } return $length; } private static function readData(&$bindata, $length) { $data = array (); for($i = 0; $i < $length; $i ++) { $data [] = array_shift ( $bindata ); } return $data; } }


你可能感兴趣的:(php获取证书序列号时openssl_x509_parse内存泄漏的替代的方案)