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.
This commit is contained in:
parent
7010ad85c5
commit
88060e7501
6 changed files with 216 additions and 9 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -35,6 +35,7 @@ TARG=\
|
|||
userpasswd\
|
||||
warning\
|
||||
wrkey\
|
||||
x5092pub\
|
||||
|
||||
DIRS=\
|
||||
factotum\
|
||||
|
|
63
sys/src/cmd/auth/x5092pub.c
Normal file
63
sys/src/cmd/auth/x5092pub.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue