Skip to main content

Managing a DID's Keys

Once the initial DID is registered, the entity can manage the lifecycle of its cryptographic material through the DID Registry API:

  • Add new keys (addVerificationMethod).
  • Associate uses with those keys (addVerificationRelationship).
  • Expire or revoke compromised keys (expireVerificationMethod, revokeVerificationMethod).
  • Rotate one key for another (rollVerificationMethod).
  • Add or revoke controllers (addController, revokeController).
Common pattern

All operations follow the same 3-step pattern: create transaction → sign off-chain → send. If you are not familiar with the flow, read DID Onboarding first.

Adding a P-256 Key to Issue Credentials

To be able to issue verifiable credentials, an entity needs a P-256 key registered as an assertionMethod. The complete flow is:

  1. Generate the P-256 key.
  2. Register the verification method.
  3. Associate it with the assertionMethod relationship.

1. Generate P-256 Key

From isbe-identity-did-gen:

./did-gen keys -c P-256

Expected output (summarized):

Private Key (JWK):
{
"kty": "EC",
"crv": "P-256",
"alg": "ES256",
"x": "<X>",
"y": "<Y>",
"d": "<D>"
}

Public Key (JWK):
{
"kty": "EC",
"crv": "P-256",
"alg": "ES256",
"x": "<X>",
"y": "<Y>"
}

Public Key (JWK) Thumbprint:
WqpsDjTHnYVNvX8qx4a1FER8NgnEBS6DjLbDVrX-Fg4

Public Key (hex of JWK):
0x7b226b7479223a224543222c22637276223a22502d323536222c...

Save the private JWK, the thumbprint, and the public JWK hex.

2. Register the Verification Method

Call POST /api/v1/contract/addVerificationMethod with:

{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"vMethodId": "WqpsDjTHnYVNvX8qx4a1FER8NgnEBS6DjLbDVrX-Fg4",
"publicKeyType": "JsonWebKey2020",
"publicKey": "0x7b226b7479223a224543222c22637276223a22502d323536222c...",
"from": "0xAe0E493C67f75381b0954644836A3C16c79D0dFD"
}

Notes:

  • vMethodId — Use the JWK thumbprint of the public key. Recommended for being deterministic and traceable.
  • publicKeyType — For keys wrapped in JWK (P-256, or also secp256k1 if you want to register it with full JWK metadata) use JsonWebKey2020. The actual curve is determined by the crv you put inside the serialized JWK. If you register a secp256k1 without a wrapper (pure 64-byte XY hex), use publicKeyType: "secp256k1". The "P-256" literal appears in the spec, but the API rejects it with 400 UNSUPPORTED; for P-256 always use JsonWebKey2020 with crv: "P-256".
  • publicKey — The public JWK hex (NOT the public hex in uncompressed format; that format is only used with publicKeyType: "secp256k1").
  • from — This is still the EOA of your control secp256k1 key, NOT the EOA derived from the P-256 key.

Sign the off-chain response (ethers.js, eth_account, or any equivalent EVM library) and send it using POST /api/v1/transactions/send. You have the complete snippets in Registration via API — step 4.

3. Associate the Verification Relationship

The cryptographic material is now registered, but it will not be usable until it is assigned a relationship. Call POST /api/v1/contract/addVerificationRelationship:

{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"name": "assertionMethod",
"vMethodId": "WqpsDjTHnYVNvX8qx4a1FER8NgnEBS6DjLbDVrX-Fg4",
"notBefore": 1774603242,
"notAfter": 2090222442,
"from": "0xAe0E493C67f75381b0954644836A3C16c79D0dFD"
}

Valid values for name:

nameUse
assertionMethodSign credentials and accreditations.
authenticationAuthenticate as the DID.
keyAgreementKey agreement for encryption.
capabilityInvocationInvoke delegated capabilities.
capabilityDelegationDelegate capabilities.

Sign and send the transaction. After confirmation, the key is ready for operational use and will appear in the DID Document under the corresponding section.

Revoking a Key

When a key is no longer secure (compromised, rotated, etc.), it must be revoked to prevent signatures made with it from being accepted.

POST /api/v1/contract/revokeVerificationMethod
{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"vMethodId": "WqpsDjTHnYVNvX8qx4a1FER8NgnEBS6DjLbDVrX-Fg4",
"notAfter": 1774700000,
"from": "0xAe0E493C67f75381b0954644836A3C16c79D0dFD"
}
  • notAfter indicates the timestamp from which the key ceases to be valid. For immediate revocation, use the current timestamp.
  • Credentials signed with this key before notAfter remain valid; subsequent ones do not.

Expiring a Key

Similar to revocation, but used when the key reaches the natural end of its useful life (the planned notAfter date). Conceptually equivalent but semantically distinct: revocation implies compromise, expiration does not.

POST /api/v1/contract/expireVerificationMethod
{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"vMethodId": "WqpsDjTHnYVNvX8qx4a1FER8NgnEBS6DjLbDVrX-Fg4",
"notAfter": 2090221463,
"from": "0xAe0E493C67f75381b0954644836A3C16c79D0dFD"
}

Rotating a Key

Rotation combines the registration of a new key and the scheduled expiration of the old one. It is useful for renewing keys without losing operability.

POST /api/v1/contract/rollVerificationMethod
{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"vMethodId": "<NEW_THUMBPRINT>",
"publicKeyType": "JsonWebKey2020",
"publicKey": "0x<NEW_HEX_JWK>",
"notBefore": 1774700000,
"notAfter": 2090300000,
"oldVMethodId": "<PREVIOUS_THUMBPRINT>",
"duration": 86400,
"from": "0xAe0E493C67f75381b0954644836A3C16c79D0dFD"
}
  • duration (in seconds) defines the overlap period during which both keys are valid. It allows time to migrate signers.
  • After that period, the old key is automatically expired.

Adding or Revoking Controllers

A controller is an account authorized to modify the DID Document. Useful for delegating administration to another entity or to a different EOA.

Add

POST /api/v1/contract/addController
{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"controller": "0x<NEW_EOA>",
"from": "0x<CURRENT_EOA>"
}

Revoke

POST /api/v1/contract/revokeController
{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"controller": "0x<EOA_TO_REVOKE>",
"from": "0x<CURRENT_EOA>"
}
Do not leave the DID without a controller

Before revoking the last controller, make sure you have at least one other registered. Otherwise, you will lose control of the DID irreversibly.

Updating Metadata

Change alsoKnownAs

POST /api/v1/contract/updateAlsoKnownAs
{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"alsoKnownAs": "urn:oid:organizationIdentifier:NEW_CODE",
"from": "0xAe0E493C67f75381b0954644836A3C16c79D0dFD"
}

Change baseDocument (JSON-LD context)

POST /api/v1/contract/updateBaseDocument
{
"did": "did:isbe:uc:z12kVar5p6bD5WCjEJcQ18Zyt8XV",
"baseDocument": "{\"@context\":[\"https://www.w3.org/ns/did/v1\",\"https://w3id.org/security/suites/jws-2020/v1\"]}",
"from": "0xAe0E493C67f75381b0954644836A3C16c79D0dFD"
}

Replacing the Complete DID Document

If you need to replace the DID Document from its root (for example, after rotating the initial secp256k1 control key), use:

POST /api/v1/contract/insertDidDocument

The body is identical to that of insertFirstDidDocument, except that the DID already exists and the operation is subject to verification of the previous controller instead of the initial authorization.

Key difference between the two endpoints
  • insertFirstDidDocument — Only for the initial registration of a DID never registered before. Requires special authorization.
  • insertDidDocument — To rewrite the document of an entity that already exists in the registry. Only the current controller can invoke it.