问题
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