#
URL Signing Algorithm
In case you don't want to use Verify API to sign your URLs, we can provide you with the algorithm to do so.
#
Overview
Signing the URL is based on the full query string of the URL, which includes all parameters and their values.
The signed URL contains the full query string and an additional parameter called re-signature.
#
Algorithm Steps
- Transform the query string to lower case
- Sort the parameters in canonical order
- Use SHA256 to create the hash for the canonical ordered query string. The secret for the hashing is your API key.
- Convert the hash result to hex string and transform it to lower case
API Key
Your API Key can be found on Realeyes XP Developer Console.
#
Implementation Example
C# Implementation
Here you can find the implementation in C#:
public string GetCannonicalQueryString(string currentQueryString)
{
var parameters = QueryHelpers.ParseQuery(currentQueryString);
var uri = "";
foreach (var key in parameters.Keys.Select(k => k.ToLowerInvariant()).OrderBy(x => x))
{
foreach (var value in parameters[key].Select(v => v.ToLowerInvariant()).OrderBy(x => x))
{
uri = QueryHelpers.AddQueryString(uri, key, value);
}
}
return uri;
}
public string Sign(string secret, string currentQueryString)
{
var cannonicalQueryString = GetCannonicalQueryString(currentQueryString);
var stringToSign = $"{cannonicalQueryString}{secret}";
var hash = System.Security.Cryptography.SHA256.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign));
return System.Convert.ToHexString(hash).ToLowerInvariant();
}
#
Step-by-Step Example
#
Input
Original query string:
?userId=User123&age=25&gender=Male
API Key: your-secret-api-key
#
Step 1: Transform to Lower Case
?userid=user123&age=25&gender=male
#
Step 2: Sort Parameters in Canonical Order
?age=25&gender=male&userid=user123
#
Step 3: Create SHA256 Hash
String to sign:
?age=25&gender=male&userid=user123your-secret-api-key
SHA256 hash (example):
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2
#
Step 4: Convert to Hex and Lower Case
Final signature:
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2
#
Result
Signed query string:
?age=25&gender=male&userid=user123&re-signature=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2
#
Alternative: Use API
API Endpoint
You can create the signed query string with POST /api/v1/redirect/create-signature endpoint with the full query string as the queryString parameter in the request body.
For more information, see the Incoming URL Signing section.
#
Validation
Validate Signature
The signed query string can be checked by calling the POST /api/v1/redirect/validate-signature endpoint with the signed query string as the queryString parameter in the request body. This will return whether the signature is valid or not.
For more information, see the Audit Verifications section.
#
Python Implementation
import hashlib
from urllib.parse import parse_qs, urlencode
def get_canonical_query_string(query_string):
"""Convert query string to canonical form (lowercase and sorted)"""
# Remove leading '?' if present
if query_string.startswith('?'):
query_string = query_string[1:]
# Parse query string
params = parse_qs(query_string)
# Convert to lowercase and sort
canonical_params = {}
for key in sorted(params.keys()):
lowercase_key = key.lower()
lowercase_values = sorted([v.lower() for v in params[key]])
canonical_params[lowercase_key] = lowercase_values
# Build canonical query string
canonical_pairs = []
for key in sorted(canonical_params.keys()):
for value in canonical_params[key]:
canonical_pairs.append(f"{key}={value}")
return "?" + "&".join(canonical_pairs)
def sign_query_string(api_key, query_string):
"""Sign a query string using SHA256"""
# Get canonical form
canonical = get_canonical_query_string(query_string)
# Create string to sign
string_to_sign = f"{canonical}{api_key}"
# Create SHA256 hash
hash_object = hashlib.sha256(string_to_sign.encode('utf-8'))
signature = hash_object.hexdigest().lower()
return signature
def create_signed_url(api_key, query_string):
"""Create a signed URL with re-signature parameter"""
signature = sign_query_string(api_key, query_string)
# Add signature to query string
if '?' in query_string:
signed_url = f"{query_string}&re-signature={signature}"
else:
signed_url = f"?re-signature={signature}"
return signed_url
# Example usage
api_key = "your-secret-api-key"
query_string = "?userId=User123&age=25&gender=Male"
signed_url = create_signed_url(api_key, query_string)
print(f"Signed URL: {signed_url}")
#
JavaScript Implementation
async function getCanonicalQueryString(queryString) {
// Remove leading '?' if present
if (queryString.startsWith('?')) {
queryString = queryString.substring(1);
}
// Parse query string
const params = new URLSearchParams(queryString);
// Convert to lowercase and sort
const sortedParams = new URLSearchParams();
const keys = Array.from(params.keys()).sort();
for (const key of keys) {
const values = params.getAll(key).map(v => v.toLowerCase()).sort();
for (const value of values) {
sortedParams.append(key.toLowerCase(), value);
}
}
return '?' + sortedParams.toString();
}
async function signQueryString(apiKey, queryString) {
// Get canonical form
const canonical = await getCanonicalQueryString(queryString);
// Create string to sign
const stringToSign = canonical + apiKey;
// Create SHA256 hash
const encoder = new TextEncoder();
const data = encoder.encode(stringToSign);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// Convert to hex string
const hashArray = Array.from(new Uint8Array(hashBuffer));
const signature = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return signature.toLowerCase();
}
async function createSignedUrl(apiKey, queryString) {
const signature = await signQueryString(apiKey, queryString);
// Add signature to query string
const separator = queryString.includes('?') ? '&' : '?';
return `${queryString}${separator}re-signature=${signature}`;
}
// Example usage
const apiKey = 'your-secret-api-key';
const queryString = '?userId=User123&age=25&gender=Male';
createSignedUrl(apiKey, queryString).then(signedUrl => {
console.log('Signed URL:', signedUrl);
});