If you’ve ever had an experience with Credential Providers in the past, you may think the title of this post is insane — it’s not.

I recently came across an interesting Github project that showed it was theoretically possible. I wanted to see if I could replicate the results myself without digging too deeply into how they did it. As it happens, I could.

I created a sample project that outlines how you can do it on Github: CredProvider.NET

.NET Credential Provider

Historically, Credential Providers have been complex components to write because they’re COM-based, and because they have weird restrictions you have to contend with since they run in the winlogon UI process. Both of these restrictions should make any sane developer run away — doubly so if you’re a .NET developer. However, if you’ve been around long enough, you may remember that COM-interop thing that makes it possible to call .NET code from COM components (and conversely, call COM components from .NET) and have the runtime do a whole host of evil to keep the two relatively stable.

The trick with interop is getting the interfaces right. Everything communicates through interfaces — it’s the only way to guarantee a stable contract between APIs. This is the crux of our implementation. You can codify them manually through MSDN definitions, but that’s error prone and not necessarily completely accurate. Thankfully, the Windows SDK contains an IDL file that defines the interfaces we need. All we need to do is convert them to something .NET understands. We do this in a two step process where we first compile the IDL to a type library, and then convert the type library to a .NET assembly.

You can compile a type library using the midl.exe tool. You’ll need to install the Windows SDK first though, otherwise you won’t have the IDL file.

midl "C:\Program Files (x86)\Windows Kits\10\Include\10.0.16299.0\um\credentialprovider.idl" 
-target NT62 /x64

You can see I’m using the latest 10.0.16299.0 build of the SDK. This particular version requires you explicitly target a version for some reason — older versions of this IDL don’t require the targeting.

This produces a few files, but most notably is the credentialprovider.tlb file. This is the type library. We need to convert this to an assembly. Normally, you’d just use tlbimp.exe, but it’s notoriously opinionated on how you talk to COM, leaving you stuck in a few ways (for instance, it expects you to throw exceptions instead of returning HRESULTs, despite the interface requiring you to return HRESULTs). It turns out the CLR Interop team built a set of tools to help you through this pain — specifically tlbimp2.exe (why they didn’t just ship this capability in the original tool is a mystery).

TlbImp2.exe credentialprovider.tlb /out:CredProvider.NET.Interop.dll 
/unsafe /verbose /preservesig namespace:CredProvider.NET

The key is the /preservesig parameter.

Once you’ve created the assembly, you can import it into a project and start creating your credential provider.

Credential Providers are made up of two core interfaces:

  • ICredentialProvider — Manages the interaction between the UI
  • ICredentialProviderCredential — tells the UI how to receive credentials from the user

There are a few related interfaces, but they are just there to enhance the capabilities of the credential provider. Once you implement your interfaces you need to decorate the class with some attributes to tell what’s exposed as COM implementations:

You only need to do this on the ICredentialProvider interface.

At this point you may notice a few annoying things. First, not all the interfaces are present in the assembly. A few interfaces were added in the Windows 8.0 timeframe, specifically the ICredentialProviderCredential2 interface, that allows you to follow the newer design guidelines. Second, some of the typedef’s don’t translate correctly. For instance, HBITMAP gets mangled. Fixing these issues requires a little surgery on the credentialprovider.idl file.

First, you need to move the library declaration to the beginning of the file. It’s already present somewhere in the middle of the file (search “library CredentialProviders”). Just move it to the beginning of the file so it includes all the extra interfaces. Second, find and replace any mangled pointer to HANDLE (e.g. HBITMAP* to HANDLE*). Now you’ll need to recompile with midl, and regenerate the library with tlbimp2.

You can see how I did it with this diff.

Unrelated, but you may notice a number of things were taken out. This is because I modified the file without making a backup (doh!), and had to search for the original online and could only find the first release (I suppose I could have just reinstalled the SDK, but that’s beside the point). Anyway, notice how there’s an ICredentialProviderCredential3? I suspect that’s how Windows Hello does the eye-of-Sauron graphic when it’s looking for you.

Windows Hello

You may also want to explicitly register for COM interop on build too. This will make the provider available on each build, but the downside is that it requires running Visual Studio as admin.

Register for COM interop

You need to tell Windows you’ve got a Credential Provider once it’s registered. You do this by adding it to the registry.

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{00006d50-0000-0000-b090-00006b0b0000}]
@=”Credential Provider.NET”

Notice that the GUID matches the Guid in the class attribute. This is intentional. This tells Windows that the COM component with that given guid is a Credential Provider, and it should new-up an instance of it.

The other thing you’ll want to figure out is how to test this thing. You’ll notice all the images above show it being used within a user session, prompted via CredUIPromptForWindowsCredentials. This is intended because testing it on your own machine’s winlogon interface is an exercise in madness. Unhandled errors that cause your provider to crash will render the logonUI unusable and, well, you’re screwed. You have to boot into safe mode and remove the provider registration, but the provider is still loaded so you can’t log into safe mode! So you need to boot into recovery mode, and… — have you made a recovery image? Using CredUIPromptForWindowsCredentials will let you test your code easily and quickly, and it generally behaves like winlogon. Lastly, test it on a VM once it’s ready for more in depth testing.

And that’s all it takes to start writing a Credential Provider in .NET.

Some Notes

It’s worth noting that this is possibly (probably?) a bad idea. I don’t know how stable this will be in the wild.

Some References

I came across this Stack Overflow post in my attempts to figure out the ICredentialProviderCredential2 problem, which lead to this sample. Their recommendation to use tlbimp2 was a huge revelation.

22 Comments

  1. John Moline

    Steve,

    Thank you for the article. I am also exploring how to create custom credential providers for Windows 7 and 10. Question for you, please: How can I create a custom credentials ui that I can invoke when one of the tile buttons is clicked at the login screen? Any ideas? Thanks ahead of time.

    Reply
    • What sort of UI do you mean? Clicking the tile raises the advise event, which you can use to trigger UI changes like changing fields. Alternatively, if that doesn’t get you enough customization, you can always pop up a Window of some sort that renders whatever you want. Though I don’t recommend doing that, because it breaks design guidelines.

      Reply
      • John Moline

        Steve, thanks for the quick response.

        So in your demo, you appear to be calling one of the prompts that comes from credui.dll. I do know that you can change the text in these features, etc. This is totally clear to me.

        What I am thinking about is this: With my custom credential provider installed, it will invoke a custom ui “pop-up” or dialog instead of the prompt within credui.dll. Can I build this dialog in .NET and invoke it from the credential provider when the user is not logged in yet? Does a .NET app have this capability to exist outside of the login screen? If not, what options do you think I should try.

        Thanks again for the advice.

        Reply
        • Ah, I think I understand. You can certainly pop up a custom UI from the login screen. I fiddled with doing this using a basic .NET Forms Window class, so presumably you can use pretty much whatever you want. You can also launch a custom app as well, but then you have to communicate back to the credential provider that you have credentials.

          You also have to be careful because the logon UI often ends up as the topmost window, so you need to anchor it properly otherwise you risk your window getting stuck behind.

          Reply
          • John Moline

            Thanks Steve. Is it possible if you can point me in the direction on how the credential provider tile will launch the custom UI? I don’t see any documentation on how to do this today?

          • John Moline

            Steve, do you have any feedback from the question I mentioned from last Friday? Is there something specific that I need to do to launch an EXE from outside of the login screen? If the session was already logged in, I understand how to launch a new process but this is different. Any ideas or pointers on what I would need to do to launch this custom UI from the credential provider tile? Again, I do appreciate any feedback. Thanks.

          • Programmatically it’s the same via CreateProcess() or the .NET Process.Start() call. Otherwise you can’t really start a process any other way from within the logon UI.

            The environment is fairly similar to a normal session, with the caveat that you’re running as system, so you can do lots of dangerous things.

          • John Moline

            Ok, understood. I appreciate you following up. Thank you for the information.

  2. Steve, thank your for this article. I am working on a credential-provider in C# and I suspect that a parameter in a method should be of an ‘out’-parameter but it isn’t in it’s interface.
    I’m talking about the GetBitmap() method that, as I understand, is called to get a IntPtr to an Bitmap-image. This IntPtr should be an out-parameter, I think. Please have look at my question: https://stackoverflow.com/questions/47835715/what-is-the-c-sharp-equivalent-of-this-c-code-with-inptr

    I’m working with CredentialProvider.Interop.dll created by someone else but I just recreated it with your steps. When I browse the classes in the .DLL I can clearly see that the parameter is not an ‘out’-parameter. Is it possible that this parameter is created wrong in the above steps?
    Thanks for your help!
    Robert

    Reply
      • Thanks for your quick reply!
        We are building it on Windows 10, version with these commands (and others of course):

        midl “C:\Program Files (x86)\Windows Kits\10\Include\10.0.16299.0\um\credentialprovider.idl” -target NT62 /x64

        C:\code54\Code54\Interop\TlbImp2.exe “C:\Users\testa\source\credentialprovider.tlb” /out:”C:\Users\testa\source\CredProvider.NET.Interop.dll” /unsafe /verbose /preservesig namespace:CredProvider.NET

        Maybe a stupid question but is there a way to just download the DLL with the correct out-parameter?
        Thanks a lot!
        Robert

        Reply
          • Great Steve,

            I really appreciate this and I’m going to test if this solves the issue with the out-parameter.

            Best,
            Robert

        • Interesting! I tried going down that route but never got it working safely. I’ll have to give this a try!

          Reply
  3. Max

    Hi Steve,

    first of all thank you for this great tutorial.
    Somehow when i run the project only the default windows login (user, password) is displayed.
    Do you have an idea what i did wrong? (The registry entry is existing)

    Best regards
    Max

    Reply
    • A couple things to check out…

      1. Did the assembly get registered correctly? The checkbox needs to be set in the project, and you need to be running as admin for it to register. You may have to run a full solution rebuild for it to take effect. Alternatively you need to register it manually using something like regasm.exe.

      2. Are you on a 32 bit build on Windows? This is currently targeted at 64 bit only. You can probably rebuild as 32 bit without issue, but it’s not been tested.

      3. This is based on a Windows 10 IDL, so you might have trouble on older builds of Windows, like 7 or 8. You could potentially try recreating the dependent assemblies using an older SDK.

      4. Are you seeing any output in the console window at all? If nothing at all, that would indicate the classes never got instantiated. If you ARE seeing output, try forcing the cred UI to redraw by moving it around on screen, collapsing the window, etc.

      Reply
      • Max

        1.) Yes the checkbox is set.
        2.) Im on 64bit
        3.) Im using Win 10
        4.) The console out put is:
        18-01-15 12:24:42Z [CredentialProvider.Initialize] cpus: CPUS_CREDUI; dwFlags: 0
        2018-01-15 12:24:42Z [CredentialProviderBase.SetUserArray] count: 0; options: CPAO_EMPTY_LOCAL
        2018-01-15 12:24:42Z [CredentialProviderBase.Advise] upAdviseContext: 1
        2018-01-15 12:24:42Z [CredentialProviderBase.GetCredentialCount]
        2018-01-15 12:24:42Z [CredentialProviderBase.GetCredentialAt] dwIndex: 0
        2018-01-15 12:24:42Z [CredentialView.GetField] dwIndex: 0; descriptors: 4
        2018-01-15 12:24:42Z [CredentialView.GetField] dwIndex: 1; descriptors: 4
        2018-01-15 12:24:42Z [CredentialView.GetField] dwIndex: 2; descriptors: 4
        2018-01-15 12:24:42Z [CredentialView.GetField] dwIndex: 3; descriptors: 4
        2018-01-15 12:24:42Z [CredentialProviderCredential.GetBitmapValue] dwFieldID: 0
        2018-01-15 12:24:42Z [CredentialProviderCredential.GetFieldState] dwFieldID: 0
        2018-01-15 12:24:42Z [CredentialProviderCredential.GetStringValue] dwFieldID: 1
        2018-01-15 12:24:42Z [CredentialProviderCredential.GetFieldState] dwFieldID: 1

        Also moving the window didn’t helped. Resizing is not possible.

        We had changed requirements in our project so i stopped trying further. But if i have time i will try it later with an older SDK.

        Thanks a lot for your help.

        Reply
        • Hmm. It’s loading the provider into Cred UI as part of the Initialize() call. Can you confirm it’s not showing up in the collapsed More Choices list below the selected provider?

          Otherwise, I vaguely remember having a similar issue once, but I don’t recall the voodoo that fixed it other than trying to force a redraw. I don’t think it was a coding change.

          I don’t think rebuilding against an older SDK will be necessary in this case because it’s loading the provider, and all we need the SDK for is getting the interfaces right.

          It looks like the log you posted is cut off after GetFieldState for id 1. Is this just a subset of the log, or is that the last thing that shows up in the console? It *should* have a bunch of stuff after that, which indicates it might be breaking while trying to load the password field, which is dwFieldID 2.

          2018-01-15 17:58:22Z [CredentialProvider.Initialize] cpus: CPUS_CREDUI; dwFlags: 0
          2018-01-15 17:58:22Z [CredentialProviderBase.SetUserArray] count: 0; options: CPAO_EMPTY_LOCAL
          2018-01-15 17:58:22Z [CredentialProviderBase.Advise] upAdviseContext: 1
          2018-01-15 17:58:22Z [CredentialProviderBase.GetCredentialCount]
          2018-01-15 17:58:22Z [CredentialProviderBase.GetCredentialAt] dwIndex: 0
          2018-01-15 17:58:22Z [CredentialView.GetField] dwIndex: 0; descriptors: 4
          2018-01-15 17:58:22Z [CredentialView.GetField] dwIndex: 1; descriptors: 4
          2018-01-15 17:58:22Z [CredentialView.GetField] dwIndex: 2; descriptors: 4
          2018-01-15 17:58:22Z [CredentialView.GetField] dwIndex: 3; descriptors: 4
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetBitmapValue] dwFieldID: 0
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetFieldState] dwFieldID: 0
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetStringValue] dwFieldID: 1
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetFieldState] dwFieldID: 1
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetStringValue] dwFieldID: 2
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetFieldState] dwFieldID: 2
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetStringValue] dwFieldID: 3
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetFieldState] dwFieldID: 3
          2018-01-15 17:58:22Z [CredentialProviderCredential.GetUserSid] Identity: …snip…
          2018-01-15 17:58:22Z [CredentialProviderCredential.Advise]
          2018-01-15 17:58:22Z [CredentialProviderCredential.Advise] pcpce is ICredentialProviderCredentialEvents2
          2018-01-15 17:58:23Z [CredentialProviderCredential.SetSelected]
          2018-01-15 17:58:23Z [CredentialProviderCredential.GetSerialization]
          2018-01-15 17:58:23Z [CredentialProviderCredential.SetDeselected]

          One annoying thing about how this is coded is that these calls don’t do a good job of handling errors. The logger doesn’t always pick them up, so it could be failing and just silently moving on to the next provider. Also, debugging is kind of a pain so you might try sticking log lines into various spots to narrow down where it might be failing.

          Reply
  4. Catana Simona

    Hi Steve,

    Great article! I managed to make a credential provider C# which partially suits my needs. However, I bumped into a problem. I want
    to display an image at a certain point, by using the method SetFieldBitmap. The problem is that the third parameter expected by the
    method should be of type _userHBITMAP

    namespace CredProvider.NET.Interop2
    {
    [SuppressUnmanagedCodeSecurity]
    public struct _userHBITMAP
    {
    public int fContext;
    public __MIDL_IWinTypes_0007 u;
    }
    }

    Method signature:
    int SetFieldBitmap(ICredentialProviderCredential pcpc, uint dwFieldID, [ComAliasName(“CredProvider.NET.Interop2.wireHBITMAP”)] ref _userHBITMAP hbmp);

    I got the IntPtr from the Bitmap, and then I tried to obtain the _userHBITMAP but without success (I get an AccessViolationException). Please see the following code:
    var hbitmap = bitmat.GetHbitmap();
    var finalBitmap = (_userHBITMAP)Marshal.PtrToStructure(intt, typeof(_userHBITMAP));
    pcpce.SetFieldBitmap(this, (uint)FIELD_ID.FI_TILEIMAGE, finalBitmap);

    GetBitmapValue works fine because it expects an IntPtr.

    I am totally lost… Could you please give me a suggestion regarding how this should be done?

    Thank you very much,
    Simona

    Reply
    • Catana Simona

      Hi,
      I have managed to resolve the issue by modifying the .idl file: changed the third parameter of SetFieldBitmap from HBITMAP to HANDLE, then created the tlb file and then the assembly.
      By doing that, I was able to use the variable hbitmap, obtained from
      var hbitmap = bitmap.GetHbitmap();
      as a parameter for the SetFieldBitmap method.

      Best regards,
      Simona

      Reply
      • Yep, this is the correct solution. You fixed it before I even had a chance to see your first comment! 🙂

        Reply

Join the conversation