How Do We Generate a Base64-Encoded SHA256 Hash of SubjectPublicKeyInfo of an X.509 Certificate, for Android N Certificate Pinning?

Viewed 31570

The documentation in the N Developer Preview for their network security configuration offers these instructions:

Certificate pinning is done by providing a set of certificates by hash of the public key (SubjectPublicKeyInfo of the X.509 certificate). A certificate chain is then only valid if the certificate chain contains at least one of the pinned public keys.

The XML that they show is broken (missing a closing tag), but otherwise suggests that the hash is SHA256 and encoded base64:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
    </domain-config>
</network-security-config>

How do we create such a hash?

I tried the approach in this gist, but openssl x509 -inform der -pubkey -noout is not liking my CRT file. I cannot readily determine if the problem is in the CRT file, the instructions, my version of openssl, or something else.

Does anyone have a known good recipe for creating this hash?

4 Answers

For setting up Android network-security-config pinning for a host that is already live, I prefer gnutls-cli (GnuTLS Client). It outputs a host's certificate info in a form where the sha256 is readibly copy-pasteable as base64 encoded. For example:

$ gnutls-cli stackoverflow.com </dev/null
<...>
 - subject `CN=*.stackexchange.com,O=Stack Exchange\, Inc.,L=New York,ST=NY,C=US', issuer `CN=DigiCert SHA2 High Assurance Server CA,OU=www.digicert.com,O=DigiCert Inc,C=US', serial 0x0e11bbd70d54b710d0c6f540b6b52ca4, RSA key 2048 bits, signed using RSA-SHA256, activated `2016-05-21 00:00:00 UTC', expires `2019-08-14 12:00:00 UTC', pin-sha256="2zKehMv7KtnGBz1d2U0bFrAOKb1aWWlrG9a0BzrOvwA="

For anyone who needs to pin the entire certificate chain these are the steps I took, on a Windows 10 machine that luckily has Anaconda installed.

  1. Save the certificate file from Chrome or Edge. Choosing the option to save as (.P7B) and make sure check "Include all certificates.."

  2. Double click the file. This should launch windows certmgr.

  3. You should now see the cert chain entries once you click on Certificates

  4. Right click on each cert chain entry. Choose all tasks and export.

  5. Choose DER encoded (.CER)

  6. Repeat for each ( noting hierarchy)

  7. Start an Anaconda Prompt.

  8. Now run @Alex Klyubin / @Gal Rom (above) excellent commands for each file.

    openssl x509 -inform DER -in prod_root.cer -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

  9. For each of the file produced. The output will the sha256 desired

Hope this helps someone. I was very stuck trying to extract the whole chain. I am sure there are better ways to do this but it has worked.

Use this to get base64 sha256 of online https url:

openssl s_client -connect google.com:443 < /dev/null 2>/dev/null | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
Related