Control the key that protects your stored data.
Bring your own key (BYOK) to add a layer around stored encrypted data. A separate AES-256-GCM data encryption key protects each record and is itself encrypted under your AWS Key Management Service (KMS) customer-managed key (CMK). You will also create an access role (IAM) and copy resource identifiers (ARNs). Requires AWS administration access.
Four steps
Create a symmetric CMK in AWS KMS.
In the AWS console, go to KMS → Customer managed keys in the region you want to use, click Create key, and pick:
Give it a descriptive alias such as alias/deny-sh-byok and add a tag deny-sh:purpose = byok so it shows up in cost-allocation reports separately. Skip the Key administrators step (your normal admins are fine). On the Define key usage permissions step, leave the IAM role list empty for now. We'll add the deny.sh role in step 2.
After creation, copy the ARN (looks like arn:aws:kms:us-east-1:123456789012:key/uuid). You'll paste it into the BYOK dashboard in step 3.
What deny.sh ever calls on this key: kms:GenerateDataKey (when wrapping a new blob) and kms:Decrypt (when reading one back). Nothing else.
Create an IAM role with our trust policy.
deny.sh assumes a role in your AWS account on every wrap and every unwrap. That role's trust policy names our AWS account as the only principal allowed to assume it.
In IAM → Roles → Create role, pick Custom trust policy and paste:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowDenyShAssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<DENY_SH_AWS_ACCOUNT_ID>:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<YOUR_TENANT_ID>"
}
}
}]
}
The DENY_SH_AWS_ACCOUNT_ID is shown in your BYOK dashboard (the ID is public and stable; it doesn't change per tenant). The YOUR_TENANT_ID is your deny.sh tenant ID, also shown in the dashboard. The sts:ExternalId condition prevents the confused-deputy problem and is non-optional in our wire format.
Then attach one inline policy with these permissions, scoped to your CMK ARN:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyShEnvelopeOps",
"Effect": "Allow",
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt"
],
"Resource": "<YOUR_CMK_ARN>"
}]
}
Give the role a name like deny-sh-byok-role and copy its ARN (looks like arn:aws:iam::123:role/deny-sh-byok-role).
Finally, update the CMK's key policy to grant this role the same two actions. KMS resource policies and IAM policies are AND-ed, so both sides have to agree. In KMS → your key → Key policy, switch to JSON view and add a statement:
{
"Sid": "AllowDenyShRoleToUseKey",
"Effect": "Allow",
"Principal": {
"AWS": "<YOUR_ROLE_ARN>"
},
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt",
"kms:DescribeKey"
],
"Resource": "*"
}
Paste both ARNs into the deny.sh BYOK dashboard.
Open /dashboard/byok while signed in to your account. Paste the CMK ARN, the IAM role ARN, and pick the region. Click Register.
State transitions:
The verify call exercises the full path: STS AssumeRole, KMS GenerateDataKey, KMS Decrypt, comparison. If anything is misconfigured, you get a precise error code back. No partial state writes.
Verify, activate, and watch your CloudTrail.
Once active, every deny.sh wrap or unwrap on your data shows up in your AWS CloudTrail as a GenerateDataKey or Decrypt event under your CMK, with our IAM role as the principal, and our session name as deny-sh-byok-<tenant-prefix>.
You also get our side of the story in /dashboard/audit: every envelope event lands in the hash-chained audit log with op type byok.envelope.created or byok.envelope.unwrapped, plus byok.config.created, byok.config.verified, byok.config.rotated, byok.config.revoked, and byok.kms.failure for any error. Cross-reference the two trails any time.
To revoke us, click Revoke in the BYOK dashboard, or just remove the IAM role's trust policy from your side. Both work. From that moment, we cannot unwrap anything we previously wrapped under your key. This blocks future unwrapping; it cannot withdraw copies already obtained. Re-register the same key and restore permissions to regain access.
To rotate: create a fresh CMK, swap its ARN in the BYOK dashboard. New writes use the new CMK; old wrapped blobs remain readable under the old CMK as long as the old CMK still exists and the IAM role still has access. (V1 leaves old blobs under the old DEK; we do not bulk-re-envelope on rotate. Retain the old key and its access permissions for records encrypted with it.)
Ready to wire it up?
BYOK is available under an Enterprise contract. Open the dashboard, paste your ARNs, and inspect CloudTrail.
Open BYOK dashboard Read the long version