Skip to main content
Private keys are the foundation of security on Hive. This guide covers how to generate, derive, and manage keys safely.

Key Types Overview

Hive accounts have four key types, each with different permissions:
Key TypePermission LevelUse Cases
OwnerHighestChange passwords, recover account, update authority
ActiveHighTransfers, power up/down, witness votes, proposals
PostingMediumPost, comment, vote, follow, reblog
MemoSpecialEncrypt/decrypt private messages
Never use your owner key for daily operations. Store it securely offline and only use it for account recovery or key changes.

Generating Random Keys

Create a new cryptographically secure random key:
1

Generate a random key

import { PrivateKey } from 'hive-tx'

const privateKey = PrivateKey.randomKey()
console.log('Private key (WIF):', privateKey.toString())
// Output: 5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw
Key generation may take up to 250ms due to entropy collection for cryptographic security.
2

Get the public key

const publicKey = privateKey.createPublic()
console.log('Public key:', publicKey.toString())
// Output: STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA
3

Store safely

// Export for secure storage
const wif = privateKey.toString()

// Store in environment variables or secure key management system
// NEVER commit keys to version control

Deriving Keys from Username and Password

Hive uses a deterministic key derivation scheme. You can generate the same keys from a username, password, and role:
import { PrivateKey } from 'hive-tx'

const username = 'alice'
const masterPassword = 'my-secure-password-123'

// Derive different role keys
const ownerKey = PrivateKey.fromLogin(username, masterPassword, 'owner')
const activeKey = PrivateKey.fromLogin(username, masterPassword, 'active')
const postingKey = PrivateKey.fromLogin(username, masterPassword, 'posting')
const memoKey = PrivateKey.fromLogin(username, masterPassword, 'memo')

console.log('Owner:', ownerKey.toString())
console.log('Active:', activeKey.toString())
console.log('Posting:', postingKey.toString())
console.log('Memo:', memoKey.toString())
Key derivation formula:
seed = username + role + password
private_key = SHA256(seed)
This is the same derivation used by Hive wallets. If you have your master password, you can always recover your keys.

Creating Keys from a Seed

Generate deterministic keys from a seed string or bytes:
import { PrivateKey } from 'hive-tx'

// From a string seed
const seed = 'my-secure-random-seed-phrase'
const key1 = PrivateKey.fromSeed(seed)

// From hex string
const hexSeed = 'a1b2c3d4e5f6...'
const key2 = PrivateKey.fromSeed(hexSeed)

// From bytes
const bytesSeed = new Uint8Array([1, 2, 3, 4, ...])
const key3 = PrivateKey.fromSeed(bytesSeed)

console.log(key1.toString())
The seed is hashed with SHA256 to produce the private key. The same seed always produces the same key.

Loading Existing Keys

Import keys from WIF (Wallet Import Format) strings:
import { PrivateKey } from 'hive-tx'

// From WIF string
const wif = '5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw'
const key = PrivateKey.from(wif)

// Or using fromString explicitly
const key2 = PrivateKey.fromString(wif)

// From Uint8Array (32 bytes)
const keyBytes = new Uint8Array(32) // your key bytes
const key3 = PrivateKey.from(keyBytes)

Working with Public Keys

Derive public keys from private keys:
import { PrivateKey } from 'hive-tx'

const privateKey = PrivateKey.from('5J...')

// Get public key with default prefix (STM)
const publicKey = privateKey.createPublic()
console.log(publicKey.toString())
// Output: STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA

// Get public key with custom prefix
const publicKeyCustom = privateKey.createPublic('TST')
console.log(publicKeyCustom.toString())
// Output: TST8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA

Secure Key Storage

Environment Variables

Store keys in environment variables:
# .env file (NEVER commit to git!)
HIVE_POSTING_KEY=5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw
HIVE_ACTIVE_KEY=5K...
import { PrivateKey } from 'hive-tx'
import * as dotenv from 'dotenv'

dotenv.config()

const postingKey = PrivateKey.from(process.env.HIVE_POSTING_KEY!)
const activeKey = PrivateKey.from(process.env.HIVE_ACTIVE_KEY!)

Encrypted Storage

For production applications, use encrypted key storage:
import { PrivateKey } from 'hive-tx'
import * as crypto from 'crypto'

// Encrypt a private key
function encryptKey(privateKey: PrivateKey, password: string): string {
  const algorithm = 'aes-256-gcm'
  const salt = crypto.randomBytes(32)
  const key = crypto.scryptSync(password, salt, 32)
  const iv = crypto.randomBytes(16)
  
  const cipher = crypto.createCipheriv(algorithm, key, iv)
  const wif = privateKey.toString()
  
  let encrypted = cipher.update(wif, 'utf8', 'hex')
  encrypted += cipher.final('hex')
  
  const authTag = cipher.getAuthTag()
  
  return JSON.stringify({
    encrypted,
    iv: iv.toString('hex'),
    salt: salt.toString('hex'),
    authTag: authTag.toString('hex')
  })
}

// Decrypt a private key
function decryptKey(encryptedData: string, password: string): PrivateKey {
  const data = JSON.parse(encryptedData)
  const algorithm = 'aes-256-gcm'
  
  const salt = Buffer.from(data.salt, 'hex')
  const key = crypto.scryptSync(password, salt, 32)
  const iv = Buffer.from(data.iv, 'hex')
  const authTag = Buffer.from(data.authTag, 'hex')
  
  const decipher = crypto.createDecipheriv(algorithm, key, iv)
  decipher.setAuthTag(authTag)
  
  let decrypted = decipher.update(data.encrypted, 'hex', 'utf8')
  decrypted += decipher.final('utf8')
  
  return PrivateKey.from(decrypted)
}

// Usage
const privateKey = PrivateKey.randomKey()
const encrypted = encryptKey(privateKey, 'my-app-password')

// Store encrypted in database
// ...

// Later, decrypt when needed
const decrypted = decryptKey(encrypted, 'my-app-password')

Key Security Best Practices

Never commit private keys to version control. Add .env and key files to .gitignore.
Never log private keys. Use the inspect() method for safe logging.
import { PrivateKey } from 'hive-tx'

const key = PrivateKey.randomKey()

// BAD - logs full private key
console.log(key.toString())
// Output: 5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw

// GOOD - logs masked version
console.log(key.inspect())
// Output: PrivateKey: 5JdeC9...BsQnsw
Use the minimum required key authority. Use posting keys for social actions, active keys for financial operations.
Rotate keys periodically. Update your active and posting keys every few months for security.
Use hardware wallets for owner keys and large amounts. Keep owner keys completely offline.

Signing Messages

Sign arbitrary 32-byte hashes:
import { PrivateKey } from 'hive-tx'
import { sha256 } from '@noble/hashes/sha2'

const privateKey = PrivateKey.from('5J...')
const message = 'Hello, Hive!'

// Hash the message (must be 32 bytes)
const messageHash = sha256(new TextEncoder().encode(message))

// Sign the hash
const signature = privateKey.sign(messageHash)
console.log('Signature:', signature.toString())

Shared Secrets (ECDH)

Compute shared secrets for memo encryption:
import { PrivateKey, PublicKey } from 'hive-tx'

const alicePrivate = PrivateKey.randomKey()
const bobPrivate = PrivateKey.randomKey()

const alicePublic = alicePrivate.createPublic()
const bobPublic = bobPrivate.createPublic()

// Both parties can compute the same shared secret
const aliceShared = alicePrivate.getSharedSecret(bobPublic)
const bobShared = bobPrivate.getSharedSecret(alicePublic)

// aliceShared === bobShared
console.log('Shared secret length:', aliceShared.length) // 64 bytes
Shared secrets are used internally by the Memo class for encrypted messages. See the Memo Encryption guide for details.

Key Validation

Validate key format on import:
import { PrivateKey } from 'hive-tx'

try {
  const key = PrivateKey.from('invalid-key-format')
} catch (error) {
  console.error('Invalid key:', error.message)
  // Error: invalid private key
}

try {
  const key = PrivateKey.fromString('5J...')
  console.log('Valid key')
} catch (error) {
  if (error.message.includes('checksum mismatch')) {
    console.error('Key checksum failed - corrupted or incorrect key')
  } else if (error.message.includes('network id mismatch')) {
    console.error('Wrong network - this key is not for Hive')
  } else {
    console.error('Invalid key format')
  }
}

Complete Key Management Example

import { PrivateKey, PublicKey, Transaction } from 'hive-tx'
import * as dotenv from 'dotenv'

dotenv.config()

class KeyManager {
  private postingKey?: PrivateKey
  private activeKey?: PrivateKey
  
  constructor(private username: string) {}
  
  // Load keys from environment
  loadFromEnv() {
    const postingWif = process.env.HIVE_POSTING_KEY
    const activeWif = process.env.HIVE_ACTIVE_KEY
    
    if (postingWif) {
      this.postingKey = PrivateKey.from(postingWif)
    }
    if (activeWif) {
      this.activeKey = PrivateKey.from(activeWif)
    }
  }
  
  // Load keys from password
  loadFromPassword(password: string) {
    this.postingKey = PrivateKey.fromLogin(this.username, password, 'posting')
    this.activeKey = PrivateKey.fromLogin(this.username, password, 'active')
  }
  
  // Get key by type
  getKey(type: 'posting' | 'active'): PrivateKey {
    const key = type === 'posting' ? this.postingKey : this.activeKey
    if (!key) {
      throw new Error(`${type} key not loaded`)
    }
    return key
  }
  
  // Sign and broadcast a transaction
  async signAndBroadcast(
    tx: Transaction,
    keyType: 'posting' | 'active'
  ): Promise<string> {
    const key = this.getKey(keyType)
    tx.sign(key)
    const result = await tx.broadcast(true)
    return result.tx_id
  }
}

// Usage
const keyManager = new KeyManager('alice')
keyManager.loadFromEnv()

const tx = new Transaction()
await tx.addOperation('vote', {
  voter: 'alice',
  author: 'bob',
  permlink: 'post',
  weight: 10000
})

const txId = await keyManager.signAndBroadcast(tx, 'posting')
console.log('Transaction ID:', txId)

Next Steps

Build docs developers (and LLMs) love