How we can replace AddDeveloperSigningCredential on AWS Serverless Lambda environment? - aws-lambda

We are using Identity Server4 with EntityFrameworkCore and we have deployed our .NET Core application as a lambda function using aws toolkit ("https://aws.amazon.com/blogs/developer/preview-of-the-aws-toolkit-for-visual-studio-2017/"). So how we can replace AddDeveloperSigningCredential on aws serverless lambda environment?
Here is our ConfigurationServerices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
string connectionString = Configuration.GetConnectionString("IdentityServer");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer()
.AddDeveloperSigningCredential()
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
}) // this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
// options.EnableTokenCleanup = true;
// options.TokenCleanupInterval = 30;
});
// Add S3 to the ASP.NET Core dependency injection framework.
services.AddAWSService<Amazon.S3.IAmazonS3>();
}

This is some example code that loads certs from the certificate store. If this is unavailable to you then you just need to serialise and persist the certificate(s) you need some other way but that ultimately yields a valid X509Certificate2 instance that you can pass into X509SecurityKey.
private static void ConfigureSigningCerts(IServiceCollection services)
{
var keys = new List<SecurityKey>();
var name = "MyCertName";
//The one that expires last at the top
var certs = X509.LocalMachine.My.SubjectDistinguishedName.Find("CN=" + name, false)
.Where(o => DateTime.UtcNow >= o.NotBefore)
.OrderByDescending(o => o.NotAfter);
if (!certs.Any()) throw new Exception("No valid certificates could be found.");
//Get first (in desc order of expiry) th
var signingCert = certs.FirstOrDefault();
if (signingCert == null) throw new InvalidOperationException("No valid signing certificate could be found.");
var signingCredential = new SigningCredentials(new X509SecurityKey(signingCert), "RS256");
services.AddSingleton<ISigningCredentialStore>(new DefaultSigningCredentialsStore(signingCredential));
foreach (var cert in certs)
{
var validationCredential = new SigningCredentials(new X509SecurityKey(cert), "RS256");
keys.Add(validationCredential.Key);
}
services.AddSingleton<IValidationKeysStore>(new DefaultValidationKeysStore(keys));
}
The constructor for X509Certificate2 can take a raw byte[] or a file path so you've got plenty of options when it comes to packaging and distributing the signing/validation certs.
To create a self signed certificate on windows you can use the command:
makecert -r -pe -n "CN=MyCertName" -b 01/01/2015 -e 01/01/2039 -eku 1.3.6.1.5.5.7.3.3 -sky signature -a sha256 -len 2048 mycert.cer
That creates a certificate named MyCertName in a file called mycert.cer.
Full docs for the tool here: https://msdn.microsoft.com/en-us/library/bfsktky3(VS.100).aspx

Related

FHIR CLIENT - Connect to NPHIES FHIR - OBB Server

How to connect NPHIES FHIR OBB(Testing) Server inorder to submit eligibility/preauthorization/claims and soon.
Lot of companies built tools to work with FHIR SERVERS.
Firely - .Net Framework / .Net Core - link
HAPI- JAVA - link
Those who were working with Firely - .NET SDK, click this link to view the documentation.
In order to send the request to NPHIES OBB SERVER, you need to pass Username, password and content-type
username: xxxxx
password: yyyyy
content-type: application/fhir+json
Check the below code to send a request to NPHIES - OBB FHIR Server
using (var handler = new HttpClientEventHandler())
{
handler.OnBeforeRequest += (sender, e) =>
{
e.RawRequest.Content.Headers.ContentType.CharSet = "";
e.RawRequest.Content.Headers.Remove("Content-Type"); // "{application/json; charset=utf-8}"
e.RawRequest.Content.Headers.Add("Content-Type", "application/fhir+json");
e.RawRequest.Content.Headers.Add("Username", xxxx);
e.RawRequest.Content.Headers.Add("Password", yyyy);
};
handler.OnAfterResponse += (sender, e) =>
{
};
using (FhirClient fhirClient = new FhirClient(
nphiesServerURL, new FhirClientSettings() { PreferredFormat = ResourceFormat.Json},handler))
{
Bundle responseBundle = fhirClient.Transaction(bundle);
}
}

Decrypt SSM stored parameter with KMS encryption, Error: InvalidCiphertextException

I am trying to decrypt a parameter stored on SSM that is encrypted with a user managed KMS key, which I just created.
This post uses outdated methods
This post says that the context used on creation should be also used on decryption with the option EncryptionContext. But when I created the key and the parameter I did not used a context. I also checked on CloudTrail and there's no information about context. And I also didn't find any place to declare a context when creating a new parameter.
There is no example in the examples repo
This Lambda is being executed with the correct permissions to Decrypt with the key and to Read from the SSM parameter store.
I am sure the parameter is fetched correctly, because I am able to retrieve the stored parameter if I do not encrypt it with the KMS key.
I also tryied using another library base64-js to encrypt the string to Uint8Array, but the result is the same.
This is the sample code:
import { DecryptCommand, KMSClient } from '#aws-sdk/client-kms';
import { GetParameterCommand, SSMClient } from '#aws-sdk/client-ssm';
const kmsClient = new KMSClient({ region: process.env.REGION });
const ssmClient = new SSMClient({ region: process.env.REGION });
try {
const response = await ssmClient.send(new GetParameterCommand({
Name: `/path/to/param`
}));
// Value below verified without KMS key
const sureItIsValid = response.Parameter?.Value as string
// Obtained the same result for buff using base64-js lib
const buff: Uint8Array = Buffer.from(sureItIsValid, 'base64');
const command = new DecryptCommand({
CiphertextBlob: buff,
// The KeyId was also verified using the alias
KeyId: 'arn:aws:kms:<REGION>:...',
});
const secrets = await kmsClient.send(command);
console.error('result');
console.log(secrets.Plaintext?.toString());
} catch (error) {
console.error('error');
console.error(JSON.stringify(error));
}
And I get:
ERROR error
ERROR {"name":"InvalidCiphertextException","$fault":"client","$metadata":{"httpStatusCode":400,"requestId":"the-request-id","attempts":1,"totalRetryDelay":0},"__type":"InvalidCiphertextException","message":"UnknownError"}
Add WithDecryption: true to your GetParameterCommand. SSM will call KMS to decrypt* the SecretString paramter and return the plaintext to us in Parameter.Value:
const command = new GetParameterCommand({
Name: '/path/to/param',
WithDecryption: true,
});
* You are using the CDK to handle your Lambda permissions, so the following will work:
param.grantRead(func); // let your Lambda function read the SSM Parameter
key.grantDecrypt(func); // let your Lambda Function decrypt the SSM Parameter

MsalClientException IDW10104 from GetAccessTokenForAppAsync

I have an ASP.NET Core Web API set up as App Service in Azure with an App Registration in our AzureAd
In appsettings.json I have (anonimized)
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "ourdomain.co.uk",
"TenantId": "n9n999n9-9999-nnnn-9n9n9-9n9n9n9n9n9",
"ClientId": "81933a15-157f-45b0-bc32-3d7d6d62f4a7",
"Audience": "https://ourdomain.co.uk/breathe.notifications-service",
"ClientSecret": "a6a6a6a~EEizqWNa8itAAAjcrycxnCtxaVgKTFx"
},
That app has an API permission in Azure Ad that allows me to call another app service, Audit. The audit service does not have any specific scopes defined but it does have an app role called Audit.Write
In the calling API i need to get a token to call audit so I run this code
var accessToken = await this.tokenAcquisition.GetAccessTokenForAppAsync(this.auditApiScope);
this.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
this.httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Note the call to GetAccessTokenForAppAsync rather than the more common GetAccessTokenForUserAsync
The scope string that I am passing is
https://ourdomain.co.uk/us.audit-service/.default
When I call GetAccessTokenForAppAsync it is failing with MSALException
IDW10104: Both client secret and client certificate cannot be null or
whitespace, and only ONE must be included in the configuration of the
web app when calling a web API. For instance, in the appsettings.json
file.
The client secret is in the AzureAd config, I am not specifying a certificate.
I now have this working and have two options but before I outline those I need to offer some extra background.
This Web Api and others we have created offer functionality to Azure Ad users and Azure B2C users. This functionality was first possible with Microsoft.Identity.Web 1.11.0 and we hjave been using 1.11.0 since it was released. However we always had an issue where we would generate thousands of exceptions because MSAL was getting confused ny which scheme to use.
We came across this blog post, Removing misleading IDX10501 logs when using multiple authentication schemes in ASP.NET Core 3.1 there is more detail in this github thread, https://github.com/oliviervaillancourt/blog/issues/3.
Our Startup.cs Configure Services looks like this
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebApiAuthentication(this.configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
services.AddAuthentication()
.AddMicrosoftIdentityWebApi(this.configuration, "AzureAdB2C", "B2CScheme", true);
services.AddAuthentication("AzureAD_OR_AzureAdB2C")
.AddMicrosoftIdentityWebApi(
jwtBearerOptions =>
{
var azureAdB2CConfig = this.configuration.GetSection("AzureAdB2C");
jwtBearerOptions.ForwardDefaultSelector = context =>
{
var token = string.Empty;
if (context.Request.Headers.TryGetValue("Authorization", out var value))
{
string authorization = value;
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
token = authorization.Substring("Bearer ".Length).Trim();
}
}
if (token == null)
{
this.logger.LogInformation($"Cannot get the Token out of the Authorization header");
}
var jwtHandler = new JwtSecurityTokenHandler();
if (jwtHandler.CanReadToken(token))
{
var jwtToken = jwtHandler.ReadJwtToken(token);
var expectedB2CIssuer = $"{azureAdB2CConfig.GetValue<string>("Instance")}/{azureAdB2CConfig.GetValue<string>("TenantId")}/v2.0/";
if (string.Compare(jwtToken.Issuer, expectedB2CIssuer, true) == 0)
{
// Claim is from B2C so this request should be validated against the B2C scheme.
this.logger.LogInformation($"Request is with a B2C issued token so refer to B2CScheme. Token issuer: {jwtToken.Issuer} B2C Issuer: {expectedB2CIssuer}");
return "B2CScheme";
}
else
{
this.logger.LogInformation($"Request is not with a B2C issued token so refer to Bearer scheme. Token issuer: {jwtToken.Issuer} B2C Issuer: {expectedB2CIssuer}");
}
}
else
{
this.logger.LogInformation("Request token could not be read so refer to Bearer scheme");
}
return "Bearer";
};
},
identityOptions =>
{
var azureAdB2CConfig = this.configuration.GetSection("AzureAdB2C");
identityOptions.Instance = azureAdB2CConfig.GetValue<string>("Instance");
identityOptions.TenantId = "AzureAD_OR_AzureAdB2C";
identityOptions.ClientId = "AzureAD_OR_AzureAdB2C";
},
"AzureAD_OR_AzureAdB2C",
false);
services.AddControllers()
.AddNewtonsoftJson();
services.AddLogging(options =>
{
// hook the Console Log Provider
options.AddConsole();
options.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
// hook the Application Insights Provider
options.AddFilter<ApplicationInsightsLoggerProvider>(string.Empty, Microsoft.Extensions.Logging.LogLevel.Trace);
// pass the InstrumentationKey provided under the appsettings
options.AddApplicationInsights(this.configuration["APPINSIGHTS_INSTRUMENTATIONKEY"]);
});
}
The logic used by the ForwardDefaultSelector is what helps us work with multiple schemes and forward ASP.NET to the right scheme.
Now back to the answer.
If I remove the ForwardDefaultSelector I no longer get the IDW10104 however that is what we use to remopve all the extraneous exceptions schemes so that is not really going to be workable.
The only viable option is to move the Web Api from the latest version of Microsoft.Identity.Web 1.21.1 to 1.16.0. The issue that is causing us to get the exception was introduced in 1.16.1. I will raise an issue on the MSAL github for 1.16.1. We were previously using 1.11.0.

How to download/export .net core localhost generated certificate [duplicate]

I am trying to make HTTPS calls to site that has 2 SSL certificates: a self-signed certificate and a certificate that was signed by the the first certificate. When I use an HttpClient to send a request to the site, the console logs an untrusted chain, shows both certificates, then print a long stack trace of that is caused by java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
I have installed both certificates on my phone and navigating Chrome to the site shows a trusted connection (it had an untrusted connection warning before I installed the certificates). I believe the issue is that the App refuses to trust self-signed certificates. I do not have access to the server and thus have no influence on its certificates, so installing a certificate signed by a trusted CA is not viable.
Solutions I've tried that have not worked.
ServicePointManager.ServerCertificateValidationCallback doesn't seem to run.
I have tried using my own function for ServicePointManager.ServerCertificateValidationCallback, but the delegate I give it never seems to run. I have the following code in my MainActivity.OnCreate method, but the console never logs the message:
System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
Console.WriteLine($"****************************************************************************************************");
return true;
};
HttpClientHandler.ServerCertificateCustomValidationCallback throws an exception.
I have tried using an HttpClientHandler and settings its ServerCertificateCustomValidationCallback, but I just get the message:
System.NotImplementedException: The method or operation is not implemented. at System.Net.Http.HttpClientHandler.set_ServerCertificateCustomValidationCallback (System.Func`5[T1,T2,T3,T4,TResult] value).
Setup code:
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
HttpClient client = new HttpClient(handler);
I was able to get this to work in both Android and iOS.
iOS was easy, just override ServicePointManager.ServerCertificateValidationCallback:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
For Android I used Bruno Caceiro's answer from a similar question and a created Dependency Service.
In my Xamarin Forms project I added a simple interface:
public interface IHTTPClientHandlerCreationService
{
HttpClientHandler GetInsecureHandler();
}
And in my Xamarin Android project I implemented the interface:
[assembly: Dependency(typeof(HTTPClientHandlerCreationService_Android))]
namespace MyApp.Droid
{
public class HTTPClientHandlerCreationService_Android : CollateralUploader.Services.IHTTPClientHandlerCreationService
{
public HttpClientHandler GetInsecureHandler()
{
return new IgnoreSSLClientHandler();
}
}
internal class IgnoreSSLClientHandler : AndroidClientHandler
{
protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
{
return SSLCertificateSocketFactory.GetInsecure(1000, null);
}
protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
{
return new IgnoreSSLHostnameVerifier();
}
}
internal class IgnoreSSLHostnameVerifier : Java.Lang.Object, IHostnameVerifier
{
public bool Verify(string hostname, ISSLSession session)
{
return true;
}
}
}
Shared code to correctly set up the HttpClient:
switch (Device.RuntimePlatform)
{
case Device.Android:
this.httpClient = new HttpClient(DependencyService.Get<Services.IHTTPClientHandlerCreationService>().GetInsecureHandler());
break;
default:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
this.httpClient = new HttpClient(new HttpClientHandler());
break;
}

PuTTY in C# for file transfer

Can any one help me with a code snippet in C# for transferring a file on my local machine to a remote server using PSCP (PuTTY) transfer methodology? I would really appreciate the help.
Thanks
You can use a library that support SCP like SSHNet or WinSCP. Both provide samples and tests that demonstrate how they work.
With SSH.Net you can upload a file using this code (from the test files):
using (var scp = new ScpClient(host, username, password))
{
scp.Connect();
scp.Upload(new FileInfo(filename), Path.GetFileName(filename));
scp.Disconnect();
}
With the WinSCP library the code looks like this (from the samples):
SessionOptions sessionOptions = new SessionOptions {
Protocol = Protocol.Sftp,
HostName = "example.com",
UserName = "user",
Password = "mypassword",
SshHostKey = "ssh-rsa 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Upload files
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
TransferOperationResult transferResult;
transferResult = session.PutFiles(#"d:\toupload\*", "/home/user/", false, transferOptions);
// Throw on any error
transferResult.Check();
}
Using SFTP and SCP supported clients with .NET Libraries might be the best option. But here is a simple way to use PSCP:
Process cmd = new Process();
cmd.StartInfo.FileName = #"C:\PuTTY\pscp.exe";
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
string argument = #"-pw pass C:\testfile.txt user#10.10.10.10:/home/usr";
cmd.StartInfo.Arguments = argument;
cmd.Start();
cmd.StandardInput.WriteLine("exit");
string output = cmd.StandardOutput.ReadToEnd();

Resources