Use of a Key Past its Expiration Date

Description

Use of a Key Past its Expiration Date is a vulnerability that occurs when a product uses cryptographic keys or passwords beyond their intended expiration dates. Cryptographic keys have expiration dates to limit the window during which a compromised key can be used, to ensure keys are refreshed with stronger algorithms over time, and to enforce good key management practices. When applications continue using expired keys, they extend the risk window for brute force attacks, may use outdated cryptographic strength, and undermine the key lifecycle management designed to maintain security.

Risk

Using expired cryptographic keys significantly increases security risk through several mechanisms. Extended key lifetime increases the total volume of data encrypted with that key, providing more material for cryptanalysis. Longer usage windows increase the probability of key compromise through operational exposure. Expired keys may use outdated key lengths or algorithms that have become vulnerable. Organizations may stop protecting keys after intended expiration, assuming they're no longer in use. Certificate expiration serves as a lifecycle management checkpoint - ignoring it removes this safeguard. The risk compounds over time: keys in use for years accumulate exposure through backups, logs, personnel changes, and potential breaches. Continued trust in expired credentials also signals poor security practices that may extend to other areas.

Solution

Implement proper key lifecycle management that enforces expiration dates. Check key and certificate expiration dates before use and reject expired credentials. Implement automated key rotation that generates and distributes new keys before expiration. Notify users and administrators before key expiration with clear guidance on renewal. Design systems to gracefully handle key transitions without service interruption. Maintain key inventories tracking all keys and their expiration dates. Use certificate management systems that automate renewal processes. For passwords, implement aging policies that require periodic changes. Configure systems to refuse connections using expired certificates. Implement monitoring and alerting for approaching key expirations.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Expired keys that have been compromised can continue to be used for authentication or decryption, allowing attackers to impersonate legitimate users or access protected data.
ConfidentialityScope: Confidentiality

Data protected by expired keys may be at increased risk if the keys have been compromised during their extended use beyond the intended security window.

Example Code

Vulnerable Code (C/Java)

The following examples demonstrate accepting expired certificates and keys:

// Vulnerable: SSL certificate validation ignoring expiration
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <time.h>

int vulnerable_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
    int err = X509_STORE_CTX_get_error(ctx);

    // Check not-yet-valid
    if (err == X509_V_ERR_CERT_NOT_YET_VALID) {
        return 0;  // Reject
    }

    // Vulnerable: No check for expiration!
    // X509_V_ERR_CERT_HAS_EXPIRED is not handled
    // Expired certificates are accepted!

    return preverify_ok;
}

// Vulnerable: Manual check missing expiration
int vulnerable_check_certificate(X509 *cert) {
    // Check if certificate is valid yet
    if (X509_cmp_time(X509_get_notBefore(cert), NULL) > 0) {
        return 0;  // Not yet valid
    }

    // Vulnerable: Missing expiration check!
    // Should check: X509_cmp_time(X509_get_notAfter(cert), NULL) < 0

    return 1;  // Accepts expired certificates!
}

// Vulnerable: API key without expiration check
typedef struct {
    char key[64];
    time_t created;
    time_t expires;  // Set but never checked!
} ApiKey;

int vulnerable_validate_api_key(ApiKey *key) {
    // Vulnerable: Only checks if key exists
    if (strlen(key->key) > 0) {
        return 1;  // Valid - but may be expired!
    }
    return 0;
}
// Vulnerable: Java certificate validation ignoring expiration
import java.security.cert.*;
import javax.net.ssl.*;

public class VulnerableCertValidator {

    // Vulnerable: Custom TrustManager ignoring expiration
    public static TrustManager[] getVulnerableTrustManagers() {
        return new TrustManager[] {
            new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String auth) {
                    // Vulnerable: No expiration check!
                }

                public void checkServerTrusted(X509Certificate[] chain, String auth)
                        throws CertificateException {
                    for (X509Certificate cert : chain) {
                        // Vulnerable: Only checks if not yet valid
                        try {
                            cert.checkValidity();
                        } catch (CertificateNotYetValidException e) {
                            throw e;  // Reject not-yet-valid
                        } catch (CertificateExpiredException e) {
                            // Vulnerable: Ignoring expiration!
                            System.out.println("Warning: Certificate expired (ignored)");
                        }
                    }
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }
        };
    }

    // Vulnerable: License key without expiration enforcement
    public boolean validateLicense(LicenseKey license) {
        // Vulnerable: Only validates signature, not expiration
        return license.verifySignature();
        // license.getExpirationDate() is never checked!
    }
}
# Vulnerable: Python certificate handling ignoring expiration
import ssl
from datetime import datetime
from cryptography import x509

def vulnerable_validate_certificate(cert_pem):
    cert = x509.load_pem_x509_certificate(cert_pem.encode())

    # Check if certificate is valid yet
    if cert.not_valid_before > datetime.utcnow():
        return False  # Not yet valid

    # Vulnerable: No expiration check!
    # Should check: cert.not_valid_after < datetime.utcnow()

    return True  # Accepts expired certificates!

# Vulnerable: JWT token without expiration check
import jwt

def vulnerable_validate_token(token, secret):
    try:
        # Vulnerable: Not verifying expiration
        payload = jwt.decode(
            token,
            secret,
            algorithms=['HS256'],
            options={'verify_exp': False}  # Expiration check disabled!
        )
        return payload
    except jwt.InvalidTokenError:
        return None

Fixed Code (C/Java)

// Fixed: Proper certificate expiration validation
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <time.h>

int strict_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
    if (!preverify_ok) {
        int err = X509_STORE_CTX_get_error(ctx);

        // Fixed: Explicitly handle expiration errors
        if (err == X509_V_ERR_CERT_HAS_EXPIRED) {
            X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
            char subject[256];
            X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof(subject));
            fprintf(stderr, "Certificate expired: %s\n", subject);
            return 0;  // Reject expired certificates
        }

        if (err == X509_V_ERR_CERT_NOT_YET_VALID) {
            return 0;  // Reject not-yet-valid
        }

        return 0;  // Reject all other errors
    }

    return 1;
}

// Fixed: Complete certificate validity check
int secure_check_certificate(X509 *cert) {
    time_t now = time(NULL);

    // Fixed: Check not-yet-valid
    if (X509_cmp_time(X509_get0_notBefore(cert), &now) > 0) {
        fprintf(stderr, "Certificate not yet valid\n");
        return 0;
    }

    // Fixed: Check expiration
    if (X509_cmp_time(X509_get0_notAfter(cert), &now) < 0) {
        fprintf(stderr, "Certificate has expired\n");
        return 0;
    }

    // Fixed: Warning for certificates expiring soon
    time_t warning_threshold = now + (30 * 24 * 60 * 60);  // 30 days
    if (X509_cmp_time(X509_get0_notAfter(cert), &warning_threshold) < 0) {
        fprintf(stderr, "Warning: Certificate expires within 30 days\n");
    }

    return 1;
}

// Fixed: API key with expiration enforcement
typedef struct {
    char key[64];
    time_t created;
    time_t expires;
} ApiKey;

int secure_validate_api_key(ApiKey *key) {
    if (strlen(key->key) == 0) {
        return 0;  // Empty key
    }

    // Fixed: Check expiration
    time_t now = time(NULL);
    if (now > key->expires) {
        fprintf(stderr, "API key has expired\n");
        return 0;  // Reject expired keys
    }

    // Fixed: Warning for keys expiring soon
    if (now > key->expires - (7 * 24 * 60 * 60)) {  // 7 days
        fprintf(stderr, "Warning: API key expires soon\n");
    }

    return 1;
}
// Fixed: Java certificate validation with proper expiration checking
import java.security.cert.*;
import javax.net.ssl.*;
import java.util.Date;

public class SecureCertValidator {

    // Fixed: TrustManager that properly checks expiration
    public static SSLContext getSecureSSLContext() throws Exception {
        // Use default trust manager which checks expiration
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
        tmf.init((KeyStore) null);

        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
        sslContext.init(null, tmf.getTrustManagers(), null);

        return sslContext;
    }

    // Fixed: Explicit expiration check for additional validation
    public static void validateCertificateExpiration(X509Certificate cert)
            throws CertificateException {

        Date now = new Date();

        // Fixed: Check if certificate is valid (includes expiration)
        try {
            cert.checkValidity(now);
        } catch (CertificateExpiredException e) {
            throw new CertificateException("Certificate has expired", e);
        } catch (CertificateNotYetValidException e) {
            throw new CertificateException("Certificate not yet valid", e);
        }

        // Fixed: Warn if expiring soon
        Date warningDate = new Date(System.currentTimeMillis() +
                                    30L * 24 * 60 * 60 * 1000);  // 30 days
        if (cert.getNotAfter().before(warningDate)) {
            System.err.println("Warning: Certificate expires on " +
                              cert.getNotAfter());
        }
    }

    // Fixed: License validation with expiration
    public boolean validateLicense(LicenseKey license) {
        // Fixed: Check signature
        if (!license.verifySignature()) {
            return false;
        }

        // Fixed: Check expiration
        Date now = new Date();
        if (license.getExpirationDate().before(now)) {
            System.err.println("License has expired");
            return false;
        }

        return true;
    }
}
# Fixed: Proper expiration validation
from datetime import datetime, timedelta
from cryptography import x509
import jwt

def secure_validate_certificate(cert_pem):
    cert = x509.load_pem_x509_certificate(cert_pem.encode())
    now = datetime.utcnow()

    # Fixed: Check not-yet-valid
    if cert.not_valid_before > now:
        raise ValueError("Certificate not yet valid")

    # Fixed: Check expiration
    if cert.not_valid_after < now:
        raise ValueError("Certificate has expired")

    # Fixed: Warning for certificates expiring soon
    warning_threshold = now + timedelta(days=30)
    if cert.not_valid_after < warning_threshold:
        print(f"Warning: Certificate expires on {cert.not_valid_after}")

    return True

# Fixed: JWT with expiration verification
def secure_validate_token(token, secret):
    try:
        # Fixed: Verify expiration (default behavior)
        payload = jwt.decode(
            token,
            secret,
            algorithms=['HS256'],
            options={
                'verify_exp': True,  # Verify expiration (default)
                'require': ['exp', 'iat']  # Require these claims
            }
        )
        return payload
    except jwt.ExpiredSignatureError:
        raise ValueError("Token has expired")
    except jwt.InvalidTokenError as e:
        raise ValueError(f"Invalid token: {e}")

# Fixed: API key validation with expiration
class SecureApiKeyValidator:
    def validate(self, api_key):
        key_record = self.get_key_record(api_key)

        if not key_record:
            return False

        # Fixed: Check expiration
        now = datetime.utcnow()
        if key_record.expires_at < now:
            self.log_expired_key_use(api_key)
            return False

        # Fixed: Warn about soon-expiring keys
        if key_record.expires_at < now + timedelta(days=7):
            self.notify_key_expiration(api_key, key_record.expires_at)

        return True

The fix properly validates expiration dates and rejects expired credentials.


Exploited in the Wild

Hospital PACS System (Healthcare, 2021)

CVE-2021-33020 documented a hospital PACS (Picture Archiving and Communication System) that used cryptographic keys past their expiration dates, compromising patient data security.

Expired Certificate Acceptance (Various, Ongoing)

Multiple applications have been found accepting expired SSL/TLS certificates, enabling man-in-the-middle attacks.


Tools to Test/Exploit


CVE Examples


References

  1. MITRE Corporation. "CWE-324: Use of a Key Past its Expiration Date." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/324.html

  2. NIST. "Recommendation for Key Management." SP 800-57. https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final

  3. OWASP Foundation. "Key Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html