Exchange
BIND Exchange enables secure, link-based sharing of encrypted BIND Bundles between insurance participants — without requiring both parties to be on the same system or have accounts with a shared platform.
A broker can package a submission, encrypt it, and send a link to a carrier. A carrier can share a certificate of insurance with a third party. An MGA can deliver a bound policy package to a retail broker. All via a simple URL and passcode.
Background
Insurance workflows constantly require sharing structured data across organizational boundaries: submissions from broker to carrier, quotes from carrier to broker, certificates to third parties, policy packages to insureds. Today this happens via email attachments, portal logins, and proprietary integrations.
BIND Exchange solves this with a simple, secure protocol: the encryption key travels in the link itself, never touching the server, so the exchange server is a zero-knowledge intermediary.
Protocol Flow
Step by step
- Sender signs the BIND Bundle as a JWS (optional — see Signing)
- Sender encrypts the bundle (or signed JWS) using JWE (
alg: "dir",enc: "A256GCM") with a random 256-bit key - Sender creates an exchange by POSTing the JWE and an optional proof JWT to the exchange server
- Server verifies the proof (if provided) against the BIND Trust Gateway to determine the trust tier
- Server stores the JWE payload and metadata, applying tier-appropriate limits
- Server responds with a retrieval URL, passcode, and trust status
- Sender shares the link and passcode with the recipient via any channel
- Recipient retrieves the manifest by POSTing the passcode to the retrieval URL
- Server verifies the passcode and rate-limits attempts
- Server returns the JWE payload embedded in a manifest envelope
- Recipient decrypts using the key from the link payload
- Recipient verifies the JWS signature (if the bundle was signed) against the issuer's JWKS
Signing
Signing is optional and at the issuer's discretion. The exchange server accepts both signed and unsigned bundles. However, signed bundles from trusted issuers receive significantly better treatment — see Trust Tiers.
How it works
Exchange uses two layers of signing. The first layer is the standard Signed Bundle — the sender signs the BIND Bundle as a JWS (ES256) with their key from the BIND Directory. See the Signed Bundles specification for the full JWS header, payload claims, and verification process.
The signed JWS becomes the plaintext that gets encrypted into the JWE:
Exchange proof (server-verifiable)
The exchange server cannot see inside the JWE, so it cannot verify the inner JWS signature. To prove trust, the sender provides a separate proof JWT when creating the exchange. This proof binds the sender's signing key to the specific JWE payload:
| Proof Header | Value |
|---|---|
alg | ES256 |
kid | <key-id> (same key used for the bundle JWS) |
| Proof Payload Claim | Value |
|---|---|
iss | <issuer-url> (same as the bundle JWS, e.g. https://bindpki.org/acme) |
sub | <base64url(SHA-256(JWE))> — hash of the encrypted payload |
iat | <timestamp> |
The server verifies this proof by:
- Extracting
issfrom the proof payload - Fetching the JWKS from
{iss}/.well-known/jwks.json - Verifying the ES256 signature against the issuer's published public key
- Confirming
submatchesSHA-256of the submitted JWE (prevents proof replay)
JWKS discovery
Issuer public keys are published in the BIND Directory at bindpki.org:
{iss}/.well-known/jwks.jsonSee the Trust Directory for how participants register and manage their keys.
Recipient-side verification
After decrypting the JWE, the recipient checks whether the plaintext is a JWS and verifies it against the signer's JWKS from the BIND Directory. See the Signed Bundles verification process for the full steps.
If the plaintext is not a JWS (i.e. it's raw JSON), the bundle is treated as unsigned.
Trust Tiers
The exchange server applies different limits based on whether the sender provides a valid proof:
| Trusted | Untrusted | |
|---|---|---|
| Proof | Valid proof JWT verified against BIND Trust Gateway | No proof, invalid proof, or issuer not in gateway |
| Max payload | 5 MB | 10 KB |
| Default expiry | 72 hours | 1 hour |
| Max expiry | 1 year + 1 day | 1 hour |
| Passcode attempts | 10 | 10 |
Untrusted exchanges are not rejected — they are accepted with restricted limits. This allows anyone to use the exchange for small payloads (certificates, simple quotes) without needing to register as a trusted issuer.
Trusted exchanges require the issuer to have registered their signing keys in the BIND Directory.
Link Payload
The bindx:// URI scheme encodes all client-side state needed to retrieve and decrypt the exchange:
bindx://<base64url(JSON)>The JSON payload contains:
| Field | Type | Description |
|---|---|---|
url | string | Full URL to the manifest endpoint |
key | string | Base64url-encoded 256-bit AES key |
exp | number | Expiry timestamp (ms since epoch) |
flag | string | Access flags (P = passcode required) |
label | string? | Human-readable label for display |
Example decoded payload:
{
"url": "https://exchange.bind-standard.org/exchange/abc123.../manifest.json",
"key": "dGhpcyBpcyBhIDI1Ni1iaXQga2V5IGZvciBBRVMtR0NN",
"exp": 1708300800000,
"flag": "P",
"label": "Acme Corp GL Submission"
}The key never leaves the client and is never sent to the server. The server stores only the encrypted payload and a hash of the passcode.
Encryption
BIND Exchange uses JWE (RFC 7516) compact serialization:
| JWE Header | Value | Notes |
|---|---|---|
alg | dir | Direct key agreement — the key in the link is the CEK |
enc | A256GCM | AES-256-GCM authenticated encryption |
cty | application/bind+json | Content type of the plaintext |
The sender generates a random 256-bit key, encrypts the BIND Bundle (or signed JWS), and produces a JWE compact string. This string is what gets uploaded to the exchange server. The key is encoded into the bindx:// link and shared out-of-band.
Access Control
| Control | Detail |
|---|---|
| Passcode | Required for all exchanges (flag P). Minimum 4 characters. |
| Rate limiting | Maximum 10 passcode attempts. After exhaustion, the exchange is permanently locked and the payload is deleted. |
| Time limit | Depends on trust tier — 72h default for trusted, 1h for untrusted. |
| Zero-knowledge | The server stores only the encrypted payload and a PBKDF2 hash of the passcode. It cannot read the content. |
Passcode hashing
Passcodes are hashed using PBKDF2-SHA256 with 310,000 iterations (OWASP recommendation) and a 128-bit random salt. The storage format is hex(salt):hex(hash), allowing future migration to stronger algorithms.
API
Create Exchange
POST /exchange
Content-Type: application/json
{
"payload": "<JWE compact serialization>",
"passcode": "secret123", // optional — generated if omitted
"label": "Acme Corp GL Policy", // optional
"exp": 259200, // optional — seconds (capped by trust tier)
"proof": "<proof JWT>" // optional — required for trusted tier
}Response (201):
{
"url": "https://exchange.bind-standard.org/exchange/abc123.../manifest.json",
"exp": 1708300800000,
"flag": "P",
"passcode": "482910",
"trusted": true,
"iss": "https://bindpki.org/acme-insurance"
}The trusted field indicates whether the exchange was verified against the BIND Trust Gateway. The iss field is only present for trusted exchanges.
If no proof is provided (or verification fails), the exchange is still created but with untrusted limits:
{
"url": "https://exchange.bind-standard.org/exchange/def456.../manifest.json",
"exp": 1708218000000,
"flag": "P",
"passcode": "739201",
"trusted": false
}Error responses:
| Status | Meaning |
|---|---|
| 400 | Invalid JWE header |
| 413 | Payload exceeds tier limit (10KB untrusted, 5MB trusted) |
Retrieve Manifest
POST /exchange/{id}/manifest.json
Content-Type: application/json
{
"recipient": "Jane Doe, Marsh McLennan",
"passcode": "482910"
}Response (200):
{
"files": [
{
"contentType": "application/bind+json",
"embedded": "<JWE compact serialization>"
}
]
}Error responses:
| Status | Meaning | Body |
|---|---|---|
| 401 | Invalid passcode | { "error": "Invalid passcode", "remainingAttempts": 7 } |
| 404 | Not found / expired | { "error": "Exchange not found or expired" } |
| 429 | Locked out | { "error": "Too many failed attempts. Exchange has been locked.", "remainingAttempts": 0 } |
Use Cases
Certificate distribution
A carrier generates a certificate of insurance as a BIND Bundle, creates an exchange, and sends the link to the certificate holder via email. The recipient opens the link, enters the passcode, and receives the structured certificate data.
Submission packages
A broker assembles a submission package — insured details, loss runs, schedule of values — as a BIND Bundle. They create an exchange and send links to multiple carriers. Each carrier retrieves the same structured data without portal logins or email attachments.
Quote responses
A carrier packages their quote — proposed coverages, premiums, terms — as a BIND Bundle and sends it back to the broker via an exchange link. The broker's system can automatically ingest the structured quote data.
Policy document exchange
After binding, the carrier shares the full policy package — policy, endorsements, certificates — as a BIND Bundle via exchange. The broker receives structured data they can feed directly into their management system.