Issue
I'm working on a Ionic-Angular app and faced quite a few issues due to clear text traffic. So I decided to switch to https even while coding but it wasn't so easy.
I open this question and propose the answer I found to keep a trace and hopefully save you some time if you would like to do the same.
Solution
Prerequisite
You need a hostname for your dev machine. This name will be declared in certificates and https services will be accessed using this hostname (URLs have to be like https://[hostname]:...
for certificate check to pass).
If your network doesn't have a DNS already, you might use something like MaraDNS hosted on your dev-machine (see P.S. for sample configuration).
Generate certificate (and signing key) in various formats
self_signed_template.config:
[req]
default_bits = 2048
default_md = sha256
prompt = no
default_keyfile = [hostname]_self_signed_key.pem
encrypt_key = no
distinguished_name = dn
req_extensions = v3_req
x509_extensions = v3_req
[dn]
C = PF
ST = Tahiti
L = Papeete
O = c4-soft
emailAddress = [email protected]
CN = [hostname]
[v3_req]
subjectAltName = critical, @alt_names
basicConstraints = critical, CA:false
keyUsage = critical, keyCertSign, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = critical, serverAuth, clientAuth
[alt_names]
DNS.1 = [hostname]
DNS.2 = localhost
DNS.3 = 10.0.2.2
self_signed.sh
#!/bin/bash
if [ -z "$1" ]
then
echo "Usage:"
echo ""
echo "self_signed.sh key_password [java_home] [hostname] [store_password] [certificates_directory_path] [cacerts_password]"
echo ""
echo " - java_home is defaulted to $JAVA_HOME"
echo " - hostname is defaulted to $HOSTNAME"
echo " - store_password is defaulted to key_password"
echo " - certificates_directory_path is defaulted to current diretory"
echo " - cacerts_password is defaulted to changeit"
echo ""
echo "Sample:"
echo "./self_signed.sh \"secr3!\" \"C:/Java/jdk1.8.0_281\" \"bravo-ch4mp\""
echo ""
exit 1
else
echo "#------------------------------------------"
echo "# This is a no-op script"
echo "# Copy / paste output to:"
echo "# - generate certificate files"
echo "# - import certificates into cacerts file"
echo "#------------------------------------------"
KEY_PASSWORD="${1}"
echo "# key password: $KEY_PASSWORD"
if [ -z "$2" ]
then
if [ -z "$JAVA_HOME" ]
then
echo "ERROR: could not locate java home"
exit 1
else
JAVA=$JAVA_HOME
fi
else
JAVA=$2
fi
JAVA=$(echo $JAVA | sed 's/\\/\//g')
echo "# java home: $JAVA"
if [ -f "${JAVA}/lib/security/cacerts" ]
then
# recent JDKs and JREs style
CACERTS="${JAVA}/lib/security/cacerts"
elif [ -f "${JAVA}/jre/lib/security/cacerts" ]
then
# legacy JDKs style (1.8 and older)
CACERTS="${JAVA}/jre/lib/security/cacerts"
else
echo "ERROR: could not locate cacerts under ${JAVA}"
exit 1
fi
echo "# cacerts path: $CACERTS"
if [ -z "${3}" ]
then
HOST="$HOSTNAME"
else
HOST="${3}"
fi
echo "# host (certificate CN): $HOST"
if [ -z "${4}" ]
then
STORE_PASSWORD="$KEY_PASSWORD"
else
STORE_PASSWORD="${4}"
fi
echo "# store password : $STORE_PASSWORD"
if [ -z "${5}" ]
then
CERTIF_DIR="."
else
CERTIF_DIR="${5}"
fi
echo "# certificates directory path: $CERTIF_DIR"
CERTIF_DIR=$(echo $CERTIF_DIR | sed 's/\\/\//g')
if [ -z "${6}" ]
then
CACERTS_PASSWORD="changeit"
else
CACERTS_PASSWORD="${6}"
fi
echo "# cacerts password: $CACERTS_PASSWORD"
echo "#------------------------------------------"
fi
echo ""
rm -f ${HOST}_self_signed.config;
sed 's/\[hostname\]/'${HOST}'/g' "${CERTIF_DIR}/self_signed_template.config" > "${CERTIF_DIR}/${HOST}_self_signed.config"
echo openssl req -config \"${CERTIF_DIR}/${HOST}_self_signed.config\" -new -keyout \"${CERTIF_DIR}/${HOST}_self_signed_key.pem\" -out \"${CERTIF_DIR}/${HOST}_self_signed_cert.pem\" -reqexts v3_req
echo ""
echo openssl x509 -req -days 365 -extfile \"${CERTIF_DIR}/${HOST}_self_signed.config\" -in \"${CERTIF_DIR}/${HOST}_self_signed_cert.pem\" -extensions v3_req -signkey \"${CERTIF_DIR}/${HOST}_self_signed_key.pem\" -out \"${CERTIF_DIR}/${HOST}_self_signed.crt\"
echo ""
echo openssl pkcs12 -export -in \"${CERTIF_DIR}/${HOST}_self_signed.crt\" -inkey \"${CERTIF_DIR}/${HOST}_self_signed_key.pem\" -name ${HOST}_self_signed -password pass:${KEY_PASSWORD} -out \"${CERTIF_DIR}/${HOST}_self_signed.pfx\"
echo ""
echo \"${JAVA}/bin/keytool\" -importkeystore -srckeystore \"${CERTIF_DIR}/${HOST}_self_signed.pfx\" -srcstorepass \"${STORE_PASSWORD}\" -srcstoretype pkcs12 -srcalias ${HOST}_self_signed -destkeystore \"${CERTIF_DIR}/${HOST}_self_signed.jks\" -deststoretype PKCS12 -deststorepass ${STORE_PASSWORD} -destalias ${HOST}_self_signed
echo ""
echo \"${JAVA}/bin/keytool\" -importkeystore -srckeystore \"${CERTIF_DIR}/${HOST}_self_signed.pfx\" -srcstorepass \"${STORE_PASSWORD}\" -srcstoretype pkcs12 -srcalias ${HOST}_self_signed -destkeystore \"${CACERTS}\" -deststorepass ${CACERTS_PASSWORD} -destalias ${HOST}_self_signed
echo ""
Then run something like ./self_signed.sh "secr3!" C:/Java/jdk1.8.0_281
.
Execute ./self_signed.sh
for each of your JDKs / JREs and then
Simply copy / paste / run all output commands at 1st execution and last command only (import certificate in JDK / JRE cacerts
file) from 2nd execution on (otherwise you'll loose previous certificates).
Admin privileges could be required to import the certificate in Java's cacerts.
On windows, Git Bash has all of sed
, openssl
and keytool
on the path.
Import this certificate as trusted root authority
If you add this certificate to trusted root authorities, your browser will display no error nor warning when navigating URLs like https://[hostname]:...
.
On windows, this can be done with certmgr.msc
(right click trusted root authorities and then import). Please comment if you successfully do the same on other OS.
Configure Ionic-Angular to serve over https with this certificate
Edit angular.json
to set "sslCert" and "sslKey" under your-project/architect/serve/options/
and point it to respectively [hostname]_self_signed.crt
and [hostname]_self_signed_key.pem
generated earlier.
This is enough for the right certificate to be picked when running ionic serve --ssl --host=[hostname]
or ionic capacitor run android -l --ssl --host=[hostname]
Embed certificate in Android project
Reminder: android resource folder is android/app/src/main/res/
under your project or app/res/
in Android Studio
First, copy [hostname]_self_signed.crt
to raw
resources, replacing -
, if any, with _
in hostname.
Create network_security_config.xml
in xml resources (careful with modified hostname)
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="@raw/[hostname]_self_signed"/>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
important note: if all your trafic is served over https, you should set cleartextTrafficPermitted
to false (which is default value since Android 9). Consider doing so for prod build at least.
Finally, edit AndroidManifest.xml
and add android:networkSecurityConfig="@xml/network_security_config"
to your <application >
tag
Embed certificate in iOS project
I have no experience with iOS, please feel free to comment or add an answer if you get it working.
Configure your backends to serve over https with self-signed certificate
Well... it really depends on the stack you use. A few samples:
- For Kestrel (.Net app debugged in Visual Studio), set
ASPNETCORE_Kestrel__Certificates__Default__Password
andASPNETCORE_Kestrel__Certificates__Default__Path
, the second pointing to the[hostname]_self_signed.pfx
- For spring-boot, copy
[hostname]_self_signed.jks
intosrc/main/resources/
and setserver.ssl
properties - Keycloak has comprensive doc to setup the server with custom certificate
- Please comment if you get other backend types working
P.S. The dwood3rc.txt
file I use for MaraDNS:
#upstream_servers = {}
#upstream_servers["."]="8.8.8.8, 8.8.4.4" # Servers we connect to
root_servers = {}
# ICANN DNS root servers
root_servers["."]="198.41.0.4, 199.9.14.201, 192.33.4.12, 199.7.91.13,"
root_servers["."]+="192.203.230.10, 192.5.5.241, 192.112.36.4, "
root_servers["."]+="198.97.190.53, 192.36.148.17, 192.58.128.30, "
root_servers["."]+="193.0.14.129, 199.7.83.42, 202.12.27.33"
# local DNS server
root_servers["bravo-ch4mp."]="192.168.1.181"
root_servers["local."]="192.168.1.181"
# The IP this program has
bind_address="127.0.0.1, 192.168.1.181, 192.168.1.132"
# The IPs allowed to connect and use the cache
recursive_acl = "127.0.0.1/16, 192.168.0.1/16"
chroot_dir = "/etc/maradns"
# This is the file Deadwood uses to read the cache to and from disk
cache_file = "dw_cache_bin"
filter_rfc1918 = 0
ip4 = {}
ip4["bravo-ch4mp."] = "192.168.1.181"
ip6 = {}
Once the DNS server is up (net start deadwood
) on my dev machine (and also firewall configured...), I configure clients to use it as primary DNS (edit wifi network properties which does not require rooted device) et voilà !
P.S.2 Keycloak standalone configuration allowing test devices to connect to OpenId endpoints over https
Copy [hostname]_self_signed.jks
to standalone/configuration/
.
Edit standalone/configuration/standalone.xml
to replace ${jboss.bind.address:127.0.0.1}
with ${jboss.bind.address:0.0.0.0}
. Save and close.
Start Keycloak with bin/standalone[.bat|.sh]
, then using bin/jboss-cli[.bat|.sh]
:
connect
/subsystem=keycloak-server/spi=hostname/provider=default:write-attribute(name=properties.frontendUrl,value="https://[hostname]:8443/auth")
/core-service=management/security-realm=UndertowRealm:add()
/core-service=management/security-realm=UndertowRealm/server-identity=ssl:add(keystore-path=[hostname]_self_signed.jks, keystore-relative-to=jboss.server.config.dir, keystore-password=[keystore_password])
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=security-realm, value=UndertowRealm)
reload
Answered By - ch4mp
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.