You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
985 lines
37 KiB
985 lines
37 KiB
<?php
|
|
|
|
class Output {
|
|
private static $colorEnum = array(
|
|
'red' => '[31m',
|
|
'green' => '[32m',
|
|
'yellow' => '[33m',
|
|
'blue' => '[34m',
|
|
'purple' => '[35m',
|
|
'sky-blue' => '[36m',
|
|
);
|
|
|
|
public static function str($content, $color = '') { // 输出字符串
|
|
if (!isset(self::$colorEnum[$color])) {
|
|
echo $content;
|
|
return;
|
|
}
|
|
echo "\033" . self::$colorEnum[$color] . $content . "\033" . "[0m";
|
|
}
|
|
|
|
public static function line($content, $color = '') { // 输出一行
|
|
self::str($content . PHP_EOL, $color);
|
|
}
|
|
}
|
|
|
|
class Fake {
|
|
private static function randLocation() { // 生成虚假地址及编码
|
|
$gb2260 = Storage::getGB2260();
|
|
$subData = array();
|
|
foreach ($gb2260 as $code => $content) {
|
|
if (substr($code, 2, 2) === '00') { continue; }
|
|
if (substr($code, 4, 2) === '00') { continue; }
|
|
$subData[$code] = $content;
|
|
}
|
|
$code = array_rand($subData);
|
|
return array(
|
|
'code' => $code,
|
|
'level_1' => $gb2260[substr($code, 0, 2) . '0000'],
|
|
'level_2' => $gb2260[substr($code, 0, 4) . '00'],
|
|
'level_3' => $gb2260[$code],
|
|
);
|
|
}
|
|
|
|
private static function randDate() { // 随机生成日期
|
|
$time = rand(3000, 12000) * 24 * 3600;
|
|
return array(
|
|
'date' => date("Ymd", $time),
|
|
'format' => date("Y-m-d", $time)
|
|
);
|
|
}
|
|
|
|
public static function randIdNum() { // 生成身份证号
|
|
$wi = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
|
|
$ai = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
|
|
$location = self::randLocation();
|
|
$date = self::randDate();
|
|
$orderNum = rand(100, 999);
|
|
$sex = ($orderNum % 2 !== 0) ? 'male' : 'female';
|
|
$idNum = $location['code'] . $date['date'] . $orderNum;
|
|
$sign = 0;
|
|
for ($i = 0; $i < 17; $i++) {
|
|
$bit = (int)$idNum{$i};
|
|
$sign += $bit * $wi[$i];
|
|
}
|
|
unset($location['code']);
|
|
return array(
|
|
'idNum' => $idNum . $ai[$sign % 11],
|
|
'location' => $location,
|
|
'date' => $date['format'],
|
|
'sex' => $sex
|
|
);
|
|
}
|
|
|
|
public static function randPhoneNum() { // 随机电话号码
|
|
$prefix = array(
|
|
130,131,132,133,134,135,136,137,138,139,
|
|
144,147,
|
|
150,151,152,153,155,156,157,158,159,
|
|
176,177,178,
|
|
180,181,182,183,184,185,186,187,188,189,
|
|
);
|
|
return $prefix[array_rand($prefix)] . rand(10000000, 99999999);
|
|
}
|
|
|
|
public static function randName($male = true) { // 随机姓名
|
|
$firstname = array(
|
|
'赵','钱','孙','李','周','吴','郑','王','冯','陈','褚','蒋','沈','韩','杨','朱','秦','姬','许','何','吕','张','孔','曹',
|
|
'戚','谢','邹','喻','章','苏','潘','葛','范','彭','鲁','韦','马','方','任','袁','柳','史','唐','姜','薛','雷','贺','陶',
|
|
'汤','殷','罗','毕','郝','常','傅','齐','顾','孟','黄','萧','尹','姚','邵','汪','祁','毛','米','戴','宋','庞','霍','倪',
|
|
'纪','项','祝','董','梁','杜','阮','季','贾','江','童','郭','梅','卢','林','钟','徐','邱','骆','夏','蔡','田','胡','凌',
|
|
);
|
|
$lastname_male = array(
|
|
'伟','刚','勇','毅','俊','峰','强','军','平','文','辉','力','明','永','健','志','兴','良','海','山','波','贵','福','龙',
|
|
'元','全','国','胜','学','祥','才','武','新','清','彬','富','顺','信','子','杰','涛','成','康','光','天','达','中','茂',
|
|
'和','彪','博','诚','震','振','壮','思','豪','邦','承','绍','松','善','磊','民','哲','江','超','浩','亮','政','瀚','行',
|
|
'翰','朗','伯','宏','鸣','斌','栋','维','启','克','伦','翔','旭','鹏','泽','晨','辰','士','建','家','致','树','德','坚',
|
|
);
|
|
$lastname_female = array(
|
|
'楠','榕','风','航','弘','秀','娟','英','慧','巧','美','娜','静','淑','珠','翠','雅','芝','玉','萍','红','娥','玲','芬',
|
|
'芳','燕','彩','春','兰','凤','洁','梅','琳','素','云','莲','雪','霞','香','月','莺','媛','艳','佳','嘉','琼','珍','莉',
|
|
'璐','晶','妍','茜','秋','珊','莎','黛','倩','婷','姣','婉','娴','瑾','颖','瑶','怡','婵','纨','仪','荷','丹','蓉','若',
|
|
'琴','蕊','薇','菁','梦','馨','瑗','韵','园','咏','卿','澜','纯','毓','悦','昭','冰','琬','羽','希','欣','滢','馥','贞',
|
|
);
|
|
$lastname = ($male) ? $lastname_male : $lastname_female;
|
|
$name = $lastname[array_rand($lastname)];
|
|
if (rand(1, 6) > 1) {
|
|
$name .= $lastname[array_rand($lastname)];
|
|
}
|
|
return $firstname[array_rand($firstname)] . $name;
|
|
}
|
|
}
|
|
|
|
class Storage {
|
|
private static $workDir = '/etc/encryption365';
|
|
|
|
public static function getGB2260() { // 读取GB2260数据
|
|
return json_decode(file_get_contents(self::$workDir . 'GB2260.json'), true);
|
|
}
|
|
|
|
public static function setClientInfo($email, $clientId, $token) { // 客户端凭证写入到本地文件
|
|
$content = 'account = ' . $email . PHP_EOL;
|
|
$content .= 'client_id = ' . $clientId . PHP_EOL;
|
|
$content .= 'access_token = ' . $token . PHP_EOL;
|
|
file_put_contents(self::$workDir . '/client.conf', $content);
|
|
}
|
|
|
|
public static function getClientInfo() { // 本地文件读取客户端凭证
|
|
$raw = explode(PHP_EOL, file_get_contents(self::$workDir . '/client.conf'));
|
|
$info = array();
|
|
foreach ($raw as $row) {
|
|
$row = explode('=', $row);
|
|
if (count($row) !== 2) { continue; }
|
|
$info[trim($row[0])] = trim($row[1]);
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
public static function setValidation($content) { // 设置验证内容
|
|
file_put_contents(self::$workDir . '/validation.txt', $content);
|
|
}
|
|
|
|
public static function delValidation() { // 删除验证文件
|
|
unlink(self::$workDir . '/validation.txt');
|
|
}
|
|
|
|
public static function getHostList() { // 获取站点列表
|
|
$file = self::$workDir . '/site.json';
|
|
if (!is_file($file)) {
|
|
file_put_contents($file, '[]');
|
|
return array();
|
|
}
|
|
return json_decode(file_get_contents($file), true);
|
|
}
|
|
|
|
public static function setHostList($hosts) { // 写入站点列表
|
|
file_put_contents(self::$workDir . '/site.json', json_encode($hosts));
|
|
}
|
|
|
|
public static function addHost($host) { // 新增一个站点
|
|
self::setHostList(array_merge(
|
|
self::getHostList(), array($host)
|
|
));
|
|
}
|
|
|
|
public static function delHost($host) { // 删除一个站点
|
|
if (!self::isHost($host)) { return; }
|
|
$list = array_flip(self::getHostList());
|
|
unset($list[$host]);
|
|
self::setHostList(array_values(array_flip($list)));
|
|
}
|
|
|
|
public static function isHost($host) { // 检查站点是否存在
|
|
return in_array($host, self::getHostList());
|
|
}
|
|
|
|
private static function setHostConfig($host, $file, $content) { // 写入站点文件
|
|
$dir = self::$workDir . '/' . $host;
|
|
if (!is_dir($dir)) {
|
|
mkdir($dir);
|
|
}
|
|
file_put_contents(self::$workDir . '/' . $host . '/' . $file, $content);
|
|
}
|
|
|
|
private static function getHostConfig($host, $file) { // 读取站点文件
|
|
return file_get_contents(self::$workDir . '/' . $host . '/' . $file);
|
|
}
|
|
|
|
public static function setPrivkey($host, $content) { // 存储站点私钥
|
|
self::setHostConfig($host, 'private.key', $content);
|
|
}
|
|
|
|
public static function getPrivkey($host) { // 读取站点私钥
|
|
return self::getHostConfig($host, 'private.key');
|
|
}
|
|
|
|
public static function setCsr($host, $content) { // 存储站点CSR记录
|
|
self::setHostConfig($host, 'csr.dat', $content);
|
|
}
|
|
|
|
public static function getCsr($host) { // 读取站点CSR记录
|
|
return self::getHostConfig($host, 'csr.dat');
|
|
}
|
|
|
|
public static function setCert($host, $content) { // 储存站点证书文件
|
|
self::setHostConfig($host, 'cert.crt', $content);
|
|
}
|
|
|
|
public static function getCert($host) { // 读取站点证书文件
|
|
return self::getHostConfig($host, 'cert.crt');
|
|
}
|
|
|
|
public static function setCaCert($host, $content) { // 存储站点CA证书
|
|
self::setHostConfig($host, 'ca.crt', $content);
|
|
}
|
|
|
|
public static function getCaCert($host) { // 读取站点CA证书
|
|
return self::getHostConfig($host, 'ca.crt');
|
|
}
|
|
|
|
public static function setInfo($host, $info) { // 记录站点信息
|
|
self::setHostConfig($host, 'info.json', json_encode($info));
|
|
}
|
|
|
|
public static function getInfo($host) { // 读取站点信息
|
|
return json_decode(self::getHostConfig($host, 'info.json'), true);
|
|
}
|
|
}
|
|
|
|
class Encryption365 {
|
|
private static $version = '1.3.1';
|
|
private static $apiEntry = 'https://encrypt365.trustocean.com';
|
|
|
|
private function callApi(string $uri, array $params) { // Encrypt365 API接口
|
|
$curl = curl_init();
|
|
curl_setopt($curl, CURLOPT_URL, self::$apiEntry . $uri);
|
|
curl_setopt($curl, CURLOPT_POST, 1);
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));
|
|
curl_setopt($curl, CURLOPT_USERAGENT, 'Encryption365-Client/' . self::$version . ';BaotaPanel-LinuxVersion');
|
|
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
|
$callResult = curl_exec($curl);
|
|
if (curl_error($curl)) {
|
|
die('Curl Error: ' . curl_error($curl));
|
|
}
|
|
curl_close($curl);
|
|
$result = json_decode($callResult, true);
|
|
if(isset($result['status']) && $result['status'] === 'error') {
|
|
return array(
|
|
'status' => 'error',
|
|
'message' => $result['message']
|
|
);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
private static function getClientInfo() { // 获取客户端ID和token
|
|
$info = Storage::getClientInfo();
|
|
if (!isset($info['client_id'])) {
|
|
die('Fail to get client_id');
|
|
}
|
|
if (!isset($info['access_token'])) {
|
|
die('Fail to get access_token');
|
|
}
|
|
return array(
|
|
'client_id' => $info['client_id'],
|
|
'access_token' => $info['access_token']
|
|
);
|
|
}
|
|
|
|
public static function getLatest() { // 获取最新版本
|
|
return self::callApi('/client/version', array());
|
|
}
|
|
|
|
public static function authcodeSend($email) { // 发送注册验证码
|
|
return self::callApi('/account/register/authcode', array(
|
|
'username' => $email
|
|
));
|
|
}
|
|
|
|
public static function accountRegister($info) { // 注册账号
|
|
return self::callApi('/account/register', array(
|
|
'username' => $info['email'],
|
|
'password' => $info['password'],
|
|
'authcode' => $info['authcode'],
|
|
'realName' => $info['realName'],
|
|
'idcardNumber' => $info['idcardNumber'],
|
|
'phoneNumber' => $info['phoneNumber'],
|
|
'country' => $info['country'],
|
|
'companyname' => $info['companyName']
|
|
));
|
|
}
|
|
|
|
public static function clientLogin($username, $password) { // 客户端登录
|
|
return self::callApi("/client/create", array(
|
|
'username' => $username,
|
|
'password' => $password,
|
|
'servername' => 'example.com',
|
|
));
|
|
}
|
|
|
|
public static function getAccountDetail() { // 获取账户信息
|
|
return self::callApi('/account/details', self::getClientInfo());
|
|
}
|
|
|
|
public static function getProducts() { // 获取可购买产品
|
|
return self::callApi('/account/products', self::getClientInfo())['products'];
|
|
}
|
|
|
|
public static function certCreate($productId, $csrCode, $domains) { // 创建证书
|
|
return self::callApi('/cert/create', self::getClientInfo() + array(
|
|
'pid' => $productId,
|
|
'period' => 'quarterly',
|
|
'csr_code' => $csrCode,
|
|
'domains' => implode(',', $domains),
|
|
'renew' => false,
|
|
'old_vendor_id' => -1,
|
|
));
|
|
}
|
|
|
|
public static function certReValidation($vendorId) { // 重新执行域名验证
|
|
return self::callApi('/cert/challenge', self::getClientInfo() + array(
|
|
'trustocean_id' => $vendorId
|
|
));
|
|
}
|
|
|
|
public static function certDetails($vendorId) { // 查询证书详细信息
|
|
return self::callApi('/cert/details', self::getClientInfo() + array(
|
|
'trustocean_id' => $vendorId
|
|
));
|
|
}
|
|
|
|
public static function certRenew($productId, $csrCode, $domains, $oldVendorId) { // 更新证书
|
|
return self::callApi('/cert/create', self::getClientInfo() + array(
|
|
'pid' => $productId,
|
|
'period' => 'quarterly',
|
|
'csr_code' => $csrCode,
|
|
'domains' => implode(',', $domains),
|
|
'renew' => true,
|
|
'old_vendor_id' => $oldVendorId,
|
|
));
|
|
}
|
|
|
|
public static function certReissue($vendorId, $csrCode, $domains) { // 重签SSL证书
|
|
return self::callApi('/cert/reissue', self::getClientInfo() + array(
|
|
'trustocean_id' => $vendorId,
|
|
'csr_code' => $csrCode,
|
|
'domains' => implode(',', $domains)
|
|
));
|
|
}
|
|
}
|
|
|
|
class Certificate {
|
|
public static function generateKeyPair($commonName, $isEcc = false) { // 生成密钥对
|
|
if (filter_var($commonName, FILTER_VALIDATE_IP)) { // 若为IP地址
|
|
$commonName = 'common-name-for-public-ip-address.com';
|
|
}
|
|
$subject = array(
|
|
"commonName" => $commonName,
|
|
"organizationName" => "Encryption365 SSL Security",
|
|
"organizationalUnitName" => "Encryption365 SSL Security",
|
|
"localityName" => "Xian",
|
|
"stateOrProvinceName" => "Shaanxi",
|
|
"countryName" => "CN",
|
|
);
|
|
if (!$isEcc) { // RSA证书
|
|
$private_key = openssl_pkey_new(array(
|
|
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
|
'private_key_bits' => 2048,
|
|
'config' => __DIR__ . '/openssl.cnf'
|
|
));
|
|
$csr_resource = openssl_csr_new($subject, $private_key, array(
|
|
'digest_alg' => 'sha256',
|
|
'config' => __DIR__ . '/openssl.cnf'
|
|
));
|
|
} else { // ECC证书
|
|
$private_key = openssl_pkey_new(array(
|
|
'private_key_type' => OPENSSL_KEYTYPE_EC,
|
|
'curve_name' => 'prime256v1',
|
|
'config' => __DIR__.'/openssl.cnf'
|
|
));
|
|
$csr_resource = openssl_csr_new($subject, $private_key, array(
|
|
'digest_alg' => 'sha384',
|
|
'config' => __DIR__ . '/openssl.cnf'
|
|
));
|
|
}
|
|
openssl_csr_export($csr_resource, $csr_string);
|
|
openssl_pkey_export($private_key, $private_key_string, null, array(
|
|
'config' => __DIR__ . '/openssl.cnf'
|
|
));
|
|
return array(
|
|
'csr_code' => $csr_string,
|
|
'key_code' => $private_key_string
|
|
);
|
|
}
|
|
|
|
public static function preCheck($domains) { // 预先检查http验证是否正常
|
|
$randStr = md5(uniqid(microtime(true), true));
|
|
Storage::setValidation($randStr);
|
|
$result = self::checkValidation($domains, $randStr);
|
|
Storage::delValidation();
|
|
return $result;
|
|
}
|
|
|
|
public static function checkValidation($domains, $expect) { // 检查http验证是否正常
|
|
foreach ($domains as $domain) {
|
|
$content = file_get_contents('http://' . $domain . '/.well-known/pki-validation/check');
|
|
if ($content !== $expect) { return false; }
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static function waitIssued($vendorId) { // 等待证书签发
|
|
$maxTime = 40;
|
|
$interval = 30;
|
|
for ($i = 0; $i < 20; $i++) {
|
|
Output::line('Let\'s wait ' . $interval . ' seconds and query certificate status...');
|
|
sleep($interval);
|
|
$detail = Encryption365::certDetails($vendorId);
|
|
if ($detail['result'] !== "success") {
|
|
Output::str('Fail to get certificate details => ');
|
|
Output::line($detail['message'], 'red');
|
|
break;
|
|
}
|
|
if ($detail['cert_status'] === 'issued_active') {
|
|
Output::line('Certificate issue success', 'green');
|
|
return $detail;
|
|
} else {
|
|
Output::str('Certificate status: ');
|
|
Output::line($detail['cert_status'], 'yellow');
|
|
}
|
|
}
|
|
return array('result' => 'fail');
|
|
}
|
|
|
|
private static function issueCert($host) { // 轮询证书签发并存储
|
|
$info = Storage::getInfo($host);
|
|
$vendorId = $info['vendorId'];
|
|
$domains = $info['domains'];
|
|
$certInfo = self::waitIssued($vendorId);
|
|
if ($certInfo['result'] !== "success") {
|
|
Output::line('Issue certificate time out, you may validate again...', 'red');
|
|
return false;
|
|
}
|
|
self::saveCert($host, $certInfo);
|
|
return true;
|
|
}
|
|
|
|
public static function saveCert($host, $certInfo) {
|
|
$info = Storage::getInfo($host);
|
|
$cert = trim($certInfo['cert_code']);
|
|
$caCert = trim($certInfo['ca_code']);
|
|
$startTime = $certInfo['created_at'];
|
|
$endTime = $certInfo['expire_at'];
|
|
$info['status'] = 'issued';
|
|
$info['createTime'] = $startTime;
|
|
$info['expireTime'] = $endTime;
|
|
Storage::setInfo($host, $info);
|
|
Storage::setCert($host, $cert);
|
|
Storage::setCaCert($host, $caCert);
|
|
Output::line('Certificate: ', 'purple');
|
|
echo $cert . PHP_EOL;
|
|
Output::line('CA Certificate: ', 'purple');
|
|
echo $caCert . PHP_EOL;
|
|
Output::str('Create time: ');
|
|
Output::line($startTime, 'sky-blue');
|
|
Output::str('Expire time: ');
|
|
Output::line($endTime, 'sky-blue');
|
|
}
|
|
|
|
public static function createCert($productId, $domains, $isEcc = false) { // 创建新的证书订单
|
|
if (!self::preCheck($domains)) {
|
|
Output::line('Inaccessible http://{domain}/.well-known/pki-validation/... as validation.');
|
|
return;
|
|
}
|
|
$host = $domains[0];
|
|
$newCsrKey = self::generateKeyPair($host, $isEcc);
|
|
Storage::setPrivkey($host, $newCsrKey['key_code']);
|
|
Storage::setCsr($host, $newCsrKey['csr_code']);
|
|
$orderRlt = Encryption365::certCreate($productId, $newCsrKey['csr_code'], $domains);
|
|
if ($orderRlt['result'] !== "success") {
|
|
Output::str('Fail to create certificate => ');
|
|
Output::line($orderRlt['message'], 'red');
|
|
return;
|
|
}
|
|
$vendorId = (string)$orderRlt['trustocean_id'];
|
|
foreach ($orderRlt['dcv_info'] as $domain => $dcvInfo) {
|
|
preg_match('/^http:\/\/[\S]*?(\/[\S]+.txt)/', $dcvInfo['http_verifylink'], $match);
|
|
$verifyLink = 'http://{domain}' . $match[1];
|
|
$verifyContent = $dcvInfo['http_filecontent'];
|
|
break;
|
|
}
|
|
Storage::setInfo($host, array(
|
|
'host' => $host,
|
|
'vendorId' => $vendorId,
|
|
'productId' => $productId,
|
|
'domains' => $domains,
|
|
'status' => 'issuing',
|
|
'isEcc' => $isEcc
|
|
));
|
|
Output::str('Vendor id: ');
|
|
Output::str($vendorId . PHP_EOL, 'sky-blue');
|
|
Output::str('Verify link: ');
|
|
Output::str($verifyLink . PHP_EOL, 'sky-blue');
|
|
Output::line('Verify content: ');
|
|
Output::str($verifyContent . PHP_EOL, 'sky-blue');
|
|
Storage::setValidation($verifyContent);
|
|
if (self::issueCert($host)) {
|
|
Storage::delValidation();
|
|
}
|
|
}
|
|
|
|
public static function reValidate($host) { // 重新发起验证
|
|
$info = Storage::getInfo($host);
|
|
if ($info['status'] === 'issued') {
|
|
Output::line('This site has been successfully issued.');
|
|
return;
|
|
}
|
|
if (!self::preCheck($info['domains'])) {
|
|
Output::line('Inaccessible http://{domain}/.well-known/pki-validation/... as validation.');
|
|
return;
|
|
}
|
|
$status = Encryption365::certReValidation($info['vendorId']);
|
|
if ($status['status'] !== "success") {
|
|
Output::str('Fail to revalidate certificate => ');
|
|
Output::line($status['message'], 'red');
|
|
return;
|
|
}
|
|
if (self::issueCert($host)) {
|
|
Storage::delValidation();
|
|
}
|
|
}
|
|
|
|
public static function renewCert($host) { // 更新证书
|
|
$info = Storage::getInfo($host);
|
|
if ($info['status'] !== 'issued') {
|
|
Output::line('This site has never been issued.', 'red');
|
|
return;
|
|
}
|
|
if (!self::preCheck($info['domains'])) {
|
|
Output::line('Inaccessible http://{domain}/.well-known/pki-validation/... as validation.');
|
|
return;
|
|
}
|
|
$newCsrKey = self::generateKeyPair($host, $info['isEcc']);
|
|
Storage::setPrivkey($host, $newCsrKey['key_code']);
|
|
Storage::setCsr($host, $newCsrKey['csr_code']);
|
|
$orderRlt = Encryption365::certRenew($info['productId'], $newCsrKey['csr_code'], $info['domains'], $info['vendorId']);
|
|
if ($orderRlt['result'] !== "success") {
|
|
Output::str('Fail to renew certificate => ');
|
|
Output::line($orderRlt['message'], 'red');
|
|
return;
|
|
}
|
|
$vendorId = (string)$orderRlt['trustocean_id'];
|
|
foreach ($orderRlt['dcv_info'] as $domain => $dcvInfo) {
|
|
preg_match('/^http:\/\/[\S]*?(\/[\S]+.txt)/', $dcvInfo['http_verifylink'], $match);
|
|
$verifyLink = 'http://{domain}' . $match[1];
|
|
$verifyContent = $dcvInfo['http_filecontent'];
|
|
break;
|
|
}
|
|
$info['status'] = 'issuing';
|
|
$info['vendorId'] = $vendorId;
|
|
unset($info['createTime']);
|
|
unset($info['expireTime']);
|
|
Storage::setInfo($host, $info);
|
|
Output::str('Vendor id: ');
|
|
Output::str($vendorId . PHP_EOL, 'sky-blue');
|
|
Output::str('Verify link: ');
|
|
Output::str($verifyLink . PHP_EOL, 'sky-blue');
|
|
Output::line('Verify content: ');
|
|
Output::str($verifyContent . PHP_EOL, 'sky-blue');
|
|
Storage::setValidation($verifyContent);
|
|
if (self::issueCert($host)) {
|
|
Storage::delValidation();
|
|
}
|
|
}
|
|
}
|
|
|
|
class RegistCtr {
|
|
public static function interact() { // 注册交互
|
|
Output::line('This process help you regist a Trustocean account, all you need is an email address to receive the verification code.');
|
|
Output::str('Email: ', 'yellow');
|
|
$email = trim(fgets(STDIN));
|
|
$result = Encryption365::authcodeSend($email);
|
|
if ($result['result'] !== 'success') {
|
|
Output::str('Fail to send auth code: ');
|
|
Output::str($result['message'], 'red');
|
|
return;
|
|
}
|
|
Output::line('There will be an email sent to ' . $email . ', please note checking.');
|
|
Output::str('Verification code: ', 'yellow');
|
|
$code = trim(fgets(STDIN));
|
|
Output::line('Set a password for your account.');
|
|
Output::str('Password: ', 'yellow');
|
|
$passwd = trim(fgets(STDIN));
|
|
Output::line('Trustocean need your Chinese ID card num, but you can just press ENTER to generate one.');
|
|
Output::str('ID card num: ', 'yellow');
|
|
$idNum = trim(fgets(STDIN));
|
|
if ($idNum === '') {
|
|
$fake = Fake::randIdNum();
|
|
Output::str('Random ID card num: ');
|
|
Output::str($fake['idNum'] . PHP_EOL, 'sky-blue');
|
|
Output::str('Random ID card info: ');
|
|
Output::str(implode(' ', $fake['location']) . ' ' . $fake['date'] . ' ' . $fake['sex'] . PHP_EOL, 'sky-blue');
|
|
}
|
|
Output::line('Please enter your real name, or press ENTER to generate one.');
|
|
Output::str('Name: ', 'yellow');
|
|
$name = trim(fgets(STDIN));
|
|
if ($name === '') {
|
|
$fake['name'] = Fake::randName(($fake['sex'] === 'male'));
|
|
Output::str('Random name: ');
|
|
Output::str($fake['name'] . PHP_EOL, 'sky-blue');
|
|
}
|
|
Output::line('Last step, enter your phone num, or randomly generate one by press ENTER.');
|
|
Output::str('Phone: ', 'yellow');
|
|
$phone = trim(fgets(STDIN));
|
|
if ($phone === '') {
|
|
$fake['phone'] = Fake::randPhoneNum();
|
|
Output::str('Random phone: ');
|
|
Output::str($fake['phone'] . PHP_EOL, 'sky-blue');
|
|
}
|
|
Output::str('It seems that all the information is complete, press ENTER to register your account...');
|
|
fgets(STDIN);
|
|
self::regist($email, $passwd, $code, $fake['name'], $fake['idNum'], $fake['phone']);
|
|
Output::line('Use "encryption365 login" command to login.');
|
|
}
|
|
|
|
private static function regist($email, $passwd, $code, $name, $idNum, $phone) { // 发起注册
|
|
$result = Encryption365::accountRegister(array(
|
|
'email' => $email,
|
|
'password' => $passwd,
|
|
'authcode' => $code,
|
|
'realName' => $name,
|
|
'idcardNumber' => $idNum,
|
|
'phoneNumber' => $phone,
|
|
'country' => 'CN',
|
|
'companyName' => '无'
|
|
));
|
|
if ($result['result'] !== 'success') {
|
|
Output::str('Fail to regist: ');
|
|
Output::str($result['message'], 'red');
|
|
return;
|
|
}
|
|
Output::str('Regist success' . PHP_EOL, 'green');
|
|
}
|
|
}
|
|
|
|
class LoginCtr {
|
|
public static function entry($params) {
|
|
switch (count($params)) {
|
|
case '0':
|
|
self::interact();
|
|
break;
|
|
case '2':
|
|
self::login($params[0], $params[1]);
|
|
break;
|
|
default:
|
|
Output::line('Unknow params');
|
|
}
|
|
}
|
|
|
|
public static function interact() { // 登录交互
|
|
Output::line('This process need a Trustocean account, if you don\'t have it currently, there are two methods:');
|
|
Output::line('1. Manually register at "https://page.trustocean.com/websecurity/welcome"');
|
|
Output::line('2. Use "encryption365 regist" command regist automatically');
|
|
Output::str('Email: ');
|
|
$email = trim(fgets(STDIN));
|
|
Output::str('Password: ');
|
|
$passwd = trim(fgets(STDIN));
|
|
self::login($email, $passwd);
|
|
}
|
|
|
|
public static function login($email, $passwd) { // 发起登录
|
|
$result = Encryption365::clientLogin($email, $passwd);
|
|
if ($result['result'] !== 'success') {
|
|
Output::str('Fail to login: ');
|
|
Output::line($result['message'], 'red');
|
|
return;
|
|
}
|
|
Output::line('Login success', 'green');
|
|
Output::line('Account status: ' . $result['status']);
|
|
Output::line('Login time: ' . $result['created_at']);
|
|
Storage::setClientInfo($email, $result['client_id'], $result['access_token']);
|
|
}
|
|
}
|
|
|
|
class ListCtr {
|
|
public static function list() { // 列出所有证书
|
|
$list = Storage::getHostList();
|
|
if (count($list) === 0) {
|
|
Output::line('There are not any host yet.', 'yellow');
|
|
return;
|
|
}
|
|
Output::line("Host\t\tID\tDomains\t\t\tCreate Time\t\tExpire Time");
|
|
foreach ($list as $host) {
|
|
$info = Storage::getInfo($host);
|
|
Output::str($info['host'] . "\t", 'yellow');
|
|
Output::str($info['vendorId'] . "\t", 'sky-blue');
|
|
Output::str(implode('/', $info['domains']) . "\t", 'purple');
|
|
if (isset($info['createTime'])) {
|
|
Output::str($info['createTime'] . "\t", 'blue');
|
|
} else {
|
|
Output::str('-' . "\t\t", 'blue');
|
|
}
|
|
if (isset($info['expireTime'])) {
|
|
Output::str($info['expireTime'] . "\t", 'blue');
|
|
} else {
|
|
Output::str('-' . "\t\t", 'blue');
|
|
}
|
|
Output::line('');
|
|
}
|
|
}
|
|
}
|
|
|
|
class IssueCtr {
|
|
public static function entry($params) {
|
|
if (count($params) === 0) {
|
|
Output::line('You must specify encryption method.', 'red');
|
|
return;
|
|
}
|
|
if ($params[0] === 'ECC') { $isEcc = true; }
|
|
if ($params[0] === 'RSA') { $isEcc = false; }
|
|
if (!isset($isEcc)) {
|
|
Output::line('You must specify RSA or ECC.', 'red');
|
|
return;
|
|
}
|
|
if (count($params) === 1) {
|
|
Output::line('You must specify at least one IP or domain name.', 'red');
|
|
return;
|
|
}
|
|
unset($params[0]);
|
|
$domains = array_values($params);
|
|
foreach ($domains as $host) {
|
|
if (!self::isHost($host)) {
|
|
Output::str($host, 'red');
|
|
Output::line(' is not a legel IP or domain name.');
|
|
return;
|
|
}
|
|
}
|
|
$productId = self::getProductId();
|
|
if (!is_numeric($productId)) {
|
|
Output::line('Illegal product ID.', 'red');
|
|
return;
|
|
}
|
|
Storage::addHost($domains[0]);
|
|
Certificate::createCert($productId, $domains, $isEcc);
|
|
}
|
|
|
|
private function isHost($host) { // 判断host是否合法
|
|
preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $host, $match);
|
|
if (count($match) !== 0) {
|
|
if (!is_numeric(substr($host, -1))) { return true; }
|
|
}
|
|
if (filter_var($host, FILTER_VALIDATE_IP)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static function getProductId() { // 获取免费证书产品ID
|
|
$products = Encryption365::getProducts();
|
|
if (count($products) === 0) {
|
|
Output::line('Can\'t find any available products.', 'red');
|
|
return;
|
|
}
|
|
foreach ($products as $productId => $product) {
|
|
if (strpos($product['title'], '免费') !== false) {
|
|
return (string)$productId;
|
|
}
|
|
}
|
|
Output::line('We\'ve encountered an error while processing your request, you might want to select the free certificate\'s product ID manually.', 'yellow');
|
|
foreach ($products as $productId => $product) {
|
|
Output::str('Product ID: ' . $productId . ' => ', 'sky-blue');
|
|
Output::line($product['title'] . ' ' . $product['class'] . ' ' . $product['useage'] . ' ' . $product['term'], 'sky-blue');
|
|
}
|
|
Output::str('Product ID: ');
|
|
return trim(fgets(STDIN));
|
|
}
|
|
}
|
|
|
|
class ReverifyCtr {
|
|
public static function entry($params) {
|
|
if (count($params) === 0) {
|
|
Output::line('Host must be specified.', 'red');
|
|
return;
|
|
}
|
|
if (count($params) > 1) {
|
|
Output::line('Too many parameters to reverify command.', 'red');
|
|
return;
|
|
}
|
|
Certificate::reValidate($params[0]);
|
|
}
|
|
}
|
|
|
|
class FlashCtr {
|
|
public static function entry($params) {
|
|
if (count($params) === 0) {
|
|
Output::line('Host must be specified.', 'red');
|
|
return;
|
|
}
|
|
if (count($params) > 1) {
|
|
Output::line('Too many parameters to flash certificate.', 'red');
|
|
return;
|
|
}
|
|
$host = $params[0];
|
|
$info = Storage::getInfo($host);
|
|
$detail = Encryption365::certDetails($info['vendorId']);
|
|
if ($detail['result'] !== "success") {
|
|
Output::str('Fail to flash certificate => ');
|
|
Output::line($detail['message'], 'red');
|
|
}
|
|
Output::str('Certificate status: ');
|
|
Output::line($detail['cert_status'], 'sky-blue');
|
|
if ($detail['cert_status'] === 'issued_active') {
|
|
Certificate::saveCert($host, $detail);
|
|
}
|
|
}
|
|
}
|
|
|
|
class RenewCtr {
|
|
public static function entry($params) {
|
|
if (count($params) === 0) {
|
|
Output::line('Host must be specified.', 'red');
|
|
return;
|
|
}
|
|
if (count($params) > 1) {
|
|
Output::line('Too many parameters to reverify command.', 'red');
|
|
return;
|
|
}
|
|
Certificate::renewCert($params[0]);
|
|
}
|
|
}
|
|
|
|
class InstallCtr {
|
|
public static function entry($params) {
|
|
if (count($params) === 0) {
|
|
Output::line('Host must be specified.', 'red');
|
|
return;
|
|
}
|
|
if (count($params) === 1) {
|
|
Output::line('You must provide at least one parameter.', 'red');
|
|
return;
|
|
}
|
|
$host = $params[0];
|
|
unset($params[0]);
|
|
$params = array_values($params);
|
|
foreach ($params as $field) {
|
|
preg_match('/^([a-zA-Z]+)=([\s\S]*)$/', $field, $match);
|
|
if (count($match) !== 3) {
|
|
Output::str($field, 'red');
|
|
Output::line(' is illegal.');
|
|
return;
|
|
}
|
|
$match[1] = strtolower($match[1]);
|
|
if ($match[1] !== 'fullchain' && $match[1] !== 'key' && $match[1] !== 'cert' && $match[1] !== 'ca' && $match[1] !== 'cmd') {
|
|
Output::str($match[1], 'red');
|
|
Output::line(' is not a legal option.');
|
|
return;
|
|
}
|
|
$option[$match[1]] = trim($match[2]);
|
|
}
|
|
if (!Storage::isHost($host)) {
|
|
Output::str($host, 'red');
|
|
Output::line(' is not exist.');
|
|
return;
|
|
}
|
|
$info = Storage::getInfo($host);
|
|
Storage::setInfo($host, array_merge($info, $option));
|
|
self::install($host);
|
|
}
|
|
|
|
public static function install($host) { // 安装证书
|
|
$info = Storage::getInfo($host);
|
|
if ($info['status'] !== 'issued') {
|
|
Output::line('This site has never been issued.', 'red');
|
|
return;
|
|
}
|
|
$fields = array('fullchain', 'key', 'cert', 'ca');
|
|
foreach ($fields as $field) {
|
|
if (isset($info[$field]) && $info[$field] !== '') {
|
|
switch ($field) {
|
|
case 'fullchain':
|
|
$content = Storage::getCert($host) . PHP_EOL . Storage::getCaCert($host);
|
|
break;
|
|
case 'key':
|
|
$content = Storage::getPrivkey($host);
|
|
break;
|
|
case 'cert':
|
|
$content = Storage::getCert($host);
|
|
break;
|
|
case 'ca':
|
|
$content = Storage::getCaCert($host);
|
|
break;
|
|
}
|
|
file_put_contents($info[$field], $content);
|
|
if ($field === 'key') {
|
|
chmod($info[$field], 0600);
|
|
}
|
|
}
|
|
}
|
|
if (isset($info['cmd']) && $info['cmd'] !== '') {
|
|
shell_exec($info['cmd']);
|
|
}
|
|
Output::line('Install OK', 'green');
|
|
}
|
|
}
|
|
|
|
class AutorenewCtr {
|
|
public static function renew() {
|
|
$list = Storage::getHostList();
|
|
foreach ($list as $host) {
|
|
$info = Storage::getInfo($host);
|
|
if ($info['status'] !== 'issued') { continue; }
|
|
$expire = strtotime($info['expireTime']);
|
|
$now = strtotime(date('Y-m-d H:i:s'));
|
|
$remainDays = intval(($expire - $now) / 3600 / 24);
|
|
if ($remainDays >= 10) { continue; }
|
|
Output::str('Renew certificate for ');
|
|
Output::line($host, 'yellow');
|
|
Certificate::renewCert($host);
|
|
InstallCtr::install($host);
|
|
}
|
|
}
|
|
}
|
|
|
|
function noParam($params) { // 命令不含参数情况
|
|
if (count($params) === 0) { return; }
|
|
echo 'Unknow params' . PHP_EOL;
|
|
exit;
|
|
}
|
|
|
|
function showHelp() { // 显示帮助信息
|
|
echo 'help message';
|
|
}
|
|
|
|
function main($argv) { // 脚本入口
|
|
unset($argv[0]);
|
|
if (count($argv) === 0) {
|
|
$argv[] = 'help';
|
|
}
|
|
$command = $argv[1];
|
|
unset($argv[1]);
|
|
$params = array_values($argv);
|
|
switch ($command) {
|
|
case 'help':
|
|
noParam($params);
|
|
showHelp();
|
|
break;
|
|
case 'regist':
|
|
noParam($params);
|
|
RegistCtr::interact();
|
|
break;
|
|
case 'login':
|
|
LoginCtr::entry($params);
|
|
break;
|
|
case 'list':
|
|
noParam($params);
|
|
ListCtr::list();
|
|
break;
|
|
case 'issue':
|
|
IssueCtr::entry($params);
|
|
break;
|
|
case 'reverify':
|
|
ReverifyCtr::entry($params);
|
|
break;
|
|
case 'flash':
|
|
FlashCtr::entry($params);
|
|
break;
|
|
case 'renew':
|
|
RenewCtr::entry($params);
|
|
break;
|
|
case 'install':
|
|
InstallCtr::entry($params);
|
|
break;
|
|
case 'autorenew':
|
|
noParam($params);
|
|
AutorenewCtr::renew();
|
|
break;
|
|
default:
|
|
echo 'Unknow command, please use "encryption365 help" to show the usage.' . PHP_EOL;
|
|
}
|
|
}
|
|
|
|
main($argv);
|
|
|
|
?>
|
|
|