flutterdartpinningpublic-key-pinning

How to do SSL public key pinning in flutter/dart?


relatively new to Flutter here (and programming in general). Only familiar with the more basic stuffs but I've now encountered the need to use a CertificatePinner such as this in flutter/dart: https://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html (I've successfully implemented this in my previous kotlin/java project in android studio). My goal is to pin public key (not certificate)

All I have is the public key in the form of a string like shown below, nothing else: "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="

How do I go about achieving this? I've asked this in an open issue on github but haven't gotten any responses yet (https://github.com/dart-lang/sdk/issues/35981). Hoping someone has managed to achieve this.

I've also scoured through other sources. I think the closest one to a solution for me is How can I do public key pinning in Flutter? but I don't quite get what is being done there and I can't comment to ask questions there since I don't have enough reputation yet.

For comparison, all I want to do is achieve the same thing in flutter/dart what I could in java/kotlin with these few lines of code:

 String hostname = "publicobject.com";
 CertificatePinner certificatePinner = new CertificatePinner.Builder()
     .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
     .build();

Thanks for your help


Solution

  • Start with the code in the answer you refer to. That takes the certificate in DER format and starts decoding it.

    ASN1Parser p = ASN1Parser(der);
    ASN1Sequence signedCert = p.nextObject() as ASN1Sequence;
    ASN1Sequence cert = signedCert.elements[0] as ASN1Sequence;
    ASN1Sequence pubKeyElement = cert.elements[6] as ASN1Sequence;
    // this is the Subject Public Key element, which describes the type of key and actual value
    

    For example, if we decode the certificate of pub.dev we find that it's an RSA key with a modulus of 65537 and a value of 2347......:

    SEQUENCE (2 elem)
      SEQUENCE (2 elem)
        OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
        NULL
      BIT STRING (1 elem)
        SEQUENCE (2 elem)
          INTEGER (2048 bit) 234782553149463204049153749736864715384123240676730175743244004248041…
          INTEGER 65537
    

    From the RFC, the SPKI fingerprint is the SHA-256 hash of this whole element.

    // you need to import dart:convert and package:crypto/crypto.dart
    var hash = base64.encode(sha256.convert(pubKeyElement.contentBytes()).bytes);
    var spkiFingerprint = 'sha256/$hash'; // should match the value you have
    

    Caveats

    The badCertificateCallback doesn't deliver the whole certificate chain, so you can't walk up the whole chain. What's worse is that it doesn't always seem to deliver the leaf certificate! Sometimes it delivers an intermediate certificate.