Skip to main content

Public key Pinning Implementation

Managing Dynamic Pinning describes how the configuration can be managed to make use of the Approov dynamic pinning feature. This section provides examples of how the pins may be set in the app at runtime using different languages and HTTP stacks.

Setting Pins

Pins can be set immediately after the Approov SDK has been initialized and any dynamic SDK configuration has been written. At this stage some pins will be available, either through the initial SDK configuration or through dynamic SDK configuration updates that have been received since the app was first installed.

This early access to the pins allows the pinning to be set up even for app development frameworks that require the information very early during the app startup. However, for these pins to actually have any impact in the app it is necessary to add code to communicate the pins to the TLS stack.

The SDK provides a getPins method that can be used by the app to obtain the currently configured set of pins to be used. The pins will be set up immediately after the initialization of the Approov SDK. The getPins method should be called with the parameter public-key-sha256, indicating the pinning type being performed. This specifies that the pinning type is the SHA256 hash of the Subject Public Key Information (SPKI) of the certificate. This is currently the only type of pinning supported. A list of acceptable pins is returned, and the certificate chain presented by the connection to that domain must contain at least one of those pins.

When managed trust roots are enabled, additional pins are provided from getPins under the * domain name. This allows the trusted root set to be provided only once, even if they are to be used for multiple API domains.

This pinning implementation should operate in the following way:

  1. The pinning implementation should check for pins associated with the specific domain; if they are defined then they are used as the only accepted set of pins.
  2. If there are no pins defined for a domain, then the implementation should check if there are any pins associated with the * domain; if there are then they are should be used as the only accepted set of pins, representing trusted root certificates.
  3. Otherwise the connection is not subject to pinning.

This approach allows a mixture of API domains that use specific pins and domains using the managed trust roots.

info

The SDK also provides a getPinsJSON method that provides the pins in marshaled JSON, for cases where the type return from getPins cannot be easily bridged between language runtimes.

Reacting to Configuration Changes

Whenever an Approov token is fetched there is a possibility that a new dynamic SDK configuration will be received. This will happen for apps whenever Dynamic Pinning Changes have been made in the account. The getPins method always returns the latest set of pins received by the SDK. If your implementation calls this method dynamically on all new TLS connections then the update will happen completely automatically.

In some cases, however, your implementation may have set pins as part of the construction of a networking stack. This is notably the case for OkHttp where the CertificatePinner needs to be set during construction. In this case your implementation should detect configuration updates after Approov token fetches and then arrange to build a revised stack using the new pins.

A isConfigChanged boolean property is included in the Fetch Result from the SDK. The property is set to true if there is an unconsumed update to the SDK configuration. The update is consumed by the first call to fetchConfig after the new configuration is received; thereafter the isConfigChanged property of new fetch results will be false until the next update occurs.

Reacting to Force Apply Pins

In some cases it is not possible for the app to immediately react to pinning changes. In this case the app may need a restart to apply the new pin configuration, or prompt the user to restart the app. Generally though we do not wish to restart the app every time there is a configuration change, as this could result in a poor user experience. Moreover, we may wish to push new pins to the configuration ahead of time so that they are more likely to be available when needed and thus avoid the need for an invasive restart altogether, since there is a higher chance of the app being restarted anyway.

One approach is to postpone any restart until there has been both a pending configuration update and a pinning failure exception has been caught by the app. That way a restart only occurs when absolutely necessary. Of course this may complicate the code by requiring special checks and logic on pinning failures.

Another mechanism is provided that is initiated by the Forcing Pin Application command. A isForceApplyPins property is provided in the Fetch Result. If this flag is set then it indicates that the last call to getPins made by the app obtained a version of the pins that is older than that which is being forced. Thus if an app has been naturally restarted since the last pin configuration update then it will already have a sufficiently recent version and the flag is not asserted,

The fact that these pins have been forced indicates that it is important that the new version is being used, perhaps because a certificate has been compromised or new pins are required to access an endpoint. If the flag is set then the app should take action to ensure that the pins are updated to the latest version, even if this means initiating an app restart. Calling getPins will reset the flag for the next time an Approov fetch is made.

A mechanism is provided to signal the need to force an immediate update where this is necessary:

approov pin -forceApplyPins

You will be asked for confirmation as follows:

WARNING: forcing pin application will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
pin update has been forced

In the typical use case, new pins should be updated a couple of weeks before they go into service and then sometime later they can be forced. This procedure only results in a restart in the minority of apps that have not been relaunched for that period. In an emergency the forcing can be issued immediately. Note that this behavior only impacts app integrations that are not able to dynamically update their pins.