// 内存泄漏问题说明:
// 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;
}
}