Certificate Pinning
Overview
Public key pinning is provided using the Android Network Security Configuration and TrustKit. To use public key pinning, you can either create an Android network security configuration XML file or set a custom TrustManager implementation. The network security configuration is supported natively on Android Nougat (API Level 24) and higher. For versions between API Level 21 and 23, the Gini SDK relies on TrustKit. The custom TrustManager
is supported on all Android versions. We recommend reading the Android Network Security Configuration guide and the TrustKit limitations for API Levels 21 to 23.
Configure pinning
The following sample configuration shows how to set the public key pin for two domains. The Gini Capture SDK’s default networking implementation uses by default pay-api.gini.net
and user.gini.net
. It should be saved under res/xml/network_security_config.xml
:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<trustkit-config
disableDefaultReportUri="true"
enforcePinning="true" />
<domain includeSubdomains="false">pay-api.gini.net</domain>
<pin-set>
<!-- old *.gini.net public key-->
<pin digest="SHA-256">cNzbGowA+LNeQ681yMm8ulHxXiGojHE8qAjI+M7bIxU=</pin>
<!-- new *.gini.net public key, active from around June 2020 -->
<pin digest="SHA-256">zEVdOCzXU8euGVuMJYPr3DUU/d1CaKevtr0dW0XzZNo=</pin>
</pin-set>
<domain-config>
<trustkit-config
disableDefaultReportUri="true"
enforcePinning="true" />
<domain includeSubdomains="false">user.gini.net</domain>
</domain-config>
</domain-config>
</network-security-config>
If you set different base URLs when instantiating with the GiniCaptureDefaultNetworkService.Builder
, make sure you set matching domains in the network security configuration XML.
The above digests serve as an example only. You should always create the digest yourself from the Gini API’s public key and use that one (see Extract hash from pay-api.gini.net). If you receiv a digest from us, always validate it by comparing it to the digest you created from the public key (see Extract hash from public key). Failing to validate a digest might lead to security vulnerabilities.
TrustKit
The TrustKit configuration tag <trustkit-config>
is required in order to deactivate TrustKit reporting and to enforce public key pinning. This is important because without it TrustKit doesn’t throw CertificateExceptions
if the local public keys don’t match any of the remote ones, effectively deactivating pinning. The only downside of enforcing pinning is that two public key hashes are required. In the example above, we created and used a “zero” key hash as a placeholder. Setting the same key hash twice doesn’t help because key hashes are stored in a set. Ideally, you should use a backup public key hash as the second one.
In your AndroidManifest.xml
you need to set the android:networkSecurityConfig
attribute on the <application>
tag to point to the XML:
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
...
<application android:networkSecurityConfig="@xml/network_security_config">
...
</manifest>
Enable pinning with a network security configuration
For the library to know about the XML, set the XML resource id using the GiniCaptureDefaultNetworkService.Builder.setNetworkSecurityConfigResId()
method:
val defaultNetworkService = GiniCaptureDefaultNetworkService.builder(context)
.setClientCredentials("gini-client-id", "GiniClientSecret", "example.com")
.setNetworkSecurityConfigResId(R.xml.network_security_config)
.build();
Enable pinning with a custom TrustManager implementation
You can also control which certificates to trust by passing your TrustManager
implementation to the GiniCaptureDefaultNetworkService.Builder.setTrustManager()
method:
Setting a custom TrustManager
overrides the network security configuration.
Extract hash from pay-api.gini.net
Extract hash from public key
You can also extract the hash from a public key. The following example shows how to extract it from a public key named pay-api.gini.pub
: