← Knowledge base

AWS

Is PQC enabled? — quick check

macOS / Linux

# 1) No-dependency check — identify this machine first.
uname -a 2>/dev/null || true

# 2) Dependency check — prompt before installing anything.
if ! command -v openssl >/dev/null 2>&1; then
  echo 'OpenSSL was not found. A local PQC proof needs OpenSSL 3.5+.'
  printf 'Install OpenSSL now? [y/N] '
  read answer
  case "$answer" in
    [Yy]*)
      if command -v brew >/dev/null 2>&1; then brew install openssl@3
      elif command -v apt-get >/dev/null 2>&1; then sudo apt-get update && sudo apt-get install -y openssl
      elif command -v dnf >/dev/null 2>&1; then sudo dnf install -y openssl
      elif command -v yum >/dev/null 2>&1; then sudo yum install -y openssl
      else echo 'No supported package manager found. Install OpenSSL 3.5+ and retry.'; exit 1
      fi ;;
    *) echo 'Install OpenSSL 3.5+ and retry for a local PQC proof.'; exit 1 ;;
  esac
fi

OPENSSL=openssl
if command -v brew >/dev/null 2>&1; then
  BREW_OPENSSL="$(brew --prefix openssl@3 2>/dev/null)/bin/openssl"
  [ -x "$BREW_OPENSSL" ] && OPENSSL="$BREW_OPENSSL"
fi

$OPENSSL version
if ! $OPENSSL list -tls-groups 2>/dev/null | grep -qiE 'X25519MLKEM768|MLKEM|Kyber'; then
  echo 'This OpenSSL does not advertise ML-KEM groups. Upgrade to OpenSSL 3.5+ or load oqsprovider, then retry.'
  exit 1
fi
$OPENSSL list -kem-algorithms 2>/dev/null | grep -iE 'mlkem|kyber'   || echo 'no native ML-KEM (try "$OPENSSL list -providers" for oqsprovider)'

# 3) Live handshake against your server. The 'Negotiated TLS1.3 group' line
#    (or 'Server Temp Key' on OpenSSL 3.5) tells you what was negotiated
#    end-to-end — no external service required.
$OPENSSL s_client -connect example.com:443 -tls1_3 -groups X25519MLKEM768   </dev/null 2>&1 |
  grep -E 'Negotiated TLS1\.3 group|Server Temp Key|Cipher is|alert'

Expected when PQC is ON

OpenSSL 3.6.2 7 Apr 2026
  X25519MLKEM768 @ default
Negotiated TLS1.3 group: X25519MLKEM768
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

What you'll see when PQC is OFF

OpenSSL 3.0.13 30 Jan 2024
no native ML-KEM
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server Temp Key: X25519, 253 bits

To enable PQ on your server, see the nginx, Apache, or Caddy KB pages — it's one line: ssl_ecdh_curve / SSLOpenSSLConfCmd Groups / tls.curve_preferences = X25519MLKEM768:X25519:secp384r1.

Windows PowerShell

# 1) No-dependency check — identify this machine first.
[System.Environment]::OSVersion.Version
Get-ComputerInfo | Select-Object OsName,OsVersion,OsBuildNumber

# 2) Dependency check — prompt before installing anything.
function Confirm-Install($Message) {
  $answer = Read-Host "$Message Install now? [y/N]"
  return ($answer -match '^[Yy]')
}

function Get-OpenSSLPath {
  $cmd = Get-Command openssl.exe -ErrorAction SilentlyContinue
  if ($cmd) { return $cmd.Source }

  $programFilesX86 = [Environment]::GetEnvironmentVariable('ProgramFiles(x86)')
  $currentPath = (Get-Location).Path
  $candidateDirs = @(
    $(if ($env:ProgramFiles) { Join-Path (Join-Path $env:ProgramFiles 'OpenSSL-Win64') 'bin' }),
    $(if ($programFilesX86) { Join-Path (Join-Path $programFilesX86 'OpenSSL-Win32') 'bin' }),
    $(if ($env:LOCALAPPDATA) { Join-Path (Join-Path (Join-Path $env:LOCALAPPDATA 'Programs') 'OpenSSL-Win64') 'bin' }),
    $currentPath,
    $(if ($currentPath) { Join-Path (Join-Path $currentPath 'openssl') 'bin' })
  )
  foreach ($dir in $candidateDirs) {
    if (-not $dir) { continue }
    $candidate = Join-Path $dir 'openssl.exe'
    if ($candidate -and (Test-Path -LiteralPath $candidate -PathType Leaf)) { return $candidate }
  }
  return $null
}

$openssl = Get-OpenSSLPath
if (-not $openssl) {
  Write-Warning 'OpenSSL 3.5+ was not found. Built-in Windows PowerShell/Schannel cannot prove the PQC named group.'
  if ((Get-Command winget -ErrorAction SilentlyContinue) -and (Confirm-Install 'Install ShiningLight OpenSSL with winget?')) {
    winget install ShiningLight.OpenSSL.Light
    $openssl = Get-OpenSSLPath
  }
  if (-not $openssl) { 'Install or pre-stage OpenSSL 3.5+ and retry.'; return }
}

& $openssl version
$groups = (& $openssl list -tls-groups 2>$null) -join [Environment]::NewLine
if ($groups -notmatch 'X25519MLKEM768|MLKEM|Kyber') {
  'Local OpenSSL does not advertise ML-KEM groups. Install or pre-stage OpenSSL 3.5+ and retry.'
  return
}
& $openssl list -kem-algorithms | Select-String -Pattern 'mlkem|kyber'

# 3) Live handshake — fully local, no api.checkpqc.app needed.
& $openssl s_client -connect example.com:443 -tls1_3 -groups X25519MLKEM768 2>&1 |
  Select-String -Pattern 'Negotiated TLS1\.3 group|Server Temp Key|Cipher is|alert'

Expected when PQC is ON

OpenSSL 3.6.2 7 Apr 2026
  X25519MLKEM768 @ default
Negotiated TLS1.3 group: X25519MLKEM768
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

What you'll see when PQC is OFF

OpenSSL 1.1.1w  11 Sep 2023
# (no mlkem line)
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
# (no group line — 1.1.1 cannot offer ML-KEM)

Schannel cannot expose the negotiated TLS named group programmatically, so a local OpenSSL binary is the most reliable offline answer on Windows.

CloudFront

Use a security policy that negotiates TLS 1.3. AWS rolled out hybrid post-quantum key agreement to CloudFront edge servers in 2024 — when a capable client offers X25519MLKEM768 CloudFront will accept it. There is no toggle to expose; pick a modern security policy:

SecurityPolicy: TLSv1.2_2021   # or TLSv1.3 only via custom policy

Application Load Balancer (ALB)

ALB exposes TLS policies like ELBSecurityPolicy-TLS13-1-3-2021-06. As of 2026 AWS has not exposed a public PQC policy for ALB. If you terminate TLS at the ALB and need hybrid PQC, use CloudFront in front of the ALB or terminate at your origin behind a Network Load Balancer.

API Gateway

Same as ALB — TLS 1.3 is supported, hybrid PQC is not yet exposed. Front it with CloudFront for PQC negotiation today.

ACM (certificates)

Certificate selection is independent of the key-agreement group. ACM-issued ECDSA P-256 or RSA 2048 certs work fine with hybrid PQC handshakes.

Verify

if ! command -v openssl >/dev/null 2>&1; then
  echo 'OpenSSL was not found.'
  printf 'Install OpenSSL now? [y/N] '; read answer
  case "$answer" in [Yy]*) sudo apt-get update && sudo apt-get install -y openssl ;; *) exit 1 ;; esac
fi
openssl s_client -connect d123.cloudfront.net:443 -tls1_3 \
  -groups X25519MLKEM768 </dev/null 2>&1 | grep "Cipher is\|alert"

Background

AWS rolled hybrid post-quantum TLS into CloudFront, API Gateway, and ALB during 2024–2025 via the s2n-tls library. The control isn't exposed as a per-distribution toggle: the edge negotiates PQ when the client offers it and the chosen security policy permits it.

Picking a security policy

What's not covered yet

References

Run the check on your site →