NIS2 Art.21(2)(h): Cryptography and Encryption Policy — Developer Implementation Guide (2026)
Most NIS2 compliance guides treat cryptography as a checkbox — "use TLS, encrypt at rest, done." Art.21(2)(h) of the NIS2 Directive sets a significantly higher bar. It requires essential and important entities to adopt documented cryptography and encryption policies covering algorithm selection, key management, and the evidence trail that NCA auditors will expect from June 2026 onward.
For SaaS teams, cloud infrastructure engineers, and developers building EU-regulated systems, Art.21(2)(h) means your cryptographic decisions — which algorithms you use, how you manage keys, how you handle deprecated ciphers — are now regulatory matters, not just engineering preferences.
This guide translates the legal text into concrete implementation requirements.
1. What Art.21(2)(h) Actually Requires
NIS2 Directive 2022/2555, Art.21(2)(h) states that entities must adopt measures addressing "the use of cryptography and, where appropriate, encryption."
Recital 79 of the Directive provides interpretive context: cryptography policies must be risk-appropriate, documented, and aligned with recognised standards. ENISA's technical guidance identifies the following components of a compliant cryptography policy:
The Four Components NCAs Will Assess
| Component | What Regulators Examine | Evidence Required |
|---|---|---|
| Algorithm selection policy | Approved/prohibited cipher lists, version constraints | Written policy, technology register |
| Key management lifecycle | Generation, storage, rotation, destruction | Key management procedure document |
| Certificate management | CA trust store, renewal automation, revocation handling | Certificate inventory, renewal logs |
| Cryptographic agility | Plan for migrating deprecated algorithms | Migration roadmap, post-quantum assessment |
The June 2026 NCA audit cycle for essential entities will include documentation requests for all four components. "We use TLS" is not a policy — it is a protocol choice. A policy specifies which TLS version, which cipher suites, why, reviewed when, and by whom.
What "Where Appropriate, Encryption" Means
The phrase "where appropriate, encryption" is not a carve-out. ENISA guidance and national transpositions consistently interpret this to mean encryption is required wherever:
- Personal data (GDPR Art.32 overlap) is processed
- Authentication credentials or secrets are stored
- Data transits untrusted networks
- Sensitive operational data (incident logs, configuration, audit trails) is persisted
For SaaS developers, this covers transport encryption (TLS), database encryption at rest, secrets management, and backup encryption — all of which must be addressed in the written policy.
2. ENISA Algorithm Selection: Approved and Prohibited
ENISA publishes annual cryptographic algorithm recommendations. The 2024 ENISA Recommendations on Cryptographic Algorithms are the reference document for Art.21(2)(h) compliance. NCAs will compare your policy against this list.
Transport Encryption
Required:
- TLS 1.3 (mandatory for new deployments)
- TLS 1.2 (acceptable legacy, cipher-restricted — see below)
Prohibited:
- TLS 1.1 and below (deprecated by RFC 8996, June 2021)
- SSL 2.0, SSL 3.0 (critically prohibited)
TLS 1.3 Cipher Suites (all mandatory-safe):
TLS_AES_256_GCM_SHA384
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256
TLS 1.2 Permitted Cipher Suites (restricted list):
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
Prohibited TLS 1.2 Cipher Suites:
TLS_RSA_* # no forward secrecy
TLS_*_CBC_* # BEAST/LUCKY13/POODLE-CBC exposure
TLS_*_RC4_* # RC4 broken
TLS_*_3DES_* # SWEET32 attack
TLS_*_EXPORT_* # export-grade (Logjam)
TLS_*_NULL_* # no encryption
TLS_*_anon_* # no authentication
Symmetric Encryption (Data at Rest)
| Algorithm | Key Length | Status | Use Case |
|---|---|---|---|
| AES-GCM | 256-bit | Recommended | Database encryption, file encryption |
| AES-CBC | 256-bit | Acceptable (authenticated) | Legacy systems only |
| AES-CBC | 128-bit | Acceptable (authenticated) | Legacy, plan migration |
| 3DES | any | Prohibited | No new use |
| DES | any | Prohibited | No use |
| RC4 | any | Prohibited | No use |
| ChaCha20-Poly1305 | 256-bit | Recommended | Mobile, embedded |
Critical rule: AES-CBC must always be combined with an HMAC-SHA256 or HMAC-SHA384 MAC. Using CBC without authentication creates padding oracle vulnerabilities. AES-GCM provides integrated authentication — prefer it for all new implementations.
Asymmetric Encryption and Key Exchange
| Algorithm | Parameters | Status |
|---|---|---|
| RSA | ≥ 3072-bit | Recommended (≥ 4096 preferred) |
| RSA | 2048-bit | Acceptable until 2030 |
| RSA | ≤ 1024-bit | Prohibited |
| ECDSA / ECDH | P-256 (secp256r1) | Recommended |
| ECDSA / ECDH | P-384 (secp384r1) | Recommended |
| ECDSA / ECDH | P-521 | Acceptable |
| EdDSA | Ed25519 | Recommended |
| EdDSA | Ed448 | Recommended |
| DH | ≥ 3072-bit | Acceptable |
| DH | ≤ 1024-bit | Prohibited |
Hash Functions
| Algorithm | Status | Use Case |
|---|---|---|
| SHA-256 | Recommended | Signatures, HMAC |
| SHA-384 | Recommended | Higher-security contexts |
| SHA-512 | Recommended | High-security contexts |
| SHA-3 family | Recommended | Future-proof, post-quantum adjacent |
| SHA-1 | Prohibited (signatures), Deprecated (HMAC) | No new use in signatures |
| MD5 | Prohibited | No use |
3. nginx TLS Hardening Configuration
A compliant nginx configuration for TLS 1.3 + restricted TLS 1.2:
# /etc/nginx/conf.d/tls-hardening.conf
# NIS2 Art.21(2)(h) compliant TLS configuration
# Last reviewed: 2026-04-16
# Owner: Platform Engineering
# Reference: ENISA Cryptographic Algorithm Recommendations 2024
ssl_protocols TLSv1.2 TLSv1.3;
# TLS 1.3: cipher suites are non-negotiable (set by OpenSSL)
# TLS 1.2: restrict to ECDHE + AESGCM/CHACHA (no CBC, no RSA key exchange)
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers on;
# ECDH curve selection (P-256 primary, P-384 secondary)
ssl_ecdh_curve X25519:prime256v1:secp384r1;
# Session management (no session tickets for perfect forward secrecy preservation)
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# HSTS (2-year max-age, include subdomains)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
# Certificate chain
ssl_certificate /etc/ssl/certs/your-domain.crt;
ssl_certificate_key /etc/ssl/private/your-domain.key;
ssl_trusted_certificate /etc/ssl/certs/chain.crt;
# DH params (4096-bit for TLS 1.2 DHE fallback)
# Generate: openssl dhparam -out /etc/ssl/dhparam.pem 4096
ssl_dhparam /etc/ssl/dhparam.pem;
NCA Audit Note: Keep a dated configuration changelog. Auditors will ask when you last reviewed your TLS configuration and who approved changes.
4. Caddy TLS Hardening Configuration
Caddy's automatic HTTPS handles certificate renewal but requires explicit cipher restrictions for Art.21(2)(h) compliance:
# Caddyfile — NIS2 Art.21(2)(h) compliant
# Last reviewed: 2026-04-16
# Owner: Platform Engineering
{
# Global TLS policy
servers {
protocols tls1.2 tls1.3
# Restrict TLS 1.2 cipher suites
# Caddy (via Go crypto/tls) defaults are already ECDHE-based
# Explicit restriction for audit trail
}
}
your-domain.com {
tls {
protocols tls1.2 tls1.3
# Curves: X25519 preferred (highest performance + security)
curves x25519 p256 p384
# Cipher suites (Go crypto/tls identifiers)
ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
}
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
}
}
Verification command (document in audit evidence):
# Test TLS configuration against ENISA requirements
# Run and save output dated for NCA evidence package
testssl.sh --protocols --ciphers --headers \
--severity HIGH --html your-domain.com \
| tee /var/log/tls-audit-$(date +%Y-%m-%d).html
5. Key Management Policy Template
Art.21(2)(h) requires a key management policy. This template covers the minimum NCA-auditable elements:
CRYPTOGRAPHIC KEY MANAGEMENT POLICY
Version: 1.0
Effective Date: [DATE]
Owner: [CISO / CTO]
Review Frequency: Annual (or after cryptographic incident)
Reference: NIS2 Art.21(2)(h), ENISA Cryptographic Recommendations 2024
1. SCOPE
Applies to all cryptographic keys used in production systems:
- TLS/HTTPS certificates and private keys
- Database encryption keys (at-rest encryption)
- API signing keys and JWT secrets
- SSH keys for infrastructure access
- Backup encryption keys
- Code signing keys
2. KEY GENERATION
- All keys generated using cryptographically secure random sources
(CSPRNG: /dev/urandom, HSM, cloud KMS hardware randomness)
- Minimum key lengths per ENISA 2024 recommendations:
RSA ≥ 3072-bit, ECDSA P-256/P-384, symmetric AES-256
- Keys never generated in untrusted environments
3. KEY STORAGE
- Private keys stored in Hardware Security Modules (HSMs) or
Cloud KMS (AWS KMS, GCP Cloud KMS, Azure Key Vault) where feasible
- Keys never stored in source code, config files, or version control
- Secrets management: HashiCorp Vault / cloud-native secrets manager
- Database encryption keys separated from data they protect
4. KEY ROTATION SCHEDULE
| Key Type | Rotation Frequency | Trigger Conditions |
|-----------------------|--------------------|--------------------------------|
| TLS certificates | Before expiry (-30d)| Compromise, CA revocation |
| API signing keys | 90 days | Personnel change, compromise |
| JWT secrets | 90 days | Compromise suspected |
| Database master keys | Annual | Staff turnover, audit finding |
| SSH infrastructure | 90 days | Staff departure, compromise |
| Backup encryption | Annual | Compromise, key material age |
5. KEY DISTRIBUTION
- Keys transmitted only over encrypted channels (TLS 1.3)
- No key material transmitted via email, Slack, or unencrypted chat
- Key sharing requires documented approval and audit log
6. KEY REVOCATION AND DESTRUCTION
- Compromised keys revoked within 4 hours of confirmed compromise
- Certificate revocation via OCSP/CRL within same timeframe
- Key destruction: NIST SP 800-88 Rev.1 (cryptographic erasure or physical)
- Destruction documented with date, method, and authorising personnel
7. CRYPTOGRAPHIC AGILITY
- Technology register maintained listing all cryptographic algorithms in use
- Annual review against ENISA recommendations for deprecated algorithms
- Post-quantum migration assessment: see Section 8
8. POST-QUANTUM PREPAREDNESS
- Current inventory: [list algorithms in use]
- NIST PQC candidates adopted: [e.g., ML-KEM for key encapsulation]
- Migration target date: 2027-2030 (per ENISA PQC timeline)
- Hybrid classical/PQC deployment: [date planned]
9. INCIDENT RESPONSE
- Cryptographic key compromise triggers NIS2 Art.23 incident assessment
- Key compromise affecting personal data also triggers GDPR Art.33 72h notification
- Incident log includes: key type, estimated exposure window, affected systems
10. AUDIT AND REVIEW
- Annual third-party review of key management practices
- NCA audit evidence: this policy + key rotation logs + HSM/KMS audit logs
- Last reviewed: [DATE] | Reviewed by: [NAME, ROLE] | Next review: [DATE]
6. Post-Quantum Cryptography: NIST PQC and EU Timeline
Art.21(2)(h) includes an implicit forward-looking obligation — ENISA guidance identifies post-quantum cryptography (PQC) migration as a required element of a complete cryptography policy. NCAs performing June 2026 audits will not require PQC deployment, but will expect a documented assessment and migration roadmap.
NIST PQC Finalized Standards (August 2024)
NIST published three post-quantum cryptographic standards in August 2024:
| Standard | Algorithm | Type | Use Case |
|---|---|---|---|
| FIPS 203 | ML-KEM (Kyber) | Key Encapsulation Mechanism | TLS key exchange, encrypted sessions |
| FIPS 204 | ML-DSA (Dilithium) | Digital Signature | Code signing, certificates |
| FIPS 205 | SLH-DSA (SPHINCS+) | Digital Signature | Alternative signature scheme |
A fourth standard, FN-DSA (FALCON), is forthcoming in 2025.
The EU Timeline
| Milestone | Date | Implication |
|---|---|---|
| ENISA PQC Recommendations published | 2024-11 | Policy framework for EU entities |
| BSI PQC Migration Guidelines | 2025 | German essential entities: document PQC roadmap |
| ETSI quantum-safe TLS profile | 2025-2026 | TLS PQC cipher suite standards |
| EU financial sector PQC transition | 2028-2030 | EBA/ESMA guidance expected |
| ENISA target: critical infra PQC deployment | 2030 | Long-lead systems (ICS/SCADA) |
Hybrid TLS 1.3 with ML-KEM (Implementation Preview)
Current browser and server support uses hybrid key exchange (classical ECDH + ML-KEM):
# Python 3.12+ with liboqs (Open Quantum Safe)
# Preview implementation — not yet production-ready for all stacks
# Document as "assessed, monitoring IETF/NIST standardization"
from oqs import KeyEncapsulation
def demonstrate_ml_kem_768():
"""
ML-KEM-768 (FIPS 203) key encapsulation demonstration.
Hybrid deployment: use alongside ECDH P-256 until PQC-only is safe.
"""
# Server: generate key pair
with KeyEncapsulation('ML-KEM-768') as server_kem:
public_key = server_kem.generate_keypair()
# Client: encapsulate shared secret
with KeyEncapsulation('ML-KEM-768') as client_kem:
ciphertext, shared_secret_client = client_kem.encap_secret(public_key)
# Server: decapsulate
shared_secret_server = server_kem.decap_secret(ciphertext)
assert shared_secret_client == shared_secret_server
return shared_secret_server # 32-byte shared secret
# For NCA audit evidence: document that PQC assessment is complete,
# hybrid deployment is planned for Q3 2027, monitoring IETF TLS PQC extensions.
What to Document for the June 2026 Audit
NCAs will accept a documented roadmap rather than deployed PQC. Your policy should state:
- Current inventory: List all asymmetric algorithms currently in production
- Threat assessment: "Harvest Now, Decrypt Later" attack window for your data sensitivity
- Migration target: Specific date for hybrid PQC deployment (e.g., "Q4 2027: TLS ML-KEM hybrid")
- Monitoring commitment: How you track IETF PQC TLS extensions, ENISA updates
7. Python NIS2CryptoPolicyAssessor
"""
NIS2CryptoPolicyAssessor — Art.21(2)(h) compliance assessment tool.
Assesses cryptographic policy coverage against ENISA 2024 recommendations.
"""
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
from datetime import date
class ComplianceStatus(Enum):
COMPLIANT = "compliant"
PARTIAL = "partial"
NON_COMPLIANT = "non_compliant"
UNKNOWN = "unknown"
class AlgorithmStatus(Enum):
APPROVED = "approved"
ACCEPTABLE = "acceptable"
DEPRECATED = "deprecated"
PROHIBITED = "prohibited"
@dataclass
class AlgorithmAssessment:
algorithm: str
key_length: Optional[str]
status: AlgorithmStatus
enisa_reference: str
migration_required_by: Optional[date] = None
replacement: Optional[str] = None
@dataclass
class KeyManagementAssessment:
has_written_policy: bool
has_rotation_schedule: bool
uses_hsm_or_kms: bool
has_destruction_procedure: bool
last_review_date: Optional[date]
def status(self) -> ComplianceStatus:
score = sum([
self.has_written_policy,
self.has_rotation_schedule,
self.uses_hsm_or_kms,
self.has_destruction_procedure,
self.last_review_date is not None and
(date.today() - self.last_review_date).days <= 365
])
if score >= 5: return ComplianceStatus.COMPLIANT
if score >= 3: return ComplianceStatus.PARTIAL
return ComplianceStatus.NON_COMPLIANT
@dataclass
class NIS2CryptoAssessment:
entity_name: str
assessment_date: date
tls_min_version: str
prohibited_protocols_disabled: bool
algorithm_inventory: list[AlgorithmAssessment] = field(default_factory=list)
key_management: Optional[KeyManagementAssessment] = None
has_pqc_assessment: bool = False
has_pqc_roadmap: bool = False
certificate_auto_renewal: bool = False
has_crypto_incident_procedure: bool = False
notes: list[str] = field(default_factory=list)
class NIS2CryptoPolicyAssessor:
"""
Assesses NIS2 Art.21(2)(h) cryptography policy compliance.
Use output as evidence package for NCA audit preparation.
"""
PROHIBITED_ALGORITHMS = {
"ssl2.0", "ssl3.0", "tls1.0", "tls1.1",
"des", "3des", "rc4", "rc2", "md5", "sha1_signatures",
"rsa_1024", "rsa_512", "dh_1024"
}
def assess(self, assessment: NIS2CryptoAssessment) -> dict:
findings = []
gaps = []
# TLS version check
if assessment.tls_min_version not in ("tls1.2", "tls1.3"):
gaps.append({
"control": "TLS minimum version",
"gap": f"Minimum TLS version '{assessment.tls_min_version}' does not meet NIS2 Art.21(2)(h). Require TLS 1.2 minimum.",
"severity": "critical",
"remediation": "Disable TLS 1.1 and below in all reverse proxies and load balancers."
})
else:
findings.append("TLS minimum version: COMPLIANT")
# Prohibited protocols
if not assessment.prohibited_protocols_disabled:
gaps.append({
"control": "Prohibited protocol enforcement",
"gap": "SSL/TLS 1.0/1.1 not confirmed disabled. NCA auditors will perform testssl.sh scan.",
"severity": "critical",
"remediation": "Run testssl.sh and document results. Disable deprecated protocols in nginx/Caddy/HAProxy."
})
# Algorithm inventory
prohibited_found = [
a for a in assessment.algorithm_inventory
if a.status == AlgorithmStatus.PROHIBITED
]
if prohibited_found:
for algo in prohibited_found:
gaps.append({
"control": f"Prohibited algorithm: {algo.algorithm}",
"gap": f"Prohibited algorithm {algo.algorithm} detected. ENISA: {algo.enisa_reference}",
"severity": "critical",
"remediation": f"Migrate to {algo.replacement or 'ENISA-approved alternative'} immediately."
})
# Key management
if assessment.key_management is None:
gaps.append({
"control": "Key management policy",
"gap": "No key management assessment provided. Art.21(2)(h) requires documented key lifecycle procedures.",
"severity": "high",
"remediation": "Complete key management policy using the NIS2 Art.21(2)(h) template."
})
else:
km_status = assessment.key_management.status()
if km_status == ComplianceStatus.NON_COMPLIANT:
gaps.append({
"control": "Key management lifecycle",
"gap": "Key management practices insufficient for Art.21(2)(h).",
"severity": "high",
"remediation": "Implement: written policy, rotation schedule, HSM/KMS, destruction procedure."
})
elif km_status == ComplianceStatus.PARTIAL:
gaps.append({
"control": "Key management lifecycle",
"gap": "Key management partially compliant. Review missing elements.",
"severity": "medium",
"remediation": "Address: " + ", ".join([
"written policy" if not assessment.key_management.has_written_policy else "",
"rotation schedule" if not assessment.key_management.has_rotation_schedule else "",
"HSM/KMS" if not assessment.key_management.uses_hsm_or_kms else "",
"destruction procedure" if not assessment.key_management.has_destruction_procedure else "",
]).strip(", ")
})
else:
findings.append("Key management lifecycle: COMPLIANT")
# Post-quantum
if not assessment.has_pqc_assessment:
gaps.append({
"control": "Post-quantum cryptography assessment",
"gap": "No PQC assessment documented. ENISA recommends all essential entities document PQC migration plans.",
"severity": "medium",
"remediation": "Document current algorithm inventory + 'Harvest Now Decrypt Later' risk + migration roadmap."
})
# Certificate renewal
if not assessment.certificate_auto_renewal:
gaps.append({
"control": "Certificate lifecycle automation",
"gap": "No automated certificate renewal. Manual renewal creates expiry risk.",
"severity": "medium",
"remediation": "Implement ACME automation (certbot, Caddy automatic TLS, cert-manager in Kubernetes)."
})
# Crypto incident procedure
if not assessment.has_crypto_incident_procedure:
gaps.append({
"control": "Cryptographic incident response",
"gap": "No documented procedure for key compromise response. NIS2 Art.23 may be triggered by key compromise.",
"severity": "medium",
"remediation": "Add cryptographic incident procedure to incident response runbook."
})
# Score
total_controls = 6
critical_gaps = len([g for g in gaps if g["severity"] == "critical"])
high_gaps = len([g for g in gaps if g["severity"] == "high"])
compliant_controls = total_controls - len(gaps)
overall = ComplianceStatus.NON_COMPLIANT
if critical_gaps == 0 and high_gaps == 0 and len(gaps) <= 1:
overall = ComplianceStatus.COMPLIANT
elif critical_gaps == 0:
overall = ComplianceStatus.PARTIAL
return {
"entity": assessment.entity_name,
"assessment_date": str(assessment.assessment_date),
"article": "NIS2 Art.21(2)(h)",
"overall_status": overall.value,
"compliant_controls": compliant_controls,
"total_controls": total_controls,
"critical_gaps": critical_gaps,
"findings": findings,
"gaps": gaps,
"nca_audit_readiness": "READY" if overall == ComplianceStatus.COMPLIANT else "NOT READY",
"priority_actions": [g["remediation"] for g in gaps if g["severity"] == "critical"]
}
8. The 25-Item NIS2 Art.21(2)(h) Developer Checklist
Use this list to prepare your NCA audit evidence package. Each item must be documented with evidence (config file, policy document, tool output, or dated review record).
Transport Encryption (TLS)
- 1. TLS 1.3 enabled on all public-facing endpoints
- 2. TLS 1.2 permitted only with ECDHE + AESGCM/CHACHA cipher suites
- 3. TLS 1.1 and below explicitly disabled and verified (testssl.sh output on file)
- 4. SSL 2.0 and SSL 3.0 disabled (confirm via testssl.sh critical check)
- 5. HSTS header deployed with ≥ 1 year max-age + includeSubDomains
- 6. Session tickets disabled (or rotated ≤ 24h for PFS preservation)
- 7. OCSP stapling enabled and verified
Encryption at Rest
- 8. Database encryption at rest enabled (AES-256-GCM or platform-equivalent)
- 9. Backup files encrypted before storage (separate key from backup data)
- 10. Filesystem-level encryption for sensitive data volumes (LUKS, dm-crypt, or equivalent)
- 11. Secrets never stored in plaintext in environment files or config repos
Key Management
- 12. Written key management policy in place (covers generation, storage, rotation, destruction)
- 13. Key rotation schedule defined per key type (see policy template Section 4)
- 14. Private keys stored in HSM, cloud KMS, or secrets manager (never in source code)
- 15. Certificate auto-renewal implemented (certbot/ACME/cert-manager)
- 16. Key destruction procedure documented (NIST SP 800-88 aligned)
Policy and Documentation
- 17. Cryptography policy document exists and is dated within 12 months
- 18. Algorithm inventory maintained (list of all cryptographic algorithms in production use)
- 19. Prohibited algorithm list defined and enforced (RC4, 3DES, MD5, SHA-1 for signatures)
- 20. Policy approved by management body (Art.21(2)(h) requires board-level accountability per Art.17)
- 21. Annual cryptographic review scheduled (next review date documented)
Post-Quantum Readiness
- 22. PQC assessment completed: current algorithm inventory vs NIST PQC FIPS 203/204/205
- 23. "Harvest Now, Decrypt Later" risk assessed for your data sensitivity level
- 24. PQC migration roadmap documented (target date for hybrid deployment)
Incident Response
- 25. Cryptographic key compromise procedure documented in incident runbook (includes NIS2 Art.23 trigger assessment)
9. NCA Audit Documentation Package
When preparing for a June 2026 NCA audit, assemble the following as your Art.21(2)(h) evidence package:
Document 1: Cryptography Policy
- Written policy document (see template in Section 5)
- Management approval signature or board minute reference
- Version history showing last annual review
Document 2: Algorithm Inventory
- Spreadsheet or system register listing all cryptographic algorithms in production
- For each: algorithm, key length, use case, system/component, review date
- Automated discovery tool output (e.g., from cloud KMS inventory, certificate transparency logs)
Document 3: TLS Configuration Evidence
- testssl.sh HTML report for each public-facing domain (dated within 30 days of audit)
- nginx/Caddy/HAProxy TLS configuration files with review timestamps
- SSL Labs grade reports (target A+ for essential entities)
Document 4: Key Management Audit Trail
- Certificate inventory with expiry dates and renewal automation confirmation
- KMS/secrets manager audit logs showing key rotation events (last 12 months)
- Key destruction records for any retired keys
Document 5: Post-Quantum Assessment
- Written assessment document (2-3 pages minimum)
- Migration roadmap with target dates
- Reference to NIST PQC FIPS 203/204/205 and ENISA PQC recommendations
10. Intersection with Other NIS2 and EU Regulations
Art.21(2)(h) does not exist in isolation. Cryptography requirements intersect with:
GDPR Art.32: Security of Processing
GDPR Art.32(1)(a) explicitly mentions "the pseudonymisation and encryption of personal data" as appropriate technical measures. For SaaS entities subject to both NIS2 and GDPR, Art.21(2)(h) and Art.32 are complementary — a single cryptography policy can satisfy both. Document the dual-compliance mapping.
NIS2 Art.21(2)(j): Multi-Factor Authentication
MFA implementations using hardware tokens (FIDO2/WebAuthn) rely on asymmetric cryptography (ECDSA on P-256). Your Art.21(2)(h) algorithm selection policy must cover the cryptographic primitives used in your authentication stack.
NIS2 Art.23: Incident Reporting
A key compromise that affects the confidentiality or integrity of your systems may trigger NIS2 Art.23 incident reporting. Your cryptographic incident response procedure (Checklist Item 25) should include an Art.23 trigger assessment.
CRA Art.14: Vulnerability Reporting (September 2026)
Software manufacturers under the Cyber Resilience Act must report actively exploited vulnerabilities within 24 hours to ENISA from September 2026. Cryptographic vulnerabilities in your product (broken cipher, weak key generation) that are actively exploited qualify. Your CRA compliance must include cryptographic vulnerability handling within the same framework as your NIS2 Art.21(2)(h) CVD policy.
Cloud Jurisdiction and Cryptography Sovereignty
For essential entities using US-headquartered cloud providers, CLOUD Act jurisdiction risk applies to encrypted data: if a US provider holds key material (KMS), US law enforcement can compel access regardless of data location. Art.21(2)(h) supply chain risk assessment (linked to Art.21(2)(d)) should document whether your encryption architecture is cloud-act-resilient — i.e., whether your key material is under EU jurisdiction. EU-native infrastructure where the provider has no US parent eliminates this exposure.
Conclusion
NIS2 Art.21(2)(h) transforms cryptographic best practices into regulatory obligations. For essential and important entities, a documented cryptography policy covering algorithm selection, key management lifecycle, certificate automation, and post-quantum preparedness is now auditable from June 2026.
The technical controls — TLS 1.3 with restricted cipher suites, AES-256-GCM for data at rest, proper key rotation, HSM or KMS storage — are achievable by any SaaS team. The gap is almost always documentation: written policies, dated reviews, and the audit trail that demonstrates these controls are consistently applied.
Start with the 25-item checklist and the key management policy template. Run testssl.sh against your endpoints and save the dated output. Document your PQC assessment even if migration is years away. This evidence package is what NCA auditors will request — and what separates compliant entities from those facing Art.32 enforcement.
EU-native infrastructure like sota.io — where key material stays under EU jurisdiction and there is no US parent subject to CLOUD Act compelled disclosure — directly addresses the supply chain cryptography risk that Art.21(2)(d) and Art.21(2)(h) jointly create for essential entities using US-headquartered cloud providers.
See also:
- NIS2 Art.21(2)(j): MFA Implementation Guide — FIDO2/passkeys, account scoping, continuous authentication
- NIS2 Art.21(2)(e): Secure Development Lifecycle Guide — CVD policy, SAST/DAST, SDL for SaaS developers
- NIS2 Art.21 Complete Risk Management Framework