Подпись ключом доступа
Чтобы пользователи вашего приложения или сайта не сталкивались с ограничениями:
-
Получите ключ доступа. Для этого заполните форму.
-
Используйте URL следующего формата:
yandexnavi://<путь>?<параметры>&client=<идентификатор клиента>&signature=<подпись>
client
- Идентификатор клиента, который вы получаете вместе с ключом. Даже если у вас несколько приложений, потребуется только один идентификатор.
signature
- Подпись — это строка, которую нужно сформировать из исходного URL с помощью ключа доступа.
Как сформировать URL с подписью
-
Составьте URL, соответствующий задаче, которую должно выполнить приложение. Следующий URL позволяет показать точку на карте:
yandexnavi://show_point_on_map?lat=55.75&lon=37.64&zoom=14
-
Добавьте к URL параметр
client
, в качестве значения укажите идентификатор:yandexnavi://show_point_on_map?lat=55.75&lon=37.64&zoom=14&client=007
-
Вычислите хэш-сумму от URL, полученного на предыдущем шаге, с помощью хеш-функции SHA-256. Затем зашифруйте полученную хэш-сумму с помощью ключа доступа.
Ключ доступа — это RSA-ключ, который вы получаете после регистрации. Физически это запись в текстовом файле. Называться файл может, например, так:
key.pem
.Ниже приведен пример шифрования с помощью утилиты OpenSSL.
Bash
$ echo -n 'yandexnavi://show_point_on_map?lat=55.75&lon=37.64&zoom=14&client=007' | openssl dgst -sha256 -sign key.pem | bаse64 | tr -d '\n' | python -c "import urllib, sys; print urllib.quote(sys.stdin.read(), safe='')"
-
Сформируйте US-ASCII строку подписи.
Чтобы передать двоичные данные в URL, их нужно перекодировать в набор символов US-ASCII. Поэтому сначала из двоичных данных сформируйте строку ASCII символов, используя base64-представление. Затем перекодируйте полученную строку в US-ASCII, используя механизм кодирования URL.
Python
from Crypto.PublicKey import RSA from Crypto.Hash import SHA256 from Crypto.Signature import PKCS1_v1_5 from base64 import b64encode from urllib import quote print "sign" with open("key.pem") as f: private_key = RSA.importKey(f.read()) h = SHA256.new(src_data) print "hash value" print h.hexdigest() signer = PKCS1_v1_5.new(private_key) signature = quote(b64encode(signer.sign(h)), safe='')
-
Используйте строку, полученную на предыдущем шаге, в качестве значения параметра
signature
. Пример URL с подписью:yandexnavi://show_point_on_map?lat=55.75&lon=37.64&zoom=14&client=007&signature=JYEYuBc7154%2Be%2BHHW8RKG0O2dVx%2B%2B...
Как конвертировать RSA-ключ
Мы генерируем RSA-ключи в формате PEM (стандарт PKCS1). В мобильных операционных системах используются и другие форматы. Чтобы обеспечить совместимость, выполните конвертацию.
Примеры конвертации с помощью утилиты OpenSSL приведены ниже.
Конвертация текстового PEM-файла в бинарный DER. В результате получим ключ в формате DER (стандарт PKCS1):
$ openssl rsa -in key.pem -out key.der -outform DER
Конвертация PEM-ключа PKCS1 в ключ PKCS8:
$ openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in key-pkcs1.pem -out key-pkcs8.pem
Конвертация PEM-ключа PKCS1 в ключ в формате DER (стандарт PKCS8):
$ openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in key-pkcs1.pem -out key-pkcs8.pem
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in key-pkcs8.pem -out key.der -nocrypt
Примеры кода нативных приложений
import android.content.Intent;
import android.net.Uri;
import android.util.Base64;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
public class YaNaviStarter {
private final String PRIVATE_KEY;
// Формирует подпись с помощью ключа.
public String sha256rsa(String key, String data) throws SecurityException {
String trimmedKey = key.replaceAll("-----\\w+ PRIVATE KEY-----", "")
.replaceAll("\\s", "");
try {
byte[] result = Base64.decode(trimmedKey, Base64.DEFAULT);
KeyFactory factory = KeyFactory.getInstance("RSA");
EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(result);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(factory.generatePrivate(keySpec));
signature.update(data.getBytes());
byte[] encrypted = signature.sign();
return Base64.encodeToString(encrypted, Base64.NO_WRAP);
} catch (Exception e) {
throw new SecurityException("Error calculating cipher data. SIC!");
}
}
// Формирует URI с подписью и запускает Яндекс Навигатор.
public void buildRoute() {
Uri uri = Uri.parse("yandexnavi://build_route_on_map").buildUpon()
.appendQueryParameter("lat_to", "55.680559")
.appendQueryParameter("lon_to", "37.549246")
.appendQueryParameter("client", "007").build();
uri = uri.buildUpon()
.appendQueryParameter("signature", sha256rsa(PRIVATE_KEY, uri.toString()))
.build();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setPackage("ru.yandex.yandexnavi");
startActivity(intent);
}
}
// Зашифровывает SHA256 с помощью DER-ключа.
func signString(string: String, key: SecKey) -> String {
let messageData = string.data(using:String.Encoding.utf8)!
var hash = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = hash.withUnsafeMutableBytes {digestBytes in
messageData.withUnsafeBytes {messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
let signature = SecKeyCreateSignature(key,
SecKeyAlgorithm.rsaSignatureDigestPKCS1v15SHA256,
hash as CFData,
nil) as Data?
return (signature?.base64EncodedString())!
}
// Преобразует PEM-ключ в DER-ключ.
func loadCert() -> SecKey {
let certificateData = NSData(
contentsOf:Bundle.main.url(forResource: "private_key", withExtension: "der")!
)
let options: [String: Any] =
[kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecAttrKeySizeInBits as String: 512]
let key = SecKeyCreateWithData(certificateData!,
options as CFDictionary,
nil)
return key!
}
// Формирует URL с подписью и запускает Яндекс Навигатор.
func buildRoute() {
var compoments = URLComponents(string: "yandexnavi://build_route_on_map")!
compoments.queryItems = [
URLQueryItem(name: "lat_to", value: "55.74"),
URLQueryItem(name: "lon_to", value: "37.64"),
URLQueryItem(name: "client", value: "007")
]
let signature = signString(string: compoments.string!, key: loadCert())
let final = compoments.string!.appending(
"&signature=" + signature.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!
)
UIApplication.shared.open(URL(string: final)!)
}
Идентификатор клиента, который вы получаете вместе с ключом. Даже если у вас несколько приложений, потребуется только один идентификатор.
Ключ доступа — это RSA-ключ, который вы получаете после регистрации. Физически это запись в текстовом файле. Называться файл может, например, так: key.pem
.
Подпись — это строка, которую нужно сформировать из исходного URL с помощью ключа доступа.