Introduction
SSL/TLS certificates are critical for web security and SEO, but they expire, get misconfigured, and develop vulnerabilities. In this guide, we'll build a comprehensive SSL certificate checker that validates certificates, checks security configuration, and monitors expiry.
Understanding SSL/TLS Certificates
SSL (Secure Sockets Layer) and its successor TLS (Transport Layer Security) encrypt data between client and server. Certificates authenticate the server's identity.
Key Components:
- Subject: Domain name(s) the certificate covers
- Issuer: Certificate Authority that issued it
- Validity Period: Not valid before / Not valid after dates
- Public Key: Used for encryption
- Signature: Cryptographic proof of authenticity
- Certificate Chain: Root + Intermediate certificates
Building the Checker
Step 1: Fetching Certificate Information
const tls = require('tls');
const https = require('https');
function checkSSLCertificate(hostname, port = 443) {
return new Promise((resolve, reject) => {
const options = {
host: hostname,
port: port,
method: 'GET',
rejectUnauthorized: false, // Allow checking invalid certs
agent: false
};
const req = https.get(options, (res) => {
const cert = res.socket.getPeerCertificate();
if (!cert || Object.keys(cert).length === 0) {
reject(new Error('No certificate found'));
return;
}
resolve({
subject: cert.subject,
issuer: cert.issuer,
validFrom: cert.valid_from,
validTo: cert.valid_to,
daysRemaining: getDaysRemaining(cert.valid_to),
serialNumber: cert.serialNumber,
fingerprint: cert.fingerprint,
subjectAltNames: cert.subjectaltname,
protocol: res.socket.getProtocol(),
cipher: res.socket.getCipher()
});
});
req.on('error', reject);
req.end();
});
}
function getDaysRemaining(validTo) {
const expiry = new Date(validTo);
const now = new Date();
const diffTime = expiry - now;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
Step 2: Certificate Validation
function validateCertificate(certInfo, hostname) {
const validation = {
valid: true,
errors: [],
warnings: []
};
// Check expiry
if (certInfo.daysRemaining < 0) {
validation.valid = false;
validation.errors.push({
type: 'expired',
message: `Certificate expired ${Math.abs(certInfo.daysRemaining)} days ago`
});
} else if (certInfo.daysRemaining < 30) {
validation.warnings.push({
type: 'expiring_soon',
message: `Certificate expires in ${certInfo.daysRemaining} days`
});
}
// Check domain match
const domainMatch = checkDomainMatch(certInfo, hostname);
if (!domainMatch) {
validation.valid = false;
validation.errors.push({
type: 'domain_mismatch',
message: `Certificate not valid for ${hostname}`
});
}
// Check protocol strength
if (certInfo.protocol && certInfo.protocol.includes('TLSv1.0')) {
validation.warnings.push({
type: 'weak_protocol',
message: 'TLS 1.0 is deprecated. Use TLS 1.2 or 1.3'
});
}
// Check cipher strength
if (certInfo.cipher && certInfo.cipher.name.includes('RC4')) {
validation.warnings.push({
type: 'weak_cipher',
message: 'Weak cipher suite detected. Update server configuration'
});
}
return validation;
}
function checkDomainMatch(certInfo, hostname) {
// Check subject CN
if (certInfo.subject.CN === hostname) {
return true;
}
// Check Subject Alternative Names
if (certInfo.subjectAltNames) {
const altNames = certInfo.subjectAltNames.split(',').map(s => s.trim().replace('DNS:', ''));
for (const altName of altNames) {
if (altName === hostname) {
return true;
}
// Check wildcard
if (altName.startsWith('*.')) {
const wildcardDomain = altName.substring(2);
if (hostname.endsWith(wildcardDomain)) {
return true;
}
}
}
}
return false;
}
Step 3: Security Audit
function auditSecurity(certInfo) {
const audit = {
score: 100,
issues: []
};
// Protocol check
if (certInfo.protocol) {
if (certInfo.protocol.includes('TLSv1.3')) {
audit.issues.push({ type: 'protocol', status: 'excellent', message: 'TLS 1.3 - Latest and most secure' });
} else if (certInfo.protocol.includes('TLSv1.2')) {
audit.issues.push({ type: 'protocol', status: 'good', message: 'TLS 1.2 - Secure' });
} else if (certInfo.protocol.includes('TLSv1.1') || certInfo.protocol.includes('TLSv1.0')) {
audit.score -= 30;
audit.issues.push({ type: 'protocol', status: 'poor', message: 'TLS 1.0/1.1 deprecated. Upgrade to TLS 1.2+' });
}
}
// Key strength check
if (certInfo.cipher) {
const keyBits = certInfo.cipher.bits || 0;
if (keyBits >= 256) {
audit.issues.push({ type: 'encryption', status: 'excellent', message: `${keyBits}-bit encryption - Very strong` });
} else if (keyBits >= 128) {
audit.issues.push({ type: 'encryption', status: 'good', message: `${keyBits}-bit encryption - Adequate` });
} else {
audit.score -= 40;
audit.issues.push({ type: 'encryption', status: 'poor', message: `${keyBits}-bit encryption - Too weak` });
}
}
// Expiry check
if (certInfo.daysRemaining < 0) {
audit.score = 0;
audit.issues.push({ type: 'expiry', status: 'critical', message: 'Certificate expired!' });
} else if (certInfo.daysRemaining < 7) {
audit.score -= 20;
audit.issues.push({ type: 'expiry', status: 'urgent', message: `Expires in ${certInfo.daysRemaining} days` });
} else if (certInfo.daysRemaining < 30) {
audit.score -= 10;
audit.issues.push({ type: 'expiry', status: 'warning', message: `Expires in ${certInfo.daysRemaining} days - renewal recommended` });
}
audit.score = Math.max(0, audit.score);
return audit;
}
Complete Checker Class
class SSLCertificateChecker {
constructor(hostname, port = 443) {
this.hostname = hostname;
this.port = port;
}
async check() {
try {
const certInfo = await checkSSLCertificate(this.hostname, this.port);
const validation = validateCertificate(certInfo, this.hostname);
const securityAudit = auditSecurity(certInfo);
return {
hostname: this.hostname,
port: this.port,
certificate: certInfo,
validation,
securityAudit,
timestamp: new Date().toISOString()
};
} catch (error) {
return {
hostname: this.hostname,
port: this.port,
error: error.message,
timestamp: new Date().toISOString()
};
}
}
}
// Usage
const checker = new SSLCertificateChecker('example.com');
checker.check().then(result => {
console.log('Certificate Info:', result.certificate);
console.log('Validation:', result.validation);
console.log('Security Audit:', result.securityAudit);
console.log('Security Score:', result.securityAudit.score);
});
Conclusion
Building an SSL certificate checker requires understanding TLS protocols, certificate validation, and security best practices. The checker we've built validates certificates, audits security configuration, and monitors expiry dates.
Try it yourself: https://techbelievers.com/tools/ssl-certificate-checker
Top comments (0)