如何使用自签名证书正确设置我的 Ionic (Angular) 开发机?

How to properly setup my Ionic (Angular) dev machine with self-signed certificate?

我正在开发 Ionic-Angular 应用程序,由于明文流量,我遇到了很多问题。所以我决定即使在编码时也切换到 https,但这并不容易。

我打开这个问题并提出我找到的答案,以备不时之需,如果您想这样做的话,希望能为您节省一些时间。

先决条件

您的开发机器需要一个主机名。此名称将在证书中声明,并且将使用此主机名访问 https 服务(URL 必须类似于 https://[hostname]:... 才能通过证书检查)。

如果您的网络还没有 DNS,您可以使用 dev-machine 上托管的 MaraDNS 之类的东西(请参阅 P.S。了解示例配置)。

生成各种格式的证书(和签名密钥)

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 = ch4mp@c4-soft.com
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 "" ]
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=""
  echo "# key password: $KEY_PASSWORD"
  
  if [ -z "" ]
  then
    if [ -z "$JAVA_HOME" ]
    then
      echo "ERROR: could not locate java home"
      exit 1
    else
      JAVA=$JAVA_HOME
    fi
  else
    JAVA=
  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 "" ]
  then
    HOST="$HOSTNAME"
  else
    HOST=""
  fi
  echo "# host (certificate CN): $HOST"
  
  if [ -z "" ]
  then
    STORE_PASSWORD="$KEY_PASSWORD"
  else
    STORE_PASSWORD=""
  fi
  echo "# store password : $STORE_PASSWORD"
  
  if [ -z "" ]
  then
    CERTIF_DIR="."
  else
    CERTIF_DIR=""
  fi
  echo "# certificates directory path: $CERTIF_DIR"
  CERTIF_DIR=$(echo $CERTIF_DIR | sed 's/\/\//g')
  
  if [ -z "" ]
  then
    CACERTS_PASSWORD="changeit"
  else
    CACERTS_PASSWORD=""
  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 ""

然后 运行 类似 ./self_signed.sh "secr3!" C:/Java/jdk1.8.0_281.

为每个 JDK/JRE 执行 ./self_signed.sh,然后 只需复制/粘贴/运行 第一次执行时的所有输出命令和 仅最后一个命令(在 JDK / JRE cacerts 文件中导入证书)来自第二次执行(否则您将丢失以前的证书)。

可能需要管理员权限才能在 Java 的 cacerts 中导入证书。

在 windows 上,Git Bash 在路径上包含 sedopensslkeytool

将此证书导入为受信任的根授权机构

如果您将此证书添加到受信任的根授权机构,您的浏览器在导航 https://[hostname]:....

等 URL 时将不会显示任何错误或警告

在 windows 上,这可以通过 certmgr.msc 完成(右键单击受信任的根权限,然后导入)。如果您在其他 OS.

上成功执行相同操作,请发表评论

配置 Ionic-Angular 使用此证书通过 https 服务

编辑angular.json设置your-project/architect/serve/options/下的“sslCert”和“sslKey”,分别指向之前生成的[hostname]_self_signed.crt[hostname]_self_signed_key.pem

这足以在运行宁ionic serve --ssl --host=[hostname]ionic capacitor run android -l --ssl --host=[hostname]

时选择正确的证书

在 Android 项目中嵌入证书

提醒:android 资源文件夹在您的项目下 android/app/src/main/res/ 或 Android Studio

中的 app/res/

首先,将 [hostname]_self_signed.crt 复制到 raw 资源,如果有的话,将 - 替换为主机名中的 _

在 xml 资源中创建 network_security_config.xml(小心修改主机名)

<?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>

重要说明:如果您的所有流量都通过 https 提供,则应将 cleartextTrafficPermitted 设置为 false(这是自 Android 9 以来的默认值)。至少考虑为产品构建这样做。

最后,编辑 AndroidManifest.xml 并将 android:networkSecurityConfig="@xml/network_security_config" 添加到您的 <application > 标签

在 iOS 项目中嵌入证书

我没有使用 iOS 的经验,如果您能正常使用,请随时发表评论或添加答案。

将您的后端配置为使用 self-signed 证书通过 https 提供服务

嗯...这真的取决于您使用的堆栈。几个样本:

  • 对于 Kestrel(在 Visual Studio 中调试的 .Net 应用程序),设置 ASPNETCORE_Kestrel__Certificates__Default__PasswordASPNETCORE_Kestrel__Certificates__Default__Path,第二个指向 [hostname]_self_signed.pfx
  • 对于spring-boot,将[hostname]_self_signed.jks复制到src/main/resources/并设置server.ssl属性
  • Keycloak 具有 setup the server 的综合文档和自定义证书
  • 如果其他后端类型正常工作,请发表评论

P.S. 我用于 MaraDNS 的 dwood3rc.txt 文件:

#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 = {}

一旦 DNS 服务器在我的开发机器上启动 (net start deadwood)(并且还配置了防火墙...),我将客户端配置为将其用作主 DNS(编辑不需要的 wifi 网络属性有根设备)等瞧!

P.S.2 Keycloak 独立配置允许测试设备通过 https

连接到 OpenId 端点

[hostname]_self_signed.jks复制到standalone/configuration/

编辑 standalone/configuration/standalone.xml 以将 ${jboss.bind.address:127.0.0.1} 替换为 ${jboss.bind.address:0.0.0.0}。保存并关闭。

使用 bin/standalone[.bat|.sh] 启动 Keycloak,然后使用 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