AWS KMS

The issuer API can leverage AWS key management service to hook into the "cryptography as a service" or "encryption as a service" features provided by aws. 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 aws kms make sure to checkout the guides here. The rest of this document assumes you already know how AWS KMS works and have a it setup.

Setting Up Access keys and Secret keys for AWS

Prerequisites

  • AWS Account: You need an active AWS account. If you don't have one, create it at AWS Console.

Create Access Keys

To securely interact with AWS services such as KMS, you need to configure your AWS access and secret keys. Here’s how to generate and configure these credentials:

  1. Log in to your AWS Management Console.
  2. Navigate to IAM (Identity and Access Management).
  3. In the left sidebar, click on Users, then choose your username.
  4. Go to the Security credentials tab.
  5. Under Access keys, click Create access key.
  6. Copy both the Access Key ID and Secret Access Key. Store them securely.

Key Creation

To create the key you can use the onboard endpoint provided by the issuer API and provide the necessary parameters to create the key in the AWS KMS service.

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": "aws",
    "keyType": "secp256r1",
    "config": {
       "auth": {
        "accessKeyId": "AKIAW......U2TQU5F",
        "secretAccessKey": "6YDrEN1t+......I3OCwSc7Sr",
        "region": "eu-central-1"
      }
    }
  },
  "did": {
    "method": "jwk"
  }
}'

Body

{
  "key": {
    "backend": "aws",
    "keyType": "secp256r1",
    "config": {
      "auth": {
        "accessKeyId": "AKIAW......U2TQU5F",
        "secretAccessKey": "6YDrEN1t+......I3OCwSc7Sr",
        "region": "eu-central-1"
      }
    }
  },
  "did": {
    "method": "jwk"
  }
}

Body Parameters

  • key
    • backend: String - Specifies the storage type of key. It can be jwk (manged by you), AWS (managed by AWS KMS) and others. Learn more about different types here.
    • keyType: String - the algorithm used to generate the key. For Vault only ed25519 is possible.
    • config
      • accessKeyId: String - The access key id for AWS KMS.
      • secretAccessKey: String - The secret access key for AWS KMS.
      • region: String - The region where the AWS KMS service is located.
  • 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": "aws",
    "config": {
      "auth": {
        "accessKeyId": "AKIAW......U2TQU5F",
        "secretAccessKey": "6YDrEN1t+......I3OCwSc7Sr",
        "region": "eu-central-1"
      }
    },
    "id": "324ebf67-6bcc-4439-8b81-260bf0a82532",
    "_publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"RfvJGDXOjz3NOSKgZ0VlijXST8S-j96DrG0C1AMNAK8\",\"y\":\"vSt2q7yIy0-AhAirMuuDmUScxkgf4JVfId-vTETraQA\"}",
    "_keyType": "secp256r1"
  },
  "issuerDid": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlJmdkpHRFhPanozTk9TS2daMFZsaWpYU1Q4Uy1qOTZEckcwQzFBTU5BSzgiLCJ5IjoidlN0MnE3eUl5MC1BaEFpck11dURtVVNjeGtnZjRKVmZJZC12VEVUcmFRQSJ9"
}

Properties

  • type: String - the type of key can be either "aws" when using AWS KMS or "jwk" when providing the key in full as JWK.
  • config
    • accessKeyId: String - The access key id for AWS KMS.
    • secretAccessKey: String - The secret access key for AWS KMS.
    • region: String - The region where the AWS KMS service is located.
  • 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.
  • issuerDid: String - the DID of the issuer. ::

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 AWS.

Example IssuerKey Object
{
  "issuerKey": {
    "type": "aws",
    "config": {
      "auth": {
        "accessKeyId": "AKIAW......U2TQU5F",
        "secretAccessKey": "6YDrEN1t+......I3OCwSc7Sr",
        "region": "eu-central-1"
      }
    },
    "id": "324ebf67-6bcc-4439-8b81-260bf0a82532",
    "_publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"RfvJGDXOjz3NOSKgZ0VlijXST8S-j96DrG0C1AMNAK8\",\"y\":\"vSt2q7yIy0-AhAirMuuDmUScxkgf4JVfId-vTETraQA\"}",
    "_keyType": "secp256r1"
  },
  "issuerDid": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlJmdkpHRFhPanozTk9TS2daMFZsaWpYU1Q4Uy1qOTZEckcwQzFBTU5BSzgiLCJ5IjoidlN0MnE3eUl5MC1BaEFpck11dURtVVNjeGtnZjRKVmZJZC12VEVUcmFRQSJ9"
}

Properties

  • type: String - the type of key can be either "aws" when using AWS KMS or "jwk" when providing the key in full as JWK.
  • id: String - the ID of the key in the Transit Engine.
  • config
    • accessKeyId: String - The access key id for AWS KMS.
    • secretAccessKey: String - The secret access key for AWS KMS.
    • region: String - The region where the AWS KMS service is located.
  • _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 AWS kms to sign the credential.

CURL

Api Reference

curl -X 'POST' \
  'https://issuer.portal.walt.id/openid4vc/jwt/issue' \
  -H 'accept: text/plain' \
  -H 'statusCallbackUri: https://example.com/$id' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuerKey": {
    "type": "aws",
    "config": {
       "auth": {
        "accessKeyId": "AKIAW......U2TQU5F",
        "secretAccessKey": "6YDrEN1t+......I3OCwSc7Sr",
        "region": "eu-central-1"
      }
    },
    "id": "324ebf67-6bcc-4439-8b81-260bf0a82532",
    "_publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"RfvJGDXOjz3NOSKgZ0VlijXST8S-j96DrG0C1AMNAK8\",\"y\":\"vSt2q7yIy0-AhAirMuuDmUScxkgf4JVfId-vTETraQA\"}",
    "_keyType": "secp256r1"
  },
  "issuerDid": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlJmdkpHRFhPanozTk9TS2daMFZsaWpYU1Q4Uy1qOTZEckcwQzFBTU5BSzgiLCJ5IjoidlN0MnE3eUl5MC1BaEFpck11dURtVVNjeGtnZjRKVmZJZC12VEVUcmFRQSJ9",
  "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>",
     ... 
  }
}'

Header Parameters

  • statusCallbackUri: URL - Receive updates on the created issuance process, e.g. when a credential was successfully claimed. The parameter expects a URL which can accept a JSON POST request. The URL can also hold a $id, which will be replaced by the issuance session id. For example: https://myurl.com/$id, https://myurl.com or https://myurl.com/test/$id

    Expand To Learn More

    Body

    The data send to the provided URL will contain a JSON body:

    • id : String - the issuance session id
    • type: String - the event type
    • data: JsonObject - the data for the event

    Event Types

    Possible events (event types) and their data are:

    • resolved_credential_offer with the credential offer as JSON (in our Web Wallet: called when the issuance offer is entered into the wallet, but not processing / accepted yet)
    • requested_token with the issuance request for the token as json object (called for the token required to receive the credentials)

    Credential issuance (called for every credential that's issued (= requested from wallet))

    • jwt_issue with jwt being the issued jwt
    • sdjwt_issue with sdjwt being the issued sdjwt
    • batch_jwt_issue with jwt being the issued jwt
    • batch_sdjwt_issue with sdjwt being the issued sdjwt
    • generated_mdoc with mdoc being the CBOR (HEX) of the signed mdoc

    To allow for secure business logic flows, if a callback URL is set, and it cannot be reached, the issuance will not commence further (after that point). If no callback URL is set, the issuance logic does not change in any way.

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": "aws",
    "config": {
      "auth": {
        "accessKeyId": "AKIAW......U2TQU5F",
        "secretAccessKey": "6YDrEN1t+......I3OCwSc7Sr",
        "region": "eu-central-1"
      }
    },
    "id": "324ebf67-6bcc-4439-8b81-260bf0a82532",
    "_publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"RfvJGDXOjz3NOSKgZ0VlijXST8S-j96DrG0C1AMNAK8\",\"y\":\"vSt2q7yIy0-AhAirMuuDmUScxkgf4JVfId-vTETraQA\"}",
    "_keyType": "secp256r1"
  },
  "issuerDid": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlJmdkpHRFhPanozTk9TS2daMFZsaWpYU1Q4Uy1qOTZEckcwQzFBTU5BSzgiLCJ5IjoidlN0MnE3eUl5MC1BaEFpck11dURtVVNjeGtnZjRKVmZJZC12VEVUcmFRQSJ9",
  "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>",
    ...
  }
}

AWS Instance Authentication

To authenticate your instance with AWS, You can Set Up an IAM Role for the EC2 Instance in AWS Console by following the steps below:

  1. Log in to your AWS Management Console.
  2. Navigate to IAM (Identity and Access Management).
  3. In the left sidebar, click on Roles.
  4. Click on Create Role.
  5. Select EC2 as the service that will use this role.
  6. Click on Next: Permissions.
  7. Attach the necessary permissions to the role.
  8. Click on Next: Tags.
  9. Add tags if necessary.
  10. Click on Next: Review.
  11. Enter a name for the role.
  12. Click on Create Role.
  13. Attach the role to the EC2 instance.
  14. Navigate to the EC2 instance in the AWS Management Console.
  15. Click on Actions > Security > Modify IAM Role.
  16. Select the role you created.
  17. Click on Save.
  18. The EC2 instance is now authenticated with AWS.
  19. You can now use the AWS SDK to interact with AWS services.

AWS config IAM

When you have set up the IAM role for the EC2 instance, you can use it to authenticate with AWS services.

Creation via Issuer API with AWS Instance Authentication

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": "aws",
    "keyType": "secp256r1",
    "config": {
         "auth": {
          "roleName": "AccessRole",
          "region": "eu-central-1"
        }
    },
  },
  "did": {
    "method": "jwk"
  }
}'

Body

{
  "key": {
    "backend": "aws",
    "keyType": "secp256r1",
    "config": {
      "auth": {
        "roleName": "AccessRole",
        "region": "eu-central-1"
      }
    }
  },
  "did": {
    "method": "jwk"
  }
}

Body Parameters

  • key
    • backend: String - Specifies the storage type of key. It can be jwk (manged by you), AWS (managed by AWS KMS) and others. Learn more about different types here.
    • keyType: String - the algorithm used to generate the key. For Vault only ed25519 is possible.
    • config
      • roleName: String - The role name for AWS KMS.
      • region: String - The region where the AWS KMS service is located.
  • 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": "aws",
    "config": {
      "auth": {
        "roleName": "AccessRole",
        "region": "eu-central-1"
      }
    },
    "id": "324ebf67-6bcc-4439-8b81-260bf0a82532",
    "_publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"RfvJGDXOjz3NOSKgZ0VlijXST8S-j96DrG0C1AMNAK8\",\"y\":\"vSt2q7yIy0-AhAirMuuDmUScxkgf4JVfId-vTETraQA\"}",
    "_keyType": "secp256r1"
  },
  "issuerDid": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlJmdkpHRFhPanozTk9TS2daMFZsaWpYU1Q4Uy1qOTZEckcwQzFBTU5BSzgiLCJ5IjoidlN0MnE3eUl5MC1BaEFpck11dURtVVNjeGtnZjRKVmZJZC12VEVUcmFRQSJ9"
}

Properties

  • type: String - the type of key can be either "aws" when using AWS KMS or "jwk" when providing the key in full as JWK.
  • config
    • roleName: String - The role name for AWS KMS.
    • region: String - The region where the AWS KMS service is located.
  • 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.
  • issuerDid: String - the DID of the issuer. ::

Example Issuance Request with AWS Instance Authentication

CURL

Api Reference

curl -X 'POST' \
  'https://issuer.portal.walt.id/openid4vc/jwt/issue' \
  -H 'accept: text/plain' \
  -H 'statusCallbackUri: https://example.com/$id' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuerKey": {
    "type": "aws",
    "config": {
       "auth": {
        "roleName": "AccessRole",
        "region": "eu-central-1"
      }
    },
    "id": "324ebf67-6bcc-4439-8b81-260bf0a82532",
    "_publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"RfvJGDXOjz3NOSKgZ0VlijXST8S-j96DrG0C1AMNAK8\",\"y\":\"vSt2q7yIy0-AhAirMuuDmUScxkgf4JVfId-vTETraQA\"}",
    "_keyType": "secp256r1"
  },
  "issuerDid": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlJmdkpHRFhPanozTk9TS2daMFZsaWpYU1Q4Uy1qOTZEckcwQzFBTU5BSzgiLCJ5IjoidlN0MnE3eUl5MC1BaEFpck11dURtVVNjeGtnZjRKVmZJZC12VEVUcmFRQSJ9",
  "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>",
     ... 
  }
}'

Header Parameters

  • statusCallbackUri: URL - Receive updates on the created issuance process, e.g. when a credential was successfully claimed. The parameter expects a URL which can accept a JSON POST request. The URL can also hold a $id, which will be replaced by the issuance session id. For example: https://myurl.com/$id, https://myurl.com or https://myurl.com/test/$id

    Expand To Learn More

    Body

    The data send to the provided URL will contain a JSON body:

    • id : String - the issuance session id
    • type: String - the event type
    • data: JsonObject - the data for the event

    Event Types

    Possible events (event types) and their data are:

    • resolved_credential_offer with the credential offer as JSON (in our Web Wallet: called when the issuance offer is entered into the wallet, but not processing / accepted yet)
    • requested_token with the issuance request for the token as json object (called for the token required to receive the credentials)

    Credential issuance (called for every credential that's issued (= requested from wallet))

    • jwt_issue with jwt being the issued jwt
    • sdjwt_issue with sdjwt being the issued sdjwt
    • batch_jwt_issue with jwt being the issued jwt
    • batch_sdjwt_issue with sdjwt being the issued sdjwt
    • generated_mdoc with mdoc being the CBOR (HEX) of the signed mdoc

    To allow for secure business logic flows, if a callback URL is set, and it cannot be reached, the issuance will not commence further (after that point). If no callback URL is set, the issuance logic does not change in any way.

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": "aws",
    "config": {
      "auth": {
        "roleName": "AccessRole",
        "region": "eu-central-1"
      }
    },
    "id": "324ebf67-6bcc-4439-8b81-260bf0a82532",
    "_publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"RfvJGDXOjz3NOSKgZ0VlijXST8S-j96DrG0C1AMNAK8\",\"y\":\"vSt2q7yIy0-AhAirMuuDmUScxkgf4JVfId-vTETraQA\"}",
    "_keyType": "secp256r1"
  },
  "issuerDid": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlJmdkpHRFhPanozTk9TS2daMFZsaWpYU1Q4Uy1qOTZEckcwQzFBTU5BSzgiLCJ5IjoidlN0MnE3eUl5MC1BaEFpck11dURtVVNjeGtnZjRKVmZJZC12VEVUcmFRQSJ9",
  "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>",
    ...
  }
}