-1

I have a C++ implementation for

  1. searching certificates base on issuer

  2. validate those certificates

  3. if there are more than one certificate

    a. set the certificates in own store

    b. show "Choose Certificate" window using CryptUIDlgSelectCertificateFromStore

  4. return a specific value from the certificate

This is done in a Windows MFC program. Now I have to do this in a Command-Line program. I was hoping to use C# for this because it is easier to develop and you can use NUGET packages.

Is there a good and "official" C# wrapper for cryptuiapi.h and wincrypt.h? I found these:

https://referencesource.microsoft.com/#system/security/system/security/cryptography/cryptoapi.cs https://referencesource.microsoft.com/#WsatUI/MMCUI/MMCSafeNativeMethods.cs,95a3d8346740ba05

...but I do not like to copy code if there is better implementation (=NUGET) somewhere.

The functions that I use are:

CertOpenStore
CertCloseStore
CertEnumCertificatesInStore
CryptUIDlgSelectCertificateFromStore
CertVerifyRevocation
CertVerifyTimeValidity
CryptAcquireCertificatePrivateKey
CryptSignCertificate
CertFindCertificateInStore
CertGetIntendedKeyUsage
CertNameToStr
CertDuplicateCertificateContext
CertAddCertificateContextToStore

As I see I have 4 choices:

  1. Implement whole thing using c# X509Store, but I have not found a CryptUIDlgSelectCertificateFromStore for c#. I can use pinvoke but I think then I should use pinvoke for all functions.
  2. Implement whole thing in unmanaged C++ and use already existing code. The main project that I have is BIG and OLD so it is complicated to extract only the useful functions.
  3. Implement console app in C# referencing a C++ CLR Wrapper which in its turn is referencing a C++ dll with unmanaged code.
  4. Implement in C# and use the above pinvoke functions which can be PIA because UNICODE/ASCII problem.
Lucian
  • 3,407
  • 2
  • 21
  • 18
  • 1
    "*I can use pinvoke but I think then I should use pinvoke for all functions.*" -- I disagree. My preferred approach would be to use X509Store where possible, but if there's a single function which you can't access that way, pinvoke it directly. `X509Store.StoreHandle` is exposed so that you can do just that. – canton7 Dec 09 '20 at 13:09
  • Ohh, Thanks! That's what I will do. – Lucian Dec 09 '20 at 13:13
  • (and `X509Certificate2` has a constructor which takes a `PCCERT_CONTEXT`, for going the other way) – canton7 Dec 09 '20 at 13:14
  • nice: I discovered this https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2ui.selectfromcollection?view=dotnet-plat-ext-5.0#System_Security_Cryptography_X509Certificates_X509Certificate2UI_SelectFromCollection_System_Security_Cryptography_X509Certificates_X509Certificate2Collection_System_String_System_String_System_Security_Cryptography_X509Certificates_X509SelectionFlag_ – Lucian Dec 09 '20 at 14:59

1 Answers1

0

To keep somebody else of loosing time this is what I found:

you can use

using System.Security.Cryptography.X509Certificates;

...

public X509Certificate2 Authenticate()
    {

        X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        store.Open(OpenFlags.OpenExistingOnly);
        X509Store storeToShow = new X509Store("teststore", StoreLocation.CurrentUser);
        storeToShow.Open(OpenFlags.ReadWrite);
        foreach (var certificate in store.Certificates)
        {
            VerboseLevel_2($"Examining {certificate.ToUsefulName()}");
            if(!certificate.CanKeyDoKeyExchange())
            {
                VerboseLevel_2($"No digintal key usage: {certificate.ToUsefulName()}");
                continue;
            }
            if (!AcceptedIssuerNames.Any(a => certificate.IssuerName.Name.Contains(a)))
            {
                VerboseLevel_2($"Not present in accepted issuers: {certificate.ToUsefulName()}");
                continue;
            }
            VerboseLevel_1($"Found {certificate.ToUsefulName()}");
            storeToShow.Add(certificate);
        }
        X509Certificate2 choosenCert = null;
        if (storeToShow.Certificates.Count > 2)
        {
            X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(storeToShow.Certificates, 
                "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.SingleSelection);
            choosenCert = scollection[0];
        }

        //https://stackoverflow.com/questions/53369841/c-sharp-x509certificate2-verify-without-revocation-test
        if (choosenCert != null && choosenCert.Verify())
        {
            return choosenCert;
        }
        return null;
    }
Lucian
  • 3,407
  • 2
  • 21
  • 18