I'm trying to create a JWT to authorize with a service account as described in Google documentation using System.IdentityModel.Tokens.Jwt. I have the following code:
byte[] key = Convert.FromBase64String("...");
var certificate = new X509Certificate2(key, "notasecret");
DateTime now = DateTime.UtcNow;
TimeSpan span = now - UnixEpoch;
Claim[] claims =
{
new Claim("iss", "email@developer.gserviceaccount.com"),
new Claim("scope", "https://www.googleapis.com/auth/plus.me"),
new Claim("aud", "https://accounts.google.com/o/oauth2/token"),
new Claim("iat", span.TotalSeconds.ToString()),
new Claim("exp", span.Add(TimeSpan.FromHours(1)).TotalSeconds.ToString())
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
var descriptor = new SecurityTokenDescriptor
{
SigningCredentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(key),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256"),
Subject = new ClaimsIdentity(claims)
};
JwtSecurityToken jwtSecurityToken = (JwtSecurityToken)handler.CreateToken(descriptor);
string json = handler.WriteToken(jwtSecurityToken);
which outputs:
{ "typ" : "JWT" , "alg" : "HS256" }
While Google explicitly states it supports SHA-256:
Service accounts rely on the RSA SHA-256 algorithm and the JWT token format
According to wtSecurityTokenHandler.InboundAlgorithmMap:
RS256 => http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
HS256 => http://www.w3.org/2001/04/xmldsig-more#hmac-sha256
So when I change my code:
new SigningCredentials(
new InMemorySymmetricSecurityKey(key),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
I'm getting an exception:
System.InvalidOperationException: IDX10632: SymmetricSecurityKey.GetKeyedHashAlgorithm( 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' ) threw an exception.
SymmetricSecurityKey: 'System.IdentityModel.Tokens.InMemorySymmetricSecurityKey'
SignatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', check to make sure the SignatureAlgorithm is supported.
Does it mean Microsoft doesn't support the algorithm Google supports exclusively?
It has been a while since this question was asked but I think that for future people coming on this page, it may be worth knowing that it's dead easy to get the same results in a few lines of code with the .NET Google Auth API (whose nuget is available here: Google.Apis.Auth
To get the Access Token, you just have to call the method RequestAccessTokenAsync and if the result is successful, you've got your token in the AccessToken property.
Note that this implementation assumes that in the developers console, you have exported your private key as a .P12 file.
Hope this answer will help.
I had to modify @abatishchev's code slightly. Otherwise, it had problems generating a certificate when deployed to non-development environments.
The problem was two-fold. If the certificate was not flagged as exportable, it would throw an exception saying something like "keyset does not exist". It would only happen on the servers, not locally, so I suspect server versions of Windows are more restrictive.
Also, it would throw a cryptography exception about a computer trust issue because certificate was created in a user keyset. Our application pools were set not to import the user profile in the advanced options, which you could do. But it was not an option for us due to compatibility issues with other apps. Setting the certificate to be created in the machine keyset mitigates the problem.
The 2 changed sections are marked with comments.