gchq/CyberChef · error · OperationError

Did not provide passphrase with locked private key.

Error message

Did not provide passphrase with locked private key.

What it means

importPrivateKey detects that the supplied PGP secret key is passphrase-locked (key.is_pgp_locked() === true) but the caller did not pass a passphrase. Without unlocking, the key material is unusable for signing or decryption, so the import aborts with an explicit cause rather than a downstream obscure failure.

Source

Thrown at src/core/lib/PGP.mjs:91

* @param {string} privateKey
* @param {string} [passphrase]
* @returns {Object}
*/
export async function importPrivateKey(privateKey, passphrase) {
    try {
        const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
            armored: privateKey,
            opts: {
                "no_check_keys": true
            }
        });
        if (key.is_pgp_locked()) {
            if (passphrase) {
                await promisify(key.unlock_pgp.bind(key))({
                    passphrase
                });
            } else {
                throw new OperationError("Did not provide passphrase with locked private key.");
            }
        }
        return key;
    } catch (err) {
        throw new OperationError(`Could not import private key: ${err}`);
    }
}

/**
 * Import public key
 *
 * @param {string} publicKey
 * @returns {Object}
 */
export async function importPublicKey (publicKey) {
    try {
        const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
            armored: publicKey,

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Provide the passphrase: importPrivateKey(privateKey, passphrase).
  2. Source the passphrase from a secure prompt or secret store rather than hardcoding.
  3. If you never want a passphrase, regenerate the key without one (kbpgp KeyManager.generate).
  4. Detect the locked state in your UI and surface a passphrase input before calling import.

Example fix

// before
const key = await PGP.importPrivateKey(armored); // locked key, no passphrase

// after
const key = await PGP.importPrivateKey(armored, passphrase);
// or prompt:
const passphrase = await promptUser('Private key passphrase');
const key = await PGP.importPrivateKey(armored, passphrase);
Defensive patterns

Strategy: validation

Validate before calling

async function importPrivateKeySafe(armored, passphrase) {
  // first import without unlocking to probe the locked state
  const probe = await importKbpgpKey(armored);
  if (probe.is_pgp_locked() && !passphrase) {
    throw new Error('This key is locked; a passphrase is required.');
  }
  return PGP.importPrivateKey(armored, passphrase);
}

Try / catch

try {
  return await PGP.importPrivateKey(armored, passphrase);
} catch (e) {
  if (/Did not provide passphrase/.test(e.message)) {
    passphrase = await promptUser('Private key passphrase');
    return PGP.importPrivateKey(armored, passphrase);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling importPrivateKey(armoredPrivateKey) or importPrivateKey(armoredPrivateKey, '') / undefined when the armored key was generated with a passphrase. Also if the passphrase argument is omitted entirely.

Common situations: User pastes a locked private key but leaves the passphrase field blank in the UI; passphrase stored in a separate secret manager that was not loaded; recipe/automation script hardcoded to skip the passphrase argument; key was just generated and locked by default.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/d897386c22b01938. Report an issue: GitHub.