goletter / hyperf-mtls
Hyperf mTLS certificate generator
Requires
- php: >=8.1
- ext-openssl: *
- hyperf/command: ~3.1.0
- hyperf/config: ~3.1.0
- hyperf/contract: ~3.1.0
- hyperf/di: ~3.1.0
README
Hyperf command package for generating local mTLS certificates.
Commands
Generate a local root CA:
php bin/hyperf.php mtls:ca --cn "Goletter Root CA"
Generate a client certificate and PKCS#12 installer files:
php bin/hyperf.php mtls:client \ --user user-001 \ --password 123456
Generate different client certificates for different users by changing --user:
php bin/hyperf.php mtls:client --user alice --password 123456 php bin/hyperf.php mtls:client --user bob --password 123456
When a client certificate is generated, its metadata is stored in the client_certificates table.
Output
By default, files are written under runtime/certs and grouped by certificate role.
runtime/certs/
ca/
ca.crt
ca.key
clients/
alice/
alice.crt
alice.key
alice.p12
alice.pfx
bob/
bob.crt
bob.key
bob.p12
bob.pfx
Use the generated .p12 file on macOS Keychain Access and the generated .pfx file on Windows Certificate Manager. Install ca.crt as a trusted root certificate when the client machine needs to trust certificates signed by this local CA.
PKCS#12 files are exported with legacy 3DES + SHA-1 so macOS Keychain can import them. OpenSSL 3's default AES-256 + SHA-256 often fails on Mac with MAC verification failed (wrong password?) even when the password is correct.
If you already have a modern .p12 that Mac rejects, convert it:
openssl pkcs12 -in yong.p12 -out yong.tmp.pem -nodes -passin pass:123456 openssl pkcs12 -export \ -in yong.tmp.pem \ -out yong-macos.p12 \ -name yong \ -passout pass:123456 \ -keypbe PBE-SHA1-3DES \ -certpbe PBE-SHA1-3DES \ -macalg sha1 rm yong.tmp.pem
Use a public CA certificate, such as Let's Encrypt or a cloud provider SSL certificate, for the server side of HTTPS. This package only generates the private CA and client certificates used for mTLS client authentication.
Middleware
Register Goletter\Mtls\Middleware\ClientCertificateMiddleware::class in the Hyperf HTTP middleware stack, then enable verification:
MTLS_VERIFY_CLIENT=true MTLS_CHECK_DATABASE=true
The middleware expects Nginx to verify the client certificate first and pass the result to Hyperf:
ssl_client_certificate /path/to/runtime/certs/ca/ca.crt; ssl_verify_client on; proxy_set_header X-SSL-Client-Verify $ssl_client_verify; proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn; proxy_set_header X-SSL-Client-DN $ssl_client_s_dn; proxy_set_header X-SSL-Client-Serial $ssl_client_serial; proxy_set_header X-SSL-Client-Fingerprint $ssl_client_fingerprint;
Optional allow lists:
MTLS_ALLOWED_CLIENT_CNS=alice,bob MTLS_ALLOWED_CLIENT_FINGERPRINTS=8F2A...,A94B...
When MTLS_CHECK_DATABASE=true, the middleware also checks client_certificates:
fingerprint or serial must exist
status must be active
expires_at must not be expired
Set status=revoked to revoke a client certificate. The next request with that certificate will return 403.
When the certificate passes, the middleware adds these request attributes:
mtls_client_cn
mtls_client_dn
mtls_client_serial
mtls_client_fingerprint
mtls_client_certificate_id
mtls_client_user
Admin Download API
The project can expose an admin API that generates a client certificate and returns the installer file as a download:
POST /api/admin/mtls/client-certificates Content-Type: application/json { "user": "alice", "password": "123456", "format": "p12" }
Supported request fields:
user Required. User/client identifier and output folder name.
password Required. Password for the p12/pfx installer file.
format Optional. p12 or pfx, defaults to p12.
cn Optional. Certificate CN, defaults to user.
friendly_name Optional. PKCS#12 friendly name, defaults to user.
The generated files are stored under runtime/certs/clients/{user} and the selected p12 or pfx file is returned as an attachment.
The generated certificate metadata is also stored in client_certificates, including:
user
cn
serial
fingerprint
status
cert_path
key_path
p12_path
pfx_path
issued_at
expires_at
Run the migration before using the command or admin API:
php bin/hyperf.php migrate
Configuration
Publish or copy publish/mtls.php to config/autoload/mtls.php.
Supported environment variables:
MTLS_VERIFY_CLIENT=false MTLS_CHECK_DATABASE=true MTLS_ALLOWED_CLIENT_CNS= MTLS_ALLOWED_CLIENT_FINGERPRINTS= MTLS_CERT_OUTPUT_DIR=/path/to/certs MTLS_CA_DAYS=3650 MTLS_CERT_DAYS=365 MTLS_KEY_BITS=2048 MTLS_CA_KEY_BITS=4096 MTLS_COUNTRY_NAME=CN MTLS_ORGANIZATION_NAME=Goletter MTLS_CA_COMMON_NAME="Goletter Root CA" MTLS_CLIENT_COMMON_NAME=goletter-client MTLS_CLIENT_FRIENDLY_NAME="Goletter Client" MTLS_CLIENT_EXPORT_PASSWORD=changeit
Private keys are written with 0600 permissions. Keep generated private keys and PKCS#12 files out of version control.