add /csr endpoint for CSR signing
Some checks failed
CI / Lint & Format (push) Failing after 16s
CI / Unit Tests (push) Has been skipped
CI / Memory Leak Check (push) Has been skipped
CI / SBOM Generation (push) Has been skipped
CI / Security Scan (push) Successful in 20s
CI / Security Tests (push) Has been skipped
CI / Advanced Security Tests (push) Has been skipped

Allow clients to submit Certificate Signing Requests instead of
having the server generate private keys. Client keeps key local.

- sign_csr() in pki.py validates and signs CSRs
- POST /csr endpoint with PoW protection
- 10 new tests for CSR functionality
- API documentation updated
This commit is contained in:
Username
2025-12-26 21:44:29 +01:00
parent 6da80aec76
commit 30bd02663b
4 changed files with 562 additions and 0 deletions

View File

@@ -1229,6 +1229,122 @@ curl -H "X-SSL-Client-SHA1: $(openssl x509 -in client.crt -fingerprint -sha1 -no
4. Server signs CSR and returns the certificate
5. Client combines their private key with the signed certificate
**Request (with PoW):**
```http
POST /csr HTTP/1.1
Host: localhost:5000
Content-Type: application/x-pem-file
X-PoW-Token: a1b2c3d4...:1700000300:24:signature
X-PoW-Solution: 12345678
-----BEGIN CERTIFICATE REQUEST-----
MIIBhDCB7gIBADBFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcM
...
-----END CERTIFICATE REQUEST-----
```
**Request (PoW disabled):**
```http
POST /csr HTTP/1.1
Host: localhost:5000
Content-Type: application/x-pem-file
-----BEGIN CERTIFICATE REQUEST-----
...
-----END CERTIFICATE REQUEST-----
```
**Response (200 OK):**
```http
HTTP/1.1 200 OK
Content-Type: application/x-pem-file
Content-Disposition: attachment; filename="alice.crt"
X-Fingerprint-SHA1: b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3
X-Certificate-Expires: 1731533400
X-Certificate-Serial: 00000000000000000000000000000002
X-Is-Admin: 0
-----BEGIN CERTIFICATE-----
MIICxDCCAaygAwIBAgIUY...
-----END CERTIFICATE-----
```
**Response Headers:**
| Header | Description |
|--------|-------------|
| `X-Fingerprint-SHA1` | SHA1 fingerprint for `X-SSL-Client-SHA1` header |
| `X-Certificate-Expires` | Unix timestamp when certificate expires |
| `X-Certificate-Serial` | Certificate serial number |
| `X-Is-Admin` | `1` if first user (admin), `0` otherwise |
**Errors:**
| Code | Description |
|------|-------------|
| 400 | CSR required (empty body) |
| 400 | Invalid CSR format |
| 400 | CSR must contain a Common Name (CN) |
| 400 | CSR signature is invalid |
| 400 | Proof-of-work required (when enabled) |
| 400 | Proof-of-work failed (invalid/expired challenge) |
| 503 | PKI_CA_PASSWORD not configured |
**Generating a CSR:**
```bash
# Generate key pair and CSR with OpenSSL
openssl ecparam -genkey -name secp384r1 -out client.key
openssl req -new -key client.key -out client.csr -subj "/CN=alice"
# Submit CSR to server
curl -X POST --data-binary @client.csr \
-H "Content-Type: application/x-pem-file" \
https://paste.example.com/csr > client.crt
# Verify the certificate
openssl x509 -in client.crt -noout -subject -issuer
```
**Creating a PKCS#12 bundle:**
```bash
# Combine key and certificate for browser/curl use
openssl pkcs12 -export -out client.p12 \
-inkey client.key -in client.crt \
-certfile ca.crt -passout pass:
# Use with curl
curl --cert client.crt --key client.key https://paste.example.com/
```
**Comparison with /register:**
| Feature | /register | /csr |
|---------|-----------|------|
| Private key | Generated server-side | Generated client-side |
| Response format | PKCS#12 bundle | PEM certificate only |
| Key security | Transmitted over network | Never leaves client |
| Complexity | Simple (one step) | More steps required |
**Notes:**
- Uses same PoW difficulty as `/register` (`FLASKPASTE_REGISTER_POW`)
- CA is auto-generated on first CSR if not present
- First user (via `/register` or `/csr`) becomes admin
- CSR must be signed (server validates signature)
- Common Name (CN) in CSR becomes certificate subject
---
## Audit Logging
FlaskPaste logs PKI certificate lifecycle events for compliance and forensics.
**Logged Events:**
| Event | Trigger | Details |
|-------|---------|---------|
| `cert_issued` | Certificate registration or issuance | Type, CN, fingerprint, expiry |
| `cert_revoked` | Certificate revocation | Serial, fingerprint |
| `auth_failure` | Revoked/expired certificate used | Fingerprint, reason |
**Log Format (production):**
```json