This post is a part of Tor v3 tutorial. Other parts are:
- Hidden Service setup
- PKI and TLS
- Client Authentication
- Installing client certificates to Firefox for Android
After setting up working Tor hidden service, the next step to ultimate security is having properly implemented Public Key Infrastructure (PKI). For this step, there are a lot of tutorials already existing and there is not much that needs to be added to them. Personally, I was using tutorial available here for the second time now and I find it very well-written. Because I am going to follow this tutorial, I will just post commands that have to be executed.
Before starting, I have to add one important remark. To make our PKI really secure one, it is crucial to have root CA air-gapped, that is device, on which it will be generated should be disconnected permanently from the internet. Good candidate for such a device might be some old laptop or Raspberry Pi Zero, as it lacks Ethernet port and anything reasonable to connect to internet. It is also important to store generated certificate in a safe place and secure it with strong non-dictionary password, which will be saved only in our mind.
If the requirements are fulfilled, we can start the setup. Below are commands to type as well as output from them, for easier determination of whether the commands were successful or not.
Preparations
At first, we need to create following directory structure:
ca ├── [drwxr-xr-x] certs ├── [drwxr-xr-x] crl ├── [-rw-r--r--] index.txt ├── [drwxr-xr-x] intermediate │ ├── [drwxr-xr-x] certs │ ├── [drwxr-xr-x] crl │ ├── [drwxr-xr-x] csr │ ├── [-rw-r--r--] index.txt │ ├── [drwxr-xr-x] newcerts │ ├── [drwx------] private │ └── [-rw-r--r--] serial ├── [drwxr-xr-x] newcerts ├── [drwx------] private └── [-rw-r--r--] serial
And file content is (enclosed between pipe symbols: |):
./index.txt: || ./intermediate/index.txt: || ./intermediate/serial: |1000 | ./serial: |1000 |
Then, we need to save this file into root/openssl.cnf
and this file into root/intermediate/openssl.cnf
. Inside them, the only thing that have to be changed is dir
property in CA_default
section. Use absolute path to your directory.
Root CA
Note: when giving values for certain fields, better give some country, state (I have just checked it’s necessary), ON, most importantly, Common Name and e-mail. Just in case some program will check if they exists.
$ openssl genrsa -aes256 -out private/ca.key.pem 8192 Generating RSA private key, 8192 bit long modulus .................++ ....++ e is 65537 (0x010001) Enter pass phrase for private/ca.key.pem: Verifying - Enter pass phrase for private/ca.key.pem: $ chmod 400 private/ca.key.pem $ openssl req -config openssl.cnf -key private/ca.key.pem -new -x509 -days 7300 \ > -sha256 -extensions v3_ca -out certs/ca.cert.pem Enter pass phrase for private/ca.key.pem: You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [GB]:PL State or Province Name [England]:lodzkie Locality Name []: Organization Name [Alice Ltd]:r4pt0r Test Systems Organizational Unit Name []: Common Name []:r4pt0r Root CA Email Address []:admin@example.com $ chmod 444 certs/ca.cert.pem $ openssl x509 -noout -text -in certs/ca.cert.pem Certificate: Data: Version: 3 (0x2) Serial Number: 9a:16:72:e8:ac:81:cd:be Signature Algorithm: sha256WithRSAEncryption Issuer: C = PL, ST = lodzkie, O = r4pt0r Test Systems, CN = r4pt0r Root CA, emailAddress = admin@example.com Validity Not Before: Feb 20 17:22:27 2018 GMT Not After : Feb 15 17:22:27 2038 GMT Subject: C = PL, ST = lodzkie, O = r4pt0r Test Systems, CN = r4pt0r Root CA, emailAddress = admin@example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (8192 bit) Modulus: 00:dd:8c:8f:5d:be:f4:0f:63:91:9c:73:bf:a8:17: <quite a lot of data> 6d:c1:3f:5c:05 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 29:53:8A:D2:ED:CF:35:C2:BB:A8:12:06:01:74:99:A3:B8:E5:DC:FE X509v3 Authority Key Identifier: keyid:29:53:8A:D2:ED:CF:35:C2:BB:A8:12:06:01:74:99:A3:B8:E5:DC:FE X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign Signature Algorithm: sha256WithRSAEncryption a9:6d:9e:d4:bf:1b:55:d8:f0:b5:e9:9d:56:e8:58:04:d6:c3: <quite a lot of data> 89:50:26:4f:3e:93:95:06:c7:38:08:c7:16:0e:d2:a2
Intermediate CA
$ openssl genrsa -aes256 -out intermediate/private/intermediate.key.pem 8192 Generating RSA private key, 8192 bit long modulus .++ ........................................................................................................................................................................................................................................................................................++ e is 65537 (0x010001) Enter pass phrase for intermediate/private/intermediate.key.pem: Verifying - Enter pass phrase for intermediate/private/intermediate.key.pem: $ chmod 400 intermediate/private/intermediate.key.pem $ openssl req -config intermediate/openssl.cnf -new -sha256 \ > -key intermediate/private/intermediate.key.pem -out intermediate/csr/intermediate.csr.pem Enter pass phrase for intermediate/private/intermediate.key.pem: You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [GB]:PL State or Province Name [England]:lodzkie Locality Name []: Organization Name [Alice Ltd]:r4pt0r Test Systems Organizational Unit Name []: Common Name []:r4pt0r Intermediate CA Email Address []:admin@example.com $ openssl ca -config openssl.cnf -extensions v3_intermediate_ca -days 3650 \ > -notext -md sha256 -in intermediate/csr/intermediate.csr.pem -out intermediate/certs/intermediate.cert.pem Using configuration from openssl.cnf Enter pass phrase for ca/private/ca.key.pem: Can't open ca/index.txt.attr for reading, No such file or directory 140341269315520:error:02001002:system library:fopen:No such file or directory:crypto/bio/bss_file.c:74:fopen('ca/index.txt.attr','r') 140341269315520:error:2006D080:BIO routines:BIO_new_file:no such file:crypto/bio/bss_file.c:81: Check that the request matches the signature Signature ok Certificate Details: Serial Number: 4096 (0x1000) Validity Not Before: Feb 20 17:35:09 2018 GMT Not After : Feb 18 17:35:09 2028 GMT Subject: countryName = PL stateOrProvinceName = lodzkie organizationName = r4pt0r Test Systems commonName = r4pt0r Intermediate CA emailAddress = admin@example.com X509v3 extensions: X509v3 Subject Key Identifier: 3D:AC:8E:21:79:5A:AD:7B:7C:92:92:65:B7:19:D0:E8:00:0E:50:70 X509v3 Authority Key Identifier: keyid:29:53:8A:D2:ED:CF:35:C2:BB:A8:12:06:01:74:99:A3:B8:E5:DC:FE X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign Certificate is to be certified until Feb 18 17:35:09 2028 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated $ openssl x509 -noout -text -in intermediate/certs/intermediate.cert.pem Certificate: Data: Version: 3 (0x2) Serial Number: 4096 (0x1000) Signature Algorithm: sha256WithRSAEncryption Issuer: C = PL, ST = lodzkie, O = r4pt0r Test Systems, CN = r4pt0r Root CA, emailAddress = admin@example.com Validity Not Before: Feb 20 17:35:09 2018 GMT Not After : Feb 18 17:35:09 2028 GMT Subject: C = PL, ST = lodzkie, O = r4pt0r Test Systems, CN = r4pt0r Intermediate CA, emailAddress = admin@example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (8192 bit) Modulus: 00:d4:c9:03:36:4a:dd:3d:ee:ca:bd:c1:d8:fe:51: <quite a lot of data> 5a:ca:74:74:c8:a2:b2:69:0a:0c:c7:f9:d6:8a:58: 41:45:73:fc:2b Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 3D:AC:8E:21:79:5A:AD:7B:7C:92:92:65:B7:19:D0:E8:00:0E:50:70 X509v3 Authority Key Identifier: keyid:29:53:8A:D2:ED:CF:35:C2:BB:A8:12:06:01:74:99:A3:B8:E5:DC:FE X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign Signature Algorithm: sha256WithRSAEncryption 15:04:2f:85:89:f6:77:82:c4:60:78:f0:4f:ac:39:ad:15:14: <quite a lot of data> 7c:71:95:db:16:02:de:01:70:fe:8f:48:94:92:11:1b $ openssl verify -CAfile certs/ca.cert.pem intermediate/certs/intermediate.cert.pem intermediate/certs/intermediate.cert.pem: OK $ cat intermediate/certs/intermediate.cert.pem certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem $ chmod 444 intermediate/certs/ca-chain.cert.pem
Server certificate
In the following parts, wherever [domain]
appears, it should be changed to hostname of our hidden service.
At first, we need to generate certificate request (CSR) on our server:
$ openssl genrsa -out [domain].onion.key.pem 4096 Generating RSA private key, 4096 bit long modulus .................++ ..............................................................................++ e is 65537 (0x010001) $ chmod 400 [domain].onion.key.pem $ openssl req -config ca/intermediate/openssl.cnf \ > -key [domain].onion.key.pem -new -sha256 -out [domain].onion.csr.pem You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [GB]:PL State or Province Name [England]:lodzkie Locality Name []: Organization Name [Alice Ltd]:r4pt0r Test Systems Organizational Unit Name []: Common Name []:[domain].onion Email Address []:admin@[domain].onion
Then, we will sign the request with intermediate CA private key, thus issuing the certificate. But first of all, we need to receive the CSR from the server, to intermediate/csr/
directory.
$ openssl ca -config intermediate/openssl.cnf -extensions server_cert -days 375 \ > -notext -md sha256 -in intermediate/csr/[domain].onion.csr.pem -out intermediate/certs/[domain].onion.cert.pem Using configuration from intermediate/openssl.cnf Enter pass phrase for ca/intermediate/private/intermediate.key.pem: Can't open ca/intermediate/index.txt.attr for reading, No such file or directory 139810167087040:error:02001002:system library:fopen:No such file or directory:crypto/bio/bss_file.c:74:fopen('ca/intermediate/index.txt.attr','r') 139810167087040:error:2006D080:BIO routines:BIO_new_file:no such file:crypto/bio/bss_file.c:81: Check that the request matches the signature Signature ok Certificate Details: Serial Number: 4096 (0x1000) Validity Not Before: Feb 20 17:52:13 2018 GMT Not After : Mar 2 17:52:13 2019 GMT Subject: countryName = PL stateOrProvinceName = lodzkie organizationName = r4pt0r Test Systems commonName = [domain].onion emailAddress = admin@[domain].onion X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Cert Type: SSL Server Netscape Comment: OpenSSL Generated Server Certificate X509v3 Subject Key Identifier: DD:6E:E8:78:91:B9:F7:F4:0A:06:3F:D2:38:6D:11:4E:3C:D3:BC:E0 X509v3 Authority Key Identifier: keyid:3D:AC:8E:21:79:5A:AD:7B:7C:92:92:65:B7:19:D0:E8:00:0E:50:70 DirName:/C=PL/ST=lodzkie/O=r4pt0r Test Systems/CN=r4pt0r Root CA/emailAddress=admin@example.com serial:10:00 X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication Certificate is to be certified until Mar 2 17:52:13 2019 GMT (375 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated $ openssl x509 -noout -text -in intermediate/certs/[domain].onion.cert.pem Certificate: Data: Version: 3 (0x2) Serial Number: 4096 (0x1000) Signature Algorithm: sha256WithRSAEncryption Issuer: C = PL, ST = lodzkie, O = r4pt0r Test Systems, CN = r4pt0r Intermediate CA, emailAddress = admin@example.com Validity Not Before: Feb 20 17:52:13 2018 GMT Not After : Mar 2 17:52:13 2019 GMT Subject: C = PL, ST = lodzkie, O = r4pt0r Test Systems, CN = [domain].onion, emailAddress = admin@[domain].onion Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (4096 bit) Modulus: 00:c5:d3:e2:a0:97:b8:4d:67:22:94:c9:be:17:e3: <quite a lof of data> 49:76:cf Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Cert Type: SSL Server Netscape Comment: OpenSSL Generated Server Certificate X509v3 Subject Key Identifier: DD:6E:E8:78:91:B9:F7:F4:0A:06:3F:D2:38:6D:11:4E:3C:D3:BC:E0 X509v3 Authority Key Identifier: keyid:3D:AC:8E:21:79:5A:AD:7B:7C:92:92:65:B7:19:D0:E8:00:0E:50:70 DirName:/C=PL/ST=lodzkie/O=r4pt0r Test Systems/CN=r4pt0r Root CA/emailAddress=admin@example.com serial:10:00 X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication Signature Algorithm: sha256WithRSAEncryption b0:92:d9:d5:3b:31:38:f6:b8:51:1f:41:e9:f7:d8:e6:33:67: <quite a lot of data> ee:c4:eb:19:86:69:00:26:8d:04:7b:97:0b:8f:f5:76 $ openssl verify -CAfile intermediate/certs/ca-chain.cert.pem intermediate/certs/[domain].onion.cert.pem intermediate/certs/[domain].onion.cert.pem: OK
httpd configuration
Finally, we can use generated files to set up HTTPS encryption on webserver. For this, I am using httpd as it is the most common webserver in use. We need following files:
[domain].onion.key.pem
– this is private key, that will be used to set up TLS session[domain].onion.cert.pem
– this is certificate that will prove our identity, so web browser will not display any warnings as long as we will have CA certificate installedca-chain.cert.pem
– this is chain of certificates we created together with intermediate CA, that consists of both CAs – root and intermediate
Below is httpd configuration file, after enabling TLS:
Listen 666 <VirtualHost *:666> ServerAdmin admin@re-ws.pl DocumentRoot "/home/r4pt0r/tor/hs/public_html" ServerName 192.168.253.4 ErrorLog "[path]/tor/hs/error_log" CustomLog "[path]/tor/hs/access_log" common ScriptAlias /cgit/ "/usr/lib/cgit/cgit.cgi/" Alias /cgit-css "/usr/share/webapps/cgit/" SSLEngine on SSLCertificateFile "[path]/tor/hs/tls/[domain].onion.cert.pem" SSLCertificateKeyFile "[path]/tor/hs/tls/[domain].onion.key.pem" SSLCACertificateFile "[path]/tor/hs/tls/ca-chain.cert.pem" </VirtualHost>
As can be seen above, all necessary files had been moved to tls
directory of our hidden service main directory.
Afterwards, one slight change is needed in torrc file:
HiddenServicePort 443 127.0.0.1:666
From now on, we need to use https://[domain].onion
to visit our site, as it is now TLS-encrypted and using port 443, which is default for HTTPS. For convenience, we can set up another httpd vhost on different port, that will redirect all HTTP traffic through HTTPS and link it to port 80, so remembering about https in address will not be necessary. But, it is only optional, so I will leave it as an exercise to the reader.
Firefox
From this point it is useful to have Firefox that is not constantly reminding about insecure connection. To prevent this, we should install CA certificate into Firefox. One remark here: as we are going to hack Firefox to trust our certificate, now our whole browsing through that instance of Firefox relies on our CAs private key. So, it is best to not use the same instance for anything else unless you are really sure, the private keys for both root and intermediate are perfectly secure.
To install the certificate, follow the screenshots below: