Files
cmp-php-openssl/cmpOpenSSLTrait.php
2024-01-25 07:46:42 +12:00

941 lines
23 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
trait cmpOpenSSLTrait {
var $ver = "20240124";
var $caDN = NULL;
var $caPub = NULL;
var $caPubPEM = "";
var $caPubFile = "";
var $caPrv = NULL;
var $caPrvPEM = "";
var $caPrvFile = "";
var $caCrt = NULL;
var $caCrtPEM = "";
var $caCrtFile = "";
var $cliDN = NULL;
var $cliPub = NULL;
var $cliPubPEM = "";
var $cliPubFile = "";
var $cliPrv = NULL;
var $cliPrvPEM = "";
var $cliPrvFile = "";
var $cliCrt = NULL;
var $cliCrtPEM = "";
var $cliCrtFile = "";
var $cmpOpenSslConf = array();
function cmpOpenSslConfSetDefault() {
$claver = __CLASS__ . "-" . $this->ver;
$this->cmpOpenSslConf = array (
"GLOBAL" => array (
"oid_section" => "OIDs",
),
"OIDs" => array (
"cmpExtOid" => "1.2.3.20190828",
"certificateTemplateName" => "\${cmpExtOid}.1",
),
"ca" => array (
"default_ca" => "cmp_ca",
),
"cmp_ca" => array (
"dir" => ".",
"certs" => "\$dir",
"crl_dir" => "\$dir}",
"database" => "\$dir/index.txt",
"new_certs_dir" => "\$dir",
"serial" => "\$dir/serial",
"crl" => "\$dir/crl.pem",
"certificate" => "\$dir/ca.crt",
"private_key" => "\$dir/ca.key",
"RANDFILE" => "\$dir/.rand",
"x509_extensions" => "cmp_x509_ext_basic",
"crl_extensions" => "cmp_crl_ext",
"default_days" => 3650,
"default_crl_days" => 30,
"default_md" => "sha256",
"preserve" => "no",
"policy" => "cmp_policy_anything",
),
"cmp_policy_anything" => array (
"countryName" => "optional",
"stateOrProvinceName" => "optional",
"localityName" => "optional",
"organizationName" => "optional",
"organizationalUnitName" => "optional",
"commonName" => "supplied",
"name" => "optional",
"emailAddress" => "optional",
),
"req" => array (
"default_bits" => 2048,
"default_keyfile" => "privkey.pem",
"default_md" => "sha256",
"distinguished_name" => "cmp_dst_ext",
"x509_extensions" => "cmp_x509_ext_rsa",
"req_extensions" => "cmp_req_ext_v3",
),
"cmp_dst_ext" => array (
"commonName" => "Common Name (eg: your user, host, or server name)",
"commonName_max" => 64,
"commonName_default" => $claver . "-commonName_default",
// "certificateTemplateName" => $claver , // "-certificateTemplateName",
"certificateTemplateName_default" => $claver ,
),
"cmp_req_ext_v3" => array (
"certificateTemplateName" => "ASN1:PRINTABLESTRING:CustomUserOffline",
),
"cmp_org" => array (
"countryName" => "Country Name (2 letter code)",
"countryName_default" => "RU",
"countryName_min" => 2,
"countryName_max" => 2,
"stateOrProvinceName" => "State or Province Name (full name)",
"stateOrProvinceName_default" => $claver . "-stateOrProvinceName_default",
"localityName" => "Locality Name (eg, city)",
"localityName_default" => $claver . "-localityName_default",
"organizationName" => "Organization Name (eg, company)",
"organizationName_default" => $claver . "-organizationName_default",
"organizationalUnitName" => "Organizational Unit Name (eg, section)",
"organizationalUnitName_default" => $claver . "-organizationalUnitName_default",
"emailAddress" => "Email Address",
"emailAddress_default" => $claver . "@example.org",
"emailAddress_max" => 64,
),
"cmp_crl_ext" => array (
"authorityKeyIdentifier" => "keyid:always,issuer:always",
),
"cmp_x509_ext_basic" => array (
"basicConstraints" => "CA:FALSE",
"subjectKeyIdentifier" => "hash",
"authorityKeyIdentifier" => "keyid,issuer:always",
),
"cmp_x509_ext_rsa" => array (
"subjectKeyIdentifier" => "hash",
"authorityKeyIdentifier" => "keyid:always,issuer:always",
"basicConstraints" => "CA:true",
"keyUsage" => "cRLSign, keyCertSign",
),
"cmp_x509_ext_srv" => array (
"basicConstraints" => "CA:FALSE",
"nsCertType" => "server",
"nsComment" => "\"OpenSSL Generated Server Certificate\"",
"subjectKeyIdentifier" => "hash",
"authorityKeyIdentifier" => "keyid,issuer:always",
"extendedKeyUsage" => "serverAuth",
"keyUsage" => "digitalSignature, keyEncipherment",
),
"cmp_x509_ext_cli" => array (
"basicConstraints" => "CA:FALSE",
"nsCertType" => "client",
"nsComment" => "\"OpenSSL Generated Client Certificate\"",
"subjectKeyIdentifier" => "hash",
"authorityKeyIdentifier" => "keyid,issuer",
"keyUsage" => "critical, nonRepudiation, digitalSignature, keyEncipherment",
"extendedKeyUsage" => "clientAuth",
),
);
return true;
}
function cmpOpenSslConfTemp() {
$a = array();
if(!isset($this->cmpOpenSslConf["GLOBAL"])) {
$this->cmpOpenSslConfSetDefault();
}
if(!isset($this->cmpOpenSslConf["GLOBAL"])) {
throw new Exception("No GLOBAL section");
}
foreach($this->cmpOpenSslConf["GLOBAL"] as $key => $val) {
$a[] = "\t" . $key . "=" . $val . "";
}
foreach($this->cmpOpenSslConf as $sec => $prm) {
if($sec == "GLOBAL")
continue;
$a[] = "";
$a[] = "[" . $sec . "]";
foreach($prm as $key => $val) {
$a[] = "\t" . $key . " = " . $val . "";
}
}
$a[] = "";
$t = join("\n", $a);
$rand = mt_rand(100000, 999999);
$file = "/tmp/openssl-$rand.conf";
$w = @file_put_contents($file, $t);
if(!$w) {
throw new Exception("Can't write file '$file'");
}
return $file;
}
function cmpOpenSslConfRead($file) {
$t = file_get_contents($file);
$a = explode("\n", $t);
$sec = "GLOBAL";
$key = "";
$val = "";
$obj = array();
for($i = 0; $i < count($a); $i++) {
$a[$i] = trim($a[$i], "\n \t\r");
if(!$a[$i])
continue;
if(substr($a[$i], 0, 1) == "#")
continue;
$m = array();
$s = "";
if(preg_match("/^(.+)#.+$/", $a[$i], $m)) {
$s = trim($m[1], "\n \t\r");
}
else {
$s = $a[$i];
}
if(preg_match("/^\[(.+)\]$/", $a[$i], $m)) {
$sec = trim($m[1], "\n \t\r");
if(!isset($obj[$sec])) {
$obj[$sec] = array();
}
continue;
}
$b = explode("=", $s, 2);
$key = trim($b[0], "\n \t\r");
$val = trim($b[1], "\n \t\r");
// echo "STR: " . $sec . " === " . $key . " === " . $val . "\n";
$obj[$sec][$key] = $val;
}
$this->cmpOpenSslConf = $obj;
// var_export($obj);
return true;
}
function cmpOpenSslParm($parm, $key, $def = "") {
if(!is_array($parm))
return $def;
if(!isset($parm[$key]))
return $def;
return $parm[$key];
}
function cmpOpenSslParmConfSection($parm, $sec, &$out) {
if(!isset($this->cmpOpenSslConf[$sec])) {
;
}
foreach($this->cmpOpenSslConf[$sec] as $key => $val) {
$m = array();
if(!preg_match("/^(.+)_default$/", $key, $m))
continue;
$key = $m[1];
$out[$key] = $this->cmpOpenSslParm(
$parm,
$key,
$val
);
}
return true;
}
function getCertInfo($crt, $prv, &$out = null) {
$txtPub = "";
$txtPrv = "";
openssl_x509_export($crt , $txtPub, false );
openssl_pkey_export($prv , $txtPrv, NULL );
if(!$out)
$out = array();
$out["fingerprint"] = array(
"default" => @openssl_x509_fingerprint($crt),
"sha256" => @openssl_x509_fingerprint($crt, "sha256")
);
$out["err"] = array();
do {
$cert = @openssl_x509_parse($txtPub);
if(!$cert) {
$out["err"][] = array(__LINE__, openssl_error_string());
break;
}
if(!isset($cert["subject"])) {
$out["err"][] = array(__LINE__, "Invalid subject");
break;
}
if(!isset($cert["subject"]["CN"])) {
$out["err"][] = array(__LINE__, "Invalid subject CN");
break;
}
if(!isset($cert["extensions"])) {
$out["err"][] = array(__LINE__, "Invalid extensions");
break;
}
if(!isset($cert["extensions"]["authorityKeyIdentifier"])) {
$out["err"][] = array(__LINE__, "Invalid extensions authorityKeyIdentifier");
break;
}
if(!isset($cert["extensions"]["subjectKeyIdentifier"])) {
$out["err"][] = array(__LINE__, "Invalid extensions subjectKeyIdentifier");
break;
}
} while(0);
if($out["err"]) {
$this->d($out["err"]);
return NULL;
}
unset($out["err"]);
$nrmz = array( /* "A" => "a", "B" => "b", "C" => "c", "D" => "d", "E" => "e", "F" => "f", */ ":" => "");
do {
if(preg_match("/^[0-9A-F:]+$/", $cert["extensions"]["authorityKeyIdentifier"])) {
$certAuth = $cert["extensions"]["authorityKeyIdentifier"];
break;
}
$a = explode("\n", $cert["extensions"]["authorityKeyIdentifier"]);
$b = null;
$c = array();
for($i = 0; $i < count($a); $i++) {
$b = explode(":", $a[$i], 2);
$c[$b[0]] = $b[1];
}
// $this->d($c);
if(!isset($c["keyid"]) || !$c["keyid"]) {
$out["err"][] = array(__LINE__, "Invalid authorityKeyIdentifier: '". $cert["extensions"]["authorityKeyIdentifier"] ."'");
return NULL;
}
$certAuth = strtolower($c["keyid"]);
} while(0);
$certAuth = strtr($certAuth, $nrmz);
$certAuth = strtolower($certAuth);
$certSubj = $cert["extensions"]["subjectKeyIdentifier"];
$certSubj = strtr($certSubj, $nrmz);
$certSubj = strtolower($certSubj);
$out["certAuth" ] = $certAuth;
$out["validFrom"] = date("Y-m-d H:i:s", $cert["validFrom_time_t" ]);
$out["validTo" ] = date("Y-m-d H:i:s", $cert["validTo_time_t" ]);
$out["certSubj" ] = $certSubj;
$out["certCN" ] = $cert["subject"]["CN"];
$out["public" ] = $txtPub;
$out["private" ] = $txtPrv;
return $out;
}
function createCA($parm, &$out = null) {
try {
$confFile = $this->cmpOpenSslConfTemp();
$digest_alg = $this->cmpOpenSslParm($parm, "digest_alg" , "sha256" );
$x509_extensions = $this->cmpOpenSslParm($parm, "x509_extensions" , "cmp_x509_ext_rsa" );
$days = $this->cmpOpenSslParm($parm, "days" , 365 );
$outfile = $this->cmpOpenSslParm($parm, "outfile" );
$serial = $this->cmpOpenSslParm($parm, "serial" , mt_rand(0, PHP_INT_MAX) );
$this->caDN = array();
$this->cmpOpenSslParmConfSection($parm, "cmp_org", $this->caDN);
$this->cmpOpenSslParmConfSection($parm, "cmp_dst_ext", $this->caDN);
if(!$this->caDN["commonName"]) {
throw new Exception("Empty commonName");
}
$confFile = $this->cmpOpenSslConfTemp();
$pcsrs = array(
"digest_alg" => $digest_alg ,
);
$pcsrn = array(
"config" => $confFile ,
"digest_alg" => $digest_alg ,
"x509_extensions" => $x509_extensions ,
);
$ppkey = array(
"config" => $confFile ,
"encrypt_key" => $this->cmpOpenSslParm($parm, "encrypt_key" , false ),
"private_key_type" => $this->cmpOpenSslParm($parm, "private_key_type" , OPENSSL_KEYTYPE_RSA ),
"private_key_bits" => $this->cmpOpenSslParm($parm, "private_key_bits" , 4096 ),
);
$this->caPrv = openssl_pkey_new($ppkey);
if(!$this->caPrv) {
throw new Exception("openssl_pkey_new: " . openssl_error_string());
}
$csr = openssl_csr_new($this->caDN, $this->caPrv, $pcsrn);
if(!$csr) {
throw new Exception("openssl_csr_new: " . openssl_error_string());
}
// Создание самоподписанного сертификата со сроком жизни $days дней
$this->caCrt = openssl_csr_sign($csr, null, $this->caPrv, $days, $pcsrs, $serial);
if(!$this->caCrt) {
throw new Exception("openssl_csr_sign: " . openssl_error_string());
}
@unlink($confFile);
} catch(Exception|Throwable $e) {
@unlink($confFile);
$this->e($e);
return NULL;
}
$this->getCertInfo($this->caCrt, $this->caPrv, $out);
$txtPub = "";
$txtPrv = "";
openssl_x509_export($this->caCrt , $txtPub, false );
openssl_pkey_export($this->caPrv , $txtPrv, NULL );
if($out !== null) {
if(!$this->getCertInfo($this->caCrt, $this->caPrv, $out))
return NULL;
}
if($outfile) {
openssl_x509_export_to_file($this->caCrt , "$outfile.crt" );
openssl_pkey_export_to_file($this->caPrv , "$outfile.prv" , NULL );
}
$this->caPub = openssl_pkey_get_public($this->caCrt);
if(!$this->caPub) {
$this->e(__LINE__, "openssl_pkey_get_public: error");
return NULL;
}
// var_export($csrout);
// echo "\n";
return true;
}
function loadFromFileCACrt($file) {
// var_dump(openssl_get_cert_locations());
$this->caCrtFile = $file;
$text = @file_get_contents($file);
if($this->loadFromTextCACrt($text)) {
$this->caCrtFile = $file;
return true;
}
return NULL;
}
function loadFromTextCACrt($text) {
$this->caCrtFile = "";
$this->caCrtPEM = $text;
if(!$this->caCrtPEM) {
$this->e(__LINE__, "Invalid CA text");
return NULL;
}
// openssl_get_privatekey()
$this->caCrt = openssl_x509_read( $this->caCrtPEM );
if(!$this->caCrt) {
$this->e(__LINE__, "openssl_x509_read: error");
return NULL;
}
// openssl_x509_parse(file_get_contents($file));
$this->caPub = openssl_pkey_get_public($this->caCrt);
if(!$this->caPub) {
$this->e(__LINE__, "openssl_pkey_get_public: error");
return NULL;
}
$pkey = openssl_pkey_get_details($this->caPub);
if(!$pkey) {
$this->e(__LINE__, "openssl_pkey_get_details: error");
return NULL;
}
$this->caPubPEM = $pkey["key"];
$this->caPub = openssl_pkey_get_public($this->caPubPEM);
if(!$this->caPub) {
$this->e(__LINE__, "openssl_pkey_get_public: error");
return NULL;
}
return true;
}
function loadFromFileCAPrv($file, $pass = NULL) {
$this->caPrvFile = $file;
$text = @file_get_contents($file);
if($this->loadFromTextCAPrv($text, $pass)) {
$this->caPrvFile = $file;
return true;
}
return NULL;
}
function loadFromTextCAPrv($text, $pass = NULL) {
$this->caPrvFile = "";
/*
if(@$file)
$this->caPrvPEM = @file_get_contents($file);
else
$this->caPrvPEM = "";
if(!$text) {
$this->e(__LINE__, "Invalid CA private key text");
return NULL;
}
*/
$this->caPrvPEM = $text;
$this->caPrv = openssl_pkey_get_private($this->caPrvPEM, $pass);
if(!$this->caPrv) {
$this->e(__LINE__, "openssl_pkey_get_private: error");
return NULL;
}
$sign = "";
$test = "test-test";
//Вычисляем подпись
if(!openssl_sign($test, $sign, $this->caPrv, "sha1WithRSAEncryption")) {
$this->e(__LINE__, "openssl_sign: error");
return NULL;
}
switch( openssl_verify($test, $sign, $this->caPub, OPENSSL_ALGO_SHA1) ) {
case 1:
// echo "корректна\n";
return true;
case 0:
// echo "некорректна\n";
$this->e(__LINE__, "Incorrect CA private key");
return NULL;
case -1:
$this->e(__LINE__, openssl_error_string());
return NULL;
}
return true;
}
function infoCA() {
var_export(openssl_x509_parse($this->caCrt));
echo "\n";
// var_export(openssl_pkey_get_details($this->caPrv));
// echo "\n";
}
function createCli($parm, &$out = null) {
try {
$confFile = $this->cmpOpenSslConfTemp();
$digest_alg = $this->cmpOpenSslParm($parm, "digest_alg" , "sha256" );
$x509_extensions = $this->cmpOpenSslParm($parm, "x509_extensions" , "" );
$days = $this->cmpOpenSslParm($parm, "days" , 365 );
$outfile = $this->cmpOpenSslParm($parm, "outfile" );
$serial = $this->cmpOpenSslParm($parm, "serial" , mt_rand(0, PHP_INT_MAX) );
$this->cliDN = array();
$this->cmpOpenSslParmConfSection($parm, "cmp_org", $this->cliDN);
$this->cmpOpenSslParmConfSection($parm, "cmp_dst_ext", $this->cliDN);
if(!$this->cliDN["commonName"]) {
throw new Exception("Empty commonName");
}
if(!$x509_extensions) {
throw new Exception("Empty x509_extensions");
}
$pcsrn = array(
"config" => $confFile ,
"digest_alg" => $digest_alg ,
"x509_extensions" => $x509_extensions ,
);
$ppkey = array(
"config" => $confFile ,
"encrypt_key" => $this->cmpOpenSslParm($parm, "encrypt_key" , false ),
"private_key_type" => $this->cmpOpenSslParm($parm, "private_key_type" , OPENSSL_KEYTYPE_RSA ),
"private_key_bits" => $this->cmpOpenSslParm($parm, "private_key_bits" , 4096 ),
);
$this->cliPrv = openssl_pkey_new($ppkey);
if(!$this->cliPrv) {
throw new Exception("openssl_csr_new: " . openssl_error_string());
}
$csr = @openssl_csr_new($this->cliDN, $this->cliPrv, $pcsrn);
if(!$csr) {
throw new Exception("openssl_csr_new: " . openssl_error_string());
}
$this->cliCrt = openssl_csr_sign($csr, $this->caCrt, $this->caPrv, $days, $pcsrn, $serial);
if(!$this->cliCrt) {
throw new Exception("openssl_csr_sign: " . openssl_error_string());
}
@unlink($confFile);
} catch(Exception|Throwable $e) {
@unlink($confFile);
$this->e($e);
return NULL;
}
$txtPub = "";
$txtPrv = "";
openssl_x509_export($this->cliCrt, $txtPub, false );
openssl_pkey_export($this->cliPrv, $txtPrv, NULL );
if($out !== null) {
if(!$this->getCertInfo($this->cliCrt, $this->cliPrv, $out))
return NULL;
}
if($outfile) {
openssl_x509_export_to_file($this->cliCrt , "$outfile.crt" );
openssl_pkey_export_to_file($this->cliPrv , "$outfile.prv" , NULL );
}
$this->cliPub = openssl_pkey_get_public($this->cliCrt);
if(!$this->cliPub) {
$this->e(__LINE__, "openssl_pkey_get_public: error");
return NULL;
}
// var_export($csrout);
// echo "\n";
return true;
}
function createClient($parm = NULL, &$out = null) {
$parm["x509_extensions"] = "cmp_x509_ext_cli";
return $this->createCli($parm, $out);
}
function createServer($parm = NULL, &$out = null) {
$parm["x509_extensions"] = "cmp_x509_ext_srv";
return $this->createCli($parm, $out);
}
function cmpOpenSslGenDh($bits = 2048) {
$a = array(
"openssl",
"dhparam",
$bits
);
if(!method_exists($this, "cmpSysExec")) {
$this->d("Invalid method cmpSysExec");
return null;
}
$arr = $this->cmpSysExec($a, array("return" => "outarr", "noerror" => 1));
if(0) {
var_export($arr);
echo "\n";
}
if($arr[0] != "-----BEGIN DH PARAMETERS-----") {
$this->d("Invalid first string");
return null;
}
$lst = count($arr) - 2;
if($arr[$lst] != "-----END DH PARAMETERS-----") {
$this->d("Invalid last string");
return null;
}
return join("\n", $arr);
}
function cmpOpenVpnGenTa() {
$a = array(
"openvpn",
"--genkey",
// Valid keytype arguments are:
"secret" // Standard OpenVPN shared secret keys
// "tls-crypt" // Alias for secret
// "tls-auth" // Alias for secret
);
$arr = $this->cmpSysExec($a, array("return" => "outarr", "noerror" => 1));
if(0) {
var_export($arr);
echo "\n";
}
$idx = array();
for($i = 0; $i < count($arr); $i++) {
if($arr[$i] == "-----BEGIN OpenVPN Static key V1-----") {
$idx["bgn"] = $i;
continue;
}
if($arr[$i] == "-----END OpenVPN Static key V1-----") {
$idx["end"] = $i;
continue;
}
}
if(!isset($idx["bgn"]) || !isset($idx["end"])) {
$this->d("Invalid output key");
return null;
}
return join("\n", $arr);
}
function cmpOpenSslGenCrl($caCrtText, $caPrvText, $outCrlFile) {
$now = date("Y-m-d H:i:s");
$dir = "/tmp/tmp-ca-dir-" . md5("tmp-ca-dir-" . $now);
if(!mkdir($dir))
return null;
$caCrtFile = $dir . "/ca.crt";
$caPrvFile = $dir . "/ca.prv";
$indexFile = $dir . "/index.txt";
$serialFile = $dir . "/serial";
file_put_contents($caCrtFile, $caCrtText);
file_put_contents($caPrvFile, $caPrvText);
file_put_contents($indexFile, "");
file_put_contents($serialFile, "");
$a = array(
"cd" ,
$dir ,
["asis", "&&"] ,
"openssl" ,
"ca" ,
"-config" ,
"65-openssl.cnf" ,
"-gencrl" ,
"-out" ,
$outCrlFile ,
"-cert" ,
$caCrtFile ,
"-keyfile" ,
$caPrvFile ,
);
$arr = $this->cmpSysExec($a, array(/* "return" => "outarr", "noerror" => 1 */));
unlink($caCrtFile);
unlink($caPrvFile);
unlink($indexFile);
unlink($serialFile);
if(!rmdir($dir)) {
$this->d("Can't remove directory '$dir'");
}
if(1) {
var_export($arr);
echo "\n";
}
return;
}
function cmpOpenSslAddCrl($caCrtText, $caPrvText, $outCrlFile, $sjCrtText) {
$now = date("Y-m-d H:i:s");
$dir = "/tmp/tmp-ca-dir-" . md5("tmp-ca-dir-" /* . $now . "-" . mt_rand(10000, 99999) */ );
if(!mkdir($dir)) {
;
// return null;
}
$caCrtFile = $dir . "/ca.crt";
$caPrvFile = $dir . "/ca.prv";
$indexFile = $dir . "/index.txt";
$serialFile= $dir . "/serial";
$sjCrtFile = $dir . "/cert.pem";
$crlFile = $dir . "/crl.pem";
if(!is_file($caCrtFile))
file_put_contents($caCrtFile, $caCrtText);
if(!is_file($caPrvFile))
file_put_contents($caPrvFile, $caPrvText);
file_put_contents($sjCrtFile, $sjCrtText);
if(!is_file($indexFile))
file_put_contents($indexFile, "");
if(!is_file($serialFile))
file_put_contents($serialFile, "");
if(is_file($outCrlFile)) {
copy($outCrlFile, $crlFile);
// $a[] = "-in";
// $a[] = $outCrlFile;
$this->d("Add IN");
}
$a = array(
"cd" ,
$dir ,
["asis", "&&"] ,
"openssl" ,
"ca" ,
"-config" ,
"65-openssl.cnf" ,
"-revoke" ,
$sjCrtFile ,
// "-out" ,
// $outCrlFile ,
"-cert" ,
$caCrtFile ,
"-keyfile" ,
$caPrvFile ,
);
// $a[] = "-in";
// $a[] = $outCrlFile;
$arr = $this->cmpSysExec($a, array(/* "return" => "outarr", "noerror" => 1 */));
if(1) {
var_export($arr);
echo "\n";
}
// unlink($caCrtFile);
// unlink($caPrvFile);
// unlink($sjCrtFile);
// unlink($indexFile);
// unlink($serialFile);
if(is_file($crlFile)) {
copy($crlFile, $outCrlFile);
// unlink($crlFile);
}
// unlink($dir . "/index.txt.attr");
// unlink($dir . "/index.txt.old");
// if(!rmdir($dir)) {
// $this->d("Can't remove directory '$dir'");
// }
return;
}
// trait
}