What I am trying to accomplish is to be able to upload data from Force.com into a Google Fusion Table held under the service account that has already been set up so that a network graph visualization can be generated based on that data and shown in a VF page within Force.com.
public class NetworkGraphTestController {
public static String base64URLEncode(Blob input) {
String output = encodingUtil.base64Encode(input);
output = output.replace('+', '-');
output = output.replace('/', '_');
while (output.endsWith('=')) {
output = output.subString(0, output.length()-1);
}
return output;
}
public static Long findSecondsBetween2DateTimes(DateTime dt1, DateTime dt2)
{
Integer intDays = dt1.Date().daysBetween(dt2.Date());
Long seconds = dt2.getTime() - dt1.getTime();
Long daysToSeconds = intDays * 24 * 60 * 60;
return daysToSeconds + seconds;
}
public static void generateJWT() {
String pkCS8PrivateKey = 'PRIVATE KEY OBTAINED VIA OPENSSL';
DateTime utc0 = DateTime.newInstance(1970, 1, 1, 0, 0, 0);
DateTime issueTime = DateTime.now();
JSONGenerator gen = JSON.createGenerator(false);
gen.writeStartObject();
gen.writeStringField('iss', 'xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@developer.gserviceaccount.com');
gen.writeStringField('scope', 'https:\\/\\/www.googleapis.com\\/auth\\/fusiontables');
gen.writeStringField('aud', 'https:\\/\\/accounts.google.com\\/o\\/oauth2\\/token');
Long now = findSecondsBetween2DateTimes(utc0, issueTime);
gen.writeNumberField('exp', now + 3500);
gen.writeNumberField('iat', now);
String claimSet = gen.getAsString().trim();
System.debug(gen.getAsString());
System.debug('Gen trimmed: ' + gen.getAsString().trim());
String header = '{"alg":"RS256", "typ":"JWT"}';
String signatureInput = base64URLEncode(blob.valueOf(header)) + '.' + base64URLEncode(blob.valueOf(claimSet));
Blob signature = Crypto.sign('RSA', blob.valueOf(signatureInput), encodingUtil.base64decode(pkCS8PrivateKey));
String jwt = signatureInput+'.'+base64URLEncode(signature);
//System.debug(jwt);
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setMethod('POST');
req.setBody('grant_type=' + encodingUtil.urlEncode('urn:ietf:params:oauth:grant-type:jwt-bearer', 'UTF-8')+'&assertion=' + encodingUtil.urlEncode(jwt, 'UTF-8'));
req.setEndpoint('https://accounts.google.com/o/oauth2/token');
System.debug('Request: ' + req + ' BODY: ' + req.getBody());
//System.debug(req.toString());
HttpResponse res = h.send(req);
System.debug(res + ' BODY: ' + res.getBody() + ' STATUS: ' + res.getStatus());
}
}
The service account has already been set up and the Fusion Tables API has been enabled in the Google API Console. I extracted the PKCS8 key from the PKCS12 p12 private key file that Google provided via OpenSSL, though I am not familiar with this process, so it is possible a mistake was made. What I did notice was that through my many tries to extract the value in that private key, the Crypto class didn't accept the key-value until I got the value that is currently stored in pkCS8PrivateKey. If I remember the process correctly, it first converted the PKCS12 file to an intermediate pem file and then it extracted the private key in the PKCS8 format which was outputted in another pem file.
I have tried not escaping the forward slashes in the iss and scope parameters as well but that makes no difference. I have also allowed access to 'https://accounts.google.com' via the Remote Site Security settings.
The creator of that question ended up in the same boat I am now, in that he is only able to retrieve an invalid_grant error upon trying to authenticate with Google. Unfortunately, his question was left unanswered and so, here I am.
I appreciate any help that can be given in regards to the problem I am facing.