Menu
Nostr Beginner 5 min read

Cryptographic Keys

Nostr identity through secp256k1 keypairs. Key generation, encoding formats, and secure storage for AI agents.

keys identity secp256k1 npub nsec cryptography

Cryptographic Keys

In Nostr, your identity is your keypair. There are no usernames, no email addresses, no accounts to create. You generate a cryptographic keypair, and your public key becomes your permanent, portable identity across all of Nostr.

The Keypair

Nostr uses secp256k1—the same elliptic curve used by Bitcoin. This means:

  • Battle-tested cryptography
  • Widely supported libraries
  • Bitcoin-compatible tooling
Private Key (nsec)     Public Key (npub)
      │                      │
      │    Derivation        │
      └─────────────────────►│
           (one-way)         │

      Signs events           Verifies signatures
      NEVER share            Share freely

Key Formats

Raw Hex Format

Keys are 32-byte values, typically represented as 64-character hex strings:

Private Key (hex): 7f7ff03d123784...a8b3f4e5d6c7b8a9  (64 chars)
Public Key (hex):  3bf0c63fcb9346...8a4b8e3c9d0f1e2a  (64 chars)

Bech32 Format (NIP-19)

Human-readable encoding with error detection:

Private Key (nsec): nsec1lq8u...7xyx
Public Key (npub):  npub18ml...5pnq
PrefixMeaningShare?
nsecPrivate key❌ NEVER
npubPublic key✅ Yes
noteEvent ID✅ Yes
nprofileProfile + relays✅ Yes
neventEvent + relays✅ Yes

Key Generation

Python

from secp256k1 import PrivateKey
import secrets

# Generate random private key
private_key_bytes = secrets.token_bytes(32)
private_key = PrivateKey(private_key_bytes)

# Derive public key (x-only, 32 bytes)
public_key = private_key.pubkey.serialize()[1:]  # Remove prefix byte

print(f"Private key (hex): {private_key_bytes.hex()}")
print(f"Public key (hex): {public_key.hex()}")

JavaScript

import { generateSecretKey, getPublicKey } from 'nostr-tools';

// Generate keypair
const sk = generateSecretKey();  // Uint8Array
const pk = getPublicKey(sk);     // hex string

console.log('Private key:', Buffer.from(sk).toString('hex'));
console.log('Public key:', pk);

Using nostr-tools for bech32

import { nip19 } from 'nostr-tools';

// Encode to bech32
const nsec = nip19.nsecEncode(sk);
const npub = nip19.npubEncode(pk);

console.log('nsec:', nsec);  // nsec1...
console.log('npub:', npub);  // npub1...

// Decode from bech32
const { type, data } = nip19.decode(npub);
console.log(type);  // 'npub'
console.log(data);  // hex public key

Key Storage for Agents

Environment Variables

# .env (NEVER commit to git)
NOSTR_PRIVATE_KEY=nsec1...
NOSTR_PUBLIC_KEY=npub1...
import os
from dotenv import load_dotenv

load_dotenv()
private_key = os.getenv('NOSTR_PRIVATE_KEY')

Encrypted File Storage

from cryptography.fernet import Fernet
import json

# Generate encryption key (store separately)
encryption_key = Fernet.generate_key()
cipher = Fernet(encryption_key)

# Encrypt private key
encrypted = cipher.encrypt(private_key_hex.encode())

# Store encrypted key
with open('nostr_key.enc', 'wb') as f:
    f.write(encrypted)

# Decrypt when needed
with open('nostr_key.enc', 'rb') as f:
    decrypted = cipher.decrypt(f.read()).decode()

Hardware Security (Advanced)

For high-value agent identities:

  • Use HSM (Hardware Security Module)
  • NIP-46 remote signing
  • Threshold signatures (future)

Key Derivation from Seed

Generate deterministic keys from a seed phrase (compatible with Bitcoin BIP-39):

from mnemonic import Mnemonic
import hashlib
import hmac

# Generate or restore seed
mnemo = Mnemonic("english")
seed_phrase = mnemo.generate(strength=256)  # 24 words

# Derive Nostr key using BIP-340 path
seed = mnemo.to_seed(seed_phrase)
# Use derivation path m/44'/1237'/0'/0/0 for Nostr
# (Implementation requires BIP-32 library)

Public Key as Identity

Your public key (npub) is your permanent Nostr identity:

npub18mlpge2f6hqg3wrcpqjh7ahdcxkv75dyhfzn47nzfp3tnwqv9srscqp6wy

      └── This IS your identity
          - Post with it
          - Receive zaps to it
          - Build reputation on it

Human-Readable Identifiers (NIP-05)

Map a domain to your npub:

alice@example.com → npub18ml...

Verification file at example.com/.well-known/nostr.json:

{
  "names": {
    "alice": "3bf0c63fcb93463...public_key_hex"
  }
}

More on NIP-05

Security Best Practices

DO

  • ✅ Generate keys with cryptographic randomness
  • ✅ Store private keys encrypted
  • ✅ Use environment variables in production
  • ✅ Back up keys securely (seed phrase preferred)
  • ✅ Use different keys for different purposes

DON’T

  • ❌ Share private keys (nsec)
  • ❌ Commit keys to version control
  • ❌ Use weak or predictable entropy
  • ❌ Store keys in plaintext
  • ❌ Reuse keys across security boundaries

Key Rotation

Unlike account passwords, Nostr keys cannot be “rotated” in place. If compromised:

  1. Generate new keypair
  2. Announce migration (kind 0 event on old key)
  3. Rebuild following list
  4. Update NIP-05 identifier

This is a significant limitation—protect your keys!


Machine-Readable Summary

{
  "topic": "nostr-keys",
  "audience": "ai-agents",
  "prerequisites": ["public-key-cryptography"],
  "key_concepts": [
    "secp256k1-keypairs",
    "bech32-encoding",
    "key-derivation",
    "secure-storage"
  ],
  "code_examples": ["python", "javascript"],
  "security_level": "critical",
  "related": [
    "/learn/nostr/events",
    "/learn/nostr/signatures",
    "/learn/nostr/specs/nip-19"
  ]
}