SSL Certificate For LAN Only Access (iOS 13)

Greetings!

I have a Bitwarden instance being self hosted at home on Ubuntu and recently found out that my iOS app I can no longer sync with the server. It turns out my certificate is no longer valid under iOS 13 due to the new extendedkey requirement by Apple. I was looking for a solution to generating a new SSL certificate for my LAN only access but can’t find any information in doing so. I tried reinstalling and generating the SSL certificate again but the error when trying to access the server through the app remains so it tells me that the installation script has yet to be updated to get around this issue.

Could someone help a newbie solve this? I can provide as much information as needed.

Again, this is a simple LAN only setup with no host names that was working fine prior to iOS 13.

Thanks!

Hello @chench0,
since you have self hosted bitwarden I guess you are a bit familiar with the console.
To fulfill the requirements [1] you can follow the reference [2] to generate a self-signed certificate. I personally would prefer to generate a CA [3].

I’ve not listed any steps here since I have no time right now to test them myself.

Hope this helps :slight_smile:

PS: you will need to delete the current certificate from your iOS (delete bitwarden app and it’s data, only thing I can think)

References

[1] Requirements for trusted certificates in iOS 13 and macOS 10.15 02.08.2020
[2] New self-signed SSL Certificate for iOS 13 02.08.2020
[3] How to create local TLS certificates for development on macOS 02.08.2020

Thank you for such a clear reply! So I think I may try [3] but I assume that when done I would need to import everything into my Bitwarden host machine, in my case, ~/bwdata/ssl/self/ ?

Yes, under .../bwdata/ssl/self you need a folder with your domain name example: .../bwdata/ssl/self/<domain>/ and there you put your certificates:

  1. certificate.crt (or .cert)
  2. private.key

example:
.../bwdata/ssl/self/example.com

Check on your config.yml the following:

  1. ssl_certificate_path
  2. ssl_key_path

(they should match the certificate paths :slight_smile: )

So I followed the instructions on that article and I ended up with three files, a .cert. (certificate), a .key and a .cer (CA). What do I do with the latter if anything?

And forgot to mention, is there a reason why these certificates look like this when opened in a text editor?

I don’t think they would work?

Hello @chench0,
They are highly in der format, thats why they look like that.
Soon I will send you a solution :slight_smile:.

Hello @chench0,
I have a tested solution which you can execute on Linux or MacOS. Instead of just creating a self signed certificate I decided to create CA first then the certificate, that way you can create and sign multiple certificates for your domain.
Secondly I also have a configuration to create a wildcard certificate. Modify as you please (DNS.1, DNS.2 in config file within the script).

Create Certificates

  1. Create a file called caCertGenerator.sh
  2. Copy the content given (see below)
  3. Replace the variables at the beginning of the script (
  4. Make it executable: chmod +x caCertGenerator.sh
  5. Run it ./caCertGenerator.sh

File: caCertGenerator.sh

#!/bin/bash

# Variables to replace ##############
CA_NAME=rootCA
# home.local
# example: www.example.com, BASE_DOMAIN=com, DOMAIN=example
BASE_DOMAIN=local
DOMAIN=home
TLS_DOMAIN=${DOMAIN}.${BASE_DOMAIN}
countryName="DE"
organizationName="Home GmbH"
emailAddress= "[email protected]${TLS_DOMAIN}"
organizationalUnitName="IT Department"
stateOrProvinceName="Bayern"
localityName="Schweinfurt"
postalCode="97422"
streetAddress="Weingartenweg 15"
organizationName="Home GmbH"
# End Variables to replace ##########

# Create folders
mkdir -p ca
mkdir -p domain
##############################
# Create configuration files
##############################

createRootCaConf() {
cat <<-EOF
[ req ]
default_bits        = 2048                   # RSA key size
encrypt_key         = yes                    # Protect private key | default = yes
default_md          = sha256                 # MD to use
utf8                = yes                    # Input is UTF-8
string_mask         = utf8only               # Emit UTF-8 strings
prompt              = no                     # Don't prompt for DN
distinguished_name  = req_distinguished_name # DN section

[req_distinguished_name]
countryName            = "${countryName}"            # C=
stateOrProvinceName    = "${stateOrProvinceName}"    # ST=
localityName           = "${localityName}"           # L=
organizationName       = "${organizationName}"       # O=
organizationalUnitName = "${organizationalUnitName}" # OU=
commonName             = "${TLS_DOMAIN}"             # CN=
emailAddress           = "[email protected]${TLS_DOMAIN}"   # CN/emailAddress=
EOF
}

# Includes commented variables, for easy customisation
createTlsConf() {
cat <<-EOF
[ req ]
default_bits        = 2048                   # RSA key size
# default_keyfile   = key.pem
# encrypt_key       = yes                    # Protect private key | default = yes
default_md          = sha256                 # MD to use
# utf8              = yes                    # Input is UTF-8
# string_mask       = utf8only               # Emit UTF-8 strings
prompt              = no                     # Don't prompt for DN

distinguished_name  = req_distinguished_name
req_extensions      = req_ext                # Desired extensions
# attributes        = req_attributes

[req_distinguished_name]
0.domainComponent       = "${BASE_DOMAIN}"
1.domainComponent       = "${DOMAIN}"
countryName             = "${countryName}"            # C=
stateOrProvinceName     = "${stateOrProvinceName}"    # ST=
localityName            = "${localityName}"           # L=
postalCode              = "${postalCode}"             # L/postalcode=
streetAddress           = "${streetAddress}"          # L/street=
organizationName        = "${organizationName}"       # O=
organizationalUnitName  = "${organizationalUnitName}" # OU=
commonName              = "${TLS_DOMAIN}"             # CN=
emailAddress            = "[email protected]${TLS_DOMAIN}"   # CN/emailAddress=
# 0.commonName          = "example.com"               # CN=
# 1.commonName          = "www.example.com"           # CN=
# 2.commonName          = "www2.example.com"          # CN=
# 3.commonName          = "www3.example.com"          # CN=

[ req_ext ]
# authorityKeyIdentifier = keyid
basicConstraints         = CA:FALSE
keyUsage                 = digitalSignature, keyEncipherment
extendedKeyUsage         = serverAuth,clientAuth
# keyUsage               = digitalSignature,serverAuth,clientAuth,keyEncipherment, dataEncipherment, nonRepudiation
# extendedKeyUsage       = codeSigning
subjectAltName           = @alt_names

[ req_attributes ]
unstructuredName         = "Home GmbH"

[ alt_names ]
DNS.1 =   ${TLS_DOMAIN}
DNS.2 = *.${TLS_DOMAIN}
EOF
}

# This has dublicate variables, but need to be created to include
# authorityKeyIdentifier. The other variables are there as they will be
# replaced otherwise
createExtConf() {
cat <<-EOF
authorityKeyIdentifier = keyid
basicConstraints       = CA:FALSE
keyUsage               = digitalSignature, keyEncipherment
extendedKeyUsage       = serverAuth,clientAuth
subjectAltName         = @alt_names

[ alt_names ]
DNS.1 =   ${TLS_DOMAIN} # Specific domain
DNS.2 = *.${TLS_DOMAIN} # Wildcard certificate, for any subdomain
EOF
}


createRootCaConf > ./ca/${CA_NAME}.csr.cnf

createTlsConf > ./domain/${TLS_DOMAIN}.csr.cnf
createExtConf > ./domain/${TLS_DOMAIN}.ext.cnf

##############
# Signing CA #
##############

# will ask for a password
openssl genrsa -aes256 -out ca/${CA_NAME}.key 2048
# Verify above
# openssl rsa  -noout -text -in private-key.pem                                                 

# will ask for a password
# Variation with config file
openssl req -x509 -new -key ca/${CA_NAME}.key -sha256 -days 730 -out ca/${CA_NAME}.pem -config ca/${CA_NAME}.csr.cnf
# Verify above
# openssl x509 -noout -text -in rootCA.pem      

##############
# Site TLS   #
##############

# Create rsa key without encryption (-aes256 is missing :) )
openssl genrsa -out domain/${TLS_DOMAIN}.key 2048
# Create Signing request (.csr)
openssl req -new -key domain/${TLS_DOMAIN}.key -out domain/${TLS_DOMAIN}.csr -config domain/${TLS_DOMAIN}.csr.cnf
# Sign the certificate with your root CA
openssl x509 -days 365 -sha256 -req -in domain/${TLS_DOMAIN}.csr -CA ca/${CA_NAME}.pem -CAkey ca/${CA_NAME}.key -CAcreateserial -extfile domain/${TLS_DOMAIN}.ext.cnf -out domain/${TLS_DOMAIN}.crt

Copy certificates

Copy the *.crt and *.key (under the domain folder created from the script) and put
them inside bwdata/ssl/self/<domainName>
Correct the path on bwdata/config.yml on ssl_certificate_path and ssl_key_path.

Import

Import on iOS link

End

Hope this helps :slight_smile:

1 Like

This is amazing @Pulsar! I truly appreciate you taking the time to put this together! I will be trying this in a few minutes and can’t wait to get it working.

Much appreciated!