📄 pgpx509keys.c
字号:
}
/*
* Convert an RFC2253 LDAP-format string to an allocated Distinguished
* Name buffer. Caller should do PGPFreeData on *pdname when done. Any
* quoting used in input must be double quotes around the whole value,
* as in O="Network Associates, Inc.".
*/
PGPError
PGPCreateDistinguishedName( PGPContextRef context, char const *str,
PGPByte **pdname)
{
PGPMemoryMgrRef mgr;
PGPASN_CONTEXT asnctx_static, *asnctx = &asnctx_static;
PGPASN_MemoryMgr asnmem;
PGPASN_Name *name = NULL;
PGPByte *dname = NULL;
PGPSize dnamesize;
PGPError err = kPGPError_NoErr;
*pdname = NULL;
mgr = PGPGetContextMemoryMgr( context );
sSetupASNCONTEXT (mgr, asnctx, &asnmem);
err = sEncodeDN( mgr, asnctx, str, &name );
if( IsPGPError( err ) )
goto error;
dnamesize = pgpasn_SizeofName (asnctx, name, TRUE);
dname = PGPNewData( mgr, dnamesize, 0 );
CHKNONNULL( dname, err );
pgpasn_PackName( asnctx, dname, dnamesize, name, &err );
CHKASNERR( err );
pgpasn_FreeName( asnctx, name );
*pdname = dname;
return kPGPError_NoErr;
error:
if( name )
pgpasn_FreeName( asnctx, name );
if( dname )
PGPFreeData( dname );
return err;
}
#endif
/*
* Add backslashes as needed for X.509 LDAP format conversion
* Allocate s2 to be twice as big as s1, for safety.
*/
static void
sEncodeLDAPString( const char *s1, char *s2 )
{
PGPBoolean inQuotes = FALSE;
PGPBoolean afterBackslash = FALSE;
char c;
while ((c = *s1++) != '\0') {
if (c == '"' && !afterBackslash)
inQuotes = !inQuotes;
if (inQuotes) {
if (c == '=' || c == '\\' || c == ',' || c == '+' ||
c == '<' || c == '>' || c == ';' || c == ' ' || c == '#')
*s2++ = '\\';
}
if (((c != '\\' || !inQuotes) && c != '"') || afterBackslash)
*s2++ = c;
afterBackslash = (c == '\\' && !afterBackslash);
}
*s2++ = '\0';
}
/*
* Convert an RFC2253 LDAP-format string to an allocated Distinguished
* Name buffer. Caller should do PGPFreeData on *pdname when done.
*/
PGPError
PGPCreateDistinguishedName( PGPContextRef context, char const *str,
PGPByte **pdname, PGPSize *pdnamelen )
{
PGPByte *dname = NULL;
PGPSize dnamelen;
char *str2 = NULL;
PGPError err = kPGPError_NoErr;
PGPMemoryMgrRef mgr = PGPGetContextMemoryMgr( context );
PGPValidatePtr( pdname );
PGPValidatePtr( pdnamelen );
*pdname = NULL;
*pdnamelen = 0;
str2 = (char *)PGPNewData( mgr, 2*strlen(str) + 1, 0 );
CHKNONNULL( str2, err );
sEncodeLDAPString( str, str2 );
err = X509CreateDistinguishedName( context, str2, &dname, &dnamelen );
if( IsPGPError( err ) )
goto error;
*pdname = dname;
*pdnamelen = dnamelen;
error:
if( IsPGPError( err ) && IsntNull(dname) )
PGPFreeData( dname );
if( str2 )
PGPFreeData( str2 );
return err;
}
/*
* Given an X509 integer (pointing past the tag/length), return a PGP
* format MPI number. If the mpbuf pointer is NULL, just return the len.
* If non-NULL, *mplen must equal the required length.
*/
static PGPError
sDecodeX509Integer (PGPByte const *buf, PGPSize len,
PGPByte *mpbuf, PGPSize *mplen)
{
PGPUInt32 bits;
/* Skip past any zero padding */
while (*buf == '\0' && len > 0) {
++buf;
--len;
}
/* Must be at least 1 byte long */
if (len == 0)
return kPGPError_X509InvalidCertificateFormat;
/* Set MPI with bitcount prefix */
if( IsNull( mpbuf ) ) {
*mplen = len + 2;
return kPGPError_NoErr;
}
pgpAssert (len + 2 == *mplen);
bits = (len-1) * 8 + hibit(*buf) + 1;
mpbuf[0] = (PGPByte)(bits >> 8);
mpbuf[1] = (PGPByte)(bits >> 0);
pgpCopyMemory( buf, mpbuf+2, len);
return kPGPError_NoErr;
}
/*
* Decode the given UTCTime buffer into a standard PGP timestamp
* If generalized is true, it is a GeneralizedTime structure, four digit
* years.
*/
static PGPError
sDecodeUTCTime (PGPByte const *buf, PGPSize len, PGPBoolean generalized,
PGPUInt32 *timestamp)
{
PGPUInt32 datefield[6]; /* year, month, day, hour, min, second */
PGPUInt32 i;
PGPUInt32 tstamp;
PGPError err = kPGPError_NoErr;
pgpAssert( IsntNull( timestamp ) );
*timestamp = 0;
pgpClearMemory (datefield, sizeof(datefield));
for (i=0; i<elemsof(datefield); ++i) {
if (len < 2 || !isdigit(buf[0]))
break;
if (!isdigit(buf[1])) {
err = kPGPError_X509InvalidCertificateFormat;
goto error;
}
datefield[i] = (buf[0]-'0') * 10 + (buf[1] - '0');
buf += 2;
len -= 2;
if (i == 0 && generalized) {
/* Two more digits */
datefield[i] = datefield[i] * 100 + (buf[0]-'0') * 10
+ (buf[1] - '0');
buf += 2;
len -= 2;
}
}
if (len != 1 || buf[0] != 'Z' || i < 5) {
err = kPGPError_X509InvalidCertificateFormat;
goto error;
}
/* Correct for Y2K conventions on UTCTime */
if (!generalized)
datefield[0] += (datefield[0] > 50) ? 1900 : 2000;
/* Get seconds since Jan 1, 1970 */
tstamp = pgpDateFromYMD (datefield[0], datefield[1], datefield[2]);
tstamp = (((tstamp*24 + datefield[3]) * 60) + datefield[4]) * 60
+ datefield[5];
*timestamp = tstamp;
error:
return err;
}
/*
* TRUE if given DN string needs quoting per RFC1779.
* Also return the number of internal backslashes needed.
*/
static PGPBoolean
sNeedsDNQuoting(PGPByte const *buf, PGPSize length, PGPUInt32 *nslashes)
{
PGPByte c='\0', prevc='\0';
PGPUInt32 nslash = 0;
PGPBoolean outerquote = FALSE;
if (*buf==' ')
outerquote = TRUE;
while (length--) {
prevc = c;
c = *buf++;
if (c==',' || c=='=' || c=='\r' || c=='+' || c=='<'
|| c=='>' || c=='#' || c==';')
outerquote = TRUE;
if (c==' ' && prevc==' ')
outerquote = TRUE;
if (c=='"' || c=='\\')
++nslash;
}
if (prevc==' ')
outerquote = TRUE;
if (IsntNull (nslashes) )
*nslashes = nslash;
return outerquote;
}
/*
* If out is non-NULL, apply RFC1779 DNS quoting to buf, outputting to out.
* In either case, return the length of what the output string would be.
* If the rev flag is set, out points at the end of the output buffer rather
* than at the beginning.
*/
static PGPUInt32
sDNQuoteString(PGPByte const *buf, PGPSize len, PGPByte *out, PGPBoolean rev)
{
PGPUInt32 outlen;
PGPBoolean needouterquote;
PGPByte c;
needouterquote = sNeedsDNQuoting( buf, len, &outlen);
outlen += len + (needouterquote ? 2 : 0);
if (IsNull( out ))
return outlen;
if (rev)
out -= outlen;
if (needouterquote)
*out++ = '"';
while (len--) {
c = *buf++;
if (c=='"' || c=='\\')
*out++ = '\\';
*out++ = c;
}
if (needouterquote)
*out++ = '"';
return outlen;
}
/*
* Called pointing at the OID for an algorithm identifier, see if it is one
* we know.
*/
static PGPError
sDecodeAlgorithmOID (PGPASN_CONTEXT *asnctx, PGPASN_OBJECT_ID *oid,
PGPPublicKeyAlgorithm *keyalgorithm, PGPHashAlgorithm *hashalgorithm)
{
PGPByte *buf = oid->val;
PGPSize len = oid->len;
PGPError err = kPGPError_NoErr;
(void) asnctx;
if( IsntNull( keyalgorithm ) )
*keyalgorithm = (PGPPublicKeyAlgorithm) 0;
if( IsntNull( hashalgorithm ) )
*hashalgorithm = (PGPHashAlgorithm) 0;
if (len == sizeof(rsaoid) && memcmp (buf, rsaoid, len) == 0) {
if( IsntNull( keyalgorithm ) )
*keyalgorithm = kPGPPublicKeyAlgorithm_RSA;
} else if (len == sizeof(rsamd5oid) && memcmp (buf, rsamd5oid, len) == 0) {
if( IsntNull( hashalgorithm ) )
*hashalgorithm = kPGPHashAlgorithm_MD5;
if( IsntNull( keyalgorithm ) )
*keyalgorithm = kPGPPublicKeyAlgorithm_RSA;
} else if (len == sizeof(rsamd2oid) && memcmp (buf, rsamd2oid, len) == 0) {
if( IsntNull( hashalgorithm ) )
*hashalgorithm = (PGPHashAlgorithm) kPGPHashAlgorithm_MD2;
if( IsntNull( keyalgorithm ) )
*keyalgorithm = kPGPPublicKeyAlgorithm_RSA;
} else if (len == sizeof(rsashaoid) && memcmp (buf, rsashaoid, len) == 0) {
if( IsntNull( hashalgorithm ) )
*hashalgorithm = (PGPHashAlgorithm) kPGPHashAlgorithm_SHA;
if( IsntNull( keyalgorithm ) )
*keyalgorithm = kPGPPublicKeyAlgorithm_RSA;
} else if (len == sizeof(dsaoid) && memcmp (buf, dsaoid, len) == 0) {
if( IsntNull( keyalgorithm ) )
*keyalgorithm = kPGPPublicKeyAlgorithm_DSA;
} else if ((len == sizeof(dsashaoid1) && memcmp (buf,dsashaoid1,len) == 0)
|| (len == sizeof(dsashaoid2) && memcmp (buf,dsashaoid2,len) == 0)) {
if( IsntNull( hashalgorithm ) )
*hashalgorithm = kPGPHashAlgorithm_SHA;
if( IsntNull( keyalgorithm ) )
*keyalgorithm = kPGPPublicKeyAlgorithm_DSA;
} else if (len == sizeof(elgoid) && memcmp (buf, elgoid, len) == 0) {
if( IsntNull( keyalgorithm ) )
*keyalgorithm = kPGPPublicKeyAlgorithm_ElGamal;
} else {
err = kPGPError_X509InvalidCertificateFormat;
}
return err;
}
/*
* Look for IP address or DNS name in subject alternative name extension
* If ipbuf is NULL, set *iplength to necessary length; else store the
* IP data into ipbuf, and treat *iplength as an input parameter telling
* what the length needs to be. (Presumably *iplength is always 4?)
* If dnsbuf is NULL, set *dnslength to necessary length; else store the
* DNS name into dnsbuf, and treat *dnslength as an input parameter telling
* what the length needs to be.
* If there is more than one IP or DNS we just return the first of each
* type.
*/
static PGPError
sDecodeIPDNSName(PGPASN_CONTEXT *asnctx, PGPASN_Certificate *cert,
PGPByte *ipbuf, PGPSize *iplength,
PGPByte *dnsbuf, PGPSize *dnslength)
{
PGPASN_Extensions *exts;
PGPASN_Extension *ext;
PGPASN_GeneralNames *gnames;
PGPASN_GeneralName *gname;
PGPASN_VariableBlock *vb;
PGPBoolean seenIP=FALSE, seenDNS=FALSE;
PGPInt32 i, j;
PGPError err = kPGPError_NoErr;
if (IsNull( ipbuf ) && IsntNull( iplength ) )
*iplength = 0;
if (IsNull( dnsbuf ) && IsntNull( dnslength ) )
*dnslength = 0;
exts = cert->tbsCertificate.extensions;
if (IsNull(exts))
return err;
/* Check extensions for subject alternative name */
for (i=0; i<exts->n && (!seenIP || !seenDNS); ++i) {
ext = exts->elt[i];
/* These are same size */
if (ext->extnID.len == sizeof(subaltnameoid)
&& memcmp(ext->extnID.val, subaltnameoid, ext->extnID.len)==0 ) {
/* Subject alternative name, just as we wanted */
pgpasn_UnpackGeneralNames( asnctx, &gnames, ext->extnValue.val,
ext->extnValue.len, &err );
if (err != 0) {
/* Skip if can't handle */
err = 0;
continue;
}
for (j=0; j<gnames->n && (!seenIP || !seenDNS); ++j) {
gname = gnames->elt[j];
if (gname->CHOICE_field_type == ASN_GENERALNAME_IPADDRESS
&& !seenIP) {
seenIP = TRUE;
vb = gname->data;
if (IsNull( ipbuf ) ) {
if( IsntNull( iplength ) )
*iplength = vb->len;
} else {
pgpCopyMemory( vb->val, ipbuf, vb->len );
}
} else if (gname->CHOICE_field_type == ASN_GENERALNAME_DNSNAME
&& !seenDNS) {
seenDNS = TRUE;
vb = gname->data;
if (IsNull( dnsbuf ) ) {
if( IsntNull( dnslength ) )
*dnslength = vb->len;
} else {
pgpCopyMemory( vb->val, dnsbuf, vb->len );
}
}
}
pgpasn_FreeGeneralNames( asnctx, gnames );
}
}
return err;
}
/*
* Convert from X.509 cert to PGP signature validity subpackets.
* If vbuf is NULL, set *vlength to the necessary length. Else
* store the data into vbuf, and treat *vlength as an input parameter
* telling what the length needs to be.
* We create a pair of PGP signature subpackets expressing creation and
* expiration dates.
*/
static PGPError
sDecodeValidity(PGPASN_CONTEXT *asnctx, PGPASN_Certificate *cert,
PGPByte *vbuf, PGPSize *vlength)
{
PGPASN_Validity *val = &cert->tbsCertificate.validity;
PGPASN_CertificateValidityDate *cvd;
PGPASN_UTCTime *utc;
PGPUInt32 creationtime;
PGPUInt32 expirationtime;
PGPUInt32 o;
PGPError err = kPGPError_NoErr;
(void)asnctx;
pgpAssert( IsntNull( vlength ) );
cvd = &val->notBefore;
utc = (PGPASN_UTCTime *)cvd->data;
err = sDecodeUTCTime (utc->val, utc->len,
(PGPBoolean)(cvd->CHOICE_field_type!=PGPASN_ID_UTCTime),
&creationtime);
if( IsPGPError( err ) )
goto error;
cvd = &val->notAfter;
utc = (PGPASN_UTCTime *)cvd->data;
err = sDecodeUTCTime (utc->val, utc->len,
(PGPBoolean)(cvd->CHOICE_field_type!=PGPASN_ID_UTCTime),
&expirationtime);
if( IsPGPError( err ) )
goto error;
if (expirationtime < creationtime) {
err = kPGPError_X509InvalidCertificateFormat;
goto error;
}
expirationtime -= creationtime;
/* Two subpackets, each with 1 byte of len, 1 byte of type, 4 of time */
if( IsNull( vbuf ) ) {
*vlength = 2 * 6;
return kPGPError_NoErr;
}
pgpAssert (*vlength == 2 * 6);
o = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -