From 57f8b6ec7591007ff22627038b51c4f4aa2a9be8 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sat, 30 Dec 2017 03:07:47 +0100 Subject: [PATCH] libsec: implement SPKI fingerprinting for okCertificate() Instead of only using a hash over the whole certificate for white/black-listing, now we can also use a hash over the Subject Public Key Info (SPKI) field of the certificate which contians the public key algorithm and the public key itself. This allows certificates to be renewed independendtly of the public key. X509dump() now prints the public key thumbprint in addition to the certificate thumbprint. tlsclient will print the certificate when run with -D flag. okCertificate() will print the public key thumbprint in its error string when no match has been found. --- sys/include/ape/libsec.h | 4 +++- sys/include/libsec.h | 1 + sys/man/8/tlssrv | 5 ++--- sys/src/cmd/tlsclient.c | 5 +++++ sys/src/libsec/port/thumb.c | 5 +++++ sys/src/libsec/port/x509.c | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 4 deletions(-) diff --git a/sys/include/ape/libsec.h b/sys/include/ape/libsec.h index bd4b352f7..610e69e28 100644 --- a/sys/include/ape/libsec.h +++ b/sys/include/ape/libsec.h @@ -382,7 +382,9 @@ int pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype); int asn1encodeRSApub(RSApub *pk, uchar *buf, int len); int asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest, uchar *buf, int len); - + +int X509digestSPKI(uchar *, int, DigestState* (*)(uchar*, ulong, uchar*, DigestState*), uchar *); + /* * elgamal */ diff --git a/sys/include/libsec.h b/sys/include/libsec.h index b1616782f..f9c75306b 100644 --- a/sys/include/libsec.h +++ b/sys/include/libsec.h @@ -375,6 +375,7 @@ int asn1encodeRSApub(RSApub *pk, uchar *buf, int len); int asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest, uchar *buf, int len); +int X509digestSPKI(uchar *, int, DigestState* (*)(uchar*, ulong, uchar*, DigestState*), uchar *); /* * elgamal diff --git a/sys/man/8/tlssrv b/sys/man/8/tlssrv index 462be928b..9ebaa0104 100644 --- a/sys/man/8/tlssrv +++ b/sys/man/8/tlssrv @@ -134,9 +134,8 @@ flag (and, optionally, the .B -x flag) -is given, the remote server must present a key -whose SHA1 hash is listed in -the file +is given, the remote server must present a public key +whose SHA1 or SHA256 hash is listed in the file .I trustedkeys but not in the file .IR excludedkeys . diff --git a/sys/src/cmd/tlsclient.c b/sys/src/cmd/tlsclient.c index 1b79fb739..7be56c279 100644 --- a/sys/src/cmd/tlsclient.c +++ b/sys/src/cmd/tlsclient.c @@ -49,6 +49,8 @@ main(int argc, char **argv) Thumbprint *thumb; AuthInfo *ai = nil; + fmtinstall('B', mpfmt); + fmtinstall('[', encodefmt); fmtinstall('H', encodefmt); ARGBEGIN{ @@ -122,6 +124,9 @@ main(int argc, char **argv) if(fd < 0) sysfatal("tlsclient: %r"); + if(debug) + X509dump(conn->cert, conn->certlen); + if(thumb){ if(!okCertificate(conn->cert, conn->certlen, thumb)) sysfatal("cert for %s not recognized: %r", servername ? servername : addr); diff --git a/sys/src/libsec/port/thumb.c b/sys/src/libsec/port/thumb.c index 39757939b..73add8db1 100644 --- a/sys/src/libsec/port/thumb.c +++ b/sys/src/libsec/port/thumb.c @@ -66,6 +66,11 @@ okCertificate(uchar *cert, int len, Thumbprint *table) if(okThumbprint(hash, SHA2_256dlen, table)) return 1; + if(X509digestSPKI(cert, len, sha2_256, hash) < 0) + return 0; + if(okThumbprint(hash, SHA2_256dlen, table)) + return 1; + len = enc64(thumb, sizeof(thumb), hash, SHA2_256dlen); while(len > 0 && thumb[len-1] == '=') len--; diff --git a/sys/src/libsec/port/x509.c b/sys/src/libsec/port/x509.c index e5b92039c..2ec9d9551 100644 --- a/sys/src/libsec/port/x509.c +++ b/sys/src/libsec/port/x509.c @@ -2897,6 +2897,32 @@ errret: return cert; } +static void +digestSPKI(int alg, uchar *pubkey, int npubkey, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest) +{ + Bytes *b = nil; + Elem e = mkseq(mkel(mkalg(alg), mkel(mkbits(pubkey, npubkey), nil))); + encode(e, &b); + freevalfields(&e.val); + (*fun)(b->data, b->len, digest, nil); + freebytes(b); +} + +int +X509digestSPKI(uchar *cert, int ncert, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest) +{ + CertX509 *c; + + c = decode_cert(cert, ncert); + if(c == nil){ + werrstr("cannot decode cert"); + return -1; + } + digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, fun, digest); + freecert(c); + return 0; +} + static char* tagdump(Tag tag) { @@ -3047,6 +3073,16 @@ X509dump(uchar *cert, int ncert) ecdomfree(&ecdom); break; } + + digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, sha2_256, digest); + print("publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, digest); + + sha2_256(cert, ncert, digest, nil); + print("cert_thumbprint sha256=%.*[\n", SHA2_256dlen, digest); + + sha1(cert, ncert, digest, nil); + print("cert_thumbprint sha1=%.*H\n", SHA1dlen, digest); + freecert(c); print("end X509dump\n"); } -- 2.44.0