Use of Default Cryptographic Key

Description

Use of Default Cryptographic Key occurs when a product uses a default cryptographic key for potentially critical functionality, which simplifies manufacturing or installation but creates security risks if administrators fail to change these defaults. Products commonly employ default keys to streamline deployment processes across organizations. However, when these defaults remain unchanged, attackers can easily bypass authentication mechanisms, decrypt sensitive data, or forge signatures across multiple installations.

Risk

Default cryptographic keys have severe implications. Mass decryption of data across all affected installations. Authentication bypass through forged tokens. Signing key compromise enabling malicious code distribution. TLS/SSL interception using known keys. Session hijacking. Man-in-the-middle attacks. Firmware signing bypass. High likelihood as default keys are often documented or can be extracted from firmware.

Solution

Prohibit default, hard-coded values that do not vary per installation during requirements phase (high effectiveness). Force administrators to change credentials upon installation during architecture and design phase (high effectiveness). Allow product administrators to modify defaults during setup or operation (moderate effectiveness). Generate unique cryptographic keys per device during manufacturing or first boot.

Common Consequences

ImpactDetails
AuthenticationScope: Authentication

Attackers can gain privileges or assume identity using default cryptographic keys for authentication tokens.
ConfidentialityScope: Confidentiality

Encrypted data can be decrypted by anyone with knowledge of the default key.

Example Code

Vulnerable Code

# Vulnerable: Default cryptographic keys

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
import jwt
import base64

# VULNERABLE: Hard-coded encryption key
DEFAULT_ENCRYPTION_KEY = b'MySuperSecretKey1234567890123456'  # 32 bytes for AES-256
DEFAULT_FERNET_KEY = base64.urlsafe_b64encode(DEFAULT_ENCRYPTION_KEY)

# VULNERABLE: Default JWT secret
DEFAULT_JWT_SECRET = "your-256-bit-secret"

# VULNERABLE: Default API signing key
DEFAULT_API_KEY = "sk_live_default_key_12345"

class VulnerableEncryption:
    def __init__(self):
        # VULNERABLE: Uses default key
        self.cipher = Fernet(DEFAULT_FERNET_KEY)

    def encrypt(self, data):
        # VULNERABLE: All instances use same key
        return self.cipher.encrypt(data.encode())

    def decrypt(self, token):
        # VULNERABLE: Attacker with default key can decrypt
        return self.cipher.decrypt(token).decode()

class VulnerableJWT:
    def __init__(self):
        # VULNERABLE: Default signing secret
        self.secret = DEFAULT_JWT_SECRET

    def create_token(self, payload):
        # VULNERABLE: Anyone knowing default can forge tokens
        return jwt.encode(payload, self.secret, algorithm='HS256')

    def verify_token(self, token):
        # VULNERABLE: Forged tokens accepted
        return jwt.decode(token, self.secret, algorithms=['HS256'])

# VULNERABLE: TLS with default certificate/key
TLS_CONFIG = {
    "cert_file": "/etc/ssl/default_cert.pem",
    "key_file": "/etc/ssl/default_key.pem",
    # VULNERABLE: Same cert/key shipped with all devices
}

# VULNERABLE: Firmware signing with default key
FIRMWARE_SIGNING_KEY = """
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu
...
-----END RSA PRIVATE KEY-----
"""  # VULNERABLE: Signing key in source code

class VulnerableFirmwareSigner:
    def __init__(self):
        # VULNERABLE: Default signing key
        self.private_key = load_pem_private_key(
            FIRMWARE_SIGNING_KEY.encode(),
            password=None
        )

    def sign_firmware(self, firmware_data):
        # VULNERABLE: Anyone can sign "legitimate" firmware
        signature = self.private_key.sign(
            firmware_data,
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        return signature
// Vulnerable: Java with default cryptographic keys

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.KeyPair;
import java.security.KeyPairGenerator;

public class VulnerableCrypto {

    // VULNERABLE: Hard-coded AES key
    private static final byte[] DEFAULT_AES_KEY =
        "DefaultKey123456".getBytes();  // 16 bytes for AES-128

    // VULNERABLE: Hard-coded HMAC secret
    private static final String DEFAULT_HMAC_SECRET =
        "default_hmac_secret_key";

    // VULNERABLE: Default RSA key pair (would be loaded from file)
    private KeyPair defaultKeyPair;

    public VulnerableCrypto() {
        // VULNERABLE: Generates same "random" key if seeded consistently
        // Or loads from default location
    }

    public byte[] encrypt(byte[] data) throws Exception {
        // VULNERABLE: Uses default key for all encryptions
        SecretKeySpec keySpec = new SecretKeySpec(DEFAULT_AES_KEY, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(data);
    }

    // VULNERABLE: Configuration with default encryption key
    public void loadConfig() {
        String encryptionKey = System.getProperty("encryption.key");
        if (encryptionKey == null) {
            // VULNERABLE: Falls back to default
            encryptionKey = new String(DEFAULT_AES_KEY);
        }
    }
}

// VULNERABLE: SSL/TLS with default keystore
public class VulnerableSSL {
    // VULNERABLE: Default keystore password
    private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";  // Java default

    // VULNERABLE: Default truststore
    private static final String DEFAULT_TRUSTSTORE = "/etc/pki/java/cacerts";

    public SSLContext createSSLContext() throws Exception {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        // VULNERABLE: Uses default password
        keyStore.load(
            new FileInputStream("keystore.jks"),
            DEFAULT_KEYSTORE_PASSWORD.toCharArray()
        );
        // ...
    }
}
// Vulnerable: Node.js with default cryptographic keys

const crypto = require('crypto');
const jwt = require('jsonwebtoken');

// VULNERABLE: Hard-coded encryption key
const DEFAULT_ENCRYPTION_KEY = 'this-is-a-default-32-byte-key!!';
const DEFAULT_IV = 'default-iv-1234!';

// VULNERABLE: Default JWT signing key
const DEFAULT_JWT_SECRET = 'your-super-secret-jwt-key';

// VULNERABLE: Default API encryption
class VulnerableApiEncryption {
    constructor() {
        // VULNERABLE: Uses default key
        this.key = Buffer.from(DEFAULT_ENCRYPTION_KEY);
        this.iv = Buffer.from(DEFAULT_IV);
    }

    encrypt(data) {
        const cipher = crypto.createCipheriv('aes-256-cbc', this.key, this.iv);
        let encrypted = cipher.update(data, 'utf8', 'hex');
        encrypted += cipher.final('hex');
        return encrypted;
    }

    decrypt(data) {
        const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, this.iv);
        let decrypted = decipher.update(data, 'hex', 'utf8');
        decrypted += decipher.final('utf8');
        return decrypted;
    }
}

// VULNERABLE: Session encryption with default
const sessionConfig = {
    secret: DEFAULT_JWT_SECRET,  // VULNERABLE
    cookie: {
        secure: true
    }
};

// VULNERABLE: Webhook signature with default key
function signWebhook(payload) {
    // VULNERABLE: Anyone can forge webhook signatures
    const hmac = crypto.createHmac('sha256', DEFAULT_ENCRYPTION_KEY);
    hmac.update(JSON.stringify(payload));
    return hmac.digest('hex');
}

// VULNERABLE: Default SSH host key (copied from template)
const DEFAULT_SSH_HOST_KEY = `
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz
...
-----END OPENSSH PRIVATE KEY-----
`;  // VULNERABLE: Same key on all devices

Fixed Code

# Fixed: Proper cryptographic key management

import os
import secrets
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
import base64

class SecureEncryption:
    def __init__(self):
        # FIXED: Load key from secure source
        self.key = self._load_encryption_key()

    def _load_encryption_key(self):
        """FIXED: Load key from environment or generate new one."""
        key_b64 = os.environ.get('ENCRYPTION_KEY')

        if not key_b64:
            raise EnvironmentError(
                "ENCRYPTION_KEY not configured. "
                "Generate with: python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\""
            )

        # FIXED: Validate key format
        try:
            key = base64.urlsafe_b64decode(key_b64)
            if len(key) != 32:
                raise ValueError("Invalid key length")
        except Exception:
            raise ValueError("Invalid ENCRYPTION_KEY format")

        return key_b64.encode()

    def encrypt(self, data):
        cipher = Fernet(self.key)
        return cipher.encrypt(data.encode())

    @staticmethod
    def generate_key():
        """FIXED: Generate cryptographically secure key."""
        return Fernet.generate_key()

class SecureJWT:
    def __init__(self):
        # FIXED: Load secret from secure source
        self.secret = self._load_jwt_secret()

    def _load_jwt_secret(self):
        """FIXED: Require configured JWT secret."""
        secret = os.environ.get('JWT_SECRET')

        if not secret:
            raise EnvironmentError(
                "JWT_SECRET not configured. "
                "Generate with: python -c \"import secrets; print(secrets.token_hex(32))\""
            )

        # FIXED: Validate minimum length
        if len(secret) < 32:
            raise ValueError("JWT_SECRET too short (minimum 32 characters)")

        return secret

    @staticmethod
    def generate_secret():
        """FIXED: Generate secure JWT secret."""
        return secrets.token_hex(32)

# FIXED: First-run key generation
class SecureKeyManager:
    @staticmethod
    def setup():
        """FIXED: Interactive key setup."""
        print("=== Cryptographic Key Setup ===\n")

        # FIXED: Generate unique keys
        encryption_key = Fernet.generate_key().decode()
        jwt_secret = secrets.token_hex(32)
        api_key = secrets.token_urlsafe(32)

        print("Add these to your environment:\n")
        print(f"export ENCRYPTION_KEY='{encryption_key}'")
        print(f"export JWT_SECRET='{jwt_secret}'")
        print(f"export API_SIGNING_KEY='{api_key}'")

        print("\nWARNING: Store these securely and never commit to version control!")

# FIXED: Per-device key generation during manufacturing
class SecureProvisioning:
    def provision_device(self, serial_number):
        """FIXED: Generate unique keys per device."""
        # FIXED: Generate unique device encryption key
        device_key = Fernet.generate_key()

        # FIXED: Generate unique signing key pair
        from cryptography.hazmat.primitives.asymmetric import rsa
        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )

        # FIXED: Store in secure element or encrypted storage
        return {
            "serial": serial_number,
            "encryption_key": device_key,
            "signing_key": private_key
        }
// Fixed: Java with proper key management

import java.security.KeyStore;
import java.security.SecureRandom;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class SecureCrypto {

    // FIXED: No hard-coded keys

    private SecretKey encryptionKey;

    public SecureCrypto() throws Exception {
        this.encryptionKey = loadEncryptionKey();
    }

    private SecretKey loadEncryptionKey() throws Exception {
        // FIXED: Load from environment or secure storage
        String keyB64 = System.getenv("ENCRYPTION_KEY");

        if (keyB64 == null || keyB64.isEmpty()) {
            throw new SecurityException(
                "ENCRYPTION_KEY not configured. Generate using setup command."
            );
        }

        // FIXED: Decode and validate
        byte[] keyBytes = Base64.getDecoder().decode(keyB64);
        if (keyBytes.length != 32) {  // AES-256
            throw new SecurityException("Invalid key length");
        }

        return new SecretKeySpec(keyBytes, "AES");
    }

    // FIXED: Generate secure key
    public static String generateKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(256, new SecureRandom());
        SecretKey key = keyGen.generateKey();
        return Base64.getEncoder().encodeToString(key.getEncoded());
    }

    // FIXED: Setup command
    public static void main(String[] args) throws Exception {
        if (args.length > 0 && "--generate-keys".equals(args[0])) {
            System.out.println("=== Key Generation ===\n");
            System.out.println("ENCRYPTION_KEY=" + generateKey());
            System.out.println("JWT_SECRET=" + generateJWTSecret());
            System.out.println("\nStore securely!");
            return;
        }

        // Normal operation
        SecureCrypto crypto = new SecureCrypto();
    }

    private static String generateJWTSecret() {
        byte[] bytes = new byte[32];
        new SecureRandom().nextBytes(bytes);
        return Base64.getEncoder().encodeToString(bytes);
    }
}

// FIXED: Secure keystore handling
public class SecureSSL {

    public SSLContext createSSLContext() throws Exception {
        // FIXED: Require keystore password from environment
        String password = System.getenv("KEYSTORE_PASSWORD");
        if (password == null) {
            throw new SecurityException("KEYSTORE_PASSWORD not configured");
        }

        // FIXED: Reject well-known default passwords
        if ("changeit".equals(password) || "password".equals(password)) {
            throw new SecurityException("Default keystore password detected");
        }

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(
            new FileInputStream(System.getenv("KEYSTORE_PATH")),
            password.toCharArray()
        );
        // ...
    }
}
// Fixed: Node.js with proper key management

const crypto = require('crypto');

// FIXED: No hard-coded keys

class SecureEncryption {
    constructor() {
        this.key = this._loadEncryptionKey();
    }

    _loadEncryptionKey() {
        const keyHex = process.env.ENCRYPTION_KEY;

        if (!keyHex) {
            throw new Error(
                'ENCRYPTION_KEY not configured. ' +
                'Generate with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"'
            );
        }

        // FIXED: Validate key format
        if (keyHex.length !== 64) {  // 32 bytes = 64 hex chars
            throw new Error('Invalid ENCRYPTION_KEY length');
        }

        return Buffer.from(keyHex, 'hex');
    }

    encrypt(data) {
        // FIXED: Generate random IV for each encryption
        const iv = crypto.randomBytes(16);
        const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv);
        let encrypted = cipher.update(data, 'utf8', 'hex');
        encrypted += cipher.final('hex');
        // FIXED: Include IV with ciphertext
        return iv.toString('hex') + ':' + encrypted;
    }

    decrypt(data) {
        const [ivHex, encryptedHex] = data.split(':');
        const iv = Buffer.from(ivHex, 'hex');
        const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv);
        let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
        decrypted += decipher.final('utf8');
        return decrypted;
    }

    // FIXED: Key generation helper
    static generateKey() {
        return crypto.randomBytes(32).toString('hex');
    }
}

// FIXED: Secure JWT configuration
function getJWTSecret() {
    const secret = process.env.JWT_SECRET;

    if (!secret) {
        throw new Error('JWT_SECRET not configured');
    }

    if (secret.length < 32) {
        throw new Error('JWT_SECRET too short');
    }

    // FIXED: Check for obvious defaults
    const defaults = ['secret', 'your-secret', 'jwt-secret', 'changeme'];
    if (defaults.some(d => secret.toLowerCase().includes(d))) {
        throw new Error('JWT_SECRET appears to be a default value');
    }

    return secret;
}

// FIXED: Setup script
if (process.argv.includes('--generate-keys')) {
    console.log('=== Key Generation ===\n');
    console.log(`ENCRYPTION_KEY=${crypto.randomBytes(32).toString('hex')}`);
    console.log(`JWT_SECRET=${crypto.randomBytes(32).toString('hex')}`);
    console.log(`API_KEY=${crypto.randomBytes(24).toString('base64url')}`);
    console.log('\nAdd these to your .env file (never commit to git!)');
    process.exit(0);
}

CVE Examples

  • CVE-2018-3825: Cloud cluster management with default master encryption key.
  • CVE-2016-1561: Backup storage with default SSH public key in authorized_keys.
  • CVE-2010-2306: IDS using identical static SSL keys across multiple devices.

  • CWE-1392: Use of Default Credentials (parent)
  • CWE-321: Use of Hard-coded Cryptographic Key (related)
  • CWE-798: Use of Hard-coded Credentials (related)

References

  1. MITRE Corporation. "CWE-1394: Use of Default Cryptographic Key." https://cwe.mitre.org/data/definitions/1394.html
  2. NIST SP 800-57. "Recommendation for Key Management"
  3. OWASP. "Cryptographic Failures"