haomingming 93d5aae5ac
Some checks failed
Build Docker / build (push) Has been cancelled
去日志
2025-12-02 16:41:30 +08:00

458 lines
14 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
namespace Extend\Ca;
use App\Constants\HttpEnumCode;
use App\Exception\BusinessException;
use App\Utils\Log;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Snowflake\IdGeneratorInterface;
use Hyperf\Utils\ApplicationContext;
use Psr\Container\ContainerInterface;
abstract class Ca
{
#[Inject]
protected ContainerInterface $container;
#[Inject]
protected Client $client;
protected string $app_id;
protected string $api_url;
protected string $secret;
public function __construct()
{
$this->container = ApplicationContext::getContainer();
$this->client = $this->container->get(Client::class);
}
/**
* 申请云证书
* @param array $data
* @param string $type
*/
abstract public function getCloudCert(array $data, string $type = "Personal");
/**
* 获取云证书签名
* @param string $user_id
* @param string $pin
* @param array $data
* @return mixed
*/
public function getCertSign(string $user_id, string $pin, array $data): mixed
{
$option = [
'form_params' => [
'entityId' => $user_id, // 用户唯一标识,由业务系统定义
'toSign' => hash_hmac("sha1", json_encode($data, JSON_UNESCAPED_UNICODE), $this->secret), // 签名原文
'pin' => $pin, // 证书PIN码
]
];
try {
$response = $this->httpRequest(
$this->api_url . '/cloud-certificate-service' . '/api/cloudCert/open/cert/sign',
$option
);
if (empty($response)) {
// 返回值为空
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
Log::getInstance()->info("获取云证书签名返回值",$option);
return $response;
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* PKCS7签名验证接口
* 对客户端签名信息进行验证,返回证书信息,同时可以配置回调服务,在验证成功后回调业务系统
*/
public function verifyPkcs7(string $sign_p7, array $data)
{
$generator = $this->container->get(IdGeneratorInterface::class);
$option = [
'form_params' => [
'opType' => "签名验证",
'requestId' => $generator->generate(),// 业务流水号,唯一
'signedData' => $sign_p7, // 签名值签名接口返回的signP7
'toSign' => hash_hmac("sha1", json_encode($data, JSON_UNESCAPED_UNICODE), $this->secret), // 签名原文
]
];
try {
$response = $this->httpRequest(
$this->api_url . '/signgw-service/api/signature/verifyPkcs7',
$option
);
if (empty($response)) {
// 返回值为空
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
return $response;
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* 添加签章配置
* @param string $user_id
* @param string $card_num
* @param array $data
* @return bool
*/
public function addUserSignConfig(string $user_id, string $card_num, array $data): bool
{
$arg = [
'form_params' => [
'userId' => $user_id,//用户标识信息为云证书entityId
'configKey' => $user_id, // 签章配置唯一标识,一张云证书配置一个
'keypairType' => "3", // 秘钥类型(3云证书)
'certSn' => $card_num, // 证书序列号,使用医生身份证号即可
'signType' => "4", // 签章方式(签章类型; 4客户端坐标签章;5客户端关键字签章;)
'signParam' => $data['sign_param'], // 签章配置,JSON
'sealImg' => $data['seal_img'], // 签章图片base64格式
'sealType' => "4",
'signTemplate' => "0",
]
];
try {
$option = [
"headers" => [
"app_id" => $this->app_id,
"signature" => $this->getRequestSign($arg),
],
];
$option = array_merge($arg, $option);
$response = $this->client->post($this->api_url . '/signature-server/api/open/signature/userSignConfig', $option);
if ($response->getStatusCode() != '200') {
// 请求失败
throw new BusinessException($response->getBody()->getContents());
}
$body = json_decode($response->getBody(), true);
if (empty($body)) {
// 返回值为空
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
if ($body['result_code'] == "1104"){
// 用户标识已经存在,请检查!
return true;
}
if ($body['result_code'] != 0) {
// 请求失败
if (!empty($body['result_msg'])) {
throw new BusinessException($body['result_msg']);
}
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
return true;
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* 删除签章配置
* @param string $user_id
* @return bool
*/
public function deleteUserSignConfig(string $user_id): bool
{
$option = [
'form_params' => [
'userId' => $user_id,//用户标识信息为云证书entityId
'configKey' => $user_id, // 签章配置唯一标识,一张云证书配置一个
]
];
try {
$this->httpRequest(
$this->api_url . '/signature-server/api/open/signature/delSignConfig',
$option
);
return true;
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* 获取用户签章图片
* @param string $user_id
* @return mixed
*/
public function getFetchUserSeal(string $user_id): mixed
{
$option = [
'form_params' => [
'userId' => $user_id,//用户标识信息为云证书entityId
]
];
try {
$response = $this->httpRequest(
$this->api_url . '/signature-server/api/open/signature/fetchUserSeal',
$option
);
if (empty($response)) {
// 返回值为空
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
return $response;
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* PDF添加电子签章
* @param string $user_id
* @param array $data
* @return mixed
*/
public function addSignPdf(string $user_id, array $data): mixed
{
$option = [
'multipart' => [
[
'name' => 'pdfFile',
'contents' => $data['pdf_file'],// 待签章PDF文件字节流
],
[
'name' => 'userId',
'contents' => $user_id, // 用户标识信息
],
[
'name' => 'configKey',
'contents' => $user_id, // 签章配置唯一标识
],
[
'name' => 'signParams',
'contents' => $data['sign_param'],// 签章参数JSON格式数据,如果不指定,那么以签章配置接口配置为准
],
[
'name' => 'cloudCertPass',
'contents' => $user_id,// 云证书PIN码云证书签章时使用
],
]
];
try {
$response = $this->httpRequest(
$this->api_url . '/signature-server/api/open/signature/signPdf',
$option
);
if (empty($response)) {
// 返回值为空
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
return $response;
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* 下载签章的pdf文件
* @param string $entity_id
* @param string $file_id
* @return string
*/
public function getSignedFile(string $entity_id, string $file_id): string
{
$arg = [
'form_params' => [
'userId' => $entity_id,// 用户标识信息为云证书entityId
'fileId' => $file_id,// 签章接口返回的文件标识
]
];
try {
$option = [
"headers" => [
"app_id" => $this->app_id,
"signature" => $this->getRequestSign($arg),
],
];
$option = array_merge($arg, $option);
$response = $this->client->post($this->api_url . '/signature-server/api/open/signature/fetchSignedFile', $option);
if ($response->getStatusCode() != '200') {
// 请求失败
throw new BusinessException($response->getBody()->getContents());
}
// 将 Stream 对象转换为字符串
return (string)$response->getBody()->getContents();
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* PDF签章文件验证
* @param array $data
* @return mixed
*/
public function verifyPdf(array $data): mixed
{
$option = [
'multipart' => [
[
'name' => 'pdfFile',
'contents' => $data['pdf_file'],// 待签章PDF文件字节流
],
]
];
try {
$response = $this->httpRequest(
$this->api_url . '/signature-server/api/open/signature/verifyPdf',
$option
);
if (empty($response)) {
// 返回值为空
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
return $response;
} catch (GuzzleException $e) {
throw new BusinessException($e->getMessage());
}
}
/**
* 封装公共请求
* @param string $path
* @param array $arg
* @return mixed
* @throws GuzzleException
*/
public function httpRequest(string $path, array $arg = []): mixed
{
// 请求前日志
// Log::getInstance()->info('CA接口请求开始', [
// '请求地址' => $path,
// '原始参数' => $arg,
// ]);
$option = [
"headers" => [
"app_id" => $this->app_id,
"signature" => $this->getRequestSign($arg),
],
];
$arg = array_merge($arg, $option);
// 合并 header 后的最终请求参数日志
// Log::getInstance()->info('CA接口最终请求参数', [
// '请求地址' => $path,
// '最终参数' => $arg,
// ]);
$response = $this->client->post($path, $arg);
// 记录原始响应(未解析)
// Log::getInstance()->info('CA接口原始响应', [
// '请求地址' => $path,
// 'HTTP状态码' => $response->getStatusCode(),
// '原始响应内容' => (string)$response->getBody(),
// ]);
if ($response->getStatusCode() != '200') {
// 请求失败
// Log::getInstance()->error('CA接口请求失败', [
// '请求地址' => $path,
// 'HTTP状态码' => $response->getStatusCode(),
// '响应内容' => $response->getBody()->getContents(),
// ]);
throw new BusinessException($response->getBody()->getContents());
}
$body = json_decode($response->getBody(), true);
// 记录接口返回的完整 body
// Log::getInstance()->info('CA接口返回BODY', [
// '请求地址' => $path,
// '返回数据' => $body,
// ]);
if (empty($body)) {
// 返回值为空
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
if ($body['result_code'] != 0) {
// 业务失败
// Log::getInstance()->error('CA业务处理失败', [
// '请求地址' => $path,
// '返回数据' => $body,
// ]);
if (!empty($body['result_msg'])) {
throw new BusinessException($body['result_msg']);
}
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
}
// 请求成功日志
// Log::getInstance()->info('CA接口请求成功', [
// '请求地址' => $path,
// '返回数据' => $body,
// ]);
return $body['body'];
}
/**
* 获取请求签名
* @param array $data
* @return string
*/
public function getRequestSign(array $data): string
{
$sign_data = array();
if (isset($data['form_params'])) {
$sign_data = $data['form_params'];
}
if (isset($data['multipart'])) {
foreach ($data['multipart'] as $item) {
// pdf进行签章时此参数为文件流不参与签名
if ($item['name'] == "pdfFile") {
continue;
}
$sign_data[$item['name']] = $item['contents'];
}
}
if (!empty($sign_data)) {
ksort($sign_data);
$data = implode('&', $sign_data);
} else {
$data = "";
}
return hash_hmac("sha1", $data, $this->secret);
}
}