# 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

  1. Transform the query string to lower case
  2. Sort the parameters in canonical order
  3. Use SHA256 to create the hash for the canonical ordered query string. The secret for the hashing is your API key.
  4. Convert the hash result to hex string and transform it to lower case

# Implementation Example


# 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


# Validation


# 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);
});

# Next Steps