Files
alfa-bank-sdk/src/Services/OpenSslCmsSigner.php
Anatoliy Poloz b7b2a8fd61 refactor(Sbp)
2026-03-17 12:26:30 +03:00

72 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Apoloz\AlfaBank\Services;
use Apoloz\AlfaBank\Contracts\SignerInterface;
use Apoloz\AlfaBank\Exceptions\SignerException;
class OpenSslCmsSigner implements SignerInterface
{
public function __construct(
protected string $certificatePath,
protected string $privateKeyPath
) {}
public function sign(string $data): string
{
if (!file_exists($this->privateKeyPath)) {
throw new SignerException("Private key file not found: {$this->privateKeyPath}");
}
if (!file_exists($this->certificatePath)) {
throw new SignerException("Certificate file not found: {$this->certificatePath}");
}
$privateKey = openssl_pkey_get_private(file_get_contents($this->privateKeyPath));
$cert = openssl_x509_read(file_get_contents($this->certificatePath));
if (!$privateKey || !$cert) {
throw new SignerException("Could not read private key or certificate for signing.");
}
$inFile = tempnam(sys_get_temp_dir(), 'alfa_in');
$outFile = tempnam(sys_get_temp_dir(), 'alfa_out');
file_put_contents($inFile, $data);
// Используем PKCS7_DETACHED согласно документации Альфа-Банка для digestSignatures
$status = openssl_pkcs7_sign(
$inFile,
$outFile,
$cert,
$privateKey,
[],
PKCS7_BINARY | PKCS7_DETACHED
);
if (!$status) {
@unlink($inFile);
@unlink($outFile);
throw new SignerException("OpenSSL sign failed: " . openssl_error_string());
}
$signedData = file_get_contents($outFile);
@unlink($inFile);
@unlink($outFile);
// Надежно извлекаем только Base64 блок подписи из S/MIME или PEM структуры
$parts = explode("\n\n", $signedData);
foreach ($parts as $part) {
// Очищаем часть от пробелов и переносов
$cleaned = str_replace(["\n", "\r", ' '], '', $part);
// Ищем блок, который выглядит как длинный Base64 (больше 128 символов) и не содержит S/MIME заголовков
if (preg_match('/^[a-zA-Z0-9\/+]+=*$/', $cleaned) && strlen($cleaned) > 128) {
return $cleaned;
}
}
throw new SignerException("Failed to extract signature body from CMS container. Output: " . substr($signedData, 0, 100));
}
}