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)); } }