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:
Owner Key
Highest authority. Used to change all other keys and recover accounts. Keep this offline and secure.
Active Key
Financial authority. Required for transfers, powering up/down, and account updates.
Posting Key
Content authority. Used for voting, posting, following, and other social actions. Safe for apps.
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)
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.
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