# External Client Certificate and Key Providers

## Introduction

This API provides the ability of products to configure SSL/TLS Client Authentication certificates and the associated Private Key for a given site, or based on the server's list of desired certificate issuer's.

Currently the API is not able to support password protected Private Keys, only pre-enabled keys. The API will later be expanded to support Smartcards, including those protected by login or PIN-codes.

Historical note: This functionality was previously designed primarily as a Smartcard provider, but have been renamed and refurbished to provide more general functionality. At the time of writing some of that code is still present, but disabled using #ifdefs, as it has not been updated to new functionality.

## Design

The libssl module object contain the member LibSSL::m_external_clientkey_manager, which is a list of all registred providers. When a SSL/TLS server requests a Client Certificate each provider in this list is asked to provide certificates and keys that are suitable for the server, based on its name and/or the list of authorities the server specified in its request.

The providers are based on the class SSL_KeyManager, defined in "modules/libssl/smartcard/smc_man.h", and this class provides the APIs necessary for the operation of the implementation's provider.

### SSL_KeyManager

GetAvailableKeys

  Asks the provider to select and add the most suitable certificate and key pairs it have available for the named server and/or the requested certificate issuers. Note that a provider does not need to check either of these, though it is recommended. The implementation should create a certificate list, and configure a SSL_PublicKeyCipher derived object (can be requested from g_ssl_api->CreatePublicKeyCipher()) to use a RSA private key (actual loading is done using the PublicKeyCipher::LoadAllKeys() API), then call the function SSL_KeyManager::SetupCertificateEntry to add the certificate.

  It is possible for the implementation to configure the SSL_PublicKeyCipher object to be used automatically, using the SSL_PublicKeyCipher::Set_UseAutomatically API. NOTE: This option should be used cautiosly, since it disables the UI query to the user, and may reveal sensitive privacy-related information about the user. It should only be used against specific sites, not general sites.

SetupCertificateEntry

  Utility function that creates a key and certificate item and adds it to the list of such items. The function takes a certificate varvector list, a fully configured public key, and name information as arguments, then creates the item. The item takes ownership of the public key object.

AllowedCertificate

  Utility function that allows the implementation to check if a issuer name (as a varvector) is in the list of issuer names provided by the server.

Register

  Registers the provider in the module's list of providers.

Unregister

  Removes the provider from the module's list of providers. This is automatic when the object is destroyed.

## OpenSSL based pseudo code implementation

    #include "modules/url/url_sn.h"
    #include "modules/libssl/handshake/asn1certlist.h"
    #include "modules/libssl/smartcard/smc_man.h"
    #include "modules/libssl/methods/sslpubkey.h"

    class MyCertAndKeyProvider : public SSL_KeyManager
    {
    	virtual OP_STATUS	GetAvailableKeys(SSL_CertificateHandler_ListHead *cipherlist,  SSL_DistinguishedName_list &ca_names, ServerName *sn, uint16 port)
    	{
    		OP_ASSERT(cipherlist)
    		OP_ASSERT(sn);

    		// Check hostname is correct ( can also be replaced with if(!AllowedCertificate(ca_names, MyCertificateIssuer))
    		if(sn->GetName().CompareI("myserver.example.com") != 0) || port != MyServerPort)
    			return OpStatus::OK; //

    		SSL_ASN1Cert_list certificate;

    		// Set the number of certificates in the chain
    		certificate.Resize(2);
    		RETURN_IF_ERROR(certificate.GetOPStatus()); // Return if there was an error
    		// Set the client certificate
    		certificate[0].Set(MY_CLIENT_CERTIFICATE, ARRAY_SIZE(MY_CLIENT_CERTIFICATE));
    		// Set the client certificate's issuer's certificate
    		certificate[1].Set(MY_CLIENT_CERTIFICATE_ISSUER, ARRAY_SIZE(MY_CLIENT_CERTIFICATE_ISSUER));
    		RETURN_IF_ERROR(certificate.GetOPStatus());

    		AutoPtr<SSL_PublicKeyCipher>  key(g_ssl_api->CreatePublicKeyCipher(SSL_RSA);
    		// Create an RSA Key

    		RETURN_OOM_IF_NULL(key.get()); // If allocation failed, return

    		SSL_varvector32 private_key;

    		// !!THIS IS NOT HOW YOU SHOULD DO IT IN REAL CODE!! IT SHOULD BE PASSWORD PROTECTED !!
    		private_key.Set(MY_PRIVATE_KEY, ARRAY_SIZE(MY_PRIVATE_KEY)); // set the private key, encoded in the PKCS format for the method (in this case RSA)
    		RETURN_IF_ERROR(private_key.GetOPStatus());

    		key->LoadAllKeys(private_key); // Load the private key data into the key handler
    		RETURN_IF_ERROR(key->GetOPStatus());

    		// Add the certificate and key to the list of available certificates, then return the status.
    		// The key is released to the function, which will delete it if there is a failure
    		return SetupCertificateEntry(cipherlist, UNI_L("My Key"), UNI_L("My Key"), certificate, key.release())
    	}
    };

    // Construct the provider object
    MyCertAndKeyProvider provider;

    provider.Register(); // Register it

    provider.Unregister(); // Unregister it