Key pairs
Sign and verify messages and transactions using Ed25519 keys
Introduction
Kit uses the primitives built-in to JavaScript’s Web Crypto API to perform cryptography. This keeps your applications small, and confers to them the security and performance characteristics of the runtime's native cryptography functions.
Ed25519 keys created or imported using the native APIs are compatible with all of Kit's cryptographic features. Kit also offers several helpers to generate, import, and transform key material.
Looking for Signers instead?
Keys are a low-level primitive. While it's important to know how they work, in a Solana application it's often more appropriate to deal with key material in terms of the accounts and wallets that sign a transaction or message. The Signers API offers an ergonomic way to build and sign transactions using accounts and their associated keys.
Installation
Key management functions are included within the @solana/kit
library but you may also install them using their standalone package.
Not all runtimes support the cryptography required by Solana
When deploying your application to a JavaScript runtime that lacks support for the Ed25519
digital signature algorithm, import our polyfill before invoking any operations
that create or make use of CryptoKey
objects.
What is a key pair?
A key pair is a data type composed of 256-bits of random data (the private key) and a Cartesian point (the public key) on the curve that is used to cryptographically sign and verify Solana transactions. The interface definition of CryptoKeyPair
is:
Together, these keys represent an address on Solana and its owner. The 256-bit address is derived from the coordinates of the public key itself.
What is a key?
A key is an object native to the JavaScript runtime that supports the CryptoKey
interface. You can use a CryptoKey
with the native SubtleCrypto
API to sign messages and to verify signatures. Kit builds on these capabilities to enable the use of CryptoKey
objects to sign and verify Solana transactions and off-chain messages.
Managing keys
Generating new keys
You can use the native SubtleCrypto#generateKey
API, or the generateKeyPair()
helper to create a new random key pair.
This is useful in cases where you need to sign for the creation of a new account with a random address, using an ephemeral key pair that can be discarded after the account is created and assigned to a program.
Importing a key
You can create a CryptoKeyPair
using the 64 bytes of a pre-generated key. This is possible to do with the native SubtleCrypto#importKey
API, but it is more convenient to use the helpers in Kit.
In cases where you have only the 32 bytes representing the private key but not its associated public key, you can use a different function that will automatically derive the public key from the private key.
Storing keys
CryptoKey
objects can be stored locally by runtimes that support the IndexedDB API.
Given an instance of an IDBDatabase
with an object store called 'MyKeyPairStore'
:
You can store a key pair like this:
And then later retrieve it like this:
Keys stored in IndexedDB are local to the host, are subject to its storage limits and eviction criteria, and can not be accessed from a domain other than the one that stored them when the host is a web browser. Keys that are evicted from storage or erased by the user can generally not be recovered.
Exporting a key
You can obtain the 32 bytes of any public key like this:
Exporting private key material requires a specially constructed CryptoKey
with its extractable property set to true
.
You can then use that CryptoKey
with the SubtleCrypto#exportKey
API. The last 32 bytes of a PKCS#8 export of the key are the private key bytes.
Security warning
Exporting private key material in JavaScript is not recommended, and you should take extreme care when doing so. Private key bytes exported through these APIs are vulnerable to theft (eg. by code running in your JavaScript sandbox) and accidental logging (eg. to the console or to a third-party logger).
Using private keys
A private key is a CryptoKey
whose type
property is set to 'private'
and whose usages
property includes 'sign'
.
Private keys can be used by their owner to produce a digital signature of a specific message. You can think of a signature as representing the key owner's approval of, agreement to, or endorsement of the message. Private key owners must never share the key with anyone.
Signing messages
Any data that you can serialize as a Uint8Array
can be signed with a CryptoKey
.
Supply a message and a private key to the signBytes()
function to obtain a 64-byte digital signature.
The same message and private key might produce a different signature every time you call this function. Some runtimes produce randomized signatures as per draft-irtf-cfrg-det-sigs-with-noise while others produce deterministic signatures as per RFC 8032.
Signing transactions
In Kit, private keys are used to digitally sign transactions on behalf of account owners, to approve spending, transfers, and modifications to account data on the blockchain.
In practice, it's rare to sign transactions like this. Typically, you create a transaction message in your application, specify which accounts are required to sign it using the Signers API, and then call signTransactionMessageWithSigners
to turn it into a signed Transaction
.
Using public keys
A public key is a CryptoKey
whose type
property is set to 'public'
and whose usages
property includes 'verify'
.
Solana uses public keys to verify that modifications to account data and balances through transactions are approved of by those who hold the private keys required to authorize such modifications. Kit generally only uses public keys to derive the address of their associated accounts, but it can also use public keys to enable your programs to verify arbitrary messages.
Verifying signatures
The public key associated with a given private key can be used by anyone to verify that a message was signed by the holder of the associated private key, as described above. The verification function requires the original message, a digital signature, and the public key associated with the owner who is believed to have produced that signature. Successful verification implies that the contents of the message signed by the owner are identical to the contents of the message that was given to the verification function. Key owners are free to share their public key with anyone they would like to grant the ability to verify their digital signatures.
If someone sends us a message and signature that they claim to be from the example above, we could use Bob's public key to verify that the signature was in fact the one produced by Bob and that the message was not modified in transit.
Verification protects equally against false claims of who produced the signature as it does against false claims about what message was signed. Both of these will return false
;
Deriving addresses
The Solana address associated with any public key can be derived using the getAddressFromPublicKey()
function.
Signatures
The SignatureBytes
type represents a 64-byte Ed25519 signature, and the Signature
type represents such a signature as a base58-encoded string.
When you acquire a string that you expect to be a base58-encoded signature (eg. of a transaction) from an untrusted network API or user input you can assert it is in fact a base58-encoded byte array of sufficient length using the assertIsSignature()
function.
Similarly, you can use the isSignature()
type guard. It will return true
if the input string conforms to the Signature
type and will refine the type for use in your program from that point onward. This method does not throw in the opposite case.
The signature()
helper combines asserting that a string is an Ed25519 signature with coercing it to the Signature
type. It's best used with untrusted input.
Runtime Support
All major JavaScript runtimes support the Ed25519 digital signature algorithm required by Solana.
Runtime | Min version | Since | |
---|---|---|---|
Desktop browsers | Chrome | v137 | May 2025 |
Edge | v137 | May 2025 | |
Firefox | v130 | Sep 2024 | |
Safari | v17 | Sep 2023 | |
Mobile browsers | Android browser | v137 | May 2025 |
Firefox for Android | v139 | May 2025 | |
Mobile Safari | iOS 17 | Sep 2023 | |
Server runtimes | Bun | v1.2.6 | Mar 2025 |
Cloudflare workerd | v1.20230419.0 | Apr 2023 | |
Deno | v1.26.1 | Oct 2022 | |
Node.js | v18.4.0 | Jun 2022 | |
Vercel Edge | v2.3.0 | May 2023 |
For additional up-to-date details on runtimes' implementation status, visit https://github.com/WICG/webcrypto-secure-curves/issues/20.
Ed25519 Polyfill
To use keys in a runtime without Ed25519 digital signature algorithm support, install the following polyfill.
Then import and install it before invoking any operations that create or make use of CryptoKey
objects.
Wherever you call install()
, make sure the call is made only once, and before any key operation requiring Ed25519 support is performed.
Security warning
Because the polyfill's implementation of Ed25519 key generation exists in userspace, it can't guarantee that the keys you generate with it are non-exportable. Untrusted code running in your JavaScript context may still be able to gain access to and/or exfiltrate secret key material.
Storing polyfilled keys in IndexedDB
Native CryptoKeys
can be stored in IndexedDB, but the keys created by this polyfill can not.
This is because, unlike native CryptoKeys
, our polyfilled key objects can not implement the
structured clone algorithm.