How to sign the URL scheme by an access key

To ensure that your app or site users launch Yandex Maps mobile app without limitations:

  1. Get an access-key. To do this, fill in the form.

  2. Use the following URL format:

    yandexmaps://maps.yandex.com/?{parameters}&client=<client ID>&signature=<signature>
    
    client
    The client ID you have got with the key. Even if you have multiple apps, you only need one ID.
    signature
    Signature is a string to be generated from the source URL using an access key.

How to generate a signed URL

  1. Compose a URL corresponding to the task for the app to perform. The following URL lets you show a point on the map:

    yandexmaps://maps.yandex.ru?ll=55.75,37.64&z=14
    
  2. Add the client parameter to the URL, enter the following identifier as its value:

    yandexmaps://maps.yandex.ru?ll=55.75,37.64&z=14&client=007
    
  3. Calculate a hash sum from the URL resulting from the previous step using SHA-256 hash function. Then use the access key to encrypt the resulting hash sum.

    The access key is an RSA key you have received after the registration. Physically, it is a record in a text file. The file can have a name, e.g., key.pem.

    Below is an example of encryption using the OpenSSL utility.

    Bash

    $ echo -n 'yandexmaps://maps.yandex.ru?ll=55.75,37.64&z=14&client=007' |
    openssl dgst -sha256 -sign key.pem |
    base64 |
    tr -d '\n' |
    python -c "import urllib, sys; print urllib.quote(sys.stdin.read(), safe='')"
    
  4. Generate a US-ASCII signature string.

    To send binary data in a URL, you should recode them to the US-ASCII character set. Therefore, first use base64 representation to convert your binary data to an ASCII string. Then convert the resulting string to US-ASCII via URL encoding.

    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='')
    
  5. Use the string resulting from the previous step as the signature parameter value. Example of a signed URL:

    yandexmaps://maps.yandex.ru?ll=55.75,37.64&z=14&client=007&signature=JYEYuBc7154%2Be%2BHHW8RKG0O2dVx%2B%2B...
    

How to convert an RSA key

We generate RSA keys in PEM format (PKCS1 standard). Other formats have also been adopted in mobile operating systems. To ensure compatibility, convert the key.

Conversion examples using the OpenSSL utility are given below.

Conversion of a text file in the PEM format to a binary DER file. As a result, we get a key in the DER format (PKCS1 standard):

$ openssl rsa -in key.pem -out key.der -outform DER

Conversion of a PKCS1 PEM key to a PKCS8 key:

$ openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in key-pkcs1.pem -out key-pkcs8.pem

Conversion of a PEM PKCS1 key to a DER key (PKCS8 standard):

$ 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

Examples of native application code

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 YaMapsStarter {

    private final String PRIVATE_KEY;

    // Generates a record using the 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!");
        }
    }

    // Generates a signed URI and starts Yandex Maps.
    public void openMap() {
        Uri uri = Uri.parse("yandexmaps://maps.yandex.ru").buildUpon()
            .appendQueryParameter("ll", 55.75,37.64)
            .appendQueryParameter("z", "14")
            .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.yandexmaps");
        startActivity(intent);
    }
}