PHP / Docusign - Проверить подпись HMA C на завершенном событии - PullRequest
1 голос
/ 03 августа 2020

Я пытаюсь защитить свой URL-адрес обратного вызова, когда срабатывает завершенное событие.

Мой контроллер:

    public function callbackSubscriptionCompleted(
        int $subscriptionId,
        DocusignService $docusignService,
        Request $request
    ) {
        $signature = $request->headers->get("X-DocuSign-Signature-1");
        $payload = file_get_contents('php://input');

        $isValid = $docusignService->isValidHash($signature, $payload);
        if (!$isValid) {
            throw new ApiException(
                Response::HTTP_BAD_REQUEST,
                'invalid_subscription',
                'Signature not OK'
            );
        }

        return new Response("Signature OK", Response::HTTP_OK);
    }

Мои функции DocusignService:

    private function createEnvelope(Company $company, Subscription $subscription, LegalRepresentative $legalRepresentative, Correspondent $correspondent, $correspondents) : array
    {
       // ...
       $data = [
            'disableResponsiveDocument' => 'false',
            'emailSubject' => 'Your Subscription',
            'emailBlurb' => 'Subscription pending',
            'status' => 'sent',
            'notification' => [
                'useAccountDefaults' => 'false',
                'reminders' => [
                    'reminderEnabled' => 'true',
                    'reminderDelay' => '1',
                    'reminderFrequency' => '1'
                ],
                'expirations' => [
                    'expireEnabled' => 'True',
                    'expireAfter' => '250',
                    'expireWarn' => '2'
                ]
            ],
            'compositeTemplates' => [
                [
                    'serverTemplates' => [
                        [
                            'sequence' => '1',
                            'templateId' => $this->templateId
                        ]
                    ],
                    'inlineTemplates' => [
                        [
                            'sequence' => '2',
                            'recipients' => [
                                'signers' => [
                                    [
                                        'email' => $legalRepresentative->getEmail(),
                                        'name' => $legalRepresentative->getLastname(),
                                        'recipientId' => '1',
                                        'recipientSignatureProviders' => [
                                            [
                                                'signatureProviderName' => 'universalsignaturepen_opentrust_hash_tsp',
                                                'signatureProviderOptions' => [
                                                    'sms' => substr($legalRepresentative->getCellphone(), 0, 3) == '+33'  ? $legalRepresentative->getCellphone() : '+33' . substr($legalRepresentative->getCellphone(), 1),
                                                ]
                                            ]
                                        ],
                                        'roleName' => 'Client',
                                        'clientUserId' => $legalRepresentative->getId(),
                                        'tabs' => [
                                            'textTabs' => $textTabs,
                                            'radioGroupTabs' => $radioTabs,
                                            'checkboxTabs' => $checkboxTabs
                                        ]
                                    ]
                                ]
                            ]
                        ]
                    ]
                ]
            ],
            'eventNotification' => [
                "url" => $this->router->generate("api_post_subscription_completed_callback", [
                    "subscriptionId" => $subscription->getId()
                ], UrlGeneratorInterface::ABSOLUTE_URL),
                "includeCertificateOfCompletion" => "false",
                "includeDocuments" => "true",
                "includeDocumentFields" => "true",
                "includeHMAC" => "true",
                "requireAcknowledgment" => "true",
                "envelopeEvents" => [
                    [
                        "envelopeEventStatusCode" => "completed"
                    ]
                ]
            ]
        ];

        $response = $this->sendRequest(
            'POST',
            $this->getBaseUri() . '/envelopes',
            [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $this->getCacheToken()
            ],
            json_encode($data)
        );
    }

    public function isValidHash(string $signature, string $payload): bool
    {
        $hexHash = hash_hmac('sha256',utf8_encode($payload),utf8_encode($this->hmacKey));
        $base64Hash = base64_encode(hex2bin($hexHash));

        return $signature === $base64Hash;
    }

Я создал свой ключ hma c в моем Docusign Connect, и я получаю подпись в заголовке и полезных данных, но проверка всегда терпела неудачу. Я следил за документацией Docusign здесь Что случилось?

PS: Извините за мой плохой английский sh

1 Ответ

1 голос
/ 03 августа 2020

Мне нравится ваш код. Убедитесь, что вы отправляете только одну подпись HMA C. Таким образом, ваш hmacKey будет правильным.

В качестве проверки я бы распечатал utf8_encode($payload) и убедился, что он выглядит правильно (это должен быть входящий XML, без заголовков). Кроме того, я не думаю, что в начале должно быть CR / NL. Это разделитель между заголовком HTTP и телом.

Обновление

Я проверил, что код PHP с веб-сайта DocuSign работает правильно.

Значение полезной нагрузки должно не содержать ни начального, ни конечного символа новой строки. Он должен начинаться с <?xml и заканчиваться >

Я подозреваю, что ваше программное обеспечение добавляет начальную или конечную новую строку.

Секрет (из DocuSign) заканчивается = . Это значение в кодировке Base64. Не не декодировать его. Просто используйте его как строку.

Другое обновление

Полезная нагрузка (тело запроса) не содержит новых строк.

Если вы печатаете полезную нагрузку, вы вам нужно будет заключить его в <pre>, так как он включает < символов. Или посмотрите исходный код страницы.

Он содержит UTF-8 XML, например,

<?xml version="1.0" encoding="utf-8"?><DocuSignEnvelopeInformation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.docusign.net/API/3.0"><EnvelopeStatus><RecipientStatuses><RecipientStatus><Type>Signer</Type><Email>larry@worldwidecorp.us</Email><UserName>Larry Kluger</UserName><RoutingOrder>1</RoutingOrder><Sent>2020-08-05T03:11:13.057</Sent><Delivered>2020-08-05T03:11:27.657</Delivered><DeclineReason xsi:nil="true" /><Status>Delivered</Status><RecipientIPAddress>5.102.239.40</RecipientIPAddress><CustomFields /><TabStatuses><TabStatus><TabType>Custom</TabType><Status>Active</Status><XPosition>223</XPosition><YPosition>744....

Мы провели дополнительное тестирование, а строка

$payload = file_get_contents('php://input');

должно быть очень в начале вашего скрипта. Проблема в том, что фреймворк может изменять поток php://input, поэтому после этого он не будет работать должным образом.

Обратите внимание: эта страница с сайта Symfony - это означает, что способ получить тело запроса:

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ParameterBag;

$app->before(function (Request $request) {
        $payload = $request->getContent();
        hmac_verify($payload, $secret);
});

Я бы попытался использовать код Symfony вместо file_get_contents('php://input');

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...