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
Related
I'm currently working on refining communications between mutually authenticated client/server applications using HTTPS. I am currently building validation logic into a C# client to help identify configuration issues when a TLS connection fails. In verifying the connection, I validate that the root CA certificate presented by the server is installed on the client, in the appropriate store, and is valid. I'm using X509Store to pull the X509Certificate2, and validating it using X509Chain.
My issue is that the certificate will report as valid even if the certificate has been disabled via MMC. So the TLS connection will fail, despite the chain reporting as valid.
It's an unlikely case, but one I'd like to handle by reporting something like "Could not connect because root CA is disabled."
Could anyone point me in the direction of a .NET or Win32 call that could be made to determine the value of "Certificate Purposes" for a certificate? Or to read the "Certificate Status" for a cert?
I read through MSDN's listing of what's in the System.Security.Cryptography namespace, and started looking into CryptoAPI and CNG, but didn't find anything so far.
Thanks!
That dialog does not "disable" a certificate it disables it "for all purposes". What this means is it counts as having an empty Enhanced Key Usage extension for purposes of EKU validation.
Normally a root certificate (or an intermediate CA certificate) will not have an EKU extension, so if you do a chain build with any ApplicationPolicy value it will be a match. Once you set it to Disable for all purposes you'll get a chain error X509ChainStatusFlags.NotValidForUsage.
If you want to build something for validating TLS you'd check either the Server Authentication or Client Authentication EKUs (depending on what you're checking):
// Server EKU or Client EKU
Oid eku = forServer ? new Oid("1.3.6.1.5.5.7.3.1") : new Oid("1.3.6.1.5.5.7.3.2");
// Test if it's valid for that purpose
chain.ChainPolicy.ApplicationPolicy.Add(eku);
If you want to "Disable" a root CA altogether, add a copy of the certificate to the Disallowed store (called "Untrusted Certificates" in the UI). That will result in a chain build producing X509ChainStatusFlags.ExplicitDistrust.
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'm trying to digitally sign a PDF document using Syncfusion PDF 10.4, like so:
PdfLoadedDocument document = new PdfLoadedDocument(inputStream);
PdfCertificate certificate = PdfCertificate.FindBySubject(certificateStoreType, certificateSubjectName);
PdfSignature signature = new PdfSignature(document, document.Pages[0], certificate, "Signatur");
signature.Bounds = new RectangleF(new PointF(5, 5), new SizeF(100, 100));
This works great for my local user account after installing a suitable certificate using MMC (adding the Certificates snap-in for My user account and storing it in Personal), but not for a service (choosing Service account this time, and picking my service). Running the same code results in no suitable certificate being found, i.e. certificate is null. Furthermore, PdfCertificate.GetCertificates() throws an AccessViolationException, which I assume is a bug on Syncfusion's end.
I can, however, reproduce the same problem without Syncfusion code:
var store = new System.Security.Cryptography.X509Certificates.X509Store("My");
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
foreach (var item in store.Certificates)
{
…
}
Run as my own user, the certificate shows up (as do all the others shown in MMC under Personal), but if I debug the service (by running it, then invoking System.Diagnostics.Debugger.Launch()), I only get a "CN=LOCAL SERVICE" certificate, which doesn't show up in MMC at all.
I'm assuming that I need to A) tell it to open the correct certificate store, or B) change something about the way the service is installed or run, such as giving it a different identity, enabling UserInteraction, etc. Currently, it runs using LocalService and with UserInteraction disabled.
From what I remember, Windows machine accounts (like LocalService) use the machine certificate store. This means that in your code, you have to access the store with StoreLocation.LocalMachine.
var store =
new System.Security.Cryptography.X509Certificates.X509Store(StoreLocation.LocalMachine);
Note that if you decide to run the service under specific identity, you should rather first login as the identity, then import the certificate to the Personal store and then, use StoreLocation.CurrentUser.
The answer appears to be that .NET doesn't support accessing service account certificate stores without P/Invoke or the like:
I don't think that any of the .NET APIs allow access to the Services Certificate store.
However, you can install the certificate into the CurrentUser store of the account that the service runs under.
I've changed the service to run under its own user (which doesn't need admin rights), ran mmc.exe as that user using runas, and imported the certificate to that user's personal store.
I ran into this problem, and to solve it had to allow the "Local Service" account to access the "Local Computer" certificate store using the tool "WinHttpCertCfg"
It is described in detail here:
https://support.microsoft.com/en-us/help/901183/how-to-call-a-web-service-by-using-a-client-certificate-for-authentication-in-an-asp-net-web-application
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).