Skip to main content
Cryptographic keys are fundamental to blockchain security. hive-tx provides complete implementations of Hive’s key system through the PrivateKey, PublicKey, and Signature classes.

Key Hierarchy

Hive uses ECDSA (secp256k1) keys, the same cryptographic system as Bitcoin. Each account has multiple key types with different permission levels:
1

Owner Key

Highest authority. Used to change all other keys and recover accounts. Keep this offline and secure.
2

Active Key

Financial authority. Required for transfers, powering up/down, and account updates.
3

Posting Key

Content authority. Used for voting, posting, following, and other social actions. Safe for apps.
4

Memo Key

Encryption authority. Used to encrypt/decrypt transfer memos. Not used for signing transactions.
Never share your private keys with untrusted applications. Posting keys are safer for apps since they can’t access funds.

PrivateKey Class

The PrivateKey class handles all private key operations: creation, signing, and key derivation.

Creating Private Keys

From WIF String

Wallet Import Format (WIF) is the standard way to store and transmit private keys:
src/helpers/PrivateKey.ts:57
import { PrivateKey } from 'hive-tx'

const key = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw')

Generate Random Key

Create a cryptographically secure random key:
src/helpers/PrivateKey.ts:179
const randomKey = PrivateKey.randomKey()
console.log(randomKey.toString()) // WIF format
Random key generation may take up to 250ms due to secure entropy collection. This is normal and ensures cryptographic security.

From Seed

Derive a key from a seed string or bytes:
src/helpers/PrivateKey.ts:83
// From string seed
const keyFromSeed = PrivateKey.fromSeed('my-secret-seed')

// From hex seed
const keyFromHex = PrivateKey.fromSeed('a1b2c3d4e5f6...')

// From bytes
const bytes = new Uint8Array([1, 2, 3, ...])
const keyFromBytes = PrivateKey.fromSeed(bytes)
The seed is hashed with SHA256 to produce the 32-byte private key.

From Login Credentials

Generate keys from username and password using Hive’s standard derivation:
src/helpers/PrivateKey.ts:105
const postingKey = PrivateKey.fromLogin('alice', 'password', 'posting')
const activeKey = PrivateKey.fromLogin('alice', 'password', 'active')
const ownerKey = PrivateKey.fromLogin('alice', 'password', 'owner')
const memoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
This uses the formula: SHA256(username + role + password)
Login-based keys match what Hive wallets generate. This is perfect for apps that authenticate with username/password instead of storing WIF keys.

Signing Messages

Sign a 32-byte hash to create a recoverable signature:
src/helpers/PrivateKey.ts:117
import { sha256 } from '@noble/hashes/sha2'

const message = new TextEncoder().encode('Hello Hive')
const hash = sha256(message)  // Must be 32 bytes

const signature = key.sign(hash)
console.log(signature.customToString()) // 130-character hex string
The sign() method expects a 32-byte hash, not the raw message. Always hash your message first with SHA256.

Deriving Public Keys

Every private key has a corresponding public key:
src/helpers/PrivateKey.ts:133
const privateKey = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41Ek...')
const publicKey = privateKey.createPublic()

console.log(publicKey.toString())
// Output: STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA
Optionally specify a custom address prefix:
const testnetPubKey = privateKey.createPublic('TST')
// Output: TST8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA

Exporting Private Keys

As WIF String

src/helpers/PrivateKey.ts:143
const wif = key.toString()
console.log(wif) // 5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw

Safe Logging

For debugging without exposing the full key:
src/helpers/PrivateKey.ts:154
console.log(key.inspect())
// Output: PrivateKey: 5JdeC9...NsQnsw
Only the first and last 6 characters are shown.

Shared Secrets for Encryption

Compute ECDH shared secrets for memo encryption:
src/helpers/PrivateKey.ts:166
const alicePrivate = PrivateKey.fromLogin('alice', 'password', 'memo')
const bobPublic = PublicKey.fromString('STM7...')

const sharedSecret = alicePrivate.getSharedSecret(bobPublic)
// Returns 64-byte Uint8Array used for AES encryption
Both parties derive the same shared secret:
  • Alice: alicePrivate.getSharedSecret(bobPublic)
  • Bob: bobPrivate.getSharedSecret(alicePublic)
This enables encrypted memos that only the sender and receiver can read.
The shared secret is SHA512(ECDH(privateKey, publicKey)). The parity byte is stripped before hashing.

PublicKey Class

Public keys are derived from private keys and used for signature verification and encryption.

Creating Public Keys

From String

src/helpers/PublicKey.ts:30
import { PublicKey } from 'hive-tx'

const pubKey = PublicKey.fromString('STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA')

From Private Key

const privateKey = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41Ek...')
const publicKey = privateKey.createPublic()

Verifying Signatures

Check if a signature is valid for a given message:
src/helpers/PublicKey.ts:54
import { sha256 } from '@noble/hashes/sha2'

const message = new TextEncoder().encode('Hello Hive')
const hash = sha256(message)

const isValid = publicKey.verify(hash, signature)
console.log(isValid) // true or false
You can pass either a Signature object or a hex string:
// With Signature object
const sig = Signature.from('1f4c3d2e...')
const valid1 = publicKey.verify(hash, sig)

// With hex string
const valid2 = publicKey.verify(hash, '1f4c3d2e...')

Exporting Public Keys

src/helpers/PublicKey.ts:68
const str = publicKey.toString()
console.log(str) // STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA

const json = publicKey.toJSON() // Same as toString()

Signature Class

Signatures prove that a message was signed by a specific private key.

Creating Signatures

From Signing

const privateKey = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41Ek...')
const hash = sha256(new TextEncoder().encode('message'))
const signature = privateKey.sign(hash)

From Hex String

src/helpers/Signature.ts:28
const sig = Signature.from('1f4c3d2e1a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e')
Signatures are 130 hex characters (65 bytes):
  • 1 byte: Recovery ID (allows public key recovery)
  • 64 bytes: ECDSA signature (r, s values)

Signature Format

As Hex String

src/helpers/Signature.ts:65
const hex = signature.customToString()
console.log(hex) // 130-character hex string

As Buffer

src/helpers/Signature.ts:50
const buffer = signature.toBuffer()
console.log(buffer) // Uint8Array(65)

Recovering Public Keys

Signatures are “recoverable” - you can extract the public key that created them:
src/helpers/Signature.ts:75
const message = sha256(new TextEncoder().encode('Hello'))
const recoveredPubKey = signature.getPublicKey(message)

console.log(recoveredPubKey.toString())
// STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA
You can pass either a Uint8Array hash or a 64-character hex string:
// From hash bytes
const pubKey1 = signature.getPublicKey(hashBytes)

// From hex string
const pubKey2 = signature.getPublicKey('a1b2c3d4...')
Signature recovery is how Hive nodes verify transactions without storing public keys separately. The signature itself contains enough information to derive the signer’s public key.

Key Formats

Private Key (WIF)

Wallet Import Format includes network ID and checksum:
5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw
│└─────────────────────────────────────────────────┘
└─ Always starts with '5' for Hive mainnet
Encoding: base58(0x80 + 32-byte-key + 4-byte-checksum)

Public Key

Includes address prefix and checksum:
STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA
│  └────────────────────────────────────────────────┘
└─ Address prefix (STM for mainnet, TST for testnet)
Encoding: prefix + base58(33-byte-compressed-key + 4-byte-checksum)

Signature

130-character hex string:
1f 4c3d2e1a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e
││ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
│└─ 64-byte ECDSA signature (r, s)
└─ 1-byte recovery ID

Security Best Practices

  • Never hardcode private keys in source code
  • Use environment variables or secure key management systems
  • Encrypt keys at rest with strong passwords
  • Use hardware wallets for owner/active keys when possible
  • Use posting keys for apps when possible (can’t access funds)
  • Never send private keys over unencrypted connections
  • Validate key formats before using them
  • Clear key data from memory after use in sensitive contexts
  • Keep owner keys offline and secure (paper wallet, hardware wallet)
  • Use active keys only for financial operations
  • Share posting keys with trusted apps for convenience
  • Rotate keys periodically, especially if compromised
  • Use testnet accounts for development
  • Never commit real keys to version control
  • Use .env files with .gitignore for local development
  • Audit dependencies that handle keys

Complete Example

Here’s a complete workflow showing key generation, signing, and verification:
import { PrivateKey, PublicKey, Signature } from 'hive-tx'
import { sha256 } from '@noble/hashes/sha2'

// Generate keys from login
const postingKey = PrivateKey.fromLogin('alice', 'password', 'posting')
const publicKey = postingKey.createPublic()

console.log('Private Key (WIF):', postingKey.toString())
console.log('Public Key:', publicKey.toString())

// Sign a message
const message = 'I agree to the terms'
const messageHash = sha256(new TextEncoder().encode(message))
const signature = postingKey.sign(messageHash)

console.log('Signature:', signature.customToString())

// Verify signature
const isValid = publicKey.verify(messageHash, signature)
console.log('Signature valid:', isValid) // true

// Recover public key from signature
const recoveredKey = signature.getPublicKey(messageHash)
console.log('Keys match:', recoveredKey.toString() === publicKey.toString()) // true

// Create shared secret for encryption
const aliceMemoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
const bobMemoKey = PrivateKey.fromLogin('bob', 'bobpassword', 'memo')

const aliceSecret = aliceMemoKey.getSharedSecret(bobMemoKey.createPublic())
const bobSecret = bobMemoKey.getSharedSecret(aliceMemoKey.createPublic())

console.log('Secrets match:', 
  Buffer.from(aliceSecret).toString('hex') === Buffer.from(bobSecret).toString('hex')
) // true

Next Steps

Transactions

Use keys to sign transactions

Operations

Learn which operations need which keys

Memo Encryption

Encrypt transfer memos

PrivateKey API

Full API reference

Build docs developers (and LLMs) love