Pinning Cheat Sheet

The Pinning Cheat Sheet is a technical guide to implementing certificate and public key pinning as discussed at the Virginia chapter's presentation [[Media:Securing-Wireless-Channels-in-the-Mobile-Space.ppt|Securing Wireless Channels in the Mobile Space]]. This guide is focused on providing clear, simple, actionable guidance for securing the channel in a hostile environment where actors could be malicious and the conference of trust a liability. Additional presentation material included [[Media:pubkey-pin-supplement.pdf|supplement with code excerpts]], [[Media:pubkey-pin-android.zip|Android sample program]], [[Media:pubkey-pin-ios.zip|iOS sample program]], [[Media:pubkey-pin-dotnet.zip|.Net sample program]], and [[Media:pubkey-pin-openssl.zip|OpenSSL sample program]].

Invariant trust of critical infrastructure such as DNS and PKI{X} with a public CA hierarchy has led to a number of high profile failures in the secure channel. This cheat sheet will help developers and organizations navigate the minefield of securing data in transit and by restoring integrity on the channel when a pre-existing relationship exists between the user and an organization or service.

This cheat sheet will not attempt to enumerate and catalogue the failures in the industry, investigate the design flaws in the scaffolding, justify the lack of accountability or liability with the providers, or explain the collusion between, for example, Browsers and CAs. The reader is directed towards [[Media:Securing-Wireless-Channels-in-the-Mobile-Space.ppt|Securing Wireless Channels in the Mobile Space]] presentation for a sampling of the tangential issues.

Introduction
Secure channels are a cornerstone to users and employees working remotely and on the go. Users and developers expect end-to-end security when sending and receiving data - especially sensitive data on channels protected by VPN, SSL, or TLS. While organizations which control DNS and CA have likely reduced risk to trivial levels under most threat models, users and developers subjugated to other's DNS and a public CA hierarchy are exposed to non-trivial amounts of risk. In fact, history has shown those relying on outside services have suffered chronic breaches in their secure channels.

The pandemic abuse of trust has resulted in users, developers and applications making security related decisions on untrusted input. The situation is somewhat of a paradox: entities such as DNS and CAs are trusted and supposed to supply trusted input; yet their input cannot be trusted. Relying on others for security related decisions is not only bad karma, it violates a number of secure coding principals (see, for example, OWASP's Injection Theory and Data Validation).

Pinning effectively removes the "conference of trust" and identifies the host or service by its public certificate or public key when holding a public/private key pair. An application which pins a certificate or public key no longer needs to depend on others - such as DNS or CAs - when making decisions relating to a peer's identity. For those familiar with SSH, you should realize that public key pinning nearly identical to SSH's StrictHostKeyChecking option. SSH had it right the entire time, and the rest of the world is beginning to realize the virtues of directly identifying a host or service by its public key.

Others who actively engage in pinning include Google and its browser Chrome. Chrome was successful in detecting the DigiNotar compromise which uncovered suspected interception by the Iranian government on its citizens. The initial report of the compromise can be found at Is This MITM Attack to Gmail's SSL?; and Google Security's immediate response at An update on attempted man-in-the-middle attacks.

What Is Pinning?
In essence, pinning is the act of verifying a host's identity based on their expected X509 certificate or public key. Put another way, its a whitelist of known certificate or public key. If more than one certificate or public key is acceptable, then the program holds a pinset. In this case, the peer's advertised identity must match one of the elements in the pinset.

Pinning leverages knowledge of the pre-existing relationship between the user and an organization or service to help make better security related decisions. Because you already have information on the server or service, you don't need to rely on generalized mechanisms meant to solve the key distribution problem. That is, you don't need to turn to DNS for names/addresses or CAs for binding and identity status (though it does not hurt).

How Do You Pin?
From 10,000 feet, the idea is to re-use the exiting protocols and infrastructure, but use it in a hardened state. For re-use, a program would keep doing the things it used to do when establishing a secure connection. To harden the channel, the program would would take advantage of the OnConnect callback offered by a library, framework or platform. In the callback, the program would verify the remote host's identity by validating its certificate or public key. That's how the pinning takes place.

While pinning does not have to occur in an OnConnect callback, its often most convenient because the underlying connection information is readily available.

When Do You Pin?
You should pin anytime you want to be relatively certain of the remote host's identity or when operating in a hostile environment. Since both are almost always true, you should probably pin all the time.

As a perfect case in point: during the two weeks or so of preparation of the presentation and cheat sheet, I've observed three closely related failures. First was Nokia/Opera willfully breaking the secure channel; second was a CA issuing a code signing certificate for malware; and third was Bit9's loss of its root key. The environment is not only hostile, its toxic.

If you are working for an organization which practices "egress filtering" as part of a Data Loss Prevention (DLP) strategy, you will likely encounter Interception Proxies. I like to refer to these things as "good" bad guys (as opposed to "bad" bad guys) since both break end-to-end security and we can't tell them apart. In this case, add the interception proxy's public key to your pinset after being requested to do so by the folks in Risk Acceptance. Don't offer to whitelist the interception proxy since it defeats your security goals.

For more reading on interception proxies, the additional risk they bestow, and how they fail, see Dr. Matthew Green's How do Interception Proxies fail? and Jeff Jarmoc's BlackHat talk SSL/TLS Interception Proxies and Transitive Trust.

What Should Be Pinned?
The first thing to decide is what should be pinned, and you have two choices: (1) you can pin the site or service's certificate; or (2) you can pin the public key. If you choose public keys, you have two additional choices: (a) pin the subjectPublicKeyInfo; or (b) pin one of the concrete types such as RSAPublicKey, DSAPublicKey, etc.

Encodings/Formats
For the purposes of this article, the objects are in X509-compatible presentation format (PKCS#1 defers to X509, which both use ASN.1 and are consistent). If you have a PEM encoded object (for example, -BEGIN CERTIFICATE-, -END CERTIFICATE-), then convert the object to DER encoding.

A certificate is an object which binds an entity (such as a person or organization) to a public key via a signature. The certificate is DER encoded, and has associated data or attributes such as Subject (who is identified or bound), Issuer (who signed it), Validity (NotBefore and NotAfter), and a Public Key.

A certificate has a subjectPublicKeyInfo. The subjectPublicKeyInfo is a key with additional information. The ASN.1 type includes an Algorithm ID, a Version, and an extensible format to hold a concrete public key. Figures 1 and 2 below show different views of the same of a RSA key, which is the subjectPublicKeyInfo.

The concrete public key is an encoded public key. The key format will usually be specified elsewhere - for example, PKCS#1 in the case of RSA Public Keys. In the case of an RSA public key, the type is RSAPublicKey and the parameters {e,n}</tt> will be ASN.1 encoded. Figures 1 and 2 above clearly show the modulus (n at line 28) and exponent (e at line 289). For DSA, the concrete type is DSAPublicKey and the encoded ASN.1 parameters would be {p,q,g,y}</tt>.

Final takeaways: (1) a certificate binds an entity to a public key; (2) a certificate has a subjectPublicKeyInfo; and (3) a subjectPublicKeyInfo has an concrete public key. For those who want to learn more, a more in-depth discussion from a programmer's perspective can be found at the CodeProject's article Cryptographic Interoperability: Keys.

Certificate
The certificate is easiest to pin. You can fetch the certificate out of band for the website, have the IT folks email your company certificate to you, use openssl s_client</tt> to retrieve the certificate etc. When the certificate expires, you would update your application. Assuming your application has no bugs or security defects, the application would be updated every year or two.

At runtime, you retrieve the website or server's certificate in the callback. Within the callback, you compare the retrieved certificate with the certificate embedded within the program. If the comparison fails, then fail the method or function.

There is a downside to pinning a certificate. If the site rotates its certificate on a regular basis, then your application would need to be updated regularly. For example, Google rotates its certificates, so you will need to update your application about once a month (if it depended on Google services). Even though Google rotates its certificates, the underlying public keys (within the certificate) remain static.

Public Key
Public key pinning is more flexible but a little trickier due to the extra steps necessary to extract the public key from a certificate. As with a certificate, the program checks the extracted public key with its embedded copy of the public key.

There are two downsides two public key pinning. First, its harder to work with keys (versus certificates) since you must extract the key from the certificate. Extraction is a minor inconvenience in Java and .Net, buts its uncomfortable in Cocoa/CocoaTouch and OpenSSL. Second, the key is static and may violate key rotation policies.

Examples of Pinning
The next section demonstrates certificate and public key pinning in Android Java, iOS, .Net, and OpenSSL. All programs attempt to connect to random.org and fetch bytes (Dr. Mads Haahr participates in AOSP's pinning program, so the site should have a static key).

Parameter validation, return value checking, and error checking has been omitted in the code below, but is present in the sample programs. So the sample code is ready for copy/paste. By far, the most uncomfortable languages are C-based: iOS and OpenSSL.

Android
Pinning in Android is accomplished through a custom X509TrustManager</tt>. The derived class is shown first, and its use is shown next.

Download: [[Media:pubkey-pin-android.zip|Android sample program]].

public final class PubKeyManager implements X509TrustManager { private static String PUB_KEY = "30820122300d06092a864886f70d0101" + "0105000382010f003082010a0282010100b35ea8adaf4cb6db86068a836f3c85" + "5a545b1f0cc8afb19e38213bac4d55c3f2f19df6dee82ead67f70a990131b6bc" + "ac1a9116acc883862f00593199df19ce027c8eaaae8e3121f7f329219464e657" + "2cbf66e8e229eac2992dd795c4f23df0fe72b6ceef457eba0b9029619e0395b8" + "609851849dd6214589a2ceba4f7a7dcceb7ab2a6b60c27c69317bd7ab2135f50" + "c6317e5dbfb9d1e55936e4109b7b911450c746fe0d5d07165b6b23ada7700b00" + "33238c858ad179a82459c4718019c111b4ef7be53e5972e06ca68a112406da38" + "cf60d2f4fda4d1cd52f1da9fd6104d91a34455cd7b328b02525320a35253147b" + "e0b7a5bc860966dc84f10d723ce7eed5430203010001";

public void checkServerTrusted(X509Certificate[] chain, String authType) {   if (!(null != authType && authType.equalsIgnoreCase("RSA"))) throw new IllegalArgumentException( "checkServerTrusted: AuthType is not RSA");

// Hack ahead: BigInteger and toString. We know a DER encoded Public Key starts // with 0x30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is no leading 0x00 to drop. RSAPublicKey pubkey = (RSAPublicKey)chain[0].getPublicKey; String encoded = new BigInteger(1 /*positive*/, pubkey.getEncoded).toString(16);

final boolean expected = PUB_KEY.equalsIgnoreCase(encoded); if (!expected) { throw new IllegalArgumentException(         "checkServerTrusted: expected public key (hash): " +          PUB_KEY + ", got public key: " + encoded); } }

PubKeyManager</tt> would be used in code similar to below.

TrustManager tm[] = { new PubKeyManager };

SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tm, null);

URL url = new URL( "https://www.random.org/integers/?" +                  "num=16&min=0&max=255&col=16&base=10&format=plain&rnd=new");

HttpsURLConnection connection = (HttpsURLConnection) url.openConnection; connection.setSSLSocketFactory(context.getSocketFactory);

InputStreamReader instream = new InputStreamReader(connection.getInputStream); StreamTokenizer tokenizer = new StreamTokenizer(instream); ...

iOS
iOS pinning is performed through a NSURLConnectionDelegate</tt>. The delegate must implement connection:canAuthenticateAgainstProtectionSpace:</tt> and connection:didReceiveAuthenticationChallenge:</tt>. The relevant code to pin a certificate is shown below

Download: [[Media:pubkey-pin-ios.zip|iOS sample program]].

-(IBAction)fetchButtonTapped:(id)sender {   NSString* requestString = @"https://www.random.org/integers/?        num=16&min=0&max=255&col=16&base=16&format=plain&rnd=new"; NSURL* requestUrl = [NSURL URLWithString:requestString];

NSURLRequest* request = [NSURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0f];

NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; }

-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace: (NSURLProtectionSpace*)space {   return [[space authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]; }

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge { if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])  {    SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];

OSStatus status = SecTrustEvaluate(serverTrust, NULL);

SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);

CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate); [(id)serverCertificateData autorelease];

const UInt8* const data = CFDataGetBytePtr(serverCertificateData); const CFIndex size = CFDataGetLength(serverCertificateData); NSData* cert1 = [NSData dataWithBytes:data length:(NSUInteger)size];

NSString *file = [[NSBundle mainBundle] pathForResource:@"random-org" ofType:@"der"];   NSData* cert2 = ;

const BOOL equal = [cert1 isEqualToData:cert2]; if(equal) { // The only good exit point return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]                   forAuthenticationChallenge: challenge];    }

// Bad dog return [[challenge sender] cancelAuthenticationChallenge: challenge]; }

.Net
.Net pinning can be achieved by using ServicePointManager</tt> as shown below.

Download: [[Media:pubkey-pin-dotnet.zip|.Net sample program]].

// Encoded RSAPublicKey private static String PUB_KEY = "30818902818100C4A06B7B52F8D17DC1CCB47362" + "C64AB799AAE19E245A7559E9CEEC7D8AA4DF07CB0B21FDFD763C63A313A668FE9D764E" + "D913C51A676788DB62AF624F422C2F112C1316922AA5D37823CD9F43D1FC54513D14B2" + "9E36991F08A042C42EAAEEE5FE8E2CB10167174A359CEBF6FACC2C9CA933AD403137EE" + "2C3F4CBED9460129C72B0203010001";

public static void Main(string[] args) { ServicePointManager.ServerCertificateValidationCallback = PinPublicKey; WebRequest wr = WebRequest.Create("https://encrypted.google.com/"); wr.GetResponse; }

public static bool PinPublicKey(object sender, X509Certificate certificate, X509Chain chain,                               SslPolicyErrors sslPolicyErrors) { if (null == certificate) return false;

String pk = certificate.GetPublicKeyString; if (pk.Equals(PUB_KEY)) return true;

// Bad dog return false; }

OpenSSL
Pinning in OpenSSL is a lot of work, but the library offers the most control. For example,with OpenSSL you can specify a single trusted root certificate and instantly enjoy magnitudes of improvement over other libraries and programs which carry around hundreds of Root CAs, Subordinate CAs, Intermediate CAs, and the like.

The code below is from pkp_pin_peer_key</tt>. The function receives control after OpenSSL performs the customary SSL/TLS negotiation with internal tests (signature checks, chaining to a trusted root, validity, and hostname). A second function - pkp_verify_cb</tt> - is not shown. It's the callback invoked during certificate chain verification. The function is useful for developing an understanding of OpenSSL's processing of the connection.

Rather than use an unconditional jump (goto</tt>), the code uses a do/while</tt> to achieve the same effect. I've been scolded for both and a do/while</tt> seems to offend the least. And I refuse to deeply nest if</tt> statements).

Download: [[Media:pubkey-pin-openssl.zip|OpenSSL sample program]].

int pkp_pin_peer_pubkey(SSL* ssl) {   if(NULL == ssl) return FALSE; X509* cert = NULL; FILE* fp = NULL; /* Scratch */ int len1 = 0, len2 = 0; unsigned char *buff1 = NULL, *buff2 = NULL; /* Result is returned to caller */ int ret = 0, result = FALSE; do   { /* http://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html */ cert = SSL_get_peer_certificate(ssl); if(!(cert != NULL)) break; /* failed */ /* Begin Gyrations to get the subjectPublicKeyInfo      */ /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ /* http://groups.google.com/group/mailing.openssl.users/browse_thread/thread/d61858dae102c6c7 */ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); if(!(len1 > 0)) break; /* failed */ /* scratch */ unsigned char* temp = NULL; /* http://www.openssl.org/docs/crypto/buffer.html */ buff1 = temp = OPENSSL_malloc(len1); if(!(buff1 != NULL)) break; /* failed */ /* http://www.openssl.org/docs/crypto/d2i_X509.html */ len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp);

/* These checks are verifying we got back the same values as when we sized the buffer. */       /* Its pretty weak since they should always be the same. But it gives us something to test. */       if(!((len1 == len2) && (temp != NULL) && ((temp - buff1) == len1))) break; /* failed */ /* End Gyrations */ /* See the warning above!!! */       /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fopen.html */ fp = fopen("random-org.der", "rx"); if(NULL ==fp) { fp = fopen("random-org.der", "r"); if(!(NULL != fp)) break; /* failed */ /* Seek to eof to determine the file's size                           */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html */ ret = fseek(fp, 0, SEEK_END); if(!(0 == ret)) break; /* failed */ /* Fetch the file's size                                              */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/ftell.html */ long size = ftell(fp);

/* Arbitrary size, but should be relatively small (less than 1K or 2K) */ if(!(size != -1 && size > 0 && size < 2048)) break; /* failed */ /* Rewind to beginning to perform the read                            */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html */ ret = fseek(fp, 0, SEEK_SET); if(!(0 == ret)) break; /* failed */ /* Re-use buff2 and len2 */ buff2 = NULL; len2 = (int)size; /* http://www.openssl.org/docs/crypto/buffer.html */ buff2 = OPENSSL_malloc(len2); if(!(buff2 != NULL)) break; /* failed */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fread.html */ /* Returns number of elements read, which should be 1 */ ret = (int)fread(buff2, (size_t)len2, 1, fp); if(!(ret == 1)) break; /* failed */ /* Re-use size. MIN and MAX macro below... */       size = len1 < len2 ? len1 : len2; /*************************/       /*****    PAYDIRT    *****/ /*************************/       if(len1 != (int)size || len2 != (int)size || 0 != memcmp(buff1, buff2, (size_t)size)) break; /* failed */ /* The one good exit point */ result = TRUE; } while(0); if(fp != NULL) fclose(fp); /* http://www.openssl.org/docs/crypto/buffer.html */ if(NULL != buff2) OPENSSL_free(buff2); /* http://www.openssl.org/docs/crypto/buffer.html */ if(NULL != buff1) OPENSSL_free(buff1); /* http://www.openssl.org/docs/crypto/X509_new.html */ if(NULL != cert) X509_free(cert); return result; }

Pinning Alternatives
Not all applications use split key cryptography. Fortunately, there are protocols which allow you to set up a secure channel based on knowledge of passwords and pre-shared secrets (rather than putting the secret on the wire in a basic authentication scheme). Two are listed below.

SRP
Secure Remote Password (SRP) is a Password Authenticated Key Exchange (PAKE) by Thomas Wu based upon Diffie-Hellman. The protocol is standardized in RFC 5054 and available in the OpenSSL library (among others). Diffie-Hellman based schemes are part of a family of problems based on Discrete Logs (DL), which are logarithms over a finite field. DL schemes are appealing because they are known to be hard (unless P=NP, which would cause computational number theorists to have a cow).

In the SRP scheme, the server uses a verifier which consists of a {salt, hash(password)}</tt> pair. The user has the password and receives the salt from the server. With lots of hand waiving, both parties select random values (nonces) and execute the protocol using g{(salt + password) rather than traditional Diffie-Hellman using gab.

EAP-PSK
EAP-PSK is Extensible Authentication Protocol using a Pre-Shared Key. EAP-PSK is specified in RFC 4764. The shared secret is used to key a block cipher, which is in turn used to exchange session keys. EAP-PSK is designed for authentication over insecure networks such as IEEE 802.11.

Pinning Gaps
There are two gaps when pinning due to reuse of the existing infrastructure and protocols. First, an explicit challenge is not sent by the program to the peer server based on the server's public information. So the program never knows if the peer can actually decrypt messages. However, the shortcoming is usually academic in practice since an adversary will receive messages it can't decrypt.

Second is revocation. Clients don't usually engage in revocation checking, so it could be possible to use a known bad certificate or key in a pinset. Even if revocation is active, Certificate Revocation Lists (CRLs) and Online Certificate Status Protocol (OCSP) can be defeated in a hostile environment. An application can take steps to remediate, with the primary means being freshness. That is, an application should be updated and distributed immediately when a critical security parameter changes.

!$@^ No Relationship ^@$!
If you don't have a pre-existing relationship, all is not lost. First, bad certificates are being spotted quicker in the field due to projects like Chromium and Certificate Patrol, and initiatives like the EFF's SSL Observatory.

Second, help is on its way, and there are three futures that will help with the issues. They are:


 * Public Key Pinning (http://www.ietf.org/id/draft-ietf-websec-key-pinning-04.txt) – an extension to the HTTP protocol allowing web host operators to instruct user agents (UAs) to remember ("pin") the hosts' cryptographic identities for a given period of time.
 * Sovereign Keys (http://www.eff.org/sovereign-keys) - operates by providing an optional and secure way of associating domain names with public keys via DNSSEC. PKI (hierarchical) is still used. Semi-centralized with append only logging.
 * Convergence (http://convergence.io) – different [geographical] views of a site and its associated data (certificates and public keys). Web of Trust is used. Semi-centralized.

While Sovereign Keys and Convergence still confer trust to outside parties, the parties involved do not serve share holder or have to protect revenue streams. Their interests are industry transparency and user security.

More Information?
Pinning is an old new thing that has been shaken, stirred, and repackaged. While "pinning" and "pinsets" are relatively new terms for old things, Jon Larimer and Kenny Root spent time on the subject at Google I/O 2012 with their Security and Privacy in Android Apps.