SSL Pinning — The Right Way to Secure App
Recently, our development team has incorporated SSL pinning certification into our Kotlin-based Android application. This step, which is clearly aimed at enhancing mobile security and privacy, was recommended by our application security team. After the implementation, the SSL certificate itself hasn’t posed any issues. However, the real challenge lies in the validity of the certificate, particularly those managed by Google.
Google’s announcement of 90 day certificates and what it means.
Yes, it’s true. Google-managed SSL certificates indeed have a validity period of 90 days. However, about one month before the expiry date, Google Cloud starts the renewal process automatically, so you are not left without a certificate. This means you do not need to worry about manually renewing the certificates. The move to a 90-day validity period is part of Google’s efforts to improve security and promote automation in the certificate issuance process.
1.Increased Security: Shorter validity periods can enhance security by ensuring that certificates are equipped with the latest security features and are less vulnerable to threats.
2.Promotion of Automation: By requiring certificates to be renewed every 90 days, Google aims to move away from complex and error-prone issuance processes.
3.Faster Adoption of Security Practices: These changes allow for faster adoption of emerging security capabilities and best practices.
4.Reduced Reliance on Revocation Checking Solutions: Decreasing certificate lifetime will also reduce ecosystem reliance on revocation checking solutions that cannot fail-closed and, in turn, offer incomplete protection.
…But is 90 days the correct length?
However, it’s worth noting that there are ongoing discussions about whether 90 days is the optimal length for certificate validity. For instance, a compromised certificate can still exist for up to 90 days, and domain registrations typically occur on an annual basis, not every 90 days. Therefore, the move towards shorter certificate lifetimes is a complex issue with various factors to consider.
While shorter validity periods might potentially enhance security, they undeniably introduce considerable challenges. The shift to 90-day certificates would represent a significant change for enterprises, and we acknowledge the difficulties this could present in managing certificate lifecycles. As certificate lifetimes decrease, traditional methods of managing certificate expirations, such as spreadsheets and notifications, become impractical. Even without shrinking certificate lifetimes, manually tracking them via a spreadsheet is a tedious task prone to human error. To adhere to industry standards and keep up with advancements in hardware and software, certificate management requires meticulous attention that becomes increasingly difficult to maintain at scale.
Moreover, the repercussions of mismanaged certificates and resulting outages can be severe. An increased workload for certificate lifecycle management could heighten the risk of human error, potentially leading to more frequent outages.
The following 3 methods are the most popular ways to implement Certificate Pinning in Android apps.
- TrustManager
- OkHttp and CertificatePinner
- Network Security Configuration
We will see only Network Security Configuration which is a best and more secure way to implement.
The Android platform provides a new, easy tool to handle network configuration — Network Security Configuration (NSC).
- Centralized security policy management
- Supports certificate pinning, certificate revocation, and other security features
- Works with all network traffic, including third-party libraries and native code
- No additional dependencies required Cons:
- Requires API level 24 or higher
It has been available since Android 7.0. With NSC, you can declare secure communication methods, including Android Certificate Pinning, using XML files. To enable the configuration, you need to bind a configuration file with the Manifest. To bind it, use the networkSecurityConfig attribute in the application tag. Lets See
- Generate a new certificate: Obtain a new certificate from a trusted Certificate Authority (CA). Make sure the new certificate contains the same public key as the expired Google-managed certificate.
- Create a PinnedCertificate class: Create a new class to represent the pinned certificate, containing the SPKI as a byte array.
data class PinnedCertificate(val spkiBytes: ByteArray)
3. Create a CertificatePinner class: Create a new class to represent the certificate pinner, containing a list of pinned certificates and a method to check if a certificate matches any of the pinned certificates.
import java.security.cert.Certificate
import java.security.cert.X509Certificate
import javax.net.ssl.X509TrustManager
class CertificatePinner(private val pinnedCertificates: List<PinnedCertificate>) : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun checkServerTrusted(chain: Array<X509Certificate>?, authType: String?) {
if (chain == null || chain.isEmpty()) {
throw CertificateException("Certificate chain is empty")
}
val certificate = chain[0]
val publicKey = certificate.publicKey
for (pinnedCertificate in pinnedCertificates) {
if (Arrays.equals(pinnedCertificate.spkiBytes, certificate.subjectPublicKeyInfo.encoded)) {
return
}
}
throw CertificateException("Certificate not trusted")
}
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
}
4. Create a NetworkSecurityConfig class: Create a new class to represent the network security configuration, containing the custom TrustManager.
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.os.Build
import androidx.annotation.RequiresApi
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
class NetworkSecurityConfig(private val context: Context) {
private val trustManagerFactory: TrustManagerFactory
init {
val certificatePinner = CertificatePinner(
listOf(
PinnedCertificate(Base64.decode("SPKI_BYTES_HERE", Base64.DEFAULT))
)
)
val trustManagers = arrayOf<TrustManager>(certificatePinner)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustManagers, null)
trustManagerFactory = sslContext.trustManagerFactory
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getSecureNetwork(networkCallback: ConnectivityManager.NetworkCallback): Network {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
val networkBuilder = NetworkBuilder()
networkBuilder.setMetered(true)
networkBuilder.setCapabilities(networkRequest.networkCapabilities)
networkBuilder.addNetworkCallback(networkCallback)
networkBuilder.setSecurityConfiguration(getSecurityConfig())
val network = connectivityManager.requestNetwork(networkBuilder.build(), networkCallback)
connectivityManager.bindProcessToNetwork(network)
return network
}
private fun getSecurityConfig(): NetworkSecurityConfiguration {
val securityConfigBuilder = NetworkSecurityConfiguration.Builder()
securityConfigBuilder.setTrustManagers(trustManagerFactory.trustManagers)
securityConfigBuilder.setTransportSecurityProperties(
TransportSecurityProperties.Builder()
.setSsl3Support(false)
.setSsl2Support(false)
5. Update the server configuration: Update the server configuration to use the new certificate. This may involve updating the server’s SSL/TLS configuration file (e.g., server.crt
, server.key
, etc.) and restarting the server.
Using Google-managed SSL certificates
GKE Google-managed SSL certificates support public and private clusters.
Limitations
- Google-managed certificates are less flexible than certificates you obtain and manage yourself. Google-managed certificates support up to 100 non-wildcard domains. Unlike self-managed certificates, Google-managed certificates don’t support wildcard domains.
- If you require self-managed certificates or if you already own SSL certificates that you would like to configure on your Ingress, see Setting up HTTPS (TLS) between client and load balancer.
- The number and type of certificates supported by an Ingress are defined by the limits of Google-managed SSL certificates.
- Updates on Google-managed certificates are not supported.
- If the certificate is revoked directly with the Certificate Authority, Google does not automatically rotate the certificate. You must delete the ManagedCertificate and create a new one.
Prerequisites
- You must own the domain name. The domain name must be no longer than 63 characters. You can use Google Domains or another registrar.
- If you use a GKE Standard cluster, the
HttpLoadBalancing
add-on must be enabled. - Your
ingressClassName
must be"gce"
. - You must apply
Ingress
andManagedCertificate
resources in the same project and namespace. - Create a reserved (static) external IP address. Reserving a static IP address ensures that it remains yours, even if you delete the Ingress. If you don’t reserve an IP address, it might change, requiring you to reconfigure your domain’s DNS records. Use Google Cloud CLI or the Google Cloud console to create a reserved IP address.
Setting up a Google-managed certificate
Summary
Both Network Security Configuration and OkHttp with CertificatePinner or TrustManager can provide secure SSL pinning for Android apps. If you need a centralized security policy management solution that works with all network traffic, Network Security Configuration is the best choice. If you only need SSL pinning for HTTP and HTTPS traffic handled by OkHttp, and you want a simple and easy-to-use solution, OkHttp with CertificatePinner or TrustManager is a good choice. Regarding the question of which approach is more secure, both approaches can provide strong security if implemented correctly. However, Network Security Configuration provides more advanced security features, such as certificate revocation and traffic encryption for cleartext connections, which can increase the overall security of your app’s network traffic.
Regardless of the implementation method you choose, it’s crucial to remember that Android Certificate Pinning is mandatory. It’s the sole means to ensure genuinely secure networking. That’s why OWASP Mobile endorses certificate pinning as the most potent defense against Man-in-the-Middle attacks.
Follow for latest article on LinkedIn