没有 Internet 连接的 WiFi 连接设备的最佳实践

Best practices for a WiFi-connected device WITHOUT Internet connectivity

我工作的公司生产的设备可以通过 WiFi 提供基于 Web 的设置和操作界面。该设备通常会在偏僻的地方使用,因此不能假设存在现有的 WiFi 网络:因此该设备的 WiFi 模块作为接入点运行,并通过 HTTP 提供花哨的 HTML5 网络应用程序(我在 2013 年最初实现时选择的 WiFi 模块唯一可用的选项)。

起初这很有效很好,但随着 Web 的发展,它正在慢慢分崩离析。特别是两个问题:

我不得不重做此功能的硬件和软件,因为原来的 WiFi 模块不再可用。如今可用的模块具有更多 CPU 的功能和存储空间(并且成本是原来的十分之一),因此现在可以做很多事情,例如通过 HTTPS 提供 Web 应用程序。对于此类设备,我普遍看到的建议是 获得适当的 SSL 证书,但我看不出这对我的情况有何影响:

也许可以通过向设备添加 DNS 服务器来使其工作,基本上使其成为强制门户,以便可以通过正常的 URL 访问它,我实际上可以购买的 SSL 证书。但是,我看到了很多潜在的问题:

这似乎将自签名证书作为唯一可行的选择,尽管所有的建议都与此相反。一个常见的反对意见是 "you're teaching users to ignore valid security warnings";我明白你的意思,但我应该怎么做呢?

是否有一些我忽略的方法可以让我的设备像 2013 年那样继续使用现代浏览器?

我同意你的看法,通过 HTTPS 在没有 public 地址的情况下访问 LAN 中的设备是一个棘手的问题。

恕我直言,应该有一个特殊的 TLS 版本来处理这些设备,但是目前还没有这样的标准。

在我看来,只有 self-signed 证书才可行。但是,当您谈论 "self-signed certificate generators" 时,这让我感到不寒而栗,因为您 永远不要为设备配备 pre-generated self-signed 证书 ! 从安全角度来看,这是你做过的最糟糕的事情之一,因为 pre-generated 证书和密钥对总是可以从固件中提取出来,然后用来攻击你的一个客户(联想和其他计算机制造商刚刚了解到在过去几年 hard way)! 关于 self-signed 证书,只有一种方法:

  1. 当设备首先start-up等待直到处理了足够的数据(播种),以便随机生成器可以生成安全的随机数据。

  2. 使用生成的随机数据生成public/private密钥对

  3. 使用之前生成的 key-pair 生成一个 self-signed 证书。使用正确的命令,您可以将有效期结束设置为您想要的任何日期。

请注意,您可以随时 re-do 步骤 3,例如当用户重新配置 IP-address 并且您必须在证书中更新它时。对于 self-developed 客户端(例如 Android 或 iOS),您可以执行 public 键固定,这意味着不必(重新)安装证书。

不幸的是,对于网络浏览器来说,即使具有相同 public 密钥的新证书也意味着您必须 re-install 信任该证书。

关于 HTML5 地理定位 API:你不能通过 HTTPS 使用这个 API 吗?如果您在 HTTPS 页面中使用 HTTP 资源,混合内容只是一个问题,但反之则应该没有问题。

顺便说一句:我发现了一些您可能感兴趣的幻灯片,即使在那种情况下可以访问 Internet。也许你从中得到了一些新的想法:https://www.w3.org/wiki/images/6/6c/TPAC2016_Local_Discovery_and_HTTPS.pdf

您可以根据需要指定 self-signed 证书的任何有效期。它是 openssl (-days) 的一个参数。

以下证书将在 100 年后到期:

openssl x509 -in localhost.pem -text -noout

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            c2:a8:fc:a1:29:02:96:dd
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = New York, L = New York, O = localhost, OU = Testing Domain, CN = localhost
        Validity
            Not Before: Apr  7 02:25:51 2018 GMT
            Not After : Mar 14 02:25:51 2118 GMT
        Subject: C = US, ST = New York, L = New York, O = localhost, OU = Testing Domain, CN = localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:96:89:c9:b4:8f:c0:2e:4c:92:15:cd:df:23:b2:
                    f1:6f:34:95:ca:e2:a4:c2:95:3f:1f:b3:50:56:16:
                    65:5a:1a:b9:99:6b:19:67:f0:13:54:76:23:4c:cd:
                    f5:2a:25:6c:d2:e0:67:e9:d5:34:0e:8f:2d:2b:dc:
                    04:3f:bb:e4:07:bd:a4:7a:ee:58:87:6b:cc:b7:1e:
                    79:fc:fc:bd:81:c7:e0:24:1d:91:14:50:70:5b:60:
                    13:ad:c7:eb:fb:67:3b:a9:e5:83:33:fb:ef:f4:26:
                    00:12:e3:af:e9:1e:e9:a2:5b:d1:98:3c:13:c0:30:
                    f0:13:9c:52:a3:b1:e7:3a:73:47:ce:ab:f9:5f:c4:
                    ff:74:1e:0c:86:ec:83:d6:b5:6d:36:31:00:26:97:
                    d8:74:39:cb:75:24:31:39:cc:e4:a1:78:38:92:07:
                    b4:a5:06:5a:8e:8b:0c:fa:7e:5e:55:7e:59:cf:f1:
                    1f:05:2e:e6:c2:6a:cf:4c:03:6a:66:b8:19:64:ae:
                    90:81:d9:28:37:2f:09:c1:b3:98:8a:bf:39:0a:0b:
                    23:db:55:79:02:83:fe:9d:be:ac:b8:3c:e5:1e:76:
                    5c:69:fe:f4:34:78:d4:36:82:0f:6e:c1:19:fe:39:
                    7f:fd:f0:4f:13:b6:2a:26:e6:ce:b0:fd:51:3d:38:
                    61:41
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                D6:DF:EB:FA:73:85:C9:22:AA:6D:79:E5:F9:16:01:2B:CC:E7:D8:D0
            X509v3 Authority Key Identifier: 
                keyid:D6:DF:EB:FA:73:85:C9:22:AA:6D:79:E5:F9:16:01:2B:CC:E7:D8:D0

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         4f:ca:30:3f:fe:61:73:16:3f:a1:6d:5a:35:c1:b6:97:d6:63:
         89:34:6e:05:e6:e6:84:7b:3b:b4:fc:89:65:7a:31:46:97:8f:
         00:11:c7:61:97:86:b8:31:c7:a7:91:f7:c9:19:4e:0a:39:d7:
         5e:95:52:17:92:8a:84:ce:6a:1b:1c:14:f2:15:18:62:78:07:
         1e:d9:3c:5d:9c:28:65:42:bf:4f:61:0f:a3:86:e6:ff:38:34:
         23:97:11:20:3c:5b:82:4a:ea:05:af:6d:5e:83:30:be:6d:64:
         de:85:b8:bb:89:60:59:7a:c8:1f:ba:2d:02:04:34:89:65:32:
         2c:10:f7:db:97:c3:ba:e2:ed:27:8a:bb:8d:9e:92:0a:d3:2e:
         ad:9e:fb:15:fc:25:73:0d:e2:f9:45:3b:18:fd:89:e6:6b:31:
         18:64:46:e2:dc:ad:e0:f3:f8:c1:b2:e6:93:bc:b3:db:9a:bc:
         d3:58:38:68:e2:28:b4:6d:ca:07:d8:af:21:dc:10:bd:c0:75:
         3f:b1:d2:24:96:5d:dd:a3:d2:e2:95:39:67:6b:06:bb:4d:91:
         76:4f:9b:ce:7f:25:39:47:73:3e:fd:81:82:15:ac:6a:2c:cb:
         82:76:04:b1:91:41:cf:8a:68:70:cd:7d:3f:2f:c9:1d:d5:a7:
         3e:d0:3d:00

您可以使用以下脚本生成它:

#! /bin/bash

mkdir /tmp/scert 2>/dev/null
rm -r /tmp/scert/* 2>/dev/null

if [ $# -ne 1 ];
then
    echo "Usage: scert <name>"
    exit
fi

if [ -e "/etc/ssl/private/.key" ];
then
    echo "/etc/ssl/private/.key already exists"
    exit
fi

if [ -e "/etc/ssl/certs/.crt" ];
then
    echo "/etc/ssl/certs/.crt already exists"
    exit
fi

if [ -e "/etc/ssl/certs/.pem" ];
then
    echo "/etc/ssl/certs/.pem already exists"
    exit
fi

echo "[req]" > /tmp/scert/tmp.cnf
echo "default_bits = 2048" >> /tmp/scert/tmp.cnf
echo "prompt = no" >> /tmp/scert/tmp.cnf
echo "default_md = sha256" >> /tmp/scert/tmp.cnf
echo "distinguished_name = dn" >> /tmp/scert/tmp.cnf
echo "" >> /tmp/scert/tmp.cnf
echo "[dn]" >> /tmp/scert/tmp.cnf
echo "C=US" >> /tmp/scert/tmp.cnf
echo "ST=New York" >> /tmp/scert/tmp.cnf
echo "L=Rochester" >> /tmp/scert/tmp.cnf
echo "O=" >> /tmp/scert/tmp.cnf
echo "OU=Testing Domain" >> /tmp/scert/tmp.cnf
echo "emailAddress=spam@uce.gov" >> /tmp/scert/tmp.cnf
echo "CN = localhost" >> /tmp/scert/tmp.cnf

echo "authorityKeyIdentifier=keyid,issuer" > /tmp/scert/tmp.ext
echo "basicConstraints=CA:FALSE" >> /tmp/scert/tmp.ext
echo "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment" >> /tmp/scert/tmp.ext
echo "subjectAltName = @alt_names" >> /tmp/scert/tmp.ext
echo "" >> /tmp/scert/tmp.ext
echo "[alt_names]" >> /tmp/scert/tmp.ext
echo "DNS.1 = localhost" >> /tmp/scert/tmp.ext

openssl genrsa -des3 -passout pass:xxxx -out /tmp/scert/tmp.pass.key 2048
openssl rsa -passin pass:xxxx -in "/tmp/scert/tmp.pass.key" -out "/tmp/scert/tmp.key"

openssl req -x509 -new -nodes -key /tmp/scert/tmp.key  -subj "/C=US/ST=New York/L=New York/O=/OU=Testing Domain/CN=localhost" -sha256 -days 36500 -out /tmp/scert/.pem

openssl req -new -sha256 -nodes -out /tmp/scert/tmp.csr -newkey rsa:2048 -keyout /tmp/scert/.key -config <( cat /tmp/scert/tmp.cnf )

openssl x509 -req -in /tmp/scert/tmp.csr -CA /tmp/scert/.pem -CAkey /tmp/scert/tmp.key -CAcreateserial -out /tmp/scert/.crt -days 36500 -sha256 -extfile /tmp/scert/tmp.ext

if [ -e "/tmp/scert/.key" ];
then
    sudo cp /tmp/scert/.key /etc/ssl/private
    sudo chown root:ssl-cert /etc/ssl/private/.key
    sudo chmod 640 /etc/ssl/private/.key
    ls -al /etc/ssl/private/.key
else
    echo "ERROR: /tmp/scert/.key not found"
fi

if [ -e "/tmp/scert/.crt" ];
then
    sudo cp /tmp/scert/.crt /etc/ssl/certs
    sudo chown root:root /etc/ssl/certs/.crt
    sudo chmod 755 /etc/ssl/certs/.crt
    ls -al /etc/ssl/certs/.crt
else
    echo "ERROR: /tmp/scert/.crt not found"
fi

if [ -e "/tmp/scert/.pem" ];
then
    sudo cp /tmp/scert/.pem /etc/ssl/certs
    sudo chown root:root /etc/ssl/certs/.pem
    sudo chmod 755 /etc/ssl/certs/.pem
    ls -al /etc/ssl/certs/.pem
else
    echo "ERROR: /tmp/scert/.pem not found"
fi

rm -r /tmp/scert/* 2>/dev/null

echo "DONE"