Using Asymmetric Key on .Net Core - cryptography

I am trying to run code from this sample
https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-store-asymmetric-keys-in-a-key-container
Under .NetCore 2.0 (Web application).
However when I try to execute any line using
CspParameters
I get the following error
'CspParameters' requires Windows Cryptographic API (CAPI), which is not available on this platform.
Suggestions please on how I work around this.
Thanks.

.NET does not store cryptographic keys, that's ultimately a feature that is (or isn't) provided by the cryptographic platform it builds on top of.
To use CspParameters with .NET Core you have to run on Windows; because that's a very thin wrapper over the (old) Windows Cryptographic API. You can't use it in UAP, because UAP only allows the newer Cryptography: Next Generation (CNG) API.
macOS can store keys in a Keychain, but .NET Core doesn't provide API to read them out.
Linux (OpenSSL) does not have any key storage mechanism other than "save this to a file and load it again", but .NET Core does not support loading asymmetric keys from files.
The only way to accomplish your goal in a cross-platform mechanism is to have your asymmetric key associated with an X.509 certificate. If you build the X509Certificate2 object for which HasPrivateKey returns true you can save it to a PFX/PKCS#12 file and then load from that file; or you can add it to an X509Store instance (the "My" store for CurrentUser is the one that works best across the platforms) and then read it back from the X509Store instance.
Despite the page you referenced claiming to be written in 2017, what it really means is the content was moved from its previous location on msdn.microsoft.com on that date. The original page was written in 2008 (at least, that's the first hit on web.archive.org), so it long predated .NET Core.

You can now do it cross-platform and it works as long as you are on .netcore 3.0 or higher and you add the latest System.Security.Cryptography.Cng nuget package (NB! this will ONLY work if your project is NOT multi-targeted - it can ONLY target netcoreapp3.0) :
using (ECDsa key = ECDsa.Create())
{
key.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKey), out _);
return Jose.JWT.Encode
(
payload: payload,
key: key,
algorithm: JwsAlgorithm.ES256,
extraHeaders: extraHeader
);
}

So just wanted to offer another option we found once we encountered this error. That CSP Parameters error is related to the RSACryptoServiceProvider . This has some issues with cross platform .NET Core. We found a Github issue that mentioned to use RSA.Create() method instead. I was using a Bouncy Castle library that still uses the RSACryptoServiceProvider. At the time of writing this answer, it looked like this.
public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey)
{
RSAParameters rp = ToRSAParameters(privKey);
RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider();
rsaCsp.ImportParameters(rp);
return rsaCsp;
}
So we just replaced it with a private method in the class that looked like this.
private RSA ToRSA(RsaPrivateCrtKeyParameters parameters)
{
RSAParameters rp = DotNetUtilities.ToRSAParameters(parameters);
return RSA.Create(rp);
}
This ran in linux, no errors. Bouncy probably just needs to update their libs.

Use this method to import public key from the Key string make sure to install BouncyCastle.NetCore nuget package
public static RSACryptoServiceProvider ImportPublicKey(string pem)
{
PemReader pr = new PemReader(new StringReader(pem));
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();// cspParams);
csp.ImportParameters(rsaParams);
return csp;
}
And then you can encrypt your data as shown below
public static string Encryption(string data,string publickey)
{
var testData = Encoding.GetEncoding("iso-8859-1").GetBytes(strText);
using (var rsa = ImportPublicKey(publickey))
{
try
{
var encryptedData = rsa.Encrypt(testData, false);
var base64Encrypted = Convert.ToBase64String(encryptedData);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}

Related

Facing issue while create the events using MS graph API

I have tried this code to generate the token:
public async Task Authenticate() {
MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new StringContent(_clientId), "client_id");
content.Add(new StringContent(_clientSecret), "client_secret");
content.Add(new StringContent("client_credentials"), "grant_type");
content.Add(new StringContent(".default"), "scope");
try {
var task = _client.PostAsync(new Uri(string.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", _tenantId)), content);
var res = task.GetAwaiter().GetResult();
if (res.StatusCode == System.Net.HttpStatusCode.OK) {
JsonDocument resJSON = await JsonDocument.ParseAsync(await res.Content.ReadAsStreamAsync());
_accessToken = resJSON.RootElement.GetProperty("access_token").GetString();
lock(this) {
_expiresAt = DateTime.UtcNow.AddSeconds(resJSON.RootElement.GetProperty("expires_in").GetInt16());
}
} else
throw new Exception(res.ReasonPhrase);
} catch (WebException ex) {
// handle web exception
}
}
But I got the error like
error_description=AADSTS1002016: You are using TLS version 1.0, 1.1 and/or 3DES cipher which are deprecated to improve the security posture of Azure AD. Your TenantID is: 334xxxx. Please refer to https://go.microsoft.com/fwlink/?linkid=2161187 and conduct needed actions to remediate the issue. For further questions, please contact your administrator.
Trace ID: c8a502xxxx
Correlation ID: 325a1dxxxxx
Timestamp: 2022-08-04 13:35:23Z
But the same code works in console application.While using this code inside the dll it throws the exception.All the versions are same - .net framework,System.text.json,system.memory etc.
Please help me to sort out this.
According to this page the default TLS version that is used, depends on the targeted .net version and the used operating system.
Targeting .net framework 4.8 should default to TLS1.2 on Windows 10/11
Any change that you are using an older version of either? Or that you are setting the tls version explicitly somewhere in your application?
Also using lock inside an asynchronous method is bad practice and might deadlock your code.
When I use .NET Framework 4.6.1, I encounter the same problem. After I switched the version to 4.7.2, the problem was not solved until I explicitly specified the version in Web.config.
<httpRuntime targetFramework="4.7.2" />

How to use YouTube Data API

I tried using YouTube Data API.
I really took a good look at everything I found on the internet. The code itself isn't the problem, but I did not find out, where to use this code. Do I simply create a python file (in Visual Studio Code for example) and run it there? Because it didn't work when I tried this...
I also saw many people using the API with the commander only, others used something in chrome (localhost:8888...). So I don`t really know what's the way to go or what I should do.
Thanks for any help :)
Best regards!
I'm not a python developer but as a guess you could start here:
https://developers.google.com/youtube/v3/quickstart/python
using pip to install the dependencies you need.
You should be able to create a simple python file that authenticates with the API and then calls a method on the on the google api client and then output it. There are some examples here:
https://github.com/youtube/api-samples/blob/master/python/
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
namespace Google.Apis.YouTube.Samples
{
/// <summary>
/// YouTube Data API v3 sample: upload a video.
/// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
/// See https://code.google.com/p/google-api-dotnet-client/wiki/GettingStarted
/// </summary>
internal class UploadVideo
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("YouTube Data API: Upload Video");
Console.WriteLine("==============================");
try
{
new UploadVideo().Run().Wait();
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
Console.WriteLine("Error: " + e.Message);
}
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
private async Task Run()
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = "Default Video Title";
video.Snippet.Description = "Default Video Description";
video.Snippet.Tags = new string[] { "tag1", "tag2" };
video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
var filePath = #"REPLACE_ME.mp4"; // Replace with path to actual movie file.
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Console.WriteLine("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}
}
}
Make sure you have python installed on your PC
Create a project: Google’s APIs and Services dashboard
Enable the Youtube v3 API: API Library
Create credentials: Credentials wizard
Now you need to get an access token and a refresh token using the credentials you created
Find an authentication example in one of the following libraries:
https://github.com/googleapis/google-api-python-client
https://github.com/omarryhan/aiogoogle (for the async version)
Copy and paste the client ID and client secret you got from step 4 and paste them in the authentication example you found in step 6 (Should search for an OAuth2 example), this step should provide with an access token and a refresh token
Copy and paste a Youtube example from either:
https://github.com/googleapis/google-api-python-client
https://github.com/omarryhan/aiogoogle (for the async version)
Replace the access token and refresh token fields with the ones you got.
Now you should be able to run the file from any terminal by typing:
python3 yourfile.py
[EDIT]
The API key is not the same as the access token. There are 2 main ways to authenticate with Google APIs:
Access and refresh token
API_KEY.
API key won't work with personal info. You need to get an access and refresh token for that (method 1).
Once you get an access token, it acts in a similar fashion to the API_KEY you got. Getting an access token is a bit more complicated than only working with an API_KEY.
A refresh token is a token you get with the access token upon authentication. Access tokens expire after 3600 seconds. When they expire, your authentication library asks Google's servers for a new access token with the refresh token. The refresh token has a very long lifetime (often indefinite), so make sure you store it securely.
To get an access token and a refresh token (user credentials), you must first create client credentials. Which should consists of 1. a client ID and 2. a client secret. These are just normal strings.
You should also, set a redirect URL in your Google app console in order to properly perform the OAuth2 flow. The OAuth2 flow is the authentication protocol that many APIs rely on to allow them to act on a user's account with the consent of the user. (e.g. when an app asks you to post on your behalf or control your account on your behalf, it typically will use this protocol.)
Aiogoogle's docs does a decent job in explaining the authentication flow(s) available by Google.
https://aiogoogle.readthedocs.io/en/latest/
But this is an async Python library. If you're not familiar with the async syntax, you can read the docs just to get a general idea of how the authentication system works and then apply it to Google's sync Python library.
About point no.6. The links I posted with Aiogoogle being one of them, are just client libraries that help you access Google's API quicker and with less boilerplate. Both libraries have documentation, where they have links to examples on how to use them. So, open the documentation, read it, search for the examples posted, try to understand how the code in the example(s) work. Then maybe download it and run it on your own machine.
I recommend that your read the docs. Hope that helps.

'CspKeyContainerInfo' requires Windows Cryptographic API (CAPI), which is not available on this platform

I have upgraded my project to asp.net core v2.2 from v2.1 and everything was used to work just fine.In the code shown below, I am trying to initilaize an RSA Key for with IdentityServer4(v2.3.2) and while trying to get a token I get the following error.
try
{
var rsaProvider = new RSACryptoServiceProvider(2048);
var rsaParametersPrivate =
RsaExtensions.RsaParametersFromXmlFile(Configuration.GetSection("JwtSettings:rsaPrivateKeyXml")
.Value);
rsaProvider.ImportParameters(rsaParametersPrivate);
var securityKey = new RsaSecurityKey(rsaProvider);
_signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);
_logger.LogInformation("InitializeRsaKey() successfully executed.");
}
catch (Exception ex)
{
var exception = new Exception("Identity Server RSA Key initialization failed. " + ex);
_logger.LogError(exception, "InitializeRsaKey() method failed.");
throw exception;
}
'CspKeyContainerInfo' requires Windows Cryptographic API (CAPI), which is not available on this platform. error.
Also, my project runs on a CentOS machine meanwhile I develop my project on Windows 10. So, I am aware that something existing in Windows is missing on Linux. To solve the problem any help and suggestion is appreciated.
I digged some github issues and found out that RSACryptoServiceProvider() intherits ICspAsymmetricAlgorithm and this class is supported only on Windows. For details check out here. To fix the problem I have replaced var rsaProvider = new RSACryptoServiceProvider(2048); line with var rsaProvider = RSA.Create(2048); and it works fine with .NET Core v2.2 on CentOS. Hope this helps those who have the same issue.

Can you please provide - Google Bigquery dependency latest Dotnet NUGET package names, because the Bigquery example programs are not working

Need help to get
Google Bigquery dependency latest Dotnet NUGET package names, because the Bigquery example programs are not working and it is giving the dependecy reference expections at IAuthorizationState
eError 1 The type 'DotNetOpenAuth.OAuth2.IAuthorizationState' exists in both 'c:\Echelon\GoogleDownloads\google-api-dotnet-client-1.8.1.source\Src\GoogleApis.Authentication.OAuth2.Tests\bin\Release\DotNetOpenAuth.dll' and 'c:\Users\Srinivasa\Documents\Visual Studio 2013\Projects\BigQueryConsole1\packages\DotNetOpenAuth.OAuth2.Client.4.3.4.13329\lib\net40-full\DotNetOpenAuth.OAuth2.Client.dll' c:\users\srinivasa\documents\visual studio 2013\Projects\BigQueryConsole1\BigQueryConsole1\Program.cs 37 24 BigQueryConsole1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetOpenAuth.OAuth2;
using Google.Apis.Authentication.OAuth2;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using Google.Apis.Bigquery.v2;
using Google.Apis.Util;
namespace BigQueryConsole1
{
class Program
{
public static void Main(string[] args)
{
// Register an authenticator.
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
// Put your client id and secret here (from https://developers.google.com/console)
// Use the installed app flow here.
provider.ClientIdentifier = "asdfasd";
provider.ClientSecret = "asdfasdf";
// Initiate an OAuth 2.0 flow to get an access token
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
// Create the service.
var service = new BigqueryService(auth);
// Do something with the BigQuery service here
// Such as... service.[some BigQuery method].Fetch();
}
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { BigqueryService.Scopes.Bigquery.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.Write(" Authorization Code: ");
string authCode = Console.ReadLine();
Console.WriteLine();
// Retrieve the access token by using the authorization code:
return arg.ProcessUserAuthorization(authCode, state);
}
}
}
You are using an old version of the library.
The latest NuGet package is available in: http://www.nuget.org/packages/Google.Apis.Bigquery.v2/
Take a look in our samples repository for samples on Google APIs (Unfortunately, we don't have a sample for BigQuery)
You should also take a look in our Getting Started guide and to be more specific in the OAuth 2.0 page.
Good luck!
[UPDATE]
We improved the OAuth 2.0 library significantly. Take a look in our blog announcement from
October 2013 - http://google-api-dotnet-client.blogspot.com/2013/10/announcing-release-of-160-beta-new.html.
In the OAuth 2.0 page I mentioned above there are the exact details of how to set your application to work with Google authorization. The library supports the OAuth 2.0 flows in Windows Phone, Windows 8 application and regular .NET 4.0 and higher.

Read SSL Certificate Details on WP8

I want to read certificate details (e.g. expiration date or CN) for security reasons.
Usually there are some properties in network classes available, that allow to check the certificate. This is missing in WP8 implementations.
Also I tried to create an SslStream but also there is no way to get any certificate detail like the RemoteCertificate on .net 4.5.
var sslStream = new SslStream(new NetworkStream(e.ConnectSocket));
The SslStream is missing everything relating security. So it looks like also BountyCastle and other libraries cannot be able to get the certificate, because the underlying framework doesn't support it.
So my questions are:
Can I read the CN or other Certificate details on WP8 using other approaches.?
If not, how can you create then seriously secure apps (line banking) on WP8 using techniques like SSL Pinning or client side certificate validation and is there any reason why this is not supported in WP8?
Regards
Holger
I issued a user voice request to Microsoft .NET team asking them to provide a solution for reading server SSL certificate details from portable class libraries (targeting also WP8). You can vote it here: http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4784983-support-server-ssl-certificate-chain-inspection-in
On Windows Phone 8.1 this can be done with HttpClient, as well as with StreamSocket (as Mike suggested).
Example for certificate validation with StreamSocket can be found here (Scenario5_Certificate in source code).
Certificate validation with HttpClient can be done by handling the ERROR_INTERNET_INVALID_CA exception, validating the server certificate using the HttpTransportInformation class, creating new instance of HttpBaseProtocolFilter class and specifying the errors to ignore.
Note that not all the errors are ignorable. You will receive an exception if you'll try to add Success, Revoked,
InvalidSignature, InvalidCertificateAuthorityPolicy, BasicConstraintsError, UnknownCriticalExtension or OtherErrors enum values.
I'm adding a sample code that bypasses certificate errors using HttpClient:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Security.Cryptography.Certificates;
using Windows.Web.Http;
using Windows.Web.Http.Filters;
namespace Example.App
{
public class HttpsHandler
{
private const int ERROR_INTERNET_INVALID_CA = -2147012851; // 0x80072f0d
public static async void HttpsWithCertificateValidation()
{
Uri resourceUri;
if (!Uri.TryCreate("https://www.pcwebshop.co.uk/", UriKind.Absolute, out resourceUri))
return;
IReadOnlyList<ChainValidationResult> serverErrors = await DoGet(null, resourceUri);
if (serverErrors != null)
{
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
foreach (ChainValidationResult value in serverErrors)
{
try {
filter.IgnorableServerCertificateErrors.Add(value);
} catch (Exception ex) {
// Note: the following values can't be ignorable:
// Success Revoked InvalidSignature InvalidCertificateAuthorityPolicy
// BasicConstraintsError UnknownCriticalExtension OtherErrors
Debug.WriteLine(value + " can't be ignorable");
}
}
await DoGet(filter, resourceUri);
}
}
private static async Task<IReadOnlyList<ChainValidationResult>> DoGet(HttpBaseProtocolFilter filter, Uri resourceUri)
{
HttpClient httpClient;
if (filter != null)
httpClient = new HttpClient(filter);
else
httpClient = new HttpClient();
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, resourceUri);
bool hadCertificateException = false;
HttpResponseMessage response;
String responseBody;
try {
response = await httpClient.SendRequestAsync(requestMessage);
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
} catch (Exception ex) {
hadCertificateException = ex.HResult == ERROR_INTERNET_INVALID_CA;
}
return hadCertificateException ? requestMessage.TransportInformation.ServerCertificateErrors : null;
}
}
}
After trying open source libs like bouncyCastle, supersocket or webSocket4net I tested an evaluation of a commercial lib named ELDOS SecureBlackbox. This test was successfull. Here is a code snipped, that gets the X509Certificates with all details:
public void OpenSSL()
{
var c = new TElSimpleSSLClient();
c.OnCertificateValidate += new TSBCertificateValidateEvent(OnCertificateValidate);
c.Address = "myhostname.com";
c.Port = 443;
c.Open();
c.Close(false);
}
private void OnCertificateValidate(object sender, TElX509Certificate x509certificate, ref TSBBoolean validate)
{
validate = true;
}
The validation is getting all certificates... if validate is set to true, the next certificate will be shown. That means the callback is called forreach certificate there.
Regards
Holger
For WP8, you can use the StreamSocket class, which has an UpgradeToSslAsync() method that will do the TLS handshake for you as an async operation. Once that completes, you can use the .Information.ServerCertificate property to check that you got the server certificate you were expecting.