Merge pull request #570 from julek-wolfssl/ocsp-responder-examples

Add OCSP responder examples
pull/565/head
David Garske 2026-04-13 09:51:11 -07:00 committed by GitHub
commit 44e31acbc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1273 additions and 0 deletions

2
.gitignore vendored
View File

@ -313,6 +313,8 @@ hash/sha512-hash
ocsp/ocsp_nonblock/ocsp_nonblock
ocsp/stapling/ocsp-client
ocsp/stapling/ocsp-server
ocsp/responder/ocsp-request-response
ocsp/responder/ocsp-responder-http
sslkeylog.log

View File

@ -0,0 +1,24 @@
# Makefile for OCSP Responder Examples
#
# wolfSSL must be built with:
# ./configure --enable-ocsp --enable-ocsp-responder
# make && sudo make install
CC = gcc
WOLFSSL_INSTALL_DIR = /usr/local
CFLAGS = -Wall -Wextra -g -I$(WOLFSSL_INSTALL_DIR)/include
LIBS = -L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl
# Uncomment for static linking:
# LIBS = $(WOLFSSL_INSTALL_DIR)/lib/libwolfssl.a -lm -lpthread
SRC = $(wildcard *.c)
TARGETS = $(patsubst %.c, %, $(SRC))
all: $(TARGETS)
%: %.c
$(CC) -o $@ $< $(CFLAGS) $(LIBS)
clean:
rm -f $(TARGETS)

View File

@ -0,0 +1,109 @@
# OCSP Responder Examples
Examples demonstrating the wolfSSL OCSP Responder API added in
[wolfSSL/wolfssl#9761](https://github.com/wolfSSL/wolfssl/pull/9761).
## Prerequisites
Build and install wolfSSL with OCSP responder support:
```sh
cd wolfssl
./configure --enable-ocsp --enable-ocsp-responder
make
sudo make install
sudo ldconfig
```
## Examples
### 1. Raw DER Request/Response (`ocsp-request-response.c`)
Demonstrates the core API without any networking:
- Parse a certificate and build a DER-encoded OCSP request
(`wc_InitOcspRequest`, `wc_EncodeOcspRequest`)
- Create an `OcspResponder`, register a signer, and set certificate statuses
(`wc_OcspResponder_new`, `wc_OcspResponder_AddSigner`,
`wc_OcspResponder_SetCertStatus`)
- Generate a signed OCSP response from the request
(`wc_OcspResponder_WriteResponse`)
- Verify the response against a `WOLFSSL_CERT_MANAGER`
(`wc_CheckCertOcspResponse`)
- Show REVOKED status and error response generation
```sh
make ocsp-request-response
./ocsp-request-response
```
Uses the wolfSSL test certs in `../../certs/` by default.
### 2. Minimal HTTP Responder (`ocsp-responder-http.c`)
A tiny HTTP server that accepts POST requests containing DER OCSP requests and
returns DER OCSP responses. Kept as small as possible.
```sh
make ocsp-responder-http
# Start the responder, marking the server cert as GOOD
./ocsp-responder-http 8080 ../../certs/ca-cert.pem ../../certs/ca-key.pem \
../../certs/server-cert.pem
# Test with OpenSSL (in another terminal)
openssl ocsp -issuer ../../certs/ca-cert.pem -cert ../../certs/server-cert.pem \
-url http://127.0.0.1:8080/ -no_nonce
```
Any certificate files listed after the CA key have their serial numbers
registered as CERT_GOOD. Certificates not registered will get CERT_UNKNOWN.
### 3. nginx + wolfclu SCGI (`nginx-scgi/`)
Production-style deployment: nginx handles HTTP and forwards raw OCSP request
bodies to wolfclu over SCGI. nginx provides TLS termination, access control,
logging, and load balancing while wolfclu focuses on OCSP processing.
Requirements:
- [wolfCLU](https://github.com/wolfSSL/wolfCLU) built and installed
- nginx with SCGI support (enabled by default)
```
+---------+ HTTP POST +-------+ SCGI +---------+
| Client |------------>| nginx |-------->| wolfclu |
|(openssl) |<------------| :8080 |<--------| :8081 |
+---------+ OCSP resp +-------+ +---------+
```
Quick start:
```sh
cd nginx-scgi
./run.sh
```
Or run manually:
```sh
# Terminal 1: Start wolfclu SCGI backend
wolfssl ocsp -scgi -port 8081 \
-rsigner ../../certs/ca-cert.pem \
-rkey ../../certs/ca-key.pem \
-CA ../../certs/ca-cert.pem
# Terminal 2: Start nginx
nginx -c $(pwd)/nginx-scgi/nginx-ocsp.conf
# Terminal 3: Test
openssl ocsp -issuer ../../certs/ca-cert.pem -cert ../../certs/server-cert.pem \
-url http://127.0.0.1:8080/ -no_nonce
```
The `nginx-ocsp.conf` file can be customized for your environment. See the
comments in the file for standalone vs. installed nginx usage.
## Shared Code
`ocsp-load-certs.h` contains file loading utilities (`LoadFile`, `LoadCertDer`,
`LoadKeyDer`) shared between the C examples.

View File

@ -0,0 +1,47 @@
# nginx configuration for proxying OCSP requests to wolfclu via SCGI.
#
# Install: copy this to /etc/nginx/sites-enabled/ or include it from
# nginx.conf, then reload nginx. Alternatively run a standalone nginx:
#
# nginx -c $(pwd)/nginx-ocsp.conf -p $(pwd)
#
# Adjust paths/ports below as needed.
# Run as a foreground process for testing (comment out for daemon mode).
daemon off;
# Uncomment and set if running without root:
# pid /tmp/nginx-ocsp.pid;
events {
worker_connections 64;
}
http {
# Where nginx writes temp files when running standalone.
client_body_temp_path /tmp/nginx-ocsp-body;
proxy_temp_path /tmp/nginx-ocsp-proxy;
fastcgi_temp_path /tmp/nginx-ocsp-fastcgi;
uwsgi_temp_path /tmp/nginx-ocsp-uwsgi;
scgi_temp_path /tmp/nginx-ocsp-scgi;
access_log /dev/stdout;
error_log /dev/stderr info;
server {
listen 8080;
location / {
# Forward OCSP requests to wolfclu running in SCGI mode.
scgi_pass 127.0.0.1:8081;
# Standard SCGI parameter: tell the backend the content length.
include scgi_params;
# Ensure the OCSP request body is forwarded.
scgi_param CONTENT_LENGTH $content_length;
scgi_param CONTENT_TYPE $content_type;
scgi_param REQUEST_METHOD $request_method;
scgi_param REQUEST_URI $request_uri;
}
}
}

View File

@ -0,0 +1,183 @@
#!/bin/sh
#
# run.sh - Start an OCSP responder using wolfclu (SCGI) behind nginx.
#
# This script:
# 1. Starts wolfclu in SCGI mode on port 8081
# 2. Starts nginx on port 8080, forwarding to wolfclu via SCGI
# 3. Sends a test OCSP query using wolfssl's built-in test certs
#
# Prerequisites:
# - wolfssl built with: --enable-ocsp --enable-ocsp-responder
# - wolfclu built and installed
# - nginx installed with SCGI support (default in most packages)
#
# Usage:
# ./run.sh [options]
#
# Options:
# --ca-cert <file> CA certificate (default: wolfSSL test ca-cert.pem)
# --ca-key <file> CA private key (default: wolfSSL test ca-key.pem)
# --index <file> OpenSSL-format index.txt (optional)
# --port <num> nginx listen port (default: 8080)
# --scgi-port <num> wolfclu SCGI port (default: 8081)
set -e
# Defaults - use wolfSSL test certificates
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CA_CERT="${SCRIPT_DIR}/../../../certs/ca-cert.pem"
CA_KEY="${SCRIPT_DIR}/../../../certs/ca-key.pem"
INDEX_FILE=""
HTTP_PORT=8080
SCGI_PORT=8081
WOLFCLU_PID=""
NGINX_PID=""
cleanup() {
echo ""
echo "Shutting down..."
[ -n "$WOLFCLU_PID" ] && kill "$WOLFCLU_PID" 2>/dev/null || true
[ -n "$NGINX_PID" ] && kill "$NGINX_PID" 2>/dev/null || true
wait 2>/dev/null || true
[ -n "$WORK_DIR" ] && rm -rf "$WORK_DIR"
echo "Done."
}
trap cleanup EXIT INT TERM
# Parse arguments
while [ $# -gt 0 ]; do
case "$1" in
--ca-cert) CA_CERT="$2"; shift 2 ;;
--ca-key) CA_KEY="$2"; shift 2 ;;
--index) INDEX_FILE="$2"; shift 2 ;;
--port) HTTP_PORT="$2"; shift 2 ;;
--scgi-port) SCGI_PORT="$2"; shift 2 ;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Validate files exist
if [ ! -f "$CA_CERT" ]; then
echo "Error: CA cert not found: $CA_CERT"
exit 1
fi
if [ ! -f "$CA_KEY" ]; then
echo "Error: CA key not found: $CA_KEY"
exit 1
fi
# Check for required tools
if ! command -v wolfssl >/dev/null 2>&1; then
echo "Error: 'wolfssl' (wolfCLU) not found in PATH"
echo "Build wolfCLU from https://github.com/wolfSSL/wolfCLU"
exit 1
fi
if ! command -v nginx >/dev/null 2>&1; then
echo "Error: nginx not found in PATH"
exit 1
fi
echo "=== OCSP Responder: nginx + wolfclu (SCGI) ==="
echo ""
echo "CA cert: $CA_CERT"
echo "CA key: $CA_KEY"
echo "HTTP port: $HTTP_PORT (nginx)"
echo "SCGI port: $SCGI_PORT (wolfclu)"
echo ""
# --- Step 1: Start wolfclu OCSP responder in SCGI mode ---
echo "Starting wolfclu OCSP responder (SCGI on port $SCGI_PORT)..."
set -- -scgi -port "$SCGI_PORT" -rsigner "$CA_CERT" -rkey "$CA_KEY" -CA "$CA_CERT"
if [ -n "$INDEX_FILE" ]; then
set -- "$@" -index "$INDEX_FILE"
fi
wolfssl ocsp "$@" &
WOLFCLU_PID=$!
sleep 1
if ! kill -0 "$WOLFCLU_PID" 2>/dev/null; then
echo "Error: wolfclu failed to start"
exit 1
fi
echo "wolfclu started (PID $WOLFCLU_PID)"
# --- Step 2: Generate nginx config with correct ports ---
WORK_DIR="$(mktemp -d "$SCRIPT_DIR/tmp.XXXXXX")"
NGINX_CONF="$WORK_DIR/nginx-ocsp.conf"
cat > "$NGINX_CONF" <<EOF
daemon off;
pid $WORK_DIR/nginx-ocsp.pid;
error_log /dev/stderr info;
events {
worker_connections 64;
}
http {
client_body_temp_path $WORK_DIR/body;
proxy_temp_path $WORK_DIR/proxy;
fastcgi_temp_path $WORK_DIR/fastcgi;
uwsgi_temp_path $WORK_DIR/uwsgi;
scgi_temp_path $WORK_DIR/scgi;
access_log /dev/stdout;
server {
listen $HTTP_PORT;
location / {
scgi_pass 127.0.0.1:$SCGI_PORT;
scgi_param REQUEST_METHOD \$request_method;
scgi_param REQUEST_URI \$request_uri;
scgi_param QUERY_STRING \$query_string;
scgi_param CONTENT_TYPE \$content_type;
scgi_param CONTENT_LENGTH \$content_length;
scgi_param DOCUMENT_URI \$document_uri;
scgi_param DOCUMENT_ROOT \$document_root;
scgi_param SCGI 1;
scgi_param SERVER_PROTOCOL \$server_protocol;
scgi_param REQUEST_SCHEME \$scheme;
scgi_param HTTPS \$https if_not_empty;
scgi_param REMOTE_ADDR \$remote_addr;
scgi_param REMOTE_PORT \$remote_port;
scgi_param SERVER_PORT \$server_port;
scgi_param SERVER_NAME \$server_name;
}
}
}
EOF
echo "Starting nginx (HTTP on port $HTTP_PORT)..."
nginx -c "$NGINX_CONF" &
NGINX_PID=$!
sleep 1
if ! kill -0 "$NGINX_PID" 2>/dev/null; then
echo "Error: nginx failed to start"
exit 1
fi
echo "nginx started (PID $NGINX_PID)"
echo ""
echo "=== OCSP responder is running ==="
echo ""
echo "Test with wolfssl:"
echo " wolfssl ocsp -issuer $CA_CERT -cert ../../certs/server-cert.pem \\"
echo " -url http://127.0.0.1:$HTTP_PORT/"
echo ""
echo "Test with openssl:"
echo " openssl ocsp -issuer $CA_CERT -cert ../../certs/server-cert.pem \\"
echo " -url http://127.0.0.1:$HTTP_PORT/ -resp_text"
echo ""
echo "Press Ctrl-C to stop."
echo ""
# Wait for either process to exit
wait

View File

@ -0,0 +1,87 @@
/* ocsp-load-certs.h
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Shared file/cert loading helpers for the OCSP responder examples. */
#ifndef OCSP_LOAD_CERTS_H
#define OCSP_LOAD_CERTS_H
#include <wolfssl/wolfcrypt/wc_port.h> /* WC_MAYBE_UNUSED */
#include <wolfssl/wolfcrypt/asn.h> /* wc_CertPemToDer, wc_KeyPemToDer */
#include <stdio.h>
#include <stdlib.h>
/* Load file into malloc'd buffer. Caller must free(*buf). */
static WC_MAYBE_UNUSED byte* LoadFile(const char* path, int* outSz)
{
FILE* f;
long sz;
byte* buf;
f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
sz = ftell(f);
fseek(f, 0, SEEK_SET);
if (sz <= 0) { fclose(f); return NULL; }
buf = (byte*)malloc((size_t)sz);
if (!buf) { fclose(f); return NULL; }
*outSz = (int)fread(buf, 1, (size_t)sz, f);
fclose(f);
if (*outSz != (int)sz) { free(buf); return NULL; }
return buf;
}
/* Load PEM certificate, convert to DER. Caller must free() result. */
static WC_MAYBE_UNUSED byte* LoadCertDer(const char* path, int* derSz)
{
byte *pem, *der;
int pemSz = 0, ret;
pem = LoadFile(path, &pemSz);
if (!pem) return NULL;
der = (byte*)malloc((size_t)pemSz);
if (!der) { free(pem); return NULL; }
ret = wc_CertPemToDer(pem, pemSz, der, pemSz, CERT_TYPE);
free(pem);
if (ret <= 0) { free(der); return NULL; }
*derSz = ret;
return der;
}
/* Load PEM private key, convert to DER. Caller must free() result. */
static WC_MAYBE_UNUSED byte* LoadKeyDer(const char* path, int* derSz)
{
byte *pem, *der;
int pemSz = 0, ret;
pem = LoadFile(path, &pemSz);
if (!pem) return NULL;
der = (byte*)malloc((size_t)pemSz);
if (!der) { free(pem); return NULL; }
ret = wc_KeyPemToDer(pem, pemSz, der, pemSz, NULL);
free(pem);
if (ret <= 0) { free(der); return NULL; }
*derSz = ret;
return der;
}
#endif /* OCSP_LOAD_CERTS_H */

View File

@ -0,0 +1,422 @@
/* ocsp-request-response.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Example: Generate and verify raw DER OCSP requests and responses using the
* wolfSSL OCSP Responder API. No networking - pure encode/decode in memory. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/ssl.h>
#include <wolfssl/ocsp.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#if defined(HAVE_OCSP) && defined(HAVE_OCSP_RESPONDER) && \
!defined(NO_FILESYSTEM)
#include "ocsp-load-certs.h"
/* Default certificate paths - these are the wolfSSL test certs */
#ifndef CA_CERT
#define CA_CERT "../../certs/ca-cert.pem"
#endif
#ifndef CA_KEY
#define CA_KEY "../../certs/ca-key.pem"
#endif
#ifndef SERVER_CERT
#define SERVER_CERT "../../certs/server-cert.pem"
#endif
#define MAX_DER_SZ 4096
#define MAX_RESP_SZ 4096
static void HexDump(const char* label, const unsigned char* data, int sz)
{
int i;
printf("%s (%d bytes):\n ", label, sz);
for (i = 0; i < sz && i < 64; i++) {
printf("%02x", data[i]);
if ((i + 1) % 32 == 0)
printf("\n ");
}
if (sz > 64)
printf("...");
printf("\n");
}
int main(int argc, char** argv)
{
int ret;
/* Certificate buffers */
byte* caCertDer = NULL;
int caCertDerSz = 0;
byte* caKeyDer = NULL;
int caKeyDerSz = 0;
byte* serverCertDer = NULL;
int serverCertDerSz = 0;
/* DecodedCert for the server cert (to build the OCSP request) */
DecodedCert serverCert;
int serverCertInit = 0;
/* DecodedCert for the CA cert (to get its subject for SetCertStatus) */
DecodedCert caCert;
int caCertInit = 0;
char caSubject[256] = {0};
word32 caSubjectSz = sizeof(caSubject);
/* OCSP request */
OcspRequest* req = NULL;
unsigned char reqDer[MAX_DER_SZ];
int reqDerSz;
/* OCSP responder */
OcspResponder* responder = NULL;
/* OCSP response */
unsigned char respDer[MAX_RESP_SZ];
word32 respDerSz;
/* For verification */
WOLFSSL_CERT_MANAGER* cm = NULL;
WOLFSSL_OCSP* ocsp = NULL;
const char* caCertFile = CA_CERT;
const char* caKeyFile = CA_KEY;
const char* serverCertFile = SERVER_CERT;
(void)argc;
(void)argv;
printf("=== OCSP Request/Response Example ===\n\n");
ret = wolfSSL_Init();
if (ret != WOLFSSL_SUCCESS) {
printf("wolfSSL_Init failed\n");
return 1;
}
/* --- Step 1: Load certificates --- */
printf("Step 1: Loading certificates...\n");
caCertDer = LoadCertDer(caCertFile, &caCertDerSz);
if (!caCertDer) {
printf("Error loading CA cert: %s\n", caCertFile);
ret = -1;
goto cleanup;
}
printf(" CA cert: %d bytes DER\n", caCertDerSz);
caKeyDer = LoadKeyDer(caKeyFile, &caKeyDerSz);
if (!caKeyDer) {
printf("Error loading CA key: %s\n", caKeyFile);
ret = -1;
goto cleanup;
}
printf(" CA key: %d bytes DER\n", caKeyDerSz);
serverCertDer = LoadCertDer(serverCertFile, &serverCertDerSz);
if (!serverCertDer) {
printf("Error loading server cert: %s\n", serverCertFile);
ret = -1;
goto cleanup;
}
printf(" Server cert: %d bytes DER\n\n", serverCertDerSz);
/* --- Step 2: Parse the server cert and build an OCSP request --- */
printf("Step 2: Building OCSP request from server certificate...\n");
/* Parse with the CA loaded so that issuerKeyHash gets populated.
* The issuerKeyHash is derived from the CA's public key and is needed
* for the OCSP request to match the responder's signer. */
{
WOLFSSL_CERT_MANAGER* tmpCm = wolfSSL_CertManagerNew();
if (tmpCm == NULL) {
printf("Error creating temp CertManager\n");
ret = -1;
goto cleanup;
}
ret = wolfSSL_CertManagerLoadCABuffer(tmpCm, caCertDer,
caCertDerSz, SSL_FILETYPE_ASN1);
if (ret != WOLFSSL_SUCCESS) {
printf("Error loading CA into temp CertManager: %d\n", ret);
wolfSSL_CertManagerFree(tmpCm);
goto cleanup;
}
ret = 0; /* Reset from WOLFSSL_SUCCESS (1) to 0 for error paths */
wc_InitDecodedCert(&serverCert, serverCertDer,
(word32)serverCertDerSz, NULL);
serverCertInit = 1;
ret = wc_ParseCert(&serverCert, CERT_TYPE, 1, tmpCm);
wolfSSL_CertManagerFree(tmpCm);
}
if (ret != 0) {
printf("Error parsing/verifying server cert: %d\n", ret);
goto cleanup;
}
/* Allocate and initialize the OCSP request from the parsed cert.
* wc_InitOcspRequest fills in issuerHash, issuerKeyHash, and serial
* from the DecodedCert automatically. */
req = wc_OcspRequest_new(NULL);
if (req == NULL) {
printf("Error allocating OCSP request\n");
ret = -1;
goto cleanup;
}
ret = wc_InitOcspRequest(req, &serverCert, 0, NULL);
if (ret != 0) {
printf("Error initializing OCSP request: %d\n", ret);
goto cleanup;
}
/* Encode the OCSP request into DER format */
reqDerSz = wc_EncodeOcspRequest(req, reqDer, sizeof(reqDer));
if (reqDerSz <= 0) {
printf("Error encoding OCSP request: %d\n", reqDerSz);
ret = reqDerSz;
goto cleanup;
}
HexDump(" OCSP Request DER", reqDer, reqDerSz);
printf("\n");
/* --- Step 3: Set up the OCSP responder --- */
printf("Step 3: Setting up OCSP responder...\n");
/* Parse the CA cert to get its subject name */
wc_InitDecodedCert(&caCert, caCertDer, (word32)caCertDerSz, NULL);
caCertInit = 1;
ret = wc_ParseCert(&caCert, CERT_TYPE, 0, NULL);
if (ret != 0) {
printf("Error parsing CA cert: %d\n", ret);
goto cleanup;
}
ret = wc_GetDecodedCertSubject(&caCert, caSubject, &caSubjectSz);
if (ret != 0) {
printf("Error getting CA subject: %d\n", ret);
goto cleanup;
}
printf(" CA Subject: %s\n", caSubject);
/* Create the responder. The second argument controls whether the CA cert
* is included in responses (1=yes). */
responder = wc_OcspResponder_new(NULL, 1);
if (responder == NULL) {
printf("Error creating OCSP responder\n");
ret = -1;
goto cleanup;
}
/* Register the CA cert and key as a signer. When the CA signs directly
* (not using a delegated responder cert), pass NULL/0 for the last two
* parameters. */
ret = wc_OcspResponder_AddSigner(responder,
caCertDer, (word32)caCertDerSz,
caKeyDer, (word32)caKeyDerSz,
NULL, 0);
if (ret != 0) {
printf("Error adding signer: %d\n", ret);
goto cleanup;
}
printf(" Signer added (CA as direct signer)\n");
/* Set the status for the server certificate's serial number.
* In production, you would populate this from a database or CRL.
* Here we mark it as CERT_GOOD with 24-hour validity. */
{
byte serial[32];
word32 serialSz = sizeof(serial);
ret = wc_GetDecodedCertSerial(&serverCert, serial, &serialSz);
if (ret != 0) {
printf("Error getting server cert serial: %d\n", ret);
goto cleanup;
}
HexDump(" Server cert serial", serial, (int)serialSz);
ret = wc_OcspResponder_SetCertStatus(responder,
caSubject, caSubjectSz,
serial, serialSz,
CERT_GOOD,
0, /* revocationTime (unused) */
WC_CRL_REASON_UNSPECIFIED,
86400 /* validityPeriod secs */);
if (ret != 0) {
printf("Error setting cert status: %d\n", ret);
goto cleanup;
}
printf(" Certificate status set to GOOD (24h validity)\n\n");
}
/* --- Step 4: Generate OCSP response --- */
printf("Step 4: Generating OCSP response...\n");
respDerSz = sizeof(respDer);
ret = wc_OcspResponder_WriteResponse(responder,
reqDer, (word32)reqDerSz,
respDer, &respDerSz);
if (ret != 0) {
printf("Error generating OCSP response: %d\n", ret);
goto cleanup;
}
HexDump(" OCSP Response DER", respDer, (int)respDerSz);
printf("\n");
/* --- Step 5: Verify the OCSP response --- */
printf("Step 5: Verifying OCSP response...\n");
cm = wolfSSL_CertManagerNew();
if (cm == NULL) {
printf("Error creating CertManager\n");
ret = -1;
goto cleanup;
}
ret = wolfSSL_CertManagerLoadCABuffer(cm, caCertDer, caCertDerSz,
SSL_FILETYPE_ASN1);
if (ret != WOLFSSL_SUCCESS) {
printf("Error loading CA into CertManager: %d\n", ret);
goto cleanup;
}
ret = 0; /* Reset from WOLFSSL_SUCCESS (1) to 0 for error paths */
ocsp = wc_NewOCSP(cm);
if (ocsp == NULL) {
printf("Error creating OCSP verifier\n");
ret = -1;
goto cleanup;
}
ret = wc_CheckCertOcspResponse(ocsp, &serverCert,
respDer, (int)respDerSz, NULL);
if (ret == 0) {
printf(" OCSP response verification: SUCCESS\n");
}
else {
printf(" OCSP response verification FAILED: %d\n", ret);
}
/* --- Step 6: Demonstrate a REVOKED status --- */
printf("\nStep 6: Demonstrating REVOKED status...\n");
{
byte serial[32];
word32 serialSz = sizeof(serial);
ret = wc_GetDecodedCertSerial(&serverCert, serial, &serialSz);
if (ret != 0) {
printf("Error getting server cert serial: %d\n", ret);
goto cleanup;
}
/* Update the status to REVOKED */
ret = wc_OcspResponder_SetCertStatus(responder,
caSubject, caSubjectSz,
serial, serialSz,
CERT_REVOKED,
time(NULL), /* revocationTime */
WC_CRL_REASON_KEY_COMPROMISE,
0);
if (ret != 0) {
printf("Error setting revoked status: %d\n", ret);
goto cleanup;
}
respDerSz = sizeof(respDer);
ret = wc_OcspResponder_WriteResponse(responder,
reqDer, (word32)reqDerSz,
respDer, &respDerSz);
if (ret != 0) {
printf("Error generating revoked response: %d\n", ret);
goto cleanup;
}
HexDump(" REVOKED OCSP Response DER", respDer, (int)respDerSz);
/* Verify - should indicate revoked */
ret = wc_CheckCertOcspResponse(ocsp, &serverCert,
respDer, (int)respDerSz, NULL);
printf(" Verification result: %d (non-zero = revoked/failed)\n", ret);
}
/* --- Step 7: Demonstrate error response --- */
printf("\nStep 7: Generating OCSP error response (unauthorized)...\n");
{
respDerSz = sizeof(respDer);
ret = wc_OcspResponder_WriteErrorResponse(OCSP_UNAUTHORIZED,
respDer, &respDerSz);
if (ret != 0) {
printf("Error encoding error response: %d\n", ret);
goto cleanup;
}
HexDump(" Error Response DER", respDer, (int)respDerSz);
}
printf("\n=== Example complete ===\n");
ret = 0;
cleanup:
if (ocsp)
wc_FreeOCSP(ocsp);
if (cm)
wolfSSL_CertManagerFree(cm);
if (responder)
wc_OcspResponder_free(responder);
if (req)
wc_OcspRequest_free(req);
if (serverCertInit)
wc_FreeDecodedCert(&serverCert);
if (caCertInit)
wc_FreeDecodedCert(&caCert);
free(caCertDer);
free(caKeyDer);
free(serverCertDer);
wolfSSL_Cleanup();
return ret;
}
#else
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
printf("This example requires --enable-ocsp --enable-ocsp-responder\n");
return 0;
}
#endif /* HAVE_OCSP && HAVE_OCSP_RESPONDER && !NO_FILESYSTEM */

View File

@ -0,0 +1,399 @@
/* ocsp-responder-http.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Minimal HTTP OCSP responder. Accepts POST requests with DER-encoded OCSP
* requests and returns DER-encoded OCSP responses. Intentionally kept as
* small as possible. Not production-hardened.
*
* Usage:
* ./ocsp-responder-http <port> <ca-cert> <ca-key> [cert-to-mark-good ...]
*
* Any certificates listed after the CA key will have their serial numbers
* registered as CERT_GOOD. Serials not registered get CERT_UNKNOWN.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/ssl.h>
#include <wolfssl/ocsp.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(HAVE_OCSP) && defined(HAVE_OCSP_RESPONDER) && \
!defined(NO_FILESYSTEM)
#include "ocsp-load-certs.h"
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#define BUF_SZ 65536
static volatile int running = 1;
/* Case-insensitive substring search (for HTTP headers per RFC 7230) */
static char* FindHeaderCI(const char* haystack, const char* needle)
{
size_t nLen = strlen(needle);
while (*haystack) {
if (strncasecmp(haystack, needle, nLen) == 0)
return (char*)haystack;
haystack++;
}
return NULL;
}
/* Large buffers as static globals to avoid 128KB on the stack each iteration */
static byte httpBuf[BUF_SZ];
static byte respBuf[BUF_SZ];
static void sigHandler(int sig)
{
(void)sig;
running = 0;
}
/* Register a certificate's serial as CERT_GOOD in the responder by loading
* and parsing it to extract the serial number. */
static int AddGoodCert(OcspResponder* resp, const char* caSubject,
word32 caSubjectSz, const char* certFile)
{
byte* certDer;
int certDerSz = 0;
DecodedCert dc;
byte serial[32];
word32 serialSz = sizeof(serial);
int ret;
certDer = LoadCertDer(certFile, &certDerSz);
if (!certDer)
return -1;
wc_InitDecodedCert(&dc, certDer, (word32)certDerSz, NULL);
ret = wc_ParseCert(&dc, CERT_TYPE, 0, NULL);
if (ret != 0) {
wc_FreeDecodedCert(&dc);
free(certDer);
return ret;
}
ret = wc_GetDecodedCertSerial(&dc, serial, &serialSz);
wc_FreeDecodedCert(&dc);
free(certDer);
if (ret != 0)
return ret;
return wc_OcspResponder_SetCertStatus(resp, caSubject, caSubjectSz,
serial, serialSz, CERT_GOOD, 0, WC_CRL_REASON_UNSPECIFIED,
86400);
}
/* Receive full HTTP request. */
static int RecvHttp(int fd, byte* buf, int bufSz)
{
int total = 0, contentLen = 0, headerEnd = 0;
while (total < bufSz - 1) {
int n = (int)recv(fd, buf + total, (size_t)(bufSz - 1 - total), 0);
if (n <= 0) break;
total += n;
buf[total] = '\0';
if (!headerEnd) {
char* hdrEnd = strstr((char*)buf, "\r\n\r\n");
if (hdrEnd) {
char* cl;
headerEnd = (int)(hdrEnd - (char*)buf) + 4;
cl = FindHeaderCI((char*)buf, "Content-Length:");
if (cl) {
long val = strtol(cl + 15, NULL, 10);
if (val > 0 && val < bufSz)
contentLen = (int)val;
}
}
}
if (headerEnd && contentLen > 0 && total >= headerEnd + contentLen)
break;
}
return total;
}
/* Extract POST body from HTTP request */
static int ParsePost(const byte* http, int httpSz,
const byte** body, int* bodySz)
{
const char* hdr = (const char*)http;
const char* end;
const char* cl;
int offset;
*body = NULL;
*bodySz = 0;
if (strncmp(hdr, "POST ", 5) != 0)
return -1;
end = strstr(hdr, "\r\n\r\n");
if (!end) return -1;
offset = (int)(end - hdr) + 4;
cl = FindHeaderCI(hdr, "Content-Length:");
if (cl) {
long val = strtol(cl + 15, NULL, 10);
if (val <= 0 || val > httpSz - offset)
return -1;
*bodySz = (int)val;
}
else {
*bodySz = httpSz - offset;
}
if (offset + *bodySz > httpSz)
return -1;
*body = http + offset;
return 0;
}
static int SendAll(int fd, const void* data, int sz)
{
const byte* p = (const byte*)data;
int remaining = sz;
while (remaining > 0) {
int n = (int)send(fd, p, (size_t)remaining, 0);
if (n <= 0) return -1;
p += n;
remaining -= n;
}
return sz;
}
static void SendOcspResp(int fd, const byte* resp, int respSz)
{
char hdr[256];
int hdrLen;
hdrLen = snprintf(hdr, sizeof(hdr),
"HTTP/1.0 200 OK\r\n"
"Content-Type: application/ocsp-response\r\n"
"Content-Length: %d\r\n"
"\r\n", respSz);
SendAll(fd, hdr, hdrLen);
SendAll(fd, resp, respSz);
}
static void SendHttpError(int fd, int code, const char* msg)
{
char buf[256];
int len = snprintf(buf, sizeof(buf),
"HTTP/1.0 %d %s\r\nContent-Length: 0\r\n\r\n", code, msg);
SendAll(fd, buf, len);
}
int main(int argc, char** argv)
{
int port;
const char* certFile;
const char* keyFile;
byte *caCertDer = NULL, *caKeyDer = NULL;
int caCertDerSz = 0, caKeyDerSz = 0;
OcspResponder* responder = NULL;
DecodedCert caCert;
int caCertInit = 0;
char caSubject[256];
word32 caSubjectSz = sizeof(caSubject);
int sockfd = -1, clientfd, opt = 1, i, ret = 0;
struct sockaddr_in addr;
struct sigaction sa;
if (argc < 4) {
printf("Usage: %s <port> <ca-cert> <ca-key> [good-cert ...]\n\n"
" port Listen port\n"
" ca-cert CA certificate (PEM)\n"
" ca-key CA private key (PEM)\n"
" good-cert Certificate(s) to mark as GOOD\n",
argv[0]);
return 1;
}
port = atoi(argv[1]);
certFile = argv[2];
keyFile = argv[3];
if (wolfSSL_Init() != WOLFSSL_SUCCESS) {
fprintf(stderr, "wolfSSL_Init failed\n");
return 1;
}
sa.sa_handler = sigHandler;
sa.sa_flags = 0; /* No SA_RESTART so accept() returns on signal */
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
/* Ignore SIGPIPE so client disconnections during writes don't crash */
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
caCertDer = LoadCertDer(certFile, &caCertDerSz);
caKeyDer = LoadKeyDer(keyFile, &caKeyDerSz);
if (!caCertDer || !caKeyDer) {
fprintf(stderr, "Error loading cert/key\n");
ret = -1;
goto cleanup;
}
wc_InitDecodedCert(&caCert, caCertDer, (word32)caCertDerSz, NULL);
caCertInit = 1;
if (wc_ParseCert(&caCert, CERT_TYPE, 0, NULL) != 0) {
fprintf(stderr, "Error parsing CA cert\n");
ret = -1;
goto cleanup;
}
if (wc_GetDecodedCertSubject(&caCert, caSubject, &caSubjectSz) != 0) {
fprintf(stderr, "Error getting CA subject\n");
ret = -1;
goto cleanup;
}
responder = wc_OcspResponder_new(NULL, 1);
if (!responder) {
fprintf(stderr, "Error creating responder\n");
ret = -1;
goto cleanup;
}
if (wc_OcspResponder_AddSigner(responder, caCertDer, (word32)caCertDerSz,
caKeyDer, (word32)caKeyDerSz,
NULL, 0) != 0) {
fprintf(stderr, "Error adding signer\n");
ret = -1;
goto cleanup;
}
/* Register any extra cert arguments as GOOD */
for (i = 4; i < argc; i++) {
if (AddGoodCert(responder, caSubject, caSubjectSz, argv[i]) == 0)
printf("Registered GOOD: %s\n", argv[i]);
else
fprintf(stderr, "Warning: could not register %s\n", argv[i]);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
ret = -1;
goto cleanup;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("setsockopt");
ret = -1;
goto cleanup;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons((unsigned short)port);
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
ret = -1;
goto cleanup;
}
if (listen(sockfd, 5) < 0) {
perror("listen");
ret = -1;
goto cleanup;
}
printf("OCSP responder listening on port %d\n", port);
while (running) {
word32 respSz;
const byte* ocspReq;
int ocspReqSz, recvLen;
struct timeval tv;
clientfd = accept(sockfd, NULL, NULL);
if (clientfd < 0) continue;
/* Set receive timeout so incomplete requests don't block forever */
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
recvLen = RecvHttp(clientfd, httpBuf, BUF_SZ);
if (recvLen <= 0 ||
ParsePost(httpBuf, recvLen, &ocspReq, &ocspReqSz) != 0) {
SendHttpError(clientfd, 400, "Bad Request");
close(clientfd);
continue;
}
respSz = sizeof(respBuf);
if (wc_OcspResponder_WriteResponse(responder, ocspReq,
(word32)ocspReqSz, respBuf, &respSz) != 0) {
respSz = sizeof(respBuf);
wc_OcspResponder_WriteErrorResponse(OCSP_INTERNAL_ERROR,
respBuf, &respSz);
}
SendOcspResp(clientfd, respBuf, (int)respSz);
close(clientfd);
}
printf("\nShutdown.\n");
cleanup:
if (sockfd >= 0) close(sockfd);
if (responder) wc_OcspResponder_free(responder);
if (caCertInit) wc_FreeDecodedCert(&caCert);
free(caCertDer);
free(caKeyDer);
wolfSSL_Cleanup();
return ret;
}
#else
int main(int argc, char** argv)
{
(void)argc; (void)argv;
printf("This example requires --enable-ocsp --enable-ocsp-responder\n");
return 0;
}
#endif /* HAVE_OCSP && HAVE_OCSP_RESPONDER && !NO_FILESYSTEM */