Skip to content

Instantly share code, notes, and snippets.

@jhoerr
Created June 25, 2015 22:36
Show Gist options
  • Save jhoerr/677fa66595c710b22599 to your computer and use it in GitHub Desktop.
Save jhoerr/677fa66595c710b22599 to your computer and use it in GitHub Desktop.
Get Google service account access token for self or impersonation using private key string
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Nito.AsyncEx;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
private static async Task<string> GetAuthorizationToken(string serviceAccountEmail, string impersonatedEmail = null)
{
string jwt = CreateJwt(serviceAccountEmail, impersonatedEmail);
return await GetTokenFromGoogleAsync(jwt);
}
private static async Task<string> GetTokenFromGoogleAsync(string jwt)
{
var dic = new Dictionary<string, string>
{
{"grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"},
{"assertion", jwt}
};
var content = new FormUrlEncodedContent(dic);
var httpClient = new HttpClient {BaseAddress = new Uri("https://www.googleapis.com")};
var response = await httpClient.PostAsync("/oauth2/v3/token", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private static string CreateJwt(string serviceAccountEmail, string impersonatedEmail = null)
{
var header = EncodeHeader();
var claimset = EncodeClaimset(serviceAccountEmail, impersonatedEmail);
var signature = EncodeSignature(header, claimset);
return String.Join(".", header, claimset, signature);
}
private static string EncodeHeader()
{
var header = new {alg = "RS256", typ = "JWT"};
var headerSerialized = JsonConvert.SerializeObject(header);
var headerEncoded = Base64UrlEncode(Encoding.UTF8.GetBytes(headerSerialized));
return headerEncoded;
}
private static string EncodeClaimset(string serviceAccountEmail, string impersonatedEmail = null)
{
var claimset = impersonatedEmail == null
? AdminClaimset(serviceAccountEmail)
: ImpersonatedClaimset(serviceAccountEmail, impersonatedEmail);
var claimsetSerialized = JsonConvert.SerializeObject(claimset);
var claimsetEncoded = Base64UrlEncode(Encoding.UTF8.GetBytes(claimsetSerialized));
return claimsetEncoded;
}
private static object AdminClaimset(string serviceAccountEmail)
{
var iat = (int)DateTime.UtcNow.Subtract(UnixEpoch).TotalSeconds;
var exp = iat + 3300;
return new
{
iss = serviceAccountEmail,
scope = "https://www.googleapis.com/auth/drive",
aud = "https://www.googleapis.com/oauth2/v3/token",
iat = iat.ToString(CultureInfo.InvariantCulture),
exp = exp.ToString(CultureInfo.InvariantCulture)
};
}
private static object ImpersonatedClaimset(string serviceAccountEmail, string impersonatedEmail)
{
var iat = (int)DateTime.UtcNow.Subtract(UnixEpoch).TotalSeconds;
var exp = iat + 3300;
return new
{
iss = serviceAccountEmail,
sub = impersonatedEmail,
scope = "https://www.googleapis.com/auth/drive",
aud = "https://www.googleapis.com/oauth2/v3/token",
iat = iat.ToString(CultureInfo.InvariantCulture),
exp = exp.ToString(CultureInfo.InvariantCulture)
};
}
private static string EncodeSignature(string headerEncoded, string claimsetEncoded)
{
var body = String.Join(".", headerEncoded, claimsetEncoded);
var signature = RsaEncryptWithPrivate(body, PrivateKey);
var signatureEncoded = Base64UrlEncode(signature);
return signatureEncoded;
}
private static string Base64UrlEncode(byte[] data)
{
return Convert.ToBase64String(data).TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
public static byte[] RsaEncryptWithPrivate(string clearText, string privateKey)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
using (var txtreader = new StringReader(privateKey))
{
var keyPair = (RsaPrivateCrtKeyParameters)new PemReader(txtreader).ReadObject();
ISigner signer = SignerUtilities.GetSigner("SHA-256withRSA");
signer.Init(true, keyPair);
signer.BlockUpdate(bytesToEncrypt, 0, bytesToEncrypt.Length);
byte[] sigBytes = signer.GenerateSignature();
return sigBytes;
}
}
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment