swobi
swobi

Reputation: 105

Defining a "ClientCertificateProvider" in FiddlerScript

I want to write a "ClientCertificateProvider" for Fiddler Classic using FiddlerScript.

Converting the FiddlerClientCertPicker C# Extension from @EricLaw to JScript.NET kind of worked, but connecting my code to FiddlerApplication.ClientCertificateProvider does not.

As far as I understand there is a "LocalCertificateSelectionCallback"-Delegate needed.

After struggling with the JScript.NET Syntax for quite a while ;-) here is what I am trying:

import System;
import Fiddler;
import System.Net.Security;
import System.Security.Cryptography;  
import System.Security.Cryptography.X509Certificates;


class Handlers
{
    // *****************
    //
    // This is the Handlers class. Pretty much everything you ever add to FiddlerScript
    // belongs right inside here, or inside one of the already-existing functions below.
    //
    // *****************

[...]          

    
    static function ProvideClientCertificate(oSession: Session, targetHost: String, 
                           localCertificates: X509CertificateCollection, 
                           remoteCertificate: X509Certificate, 
                           acceptableIssuers: String[]): X509Certificate {
        FiddlerObject.log("Client Certificate needed for " + targetHost);
        return (null);
    }

[...]
    static function Main() {
        var today: Date = new Date();
        FiddlerObject.StatusText = " CustomRules.js was loaded at: " + today;

        FiddlerApplication.Prefs.SetBoolPref("fiddler.network.https.clientcertificate.ephemeral.prompt-for-missing", false);

        var certProvider = Delegate.CreateDelegate(LocalCertificateSelectionCallback, typeof(Handlers), "ProvideClientCertificate", false, false); 
        FiddlerApplication.ClientCertificateProvider = LocalCertificateSelectionCallback(certProvider);

This is syntactically correct but the log entry is not shown.

The second argument for CreateDelegate should be "The Type representing the class that implements method." as the CreateDelegate documentation mentions.

The only entry while testing in Fiddler's "Log"-Tab is the expected:

The server [www.testservername.com] requested a client certificate, but no client certificate was available.

Upvotes: 0

Views: 181

Answers (2)

swobi
swobi

Reputation: 105

The problem in my code is, that the signature of the ProvideClientCertificate() function is not correct. Instead of "oSession : Session" the first argument has to be "sender: Object"

The correct syntax is:

    static function ProvideClientCertificate(sender: Object, targetHost: String, 
                           localCertificates: X509CertificateCollection, 
                           remoteCertificate: X509Certificate, 
                           acceptableIssuers: String[]): X509Certificate {
        FiddlerObject.log("Client Certificate needed for " + targetHost);
        return (null);
    } 

And there is no special "LocalCertificateSelectionCallback"-Delegate definition needed. The connection is simply made by:

    FiddlerApplication.ClientCertificateProvider = ProvideClientCertificate;

Now I see the expected result in Fiddler's "Log"-Tab:

Client Certificate needed for https://www.testservername.com 

Upvotes: 1

swobi
swobi

Reputation: 105

Since the FiddlerApplication.ClientCertificateProvider seems to be called in each and every HTTPS Request (not only in those who actually need a client certificate) I ended up with the following solution:

I defined a Custom Button "Default Client Cert" with BindUIButton() and added the following code which lets you choose a client certificate from your local store and then sets it as default client certificate (FiddlerApplication.oDefaultClientCertificate). When you cancel the selection the default client certificate is reset to null

import System;
import Fiddler;
import System.Net.Security;
import System.Security.Cryptography;  
import System.Security.Cryptography.X509Certificates;


class Handlers
{
    // *****************
    //
    // This is the Handlers class. Pretty much everything you ever add to FiddlerScript
    // belongs right inside here, or inside one of the already-existing functions below.
    //
    // *****************

[...]  

    BindUIButton("\uD83D\uDC51 Default Client Cert")
    public static function doDefaultClientCert(arrSess: Session[]) {
        var store: X509Store = new X509Store("My", StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

        var oAllCerts: X509Certificate2Collection = store.Certificates;
        
        var oFilteredCerts : X509Certificate2Collection = oAllCerts.Find(X509FindType.FindByTimeValid,DateTime.Now,false);

        var oClientCerts : X509Certificate2Collection = new X509Certificate2Collection();
        
        for (var i=0; i < oFilteredCerts.Count; i++) {
            var oCandidate = oFilteredCerts[i];
            if (!oCandidate.HasPrivateKey || (null == oCandidate.Extensions)) {
                continue;
            }
            var extensions = oCandidate.Extensions;
            for (var j=0; j < extensions.Count; j++) {
                var oCE = extensions[j];
                if (oCE.Oid.Value == "2.5.29.37") {  // aka "Enhanced Key Usage"
                    var enhancedKeyUsage = X509EnhancedKeyUsageExtension(oCE);
                    if (HasClientAuthenticationFlag(oCandidate, enhancedKeyUsage)) {
                        oClientCerts.Add(oCandidate);
                    }
                }
            }
        } 
        var oPickedCerts : X509Certificate2Collection = X509Certificate2UI.SelectFromCollection(oClientCerts, "Client Certificate",
            "Select a default client certificate", X509SelectionFlag.MultiSelection);
        
        if ((oPickedCerts != null) && (oPickedCerts.Count > 0))
        {
            FiddlerApplication.oDefaultClientCertificate = oPickedCerts[0];
            FiddlerApplication.UI.SetStatusText("Default Client Cert set to " + oPickedCerts[0].Subject);
            FiddlerObject.log("Default Certificate: " + oPickedCerts[0].Subject); 
        }
        else
        {
            FiddlerApplication.oDefaultClientCertificate = null;
            FiddlerApplication.UI.SetStatusText("Default Client Cert reset to NULL");
            FiddlerObject.log("Default Certificate: NULL");
        }

    }
        
    static function HasClientAuthenticationFlag(oCandidate:X509Certificate2, enhancedKeyUsage:X509EnhancedKeyUsageExtension) {
        var CLIENT_AUTH_OID = "1.3.6.1.5.5.7.3.2"; 
        for (var i = 0; i < enhancedKeyUsage.EnhancedKeyUsages.Count; i++) {
            var usageOid = enhancedKeyUsage.EnhancedKeyUsages[i].Value;
            if (usageOid == CLIENT_AUTH_OID) {
                return(true);
                break;
            }
        }
        return(false);
    } 

Thanks again to @EricLaw for this great tool and his FiddlerClientCertPicker C# Extension which I basically converted to FiddlerScript (JScript.NET).

Upvotes: 0

Related Questions