Get a Pentest and security assessment of your IT network.

Cyber Security

Client-Side Private Key Storage

TL;DR

Storing private keys on a client is inherently risky. The best approach balances security with usability, typically involving encrypted storage within the user’s browser or operating system. Avoid storing keys in plain text at all costs. This guide covers several methods, from basic to more advanced, outlining their pros and cons.

1. Understand the Risks

Before diving into solutions, acknowledge the dangers:

  • Malware: Keyloggers or other malicious software can steal keys directly.
  • Browser Security: Vulnerabilities in browsers or extensions could compromise storage.
  • Operating System Compromise: If the OS is infected, keys are at risk.
  • User Error: Users might accidentally share or lose access to their keys.

No solution is foolproof. Mitigation focuses on reducing these risks.

2. Basic Approach: Browser Local Storage (Discouraged)

While simple, storing encrypted keys in browser local storage is not recommended for sensitive applications due to security limitations. It’s vulnerable to XSS attacks and can be accessed by malicious extensions.

// Example - DO NOT USE IN PRODUCTION!  This is illustrative only.
const encryptionKey = 'your-secret-encryption-key'; // Replace with a strong key
const encryptedPrivateKey = encrypt('my private key', encryptionKey);
localStorage.setItem('privateKey', encryptedPrivateKey);

Why it’s bad: The encryptionKey is in your JavaScript code, making it easily discoverable.

3. Better Approach: Web Crypto API & Browser Key Storage

The Web Crypto API offers more secure encryption options using the browser’s built-in cryptographic functions. This allows you to encrypt the private key before storing it in local storage, and ideally use a password derived key.

  1. Generate a Random Encryption Key: Use crypto.getRandomValues() to create a strong, random encryption key.
  2. Encrypt the Private Key: Employ algorithms like AES-GCM for symmetric encryption.
  3. Store Encrypted Data: Save the encrypted private key in browser local storage.
  4. Password Protection (Recommended): Derive an encryption key from a user’s password using PBKDF2 or Argon2 before encrypting the private key. This adds another layer of security.
// Example - Simplified for illustration.
async function encryptPrivateKey(privateKey, password) {
  const encoder = new TextEncoder();
  const encodedPassword = encoder.encode(password);
  const keyMaterial = await crypto.subtle.importKey("raw", encodedPassword, { name: "PBKDF2", hash: "SHA-256" }, true, ["encrypt", "decrypt"])
  const iv = crypto.getRandomValues(new Uint8Array(16));
  const encrypted = await crypto.subtle.encrypt({
    name: 'AES-GCM',
    iv: iv,
  }, keyMaterial, encoder.encode(privateKey));
  return { ciphertext: Array.from(new Uint8Array(encrypted)), iv: Array.from(iv) };
}

Important: Handle the encryption key securely. Never store it directly in your code.

4. Advanced Approach: Operating System Key Stores

For higher security, leverage operating system-level key stores (e.g., Windows Credential Manager, macOS Keychain). This moves the private key storage outside of the browser’s control and into a more secure environment.

  • WebAuthn API: Use WebAuthn to register a public/private key pair with the OS key store.
  • Key Management Libraries: Employ libraries that abstract the complexities of interacting with OS key stores (e.g., @webauthn/key-management).

Benefits:

  • Hardware Security: Keys are often protected by hardware security modules (HSMs) or trusted platform modules (TPMs).
  • OS Protection: Benefit from the OS’s built-in security features.

Drawbacks:

  • Complexity: Requires more development effort.
  • Browser Compatibility: WebAuthn support varies across browsers.

5. Considerations for User Experience

  • Password Recovery: Implement a secure password recovery mechanism if using password-derived keys.
  • Key Backup: Provide users with options to back up their keys (e.g., seed phrases, encrypted key files).
  • Clear Communication: Explain the risks and benefits of storing private keys on the client to users.

6. Best Practices

  • Never store unencrypted keys.
  • Use strong encryption algorithms (AES-GCM, ChaCha20-Poly1305).
  • Employ a password-derived key for added security.
  • Regularly audit your code for vulnerabilities.
  • Keep dependencies up to date.
Related posts
Cyber Security

Zip Codes & PII: Are They Personal Data?

Cyber Security

Zero-Day Vulnerabilities: User Defence Guide

Cyber Security

Zero Knowledge Voting with Trusted Server

Cyber Security

ZeroNet: 51% Attack Risks & Mitigation