...
- Отсортировать параметры по имени в utf8, сравнивая побайтово. Параметры берутся из GET URI или из тела POST запроса (когда ContentType равен application/xwwwformurlencoded)
- URLкодировать имена и значения параметров по следующим правилам:
- Не кодировать определённые в RFC 3986 незарезервированные символы. Эти символы: Az, az, 09, минус (-), подчёркивание (_), точка (.) и тильда (~).
- Все остальные символы должны быть закодированы как %XY, где X и Y это шестнадцатеричные символы от 0 до 9 и от A до F (заглавные). Расширенные utf8 символы кодируются как %XY%ZA…
- Пробел кодируется как %20 (и не как +, как обычно делается в URL)
- Кодированные имена параметров отделяются от кодированных значений знаком равно (=, ASCIIкод 61), даже если параметр имеет пустое значение.
- Пары имя – значение разделяются символом амперсанда (&, ASCII – код 38).
- Создать строку для подписи в соответствии со следующей псевдо – грамматикой (где “\n” это ASCII – символов перевода строки):12345678910StringToSign = HTTPVerb + "\n" +ValueOfHostHeaderInLowercase + "\n" +HTTPRequestURI + "\n" +CanonicalizedQueryString# HTTPVerb - метод POST, GET, PUT или DELETE.# ValueOfHostHeaderInLowercase - параметр host из заголовка HTTP запроса.# HTTPRequestURI - компонент URI, абсолютный путь до, но не включая GET-параметров.# Для пустого пути ожидается '/'.# CanonicalizedQueryString - строка с предыдущего шага.
- Рассчитать совместимый с RFC 2104 HMAC по только что созданной строке StringToSign, используя секретный ключ партёра как ключ алгоритма, и SHA256 как способ хэширования.
- Сконвертировать полученную подпись в base64.
- Использовать результат как значение параметра check.
Пример расчета:php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | function http_build_query_rfc_3986($queryData, $argSeparator='&') { $r = ''; $queryData = (array) $queryData; if(!empty($queryData)) { foreach($queryData as $k=>$queryVar) { $r .= $argSeparator; $r .= $k; $r .= '='; $r .= rawurlencode($queryVar); } } return trim($r,$argSeparator); } function sign($method, $url, $params, $secretKey, $skipPort=False) { ksort($params, SORT_LOCALE_STRING); $urlParsed = parse_url($url); $path = $urlParsed['path']; $host = isset($urlParsed['host'])? $urlParsed['host']: ""; if (isset($urlParsed['port']) && $urlParsed['port'] != 80) { if (!$skipPort) { $host .= ":{$urlParsed['port']}"; } } $method = strtoupper($method) == 'POST'? 'POST': 'GET'; $data = implode("\n", array( $method, $host, $path, http_build_query_rfc_3986($params) ) ); $signature = base64_encode( hash_hmac("sha256", "{$data}", "{$secretKey}", TRUE ) ); return $signature; } // Пример вызова: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | from urllib.parse import |
urlparse,quote import |
hashlib import |
urllib import |
base64 import |
hmac def |
sign(method, |
url, |
params, |
secret_key, |
exclude=['check', |
'mac']): |
""" |
Типовой метод для подписи HTTP запросов |
""" |
url_parsed |
= |
urlparse |
(url) |
keys |
= |
[param for |
param in |
params if |
param not |
in |
exclude] |
keys.sort() |
result |
= |
[] |
for |
key in |
keys: |
value |
= |
quote( |
(params.get(key) |
or |
'').encode('utf-8'), |
safe='~' |
) |
result.append('{}={}'.format(key, |
value)) |
data |
= |
"\n".join([ |
method, |
url_parsed.hostname, |
url_parsed.path, |
"&".join(result) |
])
]) secrkey = secret_key.encode('utf-8') mesg=data.encode('utf-8') print(secrkey,mesg) digest = hmac.new( |
secrkey, |
mesg, |
hashlib.sha256 |
).digest() |
signature |
= |
base64.b64encode(digest) print(signature.decode('utf-8')) |
return |
signature # Пример вызова sign("GET", |
{ |
'login |
': 'newlogin~_-.'}, |
' |
165165165sd'); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package ru.rficb.alba; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.net.URI; import java.net.URISyntaxException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; /** * Подпись версии 2.0+ */ public class AlbaSigner { public static String sign(String method, String url, Map<String, String> params, String secretKey) throws URISyntaxException, UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException { URI uri = new URI(url); List keys = new ArrayList<>(params.keySet()); Collections.sort(keys); StringBuilder sb = new StringBuilder(); for (String key: keys) { if (sb.length() > 0) { sb.append("&"); } sb.append(String.format("%s=%s", key, URLEncoder.encode(params.get(key), "UTF-8"))); } String urlParameters = sb.toString(); String data = method.toUpperCase() + "\n" + uri.getHost() + "\n" + uri.getPath() + "\n" + urlParameters; Mac hmacInstance = Mac.getInstance("HmacSHA256"); Charset charSet = Charset.forName("UTF-8"); SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(charSet.encode(secretKey).array(), "HmacSHA256"); hmacInstance.init(keySpec); return DatatypeConverter.printBase64Binary(hmacInstance.doFinal(data.getBytes("UTF-8"))); } } |