C++ OpenSSL Parse X509 Certificate PEM


Here is a sample of OpenSSL C code parsing a certificate from a hardcoded string. Included is basically the output in bash if you parse a cert with command line the openssl command, "openssl x509 -noout -text -in cert.pem"

before compiling
sudo apt-get install libssl-dev #debian based
yum install openssl-devel #redhat based

compile on linux
g++ main.cpp -o main -lcrypto

main.cpp
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
 
using std::cout;
using std::endl;
using std::stringstream;
using std::map;
using std::vector;
using std::string;
 
//----------------------------------------------------------------------
string thumbprint(X509* x509)
{
    static const char hexbytes[] = "0123456789ABCDEF";
    unsigned int md_size;
    unsigned char md[EVP_MAX_MD_SIZE];
    const EVP_MD * digest = EVP_get_digestbyname("sha1");
    X509_digest(x509, digest, md, &md_size);
    stringstream ashex;
    for(int pos = 0; pos < md_size; pos++)
    {
        ashex << hexbytes[ (md[pos]&0xf0)>>4 ];
        ashex << hexbytes[ (md[pos]&0x0f)>>0 ];
    }
    return ashex.str();
}
//----------------------------------------------------------------------
int certversion(X509* x509)
{
    return X509_get_version(x509) +1;
}
//----------------------------------------------------------------------
string pem(X509* x509)
{
    BIO * bio_out = BIO_new(BIO_s_mem());
    PEM_write_bio_X509(bio_out, x509);
    BUF_MEM *bio_buf;
    BIO_get_mem_ptr(bio_out, &bio_buf);
    string pem = string(bio_buf->data, bio_buf->length);
    BIO_free(bio_out);
    return pem;
}
//----------------------------------------------------------------------
void _asn1dateparse(const ASN1_TIME *time, int& year, int& month, int& day, int& hour, int& minute, int& second)
{
    const char* str = (const char*) time->data;
    size_t i = 0;
    if (time->type == V_ASN1_UTCTIME) {/* two digit year */
        year = (str[i++] - '0') * 10;
        year += (str[i++] - '0');
        year += (year < 70 ? 2000 : 1900);
    } else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
        year = (str[i++] - '0') * 1000;
        year+= (str[i++] - '0') * 100;
        year+= (str[i++] - '0') * 10;
        year+= (str[i++] - '0');
    }
    month  = (str[i++] - '0') * 10;
    month += (str[i++] - '0') - 1; // -1 since January is 0 not 1.
    day  = (str[i++] - '0') * 10;
    day += (str[i++] - '0');
    hour = (str[i++] - '0') * 10;
    hour+= (str[i++] - '0');
    minute  = (str[i++] - '0') * 10;
    minute += (str[i++] - '0');
    second  = (str[i++] - '0') * 10;
    second += (str[i++] - '0');
}
//----------------------------------------------------------------------
string _asn1int(ASN1_INTEGER *bs)
{
    static const char hexbytes[] = "0123456789ABCDEF";
    stringstream ashex;
    for(int i=0; i<bs->length; i++)
    {
        ashex << hexbytes[ (bs->data[i]&0xf0)>>4  ] ;
        ashex << hexbytes[ (bs->data[i]&0x0f)>>0  ] ;
    }
    return ashex.str();
}
//----------------------------------------------------------------------
string _asn1string(ASN1_STRING *d)
{
    string asn1_string;
    if (ASN1_STRING_type(d) != V_ASN1_UTF8STRING) {
        unsigned char *utf8;
        int length = ASN1_STRING_to_UTF8( &utf8, d );
        asn1_string= string( (char*)utf8, length );
        OPENSSL_free( utf8 );
    } else { 
        asn1_string= string( (char*)ASN1_STRING_data(d), ASN1_STRING_length(d) );
    }
    return asn1_string;
}
//----------------------------------------------------------------------
string _subject_as_line(X509_NAME *subj_or_issuer)
{
    BIO * bio_out = BIO_new(BIO_s_mem());
    X509_NAME_print(bio_out,subj_or_issuer,0);
    BUF_MEM *bio_buf;
    BIO_get_mem_ptr(bio_out, &bio_buf);
    string issuer = string(bio_buf->data, bio_buf->length);
    BIO_free(bio_out);
    return issuer;
}
//----------------------------------------------------------------------
std::map<string,string> _subject_as_map(X509_NAME *subj_or_issuer)
{
    std::map<string,string> m;    
    for (int i = 0; i < X509_NAME_entry_count(subj_or_issuer); i++) {
        X509_NAME_ENTRY *e = X509_NAME_get_entry(subj_or_issuer, i);
        ASN1_STRING *d = X509_NAME_ENTRY_get_data(e);
        ASN1_OBJECT *o = X509_NAME_ENTRY_get_object(e);
        const char* key_name = OBJ_nid2sn( OBJ_obj2nid( o ) );
                m[key_name] = _asn1string(d);
    }
    return m;
}
//----------------------------------------------------------------------
string issuer_one_line(X509* x509)
{
    return _subject_as_line(X509_get_issuer_name(x509));
}
//----------------------------------------------------------------------
string subject_one_line(X509* x509)
{
    return _subject_as_line(X509_get_subject_name(x509));
}
//----------------------------------------------------------------------
std::map<string,string> subject(X509* x509)
{
    return _subject_as_map(X509_get_subject_name(x509));
}
//----------------------------------------------------------------------
std::map<string,string> issuer(X509* x509)
{
    return _subject_as_map(X509_get_issuer_name(x509));
}
//----------------------------------------------------------------------
string serial(X509* x509)
{
    return _asn1int(X509_get_serialNumber(x509));
}
//----------------------------------------------------------------------
string signature_algorithm(X509 *x509)
{
    int sig_nid = OBJ_obj2nid((x509)->sig_alg->algorithm);
    return string( OBJ_nid2ln(sig_nid) );
}
//----------------------------------------------------------------------
string public_key_type(X509 *x509)
{
    EVP_PKEY *pkey=X509_get_pubkey(x509);
    int key_type = EVP_PKEY_type(pkey->type);
    EVP_PKEY_free(pkey);
    if (key_type==EVP_PKEY_RSA) return "rsa";
    if (key_type==EVP_PKEY_DSA) return "dsa";
    if (key_type==EVP_PKEY_DH)  return "dh";
    if (key_type==EVP_PKEY_EC)  return "ecc";
    return "";
}
//----------------------------------------------------------------------
int public_key_size(X509 *x509)
{
    EVP_PKEY *pkey=X509_get_pubkey(x509);
    int key_type = EVP_PKEY_type(pkey->type);
    int keysize = -1; //or in bytes, RSA_size() DSA_size(), DH_size(), ECDSA_size();
    keysize = key_type==EVP_PKEY_RSA && pkey->pkey.rsa->n ? BN_num_bits(pkey->pkey.rsa->n) : keysize;
    keysize = key_type==EVP_PKEY_DSA && pkey->pkey.dsa->p ? BN_num_bits(pkey->pkey.dsa->p) : keysize;
    keysize = key_type==EVP_PKEY_DH  && pkey->pkey.dh->p  ? BN_num_bits(pkey->pkey.dh->p) : keysize;
    keysize = key_type==EVP_PKEY_EC  ? EC_GROUP_get_degree(EC_KEY_get0_group(pkey->pkey.ec)) : keysize;
    EVP_PKEY_free(pkey);
    return keysize;
}
//----------------------------------------------------------------------
string public_key_ec_curve_name(X509 *x509)
{
    EVP_PKEY *pkey=X509_get_pubkey(x509);
    int key_type = EVP_PKEY_type(pkey->type);
    if (key_type==EVP_PKEY_EC)
    {
        const EC_GROUP *group = EC_KEY_get0_group(pkey->pkey.ec);
        int name = (group != NULL) ? EC_GROUP_get_curve_name(group) : 0;
        return name ? OBJ_nid2sn(name) : "";
    }
    return "";
}
//----------------------------------------------------------------------
string asn1datetime_isodatetime(const ASN1_TIME *tm)
{
    int year=0, month=0, day=0, hour=0, min=0, sec=0;
    _asn1dateparse(tm,year,month,day,hour,min,sec);
 
    char buf[25]="";
    snprintf(buf, sizeof(buf)-1, "%04d-%02d-%02d %02d:%02d:%02d GMT", year, month, day, hour, min, sec);
    return string(buf);
}
//----------------------------------------------------------------------
string asn1date_isodate(const ASN1_TIME *tm)
{
    int year=0, month=0, day=0, hour=0, min=0, sec=0;
    _asn1dateparse(tm,year,month,day,hour,min,sec);
 
    char buf[25]="";
    snprintf(buf, sizeof(buf)-1, "%04d-%02d-%02d", year, month, day);
    return string(buf);
}
//----------------------------------------------------------------------
vector<string> subject_alt_names(X509 *x509)
{
    vector<string> list;
    GENERAL_NAMES* subjectAltNames = (GENERAL_NAMES*)X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
    for (int i = 0; i < sk_GENERAL_NAME_num(subjectAltNames); i++)
    {
        GENERAL_NAME* gen = sk_GENERAL_NAME_value(subjectAltNames, i);
        if (gen->type == GEN_URI || gen->type == GEN_DNS || gen->type == GEN_EMAIL)
        {
            ASN1_IA5STRING *asn1_str = gen->d.uniformResourceIdentifier;
            string san = string( (char*)ASN1_STRING_data(asn1_str), ASN1_STRING_length(asn1_str) );
            list.push_back( san );
        }
        else if (gen->type == GEN_IPADD)
        {
            unsigned char *p = gen->d.ip->data;
            if(gen->d.ip->length == 4)
            {
                stringstream ip;
                ip << (int)p[0] << '.' << (int)p[1] << '.' << (int)p[2] << '.' << (int)p[3];
                list.push_back( ip.str() );
            }
            else //if(gen->d.ip->length == 16) //ipv6?
            {
                //std::cerr << "Not implemented: parse sans ("<< __FILE__ << ":" << __LINE__ << ")" << endl;
            }
        }
        else 
        {
            //std::cerr << "Not implemented: parse sans ("<< __FILE__ << ":" << __LINE__ << ")" << endl;
        }
    }
    GENERAL_NAMES_free(subjectAltNames);
    return list;
}
//----------------------------------------------------------------------
vector<string> ocsp_urls(X509 *x509)
{
    vector<string> list;
    STACK_OF(OPENSSL_STRING) *ocsp_list = X509_get1_ocsp(x509);
    for (int j = 0; j < sk_OPENSSL_STRING_num(ocsp_list); j++)
    {
        list.push_back( string( sk_OPENSSL_STRING_value(ocsp_list, j) ) ); 
    }
    X509_email_free(ocsp_list);
    return list;
}
//----------------------------------------------------------------------
vector<string> crl_urls(X509 *x509)
{
    vector<string> list;
    int nid = NID_crl_distribution_points;
    STACK_OF(DIST_POINT) * dist_points =(STACK_OF(DIST_POINT) *)X509_get_ext_d2i(x509, nid, NULL, NULL);
    for (int j = 0; j < sk_DIST_POINT_num(dist_points); j++)
    {
        DIST_POINT *dp = sk_DIST_POINT_value(dist_points, j);
        DIST_POINT_NAME    *distpoint = dp->distpoint;
        if (distpoint->type==0)//fullname GENERALIZEDNAME
        {
            for (int k = 0; k < sk_GENERAL_NAME_num(distpoint->name.fullname); k++) 
            {
                GENERAL_NAME *gen = sk_GENERAL_NAME_value(distpoint->name.fullname, k);
                ASN1_IA5STRING *asn1_str = gen->d.uniformResourceIdentifier;
                list.push_back( string( (char*)ASN1_STRING_data(asn1_str), ASN1_STRING_length(asn1_str) ) );
            }
        }
        else if (distpoint->type==1)//relativename X509NAME
        {
            STACK_OF(X509_NAME_ENTRY) *sk_relname = distpoint->name.relativename;
            for (int k = 0; k < sk_X509_NAME_ENTRY_num(sk_relname); k++) 
            {
                X509_NAME_ENTRY *e = sk_X509_NAME_ENTRY_value(sk_relname, k);
                ASN1_STRING *d = X509_NAME_ENTRY_get_data(e);
                list.push_back( string( (char*)ASN1_STRING_data(d), ASN1_STRING_length(d) ) );
            }
        }
    }
    CRL_DIST_POINTS_free(dist_points);
    return list;
}
//----------------------------------------------------------------------
void parseCert1(X509* x509)
{
    cout <<"--------------------" << endl;
    BIO *bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
    //PEM_write_bio_X509(bio_out, x509);//STD OUT the PEM
    X509_print(bio_out, x509);//STD OUT the details
    //X509_print_ex(bio_out, x509, XN_FLAG_COMPAT, X509_FLAG_COMPAT);//STD OUT the details
    BIO_free(bio_out);
}
//----------------------------------------------------------------------
void parseCert2(X509* x509)
{
    cout <<"--------------------" << endl;
    BIO *bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
 
    long l = X509_get_version(x509);
    BIO_printf(bio_out, "Version: %ld\n", l+1);
 
    ASN1_INTEGER *bs = X509_get_serialNumber(x509);
    BIO_printf(bio_out,"Serial: ");
    for(int i=0; i<bs->length; i++) {
        BIO_printf(bio_out,"%02x",bs->data[i] );
    }
    BIO_printf(bio_out,"\n");
 
    X509_signature_print(bio_out, x509->sig_alg, NULL);
 
    BIO_printf(bio_out,"Issuer: ");
    X509_NAME_print(bio_out,X509_get_issuer_name(x509),0);
    BIO_printf(bio_out,"\n");
 
    BIO_printf(bio_out,"Valid From: ");
    ASN1_TIME_print(bio_out,X509_get_notBefore(x509));
    BIO_printf(bio_out,"\n");
 
    BIO_printf(bio_out,"Valid Until: ");
    ASN1_TIME_print(bio_out,X509_get_notAfter(x509));
    BIO_printf(bio_out,"\n");
 
    BIO_printf(bio_out,"Subject: ");
    X509_NAME_print(bio_out,X509_get_subject_name(x509),0);
    BIO_printf(bio_out,"\n");
 
    EVP_PKEY *pkey=X509_get_pubkey(x509);
    EVP_PKEY_print_public(bio_out, pkey, 0, NULL);
    EVP_PKEY_free(pkey);
 
    X509_CINF *ci=x509->cert_info;
    X509V3_extensions_print(bio_out, (char*)"X509v3 extensions", ci->extensions, X509_FLAG_COMPAT, 0);
 
    X509_signature_print(bio_out, x509->sig_alg, x509->signature);
    BIO_free(bio_out);
}
//----------------------------------------------------------------------
void parseCert3(X509* x509)
{
    cout <<"--------------------" << endl;
    //cout << pem(x509) << endl;
    cout <<"Thumbprint: " << thumbprint(x509) << endl;
    cout <<"Version: " << certversion(x509) << endl;
    cout <<"Serial: " << serial(x509) << endl;
    cout <<"Issuer: " << issuer_one_line(x509) << endl;
    map<string,string> ifields = issuer(x509);
    for(map<string, string>::iterator i = ifields.begin(), ix = ifields.end(); i != ix; i++ )
        cout << " * " << i->first << " : " << i->second << endl;
    cout <<"Subject: "    << subject_one_line(x509) << endl;
    map<string,string> sfields = subject(x509);
    for(map<string, string>::iterator i = sfields.begin(), ix = sfields.end(); i != ix; i++ )
        cout << " * " <<  i->first << " : " << i->second << endl;
    cout <<"SignatureAlgorithm: "    << signature_algorithm(x509) << endl;
    cout <<"PublicKeyType: "    << public_key_type(x509) << public_key_ec_curve_name(x509) << endl;
    cout <<"PublicKeySize: "    << public_key_size(x509) << endl;
    cout <<"NotBefore: "    << asn1datetime_isodatetime(X509_get_notBefore(x509)) << endl;
    cout <<"NotAfter: "    << asn1datetime_isodatetime(X509_get_notAfter(x509)) << endl;
    cout <<"SubjectAltName(s):" << endl;
    vector<string> sans = subject_alt_names(x509);
    for(int i=0, ix=sans.size(); i<ix; i++) {
        cout << " " << sans[i] << endl;
    }
    cout <<"CRL URLs:" << endl;
    vector<string> crls = crl_urls(x509);
    for(int i=0, ix=crls.size(); i<ix; i++) {
        cout << " " << crls[i] << endl;
    }
    cout <<"OCSP URLs:" << endl;
    vector<string> urls = ocsp_urls(x509);
    for(int i=0, ix=urls.size(); i<ix; i++) {
        cout << " " << urls[i] << endl;
    }
}
//----------------------------------------------------------------------
int main(int argc, char **argv)
{
    OpenSSL_add_all_algorithms();
 
    const char bytes[] = "-----BEGIN CERTIFICATE-----" "\n"
"MIIG4TCCBcmgAwIBAgIQCd0Ux6hVwNaX+SICZIR/jzANBgkqhkiG9w0BAQUFADBm" "\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3" "\n"
"d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j" "\n"
"ZSBDQS0zMB4XDTEzMDUxNDAwMDAwMFoXDTE2MDUxODEyMDAwMFowYDELMAkGA1UE" "\n"
"BhMCQ0ExEDAOBgNVBAgTB0FsYmVydGExEDAOBgNVBAcTB0NhbGdhcnkxGTAXBgNV" "\n"
"BAoTEFNBSVQgUG9seXRlY2huaWMxEjAQBgNVBAMMCSouc2FpdC5jYTCCASIwDQYJ" "\n"
"KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJv2n5mZfX6NV0jZof1WdXGiY5Q/W0yD" "\n"
"T6tUdIYUjgS8GDkeZJYjtwUCMYD2Wo3rF/1ZJ8p9p2WBP1F3CVvjgO+VeA7tLJsf" "\n"
"uAr+S8GE1q5tGO9+lPFkBAZkU38FNfBUblvz1imWb6ORXMc++HjUlrUB0nr2Ae8T" "\n"
"1I3K0XGArHJyW5utJ5Xm8dNEYCcs6EAXchiViVtcZ2xIlSQMs+AqhqnZXo2Tt1H+" "\n"
"f/tQhQJeMTkZ2kklUcnQ1izdTigMgkOvNzW4Oyd9Z0sBbxzUpneeH3nUB5bEv3MG" "\n"
"4JJx7cAVPE4rqjVbtm3v0QbCL/X0ZncJiKl7heKWO+j3DnDZS/oliIkCAwEAAaOC" "\n"
"A48wggOLMB8GA1UdIwQYMBaAFFDqc4nbKfsQj57lASDU3nmZSIP3MB0GA1UdDgQW" "\n"
"BBTk00KEbrhrTuVWBY2cPzTJd1c1BTBkBgNVHREEXTBbggkqLnNhaXQuY2GCB3Nh" "\n"
"aXQuY2GCCmNwLnNhaXQuY2GCDmNwLXVhdC5zYWl0LmNhghd1YXQtaW50ZWdyYXRp" "\n"
"b24uc2FpdC5jYYIQdWF0LWFwYXMuc2FpdC5jYTAOBgNVHQ8BAf8EBAMCBaAwHQYD" "\n"
"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMGEGA1UdHwRaMFgwKqAooCaGJGh0" "\n"
"dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9jYTMtZzIxLmNybDAqoCigJoYkaHR0cDov" "\n"
"L2NybDQuZGlnaWNlcnQuY29tL2NhMy1nMjEuY3JsMIIBxAYDVR0gBIIBuzCCAbcw" "\n"
"ggGzBglghkgBhv1sAQEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2lj" "\n"
"ZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFW" "\n"
"HoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBm" "\n"
"AGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0" "\n"
"AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAv" "\n"
"AEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0" "\n"
"AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAg" "\n"
"AGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBw" "\n"
"AG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBu" "\n"
"AGMAZQAuMHsGCCsGAQUFBwEBBG8wbTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au" "\n"
"ZGlnaWNlcnQuY29tMEUGCCsGAQUFBzAChjlodHRwOi8vY2FjZXJ0cy5kaWdpY2Vy" "\n"
"dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlQ0EtMy5jcnQwDAYDVR0TAQH/BAIw" "\n"
"ADANBgkqhkiG9w0BAQUFAAOCAQEAcl2YI0iMOwx2FOjfoA8ioCtGc5eag8Prawz4" "\n"
"FFs9pMFZfD/K8QvPycMSkw7kPtVjmuQWxNtRAvCSIhr/urqNLBO5Omerx8aZYCOz" "\n"
"nsmZpymxMt56DBw+KZrWIodsZx5QjVngbE/qIDLmsYgtKczhTCtgEM1h/IHlO3Ho" "\n"
"7IXd2Rr4CqeMoM2v+MTV2FYVEYUHJp0EBU/AMuBjPf6YT/WXMNq6fn+WJpxcqwJJ" "\n"
"KtBh7c2vRTklahbh1FaiJ0aFJkDH4tasbD69JQ8R2V5OSuGH6Q7EGlpNl+unqtUy" "\n"
"KsAL86HvgzF5D51C9TmFXEtXTlPKnjoqn1TC4Rqpqvh+FHWPJQ==" "\n"
"-----END CERTIFICATE-----";
    BIO *bio_mem = BIO_new(BIO_s_mem());
    BIO_puts(bio_mem, bytes);
    X509 * x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL);
    parseCert1(x509);
    parseCert2(x509);
    parseCert3(x509);
    BIO_free(bio_mem);
    X509_free(x509);
}
//----------------------------------------------------------------------

Partial Output
Version: 3
Serial: 09dd14c7a855c0d697f9220264847f8f
    Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3
Valid From: May 14 00:00:00 2013 GMT
Valid Until: May 18 12:00:00 2016 GMT
Subject: C=CA, ST=Alberta, L=Calgary, O=SAIT Polytechnic, CN=*.sait.ca
Public-Key: (2048 bit)
Modulus:
    00:9b:f6:9f:99:99:7d:7e:8d:57:48:d9:a1:fd:56:
    75:71:a2:63:94:3f:5b:4c:83:4f:ab:54:74:86:14:
    8e:04:bc:18:39:1e:64:96:23:b7:05:02:31:80:f6:
    5a:8d:eb:17:fd:59:27:ca:7d:a7:65:81:3f:51:77:
    09:5b:e3:80:ef:95:78:0e:ed:2c:9b:1f:b8:0a:fe:
    4b:c1:84:d6:ae:6d:18:ef:7e:94:f1:64:04:06:64:
    53:7f:05:35:f0:54:6e:5b:f3:d6:29:96:6f:a3:91:
    5c:c7:3e:f8:78:d4:96:b5:01:d2:7a:f6:01:ef:13:
    d4:8d:ca:d1:71:80:ac:72:72:5b:9b:ad:27:95:e6:
    f1:d3:44:60:27:2c:e8:40:17:72:18:95:89:5b:5c:
    67:6c:48:95:24:0c:b3:e0:2a:86:a9:d9:5e:8d:93:
    b7:51:fe:7f:fb:50:85:02:5e:31:39:19:da:49:25:
    51:c9:d0:d6:2c:dd:4e:28:0c:82:43:af:37:35:b8:
    3b:27:7d:67:4b:01:6f:1c:d4:a6:77:9e:1f:79:d4:
    07:96:c4:bf:73:06:e0:92:71:ed:c0:15:3c:4e:2b:
    aa:35:5b:b6:6d:ef:d1:06:c2:2f:f5:f4:66:77:09:
    88:a9:7b:85:e2:96:3b:e8:f7:0e:70:d9:4b:fa:25:
    88:89
Exponent: 65537 (0x10001)
X509v3 extensions:
    X509v3 Authority Key Identifier: 
        keyid:50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7
 
    X509v3 Subject Key Identifier: 
        E4:D3:42:84:6E:B8:6B:4E:E5:56:05:8D:9C:3F:34:C9:77:57:35:05
    X509v3 Subject Alternative Name: 
        DNS:*.sait.ca, DNS:sait.ca, DNS:cp.sait.ca, DNS:cp-uat.sait.ca, DNS:uat-integration.sait.ca, DNS:uat-apas.sait.ca
    X509v3 Key Usage: critical
        Digital Signature, Key Encipherment
    X509v3 Extended Key Usage: 
        TLS Web Server Authentication, TLS Web Client Authentication
    X509v3 CRL Distribution Points: 
 
        Full Name:
          URI:http://crl3.digicert.com/ca3-g21.crl
 
        Full Name:
          URI:http://crl4.digicert.com/ca3-g21.crl
 
    X509v3 Certificate Policies: 
        Policy: 2.16.840.1.114412.1.1
          CPS: http://www.digicert.com/ssl-cps-repository.htm
          User Notice:
            Explicit Text: 
 
    Authority Information Access: 
        OCSP - URI:http://ocsp.digicert.com
        CA Issuers - URI:http://cacerts.digicert.com/DigiCertHighAssuranceCA-3.crt
 
    X509v3 Basic Constraints: critical
        CA:FALSE
    Signature Algorithm: sha1WithRSAEncryption
         72:5d:98:23:48:8c:3b:0c:76:14:e8:df:a0:0f:22:a0:2b:46:
         73:97:9a:83:c3:eb:6b:0c:f8:14:5b:3d:a4:c1:59:7c:3f:ca:
         f1:0b:cf:c9:c3:12:93:0e:e4:3e:d5:63:9a:e4:16:c4:db:51:
         02:f0:92:22:1a:ff:ba:ba:8d:2c:13:b9:3a:67:ab:c7:c6:99:
         60:23:b3:9e:c9:99:a7:29:b1:32:de:7a:0c:1c:3e:29:9a:d6:
         22:87:6c:67:1e:50:8d:59:e0:6c:4f:ea:20:32:e6:b1:88:2d:
         29:cc:e1:4c:2b:60:10:cd:61:fc:81:e5:3b:71:e8:ec:85:dd:
         d9:1a:f8:0a:a7:8c:a0:cd:af:f8:c4:d5:d8:56:15:11:85:07:
         26:9d:04:05:4f:c0:32:e0:63:3d:fe:98:4f:f5:97:30:da:ba:
         7e:7f:96:26:9c:5c:ab:02:49:2a:d0:61:ed:cd:af:45:39:25:
         6a:16:e1:d4:56:a2:27:46:85:26:40:c7:e2:d6:ac:6c:3e:bd:
         25:0f:11:d9:5e:4e:4a:e1:87:e9:0e:c4:1a:5a:4d:97:eb:a7:
         aa:d5:32:2a:c0:0b:f3:a1:ef:83:31:79:0f:9d:42:f5:39:85:
         5c:4b:57:4e:53:ca:9e:3a:2a:9f:54:c2:e1:1a:a9:aa:f8:7e:
         14:75:8f:25

FractalSpace on 2015-07-02 00:04:45
Awesome!