Home / Docs / PKI

Keys, signatures and encrypting to someone else

The vault key is symmetric and opens the whole vault. A keypair does the two things it cannot: prove who wrote something, and encrypt to somebody who holds no vault key at all. This page is the lifecycle, run end to end on the shipped CLI.

Everything below was executed, not recalled. Every command, every output shape and every construction on this page came from running sgit pki on v0.15.0 in a throwaway home directory. Where the behaviour differs from what you might expect — and in two places it does — the page says so.

What sgit actually generates

sgit pki keygen creates two pairs at once, and prints two fingerprints:

PurposeConstructionIdentifier
EncryptionRSA-OAEP, 4096-bitfingerprint
SigningECDSA P-256signing_fingerprint

Encryption is hybrid, which is what makes it usable on files of any size: a fresh AES-256-GCM content key encrypts the payload, and RSA-OAEP wraps that content key. The same AES-256-GCM primitive as the rest of the vault — the keypair only changes how the content key gets to the recipient.

Note for anyone integrating against a spec rather than the binary: this is RSA-OAEP and ECDSA, not X25519/Ed25519. Draft material describing an X25519 sealing layer describes a design, not what v0.15.0 ships.

The lifecycle

1 · Generate

$ sgit pki keygen --label "My Vault Identity"
Enter passphrase to protect private keys:

Generating RSA-4096 encryption key... done

Key pair created:
  Label:                My Vault Identity
  Encryption:           RSA-OAEP 4096-bit
  Signing:              ECDSA P-256
  Fingerprint:          sha256:a4615402a0bc23ac
  Signing fingerprint:  sha256:69d9b4835ccf790c

Keygen prompts for a passphrase and will not proceed without one — private keys are encrypted at rest. In a script, feed it on stdin; with no terminal at all it exits with an EOFError rather than silently generating an unprotected key, which is the right failure.

RSA-4096 generation is not instant. Expect a pause.

2 · Publish the public half

$ sgit pki list
  sha256:a4615402a0bc23ac  My Vault Identity  (RSA-OAEP 4096)

$ sgit pki export sha256:a4615402a0bc23ac > my-identity.json

Export produces a JSON bundle, not a PEM file. It carries both public keys, both fingerprints and the label:

{
  "v": 1,
  "encrypt": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkq…",
  "sign":    "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQ…",
  "label":   "My Vault Identity",
  "fingerprint":         "sha256:a4615402a0bc23ac",
  "signing_fingerprint": "sha256:69d9b4835ccf790c"
}

The bundle contains no private material. Publish it, mail it, commit it, put it on your website.

3 · Import someone else’s

$ sgit pki import their-identity.json
Imported contact: Their Label
  Fingerprint: sha256:…

$ sgit pki contacts
  sha256:…  Their Label

4 · Encrypt to them, and decrypt what they send you

$ sgit pki encrypt message.txt --recipient sha256:a4615402a0bc23ac
Encrypted to message.txt.enc

$ sgit pki decrypt message.txt.enc --fingerprint sha256:a4615402a0bc23ac
Enter passphrase:
Decrypted to message.txt

Round trip verified. Two argument details that will bite you once each: encrypt takes --recipient (a contact’s fingerprint) and an optional --fingerprint for your signing key; decrypt requires --fingerprint — it will not guess which of your keys to try.

5 · Sign and verify

$ sgit pki sign report.pdf --fingerprint sha256:69d9b4835ccf790c
$ sgit pki verify report.pdf report.pdf.sig

Detached signatures, so the signed file is unchanged. Signing uses the signing fingerprint, which is the second one keygen printed.

The envelope on the wire

An encrypted file is base64 over a small JSON object — worth knowing if you are writing an interoperable client:

FieldContentsObserved size
vEnvelope version — 2 on v0.15.0
wThe AES content key, wrapped with RSA-OAEP to the recipient512 bytes (4096-bit)
iAES-GCM IV12 bytes
cCiphertext with its GCM tagplaintext + 16

A 37-byte plaintext produced a 1,076-byte file: the RSA-wrapped key dominates, so this is efficient for large payloads and heavy for tiny ones.

Where public keys live in a vault

Vaults carry public PKI keys as immutable objects at bare/keys/key-rnd-imm-*, alongside the content store at bare/data/obj-cas-imm-*. They are public by construction, so nothing there needs protecting — which is exactly why a lane addressed by a public key can be written to by a stranger.

What this is for

Signing and encrypting files is useful on its own. The larger use is vault-to-vault messaging: your public key becomes an address a stranger can write to, and your private key is the only thing that can read what arrives. That page composes this one with append lanes.

What this does not do yet

Honest edges belong on the limitations page too, and these are listed there.