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
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 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.
I am working on a key management application for the universal windows platform and would like to install CA certificates and trust anchors that can be used by system apps and 3rd-party apps. I have tried using a combination of CertificateStores.GetStoreByName and CertificateStore.Add as well as a call accessed via P/Invoke to CertAddEncodedCertificateToStore. Unfortunately, in both cases the calls succeed but the certificates are not visible using MMC and they do not appear to be used by other applications.
Is there a means of installing certificates such that they are usable system-wide (including outside the app container)? Is there any means of viewing what certificates have been installed within an app container?
By default no. Please check introduction to certificates article.
Shared certificate stores
UWP apps use the new isolationist application model introduced in Windows 8. In this model, apps run in
low-level operating system construct, called an app container, that
prohibits the app from accessing resources or files outside of itself
unless explicitly permitted to do so. The following sections describe
the implications this has on public key infrastructure (PKI).
Certificate storage per app container
Certificates that are intended for use in a specific app container are stored in per user,
per app container locations. An app running in an app container has
write access to only its own certificate storage. If the application
adds certificates to any of its stores, these certificates cannot be
read by other apps. If an app is uninstalled, any certificates
specific to it are also removed. An app also has read access to local
machine certificate stores other than the MY and REQUEST store.
Anyway, you can add a capability to your application in Package.appxmanifest. The sharedUserCertificates capability grants an app container read access to the certificates and keys contained in the user MY store and the Smart Card Trusted Roots store.
<Capabilities>
<uap:Capability Name="sharedUserCertificates" />
</Capabilities>
I just added it for testing purpose (UWP application) and the following code works fine. Certificate is added on user MY store.
string pfxCertificate = null;
string pfxPassword = "";
FileOpenPicker filePicker = new FileOpenPicker();
filePicker.FileTypeFilter.Add(".pfx");
filePicker.CommitButtonText = "Open";
try
{
StorageFile file = await filePicker.PickSingleFileAsync();
if (file != null)
{
// file was picked and is available for read
// try to read the file content
IBuffer buffer = await FileIO.ReadBufferAsync(file);
using (DataReader dataReader = DataReader.FromBuffer(buffer))
{
byte[] bytes = new byte[buffer.Length];
dataReader.ReadBytes(bytes);
// convert to Base64 for using with ImportPfx
pfxCertificate = System.Convert.ToBase64String(bytes);
}
await CertificateEnrollmentManager.UserCertificateEnrollmentManager.ImportPfxDataAsync(
pfxCertificate,
pfxPassword,
ExportOption.NotExportable,
KeyProtectionLevel.NoConsent,
InstallOptions.None,
"Test");
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
A sample is available on 8.1 if it helps. Cryptography and Certificate sample
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.