Sitemap

IAM Roles Anywhere with post-quantum cryptography

5 min readNov 1, 2026

--

AWS announced post-quantum cryptography support for IAM Roles Anywhere in March 2026.

This step-by-step guide details how to set up IAM Roles Anywhere with ML-DSA (Module-Lattice Digital Signature Algorithm) certificates, issued by our open-source serverless CA.

Press enter or click to view image in full size
IAM Roles Anywhere with ML-DSA

Create Post-quantum cryptography Serverless CA

Begin by following the instructions in Open-source Post-quantum Cryptography Serverless CA to create a ML-DSA CA.

In my case, I updated the example Cloud CA with an additional ML-DSA CA operating alongside my existing ECDSA CA. Following deployment, I downloaded my new root and issuing ML-DSA CA certs from:

Root PQC CA: https://certs.cloud-ca.com/pqc-root-ca-dev.crt

Issuing PQC CA: https://certs.cloud-ca.com/pqc-issuing-ca-dev.crt

ML-DSA Root CA
ML-DSA Root and Issuing CA chain

You’ll be able to download your own CA certificates from the equivalent domain and URL for your own serverless CA.

Configure IAM Roles Anywhere

Download the CA bundle from the public endpoint, in my case: https://certs.cloud-ca.com/pqc-ca-bundle-dev.pem. If you opted to keep your CA information private, the equivalent file can be downloaded from the S3 bucket with external in the name.

We’ll be taking a similar approach to that described in my post AWS IAM Roles Anywhere with open-source private CA.

Create Trust Anchor

  • log in to your Application AWS account
  • Select IAM, Roles
  • Scroll down to Roles Anywhere, Manage
Press enter or click to view image in full size
  • Create a trust anchor
Press enter or click to view image in full size
  • Choose your region
  • Set a name for the trust anchor, e.g. pqc-ca
  • Select External certificate bundle
  • Paste in the CA certs bundle certs from your clipboard
  • Leave notification defaults
  • Add tags if desired
  • Press Create Trust Anchor
Press enter or click to view image in full size

In my case, the ML-DSA trust anchor is set up in the same account and region as my already existing ECDSA trust anchor.

Next set up the IAM role, trust policy, and optional session policy profile, following instructions in my post AWS IAM Roles Anywhere with open-source private CA.

Get Paul Schwarzenberger’s stories in your inbox

Join Medium for free to get updates from this writer.

In my case, as I’d already done that previously for ECDSA testing with IAM Roles Anywhere, I didn’t need to repeat it.

Download AWS IAM Roles Anywhere Credential Helper

  • Download the AWS IAM Roles Anywhere Credential Helper v1.85 or later for your operating system
  • Copy the binary into your home directory
  • Alternatively copy to another location, e.g. usr/bin/local and set directory in your PATH
  • if using MacOS, remove file name extended attribute: xattr -c aws_signing_helper
  • set as executable: chmod 755 aws_signing_helper

Obtain ML-DSA client certificate

Issue a fully post-quantum client certificate from your ML-DSA CA, with AWS credentials for your CA AWS account:

git clone https://github.com/serverless-ca/terraform-aws-ca.git
cd terraform-aws-ca
pip install -r utils/requirements.txt
python utils/client-cert.py --profile <your-aws-profile> --keyalgo ml-dsa-44 --project pqc
  • ML-DSA-44 key pair is generated locally
  • CSR is signed with the local ML-DSA key and submitted to the tls-cert Lambda function, which issues the certificate signed by the ML-DSA issuing CA
  • — project pqc targets the ML-DSA deployment when more than one CA shares the AWS account, as in the example deployment — omit for an account with a single CA
  • certificate, private key (PKCS8) and CA bundle are written to your local ~/certs directory

Install OpenSSL version 4, make sure it’s the default version in PATH, and run:

openssl verify -CAfile ~/certs/ca-bundle.pem ~/certs/client-cert.crt
openssl x509 -in ~/certs/client-cert.crt -text -noout

Convert ML-DSA private key format

The AWS IAM Roles Anywhere Credential Helper version 1.8.5 only supports one of the three ML-DSA private key formats allowed by RFC 9881, and this is not the version used by Python Cryptography. Details are in this GitHub Issue which I raised on 1 November 2026.

To ensure compatibility, using OpenSSL version 4, convert the private key format:

openssl pkey -in ~/certs/client-key.pem -provparam ml-dsa.output_formats=seed-priv -out ~/certs/client-key-seedpriv.pem
openssl asn1parse -in ~/certs/client-key-seedpriv.pem -strparse 20 | cut -c1-60 # expect SEQUENCE, OCTET STRING l=32, OCTET STRING l=2560

Set AWS CLI profile

Using a text editor such as vi edit ~/.aws/config and insert:

[profile pqc]
credential_process = ./aws_signing_helper credential-process --certificate certs/client-cert.crt --private-key certs/client-key-seedpriv.pem --trust-anchor-arn arn:aws:rolesanywhere:REGION:ACCOUNT_ID:trust-anchor/TRUST_ANCHOR_ID --profile-arn arn:aws:rolesanywhere:REGION:ACCOUNT_ID:profile/PROFILE_ID --role-arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME
  • for example:
[profile pqc]
credential_process = ./aws_signing_helper credential-process --certificate certs/client-cert.crt --private-key certs/client-key-seedpriv.pem --trust-anchor-arn "arn:aws:rolesanywhere:eu-west-2:992382525818:trust-anchor/3b909a8c-091f-4367-b0bc-2198ca133449" --profile-arn "arn:aws:rolesanywhere:eu-west-2:992382525818:profile/86227258-a142-433d-a4bc-6015c99b39b9" --role-arn "arn:aws:iam::992382525818:role/roles-anywhere-s3-full-access"
  • substitute your ARNs
  • save changes

Test access

  • check AWS identity:
aws sts get-caller-identity --profile pqc
  • you should get a response like this:
{
"UserId": "AROA6ODU3UF5DTDLJCYVE:401f1972253b0bb86bd62ac8285ad6236cc8f45a",
"Account": "992382525818",
"Arn": "arn:aws:sts::992382525818:assumed-role/roles-anywhere-s3-full-access/401f1972253b0bb86bd62ac8285ad6236cc8f45a"
}
  • list all S3 buckets, this should fail as access is only allowed to a single bucket
aws s3 ls --profile pqc
An error occurred (AccessDenied) when calling the ListBuckets operation: User: arn:aws:sts::992382525818:assumed-role/roles-anywhere-s3-full-access/401f1972253b0bb86bd62ac8285ad6236cc8f45a is not authorized to perform: s3:ListAllMyBuckets because no session policy allows the s3:ListAllMyBuckets action
  • download a file from the designated S3 bucket, this should succeed
  • replace the example below with your own S3 bucket and object
aws s3 cp s3://cloud-apps-confidential/confidential.png confidential.png --profile pqc
download: s3://cloud-apps-confidential/confidential.png to ./confidential.png

Optionally you can set this AWS CLI profile as your default, in which case you don’t need to include the profile argument.

8. View connection details

  • At IAM, Roles, Roles Anywhere, navigate to your region
  • Select Subject Activity
Press enter or click to view image in full size
  • A record of connections can be seen
  • Click on the link to view the certificate used to authenticate
  • In CloudTrail, search for the CreateSession event from the rolesanywhere.amazonaws.com event source
Press enter or click to view image in full size

Details of the client, role and trust anchor can be viewed in the JSON event.

👏 🎉 🎊 Congratulations, you’ve set up and tested AWS IAM Roles Anywhere with post-quantum cryptography, using the open-source private CA 🎆 🌟 🎇

--

--

Paul Schwarzenberger
Paul Schwarzenberger

Paul Schwarzenberger is a cloud security architect and engineer, creator of OWASP Domain Protect, and cloud security trainer.