Private key is the single source of truth in blockchain. No recovery password, no support desk, no transaction rollback. Key compromise = permanent loss of all assets under its control. According to analytics, over 50% of DeFi protocol hacks result from private key leaks, with annual damage exceeding $2 billion. Yet most organizations store keys in .env files on servers or, worse, in developers' personal wallets. In real projects we've seen keys hidden in Git comments and Docker layers — each such finding eventually leads to an incident. We offer end-to-end development of a corporate key management system — from current threat landscape audit to HSM deployment and signing policies. Our engineers have 10+ years of experience in cryptography and blockchain infrastructure. Let's explore which architectures truly protect assets.
What threats does corporate key management address?
Before choosing technologies, you need to clearly define threats.
- External attacker: server breach, credential leakage, SQL injection in adjacent systems. The attacker gains access to the execution environment.
- Malicious insider: an employee with legitimate access attempts to use the key unauthorized or steal it.
- Infrastructure failure: server with key goes down at the worst possible moment. Replication without compromising security is required.
- Supply chain attack: compromised dependency, modified Docker image, BGP hijack by cloud provider.
Different threats require different protective measures. There is no single correct solution — there is a set of tools with different trade-offs between security and convenience.
Key Protection Levels
Hardware Security Modules (HSM)
HSM is a physical device that stores key material and performs cryptographic operations inside a tamper-proof enclave. The key never leaves the device in plaintext. HSM provides 100x stronger protection against key extraction compared to software storage. Average signing time is less than 10 ms, availability is 99.99%.
Cloud options: AWS CloudHSM, Azure Dedicated HSM, Google Cloud HSM. On-premise: Thales Luna, Entrust nShield. For blockchain, YubiHSM 2 is often used (affordable option around $500).
# Interaction with HSM via PKCS#11 (standard interface)
import pkcs11
from pkcs11 import Mechanism, KeyType, ObjectClass
def sign_ethereum_transaction_with_hsm(
tx_hash: bytes,
slot_id: int,
pin: str,
key_label: str
) -> tuple[int, int, int]:
"""
Signs a transaction hash with a private key inside HSM
Returns (v, r, s) signature components
"""
lib = pkcs11.lib('/usr/lib/softhsm/libsofthsm2.so') # or path to real HSM
token = lib.get_token(slot_id=slot_id)
with token.open(user_pin=pin) as session:
# Find key by label
private_key = session.get_key(
object_class=ObjectClass.PRIVATE_KEY,
key_type=KeyType.EC,
label=key_label
)
# Signing happens inside HSM, key never exported
signature = private_key.sign(tx_hash, mechanism=Mechanism.ECDSA)
# Convert DER signature to (r, s)
r, s = decode_ecdsa_signature(signature)
v = determine_recovery_id(tx_hash, r, s)
return v, r, s
def generate_key_in_hsm(session, label: str) -> None:
"""Generates a key pair inside HSM"""
# Key is generated inside HSM and never leaves in plainview
pub, priv = session.generate_keypair(
KeyType.EC,
key_length=256,
curve='secp256k1',
public_template={
pkcs11.Attribute.LABEL: label,
pkcs11.Attribute.TOKEN: True,
pkcs11.Attribute.VERIFY: True,
},
private_template={
pkcs11.Attribute.LABEL: label,
pkcs11.Attribute.TOKEN: True,
pkcs11.Attribute.PRIVATE: True,
pkcs11.Attribute.SENSITIVE: True,
pkcs11.Attribute.SIGN: True,
pkcs11.Attribute.EXTRACTABLE: False,
}
)
Multi-Party Computation (MPC)
MPC is a technology where the key never exists in full form on a single device. Several participants store key shares (shards), and transaction signing occurs through a cryptographic protocol without assembling the full key. Signing takes ~200 ms, gas saved up to 30% compared to multisig.
Used in Fireblocks, Zengo, Qredo, Coinbase Prime. Pre-standard protocols: GG18/GG20 (Lindell et al.), FROST (Flexible Round-Optimized Schnorr Threshold Signatures).
MPC signing scheme (simplified):
Participant A has: key_share_A
Participant B has: key_share_B
Participant C has: key_share_C
For signing, any 2 of 3 are needed (2-of-3 scheme):
1. A and B start the protocol
2. Exchange commitments (without revealing shards)
3. Compute partial signatures
4. Aggregate: signature = combine(partial_A, partial_B)
5. Result — valid ECDSA signature
6. key_share_A and key_share_B NEVER existed on the same device
Advantages of MPC over on-chain multisig: the signature looks like a regular single-sig transaction (less gas, does not reveal governance structure), policy enforcement off-chain (any rules can be added without changing the smart contract).
How to choose between MPC and Multisig
It's important not to confuse MPC threshold signatures with on-chain multisig (Gnosis Safe). These are different approaches with different trade-offs:
| Characteristic | MPC (TSS) | On-chain Multisig (Gnosis Safe) |
|---|---|---|
| Gas cost | Standard signature | Depends on scheme (higher) |
| Transparency | Governance structure not visible | All signers public on-chain |
| Off-chain policy | Full flexibility | None |
| Signing audit | Off-chain log | On-chain history |
| Shard recovery | More complex | Simple (add/remove owner) |
| Maturity | Relatively new | Battle-tested for years |
For most organizations: Gnosis Safe for large cold storage (transparency more important than gas), MPC for operational hot wallets (speed and policy flexibility).
Transaction Authorization Policies
Keys are only part of the system. Equally important is defining who can sign transactions, when, and under what conditions.
Policy Engine
interface TransactionPolicy {
id: string;
name: string;
conditions: PolicyCondition[];
requiredApprovals: number;
approvers: string[];
maxAmountUsd?: number;
allowedContracts?: string[];
allowedChains?: number[];
timeRestrictions?: TimeRestriction;
}
interface PolicyCondition {
type: 'amount' | 'contract' | 'method' | 'time' | 'chain';
operator: 'eq' | 'lt' | 'gt' | 'in' | 'not_in';
value: unknown;
}
class PolicyEngine {
private policies: TransactionPolicy[];
async evaluateTransaction(tx: PendingTransaction): Promise<PolicyResult> {
const matchingPolicies = this.findMatchingPolicies(tx);
if (matchingPolicies.length === 0) {
return {
allowed: false,
reason: 'No matching policy',
requiresManualReview: true
};
}
// Take the most restrictive applicable policy
const strictestPolicy = this.getMostRestrictivePolicy(matchingPolicies);
// Check limits
if (strictestPolicy.maxAmountUsd) {
const txValueUsd = await this.getTransactionValueUsd(tx);
if (txValueUsd > strictestPolicy.maxAmountUsd) {
return {
allowed: false,
reason: `Exceeds limit: $${txValueUsd} > $${strictestPolicy.maxAmountUsd}`,
requiresApproval: true,
approvers: strictestPolicy.approvers
};
}
}
// Check contract whitelist
if (strictestPolicy.allowedContracts &&
tx.to &&
!strictestPolicy.allowedContracts.includes(tx.to.toLowerCase())) {
return {
allowed: false,
reason: 'Contract not in whitelist',
requiresManualReview: true
};
}
return { allowed: true, policy: strictestPolicy };
}
}
Authorization Levels
Typical corporate hierarchy:
| Level | Transaction Amount | Required Confirmations |
|---|---|---|
| Automatic | up to $1,000 | No human |
| Single person | $1,000–$50,000 | One employee |
| Multi-person | $50,000–$500,000 | M of N confirmations |
| Board-level | over $500,000 | Committee, physical meeting |
Key Lifecycle Management
Key lifecycle: generation → registration → operational use → rotation → revocation. Each stage requires separate procedures.
Generation must occur in a trusted environment (HSM, air-gapped machine). Entropy source is verified. Generation is documented with witness signatures.
Backup sharding: keys are backed up using Shamir's Secret Sharing. 3-of-5 scheme: five shards distributed across different physical locations, three sufficient for recovery.
from secretsharing import PlaintextToHexSecretSharer
def backup_private_key(private_key_hex: str, shares: int = 5, threshold: int = 3) -> list[str]:
"""
Splits a private key into N shards, of which threshold are sufficient for recovery
Shards are stored separately: different people, different physical locations
"""
shards = PlaintextToHexSecretSharer.split_secret(
private_key_hex,
threshold,
shares
)
return shards
def recover_private_key(shards: list[str]) -> str:
"""Recovers the key from any threshold shards"""
return PlaintextToHexSecretSharer.recover_secret(shards)
# Backup procedure:
# 1. Air-gapped machine, no network
# 2. Generate 5 shards
# 3. Each shard on a separate iron key + paper backup
# 4. Envelopes sealed, signed by witnesses
# 5. Stored in different safes (office, bank safe deposit box, home safe of key employees)
How Shamir's Secret Sharing Works
Shamir's Secret Sharing is an algorithm that splits a secret into shares (shards) such that recovery requires a threshold number of shares. In a 3-of-5 scheme, any 3 shares allow reconstructing the original key, but 2 shares yield no information. This provides fault tolerance and security: loss of one or two shards is not critical, and theft of less than three is useless.
Mathematical details of Shamir's scheme
Secret S is split into n shares using a polynomial of degree k-1. Coefficients are random, the constant term equals S. Recovery requires k points, through which the polynomial is interpolated and the constant term found.
Key rotation: planned (once a year) and unplanned (suspicion of compromise, employee with access leaves). For on-chain contracts, requires changing the owner via multisig.
Revocation: upon compromise, immediate transfer of assets to a new key. You cannot simply "block" a key in the blockchain.
Audit and Monitoring
Every key usage must be logged: who requested signing, what was signed, who authorized, timestamp, IP, device fingerprint.
interface KeyUsageEvent {
eventId: string;
timestamp: Date;
keyId: string;
operation: 'sign' | 'derive' | 'export_public' | 'rotate';
requestedBy: string; // user ID or service account
approvedBy: string[]; // if approval was required
transactionHash?: string; // if transaction
transactionData?: {
to: string;
value: string;
chainId: number;
methodSignature?: string;
};
policyId: string;
ipAddress: string;
deviceId: string;
approved: boolean;
rejectionReason?: string;
}
// All events are signed by an HSM audit key — cannot be forged
// Stored in append-only storage (Kafka, CloudTrail, immutable S3 bucket)
Anomalies for alerting: signing outside business hours, atypical destination addresses, transaction volume above historical norm, attempts to sign rejected transactions.
According to the NIST SP 800-57 standard, key management must include full audit of all operations.
Disaster Recovery
A recovery plan must be documented and regularly tested (tabletop exercises). Scenarios: loss of one server with key, loss of data center, compromise of one shard, departure of key keeper.
RTO (Recovery Time Objective) for different levels:
| Level | Recovery Time Objective |
|---|---|
| Hot wallet | Minutes (automatic failover) |
| Cold storage | Hours (multi-person procedure) |
| Full key change | 24–48 hours |
Regular tests: quarterly recovery simulation in staging environment. Without testing, a recovery plan is just a document.
What's Included in the Work
- Audit of current key management scheme and threat landscape
- Architecture design (HSM, MPC, policy engine)
- Development and configuration of components
- Integration with existing tools (AWS, Hashicorp Vault, Kubernetes)
- Documentation of procedures and recovery plan
- Team training (key keepers, administrators)
- Post-deployment support
Stack and Development Timeline
Infrastructure: AWS CloudHSM or YubiHSM 2, Hashicorp Vault (secrets and policy management), Kubernetes for signing services.
MPC libraries: tss-lib (Go, GG20 implementation), ZenGo-X/multi-party-ecdsa (Rust), Fireblocks SDK for enterprise.
Backend: Go or Rust for performance-critical signing service, Node.js/Python for API gateway.
Audit: security audit of the key management system is mandatory, ideally with penetration testing.
Development timeline for a corporate KMS: 4–6 months for a production-grade system with HSM, MPC, and policy engine. Integrating with Gnosis Safe or Fireblocks instead of custom MPC cuts the time in half.
Evaluate your current key management setup — contact us for a free consultation. Order corporate KMS development and receive an architecture review as a gift.







