docker/cli · error

provided file is not a supported private key - to add a…

Error message

provided file %s is not a supported private key - to add a signer's public key use docker trust signer add

What it means

In loadPrivKeyBytesToStore (key_load.go:94-97), tufutils.ExtractPrivateKeyAttributes could not parse the bytes as a supported private key PEM (it failed to extract role/attributes). The loader concludes the file is not a supported private key and suggests using 'docker trust signer add' for public keys. This is the first check in the import pipeline.

Solutions

  1. Confirm the file is a private key PEM: head -1 <file> should show '-----BEGIN EC PRIVATE KEY-----' or '-----BEGIN RSA PRIVATE KEY-----' / '-----BEGIN PRIVATE KEY-----'.
  2. If you actually have a public key for a signer, use 'docker trust signer add <name> <key.pub> <image>' instead of 'docker trust key load'.
  3. Convert the key to a supported PEM private key format (e.g. openssl ec -in ... -out key.pem) and retry.
  4. Re-export the private key from the original source (notary key export) ensuring it is the private PEM with the role header.

Example fix

# before: passing a public key by mistake
docker trust key load mykey.pub
# after (option A): load the private key
docker trust key load mykey.pem
# after (option B): adding a signer's public key
docker trust signer add team-a mykey.pub myrepo/img
Defensive patterns

Strategy: type-guard

Validate before calling

// Sniff the PEM block type before loading to give a precise error.
func isPrivateKeyPEM(path string) (bool, error) {
    b, err := os.ReadFile(path)
    if err != nil {
        return false, err
    }
    block, _ := pem.Decode(b)
    if block == nil {
        return false, nil
    }
    switch block.Type {
    case "EC PRIVATE KEY", "RSA PRIVATE KEY", "PRIVATE KEY", "ENCRYPTED PRIVATE KEY":
        return true, nil
    default:
        return false, nil
    }
}

Type guard

// Guard that a file is a private key before invoking load.
func assertPrivateKeyFile(path string) error {
    ok, err := isPrivateKeyPEM(path)
    if err != nil {
        return err
    }
    if !ok {
        return fmt.Errorf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", path)
    }
    return nil
}

Try / catch

if _, _, err := tufutils.ExtractPrivateKeyAttributes(privKeyBytes); err != nil {
    return fmt.Errorf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", keyPath)
}

Prevention

When it happens

Trigger: Loading a file that is actually a PUBLIC key PEM ('PUBLIC KEY' block) rather than a private key; a certificate; an SSH public key; a random text/JSON file; a private key in an unsupported format (e.g. SSH private key, raw DER without PEM envelope, OpenSSH new format); a corrupt or truncated PEM.

Common situations: User confused public and private key files and passes the .pub; exported a key in a format notary does not consume (PKCS#8 vs the expected notary/TUF key PEM with role header); downloaded the wrong artifact from a key management system; concatenated/corrupted PEM.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/ec25eef693280181. Report an issue: GitHub.

Appendix: source

Thrown at cmd/docker-trust/trust/key_load.go:96

		}
		if fileInfo.Mode()&nonOwnerReadWriteMask != 0 {
			return nil, fmt.Errorf("private key file %s must not be readable or writable by others", keyPath)
		}
	}

	from, err := os.OpenFile(keyPath, os.O_RDONLY, notary.PrivExecPerms)
	if err != nil {
		return nil, err
	}
	defer from.Close()

	return io.ReadAll(from)
}

func loadPrivKeyBytesToStore(privKeyBytes []byte, privKeyImporters []trustmanager.Importer, keyPath, keyName string, passRet notary.PassRetriever) error {
	var err error
	if _, _, err = tufutils.ExtractPrivateKeyAttributes(privKeyBytes); err != nil {
		return fmt.Errorf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", keyPath)
	}
	if privKeyBytes, err = decodePrivKeyIfNecessary(privKeyBytes, passRet); err != nil {
		return fmt.Errorf("cannot load key from provided file %s: %w", keyPath, err)
	}
	// Make a reader, rewind the file pointer
	return trustmanager.ImportKeys(bytes.NewReader(privKeyBytes), privKeyImporters, keyName, "", passRet)
}

func decodePrivKeyIfNecessary(privPemBytes []byte, passRet notary.PassRetriever) ([]byte, error) {
	pemBlock, _ := pem.Decode(privPemBytes)
	_, containsDEKInfo := pemBlock.Headers["DEK-Info"]
	if containsDEKInfo || pemBlock.Type == "ENCRYPTED PRIVATE KEY" {
		// if we do not have enough information to properly import, try to decrypt the key
		if _, ok := pemBlock.Headers["path"]; !ok {
			privKey, _, err := trustmanager.GetPasswdDecryptBytes(passRet, privPemBytes, "", "encrypted")
			if err != nil {
				return []byte{}, errors.New("could not decrypt key")
			}

View on GitHub (pinned to 4f84911bfe)