Created
August 27, 2020 22:45
-
-
Save YannickRe/46a4d7b7f8c4f2ce21de7c206d307ab5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Dalion.HttpMessageSigning.Signing; | |
using Dalion.HttpMessageSigning; | |
using Microsoft.IdentityModel.Tokens; | |
using Newtonsoft.Json; | |
using System; | |
using System.Net.Http.Headers; | |
using System.Net.Http; | |
using System.Security.Cryptography.X509Certificates; | |
using System.Security.Cryptography; | |
using System.Threading.Tasks; | |
using System.Collections.Generic; | |
using System.Net; | |
using System.IdentityModel.Tokens.Jwt; | |
using System.Security.Claims; | |
namespace Services | |
{ | |
internal class MagdaAccessToken | |
{ | |
internal MagdaAccessToken() | |
{ | |
this.TokenDate = DateTime.Now; | |
} | |
[JsonProperty("access_token")] | |
public string AccessToken { get; set; } | |
public DateTime ExpireDate | |
{ | |
get | |
{ | |
return this.TokenDate.AddSeconds(this.ExpiresIn); | |
} | |
} | |
public DateTime TokenDate { get; private set; } | |
[JsonProperty("expires_in")] | |
public int ExpiresIn { get; set; } | |
[JsonProperty("token_type")] | |
public string TokenType { get; set; } | |
[JsonProperty("scope")] | |
public string Scope { get; set; } | |
} | |
public class MagdaTokenBasedServicesExample | |
{ | |
private readonly string _keyId = "rsa-key-1"; | |
private readonly string _clientId = "1234"; //TODO: Provide your client id | |
private readonly string _authUrl = "https://beta.oauth.vlaanderen.be/authorization/ws/oauth/v2/token"; | |
private readonly string _messagesEndpointUrl = "https://iv.api.tni-vlaanderen.be/api/v1/messages/messages"; | |
private MagdaAccessToken _accessToken; | |
private X509Certificate2 _clientCertificate; | |
private IRequestSignerFactory _requestSignerFactory; | |
private HttpClient _httpClient; | |
private JwtSecurityTokenHandler _tokenHandler; | |
public MagdaTokenBasedServicesExample(IRequestSignerFactory requestSignerFactory) | |
{ | |
this._requestSignerFactory = requestSignerFactory; | |
this._clientCertificate = null; // TODO: Load your VO-DCB client certificate (pfx) | |
var httpClientHandler = new HttpClientHandler(); | |
httpClientHandler.ClientCertificates.Add(this._clientCertificate); | |
this._httpClient = new HttpClient(httpClientHandler); | |
this._tokenHandler = new JwtSecurityTokenHandler(); | |
} | |
public async Task<HttpResponseMessage> SendMessage() | |
{ | |
if (this._accessToken == null || (this._accessToken != null && this._accessToken.ExpireDate <= DateTime.Now.AddMinutes(1))) | |
this._accessToken = await this.GetAccessToken(); | |
var request = new HttpRequestMessage | |
{ | |
Method = HttpMethod.Post, | |
RequestUri = new Uri(this._messagesEndpointUrl) | |
}; | |
await SignHttpRequestMessage(request); | |
// Add additional payload properties here | |
// Like MessageToSend | |
return await this._httpClient.SendAsync(request); | |
} | |
private async Task SignHttpRequestMessage(HttpRequestMessage request) | |
{ | |
request.Headers.Add("signature-public-key", WrapCertificateInJWK(this._clientCertificate, _keyId)); | |
request.Headers.Date = DateTimeOffset.UtcNow; | |
SigningSettings settings = new SigningSettings | |
{ | |
SignatureAlgorithm = SignatureAlgorithm.CreateForSigning(this._clientCertificate, HashAlgorithmName.SHA512), | |
DigestHashAlgorithm = HashAlgorithmName.SHA512, | |
EnableNonce = false, | |
Headers = new[] | |
{ | |
new HeaderName("signature-public-key") | |
}, | |
UseDeprecatedAlgorithmParameter = true | |
}; | |
var requestSigner = this._requestSignerFactory.Create(_keyId, settings); | |
await requestSigner.Sign(request); | |
request.Headers.Add("Signature", request.Headers.Authorization.Parameter); | |
request.Headers.Add("x-correlation-id", Guid.NewGuid().ToString()); | |
request.Headers.Add("Accept", "application/json"); | |
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this._accessToken.AccessToken); | |
} | |
private string WrapCertificateInJWK(X509Certificate2 certificate, string keyName) | |
{ | |
var x509SigningKey = new X509SigningCredentials(certificate); | |
var x509Key = new X509SecurityKey(certificate); | |
var cert64 = Convert.ToBase64String(x509Key.Certificate.RawData); | |
var pubKey = x509Key.PublicKey as RSA; | |
var parameters = pubKey.ExportParameters(false); | |
var exponent = Convert.ToBase64String(parameters.Exponent); | |
var modulus = Convert.ToBase64String(parameters.Modulus); | |
var webKey = new JsonWebKey | |
{ | |
Kty = "RSA", | |
Use = "sig", | |
Kid = keyName, | |
X5t = x509Key.Certificate.Thumbprint, | |
E = exponent, | |
N = modulus, | |
Alg = x509SigningKey.Algorithm | |
}; | |
webKey.X5c.Add(cert64); | |
var simplifiedWebKey = new { kty = webKey.Kty, kid = webKey.Kid, n = webKey.N, e = webKey.E, x5c = webKey.X5c }; | |
return JsonConvert.SerializeObject(simplifiedWebKey); | |
} | |
private async Task<MagdaAccessToken> GetAccessToken() | |
{ | |
var request = new HttpRequestMessage | |
{ | |
Method = HttpMethod.Post, | |
RequestUri = new Uri(this._authUrl) | |
}; | |
request.Headers.Add("Accept", "application/json"); | |
request.Headers.Add("Accept-Encoding", "gzip, deflate, br"); | |
List<string> scopes = new List<string>() { | |
"msg_mailbox_v1_P", | |
"msg_msg_v1_P", | |
"msg_statuses_v1_P" | |
}; | |
Dictionary<string, string> postParams = new Dictionary<string, string>(); | |
postParams.Add("grant_type", "client_credentials"); | |
postParams.Add("scope", string.Join(" ", scopes)); | |
postParams.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); | |
postParams.Add("client_assertion", this.GetAuthJwtToken(string.Join(" ", scopes))); | |
request.Content = new FormUrlEncodedContent(postParams); | |
var response = await this._httpClient.SendAsync(request); | |
var responseContent = await response.Content.ReadAsStringAsync(); | |
switch (response.StatusCode) | |
{ | |
case HttpStatusCode.OK: | |
return JsonConvert.DeserializeObject<MagdaAccessToken>(responseContent); | |
default: | |
throw new Exception($"HTTP call GetToken failed with status code {response.StatusCode} and message {responseContent}"); | |
} | |
} | |
private string GetAuthJwtToken(string scopes) | |
{ | |
X509SecurityKey privateKey = new X509SecurityKey(this._clientCertificate); | |
ClaimsIdentity claimsIdentity = new ClaimsIdentity(); | |
claimsIdentity.AddClaim(new Claim("sub", this._clientId)); | |
claimsIdentity.AddClaim(new Claim("scope", scopes)); | |
claimsIdentity.AddClaim(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())); | |
return this._tokenHandler.CreateEncodedJwt(this._clientId, this._authUrl, claimsIdentity, null, DateTime.UtcNow.AddMinutes(10), DateTime.UtcNow, new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256Signature)); | |
} | |
} | |
} |
Can you please share how you instantiate the MagdaTokenBasedServicesExample class. More specifically how do you build the IRequestSignerFactory dependency on the contructor ?
Can you please share how you instantiate the MagdaTokenBasedServicesExample class. More specifically how do you build the IRequestSignerFactory dependency on the contructor ?
Currently the only way of instantiating an IRequestSignerFactory object is by using dependency injection. I opened an issue on the GitHub page of Dalion.HttpMessageSigning to improve this.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Dank voor dit stukje code! Ik zat even vast bij de message signing.