DEV Community

James Moberg
James Moberg

Posted on

1

How to Check SSL Certificate using ColdFusion & CURL.exe

A CFML developer in the ColdFusion Programmers Facebook Group referenced my DEV article regarding how to identify the SSL expiration date using ColdFusion as they were encountering some issues and thought it may be due to the version of Java that was being using.

Here's a UDF that I wrote a couple years ago that leverages CURL (open source) to fetch & identify the current SSL certificate data in use.

I've added a resolveIp option so that you can override DNS lookup and bind the hostname to any IP so that both origin & WAF IP addresses can be tested. (Some web applications often have more than 1 IP address and both certificates should to be tested.)

There's also a useragent option to pass a custom user agent in case you require something specific to bypass security restrictions. (I recommend perform requests for static robots.txt files. Even if robots.txt doesn't exist, the "404 Not Found" message will return the error using the SSL connection and this is all that really matters.)

BTW: If you're going to CFSummit2023 in Las Vegas, come find me. I enjoy meeting other CFDevs. (The person that I usually talk about tech with is my .NET brother.)

result = checkSSLCertificate("https://www.adobe.com/robots.txt");

writedump(result);
Enter fullscreen mode Exit fullscreen mode

Image description

ColdFusion/CFML Source Code

Enjoy!

https://gist.github.com/JamoCA/fa7449d1f1a8b920d901b9b14a773e96

<!--- checkSSLCertificate UDF - I use ColdFusion & CURL to connect to remote HOST to identify SSL data (start/end dates, subject, subjectAltName, issuer & status) #cfml
GIST: https://gist.github.com/JamoCA/fa7449d1f1a8b920d901b9b14a773e96
BLOG: https://dev.to/gamesover/how-to-check-ssl-certificate-using-coldfusion-curlexe-2c92
TWITTER: https://twitter.com/gamesover/status/1707506769466216593
NOTE: This UDF requires CURL. https://curl.se/
--->
<cfscript>
struct function checkSSLCertificate(required string targetUrl, string userAgent="", string resolveIp="", string exePath="", boolean debug=false) output=false hint="I use CURL to connect to remote HOST to identify SSL data (start/end dates, subject, subjectAltName, issuer & status)" {
arguments.exePath = (len(arguments.exePath)) ? arguments.exePath : "C:\CURL\CURL.exe"; // set to default CURL exe path
arguments.useAgent = (len(arguments.userAgent)) ? arguments.userAgent : "FireFox 13|Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0.1";
local.errorCodes = ["1": "UNSUPPORTED_PROTOCOL", "2": "FAILED_INIT", "3": "URL_MALFORMAT", "4": "NOT_BUILT_IN", "5": "COULDNT_RESOLVE_PROXY", "6": "COULDNT_RESOLVE_HOST", "7": "COULDNT_CONNECT", "8": "FTP_WEIRD_SERVER_REPLY", "9": "REMOTE_ACCESS_DENIE", "10": "FTP_ACCEPT_FAILED", "11": "FTP_WEIRD_PASS_REPLY", "12": "FTP_ACCEPT_TIMEOUT", "13": "FTP_WEIRD_PASV_REPLY", "14": "FTP_WEIRD_227_FORMAT", "15": "FTP_CANT_GET_HOST", "16": "HTTP2", "17": "FTP_COULDNT_SET_TYPE", "18": "PARTIAL_FILE", "19": "FTP_COULDNT_RETR_FILE", "21": "QUOTE_ERROR", "22": "HTTP_RETURNED_ERROR", "23": "WRITE_ERROR", "25": "UPLOAD_FAILED", "26": "READ_ERROR", "27": "OUT_OF_MEMORY", "28": "OPERATION_TIMEDOUT", "30": "FTP_PORT_FAILED", "31": "FTP_COULDNT_USE_REST", "33": "RANGE_ERROR", "34": "HTTP_POST_ERROR", "35": "SSL_CONNECT_ERROR", "36": "BAD_DOWNLOAD_RESUME", "37": "FILE_COULDNT_READ_FILE", "38": "LDAP_CANNOT_BIND", "39": "LDAP_SEARCH_FAILED", "41": "FUNCTION_NOT_FOUND", "42": "ABORTED_BY_CALLBACK", "43": "BAD_FUNCTION_ARGUMENT", "45": "INTERFACE_FAILED", "47": "TOO_MANY_REDIRECTS", "48": "UNKNOWN_OPTION", "49": "TELNET_OPTION_SYNTAX", "51": "PEER_FAILED_VERIFICATION", "52": "GOT_NOTHING", "53": "SSL_ENGINE_NOTFOUND", "54": "SSL_ENGINE_SETFAILED", "55": "SEND_ERROR", "56": "RECV_ERROR", "58": "SSL_CERTPROBLEM", "59": "SSL_CIPHER", "60": "SSL_CACERT", "61": "BAD_CONTENT_ENCODING", "62": "LDAP_INVALID_URL", "63": "FILESIZE_EXCEEDED", "64": "USE_SSL_FAILED", "65": "SEND_FAIL_REWIND", "66": "SSL_ENGINE_INITFAILED", "67": "LOGIN_DENIED", "68": "TFTP_NOTFOUND", "69": "TFTP_PERM", "70": "REMOTE_DISK_FULL", "71": "TFTP_ILLEGAL", "72": "TFTP_UNKNOWNID", "73": "REMOTE_FILE_EXISTS", "74": "TFTP_NOSUCHUSER", "75": "CONV_FAILED", "76": "CONV_REQD", "77": "SSL_CACERT_BADFILE", "78": "REMOTE_FILE_NOT_FOUND", "79": "SSH", "80": "SSL_SHUTDOWN_FAILED", "81": "AGAIN", "82": "SSL_CRL_BADFILE", "83": "SSL_ISSUER_ERROR", "84": "FTP_PRET_FAILED", "85": "RTSP_CSEQ_ERROR", "86": "RTSP_SESSION_ERROR", "87": "FTP_BAD_FILE_LIST", "88": "CHUNK_FAILED", "89": "NO_CONNECTION_AVAILABLE", "90": "SSL_PINNEDPUBKEYNOTMATCH", "91": "SSL_INVALIDCERTSTATUS", "92": "HTTP2_STREAM", "93": "RECURSIVE_API_CALL", "94": "AUTH_ERROR", "95": "HTTP3", "96": "QUIC_CONNECT_ERROR"];
local.result = [
"ssl": [:]
,"duration": javacast("int", 0)
,"headers": [
"ip": "0.0.0.0"
,"status": javacast("int", 0)
]
,"args": [
"arguments": arguments
,"params": "-s -verbose"
,"isValidURL": isvalid("url", arguments.targetUrl)
]
,"raw": ""
];
if (len(arguments.resolveIp) && refindnocase("^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)", arguments.resolveIp)) {
local.port = "80";
local.hostname = listgetat(arguments.targetUrl, 2, "/");
if (findnocase("https://", arguments.targetUrl)) {
local.port = "443";
}
local.result.args.params = local.result.args.params & " --resolve #local.hostname#:#local.port#:#arguments.resolveIp#";
}
local.result.args.params = local.result.args.params & " -A ""#arguments.userAgent#"" #listfirst(arguments.targetUrl,'?')#";
if (find("?", arguments.targetUrl)) {
local.result.args.params = local.result.args.params & " -d """ & listrest(arguments.targetUrl, "?") & """";
}
local.result.args.params = local.result.args.params & " --stderr - 2>&1";
local.timeStart = gettickcount();
if (local.result.args.isValidURL) {
cfexecute(arguments=local.result.args.params, variable="local.result.Raw", name=arguments.exePath, timeout=15);
local.result.duration = javacast("int", gettickcount() - local.timeStart);
for (local.thisLine in listtoarray(local.result.Raw, chr(13) & chr(10))) {
if (left(trim(local.thisLine), 2) eq "< " && listlen(trim(local.thisLine), ":") gte 2) {
local.result.headers["#listrest(trim(listfirst(local.thisLine, ":")), " ")#"] = trim(listrest(local.thisLine, ":"));
} else if (left(trim(local.thisLine), 6) eq "< HTTP") {
local.result.headers["Status"] = trim(listrest(listrest(trim(local.thisLine), " "), " "));
} else if (left(trim(local.thisLine), 10) eq "* Trying") {
local.result.headers["IP"] = trim(listLast(listfirst(trim(local.thisLine), ":"), " "));
} else if (left(trim(local.thisLine), 14) eq "* Connected to") {
local.result.headers["IP"] = trim(listrest(listfirst(trim(local.thisLine), ")"), "("));
} else if (left(trim(local.thisLine), 6) eq "curl: ") {
if (!local.result.keyExists("error")) {
local.result["error"] = [:];
}
local.result.error["message"] = trim(listrest(local.thisline, ":"));
local.result.error["id"] = javacast("int", 0);
local.result.error["code"] = "";
if (find("(", local.result.error.message) && find(")", local.result.error.message)) {
local.result.error.id = javacast("int", listfirst(local.result.error.message, " ").replaceAll("\D", ""));
local.result.error.message = trim(listrest(local.result.error.message, " "));
if (local.errorCodes.keyExists("#local.result.error.id#")) {
local.result.error.code = local.errorCodes["#local.result.error.id#"];
}
}
} else if (left(trim(local.thisLine), 19) eq "More Details here: ") {
if (!local.result.keyExists("error")) {
local.result["error"] = [:];
}
local.result.error["details"] = trim(listrest(local.thisline, ":"));
} else if (left(trim(local.thisLine), 3) eq "* ") {
if (listlen(trim(local.thisLine), ":") gte 2) {
local.result.ssl["#trim(replace(listrest(trim(listfirst(local.thisLine, ":")), " "), " ", "_", "all"))#"] = trim(listrest(local.thisLine, ":"));
} else {
local.result.ssl["status"] = trim(listrest(trim(local.thisLine), " "));
}
}
}
local.result.headers.ip = local.result.headers.ip.replaceAll("\.\.\.", "");
for (local.keyname in local.result.ssl) {
if (isdate(local.result.ssl["#local.keyname#"].replaceAll("\s\d+:\d+:\d+\s", " ").replaceAll(" GMT", ""))) {
local.result.ssl["#local.keyname#"] = dateformat(local.result.ssl["#local.keyname#"].replaceAll("\s\d+:\d+:\d+\s", " ").replaceAll(" GMT", ""), "yyyy-mm-dd");
}
}
} else {
local.result.headers.status = javacast("int", 0);
local.result.error = [
"message": "Invalid URL"
,"id": javacast("int", 0)
,"code": "INVALID_URL"
];
}
if (!arguments.debug) {
structdelete(local.result, "headers");
structdelete(local.result, "args");
structdelete(local.result, "raw");
}
return local.result;
}
</cfscript>
<cfset result = CheckSSLCertificate(targetURL="https://www.coldfusion.com/robots.txt", debug=true)>
<cfdump var="#result#" label="CheckSSLCertificate UDF Results">

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay