read ssl private key from smartcard with c# - c#

I have a smartcard with a card reader and I decide to implement a client certificate in the latter.
The private key being stored in the card and the public key is stored in the server.
The application scenario is something like this:
application send frames to the Linux server
Linux server answer with a ramdom number
application read private key from (smartcard or pem file) and encrypt received random number and send the result to the server.
server try to decrypt the sent encrypted string via public key (pem file stored in the server) => if succeed access is granted else access is denied.
In my application, there is a mode of certificate authentication (static file .pem) and also uses the private key (also static .pem). I used openssl-net functions such as FromPrivateKey(string pem, string password) and PrivateEncrypt(byte[] msg, RSA.Padding padding) for read private key and encrypt data to send to the server.
The problem that is needed, is that I have no a priori way to export my private key in my smartcard since my pem file.
So after much research I understood that I should use instead of these functions type functions: "ENGINE_load_private_key" with engine "pkcs11."
So I have seen several examples of openssl configuration (eg http://openssl.6102.n7.nabble.com/Private-Key-from-Windows-Cert-Store-td20720.html) with pkcs11 (opensc, engine_pkcs11. so etc. ..) the trouble is that these configurations are for Linux.
Indeed, my need is to develop a client application windows WPF (written in C #) that can read the private key from the smartcard or from a PEM file.

Related

C# - How to merge private key with certificate

Is there a way to add private key to certificate? API of Certificate authority app returns two base64 strings (one with private key, one with certificate without private part). I want to save the Resulting certificate onto smart card.
There are some answers but there are missing libraries or they don't work at all.
What about building an X509Certificate2 from it, using e.g. Associate a private key with the X509Certificate2 class in .net
then, dump the certificate to PFX using certificate.Export(X509ContentType.Pkcs12,"password"), and dumping the byte[] to disk, and finally, launch:
certutil -importPFX <pfxFile> -p password -csp "Microsoft Smart Card Key Storage Provider" ...

UWP - Cross Device Data Encryption

My UWP app stores data in encrypted form in local SQLite database on the device. I use Windows.Security.Cryptography.DataProtection classes for static data and also data streams encryption/decryption (Ref: https://learn.microsoft.com/en-us/windows/uwp/security/cryptography)
I have provided OneDrive data backup facility with the idea that the user can backup entire database to OneDrive from one device and restore it in the app installed on another device. This may help the user use the app on multiple devices and also in case the user acquires a new device.
I use "LOCAL=user" Descriptor for the DataProtectionProvider class (Ref: https://learn.microsoft.com/en-us/uwp/api/windows.security.cryptography.dataprotection.dataprotectionprovider)
I was hoping that if I login using my Microsoft Account on two different devices and I encrypt data on one device, then restore data on other then the data should get decrypted; however this is not happening.
I was unable to get any documentation as well (apart from the references listed above). I searched SO as well for MS Support but no luck. Can somebody help me with this?
My requirement: Data encrypted on one (Windows) device should be decrypted in other (Windows) device (when a user is logged in using same Microsoft Account on both the devices).
[UPDATE]
Here's the code sample:
const BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
const string strDescriptor = "LOCAL=user";
public static async Task<string> ProtectTextAsync(string strClearText)
{
DataProtectionProvider Provider = new DataProtectionProvider(strDescriptor);
IBuffer buffMsg = CryptographicBuffer.ConvertStringToBinary(strClearText, encoding);
IBuffer buffProtected = await Provider.ProtectAsync(buffMsg);
return CryptographicBuffer.EncodeToBase64String(buffProtected);
}
public static async Task<String> UnprotectTextAsync(string strProtected)
{
DataProtectionProvider Provider = new DataProtectionProvider();
IBuffer buffProtected = CryptographicBuffer.DecodeFromBase64String(strProtected);
IBuffer buffUnprotected = await Provider.UnprotectAsync(buffProtected);
String strClearText = CryptographicBuffer.ConvertBinaryToString(encoding, buffUnprotected);
return strClearText;
}
The code is trivial; however, the process of error reproduction is important and is as follows:
I run the App on my Windows 10 Mobile (OS build: 10.0.14393.1770) then Backup data on OneDrive. My mobile shows that I am using a Microsoft Account (say NP3#msft.com) at Settings-->Accounts-->Your Info.
Now, I log-in to my Windows 10 Laptop (OS build: 15063.674 version: 1703 with Fall Creators Update SDK only applied) using NP3#msft.com account when I run the App and Restore the Backup from OneDrive.
Now, when I try to access the data, I get the error in IBuffer buffUnprotected = await Provider.UnprotectAsync(buffProtected); line of the UnprotectTextAsync method. The error is:
System.Exception: 'The specified data could not be decrypted. (Excep_FromHResult 0x8009002C)'
Please note that if I restore data backed-up on OneDrive from the same device (Mobile or Laptop), then this code works fine. So Backup/Restore functionality is working correctly with no data modification.
I'll try to keep it brief but there are a few ways you can go about this, first let's talk about the UWP storage first.
The UWP provides APIs to store user preferences and settings along with data in Three types of storage:
Local: This is basically storing data as application data in the local storage of the device itself. What can you store here? It's for all sorts of data that can be serialized. It shouldn't be too heavy else it'll throw you an Access Violation Exception. I once used it to store image as byte streams as well so it provides quite much of flexibility when it comes to storage.
PasswordVault: This is generally to store user credentials across multiple devices so that the use doesn't have to sign in to your app on every device, if you have the same Microsoft account, it'll log you in right away. You won't need to explicitly encrypt data inside it since the APIs automatically encrypt the data for you while transferring and storing the data across devices.
Roaming: Now this is what you'll be most interested in. Roaming settings are the ones that get transferred across device if you're signed in with the same Microsoft account. The data wouldn't be implicitly encrypted so you might will have to handle the security aspects for it. It's generally used to transfer Settings for the App and the Preferences for the user if he/she has something (e.g. Application Theme, Wallpaper). Windows 10 OS Utilizes this storage to transfer all sorts of stuff when you install windows 10 on another machine you can find a comprehensive list here. It's just amazing.
Finding the best Fit:
Now that We've had a look at our options, let's try to solution-ate your issue, and how to pick what storage.
Since you have to transfer data over multiple devices, the Local storage is out of question. Now we have two options left PasswordVault and RoamingStorage / RoamingSettings.
The question is, what do you want to transfer (for which you use one drive), is it just a bunch of preferences? or is it file(s) of varied sizes? or is it user credentials?
If it's user credentials, PasswordVault is the ideal fit. It'll not only handle DataTransfer but also provide seamless integrated signIn across devices if the user is using the same Microsoft Account.
If it's just a bunch of preferences, then use RoamingSettings. They'll transfer the data to the other devices using Microsoft's own APIs and all you have to do is, fetch them from the RoamingStorage container and you can start using the data.
But if it's a bunch of files, encrypted and stored on one drive and you want to decrypt on other devices below is a solution that I recommend.
Reading files from oneDrive, Decryption on Other devices:
The approach could be quite simple, if you have files that are stored on one drive,
When the use logs in to the app for the first time, check if the roamSettings for that Microsoft account for your app is present or not, since it's not it'll return you a null. In such a case, create a RoamingStorage and proceed to step 2.
Create the keys that'll be needed for encryption. (explained in
detail in the next section below)
Now that you have the keys, you perform all operations to get the data that needs to be written into the files.
Encrypt the data using the keys for encryption and once the data is encrypted, write it into a file and upload it to oneDrive.
Store the keys (explained in the next section below) into roaming storage for that Microsoft Account.
Now when the user signs in to your app using another device, as in point 1, check if there exists any roamingSettings. Only this time it won't be null and you'll get the user's roamingSettings for the app into your other device. extract the stored key from there and keep it in a variable.
Download the files from oneDrive and read their contents as string.
Use the key stored in the variable (point 6) to decrypt the data of the file
Now you have the actual data, resume your application flow.
Quick Look at Encryption
Encryption is out of the scope of this question so I'll just explain a basic joist of it and if any help is needed, please use the comments section.
The above solution would also depend on the type of encryption you're using here, if its
Symmetric: If you're using symmetric (like AES), then you generate an encryption key and an InitializationVector (also called IV) on the first device as soon as the user logs in and store them in the RoamingSettings
Asymmetric: If you're using asymmetric (like RSA), you'll generate a set of keys publicKey and privateKey use the publicKey to encrypt the data and then store it on one drive and then store the privateKey into the roaming settings.
It's not recommended to share private keys over the network incase of Asymmetric encryption but, this is a little unconventional but you're using Microsoft's established APIs to transfer data (which they claim to be secure) so that'll reduce the risk.
Please do let me know if I've skipped out anything.
Edit from the comments:
To be honest, since you mentioned a single key you're talking about AES-256. Now if you don't want the developer to have access to the key, it's kinda a default, you would be using Cryptography APIs by Microsoft for the AES. So i in a way you would be calling an API that'll give you the key and you'll be calling another API that'll be encrypting the data. Most Importantly The API would be called at runtime so either ways the developer has no access to it.
But if your query is that the developer should not even know what kind of encryption and where is being stored then in that case I would recommend you use a Factory Pattern , where in you abstract out the implementation of the data that gets encrypted you just pass in the data, that, class handles all the creating of key, encryption of data and storing of the key to roaming and then returns the encrypted data.
References:
Store and retrieve settings and other app data
Credential locker (a.k.a PasswordVault)
Windows 10 roaming settings reference
Advanced Encryption Standard-AES
RSA
#region AES Encryption
public static async Task<bool> EncryptAesFileAsync(StorageFile fileForEncryption, string aesKey256, string iv16lenght)
{
bool success = false;
try
{
//Initialize key
IBuffer key = Convert.FromBase64String(aesKey256).AsBuffer();
var m_iv = Convert.FromBase64String(iv16lenght).AsBuffer();
SymmetricKeyAlgorithmProvider provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var m_key = provider.CreateSymmetricKey(key);
//secured data
IBuffer data = await FileIO.ReadBufferAsync(fileForEncryption);
IBuffer SecuredData = CryptographicEngine.Encrypt(m_key, data, m_iv);
await FileIO.WriteBufferAsync(fileForEncryption, SecuredData);
success = true;
}
catch (Exception ex)
{
success = false;
DialogHelper.DisplayMessageDebug(ex);
}
return success;
}
public static async Task<bool> DecryptAesFileAsync(StorageFile EncryptedFile, string aesKey256, string iv16lenght)
{
bool success = false;
try
{
//Initialize key
IBuffer key = Convert.FromBase64String(aesKey256).AsBuffer();
var m_iv = Convert.FromBase64String(iv16lenght).AsBuffer();
SymmetricKeyAlgorithmProvider provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var m_key = provider.CreateSymmetricKey(key);
//Unsecured Data
IBuffer data = await FileIO.ReadBufferAsync(EncryptedFile);
IBuffer UnSecuredData = CryptographicEngine.Decrypt(m_key, data, m_iv);
await FileIO.WriteBufferAsync(EncryptedFile, UnSecuredData);
success = true;
}
catch (Exception ex)
{
success = false;
DialogHelper.DisplayMessageDebug(ex);
}
return success;
}
#endregion

RSA Crypto Service Container Private Key goes out of sync

In a client/server model
We have a RSACryptoServiceProvider key created using a well known "container name" at the startup code, and set a rule on it to Allow Generic Read, and persist the public key into a database. The clients connecting to the server, send sensitive information encrypted with the public key and the server decrypts it using the private key.
However, over time, we are observing that the public key in the crypto store (it's a machine level crypto store at %ProgramData%\Microsoft\Crypto\RSA\Machine Keys goes out of sync with the stored public key in the database) and our clients stop communicating with the server.
Are there any possible reasons as to how this happens ? Is there a way we can detect it when this happens ?

How to Encrypt a file for transport in C#?

I am trying to encrypt a file when I save it to disk and I have looked at the Crypto namespace in C#, but am unsure how I should do it. Basically I need the ability for my program to be able to both encrypt and decrypt a file. The file is just an xml file that is serialized by my program, but it can contain sensitive data like connection strings for SQL servers. My clients want the ability to email these profiles to others and open them in our application to apply the settings to their system.
I tried the AES classes in the Crypto namespace, but I don't know where to store the IV and the key so that my program on another machine will be able to decrypt it.
In a typical scenario, the flow for handling something like this would go:
The IV is static and known to the client
The end machine generates an RSA keypair, and gives the public key only to the party sending the data (the XML file)
Your AES key is generated, and encrypted using the RSA public key and sent to the client, now only the client is able to obtain that AES key using the private key it generated previously.
You encrypt the data using the AES key you securely sent to the client earlier
This means that even if someone captured the complete data stream, they wouldn't be able to decrypt your data because they don't have the private key required to obtain the AES key.

Import certificate with private key programmatically

I'm trying to use the HttpListener class in a C# application to have a mini webserver serve content over SSL. In order to do this I need to use the httpcfg tool. I have a .pfx file with my public and private key pair. If I import this key pair manually using mmc into the local machine store, everything works fine. However, if I import this key pair programmatically using the X509Store class, I am not able to connect to my mini webserver. Note that in both methods the cert is getting imported to the MY store in LocalMachine. Oddly, I am able to view the certificate in mmc once I programmatically import it and when I view it, the UI indicates that a private key is also available for this certificate.
Digging a little deeper, I notice that when I manually import the key pair, I can see a new file appear in C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys, but one does not appear when I import programmatically. On a related note, when I delete a manually imported certificate, it does not remove the corresponding private key file from the previously mentioned directory.
Ultimately, my question is this: When I programmatically add the certificate to the store, where is the private key being stored and why isn't it accessible to the HttpListener class (HttpApi)?
Note that this question is slightly related but I don't think permissioning is the problem since this is all being done as the same Windows user:
How to set read permission on the private key file of X.509 certificate from .NET
Ok, I figured it out. It had to do with the key storage parameters for the certificate object. For anyone else that runs into this problem, make sure you construct your X509Certificate2 objects that you are adding to the store using the X509KeyStorageFlags.PersistKeySet and X509KeyStorageFlags.MachineKeySet flags. This will force the private key to persist in the machine key set location which is required by HttpApi (HttpListener wraps this).
Is this a 2 way SSL? If it is then did you send over a SSL Certificate Request file generated on your machine? This Certificate Request file will be used to create the SSL and they together form a public private key pair.
Also did you try assigning the cert permission for the user account that is being used to run the web app? You can do this by using the Microsoft WSE 3.0 tool.
Not exactly the answer to your question, but here for reference of others going down this path:
Here is a link to a MS chat that gives sample C# code to do what httpcfg does, thus eliminating the need for the tool on deployment.

Categories