We have a feature where we use Windows' personal certificate store to authenticate using certificates after the user chooses a valid certificate. It simply signs a challenge with following:
public static byte[] SignDataSHA512RSA(X509Certificate2 certificate, byte[] data)
{
using (var rsa = certificate.PrivateKey as RSACryptoServiceProvider)
{
if (rsa == null)
{
return null;
}
return rsa.SignData(data, CryptoConfig.MapNameToOID("SHA512"));
}
}
It works pretty well using the RSACryptoServiceProvider class. The feature allows both regular certificates and smart card certificates. If there is a smart card and it requires PIN, windows prompts a dialog for it and all.
Now, there is an extra security requirement in this feature where if a smartcard is used for this operation (which we can find out by rsa.CspKeyContainerInfo.Removable, and HardwareDevice if you want to enforce hardware), we want to make sure that it's accessible at all times during the session. In other words, we need an event when the smart card is removed so we can log out automatically.
One crude way would be having a timer job which checks certificate.PrivateKey is accessible every minute or so, but that doesn't throw before prompting user to insert the smart card and user presses Cancel.
The feature supports Windows 7 as well, so using the UWP libraries is not an option. Any way to accomplish this?
You could use PC/SC to check for card removal. In windows it is implemented in WinSCard library. See this SO answer.
There is also a C# implementation pcsc-sharp.
Related
We are developing a C# UWP application using Visual Studio 2019. I have successfully setup monitoring of the YubiKey FIPS (4.4.5 firmware) being inserted/removed from the USB port. We setup the YubiKey to use PIV and have loaded a certificate into slot 9c (using YubiKey PIV Manager, I have not installed the mini driver). I do note that when the YubiKey is inserted into the USB, it auto loads my personal cert store with the certificate that is in slot 9c. We receive a challenge from our server and I need to use it to verify against the YubiKey. What is the next step to get the certificate from slot 9c (what if you have multiple certs on that key)? Yubico does not have an example showing how to integrate the key with an app (I don't believe Windows Hello is applicable here, no?). We are trying to use the Windows.Devices.SmartCards namespace. This namespace does not seem to have the concept of slots. Is that the correct direction or do we need to use Yubico libraries (mini driver) I'm not aware. The documentation is limited.
var yubiKeys = Readers.Where(r => r.Value.Name.Contains("Yubi", StringComparison.OrdinalIgnoreCase));
foreach (KeyValuePair<string, SmartCardReader> item in yubiKeys)
{
IReadOnlyList<SmartCard> cards = await item.Value.FindAllCardsAsync();
foreach(SmartCard card in cards)
{
SmartCardProvisioning prov = await SmartCardProvisioning.FromSmartCardAsync(card);
using (SmartCardChallengeContext context = await prov.GetChallengeContextAsync())
{
IBuffer yubiKeyChallenge = context.Challenge; // IS THIS THE CARDS ADMIN PIN?
// Challenge to acquire cert here perhaps?
// the card object has no concept of slots, would each slot be a card in the reader?
// if so, how would I use the Challenge for that card?
}
}
}
The Windows Smart Card components (including the Windows Inbox Smart Card Minidriver and the Yubico minidriver) don’t directly implement supported PIV concepts like slots or objects. Instead, the minidriver scans the PIV slots and converts any present keys to "key containers", which is how Windows deals with private keys and certificates. Each certificate in the Certificate Store has an associated key container, but since it has no concept of PIV, details like the key reference slot have been abstracted away.
As with most built-in Windows components, the app doesn’t talk to the smart card directly, instead it uses the certificate that the Certificate Propagation service adds to the personal store.
If you inspect the user's personal store with Certificate Manager (certmgr.msc), the certificate properties should have a note that you have a private key associated with the certificate.
To sign or encrypt with the private key, the app acquires the
certificate from the personal store and uses the appropriate .NET CSP
with that certificate.
In general, UWP apps have limited native interop capabilities and rely on using the rudimentary APIs of the smart card key storage provider (KSP) to later be called through the CNG interface.
Here’s an example of using the UserCertificateStore to interact with certificates within the user’s personal certificate store.
You can then use that certificate with your favorite CSP software library to sign or encrypt. A few examples here.
To access smartcard or usb token content like slots, keys in it, directly you may need to use PKCS#11.
But since you said smartcard content is being loaded by smartcard driver (CSP) to Windows Certificate store, you may use Windows.Security, X509 certificates and RSA Provider (I don't remember .NET namespaces exactly...) to give signing request using RSAProvider and CSP would in turn send that request to smartcard. You many not need to access smartcard directly for signing.
For browser based user authentication, please refer to so answer
We are generating certificates that are supposed to have an ECDSA key stored on the TPM. My understanding is that the GetECDsaPrivateKey extension method will load a handle to the algorithm for the private key in the TPM. Is this true? The certs we are testing with keep returning null and I need to know if they are simply being created incorrectly or if the test code is bad. The MSDN method description isn't very helpful in this case.
Yes, if it was told to look there.
On Windows, .NET's X509Certificate2 is backed by the Windows certificate APIs. Windows certificates have properties (in addition to their in-certificate attributes), and one of the properties identifies where the private key lives (in effect, the arguments to CngKey.Open) -- if the property is missing (and the "I have already opened the private key [here]" property is also missing) then the cert "has no private key" (cert.HasPrivateKey == false).
If your certificate is part of a Windows Certificate Store, and viewing that store via MMC shows the private key icon, then GetECDsaPrivateKey() can open it (assuming the key wasn't deleted after the fact, and that it's an ECDsa-capable key).
If you load the certificate from a .cer file, or some other means, then the cert doesn't know that there's a private key available, and it never searches for it. If you know how to find the private key and open it into an ECDsa object (e.g. ECDsaCng) then you can (in-memory) associate them by X509Certificate2 certWithKey = cert.CopyWithPrivateKey(key);. At that point, having been told where the private key lives, the new object's GetECDsaPrivateKey() works as expected.
That GetECDsaPrivateKey only gets the key if it's part of the certificate. To use a key from the TPM, you need to use the CngKey.Open() method with the name of the key and the storage provider.
In my organization, users must use SmartCard for interactive login to a Windows stations (95,Vista and 7). almost daily, we need to read the credentials stored in the SmartCard and compaire them with the ActiveDirectory, without implementing a custom credentials manager. The fields we compare are: userPrincialName and sAMAccountName.
Can you please show me a code that demonstrates how to read the credentials from the SmartCard or guide me to an article / code on the internet?
A search over internet suggeted implementing credentials manager or using other languages (like C, C++).
Also, I came across this article : http://www.codeproject.com/Articles/17013/Smart-Card-Framework-for-NET written by orouit, which is a framework for working with SmartCards - but I think this too much for my simple task. What do you think?
Well if developing under windows, once you insert smart card windows will fetch all certificates from the smart card place them to the My certificate store.
var smartCardCerts = new List<X509Certificate2>();
var myStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
myStore.Open(OpenFlags.ReadOnly);
foreach(X509Certificate2 cert in myStore.Certificates)
{
if( !cert.HasPrivateKey ) continue; // not smartcard for sure
var rsa = cert.PrivateKey as RSACryptoServiceProvider;
if( rsa==null ) continue; // not smart card cert again
if( rsa.CspKeyContainerInfo.HardwareDevice ) // sure - smartcard
{
// inspect rsa.CspKeyContainerInfo.KeyContainerName Property
// or rsa.CspKeyContainerInfo.ProviderName (your smartcard provider, such as
// "Schlumberger Cryptographic Service Provider" for Schlumberger Cryptoflex 4K
// card, etc
var name = cert.Name;
rsa.SignData(); // to confirm presence of private key - to finally authenticate
}
}
basically a lot of crypto API is available via .NET nowdays. But you could also use API directly Crypto API
for example you could access smart card directly via
CryptAcquireContext(&hProv,"\\.\<Reader Name>\<Container Name>",...)
where reader name is card reader name and container name is whatever rsa.KeyContainerName in code snippet above. There are multiple ways to access information like that and Crypto API is not very consistent or straightforward. as a hint .NET version of CryptAcquireContext is RSACryptoServiceProvider with CspParameters where you can specify container name if needed.
Well finding user in ActiveDirectory may be done via System.DirectoryServices.DirectoyEntry and System.DirectoryServices.DirectorySearcher, but do not forget System.DirectoryServices.ActiveDirectory.Forest and related API which makes some things a lot easier to figure out.
You would be able to get
I believe I have a foundational understanding of the X509Certificate and have created certificates and private keys for my Development environment. Getting more acquainted now with the X509Certificate Class and other relevant permissions also.
So in the process, I decided to test a certificate after having installed it on my system. Then using the following code, I attempted to validate the check process for certification authentication:
const string x509Cert = #"\PathToMyCertificate\LMC.cer";
var cert = new X509Certificate(x509Cert);
var pmc = new PublisherMembershipCondition(cert);
if(pmc.Check(Assembly.GetCallingAssembly().Evidence))
{
Console.WriteLine("Assembly belongs to publisher");
}
Of course as expected, the inner block of code doesn't execute. So then I figured to simply sign my assembly using the certificate key, but "simply" wasn't as simple as I'd anticipated.
I used the following code in effort of assigning the certificate to my applications Evidence:
var publisher = new Publisher(X509Certificate.CreateFromCertFile(x509Cert));
var evidence = Assembly.GetCallingAssembly().Evidence;
evidence.AddHost(publisher);
// Create an identity permission based on publisher evidence.
var x509Permission = (PublisherIdentityPermission)publisher.CreateIdentityPermission(evidence);
x509Permission.Demand();
But this didn't seem to work either. Next, I checked the properties of my project to see if there was any way to sign it there using the X509Certificate key but nothing. The only option I see that comes close is to sign with Click Once manifests; but the "Select from file" option is looking for a .pfx extension. So I think maybe this method only works to support certificates generated by Click-Once?
Per BOL, "If you have a key file or certificate in another format, store it in the Windows certificate store and select the certificate is described in the previous procedure." I installed my X509Certificate in the Trusted Root Certificate Authorities store. Wouldn't that be a Windows certificate store? Because nothing shows up in the Certificate Store window.
Searching online resources didn't yield much either unless I am using the wrong combination of keywords. Now I could create another X509Certificate and key ensuring that the extension of the key is .pfx but I wanted to make certain that I am on the right course of resolve before spinning my wheels for nothing and I don't believe that would be the answer.
So, can a .NET assembly be signed using an X509Certificate? If so, what documentation is available to assist in performing this task?
Publisher* classes are associated with Authenticode(tm).
Look for the command-line tools:
* signcode (or signtool)
* chktrust
for how you can sign any .exe, .dll (and .cab, .ocx) using a valid code-signing certificate. A google search on Authenticode or "code-signing certificate" can also be helpful.
The question is what you want to do. There exist .NET signing (using RSA keypair) used for strong-naming the assemblies, and there exists Authenticode which lets you sign any file in PE format including assemblies in DLL files. Note, that Authenticode is not .NET-specific and knows nothing about .NET. It signs PE structure.
As said by poupou, for Authenticode signing (using X.509 certificates suitable for Code Signing) you can use SignTool.exe tool. .NET will verify the signature when it loads the assembly, but in some cases such verification can take extra seconds (if the OS performs CRL and OCSP checking of certificates in the chain), slowing down assembly loading.
So you need to choose the right tool and define what you want to achieve (signing is a method, not a goal).
I'm developing a console app for personal use that uses the Exchange Web Services Managed API as an alternative to Redemption (which I, unfortunately, cannot use). My ultimate goal is to use the app in a Windows scheduled task to connect to my Exchange mailbox and carry out some tasks like creating folders at specific times during the year, moving emails to those folders, set retention policies on items, etc.
The app is coming along nicely, but I'm curious about how best to implement secure authentication for the app so I can use this at work. I know Exchange Online won't let me connect automatically with default credentials (not sure if this includes Modern Authentication...see below) and I'll have to pass them explicitly. While in development, I'm explicitly passing my user ID and password to Exchange using the following code:
string userId = UserPrincipal.Current.EmailAddress;
SecureString securePwd = new SecureString();
Console.Write("Current User ID/Email Address: {0}\n", userId);
Console.Write("Enter Password: ");
do
{
key = Console.ReadKey(true);
// Ignore the Backspace and Enter keys.
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
{
// Append the character to the password.
securePwd.AppendChar(key.KeyChar);
Console.Write("*");
}
else
{
if (key.Key == ConsoleKey.Backspace & securePwd.Length > 0)
{
securePwd.RemoveAt(securePwd.Length - 1);
Console.Write("\b \b");
}
else if (key.Key == ConsoleKey.Enter)
{
break;
}
}
} while (true);
service.Credentials = new WebCredentials(new NetworkCredential(userId, securePwd));
I was hoping, since I will be creating my Windows scheduled task with the Run whether user is logged on or not option selected and will enter my password, that the app could use and pass the same credentials that the scheduled task is using so I don't have to store my password at all, but I haven't found anything that would allow this yet.
My work has enabled Modern Authentication with Exchange Online and that appears to pass my Windows credentials to Exchange when I'm using the Outlook client without requiring me to explicitly enter my credentials for the Outlook client. Is there something similar in EWS Managed API that will allow my app to login with my Windows credentials without explicitly passing them? I know you can register your app and use OAuth, but I wanted to avoid that since this is for my personal use and I'm not sure I'll be able to register my app at work.
If the above isn't possible, I have seen a number of articles, like this one, that mention using the Windows Credential Manager and that looks promising, but it appears as though I may still need to do something additional with my password prior to storing it. If I create a credential in Windows Credential Manager and implement the code at the link above using the code below (and using the CredentialManagement NuGet package), the password is displayed in plain text.
private const string PasswordName = "CredentialTest";
public static void Main(string[] args)
{
LoadCredential();
Console.ReadKey();
}
public static void LoadCredential()
{
using (var cred = new CredentialManagement.Credential())
{
cred.Target = PasswordName;
cred.Load();
Console.WriteLine("Password: {0}", cred.Password);
}
}
Is it recommended to read in the password as a secure string, encrypt it using a key that is stored in an encrypted section of the app.config file, and store the encrypted password in Windows Credential Manager?
Another interesting item I came across was the ClientCertificateCredentials class in the EWS Managed API. Unfortunately, I haven't seen any examples of it in use. Is this an alternate method of authentication? Can a self-signed certificate be used for this method? If so, does it require storing a private key on the Exchange Online server?
I'm open to other ideas and appreciate the help.
Thanks!
Windows Credentials Manager is of course an option (that is how Outlook still stores POP3/IMAP4/SMTP passwords and used to store Exchange credentials).
Keep in mind that Office 365 will turn Basic authentication off in October 2020.
OAuth might be an option, but that means you'd still need to somehow prompt the user for the credentials and store the access and refresh tokens somehow.
The preferred solution is to use certificate based authentication - see for example https://developermessaging.azurewebsites.net/2018/09/11/authenticating-against-exchange-web-services-using-certificate-based-oauth2-tokens/
But that requires importing certificate on the server side, which requires admin privileges.