/*
* Parse X.509 certificates and return contents as a TCL key-value list.
*
* Copyright (C) 1997-2000 Sensus Consulting Ltd.
* Matt Newman <[email protected]>
* Copyright (C) 2023 Brian O'Hagan
*/
#include <tcl.h>
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/x509_vfy.h>
#include <openssl/asn1.h>
#include "tlsInt.h"
/* Define maximum certificate size. Max PEM size 100kB and DER size is 24kB. */
#define CERT_STR_SIZE 24576
/*
*-----------------------------------------------------------------------------
*
* String_to_Hex --
*
* Format contents of a binary string as a hex string
*
* Results:
* TCL byte array object with x509 identifier as a hex string
*
* Side Effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *String_to_Hex(unsigned char* input, int ilen) {
unsigned char *iptr = input;
Tcl_Obj *resultObj = Tcl_NewByteArrayObj(NULL, 0);
unsigned char *data = Tcl_SetByteArrayLength(resultObj, (Tcl_Size)ilen*2);
unsigned char *dptr = &data[0];
const char *hex = "0123456789abcdef";
if (resultObj == NULL) {
return NULL;
}
for (int i = 0; i < ilen; i++) {
*dptr++ = hex[(*iptr>>4)&0xF];
*dptr++ = hex[(*iptr++)&0xF];
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* BIO_to_Buffer --
*
* Output contents of a BIO to a buffer
*
* Results:
* Returns length of string in buffer
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Size BIO_to_Buffer(int result, BIO *bio, void *output, int olen) {
Tcl_Size len = 0;
int pending = BIO_pending(bio);
if (result) {
len = (Tcl_Size) BIO_read(bio, output, (pending < olen) ? pending : olen);
(void)BIO_flush(bio);
if (len < 0) {
len = 0;
}
}
return len;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509Extensions --
*
* Get list of X.509 Certificate Extensions
*
* Results:
* TCL list of extensions and boolean critical status
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509Extensions(Tcl_Interp *interp, X509 *cert) {
const STACK_OF(X509_EXTENSION) *exts;
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
if (resultObj == NULL) {
return NULL;
}
if ((exts = X509_get0_extensions(cert)) != NULL) {
for (int i=0; i < X509_get_ext_count(cert); i++) {
X509_EXTENSION *ex = sk_X509_EXTENSION_value(exts, i);
ASN1_OBJECT *obj = X509_EXTENSION_get_object(ex);
/* ASN1_OCTET_STRING *data = X509_EXTENSION_get_data(ex); */
int critical = X509_EXTENSION_get_critical(ex);
LAPPEND_BOOL(interp, resultObj, OBJ_nid2ln(OBJ_obj2nid(obj)), critical);
}
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509Identifier --
*
* Get X.509 certificate Authority or Subject Key Identifiers
*
* Results:
* TCL byte array object with x509 identifier as a hex string
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509Identifier(const ASN1_OCTET_STRING *astring) {
Tcl_Obj *resultObj = NULL;
if (astring != NULL) {
resultObj = String_to_Hex((unsigned char *)ASN1_STRING_get0_data(astring),
ASN1_STRING_length(astring));
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509KeyUsage --
*
* Get X.509 certificate key usage types
*
* Results:
* Tcl list of types
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509KeyUsage(Tcl_Interp *interp, X509 *cert, uint32_t xflags) {
uint32_t usage = X509_get_key_usage(cert);
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
if (resultObj == NULL) {
return NULL;
}
if ((xflags & EXFLAG_KUSAGE) && usage < UINT32_MAX) {
if (usage & KU_DIGITAL_SIGNATURE) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Digital Signature", -1));
}
if (usage & KU_NON_REPUDIATION) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Non-Repudiation", -1));
}
if (usage & KU_KEY_ENCIPHERMENT) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Key Encipherment", -1));
}
if (usage & KU_DATA_ENCIPHERMENT) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Data Encipherment", -1));
}
if (usage & KU_KEY_AGREEMENT) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Key Agreement", -1));
}
if (usage & KU_KEY_CERT_SIGN) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Certificate Signing", -1));
}
if (usage & KU_CRL_SIGN) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("CRL Signing", -1));
}
if (usage & KU_ENCIPHER_ONLY) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Encipher Only", -1));
}
if (usage & KU_DECIPHER_ONLY) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Decipher Only", -1));
}
} else {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("unrestricted", -1));
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509Purpose --
*
* Get X.509 certificate purpose
*
* Results:
* Purpose string
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
char *Tls_x509Purpose(X509 *cert) {
char *purpose = NULL;
if (X509_check_purpose(cert, X509_PURPOSE_SSL_CLIENT, 0) > 0) {
purpose = "SSL Client";
} else if (X509_check_purpose(cert, X509_PURPOSE_SSL_SERVER, 0) > 0) {
purpose = "SSL Server";
} else if (X509_check_purpose(cert, X509_PURPOSE_NS_SSL_SERVER, 0) > 0) {
purpose = "MSS SSL Server";
} else if (X509_check_purpose(cert, X509_PURPOSE_SMIME_SIGN, 0) > 0) {
purpose = "SMIME Signing";
} else if (X509_check_purpose(cert, X509_PURPOSE_SMIME_ENCRYPT, 0) > 0) {
purpose = "SMIME Encryption";
} else if (X509_check_purpose(cert, X509_PURPOSE_CRL_SIGN, 0) > 0) {
purpose = "CRL Signing";
} else if (X509_check_purpose(cert, X509_PURPOSE_ANY, 0) > 0) {
purpose = "Any";
} else if (X509_check_purpose(cert, X509_PURPOSE_OCSP_HELPER, 0) > 0) {
purpose = "OCSP Helper";
} else if (X509_check_purpose(cert, X509_PURPOSE_TIMESTAMP_SIGN, 0) > 0) {
purpose = "Timestamp Signing";
} else {
purpose = "";
}
return purpose;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509Purposes --
*
* Get X.509 certificate purpose types
*
* Results:
* Tcl list of each purpose and whether it is CA or non-CA
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509Purposes(Tcl_Interp *interp, X509 *cert) {
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
X509_PURPOSE *ptmp;
if (resultObj == NULL) {
return NULL;
}
for (int i = 0; i < X509_PURPOSE_get_count(); i++) {
ptmp = X509_PURPOSE_get0(i);
Tcl_Obj *tmpPtr = Tcl_NewListObj(0, NULL);
for (int j = 0; j < 2; j++) {
int idret = X509_check_purpose(cert, X509_PURPOSE_get_id(ptmp), j);
Tcl_ListObjAppendElement(interp, tmpPtr, Tcl_NewStringObj(j ? "CA" : "nonCA", -1));
Tcl_ListObjAppendElement(interp, tmpPtr, Tcl_NewStringObj(idret == 1 ? "Yes" : "No", -1));
}
LAPPEND_OBJ(interp, resultObj, X509_PURPOSE_get0_name(ptmp), tmpPtr);
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509Names --
*
* Get a list of Subject Alternate Names (SAN) or Issuer Alternate Names
*
* Results:
* Tcl list of alternate names
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509Names(Tcl_Interp *interp, X509 *cert, int nid, BIO *bio) {
STACK_OF(GENERAL_NAME) *names;
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
Tcl_Size len;
char buffer[1024];
if (resultObj == NULL) {
return NULL;
}
if ((names = X509_get_ext_d2i(cert, nid, NULL, NULL)) != NULL) {
for (int i=0; i < sk_GENERAL_NAME_num(names); i++) {
const GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
len = BIO_to_Buffer(name && GENERAL_NAME_print(bio, (GENERAL_NAME *) name), bio, buffer, 1024);
LAPPEND_STR(interp, resultObj, NULL, buffer, len);
}
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509ExtKeyUsage --
*
* Get a list of Extended Key Usages
*
* Returns:
* Tcl list of usages
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509ExtKeyUsage(Tcl_Interp *interp, X509 *cert, uint32_t xflags) {
uint32_t usage = X509_get_key_usage(cert);
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
if (resultObj == NULL) {
return NULL;
}
if ((xflags & EXFLAG_XKUSAGE) && usage < UINT32_MAX) {
usage = X509_get_extended_key_usage(cert);
if (usage & XKU_SSL_SERVER) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("TLS Web Server Authentication", -1));
}
if (usage & XKU_SSL_CLIENT) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("TLS Web Client Authentication", -1));
}
if (usage & XKU_SMIME) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("E-mail Protection", -1));
}
if (usage & XKU_CODE_SIGN) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Code Signing", -1));
}
if (usage & XKU_SGC) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("SGC", -1));
}
if (usage & XKU_OCSP_SIGN) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("OCSP Signing", -1));
}
if (usage & XKU_TIMESTAMP) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Time Stamping", -1));
}
if (usage & XKU_DVCS ) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("DVCS", -1));
}
if (usage & XKU_ANYEKU) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("Any Extended Key Usage", -1));
}
} else {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("unrestricted", -1));
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509CrlDp --
*
* Get list of CRL Distribution Points
*
* Returns:
* Tcl list of URIs and relative-names
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509CrlDp(Tcl_Interp *interp, X509 *cert) {
STACK_OF(DIST_POINT) *crl;
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
if (resultObj == NULL) {
return NULL;
}
if ((crl = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL)) != NULL) {
for (int i=0; i < sk_DIST_POINT_num(crl); i++) {
DIST_POINT *dp = sk_DIST_POINT_value(crl, i);
DIST_POINT_NAME *distpoint = dp->distpoint;
if (distpoint->type == 0) {
/* full-name GENERALIZEDNAME */
for (int j = 0; j < sk_GENERAL_NAME_num(distpoint->name.fullname); j++) {
GENERAL_NAME *gen = sk_GENERAL_NAME_value(distpoint->name.fullname, j);
int type;
ASN1_STRING *uri = GENERAL_NAME_get0_value(gen, &type);
if (type == GEN_URI) {
LAPPEND_STR(interp, resultObj, (char *) NULL, (char *) ASN1_STRING_get0_data(uri), (Tcl_Size) ASN1_STRING_length(uri));
}
}
} else if (distpoint->type == 1) {
/* relative-name X509NAME */
STACK_OF(X509_NAME_ENTRY) *sk_relname = distpoint->name.relativename;
for (int j = 0; j < sk_X509_NAME_ENTRY_num(sk_relname); j++) {
X509_NAME_ENTRY *e = sk_X509_NAME_ENTRY_value(sk_relname, j);
ASN1_STRING *d = X509_NAME_ENTRY_get_data(e);
LAPPEND_STR(interp, resultObj, (char *) NULL, (char *) ASN1_STRING_data(d), (Tcl_Size) ASN1_STRING_length(d));
}
}
}
CRL_DIST_POINTS_free(crl);
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509Oscp
*
* Get list of On-line Certificate Status Protocol (OSCP) URIs
*
* Results:
* Tcl list of URIs
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509Oscp(Tcl_Interp *interp, X509 *cert) {
STACK_OF(OPENSSL_STRING) *ocsp;
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
if (resultObj == NULL) {
return NULL;
}
if ((ocsp = X509_get1_ocsp(cert)) != NULL) {
for (int i = 0; i < sk_OPENSSL_STRING_num(ocsp); i++) {
LAPPEND_STR(interp, resultObj, NULL, sk_OPENSSL_STRING_value(ocsp, i), -1);
}
X509_email_free(ocsp);
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_x509CaIssuers --
*
* Get list of Certificate Authority (CA) Issuer URIs
*
* Results:
* Tcl list of CA issuer URIs
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_x509CaIssuers(Tcl_Interp *interp, X509 *cert) {
STACK_OF(ACCESS_DESCRIPTION) *ads;
ACCESS_DESCRIPTION *ad;
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
unsigned char *buf;
if (resultObj == NULL) {
return NULL;
}
if ((ads = X509_get_ext_d2i(cert, NID_info_access, NULL, NULL)) != NULL) {
for (int i = 0; i < sk_ACCESS_DESCRIPTION_num(ads); i++) {
ad = sk_ACCESS_DESCRIPTION_value(ads, i);
if (OBJ_obj2nid(ad->method) == NID_ad_ca_issuers && ad->location) {
if (ad->location->type == GEN_URI) {
Tcl_Size len = (Tcl_Size) ASN1_STRING_to_UTF8(&buf, ad->location->d.uniformResourceIdentifier);
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj((char *) buf, len));
OPENSSL_free(buf);
break;
}
}
}
/* sk_ACCESS_DESCRIPTION_pop_free(ads, ACCESS_DESCRIPTION_free); */
AUTHORITY_INFO_ACCESS_free(ads);
}
return resultObj;
}
/*
*-----------------------------------------------------------------------------
*
* Tls_NewX509Obj --
*
* Parses a X509 certificate and returns contents as a key-value Tcl list.
*
* Result:
* A Tcl List with the X509 certificate info as a key-value list
*
* Side effects:
* None
*
*-----------------------------------------------------------------------------
*/
Tcl_Obj *Tls_NewX509Obj(Tcl_Interp *interp, X509 *cert, int all) {
Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL);
BIO *bio = BIO_new(BIO_s_mem());
int mdnid, pknid, bits;
Tcl_Size len;
unsigned int ulen;
uint32_t xflags;
unsigned long flags = XN_FLAG_RFC2253 | ASN1_STRFLGS_UTF8_CONVERT;
flags &= ~ASN1_STRFLGS_ESC_MSB;
char *buffer = ckalloc(BUFSIZ > EVP_MAX_MD_SIZE ? BUFSIZ : EVP_MAX_MD_SIZE);
dprintf("Called");
if (interp == NULL || cert == NULL || bio == NULL || resultObj == NULL || buffer == NULL) {
Tcl_DecrRefCount(resultObj);
BIO_free(bio);
if (buffer != NULL) ckfree(buffer);
return NULL;
}
/* Signature algorithm and value - RFC 5280 section 4.1.1.2 and 4.1.1.3 */
/* signatureAlgorithm is the id of the cryptographic algorithm used by the
CA to sign this cert. signatureValue is the digital signature computed
upon the ASN.1 DER encoded tbsCertificate. */
{
const X509_ALGOR *sig_alg;
const ASN1_BIT_STRING *sig;
int sig_nid;
X509_get0_signature(&sig, &sig_alg, cert);
/* sig_nid = X509_get_signature_nid(cert) */
sig_nid = OBJ_obj2nid(sig_alg->algorithm);
LAPPEND_STR(interp, resultObj, "signatureAlgorithm", OBJ_nid2ln(sig_nid), -1);
if (sig_nid != NID_undef) {
LAPPEND_OBJ(interp, resultObj, "signatureValue", String_to_Hex(sig->data, sig->length));
} else {
LAPPEND_STR(interp, resultObj, "signatureValue", "", 0);
}
}
/* Version of the encoded certificate - RFC 5280 section 4.1.2.1 */
LAPPEND_LONG(interp, resultObj, "version", X509_get_version(cert)+1);
/* Unique number assigned by CA to certificate - RFC 5280 section 4.1.2.2 */
len = BIO_to_Buffer(i2a_ASN1_INTEGER(bio, X509_get0_serialNumber(cert)), bio, buffer, BUFSIZ);
LAPPEND_STR(interp, resultObj, "serialNumber", buffer, len);
/* Signature algorithm used by the CA to sign the certificate. Must match
signatureAlgorithm. RFC 5280 section 4.1.2.3 */
LAPPEND_STR(interp, resultObj, "signature", OBJ_nid2ln(X509_get_signature_nid(cert)), -1);
/* Issuer identifies the entity that signed and issued the cert. RFC 5280 section 4.1.2.4 */
len = BIO_to_Buffer(X509_NAME_print_ex(bio, X509_get_issuer_name(cert), 0, flags), bio, buffer, BUFSIZ);
LAPPEND_STR(interp, resultObj, "issuer", buffer, len);
/* Certificate validity period is the interval the CA warrants that it will
maintain info on the status of the certificate. RFC 5280 section 4.1.2.5 */
/* Get Validity - Not Before */
len = BIO_to_Buffer(ASN1_TIME_print(bio, X509_get0_notBefore(cert)), bio, buffer, BUFSIZ);
LAPPEND_STR(interp, resultObj, "notBefore", buffer, len);
/* Get Validity - Not After */
len = BIO_to_Buffer(ASN1_TIME_print(bio, X509_get0_notAfter(cert)), bio, buffer, BUFSIZ);
LAPPEND_STR(interp, resultObj, "notAfter", buffer, len);
/* Subject identifies the entity associated with the public key stored in
the subject public key field. RFC 5280 section 4.1.2.6 */
len = BIO_to_Buffer(X509_NAME_print_ex(bio, X509_get_subject_name(cert), 0, flags), bio, buffer, BUFSIZ);
LAPPEND_STR(interp, resultObj, "subject", buffer, len);
/* SHA1 Digest (Fingerprint) of cert - DER representation */
if (X509_digest(cert, EVP_sha1(), (unsigned char *)buffer, &ulen)) {
LAPPEND_OBJ(interp, resultObj, "sha1_hash", String_to_Hex((unsigned char *)buffer, (int) ulen));
}
/* SHA256 Digest (Fingerprint) of cert - DER representation */
if (X509_digest(cert, EVP_sha256(), (unsigned char *)buffer, &ulen)) {
LAPPEND_OBJ(interp, resultObj, "sha256_hash", String_to_Hex((unsigned char *)buffer, (int) ulen));
}
/* Subject Public Key Info specifies the public key and identifies the
algorithm with which the key is used. RFC 5280 section 4.1.2.7 */
if (X509_get_signature_info(cert, &mdnid, &pknid, &bits, &xflags)) {
ASN1_BIT_STRING *key;
unsigned int n;
LAPPEND_STR(interp, resultObj, "signingDigest", OBJ_nid2ln(mdnid), -1);
LAPPEND_STR(interp, resultObj, "publicKeyAlgorithm", OBJ_nid2ln(pknid), -1);
LAPPEND_INT(interp, resultObj, "bits", bits); /* Effective security bits */
key = X509_get0_pubkey_bitstr(cert);
LAPPEND_OBJ(interp, resultObj, "publicKey", String_to_Hex(key->data, key->length));
if (X509_pubkey_digest(cert, EVP_get_digestbynid(pknid), (unsigned char *)buffer, &n)) {
LAPPEND_OBJ(interp, resultObj, "publicKeyHash", String_to_Hex((unsigned char *)buffer, (int) n));
} else {
LAPPEND_STR(interp, resultObj, "publicKeyHash", "", 0);
}
/* digest of the DER representation of the certificate */
if (X509_digest(cert, EVP_get_digestbynid(mdnid), (unsigned char *)buffer, &n)) {
LAPPEND_OBJ(interp, resultObj, "signatureHash", String_to_Hex((unsigned char *)buffer, (int) n));
} else {
LAPPEND_STR(interp, resultObj, "signatureHash", "", 0);
}
}
/* Certificate Purpose. Call before checking for extensions. */
LAPPEND_STR(interp, resultObj, "purpose", Tls_x509Purpose(cert), -1);
LAPPEND_OBJ(interp, resultObj, "certificatePurpose", Tls_x509Purposes(interp, cert));
/* Get extensions flags */
xflags = X509_get_extension_flags(cert);
LAPPEND_INT(interp, resultObj, "extFlags", xflags);
/* Check if cert was issued by CA cert issuer or self signed */
LAPPEND_BOOL(interp, resultObj, "selfIssued", xflags & EXFLAG_SI);
LAPPEND_BOOL(interp, resultObj, "selfSigned", xflags & EXFLAG_SS);
LAPPEND_BOOL(interp, resultObj, "isProxyCert", xflags & EXFLAG_PROXY);
LAPPEND_BOOL(interp, resultObj, "extInvalid", xflags & EXFLAG_INVALID);
LAPPEND_BOOL(interp, resultObj, "isCACert", X509_check_ca(cert));
/* The Unique Ids are used to handle the possibility of reuse of subject
and/or issuer names over time. RFC 5280 section 4.1.2.8 */
{
const ASN1_BIT_STRING *iuid, *suid;
X509_get0_uids(cert, &iuid, &suid);
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("issuerUniqueId", -1));
if (iuid != NULL) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewByteArrayObj((const unsigned char *)iuid->data, (Tcl_Size) iuid->length));
} else {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("", -1));
}
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("subjectUniqueId", -1));
if (suid != NULL) {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewByteArrayObj((const unsigned char *)suid->data, (Tcl_Size) suid->length));
} else {
Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("", -1));
}
}
/* X509 v3 Extensions - RFC 5280 section 4.1.2.9 */
LAPPEND_INT(interp, resultObj, "extCount", X509_get_ext_count(cert));
LAPPEND_OBJ(interp, resultObj, "extensions", Tls_x509Extensions(interp, cert));
/* Authority Key Identifier (AKI) is the Subject Key Identifier (SKI) of
its signer (the CA). RFC 5280 section 4.2.1.1, NID_authority_key_identifier */
LAPPEND_OBJ(interp, resultObj, "authorityKeyIdentifier",
Tls_x509Identifier(X509_get0_authority_key_id(cert)));
/* Subject Key Identifier (SKI) is used to identify certificates that contain
a particular public key. RFC 5280 section 4.2.1.2, NID_subject_key_identifier */
LAPPEND_OBJ(interp, resultObj, "subjectKeyIdentifier",
Tls_x509Identifier(X509_get0_subject_key_id(cert)));
/* Key usage extension defines the purpose (e.g., encipherment, signature, certificate
signing) of the key in the certificate. RFC 5280 section 4.2.1.3, NID_key_usage */
LAPPEND_OBJ(interp, resultObj, "keyUsage", Tls_x509KeyUsage(interp, cert, xflags));
/* Certificate Policies - indicates the issuing CA considers its issuerDomainPolicy
equivalent to the subject CA's subjectDomainPolicy. RFC 5280 section 4.2.1.4, NID_certificate_policies */
if (xflags & EXFLAG_INVALID_POLICY) {
/* Reject cert */
}
/* Policy Mappings - RFC 5280 section 4.2.1.5, NID_policy_mappings */
/* Subject Alternative Name (SAN) contains additional URLs, DNS names, or IP
addresses bound to certificate. RFC 5280 section 4.2.1.6, NID_subject_alt_name */
LAPPEND_OBJ(interp, resultObj, "subjectAltName", Tls_x509Names(interp, cert, NID_subject_alt_name, bio));
/* Issuer Alternative Name is used to associate Internet style identities
with the certificate issuer. RFC 5280 section 4.2.1.7, NID_issuer_alt_name */
LAPPEND_OBJ(interp, resultObj, "issuerAltName", Tls_x509Names(interp, cert, NID_issuer_alt_name, bio));
/* Subject Directory Attributes provides identification attributes (e.g., nationality)
of the subject. RFC 5280 section 4.2.1.8 (subjectDirectoryAttributes) */
/* Basic Constraints identifies whether the subject of the cert is a CA and
the max depth of valid cert paths for this cert. RFC 5280 section 4.2.1.9, NID_basic_constraints */
if (!(xflags & EXFLAG_PROXY)) {
LAPPEND_LONG(interp, resultObj, "pathLen", X509_get_pathlen(cert));
} else {
LAPPEND_LONG(interp, resultObj, "pathLen", X509_get_proxy_pathlen(cert));
}
LAPPEND_BOOL(interp, resultObj, "basicConstraintsCA", xflags & EXFLAG_CA);
/* Name Constraints is only used in CA certs to indicate the name space for
all subject names in subsequent certificates in a certification path
MUST be located. RFC 5280 section 4.2.1.10, NID_name_constraints */
/* Policy Constraints is only used in CA certs to limit the length of a
cert chain for that CA. RFC 5280 section 4.2.1.11, NID_policy_constraints */
/* Extended Key Usage indicates the purposes the certified public key may be
used, beyond the basic purposes. RFC 5280 section 4.2.1.12, NID_ext_key_usage */
LAPPEND_OBJ(interp, resultObj, "extendedKeyUsage", Tls_x509ExtKeyUsage(interp, cert, xflags));
/* CRL Distribution Points identifies where CRL information can be obtained.
RFC 5280 section 4.2.1.13*/
LAPPEND_OBJ(interp, resultObj, "crlDistributionPoints", Tls_x509CrlDp(interp, cert));
/* Freshest CRL extension */
if (xflags & EXFLAG_FRESHEST) {
}
/* Authority Information Access indicates how to access info and services
for the certificate issuer. RFC 5280 section 4.2.2.1, NID_info_access */
/* On-line Certificate Status Protocol (OSCP) Responders URL */
LAPPEND_OBJ(interp, resultObj, "ocspResponders", Tls_x509Oscp(interp, cert));
/* Certificate Authority (CA) Issuers URL */
LAPPEND_OBJ(interp, resultObj, "caIssuers", Tls_x509CaIssuers(interp, cert));
/* Subject Information Access - RFC 5280 section 4.2.2.2, NID_sinfo_access */
/* Certificate Alias. If uses a PKCS#12 structure, alias will reflect the
friendlyName attribute (RFC 2985). */
{
int ilen = 0;
unsigned char *string = X509_alias_get0(cert, &ilen);
LAPPEND_STR(interp, resultObj, "alias", (char *) string, (Tcl_Size) ilen);
string = X509_keyid_get0(cert, &ilen);
LAPPEND_STR(interp, resultObj, "keyId", (char *) string, (Tcl_Size) ilen);
}
/* Certificate and dump all data */
if (all) {
Tcl_Obj *allObj = Tcl_NewByteArrayObj(NULL, 0);
Tcl_Obj *certObj = Tcl_NewByteArrayObj(NULL, 0);
unsigned char *allStr, *certStr;
if (allObj == NULL || certObj == NULL) {
Tcl_DecrRefCount(allObj);
BIO_free(bio);
ckfree(buffer);
return resultObj;
}
/* Get certificate */
certStr = Tcl_SetByteArrayLength(certObj, CERT_STR_SIZE);
len = BIO_to_Buffer(PEM_write_bio_X509(bio, cert), bio, certStr, CERT_STR_SIZE);
Tcl_SetByteArrayLength(certObj, len);
LAPPEND_OBJ(interp, resultObj, "certificate", certObj)
/* Get all info on certificate */
allStr = Tcl_SetByteArrayLength(allObj, CERT_STR_SIZE * 2);
len = BIO_to_Buffer(X509_print_ex(bio, cert, flags, 0), bio, allStr, CERT_STR_SIZE * 2);
Tcl_SetByteArrayLength(allObj, len);
LAPPEND_OBJ(interp, resultObj, "all", allObj)
}
BIO_free(bio);
ckfree(buffer);
return resultObj;
}