mTLS Setup
Setting up keys and certificates
Overview
You'll need to:
- Generate a private key and Certificate Signing Request (CSR)
- Obtain an Organization Validated (OV) SSL certificate from a recognized authority
Prerequisites
- OpenSSL installed on your system
- Administrative access to your domain's DNS
- Company registration documents (for OV validation)
Step 1: Generate Private Key and CSR
1.1 Generate Private Key
# Generate a 4096-bit RSA private key
openssl genrsa -out server.key 4096
# IMPORTANT: Keep this key secure! Never share it or commit it to version control
chmod 600 server.key1.2 Generate Certificate Signing Request (CSR)
# Generate the CSR using your private key
openssl req -new -key server.key -out server.csrYou'll be prompted for the following information:
Country Name (2 letter code) [AU]: BE
State or Province Name (full name) [Some-State]: Brabant Wallon
Locality Name (eg, city) []: Mont-Saint-Guibert
Organization Name (eg, company) [Internet Widgits Pty Ltd]: Your Company Legal Name
Organizational Unit Name (eg, section) []: IT Department
Common Name (e.g. server FQDN or YOUR name) []: webhook.yourcompany.com
Email Address []: [email protected]
# Optional fields (press Enter to skip):
A challenge password []:
An optional company name []:
⚠️ Critical Information:
- Common Name (CN): Must be your exact domain name (e.g.,
webhook.yourcompany.com) - Organization Name: Must be your legal company name (will be verified)
- Email: Use an email address that can receive validation emails
1.3 Verify Your CSR
# Check CSR contents
openssl req -in server.csr -noout -textStep 2: Obtain OV SSL Certificate
2.1 Purchase Certificate
- Visit a OV certificate reseller (we generally use GlobalSign: GlobalSign SSL Certificates, if you would like to use another one, no problem: just let us know)
- Select "Organization Validated (OV) SSL"
- Choose certificate duration (1-2 years recommended)
- Create an account or log in
2.2 Submit Your CSR
- Copy the entire contents of your CSR file:
cat server.csr - Paste the CSR submission form (including the BEGIN/END lines)
- Complete the order form with company information
2.3 Complete Organization Validation
Normally the certificate authority will verify:
- Domain ownership (via DNS, email, or HTTP file)
- Organization legitimacy (business registration, phone verification)
Domain Validation Options:
When using GlobalSign as an example:
Option A - DNS Validation (Recommended)
# Add TXT record to your DNS:
Host: _globalsign-domain-verification
Value: globalsign-verification=XXXXXXXXXXXXXOption B - HTTP File Validation
# Create verification file at:
http://webhook.yourcompany.com/.well-known/pki-validation/globalsign.txtOption C - Email Validation
- GlobalSign sends email to: admin@, webmaster@, or postmaster@ your domain
2.4 Download Certificates
After approval (typically 1-3 business days), you'll receive:
server.crt- Your server certificateintermediate.crt- intermediate certificate- Installation instructions
Setting up a mTLS client for calling our services
Step 1: Install Certificates
1.1 Create Certificate Bundle
# Combine certificates in correct order
cat server.crt intermediate.crt > server-bundle.crt
# Verify the certificate chain
openssl verify -CAfile intermediate.crt server.crt1.2 Test Certificate
# Check certificate details
openssl x509 -in server-bundle.crt -text -noout | grep -A2 "Subject:"
# Test certificate and key match
openssl x509 -noout -modulus -in server-bundle.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5
# Both commands should return the same MD5 hashStep 2: Provide us the necessary verification info
- The necessary certificate(s) to add to our trust store
- The full Subject to verify against
Step 3 (Optional): Example of calling our service
(Coming soon)
Setting up a mTLS server for webhook reception
Step 1: Obtain client certificate
You can obtain the four OV certificates to be added to you trust store here: https://support.globalsign.com/ca-certificates/intermediate-certificates/organizationssl-intermediate-certificates
Step 2: (Optional) Example of webhook server doing client verification
Create a file named webhook-server.go:
package main
import (
"crypto/tls"
"crypto/x509"
"io"
"log"
"net/http"
"os"
"strings"
"time"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Received %s request from %s", r.Method, r.RemoteAddr)
if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
w.WriteHeader(http.StatusUnauthorized)
io.WriteString(w, "mTLS authentication not provided")
return
}
cert := r.TLS.PeerCertificates[0]
if strings.Compare("cn=,...", cert.Subject.String()) != 0 {
w.WriteHeader(http.StatusForbidden)
io.WriteString(w, "mTLS authentication failed")
return
}
w.WriteHeader(http.StatusOK)
io.WriteString(w, "Webhook received successfully\n")
}
func loadAndAppendCerts(paths []string, certPool *x509.CertPool) {
for _, path := range paths {
cert, err := os.ReadFile(path)
if err != nil {
log.Fatalf("Error loading cert from path %s: %v", path, err)
}
if !certPool.AppendCertsFromPEM(cert) {
log.Fatalf("Unable to add cert from path %s to cert pool", path)
}
}
}
func main() {
// Define routes
handler := http.NewServeMux()
handler.HandleFunc("/webhook", webhookHandler)
handler.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
io.WriteString(w, "OK\n")
})
// Configure TLS
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
}
// Load all the necessary certs
caCertPool := x509.NewCertPool()
certPaths := []string{"client-ca1.pem", "client-ca2.pem"}
loadAndAppendCerts(certPaths, caCertPool)
// Activate cryptographic security of the client certs
tlsConfig.ClientCAs = caCertPool
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
// Configure the server
server := &http.Server{
Addr: ":8443",
Handler: handler,
TLSConfig: tlsConfig,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
}
log.Println("Starting HTTPS webhook server on :8443")
log.Println("Endpoints:")
log.Println(" - https://localhost:8443/webhook")
log.Println(" - https://localhost:8443/health")
// Start the server with its server certs
if err := server.ListenAndServeTLS("server-bundle.crt", "server.key"); err != nil {
log.Fatalf("Server failed: %v", err)
}
}You can run the server:
# Install Go if not already installed
# Download from https://golang.org/dl/
# Run the webhook server
go run webhook-server.go3. Setup your webhook configuration
3.1 Manually enable mTLS
Contact support via https://support.digiteal.eu or email us at [email protected] asking us to enable all webhooks to be received via mTLS and we will manually set a flag in our system enabling this
3.2 Add the url in the webhook config
If the mTLS port is different form the default port, please specify it in the request:
curl --request POST \
--url https://api-test.digiteal.eu/api/v1/webhook \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"type": "PEPPOL_INVOICE_RECEIVED",
"url": "https://api.mycompany.org:8443"
}
'4. End-to-end test
General troubleshooting
Certificate Issues
# Verify certificate is valid
openssl x509 -in server-bundle.crt -text -noout
# Check certificate expiration
openssl x509 -in server-bundle.crt -noout -enddate
# Test SSL connection
openssl s_client -connect webhook.yourcompany.com:8443 -servername webhook.yourcompany.comCommon Problems
| Issue | Solution |
|---|---|
| "x509: certificate signed by unknown authority" | Ensure you're using the bundled certificate with intermediate CA |
| "tls: private key does not match public key" | Regenerate CSR with the correct private key |
| Connection refused | Check firewall rules for port 8443 |
| Certificate name mismatch | Ensure CN in certificate matches your domain |
Certificate Renewal
⚠️ Important: OV SSL certificates typically expire after 1-2 years.
Set Renewal Reminder
# Check expiration date
openssl x509 -in server-bundle.crt -noout -enddate
# Add to calendar 30 days before expirationRenewal Process
- Generate new CSR (can reuse existing private key)
- Submit renewal request to your certificate authority
- Complete validation (usually faster for renewals)
- Replace certificates and restart server
Updated 11 days ago
