]> git.lizzy.rs Git - plan9front.git/commitdiff
libsec: add X509reqtoRSApub() function and return subject alt names in X509to*pub...
authorcinap_lenrek <cinap_lenrek@felloff.net>
Sun, 4 Jul 2021 22:00:24 +0000 (22:00 +0000)
committercinap_lenrek <cinap_lenrek@felloff.net>
Sun, 4 Jul 2021 22:00:24 +0000 (22:00 +0000)
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.

sys/include/ape/libsec.h
sys/include/libsec.h
sys/man/2/rsa
sys/src/cmd/auth/mkfile
sys/src/cmd/auth/x5092pub.c [new file with mode: 0644]
sys/src/libsec/port/x509.c

index d44ff0cde599feb3423c7bd744355d633aa627fd..09ae55c72ecce4cb4b8867f8857f2784f20e03ee 100644 (file)
@@ -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);
index bebcc98fe6be92b54cd6e8ae1377725c6e120d03..884ff4c1024e28872674f6d849a6519c13b0dba2 100644 (file)
@@ -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);
index 627991e18240d9699a1e79140dd31aac47242591..b44f18bd6f6c69fac4376d1d7039b89a3ef67c16 100644 (file)
@@ -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
index 833e257be66ad3a519ce1f2e467bba8eae0c9989..bf5dbff2d801c9c7f71b967f5a6e9288a6510439 100644 (file)
@@ -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 (file)
index 0000000..2b9ef20
--- /dev/null
@@ -0,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+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);
+}
index aaaa7fb3ee041aaee165ad5e3d4789a0fcbd9158..d0e6b1fbca67d6f923bf1f359d6f5a6231d15ac8 100644 (file)
@@ -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)
 {