feat: Support certificate revocation
parent
a99d37d2e4
commit
5f80851d44
|
@ -98,7 +98,7 @@ respectively), and the system looks like this:
|
||||||
| | 172.50.88 | |
|
| | 172.50.88 | |
|
||||||
| +-----------+ |
|
| +-----------+ |
|
||||||
+----------------172.50.0.0/24----------------+
|
+----------------172.50.0.0/24----------------+
|
||||||
fd23:0d79:d076::/64
|
fd23:0d79:d076::/64
|
||||||
```
|
```
|
||||||
|
|
||||||
### Removing a node from the network
|
### Removing a node from the network
|
||||||
|
@ -144,7 +144,7 @@ Here's what the script creates:
|
||||||
* Intermediate CA certificate (signed by Root CA)
|
* Intermediate CA certificate (signed by Root CA)
|
||||||
* Two server certificates with OCSP information (one for each Openfire instance)
|
* Two server certificates with OCSP information (one for each Openfire instance)
|
||||||
* An OCSP responder certificate (for signing OCSP responses)
|
* An OCSP responder certificate (for signing OCSP responses)
|
||||||
* Full certificate chains for both servers (server + intermediate + root)
|
* Full certificate chains for each XMPP server (server + intermediate + root)
|
||||||
* Certificate database (index.txt) for the OCSP responder to track certificate statuses
|
* Certificate database (index.txt) for the OCSP responder to track certificate statuses
|
||||||
|
|
||||||
All certificates are stored in `./_data/certs/`.
|
All certificates are stored in `./_data/certs/`.
|
||||||
|
@ -174,14 +174,56 @@ All certificates are stored in `./_data/certs/`.
|
||||||
This setup allows certificates to be checked for revocation status making a request to the
|
This setup allows certificates to be checked for revocation status making a request to the
|
||||||
OCSP responder:
|
OCSP responder:
|
||||||
```bash
|
```bash
|
||||||
```bash
|
|
||||||
openssl ocsp -url http://localhost:8888 \
|
openssl ocsp -url http://localhost:8888 \
|
||||||
-issuer _data/certs/ca/intermediate-ca/intermediate.crt \
|
-issuer _data/certs/ca/intermediate-ca/intermediate.crt \
|
||||||
-CAfile _data/certs/chain1.pem \
|
-CAfile _data/certs/xmpp1_chain.pem \
|
||||||
-cert _data/certs/server1.crt \
|
-cert _data/certs/xmpp1.crt \
|
||||||
-text
|
-text
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Certificate Revocation
|
||||||
|
|
||||||
|
The `revocation.sh` script allows you to revoke SSL certificates and
|
||||||
|
update the OCSP responder's database. You can also un-revoke certificates
|
||||||
|
that were previously revoked.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./revocation.sh --server xmpp1 [--reason reason] [--unrevoke]
|
||||||
|
```
|
||||||
|
|
||||||
|
Available revocation reasons:
|
||||||
|
- unspecified (default)
|
||||||
|
- keyCompromise
|
||||||
|
- CACompromise
|
||||||
|
- affiliationChanged
|
||||||
|
- superseded
|
||||||
|
- cessationOfOperation
|
||||||
|
- certificateHold
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```bash
|
||||||
|
# Revoke xmpp1's certificate
|
||||||
|
./revocation.sh --server xmpp1
|
||||||
|
|
||||||
|
# Revoke with specific reason
|
||||||
|
./revocation.sh --server xmpp1 --reason keyCompromise
|
||||||
|
|
||||||
|
# Remove revocation status
|
||||||
|
./revocation.sh --server xmpp1 --unrevoke
|
||||||
|
```
|
||||||
|
|
||||||
|
To verify the current status:
|
||||||
|
```bash
|
||||||
|
openssl ocsp -url http://localhost:8888 \
|
||||||
|
-issuer _data/certs/ca/intermediate-ca/intermediate.crt \
|
||||||
|
-CAfile _data/certs/xmpp1_chain.pem \
|
||||||
|
-cert _data/certs/xmpp1.crt \
|
||||||
|
-text
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: The first OCSP status check may return the previous status. Run
|
||||||
|
the check again if this happens - subsequent checks will show the
|
||||||
|
current status.
|
||||||
|
|
||||||
## How it's built
|
## How it's built
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ services:
|
||||||
# ----------------------
|
# ----------------------
|
||||||
# openssl ocsp -url http://ocsp.localhost.example:8888 \
|
# openssl ocsp -url http://ocsp.localhost.example:8888 \
|
||||||
# -issuer _data/certs/ca/intermediate-ca/intermediate.crt \
|
# -issuer _data/certs/ca/intermediate-ca/intermediate.crt \
|
||||||
# -CAfile _data/certs/chain1.pem \
|
# -CAfile _data/certs/xmpp1.pem \
|
||||||
# -cert _data/certs/server1.crt \
|
# -cert _data/certs/xmpp1.crt \
|
||||||
# -text
|
# -text
|
||||||
ocsp-responder:
|
ocsp-responder:
|
||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
|
|
|
@ -9,6 +9,12 @@ OCSP_URL="http://ocsp.localhost.example:8888"
|
||||||
CERT_DIR="./_data/certs"
|
CERT_DIR="./_data/certs"
|
||||||
XMPP_BASE_DIR="./_data/xmpp"
|
XMPP_BASE_DIR="./_data/xmpp"
|
||||||
|
|
||||||
|
# Check if OpenSSL is installed
|
||||||
|
if ! command -v openssl &> /dev/null; then
|
||||||
|
echo "Error: OpenSSL is not installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Function to check which server directories exist
|
# Function to check which server directories exist
|
||||||
get_server_instances() {
|
get_server_instances() {
|
||||||
local instances=()
|
local instances=()
|
||||||
|
|
|
@ -9,6 +9,12 @@ KEYSTORE_PASSWORD="changeit"
|
||||||
CERT_DIR="./_data/certs"
|
CERT_DIR="./_data/certs"
|
||||||
XMPP_BASE_DIR="./_data/xmpp"
|
XMPP_BASE_DIR="./_data/xmpp"
|
||||||
|
|
||||||
|
# Check if OpenSSL is installed
|
||||||
|
if ! command -v openssl &> /dev/null; then
|
||||||
|
echo "Error: OpenSSL is not installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Function to check which server directories exist
|
# Function to check which server directories exist
|
||||||
get_server_instances() {
|
get_server_instances() {
|
||||||
local instances=()
|
local instances=()
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
CERT_DIR="./_data/certs"
|
||||||
|
INDEX_FILE="${CERT_DIR}/ca/intermediate-ca/index.txt"
|
||||||
|
CA_CERT="${CERT_DIR}/ca/intermediate-ca/intermediate.crt"
|
||||||
|
CA_KEY="${CERT_DIR}/ca/intermediate-ca/private/intermediate.key"
|
||||||
|
|
||||||
|
# Default revocation reason
|
||||||
|
REASON="unspecified"
|
||||||
|
FOUND_CERT=false
|
||||||
|
ACTION="revoke" # Default action
|
||||||
|
|
||||||
|
# Usage information
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [--server servername] [--cert certificate_file] [--reason reason_code] [--unrevoke]"
|
||||||
|
echo "Revokes or unrevokes an SSL certificate and updates the OCSP database"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " --server Server name (e.g., xmpp1)"
|
||||||
|
echo " --cert Path to the certificate to revoke (alternative to --server)"
|
||||||
|
echo " --reason Reason for revocation (optional):"
|
||||||
|
echo " unspecified (default)"
|
||||||
|
echo " keyCompromise"
|
||||||
|
echo " CACompromise"
|
||||||
|
echo " affiliationChanged"
|
||||||
|
echo " superseded"
|
||||||
|
echo " cessationOfOperation"
|
||||||
|
echo " certificateHold"
|
||||||
|
echo " --unrevoke Removes the revocation status of the certificate"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse command line options
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--server)
|
||||||
|
SERVER_NAME="$2"
|
||||||
|
if [[ ! "$SERVER_NAME" =~ ^xmpp[0-9]+$ ]]; then
|
||||||
|
echo "Error: Server name must be in format 'xmpp<number>' (e.g., xmpp1)"
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
CERTIFICATE="${CERT_DIR}/${SERVER_NAME}.crt"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--cert)
|
||||||
|
CERTIFICATE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--reason)
|
||||||
|
case "$2" in
|
||||||
|
unspecified|keyCompromise|CACompromise|affiliationChanged|superseded|cessationOfOperation|certificateHold)
|
||||||
|
REASON="$2"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Invalid reason. See usage for valid reasons."
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--unrevoke)
|
||||||
|
ACTION="unrevoke"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unknown option $1"
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Validate input and files
|
||||||
|
if [ -z "$CERTIFICATE" ]; then
|
||||||
|
echo "Error: Either --server or --cert must be specified"
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
for file in "$CERTIFICATE" "$INDEX_FILE" "$CA_CERT" "$CA_KEY"; do
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo "Error: Required file not found: $file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create backup
|
||||||
|
INDEX_BACKUP="${INDEX_FILE}.$(date +%Y%m%d%H%M%S).bak"
|
||||||
|
cp "$INDEX_FILE" "$INDEX_BACKUP"
|
||||||
|
|
||||||
|
# Get certificate serial number
|
||||||
|
SERIAL=$(openssl x509 -in "$CERTIFICATE" -noout -serial | cut -d'=' -f2)
|
||||||
|
|
||||||
|
# Create new index file
|
||||||
|
> "$INDEX_FILE.new"
|
||||||
|
|
||||||
|
# Read and process each line
|
||||||
|
while read -r line; do
|
||||||
|
# Split the line into fields
|
||||||
|
status=$(echo "$line" | cut -f1)
|
||||||
|
expiry=$(echo "$line" | cut -f2)
|
||||||
|
revocation=$(echo "$line" | cut -f3)
|
||||||
|
serial=$(echo "$line" | cut -f4)
|
||||||
|
filename=$(echo "$line" | cut -f5)
|
||||||
|
subject=$(echo "$line" | cut -f6)
|
||||||
|
|
||||||
|
if [ "$serial" = "$SERIAL" ]; then
|
||||||
|
if [ "$ACTION" = "revoke" ]; then
|
||||||
|
if [ "$status" = "R" ]; then
|
||||||
|
echo "Error: Certificate is already revoked"
|
||||||
|
rm "$INDEX_FILE.new"
|
||||||
|
rm "$INDEX_BACKUP"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Create revoked entry
|
||||||
|
REVOKE_DATE=$(date -u +%y%m%d%H%M%SZ)
|
||||||
|
printf 'R\t%s\t%s,%s\t%s\t%s\t%s\n' \
|
||||||
|
"$expiry" \
|
||||||
|
"$REVOKE_DATE" \
|
||||||
|
"$REASON" \
|
||||||
|
"$serial" \
|
||||||
|
"$filename" \
|
||||||
|
"$subject" >> "$INDEX_FILE.new"
|
||||||
|
else # unrevoke
|
||||||
|
if [ "$status" != "R" ]; then
|
||||||
|
echo "Error: Certificate is not revoked"
|
||||||
|
rm "$INDEX_FILE.new"
|
||||||
|
rm "$INDEX_BACKUP"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Convert back to valid entry
|
||||||
|
printf 'V\t%s\t\t%s\t%s\t%s\n' \
|
||||||
|
"$expiry" \
|
||||||
|
"$serial" \
|
||||||
|
"$filename" \
|
||||||
|
"$subject" >> "$INDEX_FILE.new"
|
||||||
|
fi
|
||||||
|
FOUND_CERT=true
|
||||||
|
else
|
||||||
|
# Not our certificate - keep original line
|
||||||
|
echo "$line" >> "$INDEX_FILE.new"
|
||||||
|
fi
|
||||||
|
done < "$INDEX_FILE"
|
||||||
|
|
||||||
|
if [ "$FOUND_CERT" = false ]; then
|
||||||
|
echo "Error: Certificate not found in database"
|
||||||
|
echo "Looking for serial: $SERIAL"
|
||||||
|
rm "$INDEX_FILE.new"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if mv "$INDEX_FILE.new" "$INDEX_FILE"; then
|
||||||
|
if [ "$ACTION" = "revoke" ]; then
|
||||||
|
echo "Certificate successfully revoked"
|
||||||
|
echo "Reason: $REASON"
|
||||||
|
else
|
||||||
|
echo "Certificate successfully unrevoked"
|
||||||
|
fi
|
||||||
|
echo -e "\nTo verify the current status:"
|
||||||
|
echo "openssl ocsp -url http://ocsp.localhost.example:8888 \\"
|
||||||
|
echo " -issuer ${CERT_DIR}/ca/intermediate-ca/intermediate.crt \\"
|
||||||
|
echo " -CAfile ${CERT_DIR}/$(basename "$CERTIFICATE" .crt)_chain.pem \\"
|
||||||
|
echo " -cert ${CERTIFICATE} \\"
|
||||||
|
echo " -text"
|
||||||
|
echo -e "\nNote: The first OCSP status check may return the previous status."
|
||||||
|
echo " Run the check again if this happens - subsequent checks will show the current status."
|
||||||
|
# Clean up backup file on success
|
||||||
|
rm "$INDEX_BACKUP"
|
||||||
|
else
|
||||||
|
echo "Error: Failed to ${ACTION} certificate"
|
||||||
|
echo "Restoring database backup..."
|
||||||
|
cp "$INDEX_BACKUP" "$INDEX_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
Loading…
Reference in New Issue