[php实现]通过腾讯云Api上传SSL证书

2022-03-01 16:27:10 浏览数 (1)

背景

笔者使用的轻量应用服务器安装的Windows系统(集成环境没有更新证书功能),SSL证书是acme.sh生成的多域名证书,有效期90天。因为使用了腾讯云的CDN,每次生成新证书需要手动上传到腾讯云,太麻烦了。既然官方提供了上传证书的Api(UploadCertificate),服务器是php环境,就想用php撸个小工具,实现自动上传SSL证书到腾讯云。

主要思路

acme.sh(计划任务)更新证书 -> 证书更新成功hook工具网址实现上传

核心代码

执行acme.sh时,额外增加如下参数:(5秒延时可取消,网址换成自己脚本存放位置)

代码语言:javascript复制
--renew-hook "sleep 5s && curl http://127.0.0.1/Qcloud/ssl_update.php"

简版腾讯云SDK文件:Qcloud.php

代码语言:javascript复制
<?php
//腾讯云SDK

class Qcloud {
	private $SecretId;
	private $SecretKey;
	private $endpoint = "scf.tencentcloudapi.com"; //接口请求域名
	private $service = "scf"; //服务名称
	private $version = "2018-04-16"; //API版本号
	private $region = "ap-shanghai"; //地域参数

	/**
	 * SecretId 腾讯云SecretId
	 * SecretKey 腾讯云SecretKey
	*/
	function __construct($SecretId, $SecretKey){
        $this->SecretId = $SecretId;
        $this->SecretKey = $SecretKey;
    }
    
    /**
     * $api 接口请求域名
    */
    public function SetApi($api){
        $this->endpoint = $api;
        $ex = explode('.', $api);
        $this->service = $ex[0];
        //print_r($ex);
    }
    
    /**
     * $api 服务名称
    */
    public function SetService($ser){
        $this->service = $ser;
    }
    
    /**
     * $api API版本号
    */
    public function SetVersion($ver){
        $this->version = $ver;
    }
    
    /**
     * $api 地域参数
    */
    public function SetRegion($reg=null){
        $this->region = $reg;
    }
	
	public function send_reuqest($action, $param){
		$payload = json_encode($param);
		$time = time();
		$authorization = $this->generateSign($payload, $time);
		$header = [
			'Authorization: '.$authorization,
			'Content-Type: application/json; charset=utf-8',
			'X-TC-Action: '.$action,
			'X-TC-Timestamp: '.$time,
			'X-TC-Version: '.$this->version,
			'X-TC-Region: '.$this->region,
		];
		return $this->curl_post($payload, $header);
	}

	private function generateSign($payload, $time){
		$algorithm = "TC3-HMAC-SHA256";

		// step 1: build canonical request string
		$httpRequestMethod = "POST";
		$canonicalUri = "/";
		$canonicalQueryString = "";
		$canonicalHeaders = "content-type:application/json; charset=utf-8n"."host:".$this->endpoint."n";
		$signedHeaders = "content-type;host";
		$hashedRequestPayload = hash("SHA256", $payload);
		$canonicalRequest = $httpRequestMethod."n"
			.$canonicalUri."n"
			.$canonicalQueryString."n"
			.$canonicalHeaders."n"
			.$signedHeaders."n"
			.$hashedRequestPayload;
		
		// step 2: build string to sign
		$date = gmdate("Y-m-d", $time);
		$credentialScope = $date."/".$this->service."/tc3_request";
		$hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
		$stringToSign = $algorithm."n"
			.$time."n"
			.$credentialScope."n"
			.$hashedCanonicalRequest;
		
		// step 3: sign string
		$secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true);
		$secretService = hash_hmac("SHA256", $this->service, $secretDate, true);
		$secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
		$signature = hash_hmac("SHA256", $stringToSign, $secretSigning);

		// step 4: build authorization
		$authorization = $algorithm
			." Credential=".$this->SecretId."/".$credentialScope
			.", SignedHeaders=content-type;host, Signature=".$signature;

		return $authorization;
	}

	private function curl_post($payload, $header){
		$url = 'https://'.$this->endpoint.'/';
		$ch=curl_init($url);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
		curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_TIMEOUT, 10);
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
		$json=curl_exec($ch);
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		curl_close($ch);
		if($httpCode==200){
			$arr=json_decode($json,true);
			return $arr['Response'];
		}else{
			return false;
		}
	}
}

受访文件:ssl_upload.php

代码语言:javascript复制
<?php
include 'Qcloud.php';
$SecretId = 'xxxx';
$SecretKey='xxxxxx';

$param['pbk'] = file_get_contents('证书路径');
$param['pak'] = file_get_contents('私钥路径');

new ssl_upload($param, $SecretId, $SecretKey);

//输出json信息
function msg($msg='ok', $code=200){
	$arr = array(
		'code'=>$code,
		'msg'=>$msg,
	);
	exit(json_encode($arr, JSON_UNESCAPED_UNICODE));
}

//上传ssl证书
class ssl_upload{
	function __construct($opt, $SecretId, $SecretKey){
		$QC = new Qcloud($SecretId, $SecretKey);
		//公共参数
		$api = 'ssl.tencentcloudapi.com';
		$ver = '2019-12-05';
		
		$pbk = $opt['pbk'];
		$pak = isset($opt['pak']) ? $opt['pak']: null;
		$type = isset($opt['type']) ? $opt['type']:'SVR';
		$alias = isset($opt['alias']) ? $opt['alias']:'upload_'.date('Ymd');


		//功能参数
		$action = 'UploadCertificate';          //公共参数 功能
		$param['CertificatePublicKey']=$pbk;    //证书内容
		$param['CertificatePrivateKey']=$pak;   //私钥内容,证书类型为 SVR 时必填,为 CA 时可不填。
		$param['CertificateType']=$type;        //证书类型,默认 SVR。CA = 客户端证书,SVR = 服务器证书。
		$param['Alias']=$alias;                 //备注名称

		$QC->SetApi($api);      //接口请求域名
		$QC->SetVersion($ver);  //API版本号
		$rsp = $QC->send_reuqest($action, $param);

		if(isset($rsp['CertificateId'])){
			//上传成功
			$result = $rsp['CertificateId'];
			msg($result);
		}else if(isset($rsp['Error'])){
			//上传失败
			$code = $rsp['Error']['Code'];
			$msg = $rsp['Error']['Message'];
			msg($msg, $code);
		}else{
			//未知错误
			$msg = $rsp;
			$code = 500;
			msg($msg, $code);
		}
	}
}

后记

其实更新成功后,还可以做一些事情,比如通过Email/短信发送更新成功的信息,甚至可以再写代码,遍历CDN加速的域名,实现自动更换所有网站的ssl证书(主要用到的接口有DescribeDomainsConfig、UpdateDomainConfig),可以自行拓展。

0 人点赞