From: cinap_lenrek Date: Sun, 4 Jul 2021 22:00:24 +0000 (+0000) Subject: libsec: add X509reqtoRSApub() function and return subject alt names in X509to*pub... X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=88060e7501de5c117b86e1d29bc24ec8e83141a8;p=plan9front.git libsec: add X509reqtoRSApub() function and return subject alt names in X509to*pub() name buffer We need a way to parse a rsa certificate request and return the public key and subject names. The new function X509reqtoRSApub() works the same way as X509toRSApub() but on a certificate request. We also need to support certificates that are valid for multiple domain names (as tlshand does not support certificate selection). For this reason, a comma separated list is returned as the certificate subject, making it symmetric to X509rsareq() handling. A little helper is provided with this change (auth/x5092pub) that takes a certificate (or a certificate request when -r flag is provided) and outputs the RSA public key in plan 9 format appended with the subject attribute. --- diff --git a/sys/include/ape/libsec.h b/sys/include/ape/libsec.h index d44ff0cde..09ae55c72 100644 --- a/sys/include/ape/libsec.h +++ b/sys/include/ape/libsec.h @@ -365,6 +365,7 @@ RSApriv* rsaprivalloc(void); void rsaprivfree(RSApriv*); RSApub* rsaprivtopub(RSApriv*); RSApub* X509toRSApub(uchar*, int, char*, int); +RSApub* X509reqtoRSApub(uchar*, int, char*, int); RSApriv* asn1toRSApriv(uchar*, int); RSApub* asn1toRSApub(uchar*, int); void asn1dump(uchar *der, int len); diff --git a/sys/include/libsec.h b/sys/include/libsec.h index bebcc98fe..884ff4c10 100644 --- a/sys/include/libsec.h +++ b/sys/include/libsec.h @@ -357,6 +357,7 @@ RSApriv* rsaprivalloc(void); void rsaprivfree(RSApriv*); RSApub* rsaprivtopub(RSApriv*); RSApub* X509toRSApub(uchar*, int, char*, int); +RSApub* X509reqtoRSApub(uchar*, int, char*, int); RSApub* asn1toRSApub(uchar*, int); RSApriv* asn1toRSApriv(uchar*, int); void asn1dump(uchar *der, int len); diff --git a/sys/man/2/rsa b/sys/man/2/rsa index 627991e18..b44f18bd6 100644 --- a/sys/man/2/rsa +++ b/sys/man/2/rsa @@ -15,6 +15,7 @@ rsaprivtopub, rsapuballoc, rsapubfree, X509toRSApub, +X509reqtoRSApub, X509rsagen, X509rsareq, X509rsaverify, @@ -61,6 +62,9 @@ RSApub* rsaprivtopub(RSApriv*) RSApub* X509toRSApub(uchar *cert, int ncert, char *name, int nname) .PP .B +RSApub* X509reqtoRSApub(uchar *req, int nreq, char *name*, int nname) +.PP +.B RSApriv* asn1toRSApriv(uchar *priv, int npriv) .PP .B @@ -79,7 +83,7 @@ uchar* decodePEM(char *s, char *type, int *len, char **new_s) uchar* X509rsagen(RSApriv *priv, char *subj, ulong valid[2], int *certlen); .PP .B -uchar* X509rsareq(RSApriv *priv, char *subj, int *certlen); +uchar* X509rsareq(RSApriv *priv, char *subj, int *reqlen) .PP .B char* X509rsaverify(uchar *cert, int ncert, RSApub *pk) @@ -159,9 +163,10 @@ returns the public key and, if .I name is not .BR nil , -the CN part of the Distinguished Name of the -certificate's Subject. -(This is conventionally a userid or a host DNS name.) +a concatenation of the CN part of the Distinguished Name of the +certificate's Subject and further Subject Alternative Names +separated by comma. +(These are conventionally a userid or a host DNS name.) No verification is done of the certificate signature; the caller should check the fingerprint, .IR sha1(cert) , @@ -180,6 +185,12 @@ It returns .B nil if successful, else an error string. .PP +The routine +.I X509reqtoRSApub +is similar to +.I X509toRSApub +above, but decodes a X509 certificate request. +.PP .I X509rsaverifydigest takes a encoded PKCS #1 signature as used in X.509 as .IR sig [ siglen ] @@ -197,8 +208,8 @@ a issuer/subject string .IR subj , and the starting and ending validity dates, .IR valid . -Length of the allocated binary certificate is stored in -.IR certlen . +Length of the allocated binary certificate request is stored in +.IR reqlen . The subject line is conventionally of the form .IP .EX diff --git a/sys/src/cmd/auth/mkfile b/sys/src/cmd/auth/mkfile index 833e257be..bf5dbff2d 100644 --- a/sys/src/cmd/auth/mkfile +++ b/sys/src/cmd/auth/mkfile @@ -35,6 +35,7 @@ TARG=\ userpasswd\ warning\ wrkey\ + x5092pub\ DIRS=\ factotum\ diff --git a/sys/src/cmd/auth/x5092pub.c b/sys/src/cmd/auth/x5092pub.c new file mode 100644 index 000000000..2b9ef2067 --- /dev/null +++ b/sys/src/cmd/auth/x5092pub.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +int fd; +int req = 0; +char subject[1024]; + +void +usage(void) +{ + fprint(2, "usage: aux/x5092pub [-r] [file]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int tot, n; + uchar *buf; + RSApub *pub; + + quotefmtinstall(); + fmtinstall('B', mpfmt); + fmtinstall('H', encodefmt); + + ARGBEGIN{ + case 'r': + req = 1; + break; + default: + usage(); + }ARGEND + + fd = 0; + if(argc == 1) + fd = open(argv[0], OREAD); + else if(argc != 0) + usage(); + buf = nil; + tot = 0; + for(;;){ + buf = realloc(buf, tot+8192); + if(buf == nil) + sysfatal("realloc: %r"); + if((n = read(fd, buf+tot, 8192)) < 0) + sysfatal("read: %r"); + if(n == 0) + break; + tot += n; + } + if(req) + pub = X509reqtoRSApub(buf, tot, subject, sizeof(subject)); + else + pub = X509toRSApub(buf, tot, subject, sizeof(subject)); + if(pub == nil) + sysfatal("X509toRSApub: %r"); + print("key proto=rsa size=%d ek=%B n=%B subject=%q \n", mpsignif(pub->n), pub->ek, pub->n, subject); + exits(nil); +} diff --git a/sys/src/libsec/port/x509.c b/sys/src/libsec/port/x509.c index aaaa7fb3e..d0e6b1fbc 100644 --- a/sys/src/libsec/port/x509.c +++ b/sys/src/libsec/port/x509.c @@ -1589,6 +1589,7 @@ typedef struct CertX509 { int signature_alg; Bits* signature; int curve; + Bytes* ext; } CertX509; /* Algorithm object-ids */ @@ -1724,6 +1725,8 @@ static void (*namedcurves[])(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, m nil, }; +static void appendaltnames(char *name, int nname, Bytes *ext, int req); + static void freecert(CertX509* c) { @@ -1735,6 +1738,7 @@ freecert(CertX509* c) free(c->subject); freebits(c->publickey); freebits(c->signature); + freebytes(c->ext); free(c); } @@ -1863,6 +1867,7 @@ decode_cert(uchar *buf, int len) c->publickey = nil; c->signature_alg = -1; c->signature = nil; + c->ext = nil; /* Certificate */ if(!is_seq(&ecert, &elcert) || elistlen(elcert) !=3) @@ -1900,6 +1905,10 @@ decode_cert(uchar *buf, int len) esubj = &el->hd; el = el->tl; epubkey = &el->hd; + if(el->tl != nil && el->tl->hd.tag.class == Context && el->tl->hd.tag.num == 3){ + c->ext = el->tl->hd.val.u.octetsval; + el->tl->hd.val.u.octetsval = nil; /* transfer ownership */ + } if(!is_int(eserial, &c->serial)) { if(!is_bigint(eserial, &b)) goto errret; @@ -2260,6 +2269,7 @@ X509toECpub(uchar *cert, int ncert, char *name, int nname, ECdomain *dom) if(c == nil) return nil; copysubject(name, nname, c->subject); + appendaltnames(name, nname, c->ext, 0); pub = nil; if(c->publickey_alg == ALG_ecPublicKey){ ecdominit(dom, namedcurves[c->curve]); @@ -2302,6 +2312,7 @@ X509toRSApub(uchar *cert, int ncert, char *name, int nname) if(c == nil) return nil; copysubject(name, nname, c->subject); + appendaltnames(name, nname, c->ext, 0); pub = nil; if(c->publickey_alg == ALG_rsaEncryption) pub = asn1toRSApub(c->publickey->data, c->publickey->len); @@ -2644,7 +2655,7 @@ static Ints15 oid_subjectAltName = {4, 2, 5, 29, 17 }; static Ints15 oid_extensionRequest = { 7, 1, 2, 840, 113549, 1, 9, 14}; static Elist* -mkextensions(char *alts, int req) +mkextensions(char *alts, int isreq) { Elist *sl, *xl; @@ -2653,12 +2664,12 @@ mkextensions(char *alts, int req) xl = mkextel(mkseq(sl), (Ints*)&oid_subjectAltName, xl); if(xl != nil){ xl = mkel(mkseq(xl), nil); - if(req) + if(isreq) xl = mkel(mkseq( mkel(mkoid((Ints*)&oid_extensionRequest), mkel(mkset(xl), nil))), nil); } - if(req) + if(isreq) xl = mkel(mkcont(0, xl), nil); else if(xl != nil) xl = mkel(mkcont(3, xl), nil); @@ -2681,6 +2692,85 @@ splitalts(char *s) return nil; } +static void +appendaltnames(char *name, int nname, Bytes *ext, int isreq) +{ + Elem eext, ealt, edn; + Elist *el, *l; + Ints *oid; + char *alt; + int len; + + if(name == nil || ext == nil) + return; + if(decode(ext->data, ext->len, &eext) != ASN_OK) + return; + if(isreq){ + if(!is_seq(&eext, &el) || elistlen(el) != 2) + goto errext; + if(!is_oid(&el->hd, &oid) || !ints_eq(oid, (Ints*)&oid_extensionRequest)) + goto errext; + el = el->tl; + if(!is_set(&el->hd, &el)) + goto errext; + if(!is_seq(&el->hd, &el)) + goto errext; + } else { + if(!is_seq(&eext, &el)) + goto errext; + } + for(; el != nil; el = el->tl){ + if(!is_seq(&el->hd, &l) || elistlen(l) != 2) + goto errext; + if(!is_oid(&l->hd, &oid) || !ints_eq(oid, (Ints*)&oid_subjectAltName)) + continue; + el = l->tl; + break; + } + if(el == nil) + goto errext; + if(!is_octetstring(&el->hd, &ext)) + goto errext; + if(decode(ext->data, ext->len, &ealt) != ASN_OK) + goto errext; + if(!is_seq(&ealt, &el)) + goto erralt; + for(; el != nil; el = el->tl){ + ext = el->hd.val.u.octetsval; + switch(el->hd.tag.num){ + default: + continue; + case 1: /* email */ + case 2: /* DNS */ + if(ext == nil) + goto erralt; + alt = smprint("%.*s", ext->len, (char*)ext->data); + break; + case 4: /* DN */ + if(ext == nil || decode(ext->data, ext->len, &edn) != ASN_OK) + goto erralt; + alt = parse_name(&edn); + freevalfields(&edn.val); + break; + } + if(alt == nil) + goto erralt; + len = strlen(alt); + if(strncmp(name, alt, len) == 0 && strchr(",", name[len]) == nil){ + free(alt); /* same as the subject */ + continue; + } + if(name[0] != '\0') + strncat(name, ", ", nname-1); + strncat(name, alt, nname-1); + free(alt); + } +erralt: + freevalfields(&ealt.val); +errext: + freevalfields(&eext.val); +} + static Bytes* encode_rsapubkey(RSApub *pk) { @@ -2885,6 +2975,46 @@ errret: return cert; } +RSApub* +X509reqtoRSApub(uchar *req, int nreq, char *name, int nname) +{ + Elem ereq; + Elist *el; + char *subject; + Bits *bits; + RSApub *pub; + + pub = nil; + if(decode(req, nreq, &ereq) != ASN_OK) + goto errret; + if(!is_seq(&ereq, &el) || elistlen(el) != 3) + goto errret; + if(!is_seq(&el->hd, &el) || elistlen(el) < 3) + goto errret; + el = el->tl; + subject = parse_name(&el->hd); + if(subject == nil) + goto errret; + copysubject(name, nname, subject); + free(subject); + el = el->tl; + if(el->tl != nil && el->tl->hd.tag.class == Context && el->tl->hd.tag.num == 0) + appendaltnames(name, nname, el->tl->hd.val.u.octetsval, 1); + if(!is_seq(&el->hd, &el) || elistlen(el) != 2) + goto errret; + if(parse_alg(&el->hd) != ALG_rsaEncryption) + goto errret; + el = el->tl; + if(!is_bitstring(&el->hd, &bits)) + goto errret; + pub = asn1toRSApub(bits->data, bits->len); + if(pub == nil) + goto errret; +errret: + freevalfields(&ereq.val); + return pub; +} + static void digestSPKI(int alg, uchar *pubkey, int npubkey, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest) {