我尝试使用system . identity model . tokens . jwt创建一个JWT来授权一个服务帐户,如Google文档中所述。我有以下代码:
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);
输出:
{ "typ" : "JWT" , "alg" : "HS256" }
虽然谷歌明确表示它支持SHA-256:
服务帐户依赖于RSA SHA-256算法和JWT令牌格式
根据wtSecurityTokenHandler.InboundAlgorithmMap:
RS256 => http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
HS256 => http://www.w3.org/2001/04/xmldsig-more#hmac-sha256
所以当我改变我的代码时:
new SigningCredentials(
new InMemorySymmetricSecurityKey(key),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
我得到了一个例外:
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.
这是否意味着微软不支持谷歌独家支持的算法?
我不得不稍微修改@abatishchev的代码。否则,当部署到非开发环境中时,它在生成证书时会遇到问题。
问题有两个方面。如果证书未标记为可导出,它将引发异常,指出类似“keyset 不存在”的内容。它只会发生在服务器上,而不是在本地,所以我怀疑Windows的服务器版本更具限制性。
此外,由于证书是在用户密钥集中创建的,因此它将引发有关计算机信任问题的加密异常。我们的应用程序池设置为不导入高级选项中的用户配置文件,您可以这样做。但由于与其他应用程序的兼容性问题,这不是我们的选择。设置要在机器密钥集中创建的证书可以缓解此问题。
更改的2个部分标有注释。
private static async Task<string> GetAuthorizationToken(GoogleAuthOptions authOptions)
{
string jwt = CreateJwt(authOptions);
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://accounts.google.com") };
var response = await httpClient.PostAsync("/o/oauth2/token", content);
response.EnsureSuccessStatusCode();
dynamic dyn = await response.Content.ReadAsAsync<dynamic>();
return dyn.access_token;
}
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
private static string CreateJwt(GoogleAuthOptions authOptions)
{
/* changed */
const X509KeyStorageFlags certificateFlags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable;
var certificate = new X509Certificate2(Convert.FromBase64String(authOptions.CertificateKey), authOptions.CertificateSecret, certificateFlags);
/* end of change */
DateTime now = DateTime.UtcNow;
var claimset = new
{
iss = authOptions.Issuer,
scope = "https://www.googleapis.com/auth/plus.me",
aud = authOptions.Audience,
iat = ((int)now.Subtract(UnixEpoch).TotalSeconds).ToString(CultureInfo.InvariantCulture),
exp = ((int)now.AddMinutes(55).Subtract(UnixEpoch).TotalSeconds).ToString(CultureInfo.InvariantCulture)
};
// header
var header = new { typ = "JWT", alg = "RS256" };
// encoded header
var headerSerialized = JsonConvert.SerializeObject(header);
var headerBytes = Encoding.UTF8.GetBytes(headerSerialized);
var headerEncoded = TextEncodings.Base64Url.Encode(headerBytes);
// encoded claimset
var claimsetSerialized = JsonConvert.SerializeObject(claimset);
var claimsetBytes = Encoding.UTF8.GetBytes(claimsetSerialized);
var claimsetEncoded = TextEncodings.Base64Url.Encode(claimsetBytes);
// input
var input = String.Join(".", headerEncoded, claimsetEncoded);
var inputBytes = Encoding.UTF8.GetBytes(input);
// signiture
var rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
var cspParam = new CspParameters
{
KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
/* changed */
KeyNumber = (int) KeyNumber.Exchange,
Flags = CspProviderFlags.UseMachineKeyStore
/* end of change */
};
var cryptoServiceProvider = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
var signatureBytes = cryptoServiceProvider.SignData(inputBytes, "SHA256");
var signatureEncoded = TextEncodings.Base64Url.Encode(signatureBytes);
// jwt
return String.Join(".", headerEncoded, claimsetEncoded, signatureEncoded);
}
private static async Task<string> GetAuthorizationToken(GoogleAuthOptions authOptions)
{
string jwt = CreateJwt(authOptions);
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://accounts.google.com") };
var response = await httpClient.PostAsync("/o/oauth2/token", content);
response.EnsureSuccessStatusCode();
dynamic dyn = await response.Content.ReadAsAsync<dynamic>();
return dyn.access_token;
}
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
private static string CreateJwt(GoogleAuthOptions authOptions)
{
var certificate = new X509Certificate2(Convert.FromBase64String(authOptions.CertificateKey), authOptions.CertificateSecret);
DateTime now = DateTime.UtcNow;
var claimset = new
{
iss = authOptions.Issuer,
scope = "https://www.googleapis.com/auth/plus.me",
aud = authOptions.Audience,
iat = ((int)now.Subtract(UnixEpoch).TotalSeconds).ToString(CultureInfo.InvariantCulture),
exp = ((int)now.AddMinutes(55).Subtract(UnixEpoch).TotalSeconds).ToString(CultureInfo.InvariantCulture)
};
// header
var header = new { typ = "JWT", alg = "RS256" };
// encoded header
var headerSerialized = JsonConvert.SerializeObject(header);
var headerBytes = Encoding.UTF8.GetBytes(headerSerialized);
var headerEncoded = TextEncodings.Base64Url.Encode(headerBytes);
// encoded claimset
var claimsetSerialized = JsonConvert.SerializeObject(claimset);
var claimsetBytes = Encoding.UTF8.GetBytes(claimsetSerialized);
var claimsetEncoded = TextEncodings.Base64Url.Encode(claimsetBytes);
// input
var input = String.Join(".", headerEncoded, claimsetEncoded);
var inputBytes = Encoding.UTF8.GetBytes(input);
// signiture
var rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
var cspParam = new CspParameters
{
KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
};
var cryptoServiceProvider = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
var signatureBytes = cryptoServiceProvider.SignData(inputBytes, "SHA256");
var signatureEncoded = TextEncodings.Base64Url.Encode(signatureBytes);
// jwt
return String.Join(".", headerEncoded, claimsetEncoded, signatureEncoded);
}
这个问题已经问了一段时间了,但我认为,对于未来的用户来说,使用.NET Google Auth API(其nuget可在此处获得:Google.API.Auth)在几行代码中获得相同的结果可能非常容易
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
namespace GoogleTest
{
public class GoogleOAuth2
{
/// <summary>
/// Authorization scope for our requests
/// </summary>
private readonly string _defaultScope;
/// <summary>
/// Service account will be of the form nnnnnnn@developer.gserviceaccount.com
/// </summary>
private readonly string _serviceAccount;
/// <summary>
/// Set this to the full path to your service account private key file.
/// </summary>
private readonly string _certificateFile;
public GoogleOAuth2(string defaultScope, string serviceAccount, string certificateFile)
{
_defaultScope = defaultScope;
_serviceAccount = serviceAccount;
_certificateFile = certificateFile;
}
/// <summary>
/// Access Token returned by Google Token Server
/// </summary>
public string AccessToken { get; set; }
public async Task<bool> RequestAccessTokenAsync()
{
var certificate = new X509Certificate2(_certificateFile, "notasecret", X509KeyStorageFlags.Exportable);
var serviceAccountCredential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(_serviceAccount)
{
Scopes = new[] { _defaultScope }
}.FromCertificate(certificate));
var status = await serviceAccountCredential.RequestAccessTokenAsync(CancellationToken.None);
if (status)
AccessToken = serviceAccountCredential.Token.AccessToken;
return status;
}
}
}
要获取访问令牌,您只需调用方法RequestAccessTokenAsync,如果结果成功,则在AccessTown属性中获得令牌。
注意,此实现假设在开发人员控制台中,您已将私钥导出为.P12文件。
希望这个答案会有所帮助。
问题内容: 建筑: 我们有一个使用2个pubsub主题/订阅对的架构: 定期由cronjob触发主题(例如,每5分钟触发一次)。订阅是我们云功能的触发器。 主题充当我们的一项服务发布的后台作业的队列。云功能在每次执行时读取订阅,以为排队的后台作业提供服务。 这使我们可以控制后台作业的服务频率,而与将它们添加到队列的时间无关。 云功能(由触发)通过pull读取消息。它决定准备好哪些后台作业,并在成功
大佬们 vue3兼容谷歌66版本吗 vue3兼容谷歌66版本吗
我需要使用AES算法加密Java应用程序中的一些值,并在我的应用程序的Javascript模块中解密相同的值。 我在互联网上看到了一些例子,但在兼容性方面似乎有些不同。 多谢了。
我们希望使用SpringOAuth2JWT令牌支持。我们的架构如下:Spring只提供了一个REST接口,前端由AngularJS构建,AngularJS查询Spring REST接口。出于授权目的,我们的前端团队希望使用JWT。因此,我查看了SpringOAuth2JWT支持,但仍然不知道如何与前端讨论JWT令牌。在阅读了一些教程后,我实现了以下内容: 我不确定工作流程如何。我猜:前端访问/oa
单页面应用切换时要手动发送页面统计,首先在 index.html 或者 main.js 里引入谷歌统计代码: (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createE
我找不到任何关于如何使用/实现GTM谷歌内容实验的答案。 我目前已经用GA代码设置了一个GTM容器,在实验的原始页面中,我在html标记的开头有Google实验代码。我面临的问题是,并非所有用户都被计入谷歌分析实验报告。 在GTM中使用谷歌内容实验仍然是不可能的吗?(如何在Google Tag Manager中运行Google实验-似乎有点老了)