Google Identity Platform: Using OAuth 2.0 in Powershell using Firebase Admin SDK private key

此生再无相见时 提交于 2019-12-12 09:06:06

问题


Attempting to implement Firebase Admin SDK service account access using Powershell HTTP/REST and following this tutorial (there's no handy API in Powershell);

Using OAuth 2.0 for Server to Server Applications

Forming the JWT header and JWT claim set are straightforward enough and I can reproduce the examples in the tutorial, however this is where it gets tricky;

Sign the UTF-8 representation of the input using SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function) with the private key obtained from the Google API Console. The output will be a byte array. The signature must then be Base64url encoded

This my relevant code snippet (credit to @Will_Nevis to give me a clue)

...
$jws = "$encodedheader.$encodedclaim";
$encodedjws = [System.Text.Encoding]::UTF8.GetBytes($jws);

$rsaobj = New-Object System.Security.Cryptography.RSACryptoServiceProvider;
$rsaobj.FromXmlString($rsaobj.ToXmlString($jsonfile.private_key));
$sha256OID = [System.Security.Cryptography.CryptoConfig]::MapNameToOID("SHA256");
$signature = $rsaobj.SignData($encodedjws, $sha256OID);

$encodedsignature = [System.Convert]::ToBase64String($signature);
$encodedsignature = $encodedsignature.Split('=')[0]
$encodedsignature = $encodedsignature.Replace('+', '-')
$encodedsignature = $encodedsignature.Replace('/', '_')

$jwt = "$encodedheader.$encodedclaim.$encodedsignature"
...

It generates an impressive looking signature, shorter than the tutorial example although I have no idea if it's right. It doesn't actually work (400) Bad Request.

Is the above code just wrong or is there another way to do it in Powershell..?

I guess it's essential to generate the signature correctly.

Appreciate the question has been asked before, but no-one seems to have it "nailed"

Below is an sample of the Json private key file generated for my Firebase Admin SDK service account. This is all that is required to generate the token;

{
"type": "service_account",
"project_id": "abc-xyz",
"private_key_id": "nL9zQkSB1GXwF49MNcaEgCp9i7BmFr9JCBKYwgg",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvADANB_massive_key... 03i8lG\nrlsDWw==\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-fuhti@abc-xyz.iam.gserviceaccount.com",
"client_id": "156461654611800605490",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fuhti%40abc-xyz.iam.gserviceaccount.com"
  }

Test scenario

([system.security.cryptography.rsacryptograpgyserviceprovider]::create(2048)).toxmlstring($true)

yields this piece of XML (values truncated for readability) "D" is the privateExponent (private_key?) apparently

<RSAKeyValue>
    <Modulus>2htVi  ... Fay9qQ==</Modulus>
    <Exponent>AQAB</Exponent>
    <P>4dOudM8 ... t0JM=</P>
    <Q>9z+W6qw ... p+KlM=</Q>
    <DP>lOgN6  ... tbApX0=</DP>
    <DQ>h75s0 ... w93Jys=</DQ>
    <InverseQ>dDFa8H ... BlTuWs=</InverseQ>
    <D>n4EN9rDQm ... Nj7lY7G4Q==</D>
</RSAKeyValue>

That pretty much confirms that this line of code does absolutely nothing

$rsaobj.FromXmlString($rsaobj.ToXmlString($jsonfile.private_key));

回答1:


I suspect the problem is with the initialization of the RSACryptoServiceProvider with the proper public and private keys, which you've been provided as a JSON object. The .toXML() method you're calling probably isn't working.

There's some discussion of how to set your own public/private keys in this question that may be a path to take.

You might try generating a new keypair and .toXML($true) on the result to see how the XML is formatted, then massage your JSON-based key data into that format.

edit

After researching, the challenge you face is converting the PKCS#8-encoded key provided by Google into a form consumable by RSACryptoServiceProvider. .NET doesn't currently have APIs for reading this key, but there is interest in rectifying this deficiency.

  • There is an issue on the .net core for providing ASN.1 decoding facilities, which would be a prerequisite.

  • Others have written their own code to handle this specific problem.

  • There are informative blog posts out there detailing the issue.

  • There is copious documentation on DER encoding and the PKCS#8 format that your private key is in.

One workaround that seems to function is to generate P12 keys for your service account. If creating a new service account isn't an option, there are ways to convert the private key file in the .json file to P12

I created a new service account with p12 keys and the powershell code for signing is even simpler than before:

$certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($p12file,'thepasswordforthekeystore')

$dataToSign = "This is some data"
$certificate.privateKey.SignData(
    [system.text.encoding]::utf8.getbytes($dataToSign), 
    [System.Security.Cryptography.HashAlgorithmName]::SHA256,
    [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)


来源:https://stackoverflow.com/questions/51832533/google-identity-platform-using-oauth-2-0-in-powershell-using-firebase-admin-sdk

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!