Sign inSign up

jtenorio/certificate-reader

By jtenorio

•Updated 11 months ago

Webtool to extract info X.509 certificates, including public keys, private keys, and JSON Web Keys

Image
Security
0

10K+

jtenorio/certificate-reader repository overview

⁠Certificate Reader - Docker Image

A secure web application for extracting information from X.509 certificates, including public keys, private keys, and JSON Web Keys (JWK). This tool is perfect for developers working with certificates and cryptographic operations.

⁠Features

  • šŸ“„ Extract Public Key - Get the certificate public key in Base64 format
  • šŸ”‘ Extract Private Key - Export the private RSA key in PEM format
  • šŸ” Generate JWK - Create JSON Web Key representation for JWT validation
  • ✨ Generate Certificates - Create new self-signed certificates with custom names and passwords
  • šŸš€ RESTful API - Full REST API support for programmatic access
  • šŸ”’ Security First - Rate limiting, CSP headers, and no certificate storage
  • 🌐 Reverse Proxy Ready - Full support for deployment behind reverse proxies (Nginx, Traefik, etc.)
  • ā¤ļø Health Checks - Built-in health endpoints for orchestrators

⁠Quick Start

⁠Basic Usage
docker run -d -p 8080:9080 jtenorio/certificate-reader:latest

Access the application at http://localhost:8080⁠

⁠With Environment Configuration
docker run -d \
  -p 8080:9080 \
  -e ASPNETCORE_ENVIRONMENT=Production \
  jtenorio/certificate-reader:latest

⁠Docker Compose Examples

⁠Example 1: Basic Deployment
version: '3.8'

services:
  certificate-reader:
    image: jtenorio/certificate-reader:latest
    container_name: certificate-reader
    ports:
      - "8080:9080"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:9080
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:9080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

This example shows how to deploy with Nginx handling SSL/TLS termination. The application correctly handles forwarded headers and doesn't enforce HTTPS redirection.

docker-compose.yml:

version: '3.8'

services:
  certificate-reader:
    image: jtenorio/certificate-reader:latest
    container_name: certificate-reader
    volumes:
      # Mount custom appsettings.json with SSL termination enabled
      - ./appsettings.Production.json:/app/appsettings.json:ro
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:9080
    networks:
    - cert-reader-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:9080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro  # Your SSL certificates
    networks:
      - cert-reader-network
    depends_on:
      - certificate-reader
    restart: unless-stopped

networks:
  cert-reader-network:
    driver: bridge

appsettings.Production.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "localhost;yourdomain.com;*.yourdomain.com",
  "ReverseProxy": {
    "EnableForwardedHeaders": true,
    "ForwardLimit": 1,
    "SslTerminatedAtProxy": true,
    "TrustForwardedProto": true,
    "EnableDebugLogging": false
  }
}

nginx.conf:

events {
    worker_connections 1024;
}

http {
    upstream certificate-reader {
     server certificate-reader:9080;
  }

    # HTTP - Redirect to HTTPS
    server {
        listen 80;
        server_name yourdomain.com;
        return 301 https://$server_name$request_uri;
    }

    # HTTPS - SSL Termination
    server {
    listen 443 ssl http2;
   server_name yourdomain.com;

        ssl_certificate /etc/nginx/ssl/cert.pem;
        ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
     ssl_prefer_server_ciphers on;

        # Security headers
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header X-Frame-Options "DENY" always;
      add_header X-Content-Type-Options "nosniff" always;

        location / {
          proxy_pass http://certificate-reader;
         
   # Essential forwarded headers for SSL termination
     proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Forwarded-Host $host;
          proxy_set_header X-Forwarded-Port $server_port;

          # WebSocket support (if needed)
        proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection "upgrade";

   # Timeouts
        proxy_connect_timeout 60s;
  proxy_send_timeout 60s;
       proxy_read_timeout 60s;
        }

        # Health check endpoint
 location /health {
  proxy_pass http://certificate-reader/health;
            access_log off;
        }
    }
}
⁠Example 3: Using Environment Variables for Configuration

Override settings using environment variables (useful for container orchestration):

version: '3.8'

services:
  certificate-reader:
    image: jtenorio/certificate-reader:latest
    container_name: certificate-reader
    ports:
      - "8080:9080"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:9080
  # Override appsettings.json using environment variables
      - ReverseProxy__EnableForwardedHeaders=true
      - ReverseProxy__SslTerminatedAtProxy=true
      - ReverseProxy__TrustForwardedProto=true
      - ReverseProxy__ForwardLimit=1
      - AllowedHosts=localhost;yourdomain.com;*.yourdomain.com
      - Logging__LogLevel__Default=Information
    restart: unless-stopped
⁠Example 4: Complete Stack with Nginx and Let's Encrypt

Using Certbot for automatic SSL certificate management:

version: '3.8'

services:
  certificate-reader:
    image: jtenorio/certificate-reader:latest
    container_name: certificate-reader
    volumes:
      - ./appsettings.Production.json:/app/appsettings.json:ro
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:9080
    networks:
      - cert-reader-network
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
  - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - ./dhparam:/etc/ssl/certs
    networks:
      - cert-reader-network
    depends_on:
    - certificate-reader
    restart: unless-stopped

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
   - ./webroot:/var/www/html
    depends_on:
      - nginx
    command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --force-renewal -d yourdomain.com

networks:
  cert-reader-network:
    driver: bridge

volumes:
  certbot-etc:
  certbot-var:
⁠Example 5: With Traefik (Alternative Reverse Proxy)
version: '3.8'

services:
  certificate-reader:
  image: jtenorio/certificate-reader:latest
    container_name: certificate-reader
    environment:
   - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:9080
   - ReverseProxy__EnableForwardedHeaders=true
      - ReverseProxy__SslTerminatedAtProxy=true
      - ReverseProxy__TrustForwardedProto=true
 labels:
      - "traefik.enable=true"
   - "traefik.http.routers.cert-reader.rule=Host(`yourdomain.com`)"
 - "traefik.http.routers.cert-reader.entrypoints=websecure"
      - "traefik.http.routers.cert-reader.tls=true"
      - "traefik.http.routers.cert-reader.tls.certresolver=letsencrypt"
      - "traefik.http.services.cert-reader.loadbalancer.server.port=9080"
    networks:
      - traefik
    restart: unless-stopped

networks:
  traefik:
    external: true
⁠Example 6: Development Environment (No SSL)

For local development without SSL:

version: '3.8'

services:
  certificate-reader:
    image: jtenorio/certificate-reader:latest
    container_name: certificate-reader-dev
    ports:
      - "8080:9080"
    environment:
  - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:9080
      - ReverseProxy__EnableForwardedHeaders=false
      - ReverseProxy__SslTerminatedAtProxy=false
    restart: unless-stopped

⁠Configuration Override Methods

Mount a custom appsettings.json file:

volumes:
  - ./appsettings.Production.json:/app/appsettings.json:ro
⁠Method 2: Environment Variables

Use environment variables with double underscore notation:

environment:
  - ReverseProxy__SslTerminatedAtProxy=true
  - ReverseProxy__EnableForwardedHeaders=true
  - ReverseProxy__TrustForwardedProto=true
  - AllowedHosts=yourdomain.com
⁠Method 3: Docker Secrets (Swarm/Production)
version: '3.8'

services:
  certificate-reader:
 image: jtenorio/certificate-reader:latest
    secrets:
      - appsettings
 environment:
      - ASPNETCORE_ENVIRONMENT=Production
    command: sh -c "cp /run/secrets/appsettings /app/appsettings.json && dotnet JT.CertificateReader.Web.dll"

secrets:
  appsettings:
  file: ./appsettings.Production.json

⁠Port Configuration

The container exposes the following ports:

  • 9080 - HTTP endpoint (default)
  • 9081 - HTTPS endpoint (if SSL is configured)

⁠Environment Variables

VariableDescriptionDefaultExample
ASPNETCORE_ENVIRONMENTApplication environmentProductionDevelopment, Staging, Production
ASPNETCORE_URLSURLs the app listens onhttp://+:9080http://+:9080;http://+:9081
Logging__LogLevel__DefaultDefault logging levelInformationDebug, Information, Warning
Logging__LogLevel__Microsoft.AspNetCoreASP.NET Core loggingWarningInformation, Warning, Error
ReverseProxy__EnableForwardedHeadersEnable forwarded headerstruetrue, false
ReverseProxy__SslTerminatedAtProxySSL terminated at proxyfalsetrue (set when behind Nginx with SSL)
ReverseProxy__TrustForwardedProtoTrust X-Forwarded-Prototruetrue, false
ReverseProxy__ForwardLimitMax proxy hops to trust11, 2, 3
AllowedHostsAllowed host headers*localhost;yourdomain.com

⁠Reverse Proxy Configuration

The application is designed to work seamlessly behind reverse proxies like Nginx, Traefik, or Apache.

⁠Configuration File (appsettings.json)

You can mount a custom appsettings.json to configure reverse proxy settings:

docker run -d \
  -p 8080:9080 \
  -v $(pwd)/appsettings.json:/app/appsettings.json:ro \
  jtenorio/certificate-reader:latest
⁠Example appsettings.json for Reverse Proxy
{
  "Logging": {
    "LogLevel": {
   "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
  }
  },
  "AllowedHosts": "localhost;yourdomain.com;*.yourdomain.com",
  "ReverseProxy": {
  "EnableForwardedHeaders": true,
    "ForwardLimit": 1,
    "KnownProxies": [],
    "KnownNetworks": [],
    "PreserveHostHeader": false,
    "AllowedProxyHosts": [],
    "EnableDebugLogging": false,
    "SslTerminatedAtProxy": true,
    "TrustForwardedProto": true
  }
}
⁠Nginx Example
server {
    listen 443 ssl http2;
  server_name yourdomain.com;

 ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://certificate-reader:9080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
    }
}
⁠Traefik Example
version: '3.8'

services:
  certificate-reader:
    image: jtenorio/certificate-reader:latest
 labels:
      - "traefik.enable=true"
      - "traefik.http.routers.cert-reader.rule=Host(`yourdomain.com`)"
 - "traefik.http.routers.cert-reader.entrypoints=websecure"
    - "traefik.http.routers.cert-reader.tls=true"
      - "traefik.http.routers.cert-reader.tls.certresolver=letsencrypt"
      - "traefik.http.services.cert-reader.loadbalancer.server.port=9080"
    networks:
      - traefik

networks:
  traefik:
    external: true

⁠API Endpoints

⁠Web Interface
  • GET / - Main upload and generation interface
  • GET /health - Health check endpoint
  • GET /ready - Kubernetes readiness probe endpoint
⁠REST API
⁠Generate Certificate
POST /api/CertificateApi/generate
Content-Type: application/json

{
  "certificateName": "MyTestCert",
  "password": "SecureP@ssw0rd!"
}
⁠Process Certificate
POST /api/CertificateApi/process
Content-Type: multipart/form-data

--boundary
Content-Disposition: form-data; name="certificate"; filename="cert.pfx"
Content-Type: application/x-pkcs12

[binary data]
--boundary
Content-Disposition: form-data; name="password"

YourPassword
--boundary--
⁠Export Public Key
POST /api/CertificateApi/export-public-key
Content-Type: multipart/form-data

[Similar to process endpoint]
⁠Export Private Key
POST /api/CertificateApi/export-private-key
Content-Type: multipart/form-data

[Similar to process endpoint]
⁠Export JWK
POST /api/CertificateApi/export-jwk
Content-Type: multipart/form-data

[Similar to process endpoint]

⁠Security Features

  • Rate Limiting - 10 requests per minute per IP
  • Security Headers - CSP, X-Frame-Options, X-Content-Type-Options, etc.
  • Input Validation - File size limit (10 MB), content type validation
  • No Data Storage - Certificates are never stored on disk or in memory beyond processing
  • HTTPS Support - Ready for SSL/TLS termination at proxy or direct HTTPS

⁠Health Checks

The application provides health check endpoints for load balancers and orchestrators:

# Basic health check
curl http://localhost:9080/health

# Kubernetes readiness probe
curl http://localhost:9080/ready
⁠Kubernetes Deployment Example
apiVersion: apps/v1
kind: Deployment
metadata:
  name: certificate-reader
spec:
  replicas: 2
  selector:
    matchLabels:
      app: certificate-reader
  template:
    metadata:
      labels:
        app: certificate-reader
    spec:
      containers:
      - name: certificate-reader
        image: jtenorio/certificate-reader:latest
        ports:
        - containerPort: 9080
        name: http
        livenessProbe:
          httpGet:
            path: /health
 port: 9080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
    httpGet:
   path: /ready
          port: 9080
        initialDelaySeconds: 10
      periodSeconds: 5
        resources:
    requests:
      memory: "128Mi"
   cpu: "100m"
     limits:
       memory: "256Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: certificate-reader
spec:
  selector:
    app: certificate-reader
  ports:
  - port: 80
    targetPort: 9080
  type: ClusterIP

⁠Supported Certificate Formats

  • PKCS#12 (.pfx, .p12) - With or without password
  • X.509 (.cer, .crt) - Public certificates
  • PEM (.pem) - Both public and private keys

⁠Use Cases

  1. Certificate Inspection - Quickly extract information from certificates
  2. JWT Setup - Generate JWK for JWT validation
  3. API Integration - Use REST API for automated certificate processing
  4. Development Testing - Generate self-signed certificates for development
  5. Key Extraction - Export private/public keys for various cryptographic operations

⁠Volume Mounts

⁠Custom Configuration
docker run -d \
  -v $(pwd)/appsettings.json:/app/appsettings.json:ro \
  jtenorio/certificate-reader:latest
⁠Logs (if needed)
docker run -d \
  -v $(pwd)/logs:/app/logs \
  jtenorio/certificate-reader:latest

⁠Troubleshooting

⁠Container won't start
# Check logs
docker logs certificate-reader

# Verify port availability
netstat -an | grep 9080
⁠Cannot access the application
# Test from inside the container
docker exec certificate-reader wget -O- http://localhost:9080/health

# Check firewall rules
# Verify port mapping
docker ps
⁠Certificate processing fails
  • Verify the certificate format is supported
  • Check if password is correct for encrypted certificates
  • Ensure file size is under 10 MB

⁠Building from Source

# Clone the repository
git clone https://github.com/jtenoriodseldon/certificatereader.git

# Build the Docker image
docker build -t certificate-reader:latest -f JT.CertificateReader.Web/Dockerfile .

# Note: Requires GitHub Personal Access Token for private NuGet package
docker build --build-arg GITHUB_TOKEN=your_token -t certificate-reader:latest -f JT.CertificateReader.Web/Dockerfile .

⁠Technology Stack

  • .NET 9 - Latest ASP.NET Core framework
  • Alpine Linux - Minimal base image for security and size
  • Razor Pages - Modern web UI
  • REST API - Programmatic access

⁠Support & Issues

⁠License

See the repository for license information.

⁠Privacy & Security Notice

āš ļø Important: This application does NOT store any certificates. All processing is done in-memory and certificates are discarded immediately after processing. However, when deploying publicly, ensure you:

  1. Use HTTPS (SSL/TLS)
  2. Keep the application behind a firewall
  3. Monitor access logs
  4. Apply rate limiting
  5. Use strong security headers (already configured)

⁠Tags

  • latest - Latest stable version
  • .NET, ASP.NET Core, Certificate, X.509, JWK, Cryptography, Security, Alpine Linux

Made with ā¤ļø by Darth Seldon

Tag summary

Content type

Image

Digest

sha256:6c40227e7…

Size

55.7 MB

Last updated

11 months ago

docker pull jtenorio/certificate-reader