thumbprint === $thumbprint) { $found = $certificate; break; } } if ($found === false) { throw new \Exception('Сертификат с отпечатком: "' . $thumbprint . '" не найден'); } return $found; } /** * Создает совмещенную (присоединенную) подпись сообщения * * @param string $thumbprint отпечаток сертификата * @param string $unencryptedMessage подписываемое сообщение * @param string|null $pin пин-код доступа к закрытому ключу * * @throws \Exception * @return string подпись в формате PKCS#7 */ public static function createAttachedSignature(string $thumbprint, string $unencryptedMessage, string $pin = null) { $cadesCertificate = self::getCadesCertificate($thumbprint); try { $cadesAttrs = new \CPAttribute(); $cadesSignedData = new \CPSignedData(); $cadesSigner = new \CPSigner(); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при инициализации подписи')); } // Дату и время устанавливаем в формате generalizedTime https://docs.cryptopro.ru/pki/cplib/class/cdatetime?id=cdatetime-1 $currentDateTime = (new \DateTime())->format('YmdHis.u') . 'Z'; try { $cadesAttrs->set_Name(AUTHENTICATED_ATTRIBUTE_SIGNING_TIME); $cadesAttrs->set_Value($currentDateTime); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке времени подписи')); } $messageBase64 = base64_encode($unencryptedMessage); try { if (!empty($pin)) { $cadesSigner->set_KeyPin($pin); } $cadesSigner->set_Certificate($cadesCertificate); /** @var \CPAttributes $cadesAuthAttrs */ $cadesAuthAttrs = $cadesSigner->get_AuthenticatedAttributes(); $cadesAuthAttrs->Add($cadesAttrs); $cadesSignedData->set_ContentEncoding(BASE64_TO_BINARY); $cadesSignedData->set_Content($messageBase64); $cadesSigner->set_Options(CERTIFICATE_INCLUDE_WHOLE_CHAIN); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при указании данных для подписи')); } try { /** @var string $signature */ $signature = $cadesSignedData->SignCades( $cadesSigner, PKCS7_TYPE, false, ENCODE_BASE64 ); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при подписании данных')); } return $signature; } /** * Создает отсоединенную (открепленную) подпись сообщения * * @param string $thumbprint отпечаток сертификата * @param string $messageHash хеш подписываемого сообщения, сгенерированный по ГОСТ Р 34.11-2012 256 бит * @param string|null $pin пин-код доступа к закрытому ключу * * @throws \Exception * @return string подпись в формате PKCS#7 */ public static function createDetachedSignature(string $thumbprint, string $messageHash, string $pin = null) { $cadesCertificate = self::getCadesCertificate($thumbprint); try { $cadesAttrs = new \CPAttribute(); $cadesSignedData = new \CPSignedData(); $cadesHashedData = new \CPHashedData(); $cadesSigner = new \CPSigner(); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при инициализации подписи')); } // Дату и время устанавливаем в формате generalizedTime https://docs.cryptopro.ru/pki/cplib/class/cdatetime?id=cdatetime-1 $currentDateTime = (new \DateTime())->format('YmdHis.u') . 'Z'; try { $cadesAttrs->set_Name(AUTHENTICATED_ATTRIBUTE_SIGNING_TIME); $cadesAttrs->set_Value($currentDateTime); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке времени подписи')); } try { if (!empty($pin)) { $cadesSigner->set_KeyPin($pin); } $cadesSigner->set_Certificate($cadesCertificate); $cadesAuthAttrs = $cadesSigner->get_AuthenticatedAttributes(); $cadesAuthAttrs->Add($cadesAttrs); $cadesSigner->set_Options(CERTIFICATE_INCLUDE_WHOLE_CHAIN); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке сертификата')); } try { $cadesHashedData->set_Algorithm(CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256); $cadesHashedData->SetHashValue($messageHash); // Для получения объекта отсоединенной (открепленной) подписи, необходимо задать любой контент. // Этот баг описан на форуме. // https://www.cryptopro.ru/forum2/default.aspx?g=posts&m=78553#post78553 $cadesSignedData->set_Content(123); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке хеша')); } try { $signature = $cadesSignedData->SignHash($cadesHashedData, $cadesSigner, PKCS7_TYPE); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при подписании данных')); } return $signature; } /** * добавляет совмещенную (присоединенную) подпись к раннее подписанному документу (реализует метод coSign) * * @param string $thumbprint отпечаток сертификата * @param string $signedMessage * @param string|null $pin пин-код доступа к закрытому ключу * * @throws \Exception * @return string */ public static function addAttachedSignature(string $thumbprint, string $signedMessage, string $pin = null) { $cadesCertificate = self::getCadesCertificate($thumbprint); try { $cadesAttrs = new \CPAttribute(); $cadesSignedData = new \CPSignedData(); $cadesSigner = new \CPSigner(); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при инициализации подписи')); } // Дату и время устанавливаем в формате generalizedTime https://docs.cryptopro.ru/pki/cplib/class/cdatetime?id=cdatetime-1 $currentDateTime = (new \DateTime())->format('YmdHis.u') . 'Z'; try { $cadesAttrs->set_Name(AUTHENTICATED_ATTRIBUTE_SIGNING_TIME); $cadesAttrs->set_Value($currentDateTime); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке времени подписи')); } $messageBase64 = base64_encode($signedMessage); try { if (!empty($pin)) { $cadesSigner->set_KeyPin($pin); } $cadesSigner->set_Certificate($cadesCertificate); $cadesAuthAttrs = $cadesSigner->get_AuthenticatedAttributes(); $cadesAuthAttrs->Add($cadesAttrs); $cadesSignedData->set_ContentEncoding(BASE64_TO_BINARY); $cadesSignedData->set_Content($messageBase64); $cadesSigner->set_Options(CERTIFICATE_INCLUDE_WHOLE_CHAIN); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при указании данных для подписи')); } try { $cadesSignedData->VerifyCades($signedMessage, PKCS7_TYPE); $signature = $cadesSignedData->CoSignCades($cadesSigner, PKCS7_TYPE); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при подписании данных')); } return $signature; } /** * Добавляет отсоединенную (открепленную) подпись к раннее подписанному документу (реализует метод coSign) * * @param string $thumbprint отпечаток сертификата * @param string $signedMessage подписанное сообщение * @param string $messageHash хеш подписываемого сообщения, сгенерированный по ГОСТ Р 34.11-2012 256 бит * @param string|null $pin пин-код доступа к закрытому ключу * * @throws \Exception * @return string подпись в формате PKCS#7 */ public static function addDetachedSignature(string $thumbprint, string $signedMessage, string $messageHash, string $pin = null) { $cadesCertificate = self::getCadesCertificate($thumbprint); try { $cadesAttrs = new \CPAttribute(); $cadesSignedData = new \CPSignedData(); $cadesHashedData = new \CPHashedData(); $cadesSigner = new \CPSigner(); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при инициализации подписи')); } // Дату и время устанавливаем в формате generalizedTime https://docs.cryptopro.ru/pki/cplib/class/cdatetime?id=cdatetime-1 $currentDateTime = (new \DateTime())->format('YmdHis.u') . 'Z'; try { $cadesAttrs->set_Name(AUTHENTICATED_ATTRIBUTE_SIGNING_TIME); $cadesAttrs->set_Value($currentDateTime); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке времени подписи')); } try { if (!empty($pin)) { $cadesSigner->set_KeyPin($pin); } $cadesSigner->set_Certificate($cadesCertificate); $cadesAuthAttrs = $cadesSigner->get_AuthenticatedAttributes(); $cadesAuthAttrs->Add($cadesAttrs); $cadesSigner->set_Options(CERTIFICATE_INCLUDE_WHOLE_CHAIN); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке сертификата')); } try { $cadesHashedData->set_Algorithm(CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256); $cadesHashedData->SetHashValue($messageHash); // Для получения объекта отсоединенной (открепленной) подписи, необходимо задать любой контент. // Этот баг описан на форуме. // https://www.cryptopro.ru/forum2/default.aspx?g=posts&m=78553#post78553 $cadesSignedData->set_Content(123); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке хеша')); } try { $cadesSignedData->VerifyHash($cadesHashedData, $signedMessage, PKCS7_TYPE); $signature = $cadesSignedData->CoSignHash($cadesHashedData, $cadesSigner, PKCS7_TYPE); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при подписании данных')); } return $signature; } /** * создает XML подпись для документа в формате XML * * @return void */ public static function createXMLSignature() { } /** * Создает хеш сообщения по ГОСТ Р 34.11-2012 256 бит * * @param string $unencryptedMessage сообщение для хеширования * * @throws \Exception * @return string */ public static function createHash(string $unencryptedMessage) { try { $cadesHashedData = new \CPHashedData(); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при инициализации хэширования')); } $messageBase64 = base64_encode($unencryptedMessage); try { $cadesHashedData->set_Algorithm(CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256); $cadesHashedData->set_DataEncoding(BASE64_TO_BINARY); $cadesHashedData->Hash($messageBase64); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при установке хэширования')); } try { $hash = $cadesHashedData->get_Value(); } catch (\Throwable $e) { throw new \Exception(ErrorMessageHelper::getErrorMessage($e, 'Ошибка при создании хэша')); } return $hash; } /** * возвращает информацию о CSP и плагине * * @return void */ public static function getSystemInfo() { } /** * @param int $storeLocation * @param string $storeName * @param bool $validOnly Логическое значение , указывающее, возвращаются ли только действительные сертификаты. * Если значение равно true, поиск не вернет следующие типы сертификатов: *