欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

openssl 证书解析

程序员文章站 2022-03-06 18:57:46
...

简单实用例子 openssl 解析x509证书

命令行打印证书细节: openssl x509 -noout -text -in cert.crt


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

compile...
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