On Sat, May 14, 2016 at 10:46 PM, Jesse Wilson <[email protected]> wrote: > I work on OkHttp, an Android HTTP client that implements HPKP-like > certificate pinning. We recently saw a problem where different versions of > Android returned different bytes for the ASN.1 encoding of the same > certificate. This is bad: the pins don’t match! > > The certificate of interest uses a named curve (secp521r1) with ECC. I used > der2ascii to view the SPKI ASN.1 bytes. > > Older versions of Android (which use BouncyCastle) embed the curve: > > SEQUENCE { > SEQUENCE { > # ecPublicKey > OBJECT_IDENTIFIER { 1.2.840.10045.2.1 } > SEQUENCE { > INTEGER { 1 } > SEQUENCE { > # prime-field > OBJECT_IDENTIFIER { 1.2.840.10045.1.1 } > INTEGER { `01ffffffffffffff...` } > } > SEQUENCE { > OCTET_STRING { `01ffffffffffffff...` } > OCTET_STRING { `0051953eb9618e1c...` } > } > OCTET_STRING { `0400c6858e06b704...` } > INTEGER { `01ffffffffffffff...` } > INTEGER { 1 } > } > } > BIT_STRING { `0004019519de800d...` } > } > > But new versions of Android (which use Conscrypt/OpenSSL) reference the > curve by name: > > SEQUENCE { > SEQUENCE { > # ecPublicKey > OBJECT_IDENTIFIER { 1.2.840.10045.2.1 } > # secp521r1 > OBJECT_IDENTIFIER { 1.3.132.0.35 } > } > BIT_STRING { `0004019519de800d...` } > } > > The original certificate embeds the curve. > > I believe my problem is that the Java APIs I’m using don’t return raw bytes > from the certificate. Instead Java decodes the certificate into a model and > re-encodes that when the bytes are requested. The original and roundtripped > SPKI bytes are not identical. > > Does anyone else do ASN.1 encoding in order to compute a certificate’s pin? > Or is it a uniquely Java problem?! > > The spec should warn that a single SPKI may have multiple conflicting > encodings. I suggest that only encoding in the certificate should be pinned. > TLS server administrators should also be careful to not inadvertently > re-encode their SPKIs when doing maintenance.
This is kind of expected. The problem is because the first certificate (older version of Android) uses domain parameters; while the second certificate (Conscrypt/OpenSSL) uses a named curve rather than domain parameters. I've experienced similar problems with OpenSSL servers and clients. In fact, if a named curve is *not used*, then you will encounter an error "no shared ciphers" under some circumstances. Also see http://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography#Named_Curves. A certificate simply binds a public key to an identity through a trusted party's signature. Once you verify the signature on the certificate, you should canonicalize the certificate data and then pin the normalizedd data. Jeff _______________________________________________ websec mailing list [email protected] https://www.ietf.org/mailman/listinfo/websec
