相当于PHP openssl_sign()的ColdFusion - php

PHP对于ColdFusion的openssl_sign()等效于什么?这在PHP中非常完美,但是我需要在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;

对于ColdFusion,我能够工作some sample code。工作是指没有错误的流程。最后,我仍然得到无效的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”:“无效的JWT签名。” }

我将PHP中创建的所有Base64与Coldfusion进行了比较。一切都一样,直到我开始使用encryption()。据我所知,它不支持SHA256withRSA。我已经尝试过HMAC()。据我所知,这也不支持SHA256withRSA。最后,我尝试了已链接的Ben的代码。对于第一次没有上传更多详细信息,我深表歉意。我相当确定PhP的openssl_sign()是我需要复制的东西。我正在使用ColdFusion 2016。

参考方案

发布的示例CF代码无法为我编译,因此我使用了Shawn发布的线程中的签名示例。通过一些小的修改,它可以完美运行。我可以检测到的唯一区别是空格,并且php代码转义了URL中的斜线。除此之外,它在PHP和CF中产生了相同的结果。

为了清楚起见,该示例使用硬编码的JSON字符串和sample keys,因为它们很容易比较。

冷融合

<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 

PHP Count数组元素 - php

嗨,有人可以解释为什么这会返回“数组由0个元素组成”。 :$arr = array(1,3,5); $count = count($arr); if ($count = 0) { echo "An array is empty."; } else { echo "An array has $count elements.…

PHP:从函数返回值并直接回显它? - php

这可能是一个愚蠢的问题,但是……的PHPfunction get_info() { $something = "test"; return $something; } html<div class="test"><?php echo get_info(); ?></div> 有没有办…

PHP:将数据从二维数组复制到一维数组的最快方法 - php

我有一个巨大的二维PHP数组,带有500万行。$t = [ [ "id" => 1, "name" => "foo" ], [ "id" => 2, "name" => "bar" ] ]; 现在,我必须将此数组的I…

php-printf和sprintf具有不同的输出 - php

我编写了以下微型php程序来测试printf和sprintf:<?php $str_1 = printf("%x%x%x", 65, 127, 245); $str_2 = sprintf("%x%x%x", 65, 127, 245); echo $str_1 . "\n"; echo $s…

PHP-解析当前URL - php

在以下两种情况下,我都需要解析当前网址:http://mydomain.com/abc/ http://www.mydomain.com/abc/ 我可以获得“ abc”的返回值(或该位置的任何文本)。我怎样才能做到这一点? 参考方案 您可以使用parse_url();$url = 'http://www.mydomain.com/abc/…