Overview

Signing keys and certificates are managed in FusionAuth using Key Master. After creating or importing a key, use it with other FusionAuth functionality, such as signing JSON Web Tokens or SAML Requests. This page describes the admin UI for creating and managing signing keys.

You can also manage keys via the Keys API. You may also be interested in rotating your keys.

Create or Manage Keys

Navigate to Settings -> Key Master. Here you will see a list of keys and certificates.

The default Key Master listing.

From this page, you can add and import keys and certificates as well as view and remove keys. There are certain default keys that you cannot remove. See the FusionAuth limitations for more.

Select the operation from the menu on the listing page.

Finding Keys

If you have a large number of tenants, applications and signing keys, you may have difficulty keeping track of them. While there is a key search API, it can also be helpful to use the client libraries to retrieve these.

Here’s sample code for retrieving all the keys that have been assigned to a tenant or application. You can learn more about this community contributed script here.

using io.fusionauth;
using io.fusionauth.converters.helpers;
using io.fusionauth.domain;
using io.fusionauth.domain.provider;
using System.Text;

namespace DumpCertificatesInFusionAuthInstance
{
    class DumpCertificateUsage
    {

        static void Main()
        {
                string url = "YOUR_URL";
                string apiKey = "YOUR_API_KEY";
                TextWriter textWriter = Console.Out;
                DumpCertificateUsage dcu = new DumpCertificateUsage(url, apiKey, textWriter);
                dcu.DumpAllCertificates();
        }

        private readonly string _url;
        private readonly string _apiKey;
        private readonly FusionAuthSyncClient _client;
        private readonly Dictionary<Guid, Key> _keys;
        private readonly TextWriter _writer;

        public DumpCertificateUsage(string url, string apiKey, TextWriter textWriter) 
        { 
            _url = url;
            _apiKey = apiKey;
            _client = new FusionAuthSyncClient(apiKey, url);
            _keys = new Dictionary<Guid, Key>();
            _writer = textWriter;
        }

        private static string DictionaryOfErrorDataToString(IDictionary<string, object> errorData)
        {
            if (errorData == null) return string.Empty;
            var errorMsg = new StringBuilder();
            foreach (var key in errorData.Keys)
            {
                errorMsg.AppendLine($"{key}={errorData[key]}");
            }

            return errorMsg.ToString();
        }

        private static void ThrowExceptionFromClientResponse<T>(ClientResponse<T> response, string message)
        {
            if (!response.WasSuccessful())
            {
                if (response.exception != null) throw new Exception($"failed: {message}", response.exception);

                if (response.statusCode == 404) throw new Exception($"failed: {message}. Not Found");
                if (response.statusCode == 401) throw new Exception($"failed: {message}. Not Authorized");

                var msgBuilder = new StringBuilder(message);
                foreach (var general in response.errorResponse.generalErrors)
                {
                    msgBuilder.AppendLine($"{general.message}: ({general.code}) {DictionaryOfErrorDataToString(general.data)}");
                }
                foreach (var fieldData in response.errorResponse.fieldErrors)
                {
                    msgBuilder.Append($"{fieldData.Key} ");
                    foreach (var fieldError in fieldData.Value)
                    {
                        msgBuilder.AppendLine($"{fieldError.message}: ({fieldError.code}) {DictionaryOfErrorDataToString(fieldError.data)}");
                    }
                }

                throw new Exception(msgBuilder.ToString());
            }
        }

        public void DumpApplication(FusionAuthSyncClient tenantClient, Application application)
        {
            _writer.WriteLine($"  Application: {application.name}");
            if (application.jwtConfiguration != null)
            {
                if (application.jwtConfiguration.accessTokenKeyId != null)
                {
                    if (_keys.TryGetValue(application.jwtConfiguration.accessTokenKeyId.Value, out Key? key) && key != null)
                    {
                        _writer.WriteLine($"    jwtConfiguration.accessTokenKeyId: {key.name} ({key.issuer})");
                    }
                }
                if (application.jwtConfiguration.idTokenKeyId != null)
                {
                    if (_keys.TryGetValue(application.jwtConfiguration.idTokenKeyId.Value, out Key? key) && key != null)
                    {
                        _writer.WriteLine($"    jwtConfiguration.idTokenKeyId: {key.name} ({key.issuer})");
                    }
                }
            }
            if (application.samlv2Configuration != null) {
                if (application.samlv2Configuration.defaultVerificationKeyId != null)
                {
                    if (_keys.TryGetValue(application.samlv2Configuration.defaultVerificationKeyId.Value, out Key? key) && key != null)
                    {
                        _writer.WriteLine($"    samlv2Configuration.defaultVerificationKeyId: {key.name} ({key.issuer})");
                    }
                }
            }
        }

        public void DumpApplications(FusionAuthSyncClient tenantClient, IEnumerable<Application> applications)
        {
            foreach(var application in applications)
            {
                DumpApplication(tenantClient, application);
            }
        }

        public void DumpIdentityProvider(FusionAuthSyncClient tenantClient, IdentityProvider identityProvider)
        {
            var idp = identityProvider as SAMLv2IdPInitiatedIdentityProvider;
            if (idp != null)
            {
                if(idp.keyId != null)
                {
                    if (_keys.TryGetValue(idp.keyId.Value, out Key? key) && key != null)
                    {
                        _writer.WriteLine($"    {idp.name} idp.keyId: {key.name} ({key.issuer})");
                    }
                }
            }
        }

        public void DumpIdentityProviders(FusionAuthSyncClient tenantClient, IEnumerable<IdentityProvider> identityProviders)
        {
            foreach (var identityProvider in identityProviders)
            {
                DumpIdentityProvider(tenantClient, identityProvider);
            }
        }

        public void DumpTenant(Tenant tenant)
        {
            _writer.WriteLine($"Tenant: {tenant.name}");

            if(tenant.jwtConfiguration != null)
            {
                if (tenant.jwtConfiguration.accessTokenKeyId != null)
                {
                    if (_keys.TryGetValue(tenant.jwtConfiguration.accessTokenKeyId.Value, out Key? key) && key != null)
                    {
                        _writer.WriteLine($"  jwtConfiguration.accessTokenKeyId: {key.name} ({key.issuer})");
                    }
                }
                if (tenant.jwtConfiguration.idTokenKeyId != null)
                {
                    if (_keys.TryGetValue(tenant.jwtConfiguration.idTokenKeyId.Value, out Key? key) && key != null)
                    {
                        _writer.WriteLine($"  jwtConfiguration.idTokenKeyId: {key.name} ({key.issuer})");
                    }
                }
            }

            // applications
            var tenantClient = new FusionAuthSyncClient(_apiKey, _url, tenant.id.ToString());
            var applicationResponse = tenantClient.RetrieveApplications();
            if (applicationResponse != null)
            {
                ThrowExceptionFromClientResponse(applicationResponse, $"getting all applications for {tenant.name}");
                if (applicationResponse.successResponse != null)
                {
                    DumpApplications(tenantClient, applicationResponse.successResponse.applications);
                }
            }

            // identityProviders
            var idpResponse = tenantClient.RetrieveIdentityProviders();
            if (idpResponse != null)
            {
                ThrowExceptionFromClientResponse(idpResponse, $"getting all identityproviders for {tenant.name}");
                if (idpResponse.successResponse != null)
                {
                    DumpIdentityProviders(tenantClient, idpResponse.successResponse.identityProviders);
                }
            }
        }

        public void DumpTenants(IEnumerable<Tenant> tenants)
        {
            foreach (var tenant in tenants)
            {
                DumpTenant(tenant);
            }
        }

        public void DumpAllCertificates()
        {
            var keysResponse = _client.RetrieveKeys();
            ThrowExceptionFromClientResponse(keysResponse, "getting all keys");
            foreach (var key in keysResponse.successResponse.keys)
            {
                if (key.id.HasValue)
                {
                    _keys.Add(key.id.Value, key);
                }
            }

            var tenantResponse = _client.RetrieveTenants();
            ThrowExceptionFromClientResponse(tenantResponse, "getting all tenants");
            DumpTenants(tenantResponse.successResponse.tenants);
        }
    }

}

Importing vs Generating

You can import keys and certificates. This is useful if you are integrating with an external system. For instance, you may be migrating from another auth system and want to import keys generated by that auth system to ensure anything signed with those keys will continue to work.

You can generate keys as well. This is useful when FusionAuth is your system of record for such keys.

Import RSA Key Pair

Import a RSA key pair.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Key Identifier
The Key identifier. This is used in JWT metadata and, if applicable, the JWKS endpoint, as the `kid` value. When this value is omitted, one will be generated.
Algorithmrequired
The particular RSA algorithm used to generate the Key.
Public keyrequired
The PEM encoded public key to import.
Private key
The PEM encoded private key to import. If the key is to be used for token validation only, this field may be omitted as only a public key is necessary.

Import RSA Private Key

Import a RSA private key.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Key Identifier
The Key identifier. This is used in JWT metadata and, if applicable, the JWKS endpoint, as the `kid` value. When this value is omitted, one will be generated.
Algorithmrequired
The particular RSA algorithm used to generate the Key.
Private keyrequired
The PEM encoded private key to import.

Import Elliptic Curve Key Pair

Import an ECC key pair.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Key Identifier
The Key identifier. This is used in JWT metadata and, if applicable, the JWKS endpoint, as the `kid` value. When this value is omitted, one will be generated.
Public keyrequired
The PEM encoded public key to import.
Private key
The PEM encoded private key to import. If the key is to be used for token validation only, this field may be omitted as only a public key is necessary.

Import Elliptic Curve Private Key

Import an ECC private key.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Key Identifier
The Key identifier. This is used in JWT metadata and, if applicable, the JWKS endpoint, as the `kid` value. When this value is omitted, one will be generated.
Private keyrequired
The PEM encoded private key to import.

Import HMAC Secret

Import an HMAC secret.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Key Identifier
The Key identifier. This is used in JWT metadata and, if applicable, the JWKS endpoint, as the `kid` value. When this value is omitted, one will be generated.
Algorithmrequired
The particular HMAC algorithm used to generate the Key.
Secretrequired
The HMAC secret to import.

Import Public Key

Import a public key.

The type of the Key will be inferred from the PEM encoded value.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Key Identifier
The Key identifier. This is used in JWT metadata and, if applicable, the JWKS endpoint, as the `kid` value. When this value is omitted, one will be generated.
Public keyrequired
The PEM encoded public key to import.

Import Certificate

Import a certificate.

The public key will be extracted from the certificate.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Key Identifier
The Key identifier. This is used in JWT metadata and, if applicable, the JWKS endpoint, as the `kid` value. When this value is omitted, one will be generated.
Certificaterequired
The PEM encoded certificate to import.

Generate RSA Key Pair

Generate a RSA key pair.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Issuer
This name will be used as the CN issuer and subject of the certificate and it cannot be modified once created. This is an optional parameter and if omitted a default issuer will be used.
Algorithmrequired
The particular RSA algorithm used to generate the Key.
Key lengthrequired
The length of the Key.

Generate Elliptic Key Pair

Generate an ECC key pair.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Issuer
This name will be used as the CN issuer and subject of the certificate and it cannot be modified once created. This is an optional parameter and if omitted a default issuer will be used.
Algorithmrequired
The particular ECC algorithm used to generate the Key.

Generate HMAC Secret

Generate an HMAC Secret.

Form Fields

Id

When this value is omitted, a unique Id will be generated automatically.

Namerequired

The name of the Key. This must be unique.

Algorithmrequired
The particular HMAC algorithm used to generate the Key.

Limits On Updating Keys

Only the name of the Key may be changed; all other fields will remain the same. If you need to update a Key with a new certificate, algorithm or other attributes, please Import a Key.

For example, if you have a Key with an associated expiring certificate, you’ll need to follow the steps similar to those outlined in the JWT signing key rotation documentation:

  • Import this key, keypair or certificate into FusionAuth. This will create a new Key entity in FusionAuth.
  • Update the appropriate configuration with this new Key (JWT signing configuration, SAML validation configuration, etc).
  • Once the new Key is configured for use, remove the expired, previous Key.

Note that validation rules will prevent you from removing an in-use key.