Hashicorp Vault

The issuer API can leverage Hashicorp Vault's Transit Secrets Engine to hook into the "cryptography as a service" or "encryption as a service" features provided by Hashicorp Vault. With that, the issuer API can sign and verify credentials whilst keeping the secrets (signing keys) in an external secure environment.

If you are new to Hashicorp Vault make sure to checkout the guides here. The rest of this document assumes you already know how Vault works and have a transit secrets engine setup and running.

Key Creation

To create the key you can use the onboard endpoint provided by the issuer API or the CLI, UI or the API from Vault's Transit Secrets Engine.

The only thing that is important to note for the creation, that our system is only compatible with the following Key types offered by the Transit Secrets Engine:

  • ed25519

Transit Secrets Engine key types full list.

Creation via Issuer API

CURL

**Endpoint: ** /onboard/issuer | API Reference

Example Request

curl -X 'POST' \
  'https://issuer.portal.walt-test.cloud/onboard/issuer' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "key": {
    "backend": "tse",
    "keyType": "Ed25519",
    "config": {
      "server": "http://127.0.0.1:8200/v1/transit",
      "accessKey": "dev-only-token"
    }
  },
  "did": {
    "method": "key"
  }
}'

Body

{
  "key": {
    "backend": "tse",
    "keyType": "Ed25519",
    "config": {
      "server": "http://127.0.0.1:8200/v1/transit",
      "accessKey": "dev-only-token"
    }
  },
  "did": {
    "method": "key"
  }
}

Body Parameters

  • key
    • backend: String - Specifies the storage type of key. It can be jwk (manged by you), TSE (managed by Hashicorp Vault) and others. Learn more about different types here.
    • keyType: String - the algorithm used to generate the key. For Vault only ed25519 is possible.
    • config
      • server: URL - The endpoint of your Vault instance. Following the structure of https://<yourHost>/v1/<pathOfTransitEngine>, e.g. "https://vault.walt.id/v1/transit". By default the transit engine will live at /transit.
      • accessKey: String - the secret to access the key and perform sign and verify operations.
  • did:
    • method: String - Specifies the DID method. It can be key, jwk, web, cheqd.

Example Response

The onboard/issuer endpoint will return an object containing both the generated key in JWK format and the related DID.

{
  "issuerKey": {
    "type": "tse",
    "server": "https://aa86-2001-871-25f-672b-7411-c265-6ba4-4271.ngrok-free.app/v1/transit",
    "accessKey": "hvs.Jlskz3M9TnUm04KUYUpWUxfc",
    "id": "k-558834810",
    "_publicKey": [
      55,
      33,
      102,
      64,
      -88,
      -15,
      16,
      -83,
      31,
      -114,
      -70,
      99,
      -95,
      -102,
      120,
      7,
      -118,
      6,
      -10,
      5,
      35,
      -120,
      -26,
      35,
      -52,
      -71,
      27,
      -70,
      -10,
      -99,
      84,
      40
    ],
    "_keyType": "Ed25519"
  },
  "issuerDid": "did:key:z6MkiAU1PQPLxg81DNvE8Fh12rY7deJihhWpGyoxuUf1NoXV"
}

Properties

  • type: String - the type of key can be either "tse" when using Hashicorp Vault or "jwk" when providing the key in full as JWK.
  • server: URL - The endpoint of your Vault instance. Following the structure of https://<yourHost>/v1/<pathOfTransitEngine>, e.g. "https://vault.walt.id/v1/transit". By default the transit engine will live at /transit.
  • accessKey: String - the secret to access the key and perform sign and verify operations.
  • id: String - the ID of the key in the Transit Engine.
  • _publicKey (optional): Array - The public key can be fetched by the issuer API or directly provided, saving resources and reducing network requests.
  • _keyType (optional): String - The key type can be fetched by the issuer API or directly provided, saving resources and reducing network requests.

Key Usage

Once you have successfully created a key that is one of the supported types listed above, you can use it in sign and issue operations offered by the issuer API.

If you've already had a look at our /sign, /issue, /batchIssue endpoints, you have seen that they all follow a similar request body structure, where the key that should be used for signing credentials is provided via the issuerKey property. Now instead of providing the key as JWK, we provide a reference to a key stored in Vault with the required parameters and access credentials.

Below you can see an example of issuerKey object referencing a key stored in Vault.

Example IssuerKey Object
{
  "type": "tse",
  "server": "https://vault.walt.id/v1/transit",
  "accessKey": "hvs.CvHDGaLjG7OFqC6X6xU2UIc9",
  "id": "k1474327600",
  "_publicKey": [
    -17,
    83,
    1,
    26,
    9,
    -104,
    -64,
    -50,
    48,
    92,
    -112,
    -76,
    7,
    90,
    -40,
    -100,
    72,
    106,
    -51,
    -58,
    64,
    -26,
    73,
    121,
    26,
    -118,
    39,
    -8,
    -25,
    -34,
    82,
    37
  ],
  "_keyType": "Ed25519"
}

Properties

  • type: String - the type of key can be either "tse" when using Hashicorp Vault or "jwk" when providing the key in full as JWK.
  • server: URL - The endpoint of your Vault instance. Following the structure of https://<yourHost>/v1/<pathOfTransitEngine>, e.g. "https://vault.walt.id/v1/transit". By default the transit engine will live at /transit.
  • accessKey: String - the secret to access the key and perform sign and verify operations.
  • id: String - the ID of the key in the Transit Engine.
  • _publicKey (optional): Array - The public key can be fetched by the issuer API or directly provided, saving resources and reducing network requests.
  • _keyType (optional): String - The key type can be fetched by the issuer API or directly provided, saving resources and reducing network requests. ::

Example Issuance Request

Below you can see example issuance request to jwt/issue using a key created in the Vault Transit Secret Engine to sign the credential.

CURL

Api Reference

curl -X 'POST' \
  'https://issuer.portal.walt.id/openid4vc/jwt/issue' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuerKey": {
    "type": "tse",
    "server": "https://vault.walt.id/v1/transit",
    "accessKey": "dev-only-token",
    "id": "k-1481938705"
  },
  "issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
  "credentialData": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://purl.imsglobal.org/spec/ob/v3p0/context.json"
    ],
    "id": "urn:uuid:THIS WILL BE REPLACED WITH DYNAMIC DATA FUNCTION (see below)",
    "type": [
      "VerifiableCredential",
      "OpenBadgeCredential"
    ],
    "name": "JFF x vc-edu PlugFest 3 Interoperability",
    ... 
  },
  "mapping": {
    "id": "<uuid>",
     ... 
  }
}'

Body

As you can see for the property issuerKey we only provided the required parameters of the TSE Key Reference Object described above and left out _publicKey and _keyType

{
  "issuerKey": {
    "type": "tse",
    "server": "https://vault.walt.id/v1/transit",
    "accessKey": "dev-only-token",
    "id": "k-1481938705"
  },
  "issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
  "credentialData": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://purl.imsglobal.org/spec/ob/v3p0/context.json"
    ],
    "id": "urn:uuid:THIS WILL BE REPLACED WITH DYNAMIC DATA FUNCTION (see below)",
    "type": [
      "VerifiableCredential",
      "OpenBadgeCredential"
    ],
    "name": "JFF x vc-edu PlugFest 3 Interoperability",
    ...
  },
  "mapping": {
    "id": "<uuid>",
    ...
  }
}