What is the equivalent of PHP's openssl_sign()
for ColdFusion? This works perfect in PHP but I need to do this in CFML:
<?php
//helper function
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
// Read the JSON credential file my-private-key.json download from Google
$root = realpath($_SERVER["DOCUMENT_ROOT"]);
$private_key_file="$root/file.json";
$json_file = file_get_contents($private_key_file);
$info = json_decode($json_file);
$private_key = $info->{'private_key'};
//{Base64url encoded JSON header}
$jwtHeader = base64url_encode(json_encode(array(
"alg" => "RS256",
"typ" => "JWT"
)));
//{Base64url encoded JSON claim set}
$now = time();
$jwtClaim = base64url_encode(json_encode(array(
"iss" => $info->{'client_email'},
"scope" => "scope",
"aud" => "https://www.googleapis.com/oauth2/v4/token",
"exp" => $now + 3600,
"iat" => $now
)));
$data = $jwtHeader.".".$jwtClaim;
// Signature
$Sig = '';
openssl_sign($data,$Sig,$private_key,'SHA256');
$jwtSign = base64url_encode( $Sig );
//{Base64url encoded JSON header}.{Base64url encoded JSON claim set}.{Base64url encoded signature}
$jwtAssertion = $data.".".$jwtSign;
$ch = curl_init();
$url = "https://www.googleapis.com/oauth2/v4/token";
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
$data = array(
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion" => $jwtAssertion
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch) ;
echo $response;
For ColdFusion I was a able to get some sample code working. By working I mean flow with no errors. At the end I still get invalid jwt signature. ColdFusion code:
<cffunction name="base64url_encode" returntype="any" output="false">
<cfargument name="stringValue" required="true">
<cfset rawData = binaryEncode(binaryDecode(arguments.stringValue)>
<cfset rawData = replace(rawData,"+","-","ALL")>
<cfset rawData = replace(rawData,"/","_","ALL")>
<cfset rawData = replace(rawData,"=","","ALL")>
<cfreturn rawData>
</cffunction>
<cfobject name="main" component="a_assets.cfc.main">
<cfobject name="jwt" component="a_assets.cfc.JWT.sign.RSASigner">
<cfset privateKeyFile = ExpandPath('file.json')>
<cfset jsonFile = FileRead(privateKeyFile, 'utf-8')>
<cfset json = deserializeJSON(jsonFile)>
<cfset privateKey = json['private_key']>
<cfset signer = new a_assets.cfc.JWT.sign.RSASigner(privateKey, "SHA512withRSA")>
<cfset signer.addBouncyCastleProvider()>
<cfset JWT_header = structNew('ordered')>
<cfset JWT_header['alg'] = 'RS256'>
<cfset JWT_header['typ'] = 'JWT'>
<cfset JWT_header = serializeJSON(JWT_header)>
<cfset JWT_claim_set = structNew('ordered')>
<cfset JWT_claim_set['iss'] = json['client_email']>
<cfset JWT_claim_set['scope'] = 'scope'>
<cfset JWT_claim_set['aud'] = 'https://www.googleapis.com/oauth2/v4/token'>
<cfset JWT_claim_set['exp'] = main.fnEpochTime(DateAdd('h', 8, NOW()))>
<cfset JWT_claim_set['iat'] = main.fnEpochTime(DateAdd('h', 7, NOW()))>
<cfset JWT_claim_set = serializeJSON(JWT_claim_set)>
<cfset data = main.base64url_encode(JWT_header) & '.' & main.base64url_encode(JWT_claim_set)>
<cfset hashedData = signer.sign( data )>
<cfset signature = main.base64url_encode(hashedData)>
<cfset JWTData = data & '.' & signature>
<cfhttp url="https://www.googleapis.com/oauth2/v4/token" method="post" result="result">
<cfhttpparam name="grant_type" type="formField" value="urn:ietf:params:oauth:grant-type:jwt-bearer" />
<cfhttpparam name="assertion" type="formField" value="#JWTData#" />
</cfhttp>
<cfoutput>#result.filecontent#</cfoutput>
CFHTTP response error
{ "error": "invalid_grant", "error_description": "Invalid JWT Signature." }
I have compared all Base64 created in PHP to Coldfusion. Everything is identical until I get to encryption(). Which doesn't support SHA256withRSA as far I can tell. I have tried HMAC(). Which also doesn't support SHA256withRSA as far I can tell. Lastly I tried Ben's code which I have linked. I apologize for not uploading more detail the first time. I was fairly certain that PhP's openssl_sign() is what I need to replicate. I am using ColdFusion 2016.