Что эквивалентно PHP openssl_sign()
для ColdFusion? Я видел код Бена Наделя. Начинается с файла PEM, и я не могу заставить его работать. Я начинаю с JSON из Google. Это прекрасно работает в PHP, но мне нужно сделать это в CFML.
—редактировать
Позднее редактирование. Занимался рождественскими делами. Рабочий код PHP:
<?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;
Для ColdFusion я смог получить код Бена Наделя, https://www.bennadel.com/blog/2941-experimenting-with-rsa-encrypted-signature-generation-and-verification-in-coldfusion.htm, за работой. Под работой я имею в виду поток без ошибок. В конце я все еще получаю недопустимую подпись jwt. Код ColdFusion:
<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
{«error»: «invalid_grant», «error_description»: «Invalid JWT Signature.» }
Я сравнил все Base64, созданные в PHP, с Coldfusion. Все идентично, пока я не дохожу до шифрования (). Который не поддерживает SHA256 с RSA, насколько я могу судить. Я пробовал HMAC (). Который также не поддерживает SHA256 с RSA, насколько я могу судить. Наконец я попробовал код Бена, который я связал. Я прошу прощения за то, что не загружал больше деталей в первый раз. Я был вполне уверен, что php openssl_sign () — это то, что мне нужно для репликации. Я использую ColdFusion 2016.
Приведенный пример кода CF не скомпилирован для меня, поэтому я использовал пример подписи из темы, опубликованной Шоном. С некоторыми небольшими модификациями он работал отлично. Единственные различия, которые я мог обнаружить, — это пробелы и то, что код php экранирует косую черту в URL. Кроме этого, он дал одинаковые результаты как в PHP, так и в CF.
Для ясности в примере используются жестко закодированные строки JSON и образцы ключей так как те достаточно легко сравнить.
Холодный синтез
<cfscript>
privateKey = "-----BEGIN PRIVATE KEY-----#chr(10)#"& "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALX0PQoe1igW12i"& "kv1bN/r9lN749y2ijmbc/mFHPyS3hNTyOCjDvBbXYbDhQJzWVUikh4mvGBA07qTj79Xc3yBDfKP2IeyYQIFe0t0"& "zkd7R9Zdn98Y2rIQC47aAbDfubtkU1U72t4zL11kHvoa0/RuFZjncvlr42X7be7lYh4p3NAgMBAAECgYASk5wDw"& "4Az2ZkmeuN6Fk/y9H+Lcb2pskJIXjrL533vrDWGOC48LrsThMQPv8cxBky8HFSEklPpkfTF95tpD43iVwJRB/Gr"& "CtGTw65IfJ4/tI09h6zGc4yqvIo1cHX/LQ+SxKLGyir/dQM925rGt/VojxY5ryJR7GLbCzxPnJm/oQJBANwOCO6"& "D2hy1LQYJhXh7O+RLtA/tSnT1xyMQsGT+uUCMiKS2bSKx2wxo9k7h3OegNJIu1q6nZ6AbxDK8H3+d0dUCQQDTrP"& "SXagBxzp8PecbaCHjzNRSQE2in81qYnrAFNB4o3DpHyMMY6s5ALLeHKscEWnqP8Ur6X4PvzZecCWU9BKAZAkAut"& "LPknAuxSCsUOvUfS1i87ex77Ot+w6POp34pEX+UWb+u5iFn2cQacDTHLV1LtE80L8jVLSbrbrlH43H0DjU5AkEA"& "gidhycxS86dxpEljnOMCw8CKoUBd5I880IUahEiUltk7OLJYS/Ts1wbn3kPOVX3wyJs8WBDtBkFrDHW2ezth2QJ"& "ADj3e1YhMVdjJW5jqwlD/VNddGjgzyunmiZg0uOXsHXbytYmsA545S8KRQFaJKFXYYFo2kOjqOiC1T2cAzMDjCQ"& "==#chr(10)#-----END PRIVATE KEY-----#chr(10)#";
// remove key header/trailer
privateKey = privateKey.replaceAll("^-+BEGIN PRIVATE KEY-+", "");
privateKey = privateKey.replaceAll("-+END PRIVATE KEY-+", "");
privateKey = privateKey.replaceAll(chr(10), "").trim();
// sample JSON
jwtHeader = '{"alg":"RS256","typ":"JWT"}';
jwtClaim = '{"iss":"[email protected]","scope":"scope","aud":"https:\/\/www.googleapis.com\/oauth2\/v4\/token","exp":1545747624,"iat":1545744024}';
data = base64url_encode(jwtHeader) &"."& base64url_encode(jwtClaim);
// sign with private key and SHA256withRSA
keyFactory = createObject("java", "java.security.KeyFactory").getInstance("RSA");
privateSignature = createObject("java", "java.security.Signature").getInstance("SHA256withRSA");
keyBytes = binaryDecode(privateKey, "base64");
keySpec = createObject("java", "java.security.spec.PKCS8EncodedKeySpec").init(keyBytes);
privateSignature.initSign(keyFactory.generatePrivate(keySpec));
privateSignature.update(data.getBytes("utf-8"));
signedBytes = privateSignature.sign();
signature = base64url_encode(signedBytes);
jwtAssertion = data &"."& signature;
// Verify input strings match
writeOutput("<hr>jwtHeader=<br>"& jwtHeader);
writeOutput("<hr>jwtClaim =<br>"& jwtClaim);
writeOutput("<hr>data=<br>"& data);
writeOutput("<hr>jwtSign=<br>"& signature);
writeOutput("<hr>jwtAssertion=<br>"& jwtAssertion);
</cfscript>
PHP
<?php
$privateKey = "-----BEGIN PRIVATE KEY-----\n"."MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALX0PQoe1igW12i". "kv1bN/r9lN749y2ijmbc/mFHPyS3hNTyOCjDvBbXYbDhQJzWVUikh4mvGBA07qTj79Xc3yBDfKP2IeyYQIFe0t0". "zkd7R9Zdn98Y2rIQC47aAbDfubtkU1U72t4zL11kHvoa0/RuFZjncvlr42X7be7lYh4p3NAgMBAAECgYASk5wDw". "4Az2ZkmeuN6Fk/y9H+Lcb2pskJIXjrL533vrDWGOC48LrsThMQPv8cxBky8HFSEklPpkfTF95tpD43iVwJRB/Gr". "CtGTw65IfJ4/tI09h6zGc4yqvIo1cHX/LQ+SxKLGyir/dQM925rGt/VojxY5ryJR7GLbCzxPnJm/oQJBANwOCO6". "D2hy1LQYJhXh7O+RLtA/tSnT1xyMQsGT+uUCMiKS2bSKx2wxo9k7h3OegNJIu1q6nZ6AbxDK8H3+d0dUCQQDTrP". "SXagBxzp8PecbaCHjzNRSQE2in81qYnrAFNB4o3DpHyMMY6s5ALLeHKscEWnqP8Ur6X4PvzZecCWU9BKAZAkAut". "LPknAuxSCsUOvUfS1i87ex77Ot+w6POp34pEX+UWb+u5iFn2cQacDTHLV1LtE80L8jVLSbrbrlH43H0DjU5AkEA". "gidhycxS86dxpEljnOMCw8CKoUBd5I880IUahEiUltk7OLJYS/Ts1wbn3kPOVX3wyJs8WBDtBkFrDHW2ezth2QJ". "ADj3e1YhMVdjJW5jqwlD/VNddGjgzyunmiZg0uOXsHXbytYmsA545S8KRQFaJKFXYYFo2kOjqOiC1T2cAzMDjCQ". "==\n-----END PRIVATE KEY-----\n";
//helper function
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
$private_key = $privateKey;
$jwtHeader = '{"alg":"RS256","typ":"JWT"}';
$jwtClaim = '{"iss":"[email protected]","scope":"scope","aud":"https:\/\/www.googleapis.com\/oauth2\/v4\/token","exp":1545747624,"iat":1545744024}';
$data = base64url_encode( $jwtHeader) . ".". base64url_encode( $jwtClaim);
// Signature
$Sig = '';
openssl_sign($data,$Sig,$private_key,'SHA256');
$jwtSign = base64url_encode( $Sig );
$jwtAssertion = $data.".".$jwtSign;
echo "\njwtHeader=\n ". $jwtHeader;
echo "\njwtClaim=\n ". $jwtClaim;
echo "\ndata=\n". $data;
echo "\njwtSign =\n ". $jwtSign;
echo "\njwtAssertion =\n ". $jwtAssertion;
Результаты:
jwtHeader=
{"alg":"RS256","typ":"JWT"}
jwtClaim =
{"iss":"[email protected]","scope":"scope","aud":"https:\/\/www.googleapis.com\/oauth2\/v4\/token","exp":1545747624,"iat":1545744024}
data=
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzb21lZW1haWxAZXhhbXBsZS5jb20iLCJzY29wZSI6InNjb3BlIiwiYXVkIjoiaHR0cHM6XC9cL3d3dy5nb29nbGVhcGlzLmNvbVwvb2F1dGgyXC92NFwvdG9rZW4iLCJleHAiOjE1NDU3NDc2MjQsImlhdCI6MTU0NTc0NDAyNH0
jwtSign=
Ls59xceJGsv-z0A6cZKgJVIQIqFF3pWBSIR1HECLlfXcPWbFgKCfmpf0NPkJAnypOrAkdGWkwer5tp1xoogljhcd0CctoD4ckeM6FP7trJzEG1HGudwbghLlNHGmS4iYH-wFp5rLcO605ERbxpP4LZ0Y000sAVI-LWrzC0hdEMw
jwtAssertion=
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzb21lZW1haWxAZXhhbXBsZS5jb20iLCJzY29wZSI6InNjb3BlIiwiYXVkIjoiaHR0cHM6XC9cL3d3dy5nb29nbGVhcGlzLmNvbVwvb2F1dGgyXC92NFwvdG9rZW4iLCJleHAiOjE1NDU3NDc2MjQsImlhdCI6MTU0NTc0NDAyNH0.Ls59xceJGsv-z0A6cZKgJVIQIqFF3pWBSIR1HECLlfXcPWbFgKCfmpf0NPkJAnypOrAkdGWkwer5tp1xoogljhcd0CctoD4ckeM6FP7trJzEG1HGudwbghLlNHGmS4iYH-wFp5rLcO605ERbxpP4LZ0Y000sAVI-LWrzC0hdEMw
Других решений пока нет …