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 . "<br>\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 . "<br>\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 . "<br>\n";
		if ($byte == 0xa0) { //version tag.
			$length = CertSerialUtil::readLength ( $bindata );
			CertSerialUtil::readData ( $bindata, $length );
			$byte = array_shift ( $bindata );
		}

// 		echo $byte . "<br>\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;
	}
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值