Certificate development for Onion Services¶
This document is intended for those who want to develop and experiment with custom certification procedures.
For general certificate usage, check this document instead.
For research on certificates for Onion Services, check this other document.
Test CAs and certificates¶
This is a quick guide with procedures for setting up custom CAs and certificates, useful for development, Quality Assurance and testing purposes.
Please use it in a test/disposable virtual machine.
Test CA with mkcert¶
With mkcert (package), self-signed certificate management gets really simple:
mkcert '*.exav2agnwayz4hxbm2elifyn25ivey7pg7glg676nz2udsobbvsihkad.onion'
Test CA with Easy-RSA¶
Creating a custom test CA using Easy-RSA:
sudo apt update
sudo apt install -y easy-rsa
EASY_RSA="$HOME/code/easy-rsa"
mkdir -p $EASY_RSA
ln -s /usr/share/easy-rsa/* $EASY_RSA
chmod 700 $EASY_RSA
cd $EASY_RSA
./easyrsa init-pki
cat <<EOF >> pki/vars
set_var EASYRSA_REQ_COUNTRY "US"
set_var EASYRSA_REQ_PROVINCE "NewYork"
set_var EASYRSA_REQ_CITY "New York City"
set_var EASYRSA_REQ_ORG "Custom CA"
set_var EASYRSA_REQ_EMAIL "admin@example.com"
set_var EASYRSA_REQ_OU "Custom CA"
set_var EASYRSA_REQ_CN "Custom CA"
set_var EASYRSA_ALGO "ec"
set_var EASYRSA_DIGEST "sha384"
set_var EASYRSA_CURVE "secp384r1"
set_var EASYRSA_CERT_EXPIRE "365"
EOF
./easyrsa build-ca nopass
Certification using Easy-RSA¶
Select the domain:
DOMAIN="exav2agnwayz4hxbm2elifyn25ivey7pg7glg676nz2udsobbvsihkad.onion"
Create the key and certificate:
./easyrsa --subject-alt-name="DNS:${DOMAIN},DNS:*.${DOMAIN}" \
--req-c=AQ \
--req-st=Anywhere \
--req-city=Nowhere \
--req-org="The Onion Space" \
--req-email=none@example.org \
--req-ou="Onion Hyperspace" \
build-server-full ${DOMAIN} nopass
Create the fullchain version:
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
pki/issued/${DOMAIN}.crt | cat - pki/ca.crt > pki/issued/${DOMAIN}.fullchain.crt
Certification using Easy-RSA and OpenSSL¶
Select the domain:
DOMAIN="exav2agnwayz4hxbm2elifyn25ivey7pg7glg676nz2udsobbvsihkad.onion"
Create the key and the CSR:
OPENSSL=$HOME/code/openssl_certs
mkdir -p $OPENSSL
cat <<EOF > $OPENSSL/${DOMAIN}.conf
[ req ]
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
commonName_default = $DOMAIN
organizationName = The Onion Space
organizationalUnitName = Onion Hyperspace
emailAddress = none@example.org
localityName = Nowhere
stateOrProvinceName = Anywhere
countryName = The Internet
commonName = Torified Project
EOF
openssl req -batch -config $OPENSSL/${DOMAIN}.conf -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 \
-keyout $OPENSSL/${DOMAIN}.privatekey.pem -out $OPENSSL/${DOMAIN}.csr.pem -sha384 \
-nodes
openssl req -in $OPENSSL/${DOMAIN}.csr.pem -text
Sign using the custom CA created in the previous section:
cd ~/code/easy-rsa
./easyrsa import-req ~/code/openssl_certs/${DOMAIN}.csr.pem ${DOMAIN}
./easyrsa \
--subject-alt-name="DNS:${DOMAIN},DNS:*.${DOMAIN}" \
sign-req server ${DOMAIN}
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
pki/issued/${DOMAIN}.crt | cat - pki/ca.crt > ~/code/openssl_certs/${DOMAIN}.cert.pem
Certification using Easy-RSA and mkcert¶
Certificates created with this procedure currently do not work and will trigger
a SSL_ERROR_NO_CYPHER_OVERLAP
in Tor Browser.
First, adapt the Easy RSA CA into the mkcert format:
mkdir -p ~/code/easy-rsa/mkcert
cd ~/code/easy-rsa/mkcert
ln -s ../pki/ca.crt rootCA.pem
ln -s ../pki/private/ca.key rootCA-key.pem
Select the domain:
DOMAIN="exav2agnwayz4hxbm2elifyn25ivey7pg7glg676nz2udsobbvsihkad.onion"
Then create the certificate:
CAROOT=$HOME/code/easy-rsa/mkcert mkcert "*.${DOMAIN}"
Add the fullchain:
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
./_wildcard.${DOMAIN}.pem | \
cat - ~/code/easy-rsa/pki/ca.crt \
> ./_wildcard.${DOMAIN}.cert
Certification using Onionmine and Easy RSA¶
This is an experimental approach for getting the certificate using Onionmine and Easy RSA.
Define a pool:
POOL="mypool"
Create some keys:
git clone --recursive https://gitlab.torproject.org/tpo/onion-services/onionmine.git ~/onionmine
cd onionmine
./bin/provision
./onionmine config ${POOL}
echo onion > pools/${POOL}/filters.lst
./onionmine mine ${POOL}
After a while, type Ctr C
and select one of the create addresses:
DOMAIN="exav2agnwayz4hxbm2elifyn25ivey7pg7glg676nz2udsobbvsihkad.onion"
./onionmine select-candidate ${POOL} ${DOMAIN}
Then create a CSR:
cd ~/onionmine
./onionmine generate-selected-cert ${POOL}
Signing the Onion Service certificate:
cd ~/easy-rsa
./easyrsa import-req ~/onionmine/pools/${POOL}/certs/selected/csr.pem ${DOMAIN}
./easyrsa sign-req server ${DOMAIN}
References¶
- Easy-RSA:
- mkcert:
EV certs from custom CAs¶
Developing using EV certs from custom CAs is unfeasible at this point:
- It's usually not easy, or maybe even possible, to have EV cert using a custom
CA without patching the browser source code:
- At least for Firefox, "Mozilla considers an intermediate certificate to be capable of issuing EV TLS certificates when all of the following are true. The intermediate certificate: [...] either directly or transitively chains up to a root certificate included in Mozilla's root store with the TLS (Websites) trust bit turned on, and EV enabled [...] has Policy Identifiers containing one or more of: 2.23.140.1.1 (CABF EV OID), 2.5.29.32.0 (anyPolicy OID), the CA's EV OIDs used by Mozilla in ExtendedValidation.cpp, or any Policy OIDs listed in ExtendedValidation.cpp for the CA certificate. (source).
- Also, since signed certs with custom CAs right now won't work by default on [Tor Browser][] as of 2025-10 (check this issue).