Certificate pinning is a method to enhance the security of an application by closer examining the certificates provided by a peer and comparing some of their properties with known data to verify they have not been changed e.g. by a malicious third party. Although one approach to retrieve these 'known data' is to accept a certificate on first sight and comparing subsequently offered certificates to the data extracted from the first one, the SDK utilizes the safer approach of checking certificates against pre-shared information in the form of a supplied certificate pinning hash.
By default, the XignSdk pins all certificates used within the SDKs connections. Connections without valid pinning rules
are blocked. A configuration for the official XignSys XignIn-Manager xign.me is provided by the SDK itself and does
not need to be changed as long as you keep up with future SDK updates. Connections to other XignIn-Managers on the other
hand must be supplied with a certificate pinning rule. There is also an option to disable the pinning entirely, to do so
an object of XignSdkConfig has to be created with XignSdkConfig.Builder.setIsCertificatePinningEnforced(false)
and pass it to XignSdk.initialize(application:config:).
Note: Certificate pinning should only be disabled for debug builds. Release versions should provide valid configurations since disabling this option reduces the security of your application.
The SDK uses dedicated HTTP session configurations on both platforms. Changing any global session configurations does not affect the SDK. Therefore, the SDK does not provide any extra security for your own HTTP connections outside the scope of the SDK. Configuring the certificate pinning rules differ between iOS and Android. Please read the following chapter for your platform, even if you do not plan changing the configuration.
Furthermore, if you have trouble creating the necessary public key hashes, the XignSys Android SDK Demo project offers a build script task to create those hashes. For that purpose please take a look at Creating public key hashes.
On the iOS platform certificate pinning rules are configured solely in code. There is no need to edit your info.plist
for this purpose. If you do not plan on communicating with another XignIn-Manager besides xign.me there is no need to
change anything, because a configuration for that particular manager is always provided by the SDK itself.
Certificate pinning configurations can be set up via the XignSdkConfig class. In order to do so
a CertificatePinningConfiguration object must be created and then passed to the function
XignSdkConfig.Builder.addCertificatePinningConfiguration(:).
The object can be created by simply invoking its initializer and passing a domain name, an expiration date and a list of
public key hashes. A public key hash is a SHA256 hash of the public key's SubjectPublicKeyInfo of the X.509
certificate as base64 encoded character sequence.
The following example shows how a pinning rule can be added to a XignSdkConfig.
Swift
private func setupPinningConfigurations(_ builder: XignSdkConfig.Builder) throws {
// add a certificate pinning configuration for a XignIn-Manager
try builder.addCertificatePinningConfiguration(
CertificatePinningConfiguration(
domainName: "example.com",
expirationDate: "2024-01-01",
publicKeyHashes: [
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
],
includeSubdomains: true
)
)
}
It is possible to add multiple configurations this way. A configuration is identified by the domainName. Adding a
second rule for the same domainName overrides the entry. This can also be used to change the default rule for
the xign.me XignIn-Manager set by the SDK. Note that this is normally not necessary. For the other parameters please
take a look at the documentation of the CertificatePinningConfiguration class.
After adding all your pinning rules to the XignSdkConfig.Builder instance (and possible other configurations like key
material etc.) a XignSdkConfig object can be created by invoking XignSdkConfig.Builder.build(). This instance can
than be used to initialize the SDK by calling XignSdk.initialize(application:config:).
On the Android platform the certificate pinning configurations are set up via the
Network security configuration. If you are unfamiliar
with this concept, please take a look at the official Android documentation. The configuration is provided via the
resource file named network_security_config.xml.
Important: If you define your own
network_security_config.xmlfile or use other libraries that define this file, you have to add all relevant entries to your file. That includes the rule for the official XignSys XignIn-Managerxign.me, because these files are not merged automatically.
The following example shows how a configuration file could look like.
XML
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system"/>
</trust-anchors>
</base-config>
<!-- Official XignSys XignIn-Manager -->
<domain-config>
<domain includeSubdomains="true">xign.me</domain>
<pin-set expiration="2021-11-17">
<pin digest="SHA-256">YuTjuRAJwaArR2nDGGJiKtLgj4uBH7Ipr6Os8AFNqpE=</pin>
<pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</pin>
</pin-set>
<trustkit-config disableDefaultReportUri="true"
enforcePinning="true"/>
</domain-config>
<!-- additional configurations -->
<domain-config>
<domain includeSubdomains="true">your.domain.com</domain>
<pin-set expiration="2022-01-01">
<pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</pin>
<pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB</pin>
</pin-set>
<trustkit-config disableDefaultReportUri="true"
enforcePinning="true"/>
</domain-config>
</network-security-config>
After adding this file with all necessary rules, no further steps are needed to configure the certificate pinning rules on Android platforms.
The XignSys Android SDK Demo project offers a
simple task to create the necessary public key hashes from live (online) servers. Simply clone the project via Git and
open it in Android Studio. Afterwards open your local version of the file 'xignsys-android-sdk-demo/build.gradle.kts'
and add the domain of your server to the list of host names of the task named retrieveCertificatePinningHashes.
Kotlin
// This Gradle task can be used to retrieve certificate pinning hashes.
//
// Just run the gradle task named "retrieveCertificatePinningHashes". It will print the
// retrieved hashes in the console. You can then copy them and add certificate pinning
// configurations for the SDK.
register(
"retrieveCertificatePinningHashes",
com.xignsys.sdk.tasks.CertificatePinningHashesTask::class
) {
// You can add your XignIn-Managers host name to this list in order to retrieve the
// necessary certificate pinning hash.
hostNames = listOf(
"xign.me",
"your.domain.com"
)
}
After adding the hostnames, the Gradle task named retrieveCertificatePinningHashes can be run, which prints the
fetched public key hashes to the (run) console. The output looks like this:
Text
...
> Task :retrieveCertificatePinningHashes
Certificate Pinning Hashes retrieved:
Host: xign.me
SHA256 Not before - Not after Subject CN
-------------------------------------------- ---------- - ---------- ------------
YuTjuRAJwaArR2nDGGJiKtLgj4uBH7Ipr6Os8AFNqpE= 2019-11-18 - 2021-11-17 xign.me
SDG5orEv8iX6MNenIAxa8nQFNpROB/6+llsZdXHZNqs= 2017-11-02 - 2027-11-02 GeoTrust TLS RSA CA G1
i7WTqTvh0OioIruIfFR4kMPnBqrS2rdiVPl/s2uC/CY= 2013-08-01 - 2038-01-15 DigiCert Global Root G2
...
BUILD SUCCESSFUL in 9s
1 actionable task: 1 executed
Some developers may not be familiar with the concept of certificate pinning or HTTPS/TLS certificate verification. Therefore, this section gives some general information about these topics and describes the implications that emerge by using one or both in the context of the XignSys SDK.
In general, server certificates used for TLS connections are checked on two levels.
Firstly, verification of certificates is performed on the system level, where the domain, signatures, dates and other data is checked. The certificate, or at least one element in the certificate chain, has to be signed by a trusted certificate authority. Many such authorities (CAs) are provided by the system manufacturer (Apple, Google, Samsung, ...) via system updates. Each certificate is signed by a CA, which may be signed by another CA as well. During certificate verification all certificates in this chain are checked.
Secondly, certificates may be checked with certificate pinning which is explained at the top of this chapter.
Combining the checks on both levels produce the following possible cases:
This behavior has some implications on the certificate management for the servers the SDK should communicate with.
The expiration date set in the pinning configuration is not necessarily the expiration date of the certificate(s) mentioned in the configuration but that of the pinning rule described in the configuration. If this date is exceeded, the respective rule is not applied anymore.
When a server certificate expires and is not replaced by a valid new one in time, communication with the server is not possible anymore until a new certificate is installed.
When a certificate is renewed in a way that the fingerprint stays the same, pinning can stay being enforced but if an expiration date is configured in the respective rule, it has to be adjusted so the rule can still be applied after that initial date.
If in contrast a new certificate (with another fingerprint) is issued and its information can not be provided to the app beforehand, the expiration date for the respective rule has to be adjusted to be a point in time before the certificate change. This will effectively deactivate certificate pinning for that host and therefore reduces the overall security of the app.
For a production environment and with the goal in mind to provide a smooth and uninterrupted app usage experience, the following two approaches are most desirable:
Combinations of both approaches are possible as well.