function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
TanejaTaneja 

Unable to Generate HMac using SF Crypto Class for Google Maps API

My Company recently signed up for Google Maps API for business.

 

To use the API, I need to generte a HMacSHA1 signature, and add it to my HTTP request. Unfortunately, somehow, I am not able to generate the right signature.

 

For testing, I am using the values provided by google to ensure that the algorithm works fine and I get the right result. Here is the code:

 

string url = 'maps/api/geocode/json?address=New+York&sensor=false&client=clientID';
string privateKey = 'vNIXE0xscrmjlyV-12Nj_BvUPaw=';

privateKey = privateKey.replace('-', '+');
privateKey = privateKey.replace('_', '/');

 

//Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey);
Blob privateKeyBlob = Blob.valueOf(privateKey);
Blob urlBlob = Blob.valueOf(url);

Blob signatureBlob = Crypto.generateMac('HMacSHA1', urlBlob, privateKeyBlob);

 

String signature =EncodingUtil.urlEncode(EncodingUtil.base64Encode(signatureBlob), 'UTF-8');

signature = signature.replace('+', '-');
signature = signature.replace('/', '_');

 

system.debug('signature is ' +signature);

 

The generated signature should be : KrU1TzVQM7Ur0i8i7K3huiw3MsA=

 

Here is the link to Google Documentation where you can also find the same example: https://developers.google.com/maps/documentation/business/webservices

 

Few points to note:

1. I used the sample Python script provided in API Documentation and it gives the right result.

2. I think the problem is, API Documentation says that we should decode the privateKey and then provide it to the function. Although the documentation for Crypto Class says the "The value of privateKey does not need to be in decoded form.". I tried both, with and without decoding, still no result.

3. For Google API, everything has to be UTF-8 Encoded; I don't know if thats the way Encoding.Util decode's it.

 

I have tried a lot fo combinations, but could not find a solution. Any help would be highly appreciated.

 

Thanks,

Ankit

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
TanejaTaneja

Hi Anil, 

 

Here is the code ( This if for google Maps v3 ) :

 

string url = '/maps/api/geocode/json?address=New+York&sensor=false&client=PUT THE CLIENT NAME HERE';

string privateKey = 'PUT THE PRIVATE KEY HERE';
privateKey = privateKey.replace('-', '+');
privateKey = privateKey.replace('_', '/');

Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey);
//Blob privateKeyBlob = Blob.valueOf(privateKey);
Blob urlBlob = Blob.valueOf(url);
Blob signatureBlob = Crypto.generateMac('HMacSHA1', urlBlob, privateKeyBlob);

String signature =EncodingUtil.base64Encode(signatureBlob);
signature = signature.replace('+', '-');
signature = signature.replace('/', '_');

system.debug('signature is ' +signature);

url = 'https://maps.googleapis.com' + url;
url += '&signature=' + signature;

Http h = new Http();
HttpRequest req = new HttpRequest();
req.setHeader('Content-type', 'application/x-www-form-urlencoded');
req.setHeader('Content-length', '0');
req.setEndpoint(url);
req.setMethod('POST');
String responseBody;
HttpResponse res;
res = h.send(req);
responseBody = res.getBody();  
system.debug('response ' + responseBody); 

 Let me know if this helps and please Mark it as a solution if it solved your problem.

 

Best,

Ankit

All Answers

Anil Reddy.ax1727Anil Reddy.ax1727

Hi,

We have the same requirement in our project and i am unable to get the url signed.Can you please post the code to get url signed in apex for geocoding withe google maps API.

 

Thanks in Advance

TanejaTaneja

Hi Anil, 

 

Here is the code ( This if for google Maps v3 ) :

 

string url = '/maps/api/geocode/json?address=New+York&sensor=false&client=PUT THE CLIENT NAME HERE';

string privateKey = 'PUT THE PRIVATE KEY HERE';
privateKey = privateKey.replace('-', '+');
privateKey = privateKey.replace('_', '/');

Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey);
//Blob privateKeyBlob = Blob.valueOf(privateKey);
Blob urlBlob = Blob.valueOf(url);
Blob signatureBlob = Crypto.generateMac('HMacSHA1', urlBlob, privateKeyBlob);

String signature =EncodingUtil.base64Encode(signatureBlob);
signature = signature.replace('+', '-');
signature = signature.replace('/', '_');

system.debug('signature is ' +signature);

url = 'https://maps.googleapis.com' + url;
url += '&signature=' + signature;

Http h = new Http();
HttpRequest req = new HttpRequest();
req.setHeader('Content-type', 'application/x-www-form-urlencoded');
req.setHeader('Content-length', '0');
req.setEndpoint(url);
req.setMethod('POST');
String responseBody;
HttpResponse res;
res = h.send(req);
responseBody = res.getBody();  
system.debug('response ' + responseBody); 

 Let me know if this helps and please Mark it as a solution if it solved your problem.

 

Best,

Ankit

This was selected as the best answer
Anil Reddy.ax1727Anil Reddy.ax1727

Thanks Ankit ,This solved my issue.

TanejaTaneja
Please Mark that as Solution. :)
Anil Pinto 1Anil Pinto 1
Hi Taneja,

While i was searching for the equivalent methods in Salesforce for the code in Java. I found this forum post and thought would be good to ask this question here.



public String getMacValue(Map<String, String> data) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
String apiSecret = data.get(APISECRET);
log.debug("API_SECRET:{}", apiSecret);
SecretKeySpec secret_key = new SecretKeySpec(apiSecret.getBytes(), "HmacSHA256");
mac.init(secret_key);
StringBuilder buff = new StringBuilder();
buff.append(data.get(APIKEY)).append(data.get(NONCE)).append(data.get(TIMESTAMP));
if (data.get(TOKEN) != null)
buff.append(data.get(TOKEN));
if (data.get(PAYLOAD) != null)
buff.append(data.get(PAYLOAD));
log.info(buff.toString());
byte[] macHash = mac.doFinal(buff.toString().getBytes("UTF-8"));
log.info("MacHAsh:{}", Arrays.toString(macHash));
String authorizeString = Base64.encodeBase64String(toHex(macHash));
log.info("Authorize: {}", authorizeString);
return authorizeString;
}


public byte[] toHex(byte[] arr) {
String hex = Hex.encodeHexString(arr);
log.info("Apache common value:{}", hex);
return hex.getBytes();
}

Can you suggest me the proper methods to be used here. Appreciate your help in advance.

Anil Pinto