Skip to content Skip to sidebar Skip to footer

Check Ssl Certificate Expiration Date

I need to check local computer's SSl certificate expiry DATE and compare it with current date and notify user that his/her certificate is going to expire in X days. All this I need

Solution 1:

The only available option so far is forge - https://github.com/digitalbazaar/forge/blob/master/README.md or some sort of custom extension for the client.

Client side does not know nothing about other than DOM elements thus it cannot inspect SSL layer.

More on this Within a web browser, is it possible for JavaScript to obtain information about the SSL Certificate being used for the current page?

Solution 2:

Your certificate should look like this:

-----BEGIN CERTIFICATE-----
MIIGoDCCBIigAwIBAgIJAICRY3cWdgK1MA0GCSqGSIb3DQEBCwUAMIGeMQswCQYD
VQQGEwJCRzERMA8GA1UECAwIQnVsZ2FyaWExDjAMBgNVBAcMBVNvZmlhMQ8wDQYD
..
ud5Nja8+xycA/Jk7bSvB1jJjpc3oL0G9j0HOcxqQKd4e1IQXuss5V7FnQxSOVCq4
GVK0r3LkAxtl/EGmQC1DRlHAUWg=
-----END CERTIFICATE-----

You need to strip the -----BEGIN CERTIFICATE----- header and the -----END CERTIFICATE----- trailer from the certificate data, the rest is a Base64 encoded byte array. You need to decode it to an array of bytes (in this example represented as array of number, where each number represents a byte - number between 0 and 255 inclusive). That byte array is a DER encoded ASN.1 structure as defined in RFC-5280.

The below example will parse the content of the certificate after the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- traile has already been stripped.

Usage:

var pem =
"MIIGijCCBXKgAwIBAgIQEpI/gkvDS6idH2m2Zwn7ZzANBgkqhkiG9w0BAQsFADCB\n"
...
+"VQ+o34uWo7z19I8eXWSXN6P+Uj1OvHn8zNM1G/ddjQXBwMvzwwJEdVBhdK1uQw==\n";

var bytes = fromBase64(pem);

var validity = getValidity(bytes);
var notBefore = validity.notBefore;
var notAfter  = validity.notAfter;

var now = newDate();

if (    notBefore.getTime() < now.getTime() 
    &&  now.getTime() < notAfter.getTime()) 
{
  // Certificate is withing its validity days
} else {
  // Certificate is either not yet valid or has already expired.
}

Parsing:

varTYPE_INTEGER = 0x02;
varTYPE_SEQUENCE = 0x10;
varTYPE_UTC_TIME = 0x17;
varTYPE_GENERALIZED_TIME = 0x18;

functionsubArray(original, start, end) {
    var subArr = [];
    var index = 0;
    for (var i = start; i < end; i++) {
        subArr[index++] = original[i];
    }
    return subArr;
}

functiongetDigit(d) {
    switch (d) {
        default:
        case0x30: case'0': return0;
        case0x31: case'1': return1;
        case0x32: case'2': return2;
        case0x33: case'3': return3;
        case0x34: case'4': return4;
        case0x35: case'5': return5;
        case0x36: case'6': return6;
        case0x37: case'7': return7;
        case0x38: case'8': return8;
        case0x39: case'9': return9;
    }
}

functionenterTag(bytes, start, requiredTypes, name) {
    if (start + 1 > bytes.length) {
        thrownewError("Too short certificate input");
    }
    var typeByte = bytes[start   ] & 0x0FF;
    var lenByte  = bytes[start +1] & 0x0FF;

    var type     = typeByte & 0x1F;
    var len      = lenByte;

    var index    = start + 2;
    if (requiredTypes.length > 0 && requiredTypes.indexOf(type) == -1) {
        thrownewError("Invalid type");
    }
    var lengthOfLength = 0;
    if (len > 0x07F) {
        lengthOfLength = len & 0x7F;
        len = 0;
        for (var i =0; i < lengthOfLength && index < bytes.length; i++) {
            len = (len << 8 ) | (bytes[index] & 0x00FF);
            index++;
        }
    }
    if (index >= bytes.length) {
        thrownewError("Too short certificate input");
    }
    return {index: index, type: type, length: len}
}

functionprocessTag(bytes, start, requiredTypes, name) {
    var result = enterTag(bytes, start, requiredTypes, name);

    var index = result.index + result.length;   

    if (index >= bytes.length) {
        thrownewError("Too short certificate input");
    }
    var valueStart = result.index;
    var valueEnd   = result.index + result.length;
    var value = subArray(bytes, valueStart, valueEnd);
    return { index: index, type: result.type, value: value};
}

functionreadDate(bytes, start, name) {
    var date = newDate();
    var result = processTag(bytes, start, 
            [TYPE_UTC_TIME, TYPE_GENERALIZED_TIME], name);
    var index, year;
    if (result.type == 0x17) { // UTCTimeif (result.value.length < 12) {
            thrownewError("Invalid type");
        }
        var yearHigh = getDigit(result.value[0]);
        var yearLow  = getDigit(result.value[1]);
        var year2Digits = (yearHigh * 10 ) + (yearLow)
        if (year2Digits >= 50) {
            year = 1900 + year2Digits;
        } else {
            year = 2000 + year2Digits;
        }
        index = 2;
    } elseif (result.type = 0x18) { // GeneralizedTimeif (result.value.length < 14) {
            thrownewError("Invalid type");
        }
        var year1  = getDigit(result.value[0]);
        var year2  = getDigit(result.value[1]);
        var year3  = getDigit(result.value[2]);
        var year4  = getDigit(result.value[3]);
        year = (year1 * 1000) + (year2 * 100) + (year3*10) + year4;
        index = 4;
    }
    var monthHigh  = getDigit(result.value[index++]);
    var monthLow   = getDigit(result.value[index++]);
    var dayHigh    = getDigit(result.value[index++]);
    var dayhLow    = getDigit(result.value[index++]);
    var hourHigh   = getDigit(result.value[index++]);
    var hourLow    = getDigit(result.value[index++]);
    var minuteHigh = getDigit(result.value[index++]);
    var minuteLow  = getDigit(result.value[index++]);
    var secondHigh = getDigit(result.value[index++]);
    var secondLow  = getDigit(result.value[index]);

    var month = (monthHigh   * 10) + monthLow;
    var day     = (dayHigh   * 10) + dayhLow;
    var hour   = (hourHigh   * 10) + hourLow;
    var minute = (minuteHigh * 10) + minuteLow;
    var second = (secondHigh * 10) + secondLow;

    if (month < 1 || month > 12) {
        thrownewError("Invalid month");
    }
    if (day < 1 || day > 31) {
        thrownewError("Invalid day");
    }
    if (hour < 0 || hour > 24) {
        thrownewError("Invalid hour");
    }
    if (minute < 0 || minute > 59) {
        thrownewError("Invalid minute");
    }
    if (second < 0 || second > 59) {
        thrownewError("Invalid second ");
    }

    date.setUTCFullYear(year);
    date.setUTCMonth(month-1);
    date.setUTCDate(day);
    date.setUTCHours(hour);
    date.setUTCMinutes(minute);
    date.setUTCSeconds(second);

    return {
        index: result.index, 
        type: result.type, 
        length: result.length, 
        value: result.value, 
        date: date
    };
}

functiongetValidity(bytes) {
    if (bytes == null || bytes.length <= 0) {
        returnnull;
    }
    var index = 0;
    index = enterTag(bytes, index, [TYPE_SEQUENCE], "Certificate").index;
    index = enterTag(bytes, index, [TYPE_SEQUENCE], "TBSCertificate").index;
    var result = processTag(bytes, index, [0x00, 0x02], 
            "Version or SerialNumber");
    if (result.type == 0) {
        index = result.index;
        result = processTag(bytes, index, [TYPE_INTEGER], "SerialNumber")
    }
    index = result.index;
    result = processTag(bytes, index, [TYPE_SEQUENCE], 
        "Signature AlgorithmIdentifier");
    index  = result.index;
    result = processTag(bytes, index, [], "Issuer Name");
    index  = result.index;
    index  = enterTag(bytes, index, [TYPE_SEQUENCE], "Validity").index;
    result = readDate(bytes, index, "Not Before");
    var notBefore = result.date;
    index = result.index;
    result = readDate(bytes, index, "Not After");
    var notAfter = result.date;

    return {notBefore: notBefore, notAfter: notAfter};
}

functiongetNextBase64Chr(str, index, equalSignReceived, alpha) {
    var chr = null;
    var code = 0;
    var padding = equalSignReceived;
    while (index < str.length) {
        chr = str.charAt(index);
        if (chr == " " || chr == "\r" || chr == "\n" || chr == "\t") {
            index++;
            continue;
        }
        if (chr == "=") {
            padding = true;
        } else {
            if (equalSignReceived) {
                thrownewError("Invalid Base64 Endcoding.");
            }
            code = alpha.indexOf(chr);
            if (code == -1) {
                thrownewError("Invalid Base64 Encoding .");
            }
        }
        break;
    }
    return { character: chr, code: code, padding: padding, nextIndex: ++index};
}
functionfromBase64(str) {
    var alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var value = [];
    var index = 0;
    var destIndex  = 0;
    var padding = false;
    while (true) {

        var first   = getNextBase64Chr(str, index, padding, alpha);
        var second  = getNextBase64Chr(str, first .nextIndex, first .padding, alpha);
        var third   = getNextBase64Chr(str, second.nextIndex, second.padding, alpha);
        var fourth  = getNextBase64Chr(str, third .nextIndex, third .padding, alpha);

        index = fourth.nextIndex;
        padding = fourth.padding;

        // ffffffss sssstttt ttffffffvar base64_first  = first.code  == null ? 0 : first.code;
        var base64_second = second.code == null ? 0 : second.code;
        var base64_third  = third.code  == null ? 0 : third.code;
        var base64_fourth = fourth.code == null ? 0 : fourth.code;

        var a = (( base64_first  << 2 ) & 0xFC ) | ((base64_second >> 4) & 0x03);
        var b = (( base64_second << 4 ) & 0xF0 ) | ((base64_third  >> 2) & 0x0F);
        var c = (( base64_third  << 6 ) & 0xC0 ) | ((base64_fourth >> 0) & 0x3F);

        value [destIndex++] = a;
        if (!third.padding) {
            value [destIndex++] = b;
        } else {
            break;
        }
        if (!fourth.padding) {
            value [destIndex++] = c;
        } else {
            break;
        }
        if (index >= str.length) {
            break;
        }
    }
    return value;
}

Used resources:

A Layman's Guide to a Subset of ASN.1, BER, and DER

Encoding of ASN.1 UTC Time and GeneralizedTime

RFC-5280

Solution 3:

This is not possible from the client, but you could do something like this with node / shell combo.

Assuming you have access to the server the cert is running on you could do something like this on the host:

var execSync = require('child_process').execSyncvar cmd = "echo | openssl s_client -connect 127.0.0.1:443 2>/dev/null | openssl x509 -noout -dates"var stdout = execSync(cmd).toString()
// "notBefore=Feb 16 15:33:00 2017 GMT\nnotAfter=May 17 15:33:00 2017 GMT"

From there you could parse the dates reported by stdout and write them to a public resource or json file so they are readable from the client, and from there do your date comparison.

Post a Comment for "Check Ssl Certificate Expiration Date"