Как реализовать части веб-подписи JSON (JWS) и веб-ключа JSON (JWK) протокола ACME с помощью PHP? ⇐ Php
Как реализовать части веб-подписи JSON (JWS) и веб-ключа JSON (JWK) протокола ACME с помощью PHP?
Мне могла бы понадобиться помощь с реализацией частей веб-подписи JSON (JWS) и веб-ключа JSON (JWK) протокола ACME с помощью PHP.
Мои конкретные вопросы/проблемы:
[*]
В документации ACME в качестве алгоритма предлагается ES256, но hash_hmac, похоже, не имеет такой опции. Какая функция в PHP может использовать ES256 --ИЛИ-- какую опцию ACME позволит создать hash_hmac?
[*]
Какие значения приемлемы для создания веб-ключа JSON? Это криптографическая функция или случайная строка текста? Каков срок службы значения JWK? то есть. это «пароль», который нужно использовать годами, на всю жизнь учетной записи ACME? Или он одноразовый для каждого сеанса?
[*]
Должен ли $key, третий аргумент hash_hmac, каким-то образом коррелировать с JWK, или они являются отдельными и произвольными? Каков срок службы хеш-ключа? то есть. это ключ, который нужно использовать годами, на протяжении всей жизни аккаунта ACME? Или он одноразовый для каждого сеанса?
[*]
Какие функции следует использовать для генерации значения ключа hash_hmac и значений JWK?
[*]
Реализует ли openssl_sign() с OPENSSL_ALGO_SHA256 ES256 (ECDSA)?
Вот скелетный код, который успешно выполняет запросы, но получает ошибки «неверного формата JWS»:
публичная функция CreateAccount(){ $Content = $this->PrepareAccountRequest(); $this->DoAccountAPICall($Content); } частная функция ПодготовкаAccountRequest(){ $Protected = $this->Base64URL(json_encode([ "алг" => "ES256", "jwk" => 'что это', "nonce" => $this->nonce, "url" => $this->aEndpoints['newAccount'] ])); $Payload = $this->Base64URL(json_encode([ "termsOfServiceAgreed" => правда, "контакт" => [ "mailto:[email protected]", "mailto:[email protected]" ] ])); $Signature = hash_hmac('sha256', $Protected . "." . $Payload, 'fsdjjkdlsdjlkasdf', false); $Content = json_encode([ 'защищено' => $Защищено, 'полезная нагрузка' => $Полезная нагрузка, 'подпись' => $Подпись ]); вернуть $Content; } частная функция DoAccountAPICall($Content){ $ch = Curl_init(); $Заголовки = [ «Управление кэшем: без кэша», 'Тип контента: приложение/jose+json', ]; curl_setopt($ch, CURLOPT_HTTPHEADER, $Headers); Curl_setopt($ch, CURLOPT_URL, $this->aEndpoints['newAccount']); Curl_setopt ($ ch, CURLOPT_HEADER, ложь); // Не включать заголовок в вывод. Curl_setopt ($ ch, CURLOPT_POST, правда); // Выполняем POST-запрос. Curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, правда); // Выводим ответ от Curl_exec Curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'HandleHeaderLine')); Curl_setopt ($ ch, CURLOPT_CONNECTTIMEOUT, 5); // Тайм-аут соединения через пять секунд Curl_setopt ($ ch, CURLOPT_DNS_CACHE_TIMEOUT, 300); // Кэшируем DNS на 5 минут. curl_setopt($ch, CURLOPT_POSTFIELDS, $Content); $Response = curl_exec($ch); если (curl_error($ch)) { echo 'Произошла ошибка CURL.'.curl_error($ch); } локон_закрыть ($ ч); echo 'Заголовки'; эхо PP($this->aLastHeaders); echo 'Ответ'; эхо $Ответ; echo 'Ответ JSON'; $aJSONResponse = json_decode($Response,true); ПП($aJSONResponse); } частная функция Base64URL($Input){ return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($Input)); } частный $aLastHeaders = массив(); частная функция HandleHeaderLine($curl, $header_line) { $this->aLastHeaders[] = $header_line; вернуть стрлен ($header_line); } ОБНОВЛЕНИЕ:
Думаю, я близок к тому, чтобы сделать эту работу, но не думаю, что ее части подходят друг к другу совсем правильно.
публичная функция CreateKeys(){ // Создаём новый закрытый ключ $this->oOpenSSLAsymmetricKey = openssl_pkey_new([ 'private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1' ]); // Получаем ключевые детали $aKeyDetails = openssl_pkey_get_details($this->oOpenSSLAsymmetricKey); $aJSONKeyDetails = [ 'kty' => 'EC', 'crv' => 'P-256', 'алг' => 'ES256', 'использовать' => 'подпись', 'x' => base64_encode($aKeyDetails['ec']['x']), 'y' => base64_encode($aKeyDetails['ec']['y']), 'd' => base64_encode($aKeyDetails['ec']['d']) ]; $this->JWK = json_encode($aJSONKeyDetails); эхо $this->JWK; } Это выводит:
{ "кты":"ЭК", "crv":"P-256", "алг":"ES256", "use":"подпись", "x":"mlX7B4E9POpWAhnyXJxW+xA7CbjJAXp5GVAYAkKZB\/4=", "y":"5HMng\/ZKHEcIM5\/srINSugx1pmTR5hhidIvJaTno5VU=", "d":"LoksxPi5A45IyBqECoXW18B1Ld6yvYAFpSooleX4UyU=" } Это выглядит нормально, и я думаю, что оно должно быть там, где указано whatisthis в функции PrepareAccountRequest() выше.
Я также добавил функцию подписи:
публичная функция Sign($Content){ // Подписываем текст. $BinarySignature = ''; openssl_sign($Content, $BinarySignature, $this->oOpenSSLAsymmetricKey, OPENSSL_ALGO_SHA256); // Проверка подписи с использованием открытого ключа. // Возвращает 1, если подпись правильная, 0, если она неверна, и -1 или false в случае ошибки. if(1 === openssl_verify($Content, $BinarySignature, $this->sPublicKey, OPENSSL_ALGO_SHA256)){ //echo 'Подпись подтверждена!'; вернуть $BinarySignature; }еще{ //echo 'ОШИБКА подписи!'; вернуть ложь; } } Который возвращает двоичный файл. Теперь строка, предшествующая которой, гласила:
$Signature = hash_hmac('sha256', $Protected . "." . $Payload, 'fsdjjkdlsdjlkasdf', false); заменяется на:
$Signature = $this->Base64URL($this->Sign($Protected . "." . $Полезная нагрузка)); $Encoded = $this->Base64URL($Protected.$Payload.$Signature); эхо $Закодировано; Что производит:
ZXlKaGJHY2lPaUpGVXpJMU5pSXNJbXAzYXlJNkludGNJbXQwZVZ3aU9sd2lSVU5jSWl4Y0ltTnlkbHdpT2x3aVVDMHlOVFpjSWl4Y0ltRnNaMXdpT2x3aVJWTXlOVFpjSW l4Y0luVnpaVndpT2x3aWMybG5YQ0lzWENKNFhDSTZYQ0pTSzFReE9XWTRSR2hPWmtkcVN6WkZOM0l4ZW5WUlVYbGxlVzlUTTJWMGVHWjBWMVJZTkUxbEt5dHpQVndpTEZ3aWVWd2lPbH dpZHpkQ1VWeGNYQzlZYmxReGVFTm1jMmxVT0Z4Y1hDOWxSVGt3TnpONE1qWm1aa2hCTW5WRVFXZFFja2w0YTBKRE1EMWNJaXhjSW1SY0lqcGNJa2hxWW1sWFdUVXpkbTexVDNKdlNXWk5 jVmdyTTI1eE4zQTViMWwzYjNOdFN6UnhNVGw2UTBsSlF6UTlYQ0o5SWl3aWJtOXVZMlVpT2lJelRUTjRPWFE0VURaeFZXdEZhVVV4TlRsaWiweedNV2xhTkRGRWJsSlhaMGc1Vm5sT2JXW lFSRlF6VmpoSmJFZFFhrFFpTENKMWNtd2lPaUpvZEhSd2N6cGNMMXd2WVdOdFpTMXpkR0ZuYVc1bkxYWXdNaTVoY0drdWJHVjBjMlZ1WTNKNWNIUXViM0puWEM5aFkyMWxYQzl1WlhjdFl XTmpkQ0o5ZXlKMFpYSnRjMDltVTJWeWRtbGpaVUZuY21WbFpDSTZkSEoxWlN3aVkyOXVkR0ZqZENJNld5SnRZV2xzZEc4NlkyVnlkQzFoWkcxcGJrQmxlR0Z0Y0d4bExtOXlaeUlzSW0xaG FXeDBienBoWkcxcGJrQmxlR0Z0Y0d4bExtOXlaeUpkZlFNRVVDSUZUVDVHSmRpd2lGdWZHZlZ1VjFFdWVIQWY3TDJzOUxRNzgzb1ZTYkljMHhBaUVBdm9MSXo0NXdlSG1pVmo3NW02NDR4MWtlW jY2bHpPZllGdlcyMlFhcDhTZw API ACME по-прежнему выдает сообщение об ошибке Ошибка анализа JWS.
Я думаю, что это выглядит в основном правильно, но я не уверен, что у меня есть все Base64 и Base64Web правильно, а также вся кодировка JSON и кодировка Base64 и конкатенации в правильном порядке.
Мне могла бы понадобиться помощь с реализацией частей веб-подписи JSON (JWS) и веб-ключа JSON (JWK) протокола ACME с помощью PHP.
Мои конкретные вопросы/проблемы:
[*]
В документации ACME в качестве алгоритма предлагается ES256, но hash_hmac, похоже, не имеет такой опции. Какая функция в PHP может использовать ES256 --ИЛИ-- какую опцию ACME позволит создать hash_hmac?
[*]
Какие значения приемлемы для создания веб-ключа JSON? Это криптографическая функция или случайная строка текста? Каков срок службы значения JWK? то есть. это «пароль», который нужно использовать годами, на всю жизнь учетной записи ACME? Или он одноразовый для каждого сеанса?
[*]
Должен ли $key, третий аргумент hash_hmac, каким-то образом коррелировать с JWK, или они являются отдельными и произвольными? Каков срок службы хеш-ключа? то есть. это ключ, который нужно использовать годами, на протяжении всей жизни аккаунта ACME? Или он одноразовый для каждого сеанса?
[*]
Какие функции следует использовать для генерации значения ключа hash_hmac и значений JWK?
[*]
Реализует ли openssl_sign() с OPENSSL_ALGO_SHA256 ES256 (ECDSA)?
Вот скелетный код, который успешно выполняет запросы, но получает ошибки «неверного формата JWS»:
публичная функция CreateAccount(){ $Content = $this->PrepareAccountRequest(); $this->DoAccountAPICall($Content); } частная функция ПодготовкаAccountRequest(){ $Protected = $this->Base64URL(json_encode([ "алг" => "ES256", "jwk" => 'что это', "nonce" => $this->nonce, "url" => $this->aEndpoints['newAccount'] ])); $Payload = $this->Base64URL(json_encode([ "termsOfServiceAgreed" => правда, "контакт" => [ "mailto:[email protected]", "mailto:[email protected]" ] ])); $Signature = hash_hmac('sha256', $Protected . "." . $Payload, 'fsdjjkdlsdjlkasdf', false); $Content = json_encode([ 'защищено' => $Защищено, 'полезная нагрузка' => $Полезная нагрузка, 'подпись' => $Подпись ]); вернуть $Content; } частная функция DoAccountAPICall($Content){ $ch = Curl_init(); $Заголовки = [ «Управление кэшем: без кэша», 'Тип контента: приложение/jose+json', ]; curl_setopt($ch, CURLOPT_HTTPHEADER, $Headers); Curl_setopt($ch, CURLOPT_URL, $this->aEndpoints['newAccount']); Curl_setopt ($ ch, CURLOPT_HEADER, ложь); // Не включать заголовок в вывод. Curl_setopt ($ ch, CURLOPT_POST, правда); // Выполняем POST-запрос. Curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, правда); // Выводим ответ от Curl_exec Curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'HandleHeaderLine')); Curl_setopt ($ ch, CURLOPT_CONNECTTIMEOUT, 5); // Тайм-аут соединения через пять секунд Curl_setopt ($ ch, CURLOPT_DNS_CACHE_TIMEOUT, 300); // Кэшируем DNS на 5 минут. curl_setopt($ch, CURLOPT_POSTFIELDS, $Content); $Response = curl_exec($ch); если (curl_error($ch)) { echo 'Произошла ошибка CURL.'.curl_error($ch); } локон_закрыть ($ ч); echo 'Заголовки'; эхо PP($this->aLastHeaders); echo 'Ответ'; эхо $Ответ; echo 'Ответ JSON'; $aJSONResponse = json_decode($Response,true); ПП($aJSONResponse); } частная функция Base64URL($Input){ return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($Input)); } частный $aLastHeaders = массив(); частная функция HandleHeaderLine($curl, $header_line) { $this->aLastHeaders[] = $header_line; вернуть стрлен ($header_line); } ОБНОВЛЕНИЕ:
Думаю, я близок к тому, чтобы сделать эту работу, но не думаю, что ее части подходят друг к другу совсем правильно.
публичная функция CreateKeys(){ // Создаём новый закрытый ключ $this->oOpenSSLAsymmetricKey = openssl_pkey_new([ 'private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1' ]); // Получаем ключевые детали $aKeyDetails = openssl_pkey_get_details($this->oOpenSSLAsymmetricKey); $aJSONKeyDetails = [ 'kty' => 'EC', 'crv' => 'P-256', 'алг' => 'ES256', 'использовать' => 'подпись', 'x' => base64_encode($aKeyDetails['ec']['x']), 'y' => base64_encode($aKeyDetails['ec']['y']), 'd' => base64_encode($aKeyDetails['ec']['d']) ]; $this->JWK = json_encode($aJSONKeyDetails); эхо $this->JWK; } Это выводит:
{ "кты":"ЭК", "crv":"P-256", "алг":"ES256", "use":"подпись", "x":"mlX7B4E9POpWAhnyXJxW+xA7CbjJAXp5GVAYAkKZB\/4=", "y":"5HMng\/ZKHEcIM5\/srINSugx1pmTR5hhidIvJaTno5VU=", "d":"LoksxPi5A45IyBqECoXW18B1Ld6yvYAFpSooleX4UyU=" } Это выглядит нормально, и я думаю, что оно должно быть там, где указано whatisthis в функции PrepareAccountRequest() выше.
Я также добавил функцию подписи:
публичная функция Sign($Content){ // Подписываем текст. $BinarySignature = ''; openssl_sign($Content, $BinarySignature, $this->oOpenSSLAsymmetricKey, OPENSSL_ALGO_SHA256); // Проверка подписи с использованием открытого ключа. // Возвращает 1, если подпись правильная, 0, если она неверна, и -1 или false в случае ошибки. if(1 === openssl_verify($Content, $BinarySignature, $this->sPublicKey, OPENSSL_ALGO_SHA256)){ //echo 'Подпись подтверждена!'; вернуть $BinarySignature; }еще{ //echo 'ОШИБКА подписи!'; вернуть ложь; } } Который возвращает двоичный файл. Теперь строка, предшествующая которой, гласила:
$Signature = hash_hmac('sha256', $Protected . "." . $Payload, 'fsdjjkdlsdjlkasdf', false); заменяется на:
$Signature = $this->Base64URL($this->Sign($Protected . "." . $Полезная нагрузка)); $Encoded = $this->Base64URL($Protected.$Payload.$Signature); эхо $Закодировано; Что производит:
ZXlKaGJHY2lPaUpGVXpJMU5pSXNJbXAzYXlJNkludGNJbXQwZVZ3aU9sd2lSVU5jSWl4Y0ltTnlkbHdpT2x3aVVDMHlOVFpjSWl4Y0ltRnNaMXdpT2x3aVJWTXlOVFpjSW l4Y0luVnpaVndpT2x3aWMybG5YQ0lzWENKNFhDSTZYQ0pTSzFReE9XWTRSR2hPWmtkcVN6WkZOM0l4ZW5WUlVYbGxlVzlUTTJWMGVHWjBWMVJZTkUxbEt5dHpQVndpTEZ3aWVWd2lPbH dpZHpkQ1VWeGNYQzlZYmxReGVFTm1jMmxVT0Z4Y1hDOWxSVGt3TnpONE1qWm1aa2hCTW5WRVFXZFFja2w0YTBKRE1EMWNJaXhjSW1SY0lqcGNJa2hxWW1sWFdUVXpkbTexVDNKdlNXWk5 jVmdyTTI1eE4zQTViMWwzYjNOdFN6UnhNVGw2UTBsSlF6UTlYQ0o5SWl3aWJtOXVZMlVpT2lJelRUTjRPWFE0VURaeFZXdEZhVVV4TlRsaWiweedNV2xhTkRGRWJsSlhaMGc1Vm5sT2JXW lFSRlF6VmpoSmJFZFFhrFFpTENKMWNtd2lPaUpvZEhSd2N6cGNMMXd2WVdOdFpTMXpkR0ZuYVc1bkxYWXdNaTVoY0drdWJHVjBjMlZ1WTNKNWNIUXViM0puWEM5aFkyMWxYQzl1WlhjdFl XTmpkQ0o5ZXlKMFpYSnRjMDltVTJWeWRtbGpaVUZuY21WbFpDSTZkSEoxWlN3aVkyOXVkR0ZqZENJNld5SnRZV2xzZEc4NlkyVnlkQzFoWkcxcGJrQmxlR0Z0Y0d4bExtOXlaeUlzSW0xaG FXeDBienBoWkcxcGJrQmxlR0Z0Y0d4bExtOXlaeUpkZlFNRVVDSUZUVDVHSmRpd2lGdWZHZlZ1VjFFdWVIQWY3TDJzOUxRNzgzb1ZTYkljMHhBaUVBdm9MSXo0NXdlSG1pVmo3NW02NDR4MWtlW jY2bHpPZllGdlcyMlFhcDhTZw API ACME по-прежнему выдает сообщение об ошибке Ошибка анализа JWS.
Я думаю, что это выглядит в основном правильно, но я не уверен, что у меня есть все Base64 и Base64Web правильно, а также вся кодировка JSON и кодировка Base64 и конкатенации в правильном порядке.
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение