A CLI tool for creating keys and encoding strings.
This exposes a command crypt.
Install globally:
npm i -g @substrate-system/cryptOr install locally, and run with npx:
npm i -D @substrate-system/crypt
npx crypt keysFor Ed25519 keys, this will print a JSON string with
{ publicKey, privateKey } to stdout. By default (format raw):
publicKeyis in multikey formatprivateKeyis the base64url-encoded seed
For RSA keys, the private key is written to a file (requires -o option)
and the public key is printed to stdout in multikey format.
You can pass in a different encoding to use for the output with
the -f or --format option.
# Generate Ed25519 keypair (outputs both keys to stdout)
npx crypt keys
# Generate RSA keypair, write private key to file
npx crypt keys rsa -o private.pem
# Generate RSA keypair in JWK format (outputs both keys to stdout)
npx crypt keys rsa -f jwk
# Generate Ed25519 keypair with DID format
npx crypt keys -f did
# Convert between encoding formats via stdin
echo "Hello World" | npx crypt encode base64
# => SGVsbG8gV29ybGQ
echo "SGVsbG8gV29ybGQ" | npx crypt encode utf8 -i base64
# => Hello World
echo "SGVsbG8gV29ybGQ" | npx crypt encode hex -i base64
# => 48656c6c6f20576f726c64npx crypt --help
# Show help for a specific command
npx crypt keys --help
npx crypt encode --helpGenerate a new cryptographic keypair, by default ed25519.
Ed25519 keys: Output is a JSON string of { publicKey, privateKey }
to stdout. By default (-f raw), the public key is in multikey format and
the private key is a base64url-encoded seed. Use -f jwk for JWK format.
If -o is specified, the private key is written to the file and only the
public key is printed to stdout.
RSA keys: Requires -o option to specify output file for the private key
(in PKCS#8 PEM format), unless using -f jwk which outputs both keys as
JSON to stdout.
algorithm- The algorithm to use (default:ed25519)ed25519- Ed25519 elliptic curversa- RSA-PSS 2048-bit
-
-f, --format- Output format (default:raw)raw- (default) For Ed25519: public key in multikey format, private key as base64url-encoded seed. For RSA: requires-ooption.jwk- JSON Web Key format (outputs private key JWK, which includes public key inxfield for Ed25519)
-
-o, --output- Output file for private key- Required for RSA (unless using
-f jwk) - Optional for Ed25519 (if specified, private key goes to file instead of stdout)
- RSA private keys are written as PKCS#8 PEM format
- Ed25519 private keys are written as base64url-encoded seed (default)
or JWK format (if using
-f jwk)
- Required for RSA (unless using
-
-m, --multi- Use multibase encoding with prefixes (default:false)- When enabled, adds the appropriate multibase prefix for the encoding format
- Prefixes:
m(base64),M(base64pad),u(base64url),U(base64urlpad),z(base58btc),f(hex)
# Generate Ed25519 keypair (default: raw format)
npx crypt keys
# => {"publicKey":"z6Mk...", "privateKey":"mZ5t7zw8D..."}
# (publicKey is multikey format, privateKey is base64url-encoded seed)
# Generate Ed25519 keypair in JWK format
npx crypt keys -f jwk
# => {"key_ops":["sign"],"ext":true,"crv":"Ed25519","d":"...","x":"...","kty":"OKP","alg":"Ed25519"}
# (Returns private key JWK; public key is in the 'x' field)
# Generate Ed25519 keypair, save private key to file
npx crypt keys -o private.txt
# => {"publicKey":"z6Mk..."}
# (private.txt contains the base64url-encoded seed)
# Generate RSA keypair, save private key to file as PEM
npx crypt keys rsa -o private.pem
# => {"publicKey":"z5Tc..."}
# (private.pem contains PKCS#8 PEM format)
# Generate RSA keypair in JWK format
npx crypt keys rsa -f jwk
# => {...JWK with private key...}
# (Returns private key JWK; public key components are included)Convert a string from one encoding format to another. Reads input from stdin.
output-format- The desired output format (default:base58btc)base58btc,base64,hex,base64url,utf8,ascii
-
-i, --input-format- The format of the input string (default:utf8)base64,hex,base64url,base58btc,utf8,ascii
-
-m, --multi- Use multibase encoding with prefixes (default:false)- Add the appropriate multibase prefix to the output.
- Prefixes:
m(base64),u(base64url),z(base58btc),f(hex)
# Encode UTF-8 text to base58btc (default)
echo "Hello World" | npx crypt encode
# Encode UTF-8 to base64
echo "Hello World" | npx crypt encode base64
# Convert base64 to hex
echo "SGVsbG8gV29ybGQ=" | npx crypt encode hex -i base64
# Convert hex to base58btc
echo "48656c6c6f" | npx crypt encode base58btc -i hex
# Pipe between commands (publicKey is in multikey format)
npx crypt keys | jq -r .publicKey | npx crypt encode hex -i multi
# Encode with multibase prefixes
echo "Hello World" | npx crypt encode base64 --multi
# => mSGVsbG8gV29ybGQ
echo "Hello World" | npx crypt encode hex -m
# => f48656c6c6f20576f726c64
echo "Hello World" | npx crypt encode base64url --multi
# => uSGVsbG8gV29ybGQ
# convert "multikey" format to hex format
echo "z6MkiLr..." | npx crypt encode hex -i multiDecode a string from a given encoding format to UTF-8. Reads input from stdin.
input-format- The format of the input string (default:base64)base64,base64pad,hex,base64url,base58btc,ascii
# Decode base64 to UTF-8 (default)
echo "SGVsbG8gV29ybGQ=" | npx crypt decode
# Decode base64pad to UTF-8
echo "SGVsbG8gV29ybGQ=" | npx crypt decode base64pad
# => 'Hello World'
# Decode hex to UTF-8
echo "48656c6c6f20576f726c64" | npx crypt decode hex
# Decode base58btc to UTF-8
echo "JxF12TrwUP45BMd" | npx crypt decode base58btcThis uses Multikey format strings. Multikey format, is a generic, self-describing, multicodec-based public key encoding.
import { base58btc } from 'multiformats/bases/base58'
import * as varint from "multiformats/src/varint"
// Suppose you have a raw public-key Buffer/Uint8Array
const rawKeyBytes = /* ... */
// --- helper: varint-encoded multicodec prefix for ed25519-pub ---
// multicodec code for ed25519-pub is 0xED (237).
// varint encoding for 237 is two bytes: 0xED 0x01
const ED25519_MULTICODEC_VARINT = Uint8Array.from([0xed, 0x01])
const out = new Uint8Array(ED25519_MULTICODEC_VARINT.length + rawPubKey.length)
out.set(ED25519_MULTICODEC_VARINT, 0)
out.set(rawKeyBytes, ED25519_MULTICODEC_VARINT.length)
// base58btc (multibase) encode
// multiformats' base58btc.encode typically returns a string that already
// uses the 'z'
let encoded = base58btc.encode(out)
encoded = encoded.startsWith('z') ? encoded : 'z' + encoded
// This yields something like "z6Mk…", same style as in the DID doc
console.log(encoded)
/**
* Decode a Multikey multibase string (ed25519-pub) back to raw key bytes.
* Returns an object { algCode, rawKey } where algCode is the multicodec
* numeric code.
*/
function decodeMultikey (multibaseStr):{ } {
// Accept with/without leading 'z' — multiformats will accept
// without the explicit 'z' only if decoder used directly.
const cleaned = (multibaseStr.startsWith('z') ?
multibaseStr :
'z' + multibaseStr)
const decoded = base58btc.decode(cleaned) // returns Uint8Array
// read varint (we know ed25519 varint is two bytes: 0xed 0x01)
// robust approach: parse varint; here we handle single or two-byte
// varints for small values
let i = 0
let code = 0
let shift = 0
while (i < decoded.length) {
const b = decoded[i++]
code |= (b & 0x7f) << shift
if ((b & 0x80) === 0) break
shift += 7
}
const rawKey = decoded.slice(i) // remainder is the raw key bytes
return { multicodec: code, rawKey }
}Private keys are either a "raw" 32 byte string, or JWK encoded, or PEM format for RSA.