← Back to PQC Security

Crypto Inventory Scanner

Automated discovery of quantum-vulnerable cryptography across cloud infrastructure

Overview

Build a comprehensive cryptographic asset inventory as required by OMB M-23-02 and CISA guidelines. These free scripts automatically discover RSA, ECC, and DH usage across Azure and AWS.

Inventory Coverage:
  • Azure: Key Vault, App Service Certificates, VMs, Storage Accounts, SQL Databases
  • AWS: KMS, Certificate Manager (ACM), IAM Certificates, RDS, S3 encryption
  • Network: TLS/SSL configurations, VPN settings, load balancer certificates
  • Applications: Code-signing keys, API keys, database encryption keys

⚠️ Important Disclaimer

Educational Purpose Only: All code samples and scripts provided on this page are for educational and illustrative purposes. They are NOT production-ready and have NOT been thoroughly tested in all environments.

Recommendation: Treat these examples as starting points for learning and development. Adapt, test, and validate according to your specific requirements and security standards.

Quick Start

# Clone and setup
git clone https://github.com/spindynamics/pqc-tools.git
cd pqc-tools
./setup.sh
source venv/bin/activate

# Run inventory scan (Azure)
python azure/inventory_scanner.py \
  --subscription-id YOUR_SUBSCRIPTION_ID \
  --output inventory/azure-crypto-inventory.json

# Run inventory scan (AWS)
python aws/inventory_scanner.py \
  --region us-east-1 \
  --output inventory/aws-crypto-inventory.json

Inventory Scripts to Run

Script 1: Azure Key Vault Comprehensive Scan

Discovers all keys, secrets, and certificates in Azure Key Vaults

python azure/keyvault_inventory.py \
  --subscription-id YOUR_SUBSCRIPTION_ID \
  --output inventory/azure-keyvault-inventory.json \
  --include-properties \
  --check-expiration

Discovers: Key types (RSA/EC/oct), key sizes, creation dates, expiration dates, enabled/disabled status

Python Code: Azure Key Vault Scanner

#!/usr/bin/env python3
"""Azure Key Vault Cryptographic Inventory Scanner"""

import azure.identity
import azure.keyvault.keys
import azure.keyvault.secrets
import azure.keyvault.certificates
from azure.mgmt.keyvault import KeyVaultManagementClient
import json
from datetime import datetime

def scan_azure_keyvaults(subscription_id):
    """Scan all Key Vaults in subscription"""

    credential = azure.identity.DefaultAzureCredential()
    kv_mgmt_client = KeyVaultManagementClient(credential, subscription_id)

    inventory = {
        "scan_date": datetime.now().isoformat(),
        "subscription_id": subscription_id,
        "vaults": []
    }

    # List all key vaults
    vaults = kv_mgmt_client.vaults.list()

    for vault in vaults:
        vault_url = f"https://{vault.name}.vault.azure.net"
        print(f"Scanning vault: {vault.name}")

        vault_data = {
            "name": vault.name,
            "resource_group": vault.id.split('/')[4],
            "location": vault.location,
            "keys": [],
            "certificates": [],
            "vulnerable_count": 0
        }

        try:
            # Scan keys
            key_client = azure.keyvault.keys.KeyClient(vault_url, credential)
            for key_props in key_client.list_properties_of_keys():
                key = key_client.get_key(key_props.name)

                key_info = {
                    "name": key.name,
                    "type": key.key.kty,
                    "enabled": key.properties.enabled,
                    "created": key.properties.created_on.isoformat() if key.properties.created_on else None,
                    "expires": key.properties.expires_on.isoformat() if key.properties.expires_on else None,
                    "quantum_vulnerable": False
                }

                # Check if quantum-vulnerable
                if key.key.kty in ["RSA", "EC", "RSA-HSM", "EC-HSM"]:
                    key_info["quantum_vulnerable"] = True
                    vault_data["vulnerable_count"] += 1

                    if key.key.kty.startswith("RSA"):
                        key_info["key_size"] = key.key.n.bit_length() if key.key.n else None
                    elif key.key.kty.startswith("EC"):
                        key_info["curve"] = key.key.crv.value if key.key.crv else None

                vault_data["keys"].append(key_info)

            # Scan certificates
            cert_client = azure.keyvault.certificates.CertificateClient(vault_url, credential)
            for cert_props in cert_client.list_properties_of_certificates():
                cert = cert_client.get_certificate(cert_props.name)

                cert_info = {
                    "name": cert.name,
                    "subject": str(cert.properties.subject) if cert.properties.subject else None,
                    "thumbprint": cert.properties.thumbprint.hex() if cert.properties.thumbprint else None,
                    "enabled": cert.properties.enabled,
                    "expires": cert.properties.expires_on.isoformat() if cert.properties.expires_on else None,
                    "quantum_vulnerable": True  # Most X.509 certs use RSA/ECDSA
                }

                vault_data["certificates"].append(cert_info)
                vault_data["vulnerable_count"] += 1

        except Exception as e:
            vault_data["error"] = str(e)
            print(f"  Error scanning {vault.name}: {e}")

        inventory["vaults"].append(vault_data)

    # Summary
    total_vulnerable = sum(v["vulnerable_count"] for v in inventory["vaults"])
    inventory["summary"] = {
        "total_vaults": len(inventory["vaults"]),
        "total_vulnerable_assets": total_vulnerable
    }

    return inventory

if __name__ == "__main__":
    import sys
    if len(sys.argv) < 2:
        print("Usage: python keyvault_inventory.py SUBSCRIPTION_ID")
        sys.exit(1)

    subscription_id = sys.argv[1]
    results = scan_azure_keyvaults(subscription_id)

    # Save to file
    with open("azure-keyvault-inventory.json", "w") as f:
        json.dump(results, f, indent=2)

    print(f"\\nInventory complete!")
    print(f"Total vaults scanned: {results['summary']['total_vaults']}")
    print(f"Quantum-vulnerable assets: {results['summary']['total_vulnerable_assets']}")

Script 2: AWS KMS Key Inventory

Scans AWS KMS for RSA and ECC keys across all regions

python aws/kms_inventory.py \
  --profile default \
  --all-regions \
  --output inventory/aws-kms-inventory.json

Python Code: AWS KMS Scanner

#!/usr/bin/env python3
"""AWS KMS Cryptographic Inventory Scanner"""

import boto3
import json
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed

def scan_kms_region(region_name):
    """Scan KMS keys in a single region"""
    print(f"Scanning region: {region_name}")

    kms = boto3.client('kms', region_name=region_name)
    vulnerable_keys = []

    try:
        # List all KMS keys
        paginator = kms.get_paginator('list_keys')
        for page in paginator.paginate():
            for key in page['Keys']:
                key_id = key['KeyId']

                try:
                    # Get key metadata
                    metadata = kms.describe_key(KeyId=key_id)['KeyMetadata']

                    key_spec = metadata.get('KeySpec', 'SYMMETRIC_DEFAULT')
                    key_usage = metadata.get('KeyUsage', 'ENCRYPT_DECRYPT')

                    # Check if quantum-vulnerable
                    vulnerable_specs = [
                        'RSA_2048', 'RSA_3072', 'RSA_4096',
                        'ECC_NIST_P256', 'ECC_NIST_P384', 'ECC_NIST_P521',
                        'ECC_SECG_P256K1'
                    ]

                    if key_spec in vulnerable_specs:
                        key_info = {
                            "KeyId": key_id,
                            "KeyArn": metadata['Arn'],
                            "KeySpec": key_spec,
                            "KeyUsage": key_usage,
                            "KeyState": metadata['KeyState'],
                            "CreationDate": metadata['CreationDate'].isoformat(),
                            "Enabled": metadata['Enabled'],
                            "Description": metadata.get('Description', ''),
                            "Region": region_name
                        }

                        # Get key tags
                        try:
                            tags_response = kms.list_resource_tags(KeyId=key_id)
                            key_info["Tags"] = {tag['TagKey']: tag['TagValue']
                                                for tag in tags_response.get('Tags', [])}
                        except:
                            key_info["Tags"] = {}

                        vulnerable_keys.append(key_info)

                except Exception as e:
                    print(f"  Error describing key {key_id}: {e}")

    except Exception as e:
        print(f"  Error scanning region {region_name}: {e}")
        return {"region": region_name, "error": str(e), "keys": []}

    return {"region": region_name, "keys": vulnerable_keys, "count": len(vulnerable_keys)}


def scan_all_aws_kms():
    """Scan all AWS regions for KMS keys"""

    ec2 = boto3.client('ec2', region_name='us-east-1')
    regions = [r['RegionName'] for r in ec2.describe_regions()['Regions']]

    inventory = {
        "scan_date": datetime.now().isoformat(),
        "regions": [],
        "total_vulnerable_keys": 0
    }

    # Scan regions in parallel
    with ThreadPoolExecutor(max_workers=5) as executor:
        future_to_region = {executor.submit(scan_kms_region, region): region
                            for region in regions}

        for future in as_completed(future_to_region):
            region_data = future.result()
            inventory["regions"].append(region_data)
            inventory["total_vulnerable_keys"] += region_data.get("count", 0)

    return inventory


if __name__ == "__main__":
    results = scan_all_aws_kms()

    # Save to file
    with open("aws-kms-inventory.json", "w") as f:
        json.dump(results, f, indent=2, default=str)

    print(f"\\n=== AWS KMS Inventory Complete ===")
    print(f"Regions scanned: {len(results['regions'])}")
    print(f"Quantum-vulnerable keys found: {results['total_vulnerable_keys']}")

    # Summary by region
    print("\\nBy Region:")
    for region_data in sorted(results['regions'], key=lambda x: x.get('count', 0), reverse=True):
        if region_data.get('count', 0) > 0:
            print(f"  {region_data['region']}: {region_data['count']} vulnerable keys")

Script 3: X.509 Certificate Inventory (Multi-Cloud)

Discovers all SSL/TLS certificates and analyzes signature algorithms

# Azure App Service Certificates
python azure/certificate_inventory.py \
  --subscription-id YOUR_SUBSCRIPTION_ID \
  --output inventory/azure-certificates.json

# AWS Certificate Manager (ACM)
python aws/acm_inventory.py \
  --all-regions \
  --output inventory/aws-acm-certificates.json

# AWS IAM Server Certificates
python aws/iam_certificate_inventory.py \
  --output inventory/aws-iam-certificates.json

Output includes: Subject, Issuer, Serial number, Signature algorithm (RSA/ECDSA), Key size, Expiration date, SANs

Script 4: Network TLS Endpoint Inventory

Discovers all TLS endpoints and cipher suites

# Scan from endpoint list
python shared/tls_endpoint_scanner.py \
  --endpoints-file endpoints.txt \
  --output inventory/tls-endpoints.json

# Auto-discover from Azure Load Balancers
python azure/lb_endpoint_discovery.py \
  --subscription-id YOUR_SUBSCRIPTION_ID \
  --scan-tls \
  --output inventory/azure-lb-tls.json

# Auto-discover from AWS ELB/ALB
python aws/elb_endpoint_discovery.py \
  --all-regions \
  --scan-tls \
  --output inventory/aws-elb-tls.json

Script 5: Database Encryption Keys

Inventories encryption-at-rest configurations for databases

# Azure SQL Database TDE
python azure/sql_encryption_inventory.py \
  --subscription-id YOUR_SUBSCRIPTION_ID \
  --output inventory/azure-sql-encryption.json

# AWS RDS Encryption
python aws/rds_encryption_inventory.py \
  --all-regions \
  --output inventory/aws-rds-encryption.json

# AWS DynamoDB Encryption
python aws/dynamodb_encryption_inventory.py \
  --all-regions \
  --output inventory/aws-dynamodb-encryption.json

Script 6: Storage Account Encryption

Checks encryption methods for cloud storage

# Azure Storage Accounts
python azure/storage_encryption_inventory.py \
  --subscription-id YOUR_SUBSCRIPTION_ID \
  --output inventory/azure-storage-encryption.json

# AWS S3 Bucket Encryption
python aws/s3_encryption_inventory.py \
  --output inventory/aws-s3-encryption.json

Script 7: Generate Consolidated Inventory Report

Combines all inventory scans into OMB M-23-02 compliant report

python shared/consolidate_inventory.py \
  --input-dir inventory/ \
  --output reports/crypto-inventory-master.json \
  --format json,csv,html

# This generates:
# - crypto-inventory-master.json (machine-readable)
# - crypto-inventory-master.csv (spreadsheet)
# - crypto-inventory-master.html (interactive report)

Complete Inventory Workflow

#!/bin/bash
# complete-inventory.sh - Build comprehensive crypto inventory

source venv/bin/activate
mkdir -p inventory reports

echo "=== Starting Cryptographic Inventory ==="

# Azure Inventory
if [ ! -z "$AZURE_SUBSCRIPTION_ID" ]; then
    echo "\\n1. Azure Key Vault inventory..."
    python azure/keyvault_inventory.py \
      --subscription-id $AZURE_SUBSCRIPTION_ID \
      --output inventory/azure-keyvault.json

    echo "2. Azure Certificate inventory..."
    python azure/certificate_inventory.py \
      --subscription-id $AZURE_SUBSCRIPTION_ID \
      --output inventory/azure-certificates.json

    echo "3. Azure SQL encryption..."
    python azure/sql_encryption_inventory.py \
      --subscription-id $AZURE_SUBSCRIPTION_ID \
      --output inventory/azure-sql.json

    echo "4. Azure Storage encryption..."
    python azure/storage_encryption_inventory.py \
      --subscription-id $AZURE_SUBSCRIPTION_ID \
      --output inventory/azure-storage.json
fi

# AWS Inventory
if aws sts get-caller-identity &> /dev/null; then
    echo "\\n5. AWS KMS inventory..."
    python aws/kms_inventory.py \
      --all-regions \
      --output inventory/aws-kms.json

    echo "6. AWS ACM certificates..."
    python aws/acm_inventory.py \
      --all-regions \
      --output inventory/aws-acm.json

    echo "7. AWS RDS encryption..."
    python aws/rds_encryption_inventory.py \
      --all-regions \
      --output inventory/aws-rds.json

    echo "8. AWS S3 encryption..."
    python aws/s3_encryption_inventory.py \
      --output inventory/aws-s3.json
fi

# Network Scanning
if [ -f "endpoints.txt" ]; then
    echo "\\n9. TLS endpoint scanning..."
    python shared/tls_endpoint_scanner.py \
      --endpoints-file endpoints.txt \
      --output inventory/tls-endpoints.json
fi

# Consolidate
echo "\\n10. Generating consolidated report..."
python shared/consolidate_inventory.py \
  --input-dir inventory/ \
  --output reports/crypto-inventory-master \
  --format json,csv,html

echo "\\n=== Inventory Complete ==="
echo "Reports available in: reports/"
echo "  - crypto-inventory-master.json"
echo "  - crypto-inventory-master.csv"
echo "  - crypto-inventory-master.html"
echo ""
echo "Summary:"
python shared/inventory_summary.py reports/crypto-inventory-master.json

Expected Output Format

JSON Inventory Structure

{
  "scan_date": "2024-11-06T14:30:00Z",
  "inventory_version": "1.0",
  "cloud_providers": ["azure", "aws"],

  "summary": {
    "total_assets": 1247,
    "quantum_vulnerable": 856,
    "by_type": {
      "keys": 342,
      "certificates": 189,
      "tls_endpoints": 284,
      "database_encryption": 41
    },
    "by_algorithm": {
      "RSA-2048": 412,
      "RSA-4096": 178,
      "ECDSA-P256": 156,
      "ECDSA-P384": 110
    }
  },

  "assets": [
    {
      "asset_id": "azure-kv-prod-app-signing-key",
      "asset_type": "key",
      "cloud_provider": "azure",
      "resource_id": "/subscriptions/.../keys/app-signing-key",
      "name": "app-signing-key",
      "algorithm": "RSA",
      "key_size": 2048,
      "created_date": "2021-03-15",
      "expiration_date": "2025-03-15",
      "quantum_vulnerable": true,
      "risk_level": "HIGH",
      "data_classification": "confidential",
      "usage": "code-signing",
      "recommended_migration": "ML-DSA-65 or hybrid RSA+ML-DSA",
      "priority": 1
    }
  ],

  "compliance": {
    "omb_m23_02": {
      "inventory_maintained": true,
      "last_updated": "2024-11-06",
      "migration_plan_exists": false
    },
    "cnsa_2_0": {
      "compliant_assets": 391,
      "non_compliant_assets": 856,
      "target_date": "2030-01-01"
    }
  }
}

CSV Export

Spreadsheet with columns:

Use Cases for Inventory:
  • Compliance: Provide to auditors for OMB M-23-02 validation
  • Risk Management: Identify high-priority migration targets
  • Budgeting: Estimate scope and resources for PQC transition
  • Tracking: Monitor progress of migration efforts over time

CI/CD Integration

Automate inventory updates as part of your deployment pipeline:

GitHub Actions

name: PQC Crypto Inventory Scan
on:
  schedule:
    - cron: '0 0 * * 0'  # Weekly on Sunday
  workflow_dispatch:

jobs:
  inventory:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt

      - name: Azure Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Configure AWS
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Run inventory scan
        run: |
          ./complete-inventory.sh

      - name: Upload results
        uses: actions/upload-artifact@v3
        with:
          name: crypto-inventory
          path: reports/

      - name: Check for new vulnerabilities
        run: |
          python shared/inventory_diff.py \
            --baseline inventory/baseline.json \
            --current reports/crypto-inventory-master.json \
            --fail-on-new-high-risk

Additional Resources

100% Free & Open Source Educational Resource

SpinDynamics.io - Making Quantum Security Accessible to All

← Return to Homepage